From 507d05aaf1855320bc09863264f457756593d476 Mon Sep 17 00:00:00 2001 From: DrKLO Date: Tue, 11 Feb 2014 18:32:09 +0400 Subject: [PATCH] update to 1.3.21 Ability to delete add rename contacts, proper handling of new contacts in phone book New photo crop Ability to disable automatic photo download Ability to disable notifications about new registered contacts Updated Spanish localization Bug fixes --- TMessagesProj/src/main/AndroidManifest.xml | 8 +- .../java/org/telegram/TL/TLClassStore.java | 1 + .../src/main/java/org/telegram/TL/TLRPC.java | 53 +- .../telegram/messenger/BackgroundService.java | 61 + .../messenger/ConnectionsManager.java | 69 +- .../messenger/ConnectionsManager2.java | 2670 +++++++++++++++++ .../messenger/ContactsController.java | 261 +- .../org/telegram/messenger/DispatchQueue.java | 14 +- .../messenger/ExportAuthorizationAction.java | 4 +- .../telegram/messenger/FileLoadOperation.java | 48 +- .../org/telegram/messenger/FileLoader.java | 17 +- .../messenger/GcmBroadcastReceiver.java | 45 +- .../org/telegram/messenger/GcmService.java | 42 + .../messenger/MessagesController.java | 229 +- .../telegram/messenger/MessagesStorage.java | 28 +- .../telegram/messenger/SerializedData.java | 140 +- .../org/telegram/messenger/TcpConnection.java | 25 +- .../org/telegram/messenger/UserConfig.java | 8 +- .../org/telegram/messenger/Utilities.java | 10 +- .../org/telegram/objects/MessageObject.java | 6 +- .../org/telegram/ui/ApplicationLoader.java | 5 + .../telegram/ui/Cells/ChatMessageCell.java | 26 + .../org/telegram/ui/Cells/ChatOrUserCell.java | 2 +- .../org/telegram/ui/Cells/DialogCell.java | 14 +- .../java/org/telegram/ui/ChatActivity.java | 421 +-- .../org/telegram/ui/ContactsActivity.java | 64 +- .../org/telegram/ui/GalleryImageViewer.java | 22 +- .../java/org/telegram/ui/LaunchActivity.java | 4 +- .../telegram/ui/LoginActivityPhoneView.java | 2 + .../org/telegram/ui/PhotoCropActivity.java | 381 +++ .../ui/SettingsWallpapersActivity.java | 6 +- .../org/telegram/ui/UserProfileActivity.java | 14 +- .../org/telegram/ui/Views/AvatarUpdater.java | 79 +- .../org/telegram/ui/Views/LayoutListView.java | 12 +- .../java/org/telegram/ui/Views/SeekBar.java | 135 + .../src/main/res/drawable-hdpi/ic_send.png | Bin 703 -> 591 bytes .../res/drawable-hdpi/ic_send_disabled.png | Bin 725 -> 599 bytes .../src/main/res/drawable-hdpi/mic.png | Bin 0 -> 602 bytes .../main/res/drawable-hdpi/mic_pressed.png | Bin 0 -> 581 bytes .../src/main/res/drawable-hdpi/pause1.png | Bin 0 -> 175 bytes .../main/res/drawable-hdpi/pause1_pressed.png | Bin 0 -> 175 bytes .../src/main/res/drawable-hdpi/pause2.png | Bin 0 -> 175 bytes .../main/res/drawable-hdpi/pause2_pressed.png | Bin 0 -> 175 bytes .../src/main/res/drawable-hdpi/play1.png | Bin 0 -> 441 bytes .../main/res/drawable-hdpi/play1_pressed.png | Bin 0 -> 440 bytes .../src/main/res/drawable-hdpi/play2.png | Bin 0 -> 439 bytes .../main/res/drawable-hdpi/play2_pressed.png | Bin 0 -> 439 bytes .../src/main/res/drawable-hdpi/player1.png | Bin 0 -> 687 bytes .../res/drawable-hdpi/player1_pressed.png | Bin 0 -> 1210 bytes .../src/main/res/drawable-hdpi/player2.png | Bin 0 -> 676 bytes .../res/drawable-hdpi/player2_pressed.png | Bin 0 -> 1250 bytes .../src/main/res/drawable-hdpi/rec.png | Bin 0 -> 331 bytes .../src/main/res/drawable-hdpi/slidearrow.png | Bin 0 -> 420 bytes .../src/main/res/drawable-hdpi/tooltip.png | Bin 0 -> 367 bytes .../src/main/res/drawable-ldpi/ic_send.png | Bin 418 -> 343 bytes .../res/drawable-ldpi/ic_send_disabled.png | Bin 420 -> 344 bytes .../src/main/res/drawable-ldpi/mic.png | Bin 0 -> 359 bytes .../main/res/drawable-ldpi/mic_pressed.png | Bin 0 -> 356 bytes .../src/main/res/drawable-ldpi/pause1.png | Bin 0 -> 155 bytes .../main/res/drawable-ldpi/pause1_pressed.png | Bin 0 -> 157 bytes .../src/main/res/drawable-ldpi/pause2.png | Bin 0 -> 157 bytes .../main/res/drawable-ldpi/pause2_pressed.png | Bin 0 -> 157 bytes .../src/main/res/drawable-ldpi/play1.png | Bin 0 -> 285 bytes .../main/res/drawable-ldpi/play1_pressed.png | Bin 0 -> 289 bytes .../src/main/res/drawable-ldpi/play2.png | Bin 0 -> 285 bytes .../main/res/drawable-ldpi/play2_pressed.png | Bin 0 -> 287 bytes .../src/main/res/drawable-ldpi/player1.png | Bin 0 -> 368 bytes .../res/drawable-ldpi/player1_pressed.png | Bin 0 -> 532 bytes .../src/main/res/drawable-ldpi/player2.png | Bin 0 -> 368 bytes .../res/drawable-ldpi/player2_pressed.png | Bin 0 -> 538 bytes .../src/main/res/drawable-ldpi/rec.png | Bin 0 -> 223 bytes .../src/main/res/drawable-ldpi/slidearrow.png | Bin 0 -> 261 bytes .../src/main/res/drawable-ldpi/tooltip.png | Bin 0 -> 223 bytes .../src/main/res/drawable-mdpi/ic_send.png | Bin 524 -> 309 bytes .../res/drawable-mdpi/ic_send_disabled.png | Bin 531 -> 381 bytes .../src/main/res/drawable-mdpi/mic.png | Bin 0 -> 431 bytes .../main/res/drawable-mdpi/mic_pressed.png | Bin 0 -> 402 bytes .../src/main/res/drawable-mdpi/pause1.png | Bin 0 -> 163 bytes .../main/res/drawable-mdpi/pause1_pressed.png | Bin 0 -> 163 bytes .../src/main/res/drawable-mdpi/pause2.png | Bin 0 -> 163 bytes .../main/res/drawable-mdpi/pause2_pressed.png | Bin 0 -> 163 bytes .../src/main/res/drawable-mdpi/play1.png | Bin 0 -> 308 bytes .../main/res/drawable-mdpi/play1_pressed.png | Bin 0 -> 308 bytes .../src/main/res/drawable-mdpi/play2.png | Bin 0 -> 309 bytes .../main/res/drawable-mdpi/play2_pressed.png | Bin 0 -> 308 bytes .../src/main/res/drawable-mdpi/player1.png | Bin 0 -> 516 bytes .../res/drawable-mdpi/player1_pressed.png | Bin 0 -> 828 bytes .../src/main/res/drawable-mdpi/player2.png | Bin 0 -> 503 bytes .../res/drawable-mdpi/player2_pressed.png | Bin 0 -> 845 bytes .../src/main/res/drawable-mdpi/rec.png | Bin 0 -> 278 bytes .../src/main/res/drawable-mdpi/slidearrow.png | Bin 0 -> 242 bytes .../src/main/res/drawable-mdpi/tooltip.png | Bin 0 -> 285 bytes .../src/main/res/drawable-xhdpi/ic_send.png | Bin 844 -> 748 bytes .../res/drawable-xhdpi/ic_send_disabled.png | Bin 842 -> 744 bytes .../src/main/res/drawable-xhdpi/mic.png | Bin 0 -> 722 bytes .../main/res/drawable-xhdpi/mic_pressed.png | Bin 0 -> 666 bytes .../src/main/res/drawable-xhdpi/pause1.png | Bin 0 -> 192 bytes .../res/drawable-xhdpi/pause1_pressed.png | Bin 0 -> 193 bytes .../src/main/res/drawable-xhdpi/pause2.png | Bin 0 -> 194 bytes .../res/drawable-xhdpi/pause2_pressed.png | Bin 0 -> 191 bytes .../src/main/res/drawable-xhdpi/play1.png | Bin 0 -> 492 bytes .../main/res/drawable-xhdpi/play1_pressed.png | Bin 0 -> 492 bytes .../src/main/res/drawable-xhdpi/play2.png | Bin 0 -> 492 bytes .../main/res/drawable-xhdpi/play2_pressed.png | Bin 0 -> 493 bytes .../src/main/res/drawable-xhdpi/player1.png | Bin 0 -> 858 bytes .../res/drawable-xhdpi/player1_pressed.png | Bin 0 -> 1470 bytes .../src/main/res/drawable-xhdpi/player2.png | Bin 0 -> 844 bytes .../res/drawable-xhdpi/player2_pressed.png | Bin 0 -> 1482 bytes .../src/main/res/drawable-xhdpi/rec.png | Bin 0 -> 439 bytes .../main/res/drawable-xhdpi/slidearrow.png | Bin 0 -> 350 bytes .../src/main/res/drawable-xhdpi/tooltip.png | Bin 0 -> 466 bytes .../src/main/res/drawable-xxhdpi/ic_send.png | Bin 1282 -> 1053 bytes .../res/drawable-xxhdpi/ic_send_disabled.png | Bin 1310 -> 1083 bytes .../src/main/res/drawable-xxhdpi/mic.png | Bin 0 -> 1090 bytes .../main/res/drawable-xxhdpi/mic_pressed.png | Bin 0 -> 996 bytes .../src/main/res/drawable-xxhdpi/pause1.png | Bin 0 -> 263 bytes .../res/drawable-xxhdpi/pause1_pressed.png | Bin 0 -> 262 bytes .../src/main/res/drawable-xxhdpi/pause2.png | Bin 0 -> 261 bytes .../res/drawable-xxhdpi/pause2_pressed.png | Bin 0 -> 261 bytes .../src/main/res/drawable-xxhdpi/play1.png | Bin 0 -> 644 bytes .../res/drawable-xxhdpi/play1_pressed.png | Bin 0 -> 643 bytes .../src/main/res/drawable-xxhdpi/play2.png | Bin 0 -> 644 bytes .../res/drawable-xxhdpi/play2_pressed.png | Bin 0 -> 644 bytes .../src/main/res/drawable-xxhdpi/player1.png | Bin 0 -> 1312 bytes .../res/drawable-xxhdpi/player1_pressed.png | Bin 0 -> 2355 bytes .../src/main/res/drawable-xxhdpi/player2.png | Bin 0 -> 1290 bytes .../res/drawable-xxhdpi/player2_pressed.png | Bin 0 -> 2436 bytes .../src/main/res/drawable-xxhdpi/rec.png | Bin 0 -> 573 bytes .../main/res/drawable-xxhdpi/slidearrow.png | Bin 0 -> 416 bytes .../src/main/res/drawable-xxhdpi/tooltip.png | Bin 0 -> 698 bytes .../main/res/drawable/mic_button_states.xml | 6 + .../src/main/res/drawable/pause1_states.xml | 6 + .../src/main/res/drawable/pause2_states.xml | 6 + .../main/res/drawable/photo_progress_chat.xml | 10 +- .../src/main/res/drawable/play1_states.xml | 6 + .../src/main/res/drawable/play2_states.xml | 6 + .../src/main/res/drawable/progress_chat.xml | 14 + .../main/res/drawable/send_button_states.xml | 4 +- .../chat_group_incoming_document_layout.xml | 2 +- .../chat_group_incoming_video_layout.xml | 2 +- .../layout/chat_incoming_document_layout.xml | 2 +- .../res/layout/chat_incoming_video_layout.xml | 2 +- .../src/main/res/layout/chat_layout.xml | 16 +- .../res/layout/chat_outgoing_audio_layout.xml | 91 + .../layout/chat_outgoing_document_layout.xml | 2 +- .../res/layout/chat_outgoing_video_layout.xml | 2 +- .../src/main/res/values-es/strings.xml | 16 +- 147 files changed, 4500 insertions(+), 619 deletions(-) create mode 100644 TMessagesProj/src/main/java/org/telegram/messenger/BackgroundService.java create mode 100644 TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager2.java create mode 100644 TMessagesProj/src/main/java/org/telegram/messenger/GcmService.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Views/SeekBar.java create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/mic.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/mic_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/pause1.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/pause1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/pause2.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/pause2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/play1.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/play1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/play2.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/play2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/player1.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/player1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/player2.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/player2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/rec.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/slidearrow.png create mode 100755 TMessagesProj/src/main/res/drawable-hdpi/tooltip.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/mic.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/mic_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/pause1.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/pause1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/pause2.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/pause2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/play1.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/play1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/play2.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/play2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/player1.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/player1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/player2.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/player2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/rec.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/slidearrow.png create mode 100755 TMessagesProj/src/main/res/drawable-ldpi/tooltip.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/mic.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/mic_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/pause1.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/pause1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/pause2.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/pause2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/play1.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/play1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/play2.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/play2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/player1.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/player1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/player2.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/player2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/rec.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/slidearrow.png create mode 100755 TMessagesProj/src/main/res/drawable-mdpi/tooltip.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/mic.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/mic_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/pause1.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/pause1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/pause2.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/pause2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/play1.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/play1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/play2.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/play2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/player1.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/player1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/player2.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/player2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/rec.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/slidearrow.png create mode 100755 TMessagesProj/src/main/res/drawable-xhdpi/tooltip.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/mic.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/mic_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/pause1.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/pause1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/pause2.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/pause2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/play1.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/play1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/play2.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/play2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/player1.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/player1_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/player2.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/player2_pressed.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/rec.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/slidearrow.png create mode 100755 TMessagesProj/src/main/res/drawable-xxhdpi/tooltip.png create mode 100644 TMessagesProj/src/main/res/drawable/mic_button_states.xml create mode 100644 TMessagesProj/src/main/res/drawable/pause1_states.xml create mode 100644 TMessagesProj/src/main/res/drawable/pause2_states.xml create mode 100644 TMessagesProj/src/main/res/drawable/play1_states.xml create mode 100644 TMessagesProj/src/main/res/drawable/play2_states.xml create mode 100644 TMessagesProj/src/main/res/drawable/progress_chat.xml create mode 100644 TMessagesProj/src/main/res/layout/chat_outgoing_audio_layout.xml diff --git a/TMessagesProj/src/main/AndroidManifest.xml b/TMessagesProj/src/main/AndroidManifest.xml index 12755bb70..60ce4b986 100644 --- a/TMessagesProj/src/main/AndroidManifest.xml +++ b/TMessagesProj/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="160" + android:versionName="1.3.21"> + @@ -164,6 +165,9 @@ + + + diff --git a/TMessagesProj/src/main/java/org/telegram/TL/TLClassStore.java b/TMessagesProj/src/main/java/org/telegram/TL/TLClassStore.java index e342b80d3..e67ac98f5 100644 --- a/TMessagesProj/src/main/java/org/telegram/TL/TLClassStore.java +++ b/TMessagesProj/src/main/java/org/telegram/TL/TLClassStore.java @@ -414,6 +414,7 @@ public class TLClassStore { classStore.put(TLRPC.TL_messageActionTTLChange.constructor, TLRPC.TL_messageActionTTLChange.class); classStore.put(TLRPC.TL_videoEncrypted.constructor, TLRPC.TL_videoEncrypted.class); classStore.put(TLRPC.TL_documentEncrypted.constructor, TLRPC.TL_documentEncrypted.class); + classStore.put(TLRPC.TL_audioEncrypted.constructor, TLRPC.TL_audioEncrypted.class); classStore.put(TLRPC.TL_gzip_packed.constructor, TLRPC.TL_gzip_packed.class); classStore.put(TLRPC.Vector.constructor, TLRPC.Vector.class); classStore.put(TLRPC.TL_userProfilePhotoOld.constructor, TLRPC.TL_userProfilePhotoOld.class); diff --git a/TMessagesProj/src/main/java/org/telegram/TL/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/TL/TLRPC.java index f99c0f512..bbc4ab981 100644 --- a/TMessagesProj/src/main/java/org/telegram/TL/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/TL/TLRPC.java @@ -1227,16 +1227,6 @@ public class TLRPC { } } - public static class Audio extends TLObject { - public long id; - public long access_hash; - public int user_id; - public int date; - public int duration; - public int size; - public int dc_id; - } - public static class TL_audioEmpty extends Audio { public static int constructor = 0x586988d8; @@ -8776,6 +8766,19 @@ public class TLRPC { public byte[] iv; } + public static class Audio extends TLObject { + public long id; + public long access_hash; + public int user_id; + public int date; + public int duration; + public int size; + public int dc_id; + public String path; + public byte[] key; + public byte[] iv; + } + public static class MessageAction extends TLObject { public Photo photo; public UserProfilePhoto newUserPhoto; @@ -8871,6 +8874,36 @@ public class TLRPC { } } + public static class TL_audioEncrypted extends Audio { + public static int constructor = 0x555555F6; + + + public void readParams(SerializedData stream) { + id = stream.readInt64(); + access_hash = stream.readInt64(); + user_id = stream.readInt32(); + date = stream.readInt32(); + duration = stream.readInt32(); + size = stream.readInt32(); + dc_id = stream.readInt32(); + key = stream.readByteArray(); + iv = stream.readByteArray(); + } + + public void serializeToStream(SerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeInt32(user_id); + stream.writeInt32(date); + stream.writeInt32(duration); + stream.writeInt32(size); + stream.writeInt32(dc_id); + stream.writeByteArray(key); + stream.writeByteArray(iv); + } + } + public static class TL_messageActionUserUpdatedPhoto extends MessageAction { public static int constructor = 0x55555551; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BackgroundService.java b/TMessagesProj/src/main/java/org/telegram/messenger/BackgroundService.java new file mode 100644 index 000000000..d5e929230 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BackgroundService.java @@ -0,0 +1,61 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013. + */ + +package org.telegram.messenger; + +import android.app.Service; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.util.Log; + +public class BackgroundService extends Service { + + private Handler handler = new Handler(Looper.getMainLooper()); + private Runnable checkRunnable = new Runnable() { + @Override + public void run() { + check(); + } + }; + + public BackgroundService() { + + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + super.onCreate(); + check(); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + super.onStartCommand(intent, flags, startId); + Log.e("tmessages", "onStartCommand"); + return START_STICKY; + } + + private void check() { + handler.removeCallbacks(checkRunnable); + handler.postDelayed(checkRunnable, 1500); + ConnectionsManager connectionsManager = ConnectionsManager.Instance; + } + + @Override + public void onDestroy() { + super.onDestroy(); + Log.e("tmessages", "onDestroy"); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java index d9007a68f..940e82854 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java @@ -15,7 +15,6 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Build; import android.util.Base64; -import android.util.Log; import org.telegram.TL.TLClassStore; import org.telegram.TL.TLObject; @@ -38,6 +37,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. public static int APP_ID = 2458; public static String APP_HASH = "5bce48dc7d331e62c955669eb7233217"; public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here"; + public static boolean disableContactsImport = false; private HashMap datacenters = new HashMap(); private HashMap> processedMessageIdsSet = new HashMap>(); @@ -160,7 +160,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (datacenters != null) { MessagesController.Instance.updateTimerProc(); if (datacenterWithId(currentDatacenterId).authKey != null) { - if (lastPingTime < System.currentTimeMillis() - 30000) { + if (lastPingTime < System.currentTimeMillis() - 19000) { lastPingTime = System.currentTimeMillis(); generatePing(); } @@ -947,6 +947,10 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } Datacenter requestDatacenter = datacenterWithId(datacenterId); + if (!request.initRequest && requestDatacenter.lastInitVersion != currentAppVersion) { + request.rpcRequest = wrapInLayer(request.rawRequest, requestDatacenter.datacenterId, request); + } + if (requestDatacenter == null) { if (!unknownDatacenterIds.contains(datacenterId)) { unknownDatacenterIds.add(datacenterId); @@ -1145,6 +1149,10 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } Datacenter requestDatacenter = datacenterWithId(datacenterId); + if (!request.initRequest && requestDatacenter.lastInitVersion != currentAppVersion) { + request.rpcRequest = wrapInLayer(request.rawRequest, requestDatacenter.datacenterId, request); + } + if (requestDatacenter == null) { unknownDatacenterIds.add(datacenterId); continue; @@ -1209,7 +1217,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. long messageId = generateMessageId(); - SerializedData os = new SerializedData(); + boolean canCompress = (request.flags & RPCRequest.RPCRequestClassCanCompress) != 0; + + SerializedData os = new SerializedData(!canCompress); request.rpcRequest.serializeToStream(os); int requestLength = os.length(); @@ -1223,13 +1233,16 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. sessionId = requestDatacenter.authUploadSessionId; } - if ((request.flags & RPCRequest.RPCRequestClassCanCompress) != 0) { + if (canCompress) { try { byte[] data = Utilities.compress(os.toByteArray()); if (data.length < requestLength) { TLRPC.TL_gzip_packed packed = new TLRPC.TL_gzip_packed(); packed.packed_data = data; request.rpcRequest = packed; + os = new SerializedData(true); + packed.serializeToStream(os); + requestLength = os.length(); } } catch (Exception e) { FileLog.e("tmessages", e); @@ -1431,7 +1444,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } TLRPC.TL_protoMessage wrapMessage(TLObject message, long sessionId, boolean meaningful) { - SerializedData os = new SerializedData(); + SerializedData os = new SerializedData(true); message.serializeToStream(os); if (os.length() != 0) { @@ -1463,7 +1476,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. msgAck.msg_ids = new ArrayList(); msgAck.msg_ids.addAll(arr); - SerializedData os = new SerializedData(); + SerializedData os = new SerializedData(true); msgAck.serializeToStream(os); if (os.length() != 0) { @@ -1559,7 +1572,20 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. NetworkMessage networkMessage = messages.get(0); TLRPC.TL_protoMessage message = networkMessage.protoMessage; - FileLog.d("tmessages", sessionId + ":Send message " + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + message.body); + if (DEBUG_VERSION) { + if (message.body instanceof TLRPC.invokeWithLayer11) { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer11)message.body).query); + } else if (message.body instanceof TLRPC.initConnection) { + TLRPC.initConnection r = (TLRPC.initConnection)message.body; + if (r.query instanceof TLRPC.invokeWithLayer11) { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer11)r.query).query); + } else { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + r.query); + } + } else { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + message.body); + } + } long msg_time = getTimeFromMsgId(message.msg_id); long currentTime = System.currentTimeMillis() + ((long)timeDifference) * 1000; @@ -1586,7 +1612,20 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. for (NetworkMessage networkMessage : messages) { TLRPC.TL_protoMessage message = networkMessage.protoMessage; containerMessages.add(message); - FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + message.body); + if (DEBUG_VERSION) { + if (message.body instanceof TLRPC.invokeWithLayer11) { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer11)message.body).query); + } else if (message.body instanceof TLRPC.initConnection) { + TLRPC.initConnection r = (TLRPC.initConnection)message.body; + if (r.query instanceof TLRPC.invokeWithLayer11) { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + ((TLRPC.invokeWithLayer11)r.query).query); + } else { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + r.query); + } + } else { + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + message.body); + } + } } messageContainer.messages = containerMessages; @@ -2339,7 +2378,11 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. try { ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo[] networkInfos = cm.getAllNetworkInfo(); - for (NetworkInfo info : networkInfos) { + for (int a = 0; a < 2; a++) { + if (a >= networkInfos.length) { + break; + } + NetworkInfo info = networkInfos[a]; FileLog.e("tmessages", "Network: " + info.getTypeName() + " status: " + info.getState() + " info: " + info.getExtraInfo() + " object: " + info.getDetailedState() + " other: " + info); } if (networkInfos.length == 0) { @@ -2438,6 +2481,8 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. } else { if (datacenter.authKeyId == null || !Arrays.equals(keyId, datacenter.authKeyId)) { FileLog.e("tmessages", "Error: invalid auth key id " + connection); + connection.suspendConnection(true); + connection.connect(); return; } @@ -2449,6 +2494,8 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. messageData = Utilities.aesIgeEncryption(messageData, keyData.aesKey, keyData.aesIv, false, false); if (messageData == null) { FileLog.e("tmessages", "Error: can't decrypt message data " + connection); + connection.suspendConnection(true); + connection.connect(); return; } @@ -2490,6 +2537,8 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. if (!Arrays.equals(messageKey, realMessageKey)) { FileLog.e("tmessages", "***** Error: invalid message key"); + connection.suspendConnection(true); + connection.connect(); return; } @@ -2551,7 +2600,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection. public void run() { moveToDatacenter(datacenterId); } - }, 1000, false); + }, 1000); } } }, null, true, RPCRequest.RPCRequestClassGeneric, currentDatacenterId); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager2.java b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager2.java new file mode 100644 index 000000000..d9007a68f --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager2.java @@ -0,0 +1,2670 @@ +/* + * This is the source code of Telegram for Android v. 1.3.2. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013. + */ + +package org.telegram.messenger; + +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.os.Build; +import android.util.Base64; +import android.util.Log; + +import org.telegram.TL.TLClassStore; +import org.telegram.TL.TLObject; +import org.telegram.TL.TLRPC; +import org.telegram.ui.ApplicationLoader; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.TcpConnectionDelegate { + public static boolean DEBUG_VERSION = true; + public static int APP_ID = 2458; + public static String APP_HASH = "5bce48dc7d331e62c955669eb7233217"; + public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here"; + + private HashMap datacenters = new HashMap(); + private HashMap> processedMessageIdsSet = new HashMap>(); + private HashMap nextSeqNoInSession = new HashMap(); + private ArrayList sessionsToDestroy = new ArrayList(); + private ArrayList destroyingSessions = new ArrayList(); + private HashMap> quickAckIdToRequestIds = new HashMap>(); + private HashMap> messagesIdsForConfirmation = new HashMap>(); + private HashMap> processedSessionChanges = new HashMap>(); + private HashMap pingIdToDate = new HashMap(); + private ConcurrentHashMap> requestsByGuids = new ConcurrentHashMap>(100, 1.0f, 2); + private ConcurrentHashMap requestsByClass = new ConcurrentHashMap(100, 1.0f, 2); + public volatile int connectionState = 2; + + private ArrayList requestQueue = new ArrayList(); + private ArrayList runningRequests = new ArrayList(); + private ArrayList actionQueue = new ArrayList(); + + private TLRPC.TL_auth_exportedAuthorization movingAuthorization; + public static final int DEFAULT_DATACENTER_ID = Integer.MAX_VALUE; + public static final int DC_UPDATE_TIME = 60 * 60; + public int currentDatacenterId; + public int movingToDatacenterId; + private long lastOutgoingMessageId = 0; + private int useDifferentBackend = 0; + private final int SESSION_VERSION = 2; + public int timeDifference = 0; + public int currentPingTime; + private int lastDestroySessionRequestTime; + private final boolean isDebugSession = false; + private boolean updatingDcSettings = false; + private int updatingDcStartTime = 0; + private int lastDcUpdateTime = 0; + private int currentAppVersion = 0; + + public static ConnectionsManager Instance = new ConnectionsManager(); + + private boolean paused = false; + private Runnable stageRunnable; + private Runnable pingRunnable; + private long lastPingTime = System.currentTimeMillis(); + private int nextWakeUpTimeout = 60000; + private int nextSleepTimeout = 60000; + + public ConnectionsManager() { + currentAppVersion = ApplicationLoader.getAppVersion(); + lastOutgoingMessageId = 0; + movingToDatacenterId = DEFAULT_DATACENTER_ID; + loadSession(); + + if (!isNetworkOnline()) { + connectionState = 1; + } + + Timer serviceTimer = new Timer(); + serviceTimer.schedule(new TimerTask() { + @Override + public void run() { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + long currentTime = System.currentTimeMillis(); + if (ApplicationLoader.lastPauseTime != 0 && ApplicationLoader.lastPauseTime < currentTime - nextSleepTimeout) { + boolean dontSleep = false; + for (RPCRequest request : runningRequests) { + if (request.retryCount < 10 && (request.runningStartTime + 60 > (int)(currentTime / 1000)) && ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0 || (request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0)) { + dontSleep = true; + break; + } + } + if (!dontSleep) { + for (RPCRequest request : requestQueue) { + if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0 || (request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { + dontSleep = true; + break; + } + } + } + if (!dontSleep) { + if (!paused) { + FileLog.e("tmessages", "pausing network and timers by sleep time = " + nextSleepTimeout); + for (Datacenter datacenter : datacenters.values()) { + if (datacenter.connection != null) { + datacenter.connection.suspendConnection(true); + } + if (datacenter.uploadConnection != null) { + datacenter.uploadConnection.suspendConnection(true); + } + if (datacenter.downloadConnection != null) { + datacenter.downloadConnection.suspendConnection(true); + } + } + } + try { + paused = true; + if (ApplicationLoader.lastPauseTime < currentTime - nextSleepTimeout - nextWakeUpTimeout) { + ApplicationLoader.lastPauseTime = currentTime; + nextSleepTimeout = 30000; + FileLog.e("tmessages", "wakeup network in background by wakeup time = " + nextWakeUpTimeout); + if (nextWakeUpTimeout < 30 * 60 * 1000) { + nextWakeUpTimeout *= 2; + } + } else { + Thread.sleep(500); + return; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } else { + ApplicationLoader.lastPauseTime += 30 * 1000; + FileLog.e("tmessages", "don't sleep 30 seconds because of upload or download request"); + } + } + if (paused) { + paused = false; + FileLog.e("tmessages", "resume network and timers"); + } + + if (datacenters != null) { + MessagesController.Instance.updateTimerProc(); + if (datacenterWithId(currentDatacenterId).authKey != null) { + if (lastPingTime < System.currentTimeMillis() - 30000) { + lastPingTime = System.currentTimeMillis(); + generatePing(); + } + if (!updatingDcSettings && lastDcUpdateTime < (int)(System.currentTimeMillis() / 1000) - DC_UPDATE_TIME) { + updateDcSettings(); + } + processRequestQueue(0, 0); + } + } + } + }); + } + }, 1000, 1000); + } + + public void resumeNetworkMaybe() { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + if (paused) { + ApplicationLoader.lastPauseTime = System.currentTimeMillis(); + nextWakeUpTimeout = 60000; + nextSleepTimeout = 30000; + FileLog.e("tmessages", "wakeup network in background by recieved push"); + } else if (ApplicationLoader.lastPauseTime != 0) { + ApplicationLoader.lastPauseTime = System.currentTimeMillis(); + FileLog.e("tmessages", "reset sleep timeout by recieved push"); + } + } + }); + } + + public void applicationMovedToForeground() { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + if (paused) { + nextSleepTimeout = 60000; + nextWakeUpTimeout = 60000; + FileLog.e("tmessages", "reset timers by application moved to foreground"); + } + } + }); + } + + //================================================================================ + // Config and session manage + //================================================================================ + + public Datacenter datacenterWithId(int datacenterId) { + if (datacenterId == DEFAULT_DATACENTER_ID) { + return datacenters.get(currentDatacenterId); + } + return datacenters.get(datacenterId); + } + + void setTimeDifference(int diff) { + boolean store = Math.abs(diff - timeDifference) > 25; + timeDifference = diff; + if (store) { + saveSession(); + } + } + + void loadSession() { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + File configFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "config.dat"); + if (configFile.exists()) { + try { + SerializedData data = new SerializedData(configFile); + int datacenterSetId = data.readInt32(); + int version = data.readInt32(); + + if (datacenterSetId == useDifferentBackend && version == SESSION_VERSION) { + sessionsToDestroy.clear(); + int count = data.readInt32(); + for (int a = 0; a < count; a++) { + sessionsToDestroy.add(data.readInt64()); + } + timeDifference = data.readInt32(); + count = data.readInt32(); + for (int a = 0; a < count; a++) { + Datacenter datacenter = new Datacenter(data, 0); + datacenters.put(datacenter.datacenterId, datacenter); + } + currentDatacenterId = data.readInt32(); + } else { + UserConfig.clearConfig(); + } + } catch (Exception e) { + UserConfig.clearConfig(); + } + } else { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("dataconfig", Context.MODE_PRIVATE); + int datacenterSetId = preferences.getInt("datacenterSetId", 0); + if (datacenterSetId == useDifferentBackend) { + currentDatacenterId = preferences.getInt("currentDatacenterId", 0); + timeDifference = preferences.getInt("timeDifference", 0); + lastDcUpdateTime = preferences.getInt("lastDcUpdateTime", 0); + try { + sessionsToDestroy.clear(); + String sessionsString = preferences.getString("sessionsToDestroy", null); + if (sessionsString != null) { + byte[] sessionsBytes = Base64.decode(sessionsString, Base64.DEFAULT); + if (sessionsBytes != null) { + SerializedData data = new SerializedData(sessionsBytes); + int count = data.readInt32(); + for (int a = 0; a < count; a++) { + sessionsToDestroy.add(data.readInt64()); + } + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + + try { + String datacentersString = preferences.getString("datacenters", null); + if (datacentersString != null) { + byte[] datacentersBytes = Base64.decode(datacentersString, Base64.DEFAULT); + if (datacentersBytes != null) { + SerializedData data = new SerializedData(datacentersBytes); + int count = data.readInt32(); + for (int a = 0; a < count; a++) { + Datacenter datacenter = new Datacenter(data, 1); + datacenters.put(datacenter.datacenterId, datacenter); + } + } + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + } + + if (currentDatacenterId != 0 && UserConfig.clientActivated) { + Datacenter datacenter = datacenterWithId(currentDatacenterId); + if (datacenter.authKey == null) { + currentDatacenterId = 0; + datacenters.clear(); + UserConfig.clearConfig(); + } + } + + if (datacenters.size() == 0) { + if (useDifferentBackend == 0) { + Datacenter datacenter = new Datacenter(); + datacenter.datacenterId = 1; + datacenter.addAddressAndPort("173.240.5.1", 443); + datacenters.put(datacenter.datacenterId, datacenter); + + datacenter = new Datacenter(); + datacenter.datacenterId = 2; + datacenter.addAddressAndPort("95.142.192.66", 443); + datacenters.put(datacenter.datacenterId, datacenter); + + datacenter = new Datacenter(); + datacenter.datacenterId = 3; + datacenter.addAddressAndPort("174.140.142.6", 443); + datacenters.put(datacenter.datacenterId, datacenter); + + datacenter = new Datacenter(); + datacenter.datacenterId = 4; + datacenter.addAddressAndPort("31.210.235.12", 443); + datacenters.put(datacenter.datacenterId, datacenter); + + datacenter = new Datacenter(); + datacenter.datacenterId = 5; + datacenter.addAddressAndPort("116.51.22.2", 443); + datacenters.put(datacenter.datacenterId, datacenter); + } else { + Datacenter datacenter = new Datacenter(); + datacenter.datacenterId = 1; + datacenter.addAddressAndPort("173.240.5.253", 443); + datacenters.put(datacenter.datacenterId, datacenter); + + datacenter = new Datacenter(); + datacenter.datacenterId = 2; + datacenter.addAddressAndPort("95.142.192.65", 443); + datacenters.put(datacenter.datacenterId, datacenter); + + datacenter = new Datacenter(); + datacenter.datacenterId = 3; + datacenter.addAddressAndPort("174.140.142.5", 443); + datacenters.put(datacenter.datacenterId, datacenter); + } + } else if (datacenters.size() == 1) { + Datacenter datacenter = new Datacenter(); + datacenter.datacenterId = 2; + datacenter.addAddressAndPort("95.142.192.66", 443); + datacenters.put(datacenter.datacenterId, datacenter); + + datacenter = new Datacenter(); + datacenter.datacenterId = 3; + datacenter.addAddressAndPort("174.140.142.6", 443); + datacenters.put(datacenter.datacenterId, datacenter); + + datacenter = new Datacenter(); + datacenter.datacenterId = 4; + datacenter.addAddressAndPort("31.210.235.12", 443); + datacenters.put(datacenter.datacenterId, datacenter); + + datacenter = new Datacenter(); + datacenter.datacenterId = 5; + datacenter.addAddressAndPort("116.51.22.2", 443); + datacenters.put(datacenter.datacenterId, datacenter); + } + + for (Datacenter datacenter : datacenters.values()) { + datacenter.authSessionId = getNewSessionId(); + } + + if (datacenters.size() != 0 && currentDatacenterId == 0) { + currentDatacenterId = 1; + saveSession(); + } + movingToDatacenterId = DEFAULT_DATACENTER_ID; + } + }); + } + + void saveSession() { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + try { + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("dataconfig", Context.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putInt("datacenterSetId", useDifferentBackend); + Datacenter currentDatacenter = datacenterWithId(currentDatacenterId); + if (currentDatacenter != null) { + editor.putInt("currentDatacenterId", currentDatacenterId); + editor.putInt("timeDifference", timeDifference); + editor.putInt("lastDcUpdateTime", lastDcUpdateTime); + + ArrayList sessions = new ArrayList(); + if (currentDatacenter.authSessionId != 0) { + sessions.add(currentDatacenter.authSessionId); + } + if (currentDatacenter.authDownloadSessionId != 0) { + sessions.add(currentDatacenter.authDownloadSessionId); + } + if (currentDatacenter.authUploadSessionId != 0) { + sessions.add(currentDatacenter.authUploadSessionId); + } + + if (!sessions.isEmpty()) { + SerializedData data = new SerializedData(sessions.size() * 8 + 4); + data.writeInt32(sessions.size()); + for (long session : sessions) { + data.writeInt64(session); + } + editor.putString("sessionsToDestroy", Base64.encodeToString(data.toByteArray(), Base64.DEFAULT)); + } else { + editor.remove("sessionsToDestroy"); + } + + if (!datacenters.isEmpty()) { + SerializedData data = new SerializedData(); + data.writeInt32(datacenters.size()); + for (Datacenter datacenter : datacenters.values()) { + datacenter.SerializeToStream(data); + } + editor.putString("datacenters", Base64.encodeToString(data.toByteArray(), Base64.DEFAULT)); + } else { + editor.remove("datacenters"); + } + } else { + editor.remove("datacenters"); + editor.remove("sessionsToDestroy"); + editor.remove("currentDatacenterId"); + editor.remove("timeDifference"); + } + editor.commit(); + File configFile = new File(ApplicationLoader.applicationContext.getFilesDir(), "config.dat"); + if (configFile.exists()) { + configFile.delete(); + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + }); + } + + void clearRequestsForRequestClass(int requestClass, Datacenter datacenter) { + for (RPCRequest request : runningRequests) { + Datacenter dcenter = datacenterWithId(request.runningDatacenterId); + if ((request.flags & requestClass) != 0 && dcenter != null && dcenter.datacenterId == datacenter.datacenterId) { + request.runningMessageId = 0; + request.runningMessageSeqNo = 0; + request.runningStartTime = 0; + request.runningMinStartTime = 0; + request.transportChannelToken = 0; + } + } + } + + public void cleanUp() { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + Datacenter datacenter = datacenterWithId(currentDatacenterId); + recreateSession(datacenter.authSessionId, datacenter); + } + }); + } + + void recreateSession(long sessionId, Datacenter datacenter) { + messagesIdsForConfirmation.remove(sessionId); + processedMessageIdsSet.remove(sessionId); + nextSeqNoInSession.remove(sessionId); + processedSessionChanges.remove(sessionId); + pingIdToDate.remove(sessionId); + + if (sessionId == datacenter.authSessionId) { + clearRequestsForRequestClass(RPCRequest.RPCRequestClassGeneric, datacenter); + FileLog.d("tmessages", "***** Recreate generic session"); + datacenter.authSessionId = getNewSessionId(); + } + } + + long getNewSessionId() { + long newSessionId = (long)(MessagesController.random.nextDouble() * Long.MAX_VALUE); + return isDebugSession ? (0xabcd000000000000L | (newSessionId & 0x0000ffffffffffffL)) : newSessionId; + } + + long generateMessageId() { + long messageId = (long)((((double)System.currentTimeMillis() + ((double)timeDifference) * 1000) * 4294967296.0) / 1000.0); + if (messageId <= lastOutgoingMessageId) { + messageId = lastOutgoingMessageId + 1; + } + while (messageId % 4 != 0) { + messageId++; + } + lastOutgoingMessageId = messageId; + return messageId; + } + + long getTimeFromMsgId(long messageId) { + return (long)(messageId / 4294967296.0 * 1000); + } + + int generateMessageSeqNo(long session, boolean increment) { + int value = 0; + if (nextSeqNoInSession.containsKey(session)) { + value = nextSeqNoInSession.get(session); + } + if (increment) { + nextSeqNoInSession.put(session, value + 1); + } + return value * 2 + (increment ? 1 : 0); + } + + boolean isMessageIdProcessed(long sessionId, long messageId) { + ArrayList set = processedMessageIdsSet.get(sessionId); + return set != null && set.contains(messageId); + } + + void addProcessedMessageId(long sessionId, long messageId) { + ArrayList set = processedMessageIdsSet.get(sessionId); + if (set != null) { + final int eraseLimit = 1000; + final int eraseThreshold = 224; + + if (set.size() > eraseLimit + eraseThreshold) { + for (int a = 0; a < Math.min(set.size(), eraseThreshold + 1); a++) { + set.remove(0); + } + } + set.add(messageId); + } else { + ArrayList sessionMap = new ArrayList(); + sessionMap.add(messageId); + processedMessageIdsSet.put(sessionId, sessionMap); + } + } + + //================================================================================ + // Requests manage + //================================================================================ + int lastClassGuid = 1; + public int generateClassGuid() { + int guid = lastClassGuid++; + ArrayList requests = new ArrayList(); + requestsByGuids.put(guid, requests); + return guid; + } + + public void cancelRpcsForClassGuid(int guid) { + ArrayList requests = requestsByGuids.get(guid); + if (requests != null) { + for (Long request : requests) { + cancelRpc(request, true); + } + requestsByGuids.remove(guid); + } + } + + public void bindRequestToGuid(final Long request, final int guid) { + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + ArrayList requests = requestsByGuids.get(guid); + if (requests != null) { + requests.add(request); + requestsByClass.put(request, guid); + } + } + }); + } + + public void removeRequestInClass(final Long request) { + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + Integer guid = requestsByClass.get(request); + if (guid != null) { + ArrayList requests = requestsByGuids.get(guid); + if (requests != null) { + requests.remove(request); + } + } + } + }); + } + + public void updateDcSettings() { + if (updatingDcSettings) { + return; + } + updatingDcStartTime = (int)(System.currentTimeMillis() / 1000); + updatingDcSettings = true; + TLRPC.TL_help_getConfig getConfig = new TLRPC.TL_help_getConfig(); + + ConnectionsManager.Instance.performRpc(getConfig, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + if (!updatingDcSettings) { + return; + } + if (error == null) { + lastDcUpdateTime = (int)(System.currentTimeMillis() / 1000); + TLRPC.TL_config config = (TLRPC.TL_config)response; + ArrayList datacentersArr = new ArrayList(); + HashMap datacenterMap = new HashMap(); + for (TLRPC.TL_dcOption datacenterDesc : config.dc_options) { + Datacenter existing = datacenterMap.get(datacenterDesc.id); + if (existing == null) { + existing = new Datacenter(); + existing.datacenterId = datacenterDesc.id; + existing.authSessionId = (long)(MessagesController.random.nextDouble() * Long.MAX_VALUE); + datacentersArr.add(existing); + datacenterMap.put(existing.datacenterId, existing); + } + existing.addAddressAndPort(datacenterDesc.ip_address, datacenterDesc.port); + } + + if (!datacentersArr.isEmpty()) { + for (Datacenter datacenter : datacentersArr) { + Datacenter exist = datacenterWithId(datacenter.datacenterId); + if (exist == null) { + datacenters.put(datacenter.datacenterId, datacenter); + } else { + exist.replaceAddressesAndPorts(datacenter.addresses, datacenter.ports); + } + if (datacenter.datacenterId == movingToDatacenterId) { + movingToDatacenterId = DEFAULT_DATACENTER_ID; + moveToDatacenter(datacenter.datacenterId); + } + } + saveSession(); + + processRequestQueue(RPCRequest.RPCRequestClassTransportMask, 0); + } + } + updatingDcSettings = false; + } + }, null, true, RPCRequest.RPCRequestClassEnableUnauthorized | RPCRequest.RPCRequestClassGeneric, currentDatacenterId); + } + + public long performRpc(final TLObject rpc, final RPCRequest.RPCRequestDelegate completionBlock, final RPCRequest.RPCProgressDelegate progressBlock, boolean requiresCompletion, int requestClass) { + return performRpc(rpc, completionBlock, progressBlock, requiresCompletion, requestClass, DEFAULT_DATACENTER_ID); + } + + public long performRpc(final TLObject rpc, final RPCRequest.RPCRequestDelegate completionBlock, final RPCRequest.RPCProgressDelegate progressBlock, boolean requiresCompletion, int requestClass, int datacenterId) { + return performRpc(rpc, completionBlock, progressBlock, null, requiresCompletion, requestClass, datacenterId); + } + + TLObject wrapInLayer(TLObject object, int datacenterId, RPCRequest request) { + if (object.layer() > 0) { + Datacenter datacenter = datacenterWithId(datacenterId); + if (datacenter == null || datacenter.lastInitVersion != currentAppVersion) { + request.initRequest = true; + TLRPC.initConnection invoke = new TLRPC.initConnection(); + invoke.query = object; + invoke.api_id = APP_ID; + try { + invoke.lang_code = Locale.getDefault().getCountry(); + invoke.device_model = Build.MANUFACTURER + Build.MODEL; + if (invoke.device_model == null) { + invoke.device_model = "Android unknown"; + } + PackageInfo pInfo = ApplicationLoader.applicationContext.getPackageManager().getPackageInfo(ApplicationLoader.applicationContext.getPackageName(), 0); + invoke.app_version = pInfo.versionName; + if (invoke.app_version == null) { + invoke.app_version = "App version unknown"; + } + invoke.system_version = "SDK " + Build.VERSION.SDK_INT; + } catch (Exception e) { + FileLog.e("tmessages", e); + invoke.lang_code = "en"; + invoke.device_model = "Android unknown"; + invoke.app_version = "App version unknown"; + invoke.system_version = "SDK " + Build.VERSION.SDK_INT; + } + if (invoke.lang_code == null || invoke.lang_code.length() == 0) { + invoke.lang_code = "en"; + } + if (invoke.device_model == null || invoke.device_model.length() == 0) { + invoke.device_model = "Android unknown"; + } + if (invoke.app_version == null || invoke.app_version.length() == 0) { + invoke.app_version = "App version unknown"; + } + if (invoke.system_version == null || invoke.system_version.length() == 0) { + invoke.system_version = "SDK Unknown"; + } + object = invoke; + } + TLRPC.invokeWithLayer11 invoke = new TLRPC.invokeWithLayer11(); + invoke.query = object; + FileLog.d("wrap in layer", "" + object); + return invoke; + } + return object; + } + + public static volatile long nextCallToken = 0; + long performRpc(final TLObject rpc, final RPCRequest.RPCRequestDelegate completionBlock, final RPCRequest.RPCProgressDelegate progressBlock, final RPCRequest.RPCQuickAckDelegate quickAckBlock, final boolean requiresCompletion, final int requestClass, final int datacenterId) { + + final long requestToken = nextCallToken++; + + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + RPCRequest request = new RPCRequest(); + request.token = requestToken; + request.flags = requestClass; + + request.runningDatacenterId = datacenterId; + + request.rawRequest = rpc; + request.rpcRequest = wrapInLayer(rpc, datacenterId, request); + request.completionBlock = completionBlock; + request.progressBlock = progressBlock; + request.quickAckBlock = quickAckBlock; + request.requiresCompletion = requiresCompletion; + + requestQueue.add(request); + + if (paused && ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0 || (request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0)) { + ApplicationLoader.lastPauseTime = System.currentTimeMillis(); + nextSleepTimeout = 30000; + FileLog.e("tmessages", "wakeup by download or upload request"); + } + + processRequestQueue(0, 0); + } + }); + + return requestToken; + } + + public void cancelRpc(final long token, final boolean notifyServer) { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + boolean found = false; + + for (int i = 0; i < requestQueue.size(); i++) { + RPCRequest request = requestQueue.get(i); + if (request.token == token) { + found = true; + request.cancelled = true; + FileLog.d("tmessages", "===== Cancelled queued rpc request " + request.rawRequest); + requestQueue.remove(i); + break; + } + } + + for (int i = 0; i < runningRequests.size(); i++) { + RPCRequest request = runningRequests.get(i); + if (request.token == token) { + found = true; + + FileLog.d("tmessages", "===== Cancelled running rpc request " + request.rawRequest); + + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { + if (notifyServer) { + TLRPC.TL_rpc_drop_answer dropAnswer = new TLRPC.TL_rpc_drop_answer(); + dropAnswer.req_msg_id = request.runningMessageId; + performRpc(dropAnswer, null, null, false, request.flags); + } + } + + request.cancelled = true; + runningRequests.remove(i); + break; + } + } + if (!found) { + FileLog.d("tmessages", "***** Warning: cancelling unknown request"); + } + } + }); + } + + public static boolean isNetworkOnline() { + boolean status = false; + try { + ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo netInfo = cm.getNetworkInfo(0); + if (netInfo != null && netInfo.getState() == NetworkInfo.State.CONNECTED) { + status = true; + } else { + netInfo = cm.getNetworkInfo(1); + if(netInfo != null && netInfo.getState() == NetworkInfo.State.CONNECTED) { + status = true; + } + } + } catch(Exception e) { + FileLog.e("tmessages", e); + return false; + } + return status; + } + + public int getCurrentTime() { + return (int)(System.currentTimeMillis() / 1000) + timeDifference; + } + + private void processRequestQueue(int requestClass, int _datacenterId) { + final HashMap activeTransportTokens = new HashMap(); + final ArrayList transportsToResume = new ArrayList(); + + final HashMap activeDownloadTransportTokens = new HashMap(); + final ArrayList downloadTransportsToResume = new ArrayList(); + + final HashMap activeUploadTransportTokens = new HashMap(); + final ArrayList uploadTransportsToResume = new ArrayList(); + + for (Datacenter datacenter : datacenters.values()) { + if (datacenter.connection != null) { + int channelToken = datacenter.connection.channelToken; + if (channelToken != 0) { + activeTransportTokens.put(datacenter.datacenterId, channelToken); + } + } + if (datacenter.downloadConnection != null) { + int channelToken = datacenter.downloadConnection.channelToken; + if (channelToken != 0) { + activeDownloadTransportTokens.put(datacenter.datacenterId, channelToken); + } + } + if (datacenter.uploadConnection != null) { + int channelToken = datacenter.uploadConnection.channelToken; + if (channelToken != 0) { + activeUploadTransportTokens.put(datacenter.datacenterId, channelToken); + } + } + } + for (RPCRequest request : runningRequests) { + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { + Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); + if (requestDatacenter != null && !activeTransportTokens.containsKey(requestDatacenter.datacenterId) && !transportsToResume.contains(requestDatacenter.datacenterId)) { + transportsToResume.add(requestDatacenter.datacenterId); + } + } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { + Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); + if (requestDatacenter != null && !activeDownloadTransportTokens.containsKey(requestDatacenter.datacenterId) && !downloadTransportsToResume.contains(requestDatacenter.datacenterId)) { + downloadTransportsToResume.add(requestDatacenter.datacenterId); + } + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { + Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); + if (requestDatacenter != null && !activeUploadTransportTokens.containsKey(requestDatacenter.datacenterId) && !uploadTransportsToResume.contains(requestDatacenter.datacenterId)) { + uploadTransportsToResume.add(requestDatacenter.datacenterId); + } + } + } + for (RPCRequest request : requestQueue) { + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { + Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); + if (requestDatacenter != null && !activeTransportTokens.containsKey(requestDatacenter.datacenterId) && !transportsToResume.contains(requestDatacenter.datacenterId)) { + transportsToResume.add(requestDatacenter.datacenterId); + } + } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { + Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); + if (requestDatacenter != null && !activeDownloadTransportTokens.containsKey(requestDatacenter.datacenterId) && !downloadTransportsToResume.contains(requestDatacenter.datacenterId)) { + downloadTransportsToResume.add(requestDatacenter.datacenterId); + } + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { + Datacenter requestDatacenter = datacenterWithId(request.runningDatacenterId); + if (requestDatacenter != null && !activeUploadTransportTokens.containsKey(requestDatacenter.datacenterId) && !uploadTransportsToResume.contains(requestDatacenter.datacenterId)) { + uploadTransportsToResume.add(requestDatacenter.datacenterId); + } + } + } + + boolean haveNetwork = true;//activeTransportTokens.size() != 0 || isNetworkOnline(); + + if (!activeTransportTokens.containsKey(currentDatacenterId) && !transportsToResume.contains(currentDatacenterId)) { + transportsToResume.add(currentDatacenterId); + } + + for (int it : transportsToResume) { + Datacenter datacenter = datacenterWithId(it); + if (datacenter.authKey != null) { + if (datacenter.connection == null) { + datacenter.connection = new TcpConnection(datacenter.datacenterId); + datacenter.connection.delegate = this; + datacenter.connection.transportRequestClass = RPCRequest.RPCRequestClassGeneric; + } + datacenter.connection.connect(); + } + } + for (int it : downloadTransportsToResume) { + Datacenter datacenter = datacenterWithId(it); + if (datacenter.authKey != null) { + if (datacenter.downloadConnection == null) { + datacenter.downloadConnection = new TcpConnection(datacenter.datacenterId); + datacenter.downloadConnection.delegate = this; + datacenter.downloadConnection.transportRequestClass = RPCRequest.RPCRequestClassDownloadMedia; + datacenter.authDownloadSessionId = getNewSessionId(); + } + datacenter.downloadConnection.connect(); + } + } + for (int it : uploadTransportsToResume) { + Datacenter datacenter = datacenterWithId(it); + if (datacenter.authKey != null) { + if (datacenter.uploadConnection == null) { + datacenter.uploadConnection = new TcpConnection(datacenter.datacenterId); + datacenter.uploadConnection.delegate = this; + datacenter.uploadConnection.transportRequestClass = RPCRequest.RPCRequestClassUploadMedia; + datacenter.authUploadSessionId = getNewSessionId(); + } + datacenter.uploadConnection.connect(); + } + } + + final HashMap> genericMessagesToDatacenters = new HashMap>(); + + final ArrayList unknownDatacenterIds = new ArrayList(); + final ArrayList neededDatacenterIds = new ArrayList(); + final ArrayList unauthorizedDatacenterIds = new ArrayList(); + + int currentTime = (int)(System.currentTimeMillis() / 1000); + for (RPCRequest request : runningRequests) { + if (updatingDcSettings && datacenters.size() > 1 && request.rawRequest instanceof TLRPC.TL_help_getConfig) { + if (updatingDcStartTime < currentTime - 60) { + updatingDcStartTime = currentTime; + ArrayList allDc = new ArrayList(datacenters.values()); + for (int a = 0; a < allDc.size(); a++) { + Datacenter dc = allDc.get(a); + if (dc.datacenterId == request.runningDatacenterId) { + allDc.remove(a); + break; + } + } + Datacenter newDc = allDc.get(Math.abs(MessagesController.random.nextInt()) % allDc.size()); + request.runningDatacenterId = newDc.datacenterId; + } + } + + int datacenterId = request.runningDatacenterId; + if (datacenterId == DEFAULT_DATACENTER_ID) { + if (movingToDatacenterId != DEFAULT_DATACENTER_ID) { + continue; + } + datacenterId = currentDatacenterId; + } + + Datacenter requestDatacenter = datacenterWithId(datacenterId); + if (requestDatacenter == null) { + if (!unknownDatacenterIds.contains(datacenterId)) { + unknownDatacenterIds.add(datacenterId); + } + continue; + } else if (requestDatacenter.authKey == null) { + if (!neededDatacenterIds.contains(datacenterId)) { + neededDatacenterIds.add(datacenterId); + } + continue; + } else if (!requestDatacenter.authorized && request.runningDatacenterId != DEFAULT_DATACENTER_ID && request.runningDatacenterId != currentDatacenterId && (request.flags & RPCRequest.RPCRequestClassEnableUnauthorized) == 0) { + if (!unauthorizedDatacenterIds.contains(datacenterId)) { + unauthorizedDatacenterIds.add(datacenterId); + } + continue; + } + + Integer tokenIt = activeTransportTokens.get(requestDatacenter.datacenterId); + int datacenterTransportToken = tokenIt != null ? tokenIt : 0; + + Integer uploadTokenIt = activeUploadTransportTokens.get(requestDatacenter.datacenterId); + int datacenterUploadTransportToken = uploadTokenIt != null ? uploadTokenIt : 0; + + Integer downloadTokenIt = activeDownloadTransportTokens.get(requestDatacenter.datacenterId); + int datacenterDownloadTransportToken = downloadTokenIt != null ? downloadTokenIt : 0; + + double maxTimeout = 8.0; + + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { + if (datacenterTransportToken == 0) { + continue; + } + } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { + if (!haveNetwork) { + FileLog.d("tmessages", "Don't have any network connection, skipping download request"); + continue; + } + if (datacenterDownloadTransportToken == 0) { + continue; + } + maxTimeout = 40.0; + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { + if (!haveNetwork) { + FileLog.d("tmessages", "Don't have any network connection, skipping upload request"); + continue; + } + if (datacenterUploadTransportToken == 0) { + continue; + } + maxTimeout = 30.0; + } + + long sessionId = 0; + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { + sessionId = requestDatacenter.authSessionId; + } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { + sessionId = requestDatacenter.authDownloadSessionId; + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0 ) { + sessionId = requestDatacenter.authUploadSessionId; + } + + boolean forceThisRequest = (request.flags & requestClass) != 0 && (_datacenterId == Integer.MIN_VALUE || requestDatacenter.datacenterId == _datacenterId); + + if (request.rawRequest instanceof TLRPC.TL_get_future_salts || request.rawRequest instanceof TLRPC.TL_destroy_session) { + if (request.runningMessageId != 0) { + request.addRespondMessageId(request.runningMessageId); + } + request.runningMessageId = 0; + request.runningMessageSeqNo = 0; + request.transportChannelToken = 0; + forceThisRequest = false; + } + + if (((Math.abs(currentTime - request.runningStartTime) > maxTimeout) && (currentTime > request.runningMinStartTime || Math.abs(currentTime - request.runningMinStartTime) > 60.0)) || forceThisRequest) { + if (!forceThisRequest && request.transportChannelToken > 0) { + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0 && datacenterTransportToken == request.transportChannelToken) { + FileLog.d("tmessages", "Request token is valid, not retrying " + request.rawRequest); + continue; + } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { + if (datacenterDownloadTransportToken != 0 && request.transportChannelToken == datacenterDownloadTransportToken) { + FileLog.d("tmessages", "Request download token is valid, not retrying " + request.rawRequest); + continue; + } + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { + if (datacenterUploadTransportToken != 0 && request.transportChannelToken == datacenterUploadTransportToken) { + FileLog.d("tmessages", "Request upload token is valid, not retrying " + request.rawRequest); + continue; + } + } + } + + request.retryCount++; + NetworkMessage networkMessage = new NetworkMessage(); + networkMessage.protoMessage = new TLRPC.TL_protoMessage(); + + if (request.runningMessageSeqNo == 0) { + request.runningMessageSeqNo = generateMessageSeqNo(sessionId, true); + request.runningMessageId = generateMessageId(); + } + networkMessage.protoMessage.msg_id = request.runningMessageId; + networkMessage.protoMessage.seqno = request.runningMessageSeqNo; + networkMessage.protoMessage.bytes = request.serializedLength; + networkMessage.protoMessage.body = request.rpcRequest; + networkMessage.rawRequest = request.rawRequest; + networkMessage.requestId = request.token; + + request.runningStartTime = currentTime; + + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { + request.transportChannelToken = datacenterTransportToken; + addMessageToDatacenter(genericMessagesToDatacenters, requestDatacenter.datacenterId, networkMessage); + } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { + request.transportChannelToken = datacenterDownloadTransportToken; + ArrayList arr = new ArrayList(); + arr.add(networkMessage); + proceedToSendingMessages(arr, sessionId, requestDatacenter.downloadConnection, false, false); + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { + request.transportChannelToken = datacenterUploadTransportToken; + ArrayList arr = new ArrayList(); + arr.add(networkMessage); + proceedToSendingMessages(arr, sessionId, requestDatacenter.uploadConnection, false, false); + } + } + } + + boolean updatingState = MessagesController.Instance.updatingState; + + if (activeTransportTokens.get(currentDatacenterId) != null) { + if (!updatingState) { + Datacenter currentDatacenter = datacenterWithId(currentDatacenterId); + + for (Long it : sessionsToDestroy) { + if (destroyingSessions.contains(it)) { + continue; + } + if (System.currentTimeMillis() / 1000 - lastDestroySessionRequestTime > 2.0) { + lastDestroySessionRequestTime = (int)(System.currentTimeMillis() / 1000); + TLRPC.TL_destroy_session destroySession = new TLRPC.TL_destroy_session(); + destroySession.session_id = it; + destroyingSessions.add(it); + + NetworkMessage networkMessage = new NetworkMessage(); + networkMessage.protoMessage = wrapMessage(destroySession, currentDatacenter.authSessionId, false); + if (networkMessage.protoMessage != null) { + addMessageToDatacenter(genericMessagesToDatacenters, currentDatacenter.datacenterId, networkMessage); + } + } + } + } + } + + int genericRunningRequestCount = 0; + int uploadRunningRequestCount = 0; + int downloadRunningRequestCount = 0; + + for (RPCRequest request : runningRequests) { + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { + genericRunningRequestCount++; + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { + uploadRunningRequestCount++; + } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { + downloadRunningRequestCount++; + } + } + + for (int i = 0; i < requestQueue.size(); i++) { + RPCRequest request = requestQueue.get(i); + if (request.cancelled) { + requestQueue.remove(i); + i--; + continue; + } + + if (updatingDcSettings && datacenters.size() > 1 && request.rawRequest instanceof TLRPC.TL_help_getConfig) { + if (updatingDcStartTime < currentTime - 60) { + updatingDcStartTime = currentTime; + ArrayList allDc = new ArrayList(datacenters.values()); + for (int a = 0; a < allDc.size(); a++) { + Datacenter dc = allDc.get(a); + if (dc.datacenterId == request.runningDatacenterId) { + allDc.remove(a); + break; + } + } + Datacenter newDc = allDc.get(Math.abs(MessagesController.random.nextInt()) % allDc.size()); + request.runningDatacenterId = newDc.datacenterId; + } + } + + int datacenterId = request.runningDatacenterId; + if (datacenterId == DEFAULT_DATACENTER_ID) { + if (movingToDatacenterId != DEFAULT_DATACENTER_ID && (request.flags & RPCRequest.RPCRequestClassEnableUnauthorized) == 0) { + continue; + } + datacenterId = currentDatacenterId; + } + + Datacenter requestDatacenter = datacenterWithId(datacenterId); + if (requestDatacenter == null) { + unknownDatacenterIds.add(datacenterId); + continue; + } else if (requestDatacenter.authKey == null) { + neededDatacenterIds.add(datacenterId); + continue; + } else if (!requestDatacenter.authorized && request.runningDatacenterId != DEFAULT_DATACENTER_ID && request.runningDatacenterId != currentDatacenterId && (request.flags & RPCRequest.RPCRequestClassEnableUnauthorized) == 0) { + unauthorizedDatacenterIds.add(datacenterId); + continue; + } + + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0 && activeTransportTokens.get(requestDatacenter.datacenterId) == null) { + continue; + } + + if (updatingState && (request.rawRequest instanceof TLRPC.TL_account_updateStatus || request.rawRequest instanceof TLRPC.TL_account_registerDevice)) { + continue; + } + + if (request.requiresCompletion) { + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { + if (genericRunningRequestCount >= 60) + continue; + + genericRunningRequestCount++; + + Integer tokenIt = activeTransportTokens.get(requestDatacenter.datacenterId); + request.transportChannelToken = tokenIt != null ? tokenIt : 0; + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { + if (uploadRunningRequestCount >= 20) + continue; + + if (!haveNetwork) { + FileLog.d("tmessages", "Don't have any network connection, skipping upload request"); + continue; + } + + if (uploadRunningRequestCount >= 5) { + continue; + } + + Integer uploadTokenIt = activeUploadTransportTokens.get(requestDatacenter.datacenterId); + request.transportChannelToken = uploadTokenIt != null ? uploadTokenIt : 0; + + uploadRunningRequestCount++; + } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { + if (!haveNetwork) { + FileLog.d("tmessages", "Don't have any network connection, skipping download request"); + continue; + } + + if (downloadRunningRequestCount >= 5) { + continue; + } + + Integer downloadTokenIt = activeDownloadTransportTokens.get(requestDatacenter.datacenterId); + request.transportChannelToken = downloadTokenIt != null ? downloadTokenIt : 0; + + downloadRunningRequestCount++; + } + } + + long messageId = generateMessageId(); + + SerializedData os = new SerializedData(); + request.rpcRequest.serializeToStream(os); + int requestLength = os.length(); + + if (requestLength != 0) { + long sessionId = 0; + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { + sessionId = requestDatacenter.authSessionId; + } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { + sessionId = requestDatacenter.authDownloadSessionId; + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { + sessionId = requestDatacenter.authUploadSessionId; + } + + if ((request.flags & RPCRequest.RPCRequestClassCanCompress) != 0) { + try { + byte[] data = Utilities.compress(os.toByteArray()); + if (data.length < requestLength) { + TLRPC.TL_gzip_packed packed = new TLRPC.TL_gzip_packed(); + packed.packed_data = data; + request.rpcRequest = packed; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + NetworkMessage networkMessage = new NetworkMessage(); + networkMessage.protoMessage = new TLRPC.TL_protoMessage(); + networkMessage.protoMessage.msg_id = messageId; + networkMessage.protoMessage.seqno = generateMessageSeqNo(sessionId, true); + networkMessage.protoMessage.bytes = requestLength; + networkMessage.protoMessage.body = request.rpcRequest; + networkMessage.rawRequest = request.rawRequest; + networkMessage.requestId = request.token; + + request.runningMessageId = messageId; + request.runningMessageSeqNo = networkMessage.protoMessage.seqno; + request.serializedLength = requestLength; + request.runningStartTime = (int)(System.currentTimeMillis() / 1000); + if (request.requiresCompletion) { + runningRequests.add(request); + } + + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { + addMessageToDatacenter(genericMessagesToDatacenters, requestDatacenter.datacenterId, networkMessage); + } else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) { + ArrayList arr = new ArrayList(); + arr.add(networkMessage); + proceedToSendingMessages(arr, sessionId, requestDatacenter.downloadConnection, false, false); + } else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { + ArrayList arr = new ArrayList(); + arr.add(networkMessage); + proceedToSendingMessages(arr, sessionId, requestDatacenter.uploadConnection, false, false); + } else { + FileLog.e("tmessages", "***** Error: request " + request.rawRequest + " has undefined session"); + } + } else { + FileLog.e("tmessages", "***** Couldn't serialize " + request.rawRequest); + } + + requestQueue.remove(i); + i--; + } + + for (Datacenter datacenter : datacenters.values()) { + if (genericMessagesToDatacenters.get(datacenter.datacenterId) == null && datacenter.connection != null && datacenter.connection.channelToken != 0) { + ArrayList arr = messagesIdsForConfirmation.get(datacenter.authSessionId); + if (arr != null && arr.size() != 0) { + genericMessagesToDatacenters.put(datacenter.datacenterId, new ArrayList()); + } + } + } + + for (int iter : genericMessagesToDatacenters.keySet()) { + Datacenter datacenter = datacenterWithId(iter); + if (datacenter != null) { + boolean scannedPreviousRequests = false; + long lastSendMessageRpcId = 0; + + boolean hasSendMessage = false; + ArrayList arr = genericMessagesToDatacenters.get(iter); + for (NetworkMessage networkMessage : arr) { + TLRPC.TL_protoMessage message = networkMessage.protoMessage; + + Object rawRequest = networkMessage.rawRequest; + + if (rawRequest != null && (rawRequest instanceof TLRPC.TL_messages_sendMessage || + rawRequest instanceof TLRPC.TL_messages_sendMedia || + rawRequest instanceof TLRPC.TL_messages_forwardMessages || + rawRequest instanceof TLRPC.TL_messages_sendEncrypted)) { + + if (rawRequest instanceof TLRPC.TL_messages_sendMessage) { + hasSendMessage = true; + } + + if (!scannedPreviousRequests) { + scannedPreviousRequests = true; + + ArrayList currentRequests = new ArrayList(); + for (NetworkMessage currentNetworkMessage : arr) { + TLRPC.TL_protoMessage currentMessage = currentNetworkMessage.protoMessage; + + Object currentRawRequest = currentNetworkMessage.rawRequest; + + if (currentRawRequest instanceof TLRPC.TL_messages_sendMessage || + currentRawRequest instanceof TLRPC.TL_messages_sendMedia || + currentRawRequest instanceof TLRPC.TL_messages_forwardMessages || + currentRawRequest instanceof TLRPC.TL_messages_sendEncrypted) { + currentRequests.add(currentMessage.msg_id); + } + } + + long maxRequestId = 0; + for (RPCRequest request : runningRequests) { + if (request.rawRequest instanceof TLRPC.TL_messages_sendMessage || + request.rawRequest instanceof TLRPC.TL_messages_sendMedia || + request.rawRequest instanceof TLRPC.TL_messages_forwardMessages || + request.rawRequest instanceof TLRPC.TL_messages_sendEncrypted) { + if (!currentRequests.contains(request.runningMessageId)) { + maxRequestId = Math.max(maxRequestId, request.runningMessageId); + } + } + } + + lastSendMessageRpcId = maxRequestId; + } + + if (lastSendMessageRpcId != 0 && lastSendMessageRpcId != message.msg_id) { + TLRPC.TL_invokeAfterMsg invokeAfterMsg = new TLRPC.TL_invokeAfterMsg(); + invokeAfterMsg.msg_id = lastSendMessageRpcId; + invokeAfterMsg.query = message.body; + + message.body = invokeAfterMsg; + message.bytes = message.bytes + 4 + 8; + } + + lastSendMessageRpcId = message.msg_id; + } + } + + if (datacenter.connection == null) { + datacenter.connection = new TcpConnection(datacenter.datacenterId); + datacenter.connection.delegate = this; + datacenter.connection.transportRequestClass = RPCRequest.RPCRequestClassGeneric; + } + + proceedToSendingMessages(arr, datacenter.authSessionId, datacenter.connection, hasSendMessage, arr.size() != 0); + } + } + + if ((requestClass & RPCRequest.RPCRequestClassGeneric) != 0) { + if (_datacenterId == Integer.MIN_VALUE) { + for (Datacenter datacenter : datacenters.values()) { + ArrayList messagesIt = genericMessagesToDatacenters.get(datacenter.datacenterId); + if (messagesIt == null || messagesIt.size() == 0) { + generatePing(datacenter); + } + } + } else { + ArrayList messagesIt = genericMessagesToDatacenters.get(_datacenterId); + if (messagesIt == null || messagesIt.size() == 0) { + generatePing(); + } + } + } + + if (!unknownDatacenterIds.isEmpty() && !updatingDcSettings) { + updateDcSettings(); + } + + for (int num : neededDatacenterIds) { + if (num != movingToDatacenterId) { + boolean notFound = true; + for (Action actor : actionQueue) { + if (actor instanceof HandshakeAction) { + HandshakeAction eactor = (HandshakeAction)actor; + if (eactor.datacenter.datacenterId == num) { + notFound = false; + break; + } + } + } + if (notFound) { + HandshakeAction actor = new HandshakeAction(datacenterWithId(num)); + actor.delegate = this; + dequeueActor(actor, true); + } + } + } + + for (int num : unauthorizedDatacenterIds) { + if (num != currentDatacenterId && num != movingToDatacenterId && UserConfig.clientUserId != 0) { + boolean notFound = true; + for (Action actor : actionQueue) { + if (actor instanceof ExportAuthorizationAction) { + ExportAuthorizationAction eactor = (ExportAuthorizationAction)actor; + if (eactor.datacenter.datacenterId == num) { + notFound = false; + break; + } + } + } + if (notFound) { + ExportAuthorizationAction actor = new ExportAuthorizationAction(datacenterWithId(num)); + actor.delegate = this; + dequeueActor(actor, true); + } + } + } + } + + void addMessageToDatacenter(HashMap> pMap, int datacenterId, NetworkMessage message) { + ArrayList arr = pMap.get(datacenterId); + if (arr == null) { + arr = new ArrayList(); + pMap.put(datacenterId, arr); + } + arr.add(message); + } + + TLRPC.TL_protoMessage wrapMessage(TLObject message, long sessionId, boolean meaningful) { + SerializedData os = new SerializedData(); + message.serializeToStream(os); + + if (os.length() != 0) { + TLRPC.TL_protoMessage protoMessage = new TLRPC.TL_protoMessage(); + protoMessage.msg_id = generateMessageId(); + protoMessage.bytes = os.length(); + protoMessage.body = message; + protoMessage.seqno = generateMessageSeqNo(sessionId, meaningful); + return protoMessage; + } else { + FileLog.e("tmessages", "***** Couldn't serialize " + message); + return null; + } + } + + void proceedToSendingMessages(ArrayList messageList, long sessionId, TcpConnection connection, boolean reportAck, boolean requestShortTimeout) { + if (sessionId == 0) { + return; + } + + ArrayList messages = new ArrayList(); + if(messageList != null) { + messages.addAll(messageList); + } + + final ArrayList arr = messagesIdsForConfirmation.get(sessionId); + if (arr != null && arr.size() != 0) { + TLRPC.TL_msgs_ack msgAck = new TLRPC.TL_msgs_ack(); + msgAck.msg_ids = new ArrayList(); + msgAck.msg_ids.addAll(arr); + + SerializedData os = new SerializedData(); + msgAck.serializeToStream(os); + + if (os.length() != 0) { + NetworkMessage networkMessage = new NetworkMessage(); + networkMessage.protoMessage = new TLRPC.TL_protoMessage(); + + networkMessage.protoMessage.msg_id = generateMessageId(); + networkMessage.protoMessage.seqno = generateMessageSeqNo(sessionId, false); + + networkMessage.protoMessage.bytes = os.length(); + networkMessage.protoMessage.body = msgAck; + + messages.add(networkMessage); + } else { + FileLog.e("tmessages", "***** Couldn't serialize "); + } + + arr.clear(); + } + + sendMessagesToTransport(messages, connection, sessionId, reportAck, requestShortTimeout); + } + + void sendMessagesToTransport(ArrayList messagesToSend, TcpConnection connection, long sessionId, boolean reportAck, boolean requestShortTimeout) { + if (messagesToSend.size() == 0) { + return; + } + + if (connection == null) { + FileLog.e("tmessages", String.format("***** Transport for session 0x%x not found", sessionId)); + return; + } + + ArrayList currentMessages = new ArrayList(); + + int currentSize = 0; + for (int a = 0; a < messagesToSend.size(); a++) { + NetworkMessage networkMessage = messagesToSend.get(a); + currentMessages.add(networkMessage); + + TLRPC.TL_protoMessage protoMessage = networkMessage.protoMessage; + + currentSize += protoMessage.bytes; + + if (currentSize >= 3 * 1024 || a == messagesToSend.size() - 1) { + ArrayList quickAckId = new ArrayList(); + byte[] transportData = createConnectionData(currentMessages, sessionId, quickAckId, connection); + + if (transportData != null) { + if (reportAck && quickAckId.size() != 0) { + ArrayList requestIds = new ArrayList(); + + for (NetworkMessage message : messagesToSend) { + if (message.requestId != 0) { + requestIds.add(message.requestId); + } + } + + if (requestIds.size() != 0) { + int ack = quickAckId.get(0); + ArrayList arr = quickAckIdToRequestIds.get(ack); + if (arr == null) { + arr = new ArrayList(); + quickAckIdToRequestIds.put(ack, arr); + } + arr.addAll(requestIds); + } + } + + connection.sendData(transportData, reportAck, requestShortTimeout); + } else { + FileLog.e("tmessages", "***** Transport data is nil"); + } + + currentSize = 0; + currentMessages.clear(); + } + } + } + + @SuppressWarnings("unused") + byte[] createConnectionData(ArrayList messages, long sessionId, ArrayList quickAckId, TcpConnection connection) { + Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); + if (datacenter.authKey == null) { + return null; + } + + long messageId; + TLObject messageBody; + int messageSeqNo; + + if (messages.size() == 1) { + NetworkMessage networkMessage = messages.get(0); + TLRPC.TL_protoMessage message = networkMessage.protoMessage; + + FileLog.d("tmessages", sessionId + ":Send message " + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + message.body); + + long msg_time = getTimeFromMsgId(message.msg_id); + long currentTime = System.currentTimeMillis() + ((long)timeDifference) * 1000; + + if (msg_time < currentTime - 30000 || msg_time > currentTime + 25000) { + FileLog.d("tmessages", "wrap in messages continaer"); + TLRPC.TL_msg_container messageContainer = new TLRPC.TL_msg_container(); + messageContainer.messages = new ArrayList(); + messageContainer.messages.add(message); + + messageId = generateMessageId(); + messageBody = messageContainer; + messageSeqNo = generateMessageSeqNo(sessionId, false); + } else { + messageId = message.msg_id; + messageBody = message.body; + messageSeqNo = message.seqno; + } + } else { + TLRPC.TL_msg_container messageContainer = new TLRPC.TL_msg_container(); + + ArrayList containerMessages = new ArrayList(messages.size()); + + for (NetworkMessage networkMessage : messages) { + TLRPC.TL_protoMessage message = networkMessage.protoMessage; + containerMessages.add(message); + FileLog.d("tmessages", sessionId + ":DC" + datacenter.datacenterId + "> Send message (" + message.seqno + ", " + message.msg_id + "): " + message.body); + } + + messageContainer.messages = containerMessages; + + messageId = generateMessageId(); + messageBody = messageContainer; + messageSeqNo = generateMessageSeqNo(sessionId, false); + } + + SerializedData innerMessageOs = new SerializedData(); + messageBody.serializeToStream(innerMessageOs); + byte[] messageData = innerMessageOs.toByteArray(); + + SerializedData innerOs = new SerializedData(8 + 8 + 8 + 4 + 4 + messageData.length); + long serverSalt = datacenter.selectServerSalt(getCurrentTime()); + if (serverSalt == 0) { + innerOs.writeInt64(0); + } else { + innerOs.writeInt64(serverSalt); + } + innerOs.writeInt64(sessionId); + innerOs.writeInt64(messageId); + innerOs.writeInt32(messageSeqNo); + innerOs.writeInt32(messageData.length); + innerOs.writeRaw(messageData); + byte[] innerData = innerOs.toByteArray(); + + byte[] messageKeyFull = Utilities.computeSHA1(innerData); + byte[] messageKey = new byte[16]; + System.arraycopy(messageKeyFull, messageKeyFull.length - 16, messageKey, 0, 16); + + if (quickAckId != null) { + SerializedData data = new SerializedData(messageKeyFull); + quickAckId.add(data.readInt32() & 0x7fffffff); + } + + MessageKeyData keyData = Utilities.generateMessageKeyData(datacenter.authKey, messageKey, false); + + SerializedData dataForEncryption = new SerializedData(innerData.length + (innerData.length % 16)); + dataForEncryption.writeRaw(innerData); + byte[] b = new byte[1]; + while (dataForEncryption.length() % 16 != 0) { + MessagesController.random.nextBytes(b); + dataForEncryption.writeByte(b[0]); + } + + byte[] encryptedData = Utilities.aesIgeEncryption(dataForEncryption.toByteArray(), keyData.aesKey, keyData.aesIv, true, false); + + try { + SerializedData data = new SerializedData(datacenter.authKeyId.length + messageKey.length + encryptedData.length); + data.writeRaw(datacenter.authKeyId); + data.writeRaw(messageKey); + data.writeRaw(encryptedData); + + return data.toByteArray(); + } catch (Exception e) { + FileLog.e("tmessages", e); + innerData = null; + messageData = null; + System.gc(); + SerializedData data = new SerializedData(); + data.writeRaw(datacenter.authKeyId); + data.writeRaw(messageKey); + data.writeRaw(encryptedData); + + return data.toByteArray(); + } + } + + void refillSaltSet(final Datacenter datacenter) { + for (RPCRequest request : requestQueue) { + if (request.rawRequest instanceof TLRPC.TL_get_future_salts) { + return; + } + } + + for (RPCRequest request : runningRequests) { + if (request.rawRequest instanceof TLRPC.TL_get_future_salts) { + return; + } + } + + TLRPC.TL_get_future_salts getFutureSalts = new TLRPC.TL_get_future_salts(); + getFutureSalts.num = 64; + + performRpc(getFutureSalts, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + TLRPC.TL_futuresalts res = (TLRPC.TL_futuresalts)response; + if (error == null) { + int currentTime = getCurrentTime(); + datacenter.mergeServerSalts(currentTime, res.salts); + saveSession(); + } + } + }, null, true, RPCRequest.RPCRequestClassGeneric, datacenter.datacenterId); + } + + void messagesConfirmed(final long requestMsgId) { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + for (RPCRequest request : runningRequests) { + if (requestMsgId == request.runningMessageId) { + request.confirmed = true; + } + } + } + }); + } + + void rpcCompleted(final long requestMsgId) { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + for (int i = 0; i < runningRequests.size(); i++) { + RPCRequest request = runningRequests.get(i); + removeRequestInClass(request.token); + if (request.respondsToMessageId(requestMsgId)) { + runningRequests.remove(i); + i--; + } + } + } + }); + } + + void processMessage(TLObject message, long messageId, int messageSeqNo, long messageSalt, TcpConnection connection, long sessionId, long innerMsgId, long containerMessageId) { + if (message == null) { + FileLog.e("tmessages", "message is null"); + return; + } + Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); + + if (message instanceof TLRPC.TL_new_session_created) { + TLRPC.TL_new_session_created newSession = (TLRPC.TL_new_session_created)message; + ArrayList arr = processedSessionChanges.get(sessionId); + if (arr == null) { + arr = new ArrayList(); + processedSessionChanges.put(sessionId, arr); + } + if (!arr.contains(newSession.unique_id)) { + FileLog.d("tmessages", "New session:"); + FileLog.d("tmessages", String.format(" first message id: %d", newSession.first_msg_id)); + FileLog.d("tmessages", String.format(" server salt: %d", newSession.server_salt)); + FileLog.d("tmessages", String.format(" unique id: %d", newSession.unique_id)); + + long serverSalt = newSession.server_salt; + + ServerSalt serverSaltDesc = new ServerSalt(); + serverSaltDesc.validSince = getCurrentTime(); + serverSaltDesc.validUntil = getCurrentTime() + 30 * 60; + serverSaltDesc.value = serverSalt; + datacenter.addServerSalt(serverSaltDesc); + + for (RPCRequest request : runningRequests) { + Datacenter dcenter = datacenterWithId(request.runningDatacenterId); + if (request.runningMessageId < newSession.first_msg_id && (request.flags & connection.transportRequestClass) != 0 && dcenter != null && dcenter.datacenterId == datacenter.datacenterId) { + request.runningMessageId = 0; + request.runningMessageSeqNo = 0; + request.runningStartTime = 0; + request.runningMinStartTime = 0; + request.transportChannelToken = 0; + } + } + + saveSession(); + + if (sessionId == datacenter.authSessionId && datacenter.datacenterId == currentDatacenterId && UserConfig.clientActivated) { + MessagesController.Instance.getDifference(); + } + arr.add(newSession.unique_id); + } + } else if (message instanceof TLRPC.TL_msg_container) { + /*if (messageId != 0) { + long time = getTimeFromMsgId(messageId); + long currentTime = System.currentTimeMillis(); + timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); + }*/ + + TLRPC.TL_msg_container messageContainer = (TLRPC.TL_msg_container)message; + for (TLRPC.TL_protoMessage innerMessage : messageContainer.messages) { + long innerMessageId = innerMessage.msg_id; + if (innerMessage.seqno % 2 != 0) { + ArrayList set = messagesIdsForConfirmation.get(sessionId); + if (set == null) { + set = new ArrayList(); + messagesIdsForConfirmation.put(sessionId, set); + } + set.add(innerMessageId); + } + if (isMessageIdProcessed(sessionId, innerMessageId)) { + continue; + } + processMessage(innerMessage.body, 0, innerMessage.seqno, messageSalt, connection, sessionId, innerMessageId, messageId); + addProcessedMessageId(sessionId, innerMessageId); + } + } else if (message instanceof TLRPC.TL_pong) { + TLRPC.TL_pong pong = (TLRPC.TL_pong)message; + long pingId = pong.ping_id; + + ArrayList itemsToDelete = new ArrayList(); + for (Long pid : pingIdToDate.keySet()) { + if (pid == pingId) { + int time = pingIdToDate.get(pid); + int pingTime = (int)(System.currentTimeMillis() / 1000) - time; + + if (Math.abs(pingTime) < 10) { + currentPingTime = (pingTime + currentPingTime) / 2; + + if (messageId != 0) { + long timeMessage = getTimeFromMsgId(messageId); + long currentTime = System.currentTimeMillis(); + timeDifference = (int)((timeMessage - currentTime) / 1000 - currentPingTime / 2.0); + } + } + itemsToDelete.add(pid); + } else if (pid < pingId) { + itemsToDelete.add(pid); + } + } + for (Long pid : itemsToDelete) { + pingIdToDate.remove(pid); + } + } else if (message instanceof TLRPC.TL_futuresalts) { + TLRPC.TL_futuresalts futureSalts = (TLRPC.TL_futuresalts)message; + long requestMid = futureSalts.req_msg_id; + for (RPCRequest request : runningRequests) { + if (request.respondsToMessageId(requestMid)) { + if (request.completionBlock != null) { + request.completionBlock.run(futureSalts, null); + } + + messagesConfirmed(requestMid); + rpcCompleted(requestMid); + + break; + } + } + } else if (message instanceof TLRPC.DestroySessionRes) { + TLRPC.DestroySessionRes res = (TLRPC.DestroySessionRes)message; + ArrayList lst = new ArrayList(); + lst.addAll(sessionsToDestroy); + destroyingSessions.remove(res.session_id); + for (long session : lst) { + if (session == res.session_id) { + sessionsToDestroy.remove(session); + FileLog.d("tmessages", String.format("Destroyed session %d (%s)", res.session_id, res instanceof TLRPC.TL_destroy_session_ok ? "ok" : "not found")); + break; + } + } + } else if (message instanceof TLRPC.TL_rpc_result) { + TLRPC.TL_rpc_result resultContainer = (TLRPC.TL_rpc_result)message; + long resultMid = resultContainer.req_msg_id; + + boolean ignoreResult = false; + FileLog.d("tmessages", "object in rpc_result is " + resultContainer.result); + if (resultContainer.result instanceof TLRPC.RpcError) { + String errorMessage = ((TLRPC.RpcError)resultContainer.result).error_message; + FileLog.e("tmessages", String.format("***** RPC error %d: %s", ((TLRPC.RpcError)resultContainer.result).error_code, errorMessage)); + + int migrateToDatacenterId = DEFAULT_DATACENTER_ID; + + if (((TLRPC.RpcError)resultContainer.result).error_code == 303) { + ArrayList migrateErrors = new ArrayList(); + migrateErrors.add("NETWORK_MIGRATE_"); + migrateErrors.add("PHONE_MIGRATE_"); + migrateErrors.add("USER_MIGRATE_"); + for (String possibleError : migrateErrors) { + if (errorMessage.contains(possibleError)) { + String errorMsg = errorMessage.replace(possibleError, ""); + + Pattern pattern = Pattern.compile("[0-9]+"); + Matcher matcher = pattern.matcher(errorMsg); + if (matcher.find()) { + errorMsg = matcher.group(0); + } + + Integer val; + try { + val = Integer.parseInt(errorMsg); + } catch (Exception e) { + val = null; + } + + if (val != null) { + migrateToDatacenterId = val; + } else { + migrateToDatacenterId = DEFAULT_DATACENTER_ID; + } + } + } + } + + if (migrateToDatacenterId != DEFAULT_DATACENTER_ID) { + ignoreResult = true; + moveToDatacenter(migrateToDatacenterId); + } + } + + int retryRequestsFromDatacenter = -1; + int retryRequestsClass = 0; + + if (!ignoreResult) { + boolean found = false; + + for (RPCRequest request : runningRequests) { + if (request.respondsToMessageId(resultMid)) { + found = true; + + boolean discardResponse = false; + boolean isError = false; + if (request.completionBlock != null) { + TLRPC.TL_error implicitError = null; + if (resultContainer.result instanceof TLRPC.TL_gzip_packed) { + TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed)resultContainer.result; + TLObject uncomressed = Utilities.decompress(packet.packed_data, request.rawRequest); + if (uncomressed == null) { + System.gc(); + uncomressed = Utilities.decompress(packet.packed_data, request.rawRequest); + } + if (uncomressed == null) { + throw new RuntimeException("failed to decomress responce for " + request.rawRequest); + } + resultContainer.result = uncomressed; + } + if (resultContainer.result instanceof TLRPC.RpcError) { + String errorMessage = ((TLRPC.RpcError) resultContainer.result).error_message; + FileLog.e("tmessages", String.format("***** RPC error %d: %s", ((TLRPC.RpcError) resultContainer.result).error_code, errorMessage)); + + int errorCode = ((TLRPC.RpcError) resultContainer.result).error_code; + + if (errorCode == 500 || errorCode < 0) { + if ((request.flags & RPCRequest.RPCRequestClassFailOnServerErrors) != 0) { + if (request.serverFailureCount < 1) { + discardResponse = true; + request.runningMinStartTime = request.runningStartTime + 1; + request.serverFailureCount++; + } + } else { + discardResponse = true; + request.runningMinStartTime = request.runningStartTime + 1; + request.confirmed = false; + } + } else if (errorCode == 420) { + if ((request.flags & RPCRequest.RPCRequestClassFailOnServerErrors) == 0) { + double waitTime = 2.0; + + if (errorMessage.contains("FLOOD_WAIT_")) { + String errorMsg = errorMessage.replace("FLOOD_WAIT_", ""); + + Pattern pattern = Pattern.compile("[0-9]+"); + Matcher matcher = pattern.matcher(errorMsg); + if (matcher.find()) { + errorMsg = matcher.group(0); + } + + Integer val; + try { + val = Integer.parseInt(errorMsg); + } catch (Exception e) { + val = null; + } + if (val != null) { + waitTime = val; + } + } + + waitTime = Math.min(30, waitTime); + + discardResponse = true; + request.runningMinStartTime = (int)(System.currentTimeMillis() / 1000 + waitTime); + request.confirmed = false; + } + } + + implicitError = new TLRPC.TL_error(); + implicitError.code = ((TLRPC.RpcError)resultContainer.result).error_code; + implicitError.text = ((TLRPC.RpcError)resultContainer.result).error_message; + } else if (!(resultContainer.result instanceof TLRPC.TL_error)) { + if (request.rawRequest == null || !request.rawRequest.responseClass().isAssignableFrom(resultContainer.result.getClass())) { + if (request.rawRequest == null) { + FileLog.e("tmessages", "rawRequest is null"); + } else { + FileLog.e("tmessages", "***** RPC error: invalid response class " + resultContainer.result + " (" + request.rawRequest.responseClass() + " expected)"); + } + implicitError = new TLRPC.TL_error(); + implicitError.code = -1000; + } + } + + if (!discardResponse) { + if (implicitError != null || resultContainer.result instanceof TLRPC.TL_error) { + isError = true; + request.completionBlock.run(null, implicitError != null ? implicitError : (TLRPC.TL_error) resultContainer.result); + } else { + request.completionBlock.run(resultContainer.result, null); + } + } + + if (implicitError != null && implicitError.code == 401) { + isError = true; + if (datacenter.datacenterId == currentDatacenterId || datacenter.datacenterId == movingToDatacenterId) { + if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) { + if (UserConfig.clientActivated) { + UserConfig.clearConfig(); + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + NotificationCenter.Instance.postNotificationName(1234); + } + }); + } + } + } else { + datacenter.authorized = false; + saveSession(); + discardResponse = true; + if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0 || (request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) { + retryRequestsFromDatacenter = datacenter.datacenterId; + retryRequestsClass = request.flags; + } + } + } + } + + if (!discardResponse) { + if (request.initRequest && !isError) { + if (datacenter.lastInitVersion != currentAppVersion) { + datacenter.lastInitVersion = currentAppVersion; + saveSession(); + FileLog.e("tmessages", "init connection completed"); + } else { + FileLog.e("tmessages", "rpc is init, but init connection already completed"); + } + } + rpcCompleted(resultMid); + } else { + request.runningMessageId = 0; + request.runningMessageSeqNo = 0; + request.transportChannelToken = 0; + } + break; + } + } + + if (!found) { + FileLog.d("tmessages", "Response received, but request wasn't found."); + rpcCompleted(resultMid); + } + + messagesConfirmed(resultMid); + } + + if (retryRequestsFromDatacenter >= 0) { + processRequestQueue(retryRequestsClass, retryRequestsFromDatacenter); + } else { + processRequestQueue(0, 0); + } + } else if (message instanceof TLRPC.TL_msgs_ack) { + + } else if (message instanceof TLRPC.TL_ping) { + + } else if (message instanceof TLRPC.TL_bad_msg_notification) { + TLRPC.TL_bad_msg_notification badMsgNotification = (TLRPC.TL_bad_msg_notification)message; + + FileLog.e("tmessages", String.format("***** Bad message: %d", badMsgNotification.error_code)); + + if (badMsgNotification.error_code == 16 || badMsgNotification.error_code == 17 || badMsgNotification.error_code == 19 || badMsgNotification.error_code == 32 || badMsgNotification.error_code == 33 || badMsgNotification.error_code == 64) { + long realId = messageId != 0 ? messageId : containerMessageId; + if (realId == 0) { + realId = innerMsgId; + } + + if (realId != 0) { + long time = getTimeFromMsgId(messageId); + long currentTime = System.currentTimeMillis(); + timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); + } + + recreateSession(datacenter.authSessionId, datacenter); + saveSession(); + + lastOutgoingMessageId = 0; + clearRequestsForRequestClass(connection.transportRequestClass, datacenter); + } + } else if (message instanceof TLRPC.TL_bad_server_salt) { + if (messageId != 0) { + long time = getTimeFromMsgId(messageId); + long currentTime = System.currentTimeMillis(); + timeDifference = (int)((time - currentTime) / 1000 - currentPingTime / 2.0); + + lastOutgoingMessageId = Math.max(messageId, lastOutgoingMessageId); + } + + datacenter.clearServerSalts(); + + ServerSalt serverSaltDesc = new ServerSalt(); + serverSaltDesc.validSince = getCurrentTime(); + serverSaltDesc.validUntil = getCurrentTime() + 30 * 60; + serverSaltDesc.value = messageSalt; + + datacenter.addServerSalt(serverSaltDesc); + saveSession(); + + refillSaltSet(datacenter); + if (datacenter.authKey != null) { + processRequestQueue(RPCRequest.RPCRequestClassTransportMask, datacenter.datacenterId); + } + } else if (message instanceof TLRPC.MsgDetailedInfo) { + TLRPC.MsgDetailedInfo detailedInfo = (TLRPC.MsgDetailedInfo)message; + + boolean requestResend = false; + + if (detailedInfo instanceof TLRPC.TL_msg_detailed_info) { + long requestMid = ((TLRPC.TL_msg_detailed_info)detailedInfo).msg_id; + for (RPCRequest request : runningRequests) { + if (request.respondsToMessageId(requestMid)) { + requestResend = true; + break; + } + } + } else { + if (!isMessageIdProcessed(sessionId, messageId)) { + requestResend = true; + } + } + + if (requestResend) { + TLRPC.TL_msg_resend_req resendReq = new TLRPC.TL_msg_resend_req(); + resendReq.msg_ids.add(detailedInfo.answer_msg_id); + + NetworkMessage networkMessage = new NetworkMessage(); + networkMessage.protoMessage = wrapMessage(resendReq, sessionId, false); + + ArrayList arr = new ArrayList(); + arr.add(networkMessage); + sendMessagesToTransport(arr, connection, sessionId, false, true); + } else { + ArrayList set = messagesIdsForConfirmation.get(sessionId); + if (set == null) { + set = new ArrayList(); + messagesIdsForConfirmation.put(sessionId, set); + } + set.add(detailedInfo.answer_msg_id); + } + } else if (message instanceof TLRPC.TL_gzip_packed) { + TLRPC.TL_gzip_packed packet = (TLRPC.TL_gzip_packed)message; + TLObject result = Utilities.decompress(packet.packed_data, getRequestWithMessageId(messageId)); + processMessage(result, messageId, messageSeqNo, messageSalt, connection, sessionId, innerMsgId, containerMessageId); + } else if (message instanceof TLRPC.Updates) { + MessagesController.Instance.processUpdates((TLRPC.Updates)message, false); + } else { + FileLog.e("tmessages", "***** Error: unknown message class " + message); + } + } + + void generatePing() { + for (Datacenter datacenter : datacenters.values()) { + if (datacenter.datacenterId == currentDatacenterId) { + generatePing(datacenter); + } + } + } + + static long nextPingId = 0; + byte[] generatePingData(Datacenter datacenter, boolean recordTime) { + long sessionId = datacenter.authSessionId; + if (sessionId == 0) { + return null; + } + + TLRPC.TL_ping ping = new TLRPC.TL_ping(); + ping.ping_id = nextPingId++; + + if (recordTime && sessionId == datacenter.authSessionId) { + pingIdToDate.put(ping.ping_id, (int)(System.currentTimeMillis() / 1000)); + } + + NetworkMessage networkMessage = new NetworkMessage(); + networkMessage.protoMessage = wrapMessage(ping, sessionId, false); + + ArrayList arr = new ArrayList(); + arr.add(networkMessage); + return createConnectionData(arr, sessionId, null, datacenter.connection); + } + + void generatePing(Datacenter datacenter) { + if (datacenter.connection == null || datacenter.connection.channelToken == 0) { + return; + } + + byte[] transportData = generatePingData(datacenter, true); + if (transportData != null) { + datacenter.connection.sendData(transportData, false, true); + } + } + + public long needsToDecodeMessageIdFromPartialData(TcpConnection connection, byte[] data) { + if (data == null) { + return -1; + } + + Datacenter datacenter = datacenters.get(connection.getDatacenterId()); + SerializedData is = new SerializedData(data); + + byte[] keyId = is.readData(8); + SerializedData keyIdData = new SerializedData(keyId); + if (keyIdData.readInt64() == 0) { + return -1; + } else { + if (datacenter.authKeyId == null || !Arrays.equals(keyId, datacenter.authKeyId)) { + FileLog.e("tmessages", "Error: invalid auth key id " + connection); + return -1; + } + + byte[] messageKey = is.readData(16); + MessageKeyData keyData = Utilities.generateMessageKeyData(datacenter.authKey, messageKey, true); + + byte[] messageData = is.readData(data.length - 24); + messageData = Utilities.aesIgeEncryption(messageData, keyData.aesKey, keyData.aesIv, false, false); + + if (messageData == null) { + return -1; + } + + SerializedData messageIs = new SerializedData(messageData); + long messageServerSalt = messageIs.readInt64(); + long messageSessionId = messageIs.readInt64(); + + if (messageSessionId != datacenter.authSessionId && messageSessionId != datacenter.authDownloadSessionId && messageSessionId != datacenter.authUploadSessionId) { + FileLog.e("tmessages", String.format("***** Error: invalid message session ID (%d instead of %d)", messageSessionId, datacenter.authSessionId)); + finishUpdatingState(connection); + return -1; + } + + long messageId = messageIs.readInt64(); + int messageSeqNo = messageIs.readInt32(); + int messageLength = messageIs.readInt32(); + + boolean[] stop = new boolean[1]; + long[] reqMsgId = new long[1]; + stop[0] = false; + reqMsgId[0] = 0; + + while (!stop[0] && reqMsgId[0] == 0) { + int signature = messageIs.readInt32(stop); + if (stop[0]) { + break; + } + findReqMsgId(messageIs, signature, reqMsgId, stop); + } + + return reqMsgId[0]; + } + } + + private void findReqMsgId(SerializedData is, int signature, long[] reqMsgId, boolean[] failed) { + if (signature == 0x73f1f8dc) { + if (is.length() < 4) { + failed[0] = true; + return; + } + int count = is.readInt32(failed); + if (failed[0]) { + return; + } + + for (int i = 0; i < count; i++) { + is.readInt64(failed); + if (failed[0]) { + return; + } + is.readInt32(failed); + if (failed[0]) { + return; + } + is.readInt32(failed); + if (failed[0]) { + return; + } + + int innerSignature = is.readInt32(failed); + if (failed[0]) { + return; + } + + findReqMsgId(is, innerSignature, reqMsgId, failed); + if (failed[0] || reqMsgId[0] != 0) { + return; + } + } + } else if (signature == 0xf35c6d01) { + long value = is.readInt64(failed); + if (failed[0]) { + return; + } + reqMsgId[0] = value; + } else if (signature == 0x62d6b459) { + is.readInt32(failed); + if (failed[0]) { + return; + } + + int count = is.readInt32(failed); + if (failed[0]) { + return; + } + + for (int i = 0; i < count; i++) { + is.readInt32(failed); + if (failed[0]) { + return; + } + } + } else if (signature == 0x347773c5) { + is.readInt64(failed); + if (failed[0]) { + return; + } + is.readInt64(failed); + } + } + + //================================================================================ + // TCPConnection delegate + //================================================================================ + + @Override + public void tcpConnectionProgressChanged(TcpConnection connection, long messageId, int currentSize, int length) { + for (RPCRequest request : runningRequests) { + if (request.respondsToMessageId(messageId)) { + if (request.progressBlock != null) { + request.progressBlock.progress(length, currentSize); + } + break; + } + } + } + + @Override + public void tcpConnectionClosed(TcpConnection connection) { + if (connection.getDatacenterId() == currentDatacenterId && (connection.transportRequestClass & RPCRequest.RPCRequestClassGeneric) != 0) { + if (isNetworkOnline()) { + connectionState = 2; + } else { + connectionState = 1; + } + if (DEBUG_VERSION) { + try { + ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo[] networkInfos = cm.getAllNetworkInfo(); + for (NetworkInfo info : networkInfos) { + FileLog.e("tmessages", "Network: " + info.getTypeName() + " status: " + info.getState() + " info: " + info.getExtraInfo() + " object: " + info.getDetailedState() + " other: " + info); + } + if (networkInfos.length == 0) { + FileLog.e("tmessages", "no network available"); + } + } catch (Exception e) { + FileLog.e("tmessages", "NETWORK STATE GET ERROR"); + } + } + final int stateCopy = connectionState; + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + NotificationCenter.Instance.postNotificationName(703, stateCopy); + } + }); + } + } + + @Override + public void tcpConnectionConnected(TcpConnection connection) { + Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); + if (datacenter.authKey != null) { + processRequestQueue(connection.transportRequestClass, connection.getDatacenterId()); + } + } + + @Override + public void tcpConnectionQuiackAckReceived(TcpConnection connection, int ack) { + ArrayList arr = quickAckIdToRequestIds.get(ack); + if (arr != null) { + for (RPCRequest request : runningRequests) { + if (arr.contains(request.token)) { + if (request.quickAckBlock != null) { + request.quickAckBlock.quickAck(); + } + } + } + quickAckIdToRequestIds.remove(ack); + } + } + + private void finishUpdatingState(TcpConnection connection) { + if (connection.getDatacenterId() == currentDatacenterId && (connection.transportRequestClass & RPCRequest.RPCRequestClassGeneric) != 0) { + if (ConnectionsManager.Instance.connectionState == 3 && !MessagesController.Instance.gettingDifference && !MessagesController.Instance.gettingDifferenceAgain) { + ConnectionsManager.Instance.connectionState = 0; + final int stateCopy = ConnectionsManager.Instance.connectionState; + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + NotificationCenter.Instance.postNotificationName(703, stateCopy); + } + }); + } + } + } + + @Override + public void tcpConnectionReceivedData(TcpConnection connection, byte[] data) { + if (connection.getDatacenterId() == currentDatacenterId && (connection.transportRequestClass & RPCRequest.RPCRequestClassGeneric) != 0) { + if (connectionState == 1 || connectionState == 2) { + connectionState = 3; + final int stateCopy = connectionState; + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + NotificationCenter.Instance.postNotificationName(703, stateCopy); + } + }); + } + } + Datacenter datacenter = datacenterWithId(connection.getDatacenterId()); + + SerializedData is = new SerializedData(data); + + byte[] keyId = is.readData(8); + SerializedData keyIdData = new SerializedData(keyId); + if (keyIdData.readInt64() == 0) { + long messageId = is.readInt64(); + if (isMessageIdProcessed(0, messageId)) { + finishUpdatingState(connection); + return; + } + + int messageLength = is.readInt32(); + + int constructor = is.readInt32(); + + TLObject object = TLClassStore.Instance().TLdeserialize(is, constructor, getRequestWithMessageId(messageId)); + + processMessage(object, messageId, 0, 0, connection, 0, 0, 0); + + if (object != null) { + addProcessedMessageId(0, messageId); + } + } else { + if (datacenter.authKeyId == null || !Arrays.equals(keyId, datacenter.authKeyId)) { + FileLog.e("tmessages", "Error: invalid auth key id " + connection); + return; + } + + byte[] messageKey = is.readData(16); + MessageKeyData keyData = Utilities.generateMessageKeyData(datacenter.authKey, messageKey, true); + + byte[] messageData = is.readData(data.length - 24); + + messageData = Utilities.aesIgeEncryption(messageData, keyData.aesKey, keyData.aesIv, false, false); + if (messageData == null) { + FileLog.e("tmessages", "Error: can't decrypt message data " + connection); + return; + } + + SerializedData messageIs = new SerializedData(messageData); + long messageServerSalt = messageIs.readInt64(); + long messageSessionId = messageIs.readInt64(); + + if (messageSessionId != datacenter.authSessionId && messageSessionId != datacenter.authDownloadSessionId && messageSessionId != datacenter.authUploadSessionId) { + FileLog.e("tmessages", String.format("***** Error: invalid message session ID (%d instead of %d)", messageSessionId, datacenter.authSessionId)); + finishUpdatingState(connection); + return; + } + + boolean doNotProcess = false; + + long messageId = messageIs.readInt64(); + int messageSeqNo = messageIs.readInt32(); + int messageLength = messageIs.readInt32(); + + if (isMessageIdProcessed(messageSessionId, messageId)) { + doNotProcess = true; + } + + if (messageSeqNo % 2 != 0) { + ArrayList set = messagesIdsForConfirmation.get(messageSessionId); + if (set == null) { + set = new ArrayList(); + messagesIdsForConfirmation.put(messageSessionId, set); + } + set.add(messageId); + } + + byte[] realMessageKeyFull = Utilities.computeSHA1(messageData, 0, Math.min(messageLength + 32, messageData.length)); + if (realMessageKeyFull == null) { + return; + } + byte[] realMessageKey = new byte[16]; + System.arraycopy(realMessageKeyFull, realMessageKeyFull.length - 16, realMessageKey, 0, 16); + + if (!Arrays.equals(messageKey, realMessageKey)) { + FileLog.e("tmessages", "***** Error: invalid message key"); + return; + } + + if (!doNotProcess) { + int constructor = messageIs.readInt32(); + TLObject message = TLClassStore.Instance().TLdeserialize(messageIs, constructor, getRequestWithMessageId(messageId)); + + if (message == null) { + FileLog.e("tmessages", "***** Error parsing message: " + constructor); + } else { + processMessage(message, messageId, messageSeqNo, messageServerSalt, connection, messageSessionId, 0, 0); + + addProcessedMessageId(messageSessionId, messageId); + } + } else { + proceedToSendingMessages(null, messageSessionId, connection, false, false); + } + finishUpdatingState(connection); + } + } + + public TLObject getRequestWithMessageId(long msgId) { + for (RPCRequest request : runningRequests) { + if (msgId == request.runningMessageId) { + return request.rawRequest; + } + } + return null; + } + + //================================================================================ + // Move to datacenter manage + //================================================================================ + + void moveToDatacenter(final int datacenterId) { + if (movingToDatacenterId == datacenterId) { + return; + } + movingToDatacenterId = datacenterId; + + Datacenter currentDatacenter = datacenterWithId(currentDatacenterId); + clearRequestsForRequestClass(RPCRequest.RPCRequestClassGeneric, currentDatacenter); + clearRequestsForRequestClass(RPCRequest.RPCRequestClassDownloadMedia, currentDatacenter); + clearRequestsForRequestClass(RPCRequest.RPCRequestClassUploadMedia, currentDatacenter); + + if (UserConfig.clientUserId != 0) { + TLRPC.TL_auth_exportAuthorization exportAuthorization = new TLRPC.TL_auth_exportAuthorization(); + exportAuthorization.dc_id = datacenterId; + + performRpc(exportAuthorization, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + if (error == null) { + movingAuthorization = (TLRPC.TL_auth_exportedAuthorization)response; + authorizeOnMovingDatacenter(); + } else { + Utilities.globalQueue.postRunnable(new Runnable() { + @Override + public void run() { + moveToDatacenter(datacenterId); + } + }, 1000, false); + } + } + }, null, true, RPCRequest.RPCRequestClassGeneric, currentDatacenterId); + } else { + authorizeOnMovingDatacenter(); + } + } + + void authorizeOnMovingDatacenter() { + Datacenter datacenter = datacenterWithId(movingToDatacenterId); + if (datacenter == null) { + if (!updatingDcSettings) { + updateDcSettings(); + } + return; + } + + recreateSession(datacenter.authSessionId, datacenter); + + if (datacenter.authKey == null) { + datacenter.clearServerSalts(); + HandshakeAction actor = new HandshakeAction(datacenter); + actor.delegate = this; + dequeueActor(actor, true); + } + + if (movingAuthorization != null) { + TLRPC.TL_auth_importAuthorization importAuthorization = new TLRPC.TL_auth_importAuthorization(); + importAuthorization.id = UserConfig.clientUserId; + importAuthorization.bytes = movingAuthorization.bytes; + performRpc(importAuthorization, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + movingAuthorization = null; + if (error == null) { + authorizedOnMovingDatacenter(); + } else { + moveToDatacenter(movingToDatacenterId); + } + } + }, null, true, RPCRequest.RPCRequestClassGeneric, datacenter.datacenterId); + } else { + authorizedOnMovingDatacenter(); + } + } + + void authorizedOnMovingDatacenter() { + Datacenter datacenter = datacenterWithId(currentDatacenterId); + if (datacenter != null && datacenter.connection != null) { + datacenter.connection.suspendConnection(true); + } + movingAuthorization = null; + currentDatacenterId = movingToDatacenterId; + movingToDatacenterId = DEFAULT_DATACENTER_ID; + saveSession(); + processRequestQueue(0, 0); + } + + //================================================================================ + // Actors manage + //================================================================================ + + public void dequeueActor(final Action actor, final boolean execute) { + if (actionQueue.size() == 0 || execute) { + actor.execute(null); + } + actionQueue.add(actor); + } + + public void cancelActor(final Action actor) { + if (actor != null) { + actionQueue.remove(actor); + } + } + + @Override + public void ActionDidFinishExecution(final Action action, HashMap params) { + if (action instanceof HandshakeAction) { + HandshakeAction eactor = (HandshakeAction)action; + eactor.datacenter.connection.delegate = this; + saveSession(); + + if (eactor.datacenter.datacenterId == currentDatacenterId || eactor.datacenter.datacenterId == movingToDatacenterId) { + timeDifference = (Integer)params.get("timeDifference"); + + recreateSession(eactor.datacenter.authSessionId, eactor.datacenter); + } + processRequestQueue(RPCRequest.RPCRequestClassTransportMask, eactor.datacenter.datacenterId); + } else if (action instanceof ExportAuthorizationAction) { + ExportAuthorizationAction eactor = (ExportAuthorizationAction)action; + + Datacenter datacenter = eactor.datacenter; + datacenter.authorized = true; + saveSession(); + processRequestQueue(RPCRequest.RPCRequestClassTransportMask, datacenter.datacenterId); + } + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + actionQueue.remove(action); + action.delegate = null; + } + }); + } + + @Override + public void ActionDidFailExecution(final Action action) { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + actionQueue.remove(action); + action.delegate = null; + } + }); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java index cd6a66eca..85b649bf8 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java @@ -183,105 +183,110 @@ public class ContactsController { private HashMap readContactsFromPhoneBook() { HashMap contactsMap = new HashMap(); - ContentResolver cr = ApplicationLoader.applicationContext.getContentResolver(); - String ids = ""; - Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projectioPhones, null, null, null); - if (pCur != null) { - if (pCur.getCount() > 0) { + try { + ContentResolver cr = ApplicationLoader.applicationContext.getContentResolver(); + String ids = ""; + Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projectioPhones, null, null, null); + if (pCur != null) { + if (pCur.getCount() > 0) { + while (pCur.moveToNext()) { + String number = pCur.getString(1); + if (number == null || number.length() == 0) { + continue; + } + number = PhoneFormat.stripExceptNumbers(number); + if (number.length() == 0) { + continue; + } + Integer id = pCur.getInt(0); + if (ids.length() != 0) { + ids += ","; + } + ids += id; + + int type = pCur.getInt(2); + Contact contact = contactsMap.get(id); + if (contact == null) { + contact = new Contact(); + contact.first_name = ""; + contact.last_name = ""; + contact.id = id; + contactsMap.put(id, contact); + } + + boolean addNumber = true; + if (number.length() > 8) { + String shortNumber = number.substring(number.length() - 8); + if (contact.shortPhones.contains(shortNumber)) { + addNumber = false; + } else { + contact.shortPhones.add(shortNumber); + } + } else { + if (contact.shortPhones.contains(number)) { + addNumber = false; + } else { + contact.shortPhones.add(number); + } + } + if (addNumber) { + contact.phones.add(number); + contact.phoneDeleted.add(0); + } + + if (type == ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM) { + contact.phoneTypes.add(pCur.getString(3)); + } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_HOME) { + contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneHome)); + } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE) { + contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneMobile)); + } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_WORK) { + contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneWork)); + } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_MAIN) { + contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneMain)); + } else { + contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneOther)); + } + } + } + pCur.close(); + } + + pCur = cr.query(ContactsContract.Data.CONTENT_URI, projectionNames, ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " IN (" + ids + ") AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'", null, null); + if (pCur != null && pCur.getCount() > 0) { while (pCur.moveToNext()) { - String number = pCur.getString(1); - if (number == null || number.length() == 0) { - continue; - } - number = PhoneFormat.stripExceptNumbers(number); - if (number.length() == 0) { - continue; - } - Integer id = pCur.getInt(0); - if (ids.length() != 0) { - ids += ","; - } - ids += id; - - int type = pCur.getInt(2); + int id = pCur.getInt(0); + String fname = pCur.getString(1); + String sname = pCur.getString(2); + String sname2 = pCur.getString(3); + String mname = pCur.getString(4); Contact contact = contactsMap.get(id); - if (contact == null) { - contact = new Contact(); - contact.first_name = ""; - contact.last_name = ""; - contact.id = id; - contactsMap.put(id, contact); - } - - boolean addNumber = true; - if (number.length() > 8) { - String shortNumber = number.substring(number.length() - 8); - if (contact.shortPhones.contains(shortNumber)) { - addNumber = false; - } else { - contact.shortPhones.add(shortNumber); + if (contact != null) { + contact.first_name = fname; + contact.last_name = sname; + if (contact.first_name == null) { + contact.first_name = ""; } - } else { - if (contact.shortPhones.contains(number)) { - addNumber = false; - } else { - contact.shortPhones.add(number); + if (mname != null && mname.length() != 0) { + if (contact.first_name.length() != 0) { + contact.first_name += " " + mname; + } else { + contact.first_name = mname; + } + } + if (contact.last_name == null) { + contact.last_name = ""; + } + if (contact.last_name.length() == 0 && contact.first_name.length() == 0 && sname2 != null && sname2.length() != 0) { + contact.first_name = sname2; } - } - if (addNumber) { - contact.phones.add(number); - contact.phoneDeleted.add(0); - } - - if (type == ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM) { - contact.phoneTypes.add(pCur.getString(3)); - } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_HOME) { - contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneHome)); - } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE) { - contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneMobile)); - } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_WORK) { - contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneWork)); - } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_MAIN) { - contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneMain)); - } else { - contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneOther)); } } + pCur.close(); } - pCur.close(); - } - - pCur = cr.query(ContactsContract.Data.CONTENT_URI, projectionNames, ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " IN (" + ids + ") AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'", null, null); - if (pCur != null && pCur.getCount() > 0) { - while (pCur.moveToNext()) { - int id = pCur.getInt(0); - String fname = pCur.getString(1); - String sname = pCur.getString(2); - String sname2 = pCur.getString(3); - String mname = pCur.getString(4); - Contact contact = contactsMap.get(id); - if (contact != null) { - contact.first_name = fname; - contact.last_name = sname; - if (contact.first_name == null) { - contact.first_name = ""; - } - if (mname != null && mname.length() != 0) { - if (contact.first_name.length() != 0) { - contact.first_name += " " + mname; - } else { - contact.first_name = mname; - } - } - if (contact.last_name == null) { - contact.last_name = ""; - } - if (contact.last_name.length() == 0 && contact.first_name.length() == 0 && sname2 != null && sname2.length() != 0) { - contact.first_name = sname2; - } - } - } - pCur.close(); + } catch (Exception e) { + FileLog.e("tmessages", e); + contactsMap.clear(); } return contactsMap; } @@ -303,18 +308,42 @@ public class ContactsController { return ret; } - public void performSyncPhoneBook(final HashMap contactHashMap, final boolean request, final boolean first) { + public void performSyncPhoneBook(final HashMap contactHashMap, final boolean requ, final boolean first) { Utilities.globalQueue.postRunnable(new Runnable() { @Override public void run() { + boolean request = requ; + if (request && first) { + if (UserConfig.contactsHash != null && UserConfig.contactsHash.length() != 0) { + UserConfig.contactsHash = ""; + UserConfig.saveConfig(false); + request = false; + } + } + FileLog.e("tmessages", "start read contacts from phone"); final HashMap contactsMap = readContactsFromPhoneBook(); final HashMap contactsBookShort = new HashMap(); int oldCount = contactHashMap.size(); + if (ConnectionsManager.disableContactsImport) { + if (requ && first) { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + contactsBookSPhones = contactsBookShort; + contactsBook = contactsMap; + contactsSyncInProgress = false; + contactsBookLoaded = true; + loadContacts(true); + } + }); + } + return; + } + ArrayList toImport = new ArrayList(); if (!contactHashMap.isEmpty()) { - HashMap contactsMapCopy = new HashMap(contactsMap); for (HashMap.Entry pair : contactsMap.entrySet()) { Integer id = pair.getKey(); Contact value = pair.getValue(); @@ -413,7 +442,7 @@ public class ContactsController { } }); - FileLog.e("tmessages", "done procrssing contacts"); + FileLog.e("tmessages", "done processing contacts"); if (request) { if (!toImport.isEmpty()) { @@ -426,7 +455,9 @@ public class ContactsController { public void run(TLObject response, TLRPC.TL_error error) { if (error == null) { FileLog.e("tmessages", "contacts imported"); - MessagesStorage.Instance.putCachedPhoneBook(contactsMap); + if (!contactsMap.isEmpty()) { + MessagesStorage.Instance.putCachedPhoneBook(contactsMap); + } TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts)response; MessagesStorage.Instance.putUsersAndChats(res.users, null, true, true); ArrayList cArr = new ArrayList(); @@ -462,7 +493,17 @@ public class ContactsController { }); } } else { - MessagesStorage.Instance.putCachedPhoneBook(contactsMap); + if (!contactsMap.isEmpty()) { + MessagesStorage.Instance.putCachedPhoneBook(contactsMap); + } + if (first) { + Utilities.stageQueue.postRunnable(new Runnable() { + @Override + public void run() { + loadContacts(true); + } + }); + } } } }); @@ -890,6 +931,9 @@ public class ContactsController { @Override public void run() { try { + if (ConnectionsManager.disableContactsImport) { + return; + } Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, currentAccount.name).appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, currentAccount.type).build(); Cursor c1 = ApplicationLoader.applicationContext.getContentResolver().query(rawContactUri, new String[]{BaseColumns._ID, ContactsContract.RawContacts.SYNC2}, null, null, null); HashMap bookContacts = new HashMap(); @@ -922,20 +966,8 @@ public class ContactsController { TLRPC.TL_contact contact = new TLRPC.TL_contact(); contact.user_id = uid; newC.add(contact); - if (!delayedContactsUpdate.isEmpty()) { - int idx = delayedContactsUpdate.indexOf(-uid); - if (idx != -1) { - delayedContactsUpdate.remove(idx); - } - } } else if (uid < 0) { contactsTD.add(-uid); - if (!delayedContactsUpdate.isEmpty()) { - int idx = delayedContactsUpdate.indexOf(-uid); - if (idx != -1) { - delayedContactsUpdate.remove(idx); - } - } } } } @@ -952,8 +984,10 @@ public class ContactsController { } if (user == null) { user = MessagesController.Instance.users.get(newContact.user_id); + } else { + MessagesController.Instance.users.putIfAbsent(user.id, user); } - if (user == null || user.phone == null && user.phone.length() == 0) { + if (user == null || user.phone == null || user.phone.length() == 0) { reloadContacts = true; continue; } @@ -989,6 +1023,8 @@ public class ContactsController { } if (user == null) { user = MessagesController.Instance.users.get(uid); + } else { + MessagesController.Instance.users.putIfAbsent(user.id, user); } if (user == null) { reloadContacts = true; @@ -1095,7 +1131,7 @@ public class ContactsController { } public long addContactToPhoneBook(TLRPC.User user) { - if (currentAccount == null || user == null || user.phone == null || user.phone.length() == 0) { + if (currentAccount == null || user == null || user.phone == null || user.phone.length() == 0 || ConnectionsManager.disableContactsImport) { return -1; } long res = -1; @@ -1146,6 +1182,9 @@ public class ContactsController { } private void deleteContactFromPhoneBook(int uid) { + if (ConnectionsManager.disableContactsImport) { + return; + } ContentResolver contentResolver = ApplicationLoader.applicationContext.getContentResolver(); synchronized (observerLock) { ignoreChanges = true; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java index d4d6ed09e..00d974daa 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java @@ -42,14 +42,10 @@ public class DispatchQueue extends Thread { } public void postRunnable(Runnable runnable) { - postRunnable(runnable, 0, false); + postRunnable(runnable, 0); } - public void postRunnable(Runnable runnable, boolean inFront) { - postRunnable(runnable, 0, true); - } - - public void postRunnable(Runnable runnable, int delay, boolean inFront) { + public void postRunnable(Runnable runnable, int delay) { if (handler == null) { try { synchronized (handlerSyncObject) { @@ -62,11 +58,7 @@ public class DispatchQueue extends Thread { if (handler != null) { if (delay <= 0) { - if (inFront) { - handler.postAtFrontOfQueue(runnable); - } else { - handler.post(runnable); - } + handler.post(runnable); } else { handler.postDelayed(runnable, delay); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java b/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java index 9fe4965f3..355dd8512 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java @@ -53,7 +53,7 @@ public class ExportAuthorizationAction extends Action { public void run() { beginExport(); } - }, retryCount * 1500, false); + }, retryCount * 1500); } } } @@ -84,7 +84,7 @@ public class ExportAuthorizationAction extends Action { public void run() { beginExport(); } - }, retryCount * 1500, false); + }, retryCount * 1500); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java index 93f2b9568..5e79153f0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java @@ -94,6 +94,24 @@ public class FileLoadOperation { ext = ".mp4"; } + public FileLoadOperation(TLRPC.Audio audioLocation) { + if (audioLocation instanceof TLRPC.TL_audio) { + location = new TLRPC.TL_inputAudioFileLocation(); + datacenter_id = audioLocation.dc_id; + location.id = audioLocation.id; + location.access_hash = audioLocation.access_hash; + } else if (audioLocation instanceof TLRPC.TL_audioEncrypted) { + location = new TLRPC.TL_inputEncryptedFileLocation(); + location.id = audioLocation.id; + location.access_hash = audioLocation.access_hash; + datacenter_id = audioLocation.dc_id; + iv = new byte[32]; + System.arraycopy(audioLocation.iv, 0, iv, 0, iv.length); + key = audioLocation.key; + } + ext = ".m4a"; + } + public FileLoadOperation(TLRPC.Document documentLocation) { if (documentLocation instanceof TLRPC.TL_document) { location = new TLRPC.TL_inputDocumentFileLocation(); @@ -220,14 +238,14 @@ public class FileLoadOperation { opts.inSampleSize = (int)scaleFactor; } - opts.inPreferredConfig = Bitmap.Config.RGB_565; + opts.inPreferredConfig = Bitmap.Config.ARGB_8888; opts.inDither = false; image = BitmapFactory.decodeStream(is, null, opts); is.close(); if (image == null) { - if (!dontDelete) { - cacheFileFinal.delete(); - } + //if (!dontDelete) { + // cacheFileFinal.delete(); + //} } else { if (filter != null && image != null) { float bitmapW = image.getWidth(); @@ -252,13 +270,17 @@ public class FileLoadOperation { Utilities.stageQueue.postRunnable(new Runnable() { @Override public void run() { - delegate.didFinishLoadingFile(FileLoadOperation.this); + if (image == null) { + delegate.didFailedLoadingFile(FileLoadOperation.this); + } else { + delegate.didFinishLoadingFile(FileLoadOperation.this); + } } }); } catch (Exception e) { - if (!dontDelete) { - cacheFileFinal.delete(); - } + //if (!dontDelete) { + // cacheFileFinal.delete(); + //} FileLog.e("tmessages", e); } } @@ -414,7 +436,7 @@ public class FileLoadOperation { opts.inSampleSize = (int) scaleFactor; } - opts.inPreferredConfig = Bitmap.Config.RGB_565; + opts.inPreferredConfig = Bitmap.Config.ARGB_8888; opts.inDither = false; try { if (renamed) { @@ -442,10 +464,14 @@ public class FileLoadOperation { } } - if (FileLoader.Instance.runtimeHack != null) { + if (image != null && FileLoader.Instance.runtimeHack != null) { FileLoader.Instance.runtimeHack.trackFree(image.getRowBytes() * image.getHeight()); } - delegate.didFinishLoadingFile(FileLoadOperation.this); + if (image != null) { + delegate.didFinishLoadingFile(FileLoadOperation.this); + } else { + delegate.didFailedLoadingFile(FileLoadOperation.this); + } } catch (Exception e) { FileLog.e("tmessages", e); delegate.didFailedLoadingFile(FileLoadOperation.this); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index 1bd9f831b..1ff6dbd3b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -391,8 +391,8 @@ public class FileLoader { }); } - public void cancelLoadFile(final TLRPC.Video video, final TLRPC.PhotoSize photo, final TLRPC.Document document) { - if (video == null && photo == null && document == null) { + public void cancelLoadFile(final TLRPC.Video video, final TLRPC.PhotoSize photo, final TLRPC.Document document, final TLRPC.Audio audio) { + if (video == null && photo == null && document == null && audio == null) { return; } Utilities.fileUploadQueue.postRunnable(new Runnable() { @@ -405,6 +405,8 @@ public class FileLoader { fileName = MessageObject.getAttachFileName(photo); } else if (document != null) { fileName = MessageObject.getAttachFileName(document); + } else if (audio != null) { + fileName = MessageObject.getAttachFileName(audio); } if (fileName == null) { return; @@ -422,7 +424,7 @@ public class FileLoader { return loadOperationPaths.containsKey(fileName); } - public void loadFile(final TLRPC.Video video, final TLRPC.PhotoSize photo, final TLRPC.Document document) { + public void loadFile(final TLRPC.Video video, final TLRPC.PhotoSize photo, final TLRPC.Document document, final TLRPC.Audio audio) { Utilities.fileUploadQueue.postRunnable(new Runnable() { @Override public void run() { @@ -433,6 +435,8 @@ public class FileLoader { fileName = MessageObject.getAttachFileName(photo); } else if (document != null) { fileName = MessageObject.getAttachFileName(document); + } else if (audio != null) { + fileName = MessageObject.getAttachFileName(audio); } if (fileName == null) { return; @@ -451,6 +455,9 @@ public class FileLoader { } else if (document != null) { operation = new FileLoadOperation(document); operation.totalBytesCount = document.size; + } else if (audio != null) { + operation = new FileLoadOperation(audio); + operation.totalBytesCount = audio.size; } final String arg1 = fileName; @@ -888,7 +895,7 @@ public class FileLoader { } void enqueueImageProcessingOperationWithImage(final Bitmap image, final String filter, final String key, final CacheImage img) { - if (image == null || key == null) { + if (key == null) { return; } @@ -907,7 +914,7 @@ public class FileLoader { @Override public void run() { img.callAndClear(image); - if (memCache.get(key) == null) { + if (image != null && memCache.get(key) == null) { memCache.put(key, image); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java index e045bfc92..9196e5649 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/GcmBroadcastReceiver.java @@ -9,55 +9,20 @@ package org.telegram.messenger; import android.app.Activity; -import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.os.PowerManager; +import android.support.v4.content.WakefulBroadcastReceiver; -import com.google.android.gms.gcm.GoogleCloudMessaging; - -public class GcmBroadcastReceiver extends BroadcastReceiver { +public class GcmBroadcastReceiver extends WakefulBroadcastReceiver { public static final int NOTIFICATION_ID = 1; @Override public void onReceive(final Context context, final Intent intent) { FileLog.d("tmessages", "GCM received intent: " + intent); + ComponentName comp = new ComponentName(context.getPackageName(), GcmService.class.getName()); + startWakefulService(context, (intent.setComponent(comp))); setResultCode(Activity.RESULT_OK); - - if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) { - PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); - final PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "lock"); - wl.acquire(); - - SharedPreferences preferences = context.getSharedPreferences("Notifications", Context.MODE_PRIVATE); - boolean globalEnabled = preferences.getBoolean("EnableAll", true); - if (!globalEnabled) { - FileLog.d("tmessages", "GCM disabled"); - return; - } - - Thread thread = new Thread(new Runnable() { - public void run() { - GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context); - String messageType = gcm.getMessageType(intent); - ConnectionsManager.Instance.resumeNetworkMaybe(); - wl.release(); - } - }); - thread.setPriority(Thread.MAX_PRIORITY); - thread.start(); - } else if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) { - String registration = intent.getStringExtra("registration_id"); - if (intent.getStringExtra("error") != null) { - FileLog.e("tmessages", "Registration failed, should try again later."); - } else if (intent.getStringExtra("unregistered") != null) { - FileLog.e("tmessages", "unregistration done, new messages from the authorized sender will be rejected"); - } else if (registration != null) { - FileLog.e("tmessages", "registration id = " + registration); - } - } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/GcmService.java b/TMessagesProj/src/main/java/org/telegram/messenger/GcmService.java new file mode 100644 index 000000000..c5639dbbf --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/GcmService.java @@ -0,0 +1,42 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013. + */ + +package org.telegram.messenger; + +import android.app.IntentService; +import android.content.Intent; + +public class GcmService extends IntentService { + + public GcmService() { + super("GcmService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) { +// SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Context.MODE_PRIVATE); +// boolean globalEnabled = preferences.getBoolean("EnableAll", true); +// if (!globalEnabled) { +// FileLog.d("tmessages", "GCM disabled"); +// return; +// } + ConnectionsManager.Instance.resumeNetworkMaybe(); + } else if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) { + String registration = intent.getStringExtra("registration_id"); + if (intent.getStringExtra("error") != null) { + FileLog.e("tmessages", "Registration failed, should try again later."); + } else if (intent.getStringExtra("unregistered") != null) { + FileLog.e("tmessages", "unregistration done, new messages from the authorized sender will be rejected"); + } else if (registration != null) { + FileLog.e("tmessages", "registration id = " + registration); + } + } + GcmBroadcastReceiver.completeWakefulIntent(intent); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index c27eecf49..9341b1759 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -53,12 +53,12 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Semaphore; public class MessagesController implements NotificationCenter.NotificationCenterDelegate { - public ConcurrentHashMap chats = new ConcurrentHashMap(100, 1.0f, 1); - public ConcurrentHashMap encryptedChats = new ConcurrentHashMap(10, 1.0f, 1); - public ConcurrentHashMap users = new ConcurrentHashMap(100, 1.0f, 1); + public ConcurrentHashMap chats = new ConcurrentHashMap(100, 1.0f, 2); + public ConcurrentHashMap encryptedChats = new ConcurrentHashMap(10, 1.0f, 2); + public ConcurrentHashMap users = new ConcurrentHashMap(100, 1.0f, 2); public ArrayList dialogs = new ArrayList(); public ArrayList dialogsServerOnly = new ArrayList(); - public ConcurrentHashMap dialogs_dict = new ConcurrentHashMap(100, 1.0f, 1); + public ConcurrentHashMap dialogs_dict = new ConcurrentHashMap(100, 1.0f, 2); public SparseArray dialogMessage = new SparseArray(); public ConcurrentHashMap> printingUsers = new ConcurrentHashMap>(100, 1.0f, 2); public HashMap printingStrings = new HashMap(); @@ -137,6 +137,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter public int type; public TLRPC.FileLocation location; public TLRPC.TL_video videoLocation; + public TLRPC.TL_audio audioLocation; public TLRPC.TL_document documentLocation; public MessageObject obj; public TLRPC.EncryptedChat encryptedChat; @@ -1476,35 +1477,39 @@ public class MessagesController implements NotificationCenter.NotificationCenter } public void sendMessage(TLRPC.User user, long peer) { - sendMessage(null, 0, 0, null, null, null, null, user, null, peer); + sendMessage(null, 0, 0, null, null, null, null, user, null, null, peer); } public void sendMessage(MessageObject message, long peer) { - sendMessage(null, 0, 0, null, null, message, null, null, null, peer); + sendMessage(null, 0, 0, null, null, message, null, null, null, null, peer); } public void sendMessage(TLRPC.TL_document document, long peer) { - sendMessage(null, 0, 0, null, null, null, null, null, document, peer); + sendMessage(null, 0, 0, null, null, null, null, null, document, null, peer); } public void sendMessage(String message, long peer) { - sendMessage(message, 0, 0, null, null, null, null, null, null, peer); + sendMessage(message, 0, 0, null, null, null, null, null, null, null, peer); } public void sendMessage(TLRPC.FileLocation location, long peer) { - sendMessage(null, 0, 0, null, null, null, location, null, null, peer); + sendMessage(null, 0, 0, null, null, null, location, null, null, null, peer); } public void sendMessage(double lat, double lon, long peer) { - sendMessage(null, lat, lon, null, null, null, null, null, null, peer); + sendMessage(null, lat, lon, null, null, null, null, null, null, null, peer); } public void sendMessage(TLRPC.TL_photo photo, long peer) { - sendMessage(null, 0, 0, photo, null, null, null, null, null, peer); + sendMessage(null, 0, 0, photo, null, null, null, null, null, null, peer); } public void sendMessage(TLRPC.TL_video video, long peer) { - sendMessage(null, 0, 0, null, video, null, null, null, null, peer); + sendMessage(null, 0, 0, null, video, null, null, null, null, null, peer); + } + + public void sendMessage(TLRPC.TL_audio audio, long peer) { + sendMessage(null, 0, 0, null, null, null, null, null, null, audio, peer); } public void sendTTLMessage(TLRPC.EncryptedChat encryptedChat) { @@ -1548,7 +1553,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter performSendEncryptedRequest(reqSend, newMsgObj, encryptedChat, null); } - private void sendMessage(String message, double lat, double lon, TLRPC.TL_photo photo, TLRPC.TL_video video, MessageObject msgObj, TLRPC.FileLocation location, TLRPC.User user, TLRPC.TL_document document, long peer) { + private void sendMessage(String message, double lat, double lon, TLRPC.TL_photo photo, TLRPC.TL_video video, MessageObject msgObj, TLRPC.FileLocation location, TLRPC.User user, TLRPC.TL_document document, TLRPC.TL_audio audio, long peer) { TLRPC.Message newMsg = null; int type = -1; if (message != null) { @@ -1622,6 +1627,13 @@ public class MessagesController implements NotificationCenter.NotificationCenter type = 7; newMsg.message = "-1"; newMsg.attachPath = document.path; + } else if (audio != null) { + newMsg = new TLRPC.TL_message(); + newMsg.media = new TLRPC.TL_messageMediaAudio(); + newMsg.media.audio = audio; + type = 8; + newMsg.message = "-1"; + newMsg.attachPath = audio.path; } if (newMsg == null) { return; @@ -1699,7 +1711,7 @@ public class MessagesController implements NotificationCenter.NotificationCenter reqSend.media = new TLRPC.TL_decryptedMessageMediaEmpty(); performSendEncryptedRequest(reqSend, newMsgObj, encryptedChat, null); } - } else if (type == 1 || type == 2 || type == 3 || type == 5 || type == 6 || type == 7) { + } else if (type >= 1 && type <= 3 || type >= 5 && type <= 8) { if (encryptedChat == null) { TLRPC.TL_messages_sendMedia reqSend = new TLRPC.TL_messages_sendMedia(); reqSend.peer = sendToPeer; @@ -1753,6 +1765,15 @@ public class MessagesController implements NotificationCenter.NotificationCenter delayedMessage.obj = newMsgObj; delayedMessage.documentLocation = document; performSendDelayedMessage(delayedMessage); + } else if (type == 8) { + reqSend.media = new TLRPC.TL_inputMediaUploadedAudio(); + reqSend.media.duration = audio.duration; + DelayedMessage delayedMessage = new DelayedMessage(); + delayedMessage.sendRequest = reqSend; + delayedMessage.type = 3; + delayedMessage.obj = newMsgObj; + delayedMessage.audioLocation = audio; + performSendDelayedMessage(delayedMessage); } } else { TLRPC.TL_decryptedMessage reqSend = new TLRPC.TL_decryptedMessage(); @@ -1837,6 +1858,22 @@ public class MessagesController implements NotificationCenter.NotificationCenter delayedMessage.encryptedChat = encryptedChat; delayedMessage.documentLocation = document; performSendDelayedMessage(delayedMessage); + } else if (type == 8) { + reqSend.media = new TLRPC.TL_decryptedMessageMediaAudio(); + reqSend.media.iv = new byte[32]; + reqSend.media.key = new byte[32]; + random.nextBytes(reqSend.media.iv); + random.nextBytes(reqSend.media.key); + reqSend.media.duration = audio.duration; + reqSend.media.size = audio.size; + + DelayedMessage delayedMessage = new DelayedMessage(); + delayedMessage.sendEncryptedRequest = reqSend; + delayedMessage.type = 3; + delayedMessage.obj = newMsgObj; + delayedMessage.encryptedChat = encryptedChat; + delayedMessage.audioLocation = audio; + performSendDelayedMessage(delayedMessage); } } } else if (type == 4) { @@ -1852,10 +1889,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } - private void processSendedMessage(TLRPC.Message newMsg, TLRPC.Message sendedMessage, TLRPC.EncryptedFile file, TLRPC.DecryptedMessage decryptedMessage) { - if (sendedMessage != null) { - if (sendedMessage.media instanceof TLRPC.TL_messageMediaPhoto && sendedMessage.media.photo != null && newMsg.media instanceof TLRPC.TL_messageMediaPhoto && newMsg.media.photo != null) { - for (TLRPC.PhotoSize size : sendedMessage.media.photo.sizes) { + private void processSendedMessage(TLRPC.Message newMsg, TLRPC.Message sentMessage, TLRPC.EncryptedFile file, TLRPC.DecryptedMessage decryptedMessage) { + if (sentMessage != null) { + if (sentMessage.media instanceof TLRPC.TL_messageMediaPhoto && sentMessage.media.photo != null && newMsg.media instanceof TLRPC.TL_messageMediaPhoto && newMsg.media.photo != null) { + for (TLRPC.PhotoSize size : sentMessage.media.photo.sizes) { if (size instanceof TLRPC.TL_photoSizeEmpty) { continue; } @@ -1875,11 +1912,11 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } } - sendedMessage.message = newMsg.message; - sendedMessage.attachPath = newMsg.attachPath; - } else if (sendedMessage.media instanceof TLRPC.TL_messageMediaVideo && sendedMessage.media.video != null && newMsg.media instanceof TLRPC.TL_messageMediaVideo && newMsg.media.video != null) { + sentMessage.message = newMsg.message; + sentMessage.attachPath = newMsg.attachPath; + } else if (sentMessage.media instanceof TLRPC.TL_messageMediaVideo && sentMessage.media.video != null && newMsg.media instanceof TLRPC.TL_messageMediaVideo && newMsg.media.video != null) { TLRPC.PhotoSize size2 = newMsg.media.video.thumb; - TLRPC.PhotoSize size = sendedMessage.media.video.thumb; + TLRPC.PhotoSize size = sentMessage.media.video.thumb; if (size2.location != null && size.location != null && !(size instanceof TLRPC.TL_photoSizeEmpty) && !(size2 instanceof TLRPC.TL_photoSizeEmpty)) { String fileName = size2.location.volume_id + "_" + size2.location.local_id; String fileName2 = size.location.volume_id + "_" + size.location.local_id; @@ -1891,12 +1928,26 @@ public class MessagesController implements NotificationCenter.NotificationCenter boolean result = cacheFile.renameTo(cacheFile2); FileLoader.Instance.replaceImageInCache(fileName, fileName2); size2.location = size.location; - sendedMessage.message = newMsg.message; - sendedMessage.attachPath = newMsg.attachPath; + sentMessage.message = newMsg.message; + sentMessage.attachPath = newMsg.attachPath; } - } else if (sendedMessage.media instanceof TLRPC.TL_messageMediaDocument && sendedMessage.media.document != null && newMsg.media instanceof TLRPC.TL_messageMediaDocument && newMsg.media.document != null) { - sendedMessage.message = newMsg.message; - sendedMessage.attachPath = newMsg.attachPath; + } else if (sentMessage.media instanceof TLRPC.TL_messageMediaDocument && sentMessage.media.document != null && newMsg.media instanceof TLRPC.TL_messageMediaDocument && newMsg.media.document != null) { + sentMessage.message = newMsg.message; + sentMessage.attachPath = newMsg.attachPath; + } else if (sentMessage.media instanceof TLRPC.TL_messageMediaAudio && sentMessage.media.audio != null && newMsg.media instanceof TLRPC.TL_messageMediaAudio && newMsg.media.audio != null) { + sentMessage.message = newMsg.message; + sentMessage.attachPath = newMsg.attachPath; + + String fileName = newMsg.media.audio.dc_id + "_" + newMsg.media.audio.id + ".m4a"; + String fileName2 = sentMessage.media.audio.dc_id + "_" + sentMessage.media.audio.id + ".m4a"; + if (fileName.equals(fileName2)) { + return; + } + File cacheFile = new File(Utilities.getCacheDir(), fileName); + File cacheFile2 = new File(Utilities.getCacheDir(), fileName2); + cacheFile.renameTo(cacheFile2); + sentMessage.media.audio.dc_id = newMsg.media.audio.dc_id; + sentMessage.media.audio.id = newMsg.media.audio.id; } } else if (file != null) { if (newMsg.media instanceof TLRPC.TL_messageMediaPhoto && newMsg.media.photo != null) { @@ -1953,6 +2004,32 @@ public class MessagesController implements NotificationCenter.NotificationCenter newMsg.media.document.path = document.path; newMsg.media.document.thumb = document.thumb; newMsg.media.document.dc_id = file.dc_id; + ArrayList arr = new ArrayList(); + arr.add(newMsg); + MessagesStorage.Instance.putMessages(arr, false, true); + } else if (newMsg.media instanceof TLRPC.TL_messageMediaAudio && newMsg.media.audio != null) { + TLRPC.Audio audio = newMsg.media.audio; + newMsg.media.audio = new TLRPC.TL_audioEncrypted(); + newMsg.media.audio.id = file.id; + newMsg.media.audio.access_hash = file.access_hash; + newMsg.media.audio.user_id = audio.user_id; + newMsg.media.audio.date = audio.date; + newMsg.media.audio.duration = audio.duration; + newMsg.media.audio.size = file.size; + newMsg.media.audio.dc_id = file.dc_id; + newMsg.media.audio.key = decryptedMessage.media.key; + newMsg.media.audio.iv = decryptedMessage.media.iv; + newMsg.media.audio.path = audio.path; + + String fileName = audio.dc_id + "_" + audio.id + ".m4a"; + String fileName2 = newMsg.media.audio.dc_id + "_" + newMsg.media.audio.id + ".m4a"; + if (fileName.equals(fileName2)) { + return; + } + File cacheFile = new File(Utilities.getCacheDir(), fileName); + File cacheFile2 = new File(Utilities.getCacheDir(), fileName2); + cacheFile.renameTo(cacheFile2); + ArrayList arr = new ArrayList(); arr.add(newMsg); MessagesStorage.Instance.putMessages(arr, false, true); @@ -2210,6 +2287,14 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else { FileLoader.Instance.uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); } + } else if (message.type == 3) { + String location = message.audioLocation.path; + delayedMessages.put(location, message); + if (message.sendRequest != null) { + FileLoader.Instance.uploadFile(location, null, null); + } else { + FileLoader.Instance.uploadFile(location, message.sendEncryptedRequest.media.key, message.sendEncryptedRequest.media.iv); + } } } }); @@ -2304,15 +2389,12 @@ public class MessagesController implements NotificationCenter.NotificationCenter } else if (message.type == 2) { message.sendRequest.media.file = file; performSendMessageRequest(message.sendRequest, message.obj); + } else if (message.type == 3) { + message.sendRequest.media.file = file; + performSendMessageRequest(message.sendRequest, message.obj); } } else if (encryptedFile != null) { - if (message.type == 0) { - performSendEncryptedRequest(message.sendEncryptedRequest, message.obj, message.encryptedChat, encryptedFile); - } else if (message.type == 1) { - performSendEncryptedRequest(message.sendEncryptedRequest, message.obj, message.encryptedChat, encryptedFile); - } else if (message.type == 2) { - performSendEncryptedRequest(message.sendEncryptedRequest, message.obj, message.encryptedChat, encryptedFile); - } + performSendEncryptedRequest(message.sendEncryptedRequest, message.obj, message.encryptedChat, encryptedFile); } delayedMessages.remove(location); } @@ -2933,6 +3015,21 @@ public class MessagesController implements NotificationCenter.NotificationCenter } } + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + for (TLRPC.User user : res.users) { + users.put(user.id, user); + if (user.id == UserConfig.clientUserId) { + UserConfig.currentUser = user; + } + } + for (TLRPC.Chat chat : res.chats) { + chats.put(chat.id, chat); + } + } + }); + MessagesStorage.Instance.storageQueue.postRunnable(new Runnable() { @Override public void run() { @@ -3015,15 +3112,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter Utilities.RunOnUIThread(new Runnable() { @Override public void run() { - for (TLRPC.User user : res.users) { - users.put(user.id, user); - if (user.id == UserConfig.clientUserId) { - UserConfig.currentUser = user; - } - } - for (TLRPC.Chat chat : res.chats) { - chats.put(chat.id, chat); - } for (HashMap.Entry> pair : messages.entrySet()) { Long key = pair.getKey(); ArrayList value = pair.getValue(); @@ -3315,7 +3403,6 @@ public class MessagesController implements NotificationCenter.NotificationCenter final ArrayList tasks = new ArrayList(); final ArrayList contactsIds = new ArrayList(); MessageObject lastMessage = null; - boolean usersAdded = false; boolean checkForUsers = true; ConcurrentHashMap usersDict; @@ -3339,6 +3426,27 @@ public class MessagesController implements NotificationCenter.NotificationCenter chatsDict = chats; } + if (usersArr != null || chatsArr != null) { + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + if (usersArr != null) { + for (TLRPC.User user : usersArr) { + users.put(user.id, user); + if (user.id == UserConfig.clientUserId) { + UserConfig.currentUser = user; + } + } + } + if (chatsArr != null) { + for (TLRPC.Chat chat : chatsArr) { + chats.put(chat.id, chat); + } + } + } + }); + } + int interfaceUpdateMask = 0; for (TLRPC.Update update : updates) { @@ -3629,25 +3737,10 @@ public class MessagesController implements NotificationCenter.NotificationCenter dialog.unread_count = 0; dialog.top_message = 0; dialog.last_message_date = update.date; - usersAdded = true; + Utilities.RunOnUIThread(new Runnable() { @Override public void run() { - if (usersArr != null) { - for (TLRPC.User user : usersArr) { - users.put(user.id, user); - if (user.id == UserConfig.clientUserId) { - UserConfig.currentUser = user; - } - } - } - if (chatsArr != null) { - for (TLRPC.Chat chat : chatsArr) { - chats.put(chat.id, chat); - } - } - - dialogs_dict.put(dialog.id, dialog); dialogs.add(dialog); dialogsServerOnly.clear(); @@ -3723,27 +3816,11 @@ public class MessagesController implements NotificationCenter.NotificationCenter MessagesStorage.Instance.putMessages(messagesArr, true, true); } - final boolean usersAddedConst = usersAdded; if (!messages.isEmpty() || !markAsReadMessages.isEmpty() || !deletedMessages.isEmpty() || !printChanges.isEmpty() || !chatInfoToUpdate.isEmpty() || !updatesOnMainThread.isEmpty() || !markAsReadEncrypted.isEmpty() || !contactsIds.isEmpty()) { Utilities.RunOnUIThread(new Runnable() { @Override public void run() { int updateMask = interfaceUpdateMaskFinal; - if (!usersAddedConst) { - if (usersArr != null) { - for (TLRPC.User user : usersArr) { - users.put(user.id, user); - if (user.id == UserConfig.clientUserId) { - UserConfig.currentUser = user; - } - } - } - if (chatsArr != null) { - for (TLRPC.Chat chat : chatsArr) { - chats.put(chat.id, chat); - } - } - } boolean avatarsUpdate = false; if (!updatesOnMainThread.isEmpty()) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java index e1e9cf2c3..8d7b1c673 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java @@ -334,7 +334,7 @@ public class MessagesStorage { FileLog.e("tmessages", e); } } - }, true); + }); } public void clearUserPhotos(final int uid) { @@ -967,7 +967,7 @@ public class MessagesStorage { FileLog.e("tmessages", e); } } - }, true); + }); } public void putContacts(final ArrayList contacts, final boolean deleteAll) { @@ -1102,21 +1102,21 @@ public class MessagesStorage { } cursor.dispose(); } catch (Exception e) { + contactHashMap.clear(); FileLog.e("tmessages", e); - } finally { - ContactsController.Instance.performSyncPhoneBook(contactHashMap, true, true); } + ContactsController.Instance.performSyncPhoneBook(contactHashMap, true, true); } - }, true); + }); } public void getContacts() { storageQueue.postRunnable(new Runnable() { @Override public void run() { + ArrayList contacts = new ArrayList(); + ArrayList users = new ArrayList(); try { - ArrayList contacts = new ArrayList(); - ArrayList users = new ArrayList(); SQLiteCursor cursor = database.queryFinalized("SELECT * FROM contacts WHERE 1"); String uids = ""; while (cursor.next()) { @@ -1150,12 +1150,14 @@ public class MessagesStorage { } cursor.dispose(); } - ContactsController.Instance.processLoadedContacts(contacts, users, 1); } catch (Exception e) { + contacts.clear(); + users.clear(); FileLog.e("tmessages", e); } + ContactsController.Instance.processLoadedContacts(contacts, users, 1); } - }, true); + }); } public void putMediaCount(final long uid, final int count) { @@ -1203,7 +1205,7 @@ public class MessagesStorage { FileLog.e("tmessages", e); } } - }, true); + }); } public void loadMedia(final long uid, final int offset, final int count, final int max_id, final int classGuid) { @@ -1279,7 +1281,7 @@ public class MessagesStorage { MessagesController.Instance.processLoadedMedia(res, uid, offset, count, max_id, true, classGuid); } } - }, true); + }); } public void putMedia(final long uid, final ArrayList messages) { @@ -1469,7 +1471,7 @@ public class MessagesStorage { MessagesController.Instance.processLoadedMessages(res, dialog_id, offset, count_query, max_id, true, classGuid, min_unread_id, max_unread_id, count_unread, max_unread_date, forward); } } - }, true); + }); } public void startTransaction(boolean useQueue) { @@ -2691,7 +2693,7 @@ public class MessagesStorage { MessagesController.Instance.processLoadedDialogs(dialogs, encryptedChats, 0, 0, 100, true, true); } } - }, true); + }); } public void putDialogs(final TLRPC.messages_Dialogs dialogs) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java b/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java index b7d64b164..db3abdc93 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SerializedData.java @@ -22,12 +22,23 @@ public class SerializedData { private DataOutputStream out; private ByteArrayInputStream inbuf; private DataInputStream in; + private boolean justCalc = false; + private int len; public SerializedData() { outbuf = new ByteArrayOutputStream(); out = new DataOutputStream(outbuf); } + public SerializedData(boolean calculate) { + if (!calculate) { + outbuf = new ByteArrayOutputStream(); + out = new DataOutputStream(outbuf); + } + justCalc = calculate; + len = 0; + } + public SerializedData(int size) { outbuf = new ByteArrayOutputStream(size); out = new DataOutputStream(outbuf); @@ -50,13 +61,17 @@ public class SerializedData { in = new DataInputStream(inbuf); } - public void writeInt32(int x){ - writeInt32(x, out); + public void writeInt32(int x) { + if (!justCalc) { + writeInt32(x, out); + } else { + len += 4; + } } - protected void writeInt32(int x, DataOutputStream out){ + private void writeInt32(int x, DataOutputStream out) { try { - for(int i = 0; i < 4; i++){ + for(int i = 0; i < 4; i++) { out.write(x >> (i * 8)); } } catch(IOException gfdsgd) { @@ -65,10 +80,14 @@ public class SerializedData { } public void writeInt64(long i) { - writeInt64(i, out); + if (!justCalc) { + writeInt64(i, out); + } else { + len += 8; + } } - protected void writeInt64(long x, DataOutputStream out){ + private void writeInt64(long x, DataOutputStream out) { try { for(int i = 0; i < 8; i++){ out.write((int)(x >> (i * 8))); @@ -90,10 +109,14 @@ public class SerializedData { } public void writeBool(boolean value) { - if (value) { - writeInt32(0x997275b5); + if (!justCalc) { + if (value) { + writeInt32(0x997275b5); + } else { + writeInt32(0xbc799737); + } } else { - writeInt32(0xbc799737); + len += 4; } } @@ -143,9 +166,13 @@ public class SerializedData { return 0; } - public void writeRaw(byte[] b){ + public void writeRaw(byte[] b) { try { - out.write(b); + if (!justCalc) { + out.write(b); + } else { + len += b.length; + } } catch(Exception x) { FileLog.e("tmessages", "write raw error"); } @@ -153,7 +180,11 @@ public class SerializedData { public void writeRaw(byte[] b, int offset, int count) { try { - out.write(b, offset, count); + if (!justCalc) { + out.write(b, offset, count); + } else { + len += count; + } } catch(Exception x) { FileLog.e("tmessages", "write raw error"); } @@ -161,7 +192,11 @@ public class SerializedData { public void writeByte(int i) { try { - out.writeByte((byte)i); + if (!justCalc) { + out.writeByte((byte)i); + } else { + len += 1; + } } catch (Exception e) { FileLog.e("tmessages", "write byte error"); } @@ -169,13 +204,17 @@ public class SerializedData { public void writeByte(byte b) { try { - out.writeByte(b); + if (!justCalc) { + out.writeByte(b); + } else { + len += 1; + } } catch (Exception e) { FileLog.e("tmessages", "write byte error"); } } - public void readRaw(byte[] b){ + public void readRaw(byte[] b) { try { in.read(b); } catch(Exception x) { @@ -189,7 +228,7 @@ public class SerializedData { return arr; } - public String readString(){ + public String readString() { try { int sl = 1; int l = in.read(); @@ -233,20 +272,36 @@ public class SerializedData { return null; } - public void writeByteArray(byte[] b){ + public void writeByteArray(byte[] b) { try { if (b.length <= 253){ - out.write(b.length); + if (!justCalc) { + out.write(b.length); + } else { + len += 1; + } } else { - out.write(254); - out.write(b.length); - out.write(b.length >> 8); - out.write(b.length >> 16); + if (!justCalc) { + out.write(254); + out.write(b.length); + out.write(b.length >> 8); + out.write(b.length >> 16); + } else { + len += 4; + } + } + if (!justCalc) { + out.write(b); + } else { + len += b.length; } - out.write(b); int i = b.length <= 253 ? 1 : 4; while((b.length + i) % 4 != 0){ - out.write(0); + if (!justCalc) { + out.write(0); + } else { + len += 1; + } i++; } } catch(Exception x) { @@ -265,17 +320,33 @@ public class SerializedData { public void writeByteArray(byte[] b, int offset, int count) { try { if(count <= 253){ - out.write(count); + if (!justCalc) { + out.write(count); + } else { + len += 1; + } } else { - out.write(254); - out.write(count); - out.write(count >> 8); - out.write(count >> 16); + if (!justCalc) { + out.write(254); + out.write(count); + out.write(count >> 8); + out.write(count >> 16); + } else { + len += 4; + } + } + if (!justCalc) { + out.write(b, offset, count); + } else { + len += count; } - out.write(b, offset, count); int i = count <= 253 ? 1 : 4; while ((count + i) % 4 != 0){ - out.write(0); + if (!justCalc) { + out.write(0); + } else { + len += 1; + } i++; } } catch(Exception x) { @@ -292,7 +363,7 @@ public class SerializedData { return 0; } - public void writeDouble(double d){ + public void writeDouble(double d) { try { writeInt64(Double.doubleToRawLongBits(d)); } catch(Exception x) { @@ -301,7 +372,10 @@ public class SerializedData { } public int length() { - return isOut ? outbuf.size() : inbuf.available(); + if (!justCalc) { + return isOut ? outbuf.size() : inbuf.available(); + } + return len; } protected void set(byte[] newData) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java b/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java index a1484ff4c..1bdf0ff57 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TcpConnection.java @@ -10,6 +10,7 @@ package org.telegram.messenger; import java.io.IOException; import java.net.InetSocketAddress; +import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Timer; @@ -51,6 +52,7 @@ public class TcpConnection extends PyroClientAdapter { private int willRetryConnectCount = 5; private boolean isNextPort = false; private final Integer timerSync = 1; + private boolean wasConnected; public int transportRequestClass; @@ -102,6 +104,7 @@ public class TcpConnection extends PyroClientAdapter { FileLog.d("tmessages", String.format(TcpConnection.this + " Connecting (%s:%d)", hostAddress, hostPort)); firstPacket = true; restOfTheData = null; + wasConnected = false; hasSomeDataSinceLastConnect = false; if (client != null) { client.removeListener(TcpConnection.this); @@ -113,7 +116,7 @@ public class TcpConnection extends PyroClientAdapter { if (isNextPort) { client.setTimeout(8000); } else { - client.setTimeout(35000); + client.setTimeout(15000); } selector.wakeup(); } catch (Exception e) { @@ -141,7 +144,7 @@ public class TcpConnection extends PyroClientAdapter { failedConnectionCount++; if (failedConnectionCount == 1) { if (hasSomeDataSinceLastConnect) { - willRetryConnectCount = 5; + willRetryConnectCount = 3; } else { willRetryConnectCount = 1; } @@ -217,6 +220,7 @@ public class TcpConnection extends PyroClientAdapter { firstPacket = true; restOfTheData = null; channelToken = 0; + wasConnected = false; } public void suspendConnection(boolean task) { @@ -306,7 +310,7 @@ public class TcpConnection extends PyroClientAdapter { Datacenter datacenter = ConnectionsManager.Instance.datacenterWithId(datacenterId); datacenter.storeCurrentAddressAndPortNum(); isNextPort = false; - client.setTimeout(35000); + client.setTimeout(20000); } hasSomeDataSinceLastConnect = true; @@ -407,7 +411,7 @@ public class TcpConnection extends PyroClientAdapter { } } - public void handleDisconnect(PyroClient client, Exception e) { + public void handleDisconnect(PyroClient client, Exception e, boolean timedout) { synchronized (timerSync) { if (reconnectTimer != null) { reconnectTimer.cancel(); @@ -419,9 +423,11 @@ public class TcpConnection extends PyroClientAdapter { } else { FileLog.d("tmessages", "Disconnected " + TcpConnection.this); } + boolean swirchToNextPort = wasConnected && hasSomeDataSinceLastConnect; firstPacket = true; restOfTheData = null; channelToken = 0; + wasConnected = false; if (connectionState != TcpConnectionState.TcpConnectionStageSuspended && connectionState != TcpConnectionState.TcpConnectionStageIdle) { connectionState = TcpConnectionState.TcpConnectionStageIdle; } @@ -446,7 +452,7 @@ public class TcpConnection extends PyroClientAdapter { } if (ConnectionsManager.isNetworkOnline()) { isNextPort = true; - if (failedConnectionCount > willRetryConnectCount) { + if (failedConnectionCount > willRetryConnectCount || swirchToNextPort) { Datacenter datacenter = ConnectionsManager.Instance.datacenterWithId(datacenterId); datacenter.nextAddressOrPort(); failedConnectionCount = 0; @@ -486,6 +492,7 @@ public class TcpConnection extends PyroClientAdapter { public void connectedClient(PyroClient client) { connectionState = TcpConnectionState.TcpConnectionStageConnected; channelToken = generateChannelToken(); + wasConnected = true; FileLog.d("tmessages", String.format(TcpConnection.this + " Connected (%s:%d)", hostAddress, hostPort)); if (delegate != null) { final TcpConnectionDelegate finalDelegate = delegate; @@ -500,18 +507,18 @@ public class TcpConnection extends PyroClientAdapter { @Override public void unconnectableClient(PyroClient client, Exception cause) { - handleDisconnect(client, cause); + handleDisconnect(client, cause, false); } @Override public void droppedClient(PyroClient client, IOException cause) { super.droppedClient(client, cause); - handleDisconnect(client, cause); + handleDisconnect(client, cause, (cause instanceof SocketTimeoutException)); } @Override public void disconnectedClient(PyroClient client) { - handleDisconnect(client, null); + handleDisconnect(client, null, false); } @Override @@ -527,7 +534,5 @@ public class TcpConnection extends PyroClientAdapter { @Override public void sentData(PyroClient client, int bytes) { - failedConnectionCount = 0; - FileLog.d("tmessages", TcpConnection.this + " bytes sent " + bytes); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java index e17b045af..2d2de9144 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java @@ -27,6 +27,7 @@ public class UserConfig { public static int lastSendMessageId = -210000; public static int lastLocalId = -210000; public static String contactsHash = ""; + public static String importHash = ""; private final static Integer sync = 1; public static boolean saveIncomingPhotos = false; @@ -54,6 +55,7 @@ public class UserConfig { editor.putInt("lastSendMessageId", lastSendMessageId); editor.putInt("lastLocalId", lastLocalId); editor.putString("contactsHash", contactsHash); + editor.putString("importHash", importHash); editor.putBoolean("saveIncomingPhotos", saveIncomingPhotos); if (withFile) { SerializedData data = new SerializedData(); @@ -69,6 +71,7 @@ public class UserConfig { editor.putInt("lastSendMessageId", lastSendMessageId); editor.putInt("lastLocalId", lastLocalId); editor.putString("contactsHash", contactsHash); + editor.putString("importHash", importHash); editor.putBoolean("saveIncomingPhotos", saveIncomingPhotos); editor.remove("user"); } @@ -102,7 +105,7 @@ public class UserConfig { lastSendMessageId = data.readInt32(); lastLocalId = data.readInt32(); contactsHash = data.readString(); - data.readString(); + importHash = data.readString(); saveIncomingPhotos = data.readBool(); if (currentUser.status != null) { if (currentUser.status.expires != 0) { @@ -136,6 +139,7 @@ public class UserConfig { lastSendMessageId = preferences.getInt("lastSendMessageId", -210000); lastLocalId = preferences.getInt("lastLocalId", -210000); contactsHash = preferences.getString("contactsHash", ""); + importHash = preferences.getString("importHash", ""); saveIncomingPhotos = preferences.getBoolean("saveIncomingPhotos", false); } if (lastLocalId > -210000) { @@ -160,6 +164,7 @@ public class UserConfig { lastSendMessageId = preferences.getInt("lastSendMessageId", -210000); lastLocalId = preferences.getInt("lastLocalId", -210000); contactsHash = preferences.getString("contactsHash", ""); + importHash = preferences.getString("importHash", ""); saveIncomingPhotos = preferences.getBoolean("saveIncomingPhotos", false); String user = preferences.getString("user", null); if (user != null) { @@ -185,6 +190,7 @@ public class UserConfig { currentUser = null; registeredForPush = false; contactsHash = ""; + importHash = ""; lastLocalId = -210000; lastSendMessageId = -210000; saveIncomingPhotos = false; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java index 3fb5ce76d..2eb549822 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java @@ -749,9 +749,9 @@ public class Utilities { return storageDir; } - public static String getPath(final Context context, final Uri uri) { + public static String getPath(final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; - if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { + if (isKitKat && DocumentsContract.isDocumentUri(ApplicationLoader.applicationContext, uri)) { if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); @@ -762,7 +762,7 @@ public class Utilities { } else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); - return getDataColumn(context, contentUri, null, null); + return getDataColumn(ApplicationLoader.applicationContext, contentUri, null, null); } else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); @@ -782,10 +782,10 @@ public class Utilities { split[1] }; - return getDataColumn(context, contentUri, selection, selectionArgs); + return getDataColumn(ApplicationLoader.applicationContext, contentUri, selection, selectionArgs); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { - return getDataColumn(context, uri, null, null); + return getDataColumn(ApplicationLoader.applicationContext, uri, null, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } diff --git a/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java index 95ce61df5..b86f42264 100644 --- a/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/objects/MessageObject.java @@ -22,7 +22,6 @@ import org.telegram.ui.ApplicationLoader; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Calendar; -import java.util.Date; import java.util.GregorianCalendar; public class MessageObject { @@ -303,6 +302,8 @@ public class MessageObject { return getAttachFileName(messageOwner.media.video); } else if (messageOwner.media instanceof TLRPC.TL_messageMediaDocument) { return getAttachFileName(messageOwner.media.document); + } else if (messageOwner.media instanceof TLRPC.TL_messageMediaAudio) { + return getAttachFileName(messageOwner.media.audio); } return ""; } @@ -328,6 +329,9 @@ public class MessageObject { } else if (attach instanceof TLRPC.PhotoSize) { TLRPC.PhotoSize photo = (TLRPC.PhotoSize)attach; return photo.location.volume_id + "_" + photo.location.local_id + ".jpg"; + } else if (attach instanceof TLRPC.Audio) { + TLRPC.Audio audio = (TLRPC.Audio)attach; + return audio.dc_id + "_" + audio.id + ".m4a"; } return ""; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java index 8e569011f..fde77a624 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ApplicationLoader.java @@ -11,6 +11,7 @@ package org.telegram.ui; import android.app.Activity; import android.app.Application; import android.content.Context; +import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -25,6 +26,7 @@ import com.google.android.gms.common.GooglePlayServicesUtil; import com.google.android.gms.gcm.GoogleCloudMessaging; import org.telegram.PhoneFormat.PhoneFormat; +import org.telegram.messenger.BackgroundService; import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; @@ -60,6 +62,7 @@ public class ApplicationLoader extends Application { @Override public void onCreate() { super.onCreate(); + currentLocale = Locale.getDefault(); Instance = this; @@ -130,6 +133,8 @@ public class ApplicationLoader extends Application { lastPauseTime = System.currentTimeMillis(); FileLog.e("tmessages", "start application with time " + lastPauseTime); + + startService(new Intent(this, BackgroundService.class)); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java new file mode 100644 index 000000000..ebcd0c611 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -0,0 +1,26 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013. + */ + +package org.telegram.ui.Cells; + +import android.content.Context; +import android.util.AttributeSet; + +public class ChatMessageCell extends BaseCell { + public ChatMessageCell(Context context) { + super(context); + } + + public ChatMessageCell(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ChatMessageCell(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java index 9e23350c6..bd7e19da4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatOrUserCell.java @@ -393,7 +393,7 @@ public class ChatOrUserCell extends BaseCell { if (value == 0) { value = user.status.expires; } - onlineString = getResources().getString(R.string.LastSeen) + " " + Utilities.formatDateOnline(value); + onlineString = Utilities.formatDateOnline(value); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java index 6adea8808..23a1a1ffe 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -394,18 +394,22 @@ public class DialogCell extends BaseCell { if (encryptedChat instanceof TLRPC.TL_encryptedChatRequested) { messageString = ApplicationLoader.applicationContext.getString(R.string.EncryptionProcessing); } else if (encryptedChat instanceof TLRPC.TL_encryptedChatWaiting) { - messageString = String.format(ApplicationLoader.applicationContext.getString(R.string.AwaitingEncryption), user.first_name); + if (user != null && user.first_name != null) { + messageString = String.format(ApplicationLoader.applicationContext.getString(R.string.AwaitingEncryption), user.first_name); + } else { + messageString = String.format(ApplicationLoader.applicationContext.getString(R.string.AwaitingEncryption), ""); + } } else if (encryptedChat instanceof TLRPC.TL_encryptedChatDiscarded) { messageString = ApplicationLoader.applicationContext.getString(R.string.EncryptionRejected); } else if (encryptedChat instanceof TLRPC.TL_encryptedChat) { if (encryptedChat.admin_id == UserConfig.clientUserId) { - if (user != null) { + if (user != null && user.first_name != null) { messageString = String.format(ApplicationLoader.applicationContext.getString(R.string.EncryptedChatStartedOutgoing), user.first_name); + } else { + messageString = String.format(ApplicationLoader.applicationContext.getString(R.string.EncryptedChatStartedOutgoing), ""); } } else { - if (user != null) { - messageString = String.format(ApplicationLoader.applicationContext.getString(R.string.EncryptedChatStartedIncoming), user.first_name); - } + messageString = ApplicationLoader.applicationContext.getString(R.string.EncryptedChatStartedIncoming); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 515a1ad88..131909e7b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -22,6 +22,7 @@ import android.graphics.Rect; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.BitmapDrawable; import android.media.MediaPlayer; +import android.media.MediaRecorder; import android.media.ThumbnailUtils; import android.net.Uri; import android.os.Bundle; @@ -37,6 +38,7 @@ import android.text.SpannableStringBuilder; import android.text.TextWatcher; import android.text.style.ClickableSpan; import android.text.style.ImageSpan; +import android.util.Log; import android.util.TypedValue; import android.view.Display; import android.view.KeyEvent; @@ -121,6 +123,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private TextView emptyView; private View bottomOverlay; private TextView bottomOverlayText; + private ImageButton audioSendButton; private MessageObject selectedObject; private MessageObject forwaringMessage; private TextView secretViewStatusTextView; @@ -179,6 +182,10 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa private CharSequence lastPrintString; + private MediaRecorder audioRecorder = null; + private TLRPC.TL_audio recordingAudio = null; + private File recordingAudioFile = null; + ActionMode mActionMode = null; private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { @Override @@ -380,6 +387,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa View bottomOverlayChat = fragmentView.findViewById(R.id.bottom_overlay_chat); progressView = fragmentView.findViewById(R.id.progressLayout); pagedownButton = fragmentView.findViewById(R.id.pagedown_button); + audioSendButton = (ImageButton)fragmentView.findViewById(R.id.chat_audio_send_button); View progressViewInner = progressView.findViewById(R.id.progressLayoutInner); updateContactStatus(); @@ -460,7 +468,9 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa chatListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView adapter, View view, int position, long id) { - createMenu(view, false); + if (mActionMode == null) { + createMenu(view, false); + } return true; } }); @@ -502,8 +512,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa messsageEditText = (EditText)fragmentView.findViewById(R.id.chat_text_edit); sendButton = (ImageButton)fragmentView.findViewById(R.id.chat_send_button); - sendButton.setImageResource(R.drawable.send_button_states); sendButton.setEnabled(false); + sendButton.setVisibility(View.INVISIBLE); emojiButton = (ImageView)fragmentView.findViewById(R.id.chat_smile_button); if (loading && messages.isEmpty()) { @@ -568,6 +578,18 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } }); + audioSendButton.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { + startRecording(); + } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) { + stopRecording(); + } + return false; + } + }); + pagedownButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -590,6 +612,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } }); + checkSendButton(); + messsageEditText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { @@ -602,6 +626,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa message = message.replaceAll("\n\n+", "\n\n"); message = message.replaceAll(" +", " "); sendButton.setEnabled(message.length() != 0); + checkSendButton(); if (message.length() != 0 && lastTypingTimeSend < System.currentTimeMillis() - 5000 && !ignoreTextChange) { int currentTime = ConnectionsManager.Instance.getCurrentTime(); @@ -722,6 +747,18 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa return fragmentView; } + private void checkSendButton() { + sendButton.setVisibility(View.VISIBLE); + audioSendButton.setVisibility(View.INVISIBLE); +// if (messsageEditText.length() > 0) { +// sendButton.setVisibility(View.VISIBLE); +// audioSendButton.setVisibility(View.INVISIBLE); +// } else { +// sendButton.setVisibility(View.INVISIBLE); +// audioSendButton.setVisibility(View.VISIBLE); +// } + } + private void sendMessage() { String message = messsageEditText.getText().toString().trim(); if (processSendingText(message)) { @@ -782,6 +819,84 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa paused = true; } + private void startRecording() { + if (audioRecorder != null) { + return; + } + + recordingAudio = new TLRPC.TL_audio(); + recordingAudio.dc_id = Integer.MIN_VALUE; + recordingAudio.id = UserConfig.lastLocalId; + recordingAudio.user_id = UserConfig.clientUserId; + UserConfig.lastLocalId--; + UserConfig.saveConfig(false); + + recordingAudioFile = new File(Utilities.getCacheDir(), MessageObject.getAttachFileName(recordingAudio)); + + audioRecorder = new MediaRecorder(); + audioRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); + audioRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); + audioRecorder.setOutputFile(recordingAudioFile.getAbsolutePath()); + if(android.os.Build.VERSION.SDK_INT >= 10) { + audioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); + } else { + audioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); + } + audioRecorder.setAudioSamplingRate(24000); + audioRecorder.setAudioChannels(1); + audioRecorder.setAudioEncodingBitRate(16000); + + try { + audioRecorder.prepare(); + audioRecorder.start(); + } catch (Exception e) { + Log.e("tmessages", "prepare() failed"); + } + } + + private void stopRecording() { + try { + audioRecorder.stop(); + audioRecorder.release(); + audioRecorder = null; + + recordingAudio.date = ConnectionsManager.Instance.getCurrentTime(); + recordingAudio.size = (int)recordingAudioFile.length(); + recordingAudio.path = recordingAudioFile.getAbsolutePath(); + int duration = 0; + + MediaPlayer player = new MediaPlayer(); + try { + player.setDataSource(recordingAudio.path); + player.prepare(); + duration = player.getDuration(); + recordingAudio.duration = duration / 1000; + } catch (Exception e) { + FileLog.e("tmessages", e); + } finally { + try { + player.release(); + player = null; + } catch (Exception e) { + FileLog.e("tmessages", e); + } + } + + if (duration > 500) { + MessagesController.Instance.sendMessage(recordingAudio, dialog_id); + } else { + recordingAudio = null; + recordingAudioFile.delete(); + recordingAudioFile = null; + } + } catch (Exception e) { + FileLog.e("tmessages", e); + recordingAudio = null; + recordingAudioFile.delete(); + recordingAudioFile = null; + } + } + private void updateSecretStatus() { if (bottomOverlay == null) { return; @@ -1125,15 +1240,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (imageUri.getScheme().contains("file")) { imageFilePath = imageUri.getPath(); } else { - ActionBarActivity inflaterActivity = parentActivity; - if (inflaterActivity == null) { - inflaterActivity = (ActionBarActivity)getActivity(); - } - if (inflaterActivity == null) { - return; - } try { - imageFilePath = Utilities.getPath(inflaterActivity, imageUri); + imageFilePath = Utilities.getPath(imageUri); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -1158,15 +1266,8 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa Utilities.addMediaToGallery(currentPicturePath); currentPicturePath = null; } else { - ActionBarActivity inflaterActivity = parentActivity; - if (inflaterActivity == null) { - inflaterActivity = (ActionBarActivity)getActivity(); - } - if (inflaterActivity == null) { - return; - } try { - videoPath = Utilities.getPath(inflaterActivity, uri); + videoPath = Utilities.getPath(uri); } catch (Exception e) { FileLog.e("tmessages", e); } @@ -1477,7 +1578,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } } else if (id == MessagesController.updateInterfaces) { int updateMask = (Integer)args[0]; - if ((updateMask & MessagesController.UPDATE_MASK_NAME) != 0 || (updateMask & MessagesController.UPDATE_MASK_STATUS) != 0 || (updateMask & MessagesController.UPDATE_MASK_CHAT_NAME) != 0) { + if ((updateMask & MessagesController.UPDATE_MASK_NAME) != 0 || (updateMask & MessagesController.UPDATE_MASK_STATUS) != 0 || (updateMask & MessagesController.UPDATE_MASK_CHAT_NAME) != 0 || (updateMask & MessagesController.UPDATE_MASK_CHAT_MEMBERS) != 0) { updateSubtitle(); updateOnlineCount(); } @@ -2406,6 +2507,10 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa TLRPC.TL_document document = (TLRPC.TL_document)selectedObject.messageOwner.media.document; document.path = selectedObject.messageOwner.attachPath; MessagesController.Instance.sendMessage(document, dialog_id); + } else if (selectedObject.type == 18 || selectedObject.type == 19) { + TLRPC.TL_audio audio = (TLRPC.TL_audio)selectedObject.messageOwner.media.audio; + audio.path = selectedObject.messageOwner.attachPath; + MessagesController.Instance.sendMessage(audio, dialog_id); } ArrayList arr = new ArrayList(); arr.add(selectedObject.messageOwner.id); @@ -2732,88 +2837,102 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { view.setBackgroundColor(0); } - int messageType = holder.message.type; - if (!disableSelection) { - if (messageType == 2 || messageType == 4 || messageType == 6) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_photo_states); - } else if (messageType == 3 || messageType == 5 || messageType == 7) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_photo_states); - } else if (messageType == 0 || messageType == 8) { - holder.messageLayout.setBackgroundResource(R.drawable.chat_outgoing_text_states); - holder.messageLayout.setPadding(Utilities.dp(11), Utilities.dp(7), Utilities.dp(18), 0); - } else if (messageType == 1 || messageType == 9) { - holder.messageLayout.setBackgroundResource(R.drawable.chat_incoming_text_states); - holder.messageLayout.setPadding(Utilities.dp(19), Utilities.dp(7), Utilities.dp(9), 0); - } else if (messageType == 12) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_text_states); - holder.chatBubbleView.setPadding(Utilities.dp(6), Utilities.dp(6), Utilities.dp(18), 0); - } else if (messageType == 13) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_text_states); - holder.chatBubbleView.setPadding(Utilities.dp(15), Utilities.dp(6), Utilities.dp(9), 0); - } else if (messageType == 16) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_text_states); - holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), 0); - } else if (messageType == 17) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_text_states); - holder.chatBubbleView.setPadding(Utilities.dp(18), Utilities.dp(9), Utilities.dp(9), 0); - } + updateRowBackground(holder, disableSelection, selected); + } + } + } + + private void updateRowBackground(ChatListRowHolderEx holder, boolean disableSelection, boolean selected) { + int messageType = holder.message.type; + if (!disableSelection) { + if (messageType == 2 || messageType == 4 || messageType == 6) { + holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_photo_states); + } else if (messageType == 3 || messageType == 5 || messageType == 7) { + holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_photo_states); + } else if (messageType == 0 || messageType == 8) { + holder.messageLayout.setBackgroundResource(R.drawable.chat_outgoing_text_states); + holder.messageLayout.setPadding(Utilities.dp(11), Utilities.dp(7), Utilities.dp(18), 0); + } else if (messageType == 1 || messageType == 9) { + holder.messageLayout.setBackgroundResource(R.drawable.chat_incoming_text_states); + holder.messageLayout.setPadding(Utilities.dp(19), Utilities.dp(7), Utilities.dp(9), 0); + } else if (messageType == 12) { + holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_text_states); + holder.chatBubbleView.setPadding(Utilities.dp(6), Utilities.dp(6), Utilities.dp(18), 0); + } else if (messageType == 13) { + holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_text_states); + holder.chatBubbleView.setPadding(Utilities.dp(15), Utilities.dp(6), Utilities.dp(9), 0); + } else if (messageType == 16) { + holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_text_states); + holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), 0); + } else if (messageType == 17) { + holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_text_states); + holder.chatBubbleView.setPadding(Utilities.dp(18), Utilities.dp(9), Utilities.dp(9), 0); + } else if (messageType == 18) { + holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_text_states); + holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), Utilities.dp(6)); + } + } else { + if (messageType == 2 || messageType == 4 || messageType == 6) { + if (selected) { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_photo_selected); } else { - if (messageType == 2 || messageType == 4 || messageType == 6) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_photo_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_photo); - } - } else if (messageType == 3 || messageType == 5 || messageType == 7) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_photo_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_photo); - } - } else if (messageType == 0 || messageType == 8) { - if (selected) { - holder.messageLayout.setBackgroundResource(R.drawable.msg_out_selected); - } else { - holder.messageLayout.setBackgroundResource(R.drawable.msg_out); - } - holder.messageLayout.setPadding(Utilities.dp(11), Utilities.dp(7), Utilities.dp(18), 0); - } else if (messageType == 1 || messageType == 9) { - if (selected) { - holder.messageLayout.setBackgroundResource(R.drawable.msg_in_selected); - } else { - holder.messageLayout.setBackgroundResource(R.drawable.msg_in); - } - holder.messageLayout.setPadding(Utilities.dp(19), Utilities.dp(7), Utilities.dp(9), 0); - } else if (messageType == 12) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out); - } - holder.chatBubbleView.setPadding(Utilities.dp(6), Utilities.dp(6), Utilities.dp(18), 0); - } else if (messageType == 13) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in); - } - holder.chatBubbleView.setPadding(Utilities.dp(15), Utilities.dp(6), Utilities.dp(9), 0); - } else if (messageType == 16) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out); - } - holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), 0); - } else if (messageType == 17) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in); - } - holder.chatBubbleView.setPadding(Utilities.dp(18), Utilities.dp(9), Utilities.dp(9), 0); - } + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_photo); } + } else if (messageType == 3 || messageType == 5 || messageType == 7) { + if (selected) { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_photo_selected); + } else { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_photo); + } + } else if (messageType == 0 || messageType == 8) { + if (selected) { + holder.messageLayout.setBackgroundResource(R.drawable.msg_out_selected); + } else { + holder.messageLayout.setBackgroundResource(R.drawable.msg_out); + } + holder.messageLayout.setPadding(Utilities.dp(11), Utilities.dp(7), Utilities.dp(18), 0); + } else if (messageType == 1 || messageType == 9) { + if (selected) { + holder.messageLayout.setBackgroundResource(R.drawable.msg_in_selected); + } else { + holder.messageLayout.setBackgroundResource(R.drawable.msg_in); + } + holder.messageLayout.setPadding(Utilities.dp(19), Utilities.dp(7), Utilities.dp(9), 0); + } else if (messageType == 12) { + if (selected) { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_selected); + } else { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out); + } + holder.chatBubbleView.setPadding(Utilities.dp(6), Utilities.dp(6), Utilities.dp(18), 0); + } else if (messageType == 13) { + if (selected) { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_selected); + } else { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in); + } + holder.chatBubbleView.setPadding(Utilities.dp(15), Utilities.dp(6), Utilities.dp(9), 0); + } else if (messageType == 16) { + if (selected) { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_selected); + } else { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out); + } + holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), 0); + } else if (messageType == 17) { + if (selected) { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_selected); + } else { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in); + } + holder.chatBubbleView.setPadding(Utilities.dp(18), Utilities.dp(9), Utilities.dp(9), 0); + } else if (messageType == 18) { + if (selected) { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_selected); + } else { + holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out); + } + holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), Utilities.dp(6)); } } } @@ -2952,6 +3071,14 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { view = li.inflate(R.layout.chat_incoming_document_layout, viewGroup, false); } + } else if (type == 18) { + view = li.inflate(R.layout.chat_outgoing_audio_layout, viewGroup, false); + } else if (type == 19) { + if (currentChat != null) { + view = li.inflate(R.layout.chat_group_incoming_document_layout, viewGroup, false); + } else { + view = li.inflate(R.layout.chat_incoming_document_layout, viewGroup, false); + } } } @@ -2975,89 +3102,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa } else { view.setBackgroundColor(0); } - int messageType = holder.message.type; - if (!disableSelection) { - if (messageType == 2 || messageType == 4 || messageType == 6) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_photo_states); - } else if (messageType == 3 || messageType == 5 || messageType == 7) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_photo_states); - } else if (messageType == 0 || messageType == 8) { - holder.messageLayout.setBackgroundResource(R.drawable.chat_outgoing_text_states); - holder.messageLayout.setPadding(Utilities.dp(11), Utilities.dp(7), Utilities.dp(18), 0); - } else if (messageType == 1 || messageType == 9) { - holder.messageLayout.setBackgroundResource(R.drawable.chat_incoming_text_states); - holder.messageLayout.setPadding(Utilities.dp(19), Utilities.dp(7), Utilities.dp(9), 0); - } else if (messageType == 12) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_text_states); - holder.chatBubbleView.setPadding(Utilities.dp(6), Utilities.dp(6), Utilities.dp(18), 0); - } else if (messageType == 13) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_text_states); - holder.chatBubbleView.setPadding(Utilities.dp(15), Utilities.dp(6), Utilities.dp(9), 0); - } else if (messageType == 16) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_outgoing_text_states); - holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), 0); - } else if (messageType == 17) { - holder.chatBubbleView.setBackgroundResource(R.drawable.chat_incoming_text_states); - holder.chatBubbleView.setPadding(Utilities.dp(18), Utilities.dp(9), Utilities.dp(9), 0); - } - } else { - if (messageType == 2 || messageType == 4 || messageType == 6) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_photo_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_photo); - } - } else if (messageType == 3 || messageType == 5 || messageType == 7) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_photo_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_photo); - } - } else if (messageType == 0 || messageType == 8) { - if (selected) { - holder.messageLayout.setBackgroundResource(R.drawable.msg_out_selected); - } else { - holder.messageLayout.setBackgroundResource(R.drawable.msg_out); - } - holder.messageLayout.setPadding(Utilities.dp(11), Utilities.dp(7), Utilities.dp(18), 0); - } else if (messageType == 1 || messageType == 9) { - if (selected) { - holder.messageLayout.setBackgroundResource(R.drawable.msg_in_selected); - } else { - holder.messageLayout.setBackgroundResource(R.drawable.msg_in); - } - holder.messageLayout.setPadding(Utilities.dp(19), Utilities.dp(7), Utilities.dp(9), 0); - } else if (messageType == 12) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out); - } - holder.chatBubbleView.setPadding(Utilities.dp(6), Utilities.dp(6), Utilities.dp(18), 0); - } else if (messageType == 13) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in); - } - holder.chatBubbleView.setPadding(Utilities.dp(15), Utilities.dp(6), Utilities.dp(9), 0); - } else if (messageType == 16) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_out); - } - holder.chatBubbleView.setPadding(Utilities.dp(9), Utilities.dp(9), Utilities.dp(18), 0); - } else if (messageType == 17) { - if (selected) { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in_selected); - } else { - holder.chatBubbleView.setBackgroundResource(R.drawable.msg_in); - } - holder.chatBubbleView.setPadding(Utilities.dp(18), Utilities.dp(9), Utilities.dp(9), 0); - } - } - + updateRowBackground(holder, disableSelection, selected); holder.update(); return view; @@ -3081,7 +3126,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa @Override public int getViewTypeCount() { - return 18; + return 20; } @Override @@ -3673,7 +3718,7 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa processRowSelect(view); return; } - if (message.messageOwner.media.user_id != UserConfig.clientUserId) { + if (message.messageOwner.media.user_id != UserConfig.clientUserId && message.messageOwner.media.user_id != 0) { UserProfileActivity fragment = new UserProfileActivity(); Bundle args = new Bundle(); args.putInt("user_id", message.messageOwner.media.user_id); @@ -3738,9 +3783,9 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa if (file != null) { loadingFile.remove(file); if (message.type == 6 || message.type == 7) { - FileLoader.Instance.cancelLoadFile(message.messageOwner.media.video, null, null); + FileLoader.Instance.cancelLoadFile(message.messageOwner.media.video, null, null, null); } else if (message.type == 16 || message.type == 17) { - FileLoader.Instance.cancelLoadFile(null, null, message.messageOwner.media.document); + FileLoader.Instance.cancelLoadFile(null, null, message.messageOwner.media.document, null); } updateVisibleRows(); } @@ -3899,9 +3944,9 @@ public class ChatActivity extends BaseFragment implements SizeNotifierRelativeLa progressByTag.put((Integer)actionProgress.getTag(), fileName); addToLoadingFile(fileName, actionProgress); if (message.type == 6 || message.type == 7) { - FileLoader.Instance.loadFile(message.messageOwner.media.video, null, null); + FileLoader.Instance.loadFile(message.messageOwner.media.video, null, null, null); } else if (message.type == 16 || message.type == 17) { - FileLoader.Instance.loadFile(null, null, message.messageOwner.media.document); + FileLoader.Instance.loadFile(null, null, message.messageOwner.media.document, null); } updateVisibleRows(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java index 7d9742281..765cca3c7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java @@ -8,10 +8,12 @@ package org.telegram.ui; +import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.support.v4.internal.view.SupportMenuItem; @@ -30,12 +32,15 @@ import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; +import org.telegram.TL.TLObject; import org.telegram.TL.TLRPC; +import org.telegram.messenger.ConnectionsManager; import org.telegram.messenger.ContactsController; import org.telegram.messenger.FileLog; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; +import org.telegram.messenger.RPCRequest; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.ui.Cells.ChatOrUserCell; @@ -48,6 +53,7 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; +import java.util.Locale; import java.util.Timer; import java.util.TimerTask; @@ -70,6 +76,8 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter private SupportMenuItem searchItem; private Timer searchDialogsTimer; + private String inviteText; + private boolean updatingInviteText = false; public ArrayList searchResult; public ArrayList searchResultNames; public ContactsActivityDelegate delegate; @@ -95,6 +103,15 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter ignoreUsers = (HashMap)NotificationCenter.Instance.getFromMemCache(7); } } + + + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); + inviteText = preferences.getString("invitetext", null); + int time = preferences.getInt("invitetexttime", 0); + if (inviteText == null || time + 86400 < (int)(System.currentTimeMillis() / 1000)) { + updateInviteText(); + } + return true; } @@ -165,14 +182,20 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter } else { int section = listViewAdapter.getSectionForPosition(i); int row = listViewAdapter.getPositionInSectionForPosition(i); + if (row < 0 || section < 0) { + return; + } TLRPC.User user = null; if (usersAsSections) { if (section < ContactsController.Instance.sortedUsersSectionsArray.size()) { ArrayList arr = ContactsController.Instance.usersSectionsDict.get(ContactsController.Instance.sortedUsersSectionsArray.get(section)); - if (row >= arr.size()) { + if (row < arr.size()) { + TLRPC.TL_contact contact = arr.get(row); + user = MessagesController.Instance.users.get(contact.user_id); + } else { return; } - user = MessagesController.Instance.users.get(arr.get(row).user_id); + } } else { if (section == 0) { @@ -180,7 +203,7 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter try { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_TEXT, getStringEntry(R.string.InviteText)); + intent.putExtra(Intent.EXTRA_TEXT, inviteText != null ? inviteText : getStringEntry(R.string.InviteText)); startActivity(intent); } catch (Exception e) { FileLog.e("tmessages", e); @@ -554,6 +577,41 @@ public class ContactsActivity extends BaseFragment implements NotificationCenter } } + private void updateInviteText() { + if (updatingInviteText) { + return; + } + updatingInviteText = true; + TLRPC.TL_help_getInviteText req = new TLRPC.TL_help_getInviteText(); + req.lang_code = Locale.getDefault().getCountry(); + if (req.lang_code == null || req.lang_code.length() == 0) { + req.lang_code = "en"; + } + ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { + @Override + public void run(TLObject response, TLRPC.TL_error error) { + if (error != null) { + return; + } + final TLRPC.TL_help_inviteText res = (TLRPC.TL_help_inviteText)response; + if (res.message.length() == 0) { + return; + } + Utilities.RunOnUIThread(new Runnable() { + @Override + public void run() { + updatingInviteText = false; + SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString("invitetext", res.message); + editor.putInt("invitetexttime", (int) (System.currentTimeMillis() / 1000)); + editor.commit(); + } + }); + } + }, null, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors); + } + private void updateVisibleRows(int mask) { if (listView == null) { return; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java index 14e1f65b0..9c1f4a3a8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GalleryImageViewer.java @@ -67,6 +67,7 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif private String currentFileName; private int user_id = 0; private Point displaySize = new Point(); + private boolean cancelRunning = false; private ArrayList imagesArrTemp = new ArrayList(); private HashMap imagesByIdsTemp = new HashMap(); @@ -224,7 +225,7 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif deleteButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - if (mViewPager == null) { + if (mViewPager == null || localPagerAdapter == null || localPagerAdapter.imagesArr == null) { return; } int item = mViewPager.getCurrentItem(); @@ -645,9 +646,20 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif } } + @Override + public void onBackPressed() { + super.onBackPressed(); + cancelRunning = true; + mViewPager.setAdapter(null); + localPagerAdapter = null; + finish(); + System.gc(); + } + private void processSelectedMenu(int itemId) { switch (itemId) { case android.R.id.home: + cancelRunning = true; mViewPager.setAdapter(null); localPagerAdapter = null; finish(); @@ -958,9 +970,9 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif } if (loadFile) { if (!FileLoader.Instance.isLoadingFile(fileName)) { - FileLoader.Instance.loadFile(message.messageOwner.media.video, null, null); + FileLoader.Instance.loadFile(message.messageOwner.media.video, null, null, null); } else { - FileLoader.Instance.cancelLoadFile(message.messageOwner.media.video, null, null); + FileLoader.Instance.cancelLoadFile(message.messageOwner.media.video, null, null, null); } checkCurrentFile(); processViews(playButton, message); @@ -988,7 +1000,9 @@ public class GalleryImageViewer extends AbstractGalleryActivity implements Notif public void destroyItem(View collection, int position, Object view) { ((ViewPager)collection).removeView((View)view); PZSImageView iv = (PZSImageView)((View)view).findViewById(R.id.page_image); - FileLoader.Instance.cancelLoadingForImageView(iv); + if (cancelRunning) { + FileLoader.Instance.cancelLoadingForImageView(iv); + } iv.clearImage(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index c7acff8b4..65a32b3a3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -54,7 +54,7 @@ public class LaunchActivity extends PausableActivity { } String path = null; if (parcelable instanceof Uri) { - path = Utilities.getPath(this, (Uri)parcelable); + path = Utilities.getPath((Uri)parcelable); } else { path = intent.getParcelableExtra(Intent.EXTRA_STREAM).toString(); if (path.startsWith("content:")) { @@ -79,7 +79,7 @@ public class LaunchActivity extends PausableActivity { } String path = null; if (parcelable instanceof Uri) { - path = Utilities.getPath(this, (Uri)parcelable); + path = Utilities.getPath((Uri)parcelable); } else { path = parcelable.toString(); if (path.startsWith("content:")) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityPhoneView.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityPhoneView.java index 5bde3e5a7..c7b0cfa37 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityPhoneView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivityPhoneView.java @@ -253,7 +253,9 @@ public class LoginActivityPhoneView extends SlideView implements AdapterView.OnI public void selectCountry(String name) { int index = countriesArray.indexOf(name); if (index != -1) { + ignoreOnTextChange = true; codeField.setText(countriesMap.get(name)); + countryButton.setText(name); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java new file mode 100644 index 000000000..073e425e2 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoCropActivity.java @@ -0,0 +1,381 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013. + */ + +package org.telegram.ui; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.drawable.BitmapDrawable; +import android.os.Bundle; +import android.support.v7.app.ActionBar; +import android.util.AttributeSet; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.FrameLayout; + +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Views.BaseFragment; + +import java.io.File; + +public class PhotoCropActivity extends BaseFragment { + + public interface PhotoCropActivityDelegate { + public abstract void didFinishCrop(Bitmap bitmap); + } + + private class PhotoCropView extends FrameLayout { + + Paint rectPaint = null; + Paint circlePaint = null; + Paint halfPaint = null; + float rectSize = 600; + float rectX = -1, rectY = -1; + int draggingState = 0; + float oldX = 0, oldY = 0; + int bitmapWidth, bitmapHeight, bitmapX, bitmapY; + int viewWidth, viewHeight; + + public PhotoCropView(Context context) { + super(context); + init(); + } + + public PhotoCropView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public PhotoCropView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + rectPaint = new Paint(); + rectPaint.setColor(0xfffafafa); + rectPaint.setStrokeWidth(Utilities.dp(2)); + rectPaint.setStyle(Paint.Style.STROKE); + circlePaint = new Paint(); + circlePaint.setColor(0x7fffffff); + halfPaint = new Paint(); + halfPaint.setColor(0x3f000000); + setBackgroundColor(0xff000000); + + setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + float x = motionEvent.getX(); + float y = motionEvent.getY(); + int cornerSide = Utilities.dp(14); + if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { + if (rectX - cornerSide < x && rectX + cornerSide > x && rectY - cornerSide < y && rectY + cornerSide > y) { + draggingState = 1; + } else if (rectX - cornerSide + rectSize < x && rectX + cornerSide + rectSize > x && rectY - cornerSide < y && rectY + cornerSide > y) { + draggingState = 2; + } else if (rectX - cornerSide < x && rectX + cornerSide > x && rectY - cornerSide + rectSize < y && rectY + cornerSide + rectSize > y) { + draggingState = 3; + } else if (rectX - cornerSide + rectSize < x && rectX + cornerSide + rectSize > x && rectY - cornerSide + rectSize < y && rectY + cornerSide + rectSize > y) { + draggingState = 4; + } else if (rectX < x && rectX + rectSize > x && rectY < y && rectY + rectSize > y) { + draggingState = 5; + } else { + draggingState = 0; + } + oldX = x; + oldY = y; + } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) { + draggingState = 0; + } else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE && draggingState != 0) { + float diffX = x - oldX; + float diffY = y - oldY; + if (draggingState == 5) { + rectX += diffX; + rectY += diffY; + + if (rectX < bitmapX) { + rectX = bitmapX; + } else if (rectX + rectSize > bitmapX + bitmapWidth) { + rectX = bitmapX + bitmapWidth - rectSize; + } + if (rectY < bitmapY) { + rectY = bitmapY; + } else if (rectY + rectSize > bitmapY + bitmapHeight) { + rectY = bitmapY + bitmapHeight - rectSize; + } + } else if (draggingState == 1) { + if (rectSize - diffX < 160) { + diffX = rectSize - 160; + } + if (rectX + diffX < bitmapX) { + diffX = bitmapX - rectX; + } + if (rectY + diffX < bitmapY) { + diffX = bitmapY - rectY; + } + rectX += diffX; + rectY += diffX; + rectSize -= diffX; + } else if (draggingState == 2) { + if (rectSize + diffX < 160) { + diffX = -(rectSize - 160); + } + if (rectX + rectSize + diffX > bitmapX + bitmapWidth) { + diffX = bitmapX + bitmapWidth - rectX - rectSize; + } + if (rectY - diffX < bitmapY) { + diffX = rectY - bitmapY; + } + rectY -= diffX; + rectSize += diffX; + } else if (draggingState == 3) { + if (rectSize - diffX < 160) { + diffX = rectSize - 160; + } + if (rectX + diffX < bitmapX) { + diffX = bitmapX - rectX; + } + if (rectY + rectSize - diffX > bitmapY + bitmapHeight) { + diffX = rectY + rectSize - bitmapY - bitmapHeight; + } + rectX += diffX; + rectSize -= diffX; + } else if (draggingState == 4) { + if (rectX + rectSize + diffX > bitmapX + bitmapWidth) { + diffX = bitmapX + bitmapWidth - rectX - rectSize; + } + if (rectY + rectSize + diffX > bitmapY + bitmapHeight) { + diffX = bitmapY + bitmapHeight - rectY - rectSize; + } + rectSize += diffX; + if (rectSize < 160) { + rectSize = 160; + } + } + + oldX = x; + oldY = y; + invalidate(); + } + return true; + } + }); + } + + private void updateBitmapSize() { + if (viewWidth == 0 || viewHeight == 0) { + return; + } + float percX = (rectX - bitmapX) / bitmapWidth; + float percY = (rectY - bitmapY) / bitmapHeight; + float percSize = rectSize / bitmapWidth; + float w = imageToCrop.getWidth(); + float h = imageToCrop.getHeight(); + float scaleX = viewWidth / w; + float scaleY = viewHeight / h; + if (scaleX > scaleY) { + bitmapHeight = viewHeight; + bitmapWidth = (int)Math.ceil(w * scaleY); + } else { + bitmapWidth = viewWidth; + bitmapHeight = (int)Math.ceil(h * scaleX); + } + bitmapX = (viewWidth - bitmapWidth) / 2; + bitmapY = (viewHeight - bitmapHeight) / 2; + + if (rectX == -1 && rectY == -1) { + if (bitmapWidth > bitmapHeight) { + rectY = bitmapY; + rectX = (viewWidth - bitmapHeight) / 2; + rectSize = bitmapHeight; + } else { + rectX = bitmapX; + rectY = (viewHeight - bitmapWidth) / 2; + rectSize = bitmapWidth; + } + } else { + rectX = percX * bitmapWidth + bitmapX; + rectY = percY * bitmapHeight + bitmapY; + rectSize = percSize * bitmapWidth; + } + invalidate(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + viewWidth = right - left; + viewHeight = bottom - top; + updateBitmapSize(); + } + + public Bitmap getBitmap() { + float percX = (rectX - bitmapX) / bitmapWidth; + float percY = (rectY - bitmapY) / bitmapHeight; + float percSize = rectSize / bitmapWidth; + int x = (int)(percX * imageToCrop.getWidth()); + int y = (int)(percY * imageToCrop.getHeight()); + int size = (int)(percSize * imageToCrop.getWidth()); + try { + return Bitmap.createBitmap(imageToCrop, x, y, size, size); + } catch (Exception e) { + FileLog.e("tmessags", e); + System.gc(); + try { + return Bitmap.createBitmap(imageToCrop, x, y, size, size); + } catch (Exception e2) { + FileLog.e("tmessages", e2); + } + } + return null; + } + + @Override + protected void onDraw(Canvas canvas) { + if (drawable != null) { + drawable.setBounds(bitmapX, bitmapY, bitmapX + bitmapWidth, bitmapY + bitmapHeight); + drawable.draw(canvas); + } + canvas.drawRect(bitmapX, bitmapY, bitmapX + bitmapWidth, rectY, halfPaint); + canvas.drawRect(bitmapX, rectY, rectX, rectY + rectSize, halfPaint); + canvas.drawRect(rectX + rectSize, rectY, bitmapX + bitmapWidth, rectY + rectSize, halfPaint); + canvas.drawRect(bitmapX, rectY + rectSize, bitmapX + bitmapWidth, bitmapY + bitmapHeight, halfPaint); + + canvas.drawRect(rectX, rectY, rectX + rectSize, rectY + rectSize, rectPaint); + + int side = Utilities.dp(7); + canvas.drawRect(rectX - side, rectY - side, rectX + side, rectY + side, circlePaint); + canvas.drawRect(rectX + rectSize - side, rectY - side, rectX + rectSize + side, rectY + side, circlePaint); + canvas.drawRect(rectX - side, rectY + rectSize - side, rectX + side, rectY + rectSize + side, circlePaint); + canvas.drawRect(rectX + rectSize - side, rectY + rectSize - side, rectX + rectSize + side, rectY + rectSize + side, circlePaint); + } + } + + private Bitmap imageToCrop; + private BitmapDrawable drawable; + public PhotoCropActivityDelegate delegate = null; + private PhotoCropView view; + private boolean sameBitmap = false; + private boolean doneButtonPressed = false; + + @Override + public boolean onFragmentCreate() { + super.onFragmentCreate(); + String photoPath = getArguments().getString("photoPath"); + if (photoPath == null) { + return false; + } + File f = new File(photoPath); + if (!f.exists()) { + return false; + } + Point displaySize = new Point(); + Display display = ((WindowManager)ApplicationLoader.applicationContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + if(android.os.Build.VERSION.SDK_INT < 13) { + displaySize.set(display.getWidth(), display.getHeight()); + } else { + display.getSize(displaySize); + } + int size = Math.max(displaySize.x, displaySize.y); + imageToCrop = FileLoader.loadBitmap(photoPath, size, size); + if (imageToCrop == null) { + return false; + } + drawable = new BitmapDrawable(imageToCrop); + return true; + } + + @Override + public void onFragmentDestroy() { + super.onFragmentDestroy(); + drawable = null; + if (imageToCrop != null && !sameBitmap) { + imageToCrop.recycle(); + imageToCrop = null; + } + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + if (fragmentView == null) { + fragmentView = view = new PhotoCropView(this.getActivity()); + fragmentView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); + } else { + ViewGroup parent = (ViewGroup)fragmentView.getParent(); + if (parent != null) { + parent.removeView(fragmentView); + } + } + return fragmentView; + } + + @Override + public boolean canApplyUpdateStatus() { + return false; + } + + @Override + public void applySelfActionBar() { + if (parentActivity == null) { + return; + } + ActionBar actionBar = parentActivity.getSupportActionBar(); + actionBar.setDisplayShowCustomEnabled(true); + actionBar.setDisplayShowHomeEnabled(false); + actionBar.setDisplayShowTitleEnabled(false); + actionBar.setDisplayHomeAsUpEnabled(false); + + actionBar.setCustomView(R.layout.settings_do_action_layout); + View cancelButton = actionBar.getCustomView().findViewById(R.id.cancel_button); + cancelButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + finishFragment(); + } + }); + View doneButton = actionBar.getCustomView().findViewById(R.id.done_button); + doneButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (delegate != null && !doneButtonPressed) { + Bitmap bitmap = view.getBitmap(); + if (bitmap == imageToCrop) { + sameBitmap = true; + } + delegate.didFinishCrop(bitmap); + doneButtonPressed = true; + } + finishFragment(); + } + }); + } + + @Override + public void onResume() { + super.onResume(); + if (getActivity() == null) { + return; + } + ((ApplicationActivity)parentActivity).updateActionBar(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java index f793dd5ef..be4678a69 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SettingsWallpapersActivity.java @@ -246,11 +246,11 @@ public class SettingsWallpapersActivity extends BaseFragment implements Notifica progressBar.setVisibility(View.VISIBLE); loadingSize = size; selectedColor = 0; - FileLoader.Instance.loadFile(null, size, null); + FileLoader.Instance.loadFile(null, size, null, null); backgroundImage.setBackgroundColor(0); } else { if (loadingFile != null) { - FileLoader.Instance.cancelLoadFile(null, loadingSize, null); + FileLoader.Instance.cancelLoadFile(null, loadingSize, null, null); } loadingFileObject = null; loadingFile = null; @@ -263,7 +263,7 @@ public class SettingsWallpapersActivity extends BaseFragment implements Notifica } } else { if (loadingFile != null) { - FileLoader.Instance.cancelLoadFile(null, loadingSize, null); + FileLoader.Instance.cancelLoadFile(null, loadingSize, null, null); } if (selectedBackground == 1000001) { backgroundImage.setImageResource(R.drawable.background_hd); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java index 279b95569..f81cc5ab1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/UserProfileActivity.java @@ -78,7 +78,7 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen if (dialog_id != 0) { currentEncryptedChat = MessagesController.Instance.encryptedChats.get((int)(dialog_id >> 32)); } - return true; + return MessagesController.Instance.users.get(user_id) != null; } @Override @@ -471,15 +471,9 @@ public class UserProfileActivity extends BaseFragment implements NotificationCen builder.setPositiveButton(getStringEntry(R.string.OK), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { - TLRPC.TL_auth_resetAuthorizations req = new TLRPC.TL_auth_resetAuthorizations(); - ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() { - @Override - public void run(TLObject response, TLRPC.TL_error error) { - ArrayList arrayList = new ArrayList(); - arrayList.add(user); - ContactsController.Instance.deleteContact(arrayList); - } - }, null, true, RPCRequest.RPCRequestClassGeneric); + ArrayList arrayList = new ArrayList(); + arrayList.add(user); + ContactsController.Instance.deleteContact(arrayList); } }); builder.setNegativeButton(getStringEntry(R.string.Cancel), null); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java index 04b67a591..3f576e371 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/AvatarUpdater.java @@ -10,11 +10,10 @@ package org.telegram.ui.Views; import android.app.Activity; import android.content.Intent; -import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; +import android.os.Bundle; import android.provider.MediaStore; -import android.support.v4.app.Fragment; import org.telegram.TL.TLRPC; import org.telegram.messenger.FileLoader; @@ -22,17 +21,19 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; +import org.telegram.ui.ApplicationActivity; +import org.telegram.ui.PhotoCropActivity; import java.io.File; -public class AvatarUpdater implements NotificationCenter.NotificationCenterDelegate { +public class AvatarUpdater implements NotificationCenter.NotificationCenterDelegate, PhotoCropActivity.PhotoCropActivityDelegate { public String currentPicturePath; private TLRPC.PhotoSize smallPhoto; private TLRPC.PhotoSize bigPhoto; public String uploadingAvatar = null; File picturePath = null; public Activity parentActivity = null; - public Fragment parentFragment = null; + public BaseFragment parentFragment = null; public AvatarUpdaterDelegate delegate; private boolean clearAfterUpdate = false; public boolean returnOnly = false; @@ -85,22 +86,38 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg private void startCrop(String path) { try { - Intent cropIntent = new Intent("com.android.camera.action.CROP"); - cropIntent.setDataAndType(Uri.fromFile(new File(path)), "image/*"); - cropIntent.putExtra("crop", "true"); - cropIntent.putExtra("aspectX", 1); - cropIntent.putExtra("aspectY", 1); - cropIntent.putExtra("outputX", 800); - cropIntent.putExtra("outputY", 800); - cropIntent.putExtra("scale", true); - cropIntent.putExtra("return-data", false); - picturePath = Utilities.generatePicturePath(); - cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(picturePath)); - cropIntent.putExtra("output", Uri.fromFile(picturePath)); if (parentFragment != null) { - parentFragment.startActivityForResult(cropIntent, 2); - } else if (parentActivity != null) { - parentActivity.startActivityForResult(cropIntent, 2); + ApplicationActivity activity = (ApplicationActivity)parentFragment.parentActivity; + if (activity == null) { + activity = (ApplicationActivity)parentFragment.getActivity(); + } + if (activity == null) { + return; + } + Bundle params = new Bundle(); + params.putString("photoPath", path); + PhotoCropActivity photoCropActivity = new PhotoCropActivity(); + photoCropActivity.delegate = this; + photoCropActivity.setArguments(params); + activity.presentFragment(photoCropActivity, "crop", false); + } else { + Intent cropIntent = new Intent("com.android.camera.action.CROP"); + cropIntent.setDataAndType(Uri.fromFile(new File(path)), "image/*"); + cropIntent.putExtra("crop", "true"); + cropIntent.putExtra("aspectX", 1); + cropIntent.putExtra("aspectY", 1); + cropIntent.putExtra("outputX", 800); + cropIntent.putExtra("outputY", 800); + cropIntent.putExtra("scale", true); + cropIntent.putExtra("return-data", false); + picturePath = Utilities.generatePicturePath(); + cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(picturePath)); + cropIntent.putExtra("output", Uri.fromFile(picturePath)); + if (parentFragment != null) { + parentFragment.startActivityForResult(cropIntent, 2); + } else if (parentActivity != null) { + parentActivity.startActivityForResult(cropIntent, 2); + } } } catch (Exception e) { FileLog.e("tmessages", e); @@ -120,26 +137,15 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg if (data == null) { return; } - Uri imageUri = data.getData(); - Cursor cursor = null; try { - if (parentFragment != null) { - cursor = parentFragment.getActivity().getContentResolver().query(imageUri, new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null); - } else if (parentActivity != null) { - cursor = parentActivity.getContentResolver().query(imageUri, new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null); + Uri imageUri = data.getData(); + if (imageUri != null) { + String imageFilePath = Utilities.getPath(imageUri); + startCrop(imageFilePath); } } catch (Exception e) { FileLog.e("tmessages", e); - return; } - if (cursor == null) { - return; - } - if (cursor.moveToFirst()) { - String imageFilePath = cursor.getString(0); - startCrop(imageFilePath); - } - cursor.close(); } else if (requestCode == 2) { Bitmap bitmap = FileLoader.loadBitmap(picturePath.getAbsolutePath(), 800, 800); processBitmap(bitmap); @@ -168,6 +174,11 @@ public class AvatarUpdater implements NotificationCenter.NotificationCenterDeleg } } + @Override + public void didFinishCrop(Bitmap bitmap) { + processBitmap(bitmap); + } + @Override public void didReceivedNotification(int id, final Object... args) { if (id == FileLoader.FileDidUpload) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/LayoutListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/LayoutListView.java index 9c6636634..fa037f109 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Views/LayoutListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/LayoutListView.java @@ -40,11 +40,19 @@ public class LayoutListView extends ListView { post(new Runnable() { @Override public void run() { - setSelectionFromTop(scrollTo, offset - getPaddingTop()); + try { + setSelectionFromTop(scrollTo, offset - getPaddingTop()); + } catch (Exception e) { + e.printStackTrace(); + } } }); } else { - super.onLayout(changed, left, top, right, bottom); + try { + super.onLayout(changed, left, top, right, bottom); + } catch (Exception e) { + e.printStackTrace(); + } } height = (bottom - top); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Views/SeekBar.java b/TMessagesProj/src/main/java/org/telegram/ui/Views/SeekBar.java new file mode 100644 index 000000000..0d55da6ac --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Views/SeekBar.java @@ -0,0 +1,135 @@ +/* + * This is the source code of Telegram for Android v. 1.3.x. + * It is licensed under GNU GPL v. 2 or later. + * You should have received a copy of the license in this archive (see LICENSE). + * + * Copyright Nikolai Kudashov, 2013. + */ + +package org.telegram.ui.Views; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; + +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; + +public class SeekBar extends View { + Drawable thumbDrawable1; + Drawable thumbDrawablePressed1; + Drawable thumbDrawable2; + Drawable thumbDrawablePressed2; + static Paint innerPaint1 = new Paint(); + static Paint outerPaint1 = new Paint(); + static Paint innerPaint2 = new Paint(); + static Paint outerPaint2 = new Paint(); + public int type; + public int thumbX = 0; + public int thumbDX = 0; + private boolean pressed = false; + private boolean dragging = false; + private int thumbWidth; + private int thumbHeight; + + public SeekBar(Context context) { + super(context); + init(); + } + + public SeekBar(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public SeekBar(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + private void init() { + if (thumbDrawable1 == null) { + thumbDrawable1 = getResources().getDrawable(R.drawable.player1); + thumbDrawablePressed1 = getResources().getDrawable(R.drawable.player1_pressed); + thumbDrawable2 = getResources().getDrawable(R.drawable.player2); + thumbDrawablePressed2 = getResources().getDrawable(R.drawable.player2_pressed); + innerPaint1.setColor(0xffb4e396); + outerPaint1.setColor(0xff6ac453); + innerPaint2.setColor(0xffd9e2eb); + outerPaint2.setColor(0xff86c5f8); + thumbWidth = thumbDrawable1.getIntrinsicWidth(); + thumbHeight = thumbDrawable1.getIntrinsicHeight(); + } + + setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + float x = motionEvent.getX(); + float y = motionEvent.getY(); + if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) { + int additionWidth = (getMeasuredHeight() - thumbWidth) / 2; + if (thumbX - additionWidth <= x && x <= thumbX + thumbWidth + additionWidth && y >= 0 && y <= getMeasuredHeight()) { + pressed = true; + thumbDX = (int)(x - thumbX); + invalidate(); + getParent().requestDisallowInterceptTouchEvent(true); + return true; + } + } else if (motionEvent.getAction() == MotionEvent.ACTION_UP || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) { + if (pressed) { + pressed = false; + invalidate(); + return true; + } + } else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) { + if (pressed) { + thumbX = (int)(x - thumbDX); + if (thumbX < 0) { + thumbX = 0; + } else if (thumbX > getMeasuredWidth() - thumbWidth) { + thumbX = getMeasuredWidth() - thumbWidth; + } + invalidate(); + return true; + } + } + return false; + } + }); + } + + @Override + protected void onDraw(Canvas canvas) { + Drawable thumb = null; + Paint inner = null; + Paint outer = null; + if (type == 0) { + if (!pressed) { + thumb = thumbDrawable1; + } else { + thumb = thumbDrawablePressed1; + } + inner = innerPaint1; + outer = outerPaint1; + } else if (type == 1) { + if (!pressed) { + thumb = thumbDrawable2; + } else { + thumb = thumbDrawablePressed2; + } + inner = innerPaint2; + outer = outerPaint2; + } + int height = getMeasuredHeight(); + int width = getMeasuredWidth(); + int y = (height - thumbHeight) / 2; + canvas.drawRect(thumbWidth / 2, height / 2 - Utilities.dp(1), width - thumbWidth / 2, height / 2 + Utilities.dp(1), inner); + canvas.drawRect(thumbWidth / 2, height / 2 - Utilities.dp(1), thumbWidth / 2 + thumbX, height / 2 + Utilities.dp(1), outer); + thumb.setBounds(thumbX, y, thumbX + thumbWidth, y + thumbHeight); + thumb.draw(canvas); + } +} diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_send.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_send.png index da37f80332643da060d9ec4b60b7749b39e130b8..50e63ae1ec05da8c71fb04c6e925ca40575891a2 100755 GIT binary patch delta 548 zcmV+<0^9w+1sp(f9CN9>-3*KhC{<`P%Ne_jo?%oaa90p2Xx%d)l(BGF*XcR?JGm zpYR-f0bj%OaRWTVUAit1NCRGoe+%FTc9eFxEawEW7&U-YU2|W8oWZwIfw%xppXc%f zat|-XIRmPu;8OxwHVM#%-+c6wASducSdb+9Uk}iKfrb3tCd#aVGJH+ z)le!8XIS>9;2~Ofzv{qS@PQ5>=qN`h@$%GGrZ%0fgy!9rqt9yYrIfZ|&9gb0A)Q+A zt_rXYul<#^2GpeKugVJ*pcr9rS?D%ln>TWR3`;zdk_E1$``i=C-fvQu@CN;AC6qni zglaTmPo0P?uDPa}>bFbBx89ZMe3w&}azBaWk)U`I2X mf_LLQoC?b38S9_)E5HD$N?*8k;qJWv0000QAA2B?cQu+V_70yM7K*< z-L#}3wwuJRE+Qf#BBJ-@nVgQj2NVpd8UP+n_GsJ^3x*vwh@UfUc7> zHUWc1Q3u_&1!@A&RYn2b2Dni`$Iwe>5Xq(qP!+IlxO`ktj_6)kP!;f*fBa;j&biB1 z0b0N+5DL&l9r}Uhf`aORjwxBxT1$o0#E&72r39_t% zcF{!b_XU&WZq&>m&P`xm?86+%8Z$KU@N`ur=V{t*bpjZs8AfqDg5@TS7huicrTxU) zj|FJ}Xu@OqR--L25IX>Se-Dy=*=A5Z^+=bRPMi9bVjXabd==Avgri&m*iVz{fU_~c zamtSX_M%_(ieOaY`SXBWB(T?ZH|c;g5r8pF5pZvQ8+WNK40M-dt^wYf-G`GC=)Zk~i@gnWQq5wo?retBV*UF-Cbc$T@1v5iv@7)ZYoZ vvLO;6iK(K6+R9@+ON=D8n!_TY`W9dST%n9J&R@_k00000NkvXXu0mjfRgfxr diff --git a/TMessagesProj/src/main/res/drawable-hdpi/ic_send_disabled.png b/TMessagesProj/src/main/res/drawable-hdpi/ic_send_disabled.png index 93b2f1604a8f79afe64e0e4f01fa9e5c6ffae4b7..4473d1c6f36d8a3ce2354d4313981fc9e42166f0 100755 GIT binary patch delta 557 zcmV+|0@D4}1=j>2iBL{Q4GJ0x0000DNk~Le0000W0000W2nGNE0CReJ^pPPqe*x)9 zL_t(|+QgU9M?+B@$M4n^BO*q`h%q8Y#QuPYo_dO&dW!uGQH-poy%Z@@r0HetCE9B* z#WbafrWA3Bh!GK4-ygrr&#|-JyL;~K$JchwJ;(2J&iS3+IVV=BRAwy8DnccwW<_jq z=nI;IE}$!DK5BqxsLkaZgWNz1e^CMaLJg&!mgSs57Q+T0t82a!$Qg7S7KjVb=+iFC zAotKxlrx~(G<3=!%O(Ndpm!hr2;>BM2nrGx|LX(vpJI?lXeB5>Bfvm1(4hbs8U*0l zz>>NkPhLSJz@5S1o)jRf(6ASP56+5~VURsunyw&mrCbUr;kh6$P+o$>e{dRh>o5V0 zi)=`igi<2<6VM2+yI*slE$G1R(ZKP!J7|<+9C%r-D{@`80d7-PwKaR2MxWMvODJ`t znzk99A)U3LT@_#*TI&_H2CT``U#5ZzkPp#0E#)Rum6Z}8B?8Z+QGzSzu6wp}_M5az zXoG*XW-DjD39IplJ*9RDe~=64xXNqn>rgW>17(it<{+JI5r9b_{pccl%#K?@rp*PI z_1OLgI%3DKCIZIxxC6c^Z?j{^bz`3z1zX$z0000;lZe+K&l52u*Yp7No;*+Phyu`Q;##iuO++*_P#5sZ$2~46M|9CPs0&yjzxceji@Xld zC34Cppx-F!psTh(O#r$|DWIzWHwx$&dg&A*d1nGt1uPm)9~YD(x)Bsqe+7IbKUm0f zuJQ$dZekV)1ZW};8-dRI1=Rr^VP8oFbh}U}^u{HEUZDJqza_}Jpsm?#R{hPD_emZi zcZLMoMjmI_a=F|`zD}ppukOhNb<~X>Fv>52oax805%h~)=p$LfhB_YZt`5mL>b9%R z00t?BA?y!gx=G>%1fn(Cf5dZm`7tdG0F8Kb-)gi48e#`vZ+`Mr)@f8vJcdghPMi8Q z#5&*@`7)yQ2uJwFq}`6pHRCwC# zSI6UE3XJF2NTu@F< z7xWWY7v{Fwi$KIeV4@cR8yoy>0nr!e3-kr_7Dm_Xwo`OO=O;)x=btW7+k)raPoTx7 z#`}NWLf~1$(UJoEOpUV77Zw8N8jhM2sAvw3eV}1LE^`hv92F_xvIiMVF+>$52d05H zA95K>Q?8(~85Vb?KnZBlgJ~ek%C-y_1MLHtGjLbzqOb{^dIHjJ+0Cs%Q!g8CtYx?}VBEX;(aZ%Gn5Q=gv)Voc z_8|1CAs&U>1$NlE{2^BWxKRb9tnw$fdO$0g~UulN3uRd+BStHh9A(?G@EZ|Kmh+ oO#VWl-lbC$9qf<5v7Z7A0NW0ksT~_cBme*a07*qoM6N<$g2dVXlK=n! literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/mic_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/mic_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..3cf43e04bd343951dac40e1be5bad746301640fe GIT binary patch literal 581 zcmV-L0=oT)P)+WXIVIO=jzIpHa#y7JwFEm|j87?gWEO1-{nqu&@J>i%% z;(|B{oB+$3{|?v!8cxaK0puB=-iO?vuRe49n(-?jN^%N}ImC#$Q-~z|2;>Klt0H+n z0_%o*Tk~5Yw>If_lik|rUkez4Kp+qZIKKxy<#%Wpq_|ys|Dr@)4?Lev0&VSXm)7(k z@S>DQhNCJ3OvSMYM3lrV@Zmr%=CWvA zS}ae59MVi7PzEeIW`Ud%PytfFg|_Kk`cfdTDE=Hdgjtv?%f4tAIOF(;J|w^<**o2>qDMqjyyp%tiGgw2RlHA<2VxvK+Ite zVjwRtHcnrMT;ErN)|;*Z=fJpDZSK;K>|{eF-oCSiN+H T<;0?Z00000NkvXXu0mjfl6UgJ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause1.png b/TMessagesProj/src/main/res/drawable-hdpi/pause1.png new file mode 100755 index 0000000000000000000000000000000000000000..30e945f3165d96a6a7f2c94331b9d55363e30190 GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^5}1{rUgjo>`Ysn7Nmc z+ekgJRweAc4oA$n4&ONvUroLrT*10ntS8DK3&wELNs#%(tKY!D%-|8rXLz+Zc^1$x N22WQ%mvv4FO#l!xG<5&~ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/pause1_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..0053b7b035afbb7dd2f76da9351bfe7b50a4c305 GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^5}1{rUgjo>`Ysn7Nmc z+vs@lJmIkSIsUhfclgec_-gX~;0o5oVm(m?Sulo^PJ+xQUi}6JW(J#HK0}E;D?S1Z OWAJqKb6Mw<&;$T|_%?w6 literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/pause2.png b/TMessagesProj/src/main/res/drawable-hdpi/pause2.png new file mode 100755 index 0000000000000000000000000000000000000000..f96f878e98c6a2f194a0093690d5177920c8780e GIT binary patch literal 175 zcmeAS@N?(olHy`uVBq!ia0vp^5}1{rUgjo>`Ysn7Nmc z+vs@aE#o!!ZcbM{+TlA#;;YH`gDY4Ui}geqWWg9tItenLc=a0?m>D8!`3w~}1{rUgjo>`Ysn7Nmc z+sJ+ON3F2;F{`hq9Z~b^shagW;Yw2=x0|lSRv1H3%z*D_o47**BZJRfK0}Q!p&3BK O7(8A5T-G@yGywo+=rz3n literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play1.png b/TMessagesProj/src/main/res/drawable-hdpi/play1.png new file mode 100755 index 0000000000000000000000000000000000000000..8bb953bcf48a18b787b5b9674d8974d1c626bac4 GIT binary patch literal 441 zcmV;q0Y?6bP)N~1izyOVpOlbUo;)#vg7Qg^M5Q9?MRUi(eg#|F60>tZp_y7m#FR;RAGV z3W#%ozBxrLAD{y>AU+B7%^V<>poIl6kO0j)3@kJ-d?HFyU#QxOQf z1L7mNn$Lx$`dF})jApE(r6$yrBfOs7c9{^$l zT37%B_CS0di01&Y1R=$Yz>M~vf;jvF#2rA~2h4EaspSJ?zyriBKzzeLSpe1Hy4 z0dX$SH>arO19V^p#3zBinFGWUw6FjM5}ywguOq2JfI|DguFb zK%51{dgz8z>VscEJOxx)0S(4kS&?hOdLS+X;yZ+DG*T_N0K^4Ae305@(F-6>0b)Z^ z3@641pz@^;THbsiCoJ(<-WS|@{C8V$te)G{3A19+JZ4Rfjj ikOivHR6-K~5MTgT1WpdN>XiQg00008UDjMx h)?ih3i5E=-7yx6}OysI+4~+l-002ovPDHLkV1jF@vi1M~ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/play2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/play2_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..08e691986291dbc8fde8c085c4f24c65f6f946b4 GIT binary patch literal 439 zcmV;o0Z9IdP)179|lfq(1APH^kLw% z1CP*x+^mmN9JEn_9uDCe>KwQqtpxwI0$1=FTnzSnB#5dHhotFiC5UPp9!S%IoDT!1 zeK|WO%V`E}ID?NQTP!nap2$=6PPTtqhN4z+m*8)v%bKgo h8sue{c+psZ0RYLJO<=`=d?o+@002ovPDHLkV1l3Gx1<07 literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/player1.png b/TMessagesProj/src/main/res/drawable-hdpi/player1.png new file mode 100755 index 0000000000000000000000000000000000000000..6ec1a5d281f4a1ccf5986f08f79126ec9bef9278 GIT binary patch literal 687 zcmV;g0#N;lP)nA_7PX zO)C_KnxHb2hYHXMR1DK^!V-c$-UoF-%`ZGkZqGr#pkpUMfayl_5vr4f0@TLphqBOl zRe%7Z& z|Ekl)O$#rHmVHJX4Cl2AG2a`1ge%8qy`oz2Tke+xuO~UUuY;y6E%MA&&HPiCM=N_X zr0%CIs1GkXq5R5s{@-Z0JSRfdxQjbO`B3ZRv>Yh4ckDi>7Ug)kqYm3bGcVg%G)72}Cu zegv)Jpc0m#vRt4?nLbLk-y}yOCFu=NxCMsVGs3&S>;!oL7D_&~=nP-~S)_?|>cz7y#P! VxnE#JB>n&Z002ovPDHLkV1jlWF#7-i literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/player1_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/player1_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..f5698eb118e03763c232b830be19d3a847dd8495 GIT binary patch literal 1210 zcmV;r1V#IaP)Nh!Q4v(cKb4Bb4-x!O@S_z( zs-R#M7c5m#5$l2lam9ryYOGb8WoDk|#YuXTChbcz!3T$A-n^MPbKkw^-aC9zLpl4O z<4X18xMODq_bLG<10G;J5Cq-;9Y8Datv@$Smyk5)LZA{D(Z|}Cz*XQ9@WpOI(h@s> z(lp4AwXg}O2hN#Af%kS0+5)WWG0T~sanXj~u+YQ@&X|jtd>9kVhhItDOIEK1sS#beDBkyoC)CzKLg2U{Ezb*4>(pa!N*uY&4;pgM#(UX%qS5XewwyI== zDq*;_g=#UzNyoxL1Hg zMb9we%cRxR%qu>VqfwNQFJ?;4x|D8aBq0-hj=|?D8ks)JM+up6ndm=`D50O-R%E$K zSH|b2c!za}7RBf1k^g>5=q>Sq;fjje2=*;Wgzq90{;FE+^P%ME9S!?dvS^%g8Ym2I zg99&t&&>Q6_7K!OwM5K=jzZK;h1Ov^iv4y<=#!iTWW`s=yPU<)i4RIInICFG-P4q= zuYk}La@d+oshi2-`Pr3e1dgs%l9`*y2%RG#J%)S$5hl0Z{flu4_xl)RErmp+h9vO0^Epa(jy*?&O`W8oX#h)OTT>rrrp>0P4ur zEkP8`vx^MU&4z?1KB!VtHvpH3iL13Ap6ZOYPuFs_AMS0WC6c2SoyT3(HfSF(1ej)7 zY96w9iSQZ?s&dfnz^gtv>6~WGU0S>Dl#gq^Z}sF>eDpK7bfx$pU6UPgd&5rL;z=`usU$AIMPCk zh;vK#j0i!Xr{rix7+eQn_YMUgf(bDxT*^q1A_ctJ&8LG>R9@RpIXB8sS#A_{_dA>vDUb;s0|XF1h9Fc1 zH9&7r0!l$w(5rP10YdzF57YrQ{_v>ClsI$@omv3`Oecl`sG9M(1GPx?Lfg=7QGfs< z0)GhV_PDxD(1e7F839R{F&QhS**qe>yK9p6!$pAkZhbQT^nu`=E(~23N=*o=)dB*H zSjv8j#duKHLn46v77KD46wm?c`GsAgo0Vl)E>U%B*wbFpBt26YmFFEfv`Wns)$kH4 z?E`F>7|l^STNXYoMKknB5v0NbUHb#DK^HEYS4IXLlvznfAg~gJH-J?Fmq%P4v!WH)d=?ZWhPL96ED{qm1LeS zXbSNZ%BH;^UJKF&^X(_8+`gsIsROlWN_K%Z?Eu=zZ(&uH`p_~=M%5dsfJE#0U=b*$ zidCQDBp|+E$5IIpnYE|g4*@Ff*4=rqcPN@IonAUdK#?#~!6OPTW2szYaMj*DHJ?<{WYKu0R{l0x0VEb%Uj6+0000< KMNUMnLSTZPxFB5s literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/player2_pressed.png b/TMessagesProj/src/main/res/drawable-hdpi/player2_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..879278c30e36f4b656072ffc13c8b51372fe7db4 GIT binary patch literal 1250 zcmV<81ReW{P)C?@=zodEEozZk=UX|jTRaZFNqoxW7sppGmrHn86YwhF1zrZyzNvoF)uBaP#drvXa^b!?7aj0 z0{je2m75Tk#3#VoBFMLxcpvBmPFJt2epn7d9|8ydw<@rRD2XGu%ZRf0h#5@Lvw9t_ zL0ma;KiRz>=$v^la}=b0S}dVgfWts5S<6U@bU_Iy*L*Md#_~OA7>1CHBwMwlwh`nu zn9CyY1xU4k)Yti*74mo)C-W^}y8xtc#aHb+;|X~FF2?9mGpXiuJl$dF#%i)W2vXbx z*!69>G7udEEzsLau zgO^H)HG(8)I8d+Tk4E|)tphn9y<34&n7rKP~$e1zJ}h&yMC{%Zf(N~8*|KE*fbW9^!*@A{B*kPnE}g zVw_+*n94TcPDh%($m-aTo5yhI;Y>1fkff#@3ANceekRJoW0du|J0NOCXqz zv9DZ8XqFgv7;0;xgc@yzk~r;(%sAV_vd`3P4^ECt&5ePJ!V@xmkrE1$q?vR@DC0oJ z8Y+9+oy+C?R*(|XbGALlVG)YjnOZjjuAWp_{&eDTXV|C;LS!1DghmOWdRK&2uzn^v z{IJZF?CDL@?FZrZt`f02tJXk5HRf~@ z#r`BE^q4OKb$nAQnrifgeUDs`YG4ESJ5a2xs4?|5G97k`)b`2LxVp8HhRZ9;teR~n zOBbC8owil0>2IZD>4r)XN<2+$1}V#aB2d?PCqnmtTbAoXZ5{k1=42V z_2Q#$YU)1V91(G6Y$DcKiu$0UKy)J7OQ@8Wxo zfy}R^_V&CNziHOo9XW=|3H*0$S>X9N@V)u5Aycp-De{9AuUUBU#YZ^>lFzskoK2NA%h02DU4W7K|OS z3*W>$)ChN!Gk?@ksOwrYgVncKP+&WQq57;Hsv^&s3&jNVn>`I2Gbgm|b-2^*YHaj? z{n65ZGd(#6OcctJ6y#hO3r){37_XS`$*RD-#E~tfAc)T8QJFv+(`pr^l$eSVVpupPEKY2kVufTO> z^{4M0a=4F}1}uNb6yIPW+u_{!Lw^$^Pe5asf*Io@O@|su&v!Zk>Iy5jc ZZ2F=4eAl!ahkzbs@O1TaS?83{1OOlNa4`S? literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/slidearrow.png b/TMessagesProj/src/main/res/drawable-hdpi/slidearrow.png new file mode 100755 index 0000000000000000000000000000000000000000..b04164901d6f00d064edfdbef085baa20db28900 GIT binary patch literal 420 zcmV;V0bBlwP)jNRfzo>bFEuSJ1O+$Nl&S@enC zf*{Zq;Y^`-v`(q*phD*kG^)Rw2B_zOUhO%i0U9V&qD>e22RfNb%{25?Wh|(x&^-Fk zOv9!g%vjJRno~$Jy|i_FI!#kK&|B-AHh`W(=PRXt%K|zU|NX?c$*}qp@c@m{0=lF0 z)exA}Cf9uqv^lLEo#-l)WS_g}3w1)76Zcv<2JpJZ>l`NCXl7~JBD!a>l_03_l?Pr9 zf)>1I!4EdvdLMzsP5VyfrKmpu@CD&eVS$eRtU^~~l;?RL9oOSu0R{l=kEP!_7_t5U O0000ra@ocD)4hB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrIztFfw|&IEGZ*dUMO0?~nnHYoPU( z5=Ncozxv≤qt-g;-pujCmPl)R|C}HM4KtF^Q)+SD*5S#`ez_E8eyu!|%5At_Myp z&)tb$e1EBUasU1o*{>8!<3FsuC~3W1F4+I~7G10R?OPu6=UdOY_B~B)uitgQUFE;- zzka)m?X$Ci^}CO)f;u65_oUvL9r@KXXM;mC8?Tf}#)Sor_!+mDcbzJ-T5~F0eQ&yz z?X`KPe`lBcD=O=n*S+5B>$}7&fuZj=9aYi4WT)_ZQoci(rPFiulP7PcTiUu#RJYDN o>-lwa+rFbeZ!cce&%n$ut<7tK&5x_Ufg#4=>FVdQ&MBb@09~4!B>(^b literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-ldpi/ic_send.png b/TMessagesProj/src/main/res/drawable-ldpi/ic_send.png index f094ca9096b1ae7c31e91ffa2d461ffc52d4f790..9e921724d5e8a5dab1cb3e5dbebc47b8e0eb16f4 100755 GIT binary patch delta 299 zcmV+`0o4AY1J?o}iBL{Q4GJ0x0000DNk~Le0000G0000G2nGNE03Y-JVUZy>e*o!8 zL_t(|+G5n6_9%yef#E3-uV)|wFadEj5U&H`dq4~_jFVIY_CeW-Ks*_Up8##KyP`cn8GS@L0kO#8E(e28j0pu@kxh z3|PE@3phayIS)0InFT6wgv8i0e}H-`5at41GLir&-88{mU_+#)UqHMVh`WLKCP;t< z8rn_-G=2o)xloNSkR@24nsKGR7f?-ep@v`pkOAIU#BV}1E`~ZACjc4Xio`z-#2rAq xiP(??8E^`SFGFoSPHI$u42Wi+000CS0Cp!Vw@0K|Od(tG8F&h6 z(0WqxGByKu192-5A0(%=VS%PGQK*;tfcPvWe*o%9 zL_t(|+G1S0c5My=1H)4wUe7=VU;^T5AYKQ=_kb8=7$>O)?1Qoufp{_yKLO%8AQmFl zfURg^AdQVsgC;`_#%sV1ApVO*juYyo+d#Y&h>dX>@D7Nt;jx4nh@*h`3=r=HVkdM1 z7_fK)7jS|aavo|ZGYeGW2#K+0e*pDXAj}22WF!Glx@m&Bz=lXozkql#5O)LdO^^T! zG_;)vX#5DobD2Tnu$KP5?5%6^VZwh&zCI y6R{x)GT;;tUxwOvoYbgb{Qv(y10?_;zyPvEFSFO0f=t^@d_X|rqn=W5Dvs=fOtC)TaxN!e82;U zk3q2qAp<``BZnAZ33V;V)luk@e~i#{2=iJRG);oS(11t_Ko02u;$^@z`3u=VTmY0d z<)CbJXas@8V$sw+1!8#?B!M(2h5-YpO9=r`8eR;<-9Q)qMK-V$h*d~Pa$kUW0m!>R zgPy_!S&+O%tYPnfcnT0t1hLWcGR#0?5-!LU-9S7S>N>0dW}q1{u?WP!P6*kG&%jeq zgVvLhm$4bR8;Dzh_#io@4GT1li9)^92gGM7DGxygJ_2GHXrNOJ82|tO&p=H85MTff WWkcyj*+)eH0000(^mK6yskoK&=l_3uW}}Ghd2>}K zbTt)CXJuuTb)S~*a*ZKGhGUUL0Aqmvg(``5PTx)4jKU^L#TQIhBwSq3>d$O;z-JX( zo8!7hR*7#5I$kiem8#mcI+R{@SjC#g%s*vcr>m}y50lL0?Ca}TZx--&d~LWP+Ob`M z^=Z7qH^xVI9cnm5)D@)poRrn&U#yXJxW)CvMPpl2*Hh=KGAuV4yzT23cFk(|A%2C; zOko}KBd!%|9TlV!=JT^O=PpbP0l+XkK D?oods literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-ldpi/mic_pressed.png b/TMessagesProj/src/main/res/drawable-ldpi/mic_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..761d1603e97bc52eafdd06a90b68aadd0ef56c5a GIT binary patch literal 356 zcmV-q0h|7bP)c{F00004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy_DMuRRCwBA z{Qv(y12WK>@x~HJMM1gCu4RZEN0w(q)&Nu-2&C3Apn@Qvru8rm6Pjcf114xh=Adg( z!JkVPX>CnAV*B8Wh#nfLITR zU4eKDQc(T{Vjm#h4aAo~7J+Oy2#uV4U=r8?q^*Fs1B$JnV);-t2SGN#($8P0q9&lC zUr=|m0jWA5u7KJa1WtTFiJ3r78q^se#rL3WB_L*lvS$GeN{6RbpeB2$X-;UK-Uqey zAV%_rYGemeKcHL=pu(Rx^8=D5upm$|Bf1aXA5lGa#6d~os)2=x!6h4AMivQ%MNh>!m%TVx57fxu>FVdQ&MBb@0FuBi A-v9sr literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-ldpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-ldpi/pause1_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..4ef1f6a726113e279f99b48d0190c7dc38900514 GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^JV4CH!3HFy_x^nYq*&4&eH|GX)}JtE?Rp91OP07s zlmzFem6RtIr7}3ClseUmAGnPVPbH}MwgLAg5gFNpLp4#dv}0Z89ZJ6T-G@yGywn* C?=T1e literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-ldpi/pause2.png b/TMessagesProj/src/main/res/drawable-ldpi/pause2.png new file mode 100755 index 0000000000000000000000000000000000000000..81282874391d538b7ef564f866238224b514c97a GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^JV4CH!3HFy_x^nYq*&4&eH|GX)}JtE?Rp91OP07s zlmzFem6RtIr7}3ClseUmAGnPVPbH}MwgLAg5g;cpSW1s2X>%V22WQ%mvv4FO#sy- BEzbY| literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-ldpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-ldpi/pause2_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..b6fbdfd5baa775f32a5b1a1767c80dcaf31dc43c GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^JV4CH!3HFy_x^nYq*&4&eH|GX)}JtE?Rp91OP07s zlmzFem6RtIr7}3CBEfI7VI6a|Ns9# z@4%Cml#t-T_UQJ!&vQOC&Y0QAkkEALkAn?khd!g}P2t6iY+D+BFa%uAQkZslS)<^K zfB!9be)An!>u_hgZRY**7&8TCKZQKTN2?ufGj(oclzqeSC`L_5w0TYmOUL|80t+it z1*S9p{m60T;PQt?CpaBi**~&sbJVbCvLrc|9KDe4&N=WcvdvrUhH~e$sjG2uL2~C&&IM^_D=rfw$6kg28wx!_*L%`)Mg=xxe%sed& zA9)un%Tjoz;>OI;AhM0&W9Wiq4l@*#4O;#g3$T|l6zU02?Ap)#QBa}olvB&2sSYt^ zYvX5KaWCj~Q?@Sd}6kq$AOAwT%0Z#CdO-e|$S;scYTJY%!z hv>Pq#hZq>y7p>mJbY44$rjF6*2UngDGcXD$E$ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-ldpi/player1.png b/TMessagesProj/src/main/res/drawable-ldpi/player1.png new file mode 100755 index 0000000000000000000000000000000000000000..e49d95d949764537aade8ccf87829e876e153439 GIT binary patch literal 368 zcmV-$0gwKPP)h9u34?t=eNj12zZ@y34?1Aw6l68Ma) z;vdiNH4H5O)&cRFf4o38W^7=jgQq~u2j?;W{{eLN0vs-Qf-Mjqpt}MGcnieOF$@5D z1LWtEPzAWa3m`rX^)hBkL`qbOKuQvtaQ;F4{1~Y5DY{z50nd#90R{l+i&bzNS_m)z O0000p;?-djhV2 zB47<7$N<|!6?YwVM)6U1afm2x*FbcP@X)LE2n?d}(xBo1Nfj;LyB_%jt0KxOp-fwe zEA{9ukVWs&5Q)yq_^f#z=)gqx?n*=7LgA(pe?%A`>zw$D5%x;j>a?do8ln;b3&X?i z--naSJq0qT(zZ4UgI>HGp0El~;jHsfvk)RM)uekj{A0YqIWK^q_;W*e3$pXQ1W-#^ zbT5@};syF$k}8Is0&VIugwqtlQjnklVG4Sk-r)hxn;paLuEnqe7|EHAnX{zEZR@B;0{4gRCJ02r$Ofv)%r zWb(n4h%u}I+6ar&YQam;0J0dAJ=HO!rotaVB c`A2{O07t`|31kcPod5s;07*qoM6N<$f@E{!&j0`b literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-ldpi/rec.png b/TMessagesProj/src/main/res/drawable-ldpi/rec.png new file mode 100755 index 0000000000000000000000000000000000000000..2d08c58f65c0365db02ef2e2fa99242d6179bd98 GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtmUzPnffIy#(?lOI#yL zg7ec#$`gxH85~pclTsBta}(23gHjVyDhp4h+5i<5db&75}sFeszUpWxJsp5$4|+CgV_q#l9`0|MyNPCh;&3daP5&SV76Cd zV`5m6ATRgxt^#YM!ymzJl@k+>`Z%n=!*0aWSRowpK~rEoODE&gI0hC8hB_AMuMe7< R%z&0Nc)I$ztaD0e0s#9(NFV?J literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-ldpi/slidearrow.png b/TMessagesProj/src/main/res/drawable-ldpi/slidearrow.png new file mode 100755 index 0000000000000000000000000000000000000000..12fae28d7b07f620e25b98f1393db253be83b552 GIT binary patch literal 261 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)T!3HEZPnf;}NU@|l`Z_W&tUqDS+Vv91mn?CO zC<)F_D=AMbN@Z|N$xljE@XSq2PYp^gTe~ HDWM4fAkSF# literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-ldpi/tooltip.png b/TMessagesProj/src/main/res/drawable-ldpi/tooltip.png new file mode 100755 index 0000000000000000000000000000000000000000..4385e07b484dd4729b68d51e0a82561bdd60c7c3 GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^VnD3O!3HGno{rW7QY`6?zK#qG>ra@ocD)4hB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrH1%6?(cjhE&{obIp+JfB^?<0B_-m zE7@QEnZLer&}o&c*YmLUL*hOLhnrJzwX%L88WrspOKAWrIv6) T`V2!8pydpnu6{1-oD!M^5@ delta 481 zcmV<70UrLf0*nM9iBL{Q4GJ0x0000DNk~Le0000R0000R2nGNE0FBi2Rgob#e*vCJ zL_t(|+O(FzM+8wE$KP&G#Ii&z5fNRAWzwIq|3P$$=u~2FX^E)qQldyH#h&)M*G|i5 zPMfr%)ghKhhlq&2kMHD}#(4AQ&5w_L>>Ho??eF({`wHHEuO@^jfg52FcN=rYdyBvW z*WeK>i5x@8e6DA~C8f(?Bu|;pf28MmNR{jj3}U{Uq4Wu?fKeAR6+yzDX>h_!Gf6|4 zZ?HF%K7&;osYzzhm;}d^t}Qh0$4nnAV9o^R?8CAdhF}MrVdlO}GW!I5%TiRpA$UQS z>q(gwwY8tI*(N|!la`WXE%5DNoOEejmxeV9Lv`3Z*He5@)v>BIM}=$8e^CAo4!}`V zW?Qow*L99aT(o2)t4(H9<_QuvYvUD1J zUACA}GN9xFlGv`6+6iB_l=LaNg5)P@V<@XyNFLNN1%dH(LR$QUJ!X>n&LUGZN5BKO zZCg|mSr;@wCu^I^gyy&fH(UI45;7*M4m0VxzFkS};BB)efygP8Y){QdhCff^_!VFP Xdgf)3Z@7=G00000NkvXXu0mjf(3RR- diff --git a/TMessagesProj/src/main/res/drawable-mdpi/ic_send_disabled.png b/TMessagesProj/src/main/res/drawable-mdpi/ic_send_disabled.png index d76dca75f2e31ec7f118376ec9e88742c55bfc3b..c183f5f92bafe70105965b3c482929d77d223b95 100755 GIT binary patch delta 336 zcmV-W0k8g(1pNXbiBL{Q4GJ0x0000DNk~Le0000L0000L2nGNE0I3(HmXRSie-BAS zK~#9!VqCj+Z5jgugE0`d0`VgT5>`S z*g!1Q@TEYkONmRdfhZup1TuWxf4X%ThBE@wDUAIVh=s_E@by4k4@|R{(G6V)#BmfR zQIO$zObj#tEJ$o03S9{IKFm-FGQ)Ej5O=@~LwBheIfeo8Wo!is%ur(@4O#}tqG*OKfO_*jxk<_Z%Kim4tb<&`kPQVTCZ$>ARwvj1 iLDvMjMgM0AQ}=+D^0+@n)Or-;3!6p^}=C{jw%VP>y;?X(PY zitLJ3hlogrh$Z?ye#6f+#+&!v{P@_%yz$xJ{C>YTQwf5gErh6nJ7EyM?`!X^0uS7R z0az9#hLT0JUI5o5T>%qi$^?^Mf0iLtvJWuIxHeDHXRrz;UC30#IeX^78D(lEO=OYA z-bDHW)=Z>2nWbX}oRV}s48tki8!MPIL38$H*bMJr4_sj8esnVX21CPAG{G@=MV1?R znO504vTU|#aLA;!JlQ=MI~XS}?Qm&Qvm{i9Ez)|HZd7$_vF2#f+S8POe}W@$B2yQ} ztTW>RHEYwlLnFeY0d`dqGL_XPWmKmVBx}|h1s`i>X4~}7T!lIY_rW1Id#*rXbM%oR9{O@R2ep{LLZ@ zG)GJaZr8M^I*+`a$+ diff --git a/TMessagesProj/src/main/res/drawable-mdpi/mic.png b/TMessagesProj/src/main/res/drawable-mdpi/mic.png new file mode 100755 index 0000000000000000000000000000000000000000..2b9dec25860b8ffe5609cf476f29216f593c90da GIT binary patch literal 431 zcmV;g0Z{&lP)Z3Cbv;vD} z2B=sCx*#?Kk{NJ=WMT}E!()IPF$OT>F@TvE14sg+1~4$N08<_~DFRa%BPkkT`l#ZC zzesri1YYA&{~D^66UzS%GT<7NqXnfm;V}Rvrw*lWf($qU<%B@#N+d&(!8NEH2n0gu zBOn9TKsot9r%3|?6XZ-2Ag%}EeNYTyn}Fm%YCvkCde$?pUAq>Ys4jv9Dwhew63`OpJWyc=)V~*?@&-Wc2Q3dk0eBFqhXLjS5MTx3SwNgX zAm$bTaT?I}-{6FVBmojg1ma~-+iajVGeBJe3hYfljk{4&A{HBVLoo=z3QWVno0$Lt Z3;U29 literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/mic_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/mic_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..e1b29c5bc539707206f8a8160c95eb4eadec222e GIT binary patch literal 402 zcmV;D0d4+?P)%oDtPR9Zp-wDC zD-XIsP6lFWAifI3{6OpsiiLeZyb_4Rfp`}X2Z5Xh#7$US0do2}sAiC!eXv-_gxUfM zhx0&O2gC+Q8V#Ug=b>s2LiI2(A{A+@Ks*bG69~lI0w7KU;@{{++HYu)wG4>;fY=7f w6&Ij^4T^=`gv!L-NQz*o)Nt@-CV&6~02%u^rh(d@K>z>%07*qoM6N<$f_^@iF#rGn literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause1.png b/TMessagesProj/src/main/res/drawable-mdpi/pause1.png new file mode 100755 index 0000000000000000000000000000000000000000..35570d84a2578cb1760c8d425b09eedb536e9167 GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!3HFQtmCqP6id3JuOkD)`V;1?T`z%r$r9Iy zlHmNblJdl&R0hYC{G?O`&)mfH)S%SFl*+=BsWw1Grk*a2Ar-fh{`~)M&#c#(b1RB8L>FVdQ I&MBb@0Q5^U4gdfE literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause1_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..557cac120af92a12387d0c8731b194b9abb79f7a GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!3HFQtmCqP6id3JuOkD)`V;1?T`z%r$r9Iy zlHmNblJdl&R0hYC{G?O`&)mfH)S%SFl*+=BsWw1Grk*a2Ar-fh{`~)M&#cPSnJF64{+_r6G{njkkf}7^6Nv0}~I!r*C}nuO$~90UE&I>FVdQ I&MBb@00c)fPyhe` literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause2.png b/TMessagesProj/src/main/res/drawable-mdpi/pause2.png new file mode 100755 index 0000000000000000000000000000000000000000..50f4366db2c9f9ce5f8fa75559ae78f83a563dc3 GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!3HFQtmCqP6id3JuOkD)`V;1?T`z%r$r9Iy zlHmNblJdl&R0hYC{G?O`&)mfH)S%SFl*+=BsWw1Grk*a2Ar-fh{`~)M&#c literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/pause2_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..50305fead6113e48cc090ccf9af1aa514a813533 GIT binary patch literal 163 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!3HFQtmCqP6id3JuOkD)`V;1?T`z%r$r9Iy zlHmNblJdl&R0hYC{G?O`&)mfH)S%SFl*+=BsWw1Grk*a2Ar-fh{`~)M&#c}1^~8%Cwl_8|Wb24G zn7}xr!9ublpSjRfpm##s5jo|kzr0az>{?%XvIPm zX$I!NfPB+OT=Hs4F`Sno+O=zDcyQ>id3e~x?TT`HzlJ4S!vST5dt67>M|xcO!P;Tn zu!Fy2e>01RVS?T^75^Sa_Qo9y9r_cbddnLuI67ntGLNMma97Y{OUhM<^FL8}BJKzG zk}1eVOsvqP?+kAzMeh z!34$`4Hl9e`OJl;0>zV(88|*L6mkpPXLzJh#M8iNsSw9?M7{Awj|_9d0iLWAK`R!j zNHZ`82IQMQ;*wWWis8Hz(XL%H!-GSA&BMbkZda7s`!y`t8V)Ec+~Yd3KGNgL57rLr zh8_GJ`uf{#+zyY2cj9b4-t^CMobU-+ZA(BDi0E1PbYLG~Sz%-zb7(8A5T-G@yGywo% CuV`ri literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play2.png b/TMessagesProj/src/main/res/drawable-mdpi/play2.png new file mode 100755 index 0000000000000000000000000000000000000000..9d5a7c907ac44df874f508681f33a4ade4cc0b47 GIT binary patch literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!3HFQtmCqP6id3JuOkD)`V;1?T`z%r$r9Iy zlHmNblJdl&R0hYC{G?O`&)mfH)S%SFl*+=BsWw1G$30yfLn>}1mCX57!pF?)%qy@z zL4nzTvCv51KHH-#g=Z?q83Z0MJ`z*d$9N?0i39_4k;5L54u7VNl9p@-8YHhwSQ_B$ zX~w{|Xu-X#BcgUbE<1#lY~&07VK7l3F5+-|XU__EKKZ~R9)?DDhdrVl@tY^C_{c3# z&QK^XP|waeA?ZL&j^{iHWph1g#rTB4=(kbZs>Q2;W={4Vb8n?pC|17 zAl{Mg`1fbujfO_a4a`}uO@kf_r!=TvVc5jr(7?#>aEt15?_7CM05N#F`njxgN@xNA D^ss3i literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/play2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/play2_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..52ce63376f7621f679a6bc9bf11c31f85e03e383 GIT binary patch literal 308 zcmeAS@N?(olHy`uVBq!ia0vp^0zfRp!3HFQtmCqP6id3JuOkD)`V;1?T`z%r$r9Iy zlHmNblJdl&R0hYC{G?O`&)mfH)S%SFl*+=BsWw1G$2?seLn>}1UCG&D)ZW;*kgX%$ zU;^We1`ElKeC9$^f#ONY3>+UA3b_UDGd$8L;%Q*CREXm`qTYC;M}|4!08iG5pcM;M zq#2k41M*EDamlMG#c*DVXxFZp;lZK5=HX!%w=2r+{Th~R4F{AJ?r|MiAL()B2Wy9Q z!w&wA{mm>Mh6#GxRQ!7w*&BB-bm&iz>Md`u;OLMo$UK&Mz+FL)Eh$$a&i_Q^iMSu! zM>a3`SL2>@-~i7J#;sqaR(|9(Iv||I5Xqo$fZ^Ca)s>8L*-1bjF?hQAxvXG7HQA(|{v>wSZ^f?r`VkQD=jy zascB~SXO|d!O0ik5=36#1TbE~QcV%U5I6cc@X)P6x1c4!2L8S}VDd+R zWzz|GLdS+EnlhX$>8fa6sT$D&{j}Hd6f?JdLM#?2cFq9}6~VZ|xN9}?`-COsN@!r+ z#-(77iAJ$qAeg1jmRv8G#Oo~rgUDFJFY67}4x z;kJ(nCG{UdQcJ$c#n&a7cV)RpCC9wD)Y?T@Heftvqe!wm{zQxrB|%L}@J-^gVt8aI zQy!kWvJViZ2P|(x^Z_4!AEt4Ap~{6pn!vSj7Qq94g7d%0>cjXMbi*{`n)k$#=uB-` zfGe4CEkPI2B$kS}aQL*0#D((&+{5tuKo`#crQeY+0R{kT!HiRYuJLUE0000K|xPmyol$DSZ{)e zMGHkwg*K8_FCJ2*Md`t6i%FZNo81||+02$D+59^2!OZTw?|bjfn>TcL;wbr_@&4-P zn*V7Xfga#2FbKqfcKLk`muXtZ@;zKpYyXx=&?uau#>9Cl`lZfspev(5R?H zm7MV$mD1~0&fm}?4t7N3z9I^ZEunnHbij+CL*CFWug)uOU0zFiZ)(>mVsn&1 zALmY)P6B_>xZ-q6+u!Ovpc}?Xh;(C`Mn6jX1;iD1a|(fxYG+ZCyrAwym%SpFZ-aV~ zXmktkV4srJZ&FL`YKlCsk761Drr`bSAOSJ%0Lz5a7|rSTXwFE~CC47L>@k@dk@x%c z9voA?X2Yc&{2MM$8f_$lHn0wH9j&4h1<{WKJptk#X*VUv;l0)9n=GEo%fO*JK`h93 z`Bf{Az8FLD(H9M|TVBlHl6V%xvo)WJ2F~zWhrx1O z1d7nH0g5KiAtzl`wBX2cP99~0r#AJp&(4NLa~?d~984IuG-On+WEazg=)Eo%nAsuo zfkAu9m?A>UcGP)yA(nrQOBBN*-?l6fjMzA6+q=bZQqQU%8XZu>EnUxl#g{!aYaP3) z(KoXME|OJO<_!f{C+FLcyctF6L$W#lqDP3;LDlHsyOjc&l}b#Rs`S*XeL$LSSY8I` z12z2K;>Pufl?%7LgY(!~#0gXcN8M!gVEmB1%_QSm^~93yOlMlanaQ}*L5HJhmZH9J t)U<5r3+E18WB9eN3+MmR|B){N1^~IqczM6>HpTz|002ovPDHLkV1fZN;!6Mk literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/player2_pressed.png b/TMessagesProj/src/main/res/drawable-mdpi/player2_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..946cf59d7efe46f6986c129d26ebfd08608a12a5 GIT binary patch literal 845 zcmV-T1G4;yP)wMO{AQUAOS0Mg_}~lkHZ$Kh^X9!b-0@dg@;|mQ z8Tb63Ya@^VE(4=L3Xs|NeP9Jx2Ie55bSuCE_kkN79AXFf0+E-U3LFET0Ve^I_wh@< zfbT~j99R=Xp-d;JWi?VO){%jxTcOc&J+I~4- zJ_WH!(==mne*yO(vhNIqZ6e!wgctbb$l=KIcI4DT(a@SaAV0;1HsKBf4;y1`+YAj0 z-Ff&>c$O15t#7Rr1Myp8TA?Ilo2-)|G6k8Y0z)cqYE>)3BlZcxtU}9no;O(zA+*V< znW&K*xzgeU1vWfr3#j%zwQeZQJHnxeCO{R}gMH$TzHS zE@r4klnt}aUDL)jRxMPMQe0YTS`{Qk#QZkdv(iro`FFSYQ$zbmIymbBv@4F`a?p;mV7a%f-VFvvu;}oDJZp9Q~nre9$ z9)^ZDpjOmd%>C@vi)f=;H4Ss9xLcf76*bxoIlAgyq^|^b9=reOCP3{0umzE1DOY+_ z%9hgY$mtvYj)L|z;nM*JQ*}I`rzat?$=elU#n=txX|+;POx?J zlpMRSb^0dL=kg|Sy3HY0*}MFqmq(x4ID7PILmXs}zWT z6pl`wHf>u2XMdBZ!yKMR#t#DvWIAjOMHXr^M15s-71U!baQz|Dz_5ABln|x~aZE?l z8*ebjGM-V1V`FL%6!B|W!@QeQV+WguE5k-rfqeF?M+XC3JQTew9tNH0(0ym%B)fz| mdY)x*8i&An#*K_jJPb^RF+W<>H4X#a!{F)a=d#Wzp$PzI229rg literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/tooltip.png b/TMessagesProj/src/main/res/drawable-mdpi/tooltip.png new file mode 100755 index 0000000000000000000000000000000000000000..01b2766d253318010163b0025a3d945b8fd4d321 GIT binary patch literal 285 zcmeAS@N?(olHy`uVBq!ia0vp^azL!h!3HFE-n+dJNU@|l`Z_W&tUqDS+Vv91mn?CO zC<)F_D=AMbN@Z|N$xljE@XSq2PYp^p8dXBY-3Z(le5e$XZ*@ze!o8T`sh)+KLYM0Y4=`U-Fh_N=J08Til43R(-RIh zv2u&)L~MXEe()#sHN9e3zw0aObjAWsi(|erHL1qc%PyxZOLk4Zxi&>c^W2O#OCvX* c>1SYOc&MsVzNv2$AJFLxp00i_>zopr0RMAzB>(^b literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/ic_send.png b/TMessagesProj/src/main/res/drawable-xhdpi/ic_send.png index 73e990f38b943786bed94a91ee1e10a0946fbe20..4c42bc008ef627b4eab3fa8493047310b170f30c 100755 GIT binary patch delta 706 zcmV;z0zLiA2J8hPiBL{Q4GJ0x0000DNk~Le0000g0000g2nGNE0L8<}Tah6+e|||s zK~#9!%$djQVk0s26$8M?0wykP=3r5slbuUIE}3cy^Ve|ua7m4PQw zni)9jDrgY!Rv6$mYV*`|^F(g9l*ix6d*oGW_in<>=nJZpEpabAPCg&pzTYAD>4LI0 z6X-d7;7fx29R#c{y{{ig+WiOzj;$QN>$6ZYxkuhdNL(}m)t!YrKKCM5a%>lHE-R&@ zp{T$j;zl?LS$=z@oxUqQe^fp3gOBlbBE*eld6I+&DKfuIn9UTY_J~$+KU`F zn(*5P$L1>gVWUAR zsLk0tBF_`xkp1H-ajk?Vl<=9(p^_52`8z~Rwqe;VZ`GHAizM*1p! z$cK@of*R{DQBjOZ7>ZB+O|=BTS{wl`BJOww+y>xdoT)+tv}`(hB^PY~s-StmHq#5< z0M*z-gmMM*DgRG6nepf7ye~LjrXIszhB1DJzhD$Zg+&2>S#1!CsqyEE8}M zQt=gr)zER*k}tqs9n%398F0cE*m1_c;lO37z!-)znVAT|?t>hE}KS5p%3Y6NhWL+cRYshR~ z*a76X&~4UL$Oi&OgTk;bf6`fq5)5oXHqNhn)&fW9Tv1mcTmGD-wO_zyt24Ce9LxI# zH&|~W_1417Ru)*Enq=FRNOOa#bR#NsV@%gO$Q=ZlhUKYg8VTbxGU6L_8FDG-HLsM^ z39Yi>>xmhPy9s1>Bbzh~0uBpOLZl2l( z>0mX(pt{R*ld{1Tc*hYNa?THeOms{XCmouWUW+p2q~P7$3VN-1?IQA#OA+S_hAMAtpV z649lU(qV}d5fRybKfaUancaP}^XAR;<*S|9H{&z!{oe2Qn=Mf+7TX=iIU}Ew56EAR zMdkB(t#L{64|$lpNq#0zlT%L2f8>H*H6fR`}i)<7#QT%SfLgbv*5jH*gW zz-wUu%b=GA?w3OTb%DFW0DU1>4c%7;-Y@~2RE{f#SFD3P1z;}G-Ejq#e}N}ani)6~ zS5PnDZ9l*XYV*`|^G0s7l*hlxN90v%_n!RB=m)BlEpabALB1`OO1tD>?kwc+xd*wDW4j;cGEzDk2ns9$ zZiFkJ<+oef>HE?{)e}GXe>h(!eB4-87U7_#o~*~%Or1oZ)qFV42FG5YJ;-q*5x;$K zY_2jS#p#b1=m~i!LckkrqJn%ZGPB0ImO*q!FlGk)8l}gq#=~2~?3Wylob6 k96KdywVLxE^eeys6x1o0OBDoA%K!iX07*qoM6N<$f@!rvApigX delta 800 zcmV+*1K<4U1B%W5y}rPXSw zMtj)prKRNFXmmAFjYKPzP)b=sC`;(?$8Y#`yxES$<;VB3NhUt?d;8|Sw>!~p zw-rde@+Sf~vImkE2e?DXh5~x#9CF)X0xeqyj-0>ZVus@I=?6n!lDgl=v z6<=XkUCqxBxZ*1?hHVb`oeBf4_yQc%F&%K30cU)Hon-tQ4qTNAjA1x4?u_`i0nMHV zG@FJJVZ+{M!+=Zl925iOul*fz){Dw8e{kOgki+_f zGhI^*_!u&48-}$};QvD4Ey#BQ&e?=*S-6=T__QdX&5k8&5^|G(&mjv%!f=&;WO)KZ z;0@Xn8UrR_CkCEp;{tafuf_$IG_P$c_jR2CtEOOGC8rR$4S78(P-@4Lb&Y@>$YNgD zA>_BvZPr!D7XsEsg<)N!f3pxJ82B0TuG8sgffIDDsH>1If6md`FJQCPJZ(Cs^1i_h z)(1!JWXSP8) zSluwF;qu(1Y;eVKcFcyH^MfE09TNpg!UA^QFbe1qqy_0oJsPOdawchA zts)|#5v^iOzmxOy9Pi`4j}~zcKX=}}=i~R@z30xo$7r|P2|p>9%l5}CumofPllxjg z4cG@RrF|}!bM@VkP?t{N5U4T<(>7*uUzK$_Rh_C5=mKtlIR)K0*6oUuz%h^x@U_xx z6P*C&e;Ca~BDj#va2#JCp9nAcI0$4T5Xio;V1Y5eS%ZWx=0`CZGvu0-As( zpa~>#;%SQ$tnrrtWjs9vDg-_v_@omU1r|ht$5V*y< zrw%u)F`--Uz#G2sXSU0hgJb%f<=KCs)8AFam5a z@e=DpJ82-Rt*&lux=@88P5<5vBixRr{NJ5?~VD zXbK^#BXas^9lbgY8h&LuPRG1TxkYZXydRu+mFbKCb;2Q@<57mJf;D+tr=7KC->|$0 zBm=zBEyEJYJf%;?|B`^xB6L}l($Mf)Yb%707*qoM6N<$ Ef@*|3P5=M^ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/mic_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/mic_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..62e655bea863759108835104e9d6fdda2e4bc01d GIT binary patch literal 666 zcmV;L0%iS)P)`-sgux2>JUYzK=5REG6JJ6b`*+0Df$ROcq)RVPLY=+2ol6V=%V1I zKgm9RS4L00=n%2)O}ZH~=u*tOFA<4WI!ufCkV28o&qY3vJ#B=j`iO zpbGHqAbw{YfUeLM6s}(#0K7?JcM+f^G|mdwB?kZ{N$eH^G?;yW*)Bm}Hj-nXyd-u5 z0ct{Hl9>a$HUO~Ck0f>t0qR0ylp!rrz}^^%d*oYWMjsX0>Il%L$85G76Br-oDyTUk zd6Rtd%)K1*YTG`#C<{B^2xx~1^axs_(crFUhT1>rT%chAwns6|R{k%$rUQy|N;yta4*q*&4&eH|GX)}JtE?Rp91OP07s zlmzFem6RtIr7}3Cyta4*q*&4&eH|GX)}JtE?Rp91OP07s zlmzFem6RtIr7}3Cyta4*q*&4&eH|GX)}JtE?Rp91OP07s zlmzFem6RtIr7}3CYuwA)dDZm47spBOb%%{FPsvp4leuvJU{7$_rt2TF{#X060`(&S2fhM1 dk*ouv2@JEl_?!)AW&406Jzf1=);T3K0RYn9JthDE literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/pause2_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..09e75bb8c2afa9f715fe35d72209464f15276a78 GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^3P7yF!3HE>yta4*q*&4&eH|GX)}JtE?Rp91OP07s zlmzFem6RtIr7}3C&T|iP~S$ug`EChU#z&Gd{C|d|f)<7L}LC!)zvIW|p3DOn-~&{^qPBo!71Y2h$Xf_VGT@P9ECeKZ&PI*1qb}gs4;A)DmHnbBAW3nh^tf`= z1te+i9c}I<>H?A+chfp|S9JkNneUf^mB1G`1`qznlH$N49{n!CQ2x{sxC5u)bNohV zfetw0RdpHqCoO^PNrQ)&fgx)g`IsN4z%4jumq$(<0#964FWRr;cd!q3W*vN1fgf-J zHo*P7(>F~Ce!&gv{2f>0L=|Z9sr%Z;a;fufh`}cb#9HTRN)V*ZQ3Zn4`9Fa^2X0w> ipzs2loO+Y|2`~UWPD$#OGRuMh0000Vw0~ree$pPqqE0DGj z@L2&B&12ymtA`1aY7Ce%yg@7dDY}7bA>H?1aP-cHr*e|LAk~CLJhbu>2 zK$79!(dJ&FE+8pzH?4DbRTq$y_%nXrq&Z5H>}Q4K20h5H{La(2}E#wG@FO7!5(- z6a<1((0}1SOM^Y_-E)5Uc<3AM_wG6OzRR22I{!Fw&Ygj=Ge`m)gFYx(3HYQz3p{|F zg@7bSi58{Q1tcYw#g}KvLck{pe1i@^*+M|F0-E3n!1U!LB>KrvIDx{6r?N! zd=^0synzas*A|d0g9dm31q%U57Tl4ng@B~M*=TTf)CC;-p~C*Cv0qdLBq^?xE?17a zfF#4cqr<&KT|koOZrbGTsxBZY^Zin^5*UJgaO;08C=NX4(eDI|OxV8j|nKIX?Ma0%+{^2mup;GWCsS^IVT3UYnzoT`km literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/play2_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/play2_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..164ff9e103fbc1e8c607c3cb0565648188a5150c GIT binary patch literal 493 zcmV8fF^hZ zc?$tao)S$;sS8L-EQ>Es%0j?r5%>ljfU<>vWEnKTGbmUHNY+6c+<=UQfMf^sz&S`; z2>2|38h8U0FsCgbSps#?0YwV|Nfz9btc8H2$l0iKcGLwN`=P@AsIgyE1te*%lpa@( zx_~6Zy`#;&L|s5q;BMOB?y4>zDf9iZZ6)vl_Q9S1F|RoAh)2IOFqA*F1g^m$c>jGP zw7?Un@~XNF{gdv2jd6pAn1LZ{9Q&A`q`(z8W|zlK5&{ogRxjGG<5#c?Hm4nYT7fTc z0M@|mf2VJf5`2S8*7-B3#)&J?;!{W3$8xFjR*1pJ2_#zQNlFl;&T$2T)%h=hhy%AQ jK2Ugp9H-tSKLQK@?%PTN;+n#Z00000NkvXXu0mjfsMN}i literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/player1.png b/TMessagesProj/src/main/res/drawable-xhdpi/player1.png new file mode 100755 index 0000000000000000000000000000000000000000..2a11310e6a6bda35d49508a843c9891c1c868b91 GIT binary patch literal 858 zcmV-g1Eu_lP)`6pHRCwC# zSvzkNK@h&ZJ3CI4_lrmf1tLfg&?JZ;AzJ8YC@3h>@jv(<5d|V89ij;}9iSA!!QmOi zBLoPLw|(bZz7cEb0NK8sTi?afjr7The7pC3J2ShpVEDxu>KbD@N1gf`x&j7S;}bJw>J4b_soCfLyILU_AT-bLqNA~4&*qO9Z`1XnvApY%r%C90!M(p z1>$&wXE+{kbp^D|O~0N1k2jlq7vY&z#>}3U02EQ4y2omn?5HkKkTeP)JoA7dV4ri4 z?Pv?=@|R^USg7;0FZ01X&I67IwmApcH=tizL~rH3wdWSG+uR5pWF-*4%RI=)HaP;m zs$$M!c%lg0Hk7BH>R@Px(Dz2Mm!N$>PD*V7YS7!1$rFdQIQIb>TuD*DCoJSPTYzeB zET=@C-d!}(RzYz$gO8&L3wVMBY}$5=TCgoye_DV^*=iK>kv$N);8SC(tHtC$g8VaM zeW8CuP?dVYGT#@inRb7a)7+)7qxSf?;e=g^8aA;6goN}fA#s{s^z!#W3FOPHE3ARv z$i`IrWmIERAhPSg8xY@#^L`Uet91pMYdF-7p?j=Qt4t;KmO2!_G^%aV5D<>znwZ2a k`<*xwkO^pW0lx(p0G=Zk>nRF?&j0`b07*qoM6N<$f-PEo4gdfE literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/player1_pressed.png b/TMessagesProj/src/main/res/drawable-xhdpi/player1_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..e3ebd1b5e417967076f6afd7276708a7bce5417c GIT binary patch literal 1470 zcmV;v1ws0WP)1mLYmEXv%7OVzn8p8ep&aL+1adtKKRJYo1K~W zz4v?H@0jq-uXMAO+yq{J5r~y9!{P!jf6EWN|X}aS$&d4VF$ig*; zH}agZ7Jpl~ExMS?H!#UgDe(Nqfj4VKpjW@3jr3($#2u1N&WMaUQ@&oDj&$&>u?b`@ z%DXfSJPahmBybh*7&({BkcVZn^^T0X$*MMCI&wD87`HIV>83vh+z)(J?tY`==C$R1 zb2acNF_oljbOvN=?hr_1tG0gAJ&qL|<(9Cq8tZj$OuzS2;O}Y(Tns!(27g4xtf!XP zcSsGBG64d=az|{?@@@bZ0&f9s5CZFf0X0W@@V~pOvQJ>fPSngRMcrd~&p1}&QcYkL za3=6!5KsJ|c1Wtt$hbAYgtThyS+Tm45QG;<*Jf=_z6kUIo%-0uL!bAnGF6divA%@5 z9VA^!y4Uz2uxHW8A9=WcncA@V{!J~_pt(*dPSO0yzdy76QpZvIRdSKr^S{Vvq$S4E+aJggq&Vij=*J$ zSL8T0Qo8O0VzvsuJ@s2pl(Aqw(6ex(A-@i6z9-C1o)vc^Jm{WtLP92B&w~(y3I8b< zq+7`E;p8qLV;n8E0^P*L2T~p}9o>%DW_lmM-t(vyqo@ZCkLsQT{^Cx{8^Q zAo0CYfTO-=K4tbWTkI|(4koVMr&h=#92(^P&x#M6I_Y8rQe-BZ0=}la%5eFO z|G0uT6&zDWTU|!4%8{0F6X_ZBlM_EfG`N?~n7zwAevypsAoWvKy*}WFfZELaNszWfl>nD* z@YB(2^J=DhgVcqd1Ag@6mS-aOK$X_i-0szC+k&Q!`+&ni2&nc5Qx)iWMQFu-cnICI^v*;ch)^(urCg4Co+NVTf`%&IDR zHvn6hzb}?5Ap00N6m;NW5uinlX%N7X*gaQH%I#B z@rWVa1zhwviF?2_gzWet0OXl)A8^I8JAa0V*@_APdG_mvz@-Z9N!}mh5R$Pjz>t4p zHfSa72k-$Rvev>V$X~$Ykq_hx(X7|n3V=L|8~|E;4)JqXg-IImH z(;3t;2e|Kb1@2nT)>2Di5)&!`XviD(W)&QF?r2wl&om8HAmFN*Oz~l^)x+VS3(@=~ zWvLs#vIxGj6sKVX$R$N@^(O0YN(CfTWsL& z%}^5!A|Gg0;6Rhewt%>JsOvO}a5{q_Ulb*>dGs`-Ok_@gmfi=D3zB#|u7Z8a?mPy! z&PiQpyOi(wXe(~yOA0WJz6MO5IV^d(4{+n(0SZ{dL>{OGnDWL{K;(Js87pNKZ0Hmo zH+&ZGv7EAFxd*mMa!P=H*+RKBQ%PZqFZf*8YN{~#Rgj;sRu?)af={XaWWO$2FmZiU zQ`}{@dq7Tg+UACx)RfxQ-v=NhZiIL#dhy`Pz&yyGsES7xzLB+?@=Kc_C@|R#U=hR* zy^L?8XtiO$eI<*^F?^4Wm{~r}UpSql_??`uGgd5W*^diie6#R7aVQ`ZP~!s51Q-A@ WDgnbN@!%2w0000XChpGY=NxiA(B--Ry_f?gTHkXPm6Tleos}qX~HTuAI zU=Pp_NEYw?2N(fPvg3vlpn@L(Zgfa{A2T9<*V}}sz%K&Zu6h!pFfF>pb8gu^DT<;h z1*Mt%HC@wXEunR0^=v>-={o*3 zvT$-Sxk-y_J3;E!l1rn&D?r3a0`~&1bLR%0_8DKB?@N!*6RE{^)J1h;DwcZ4$Qr!U z&jT+2KRH3*KH#@_Uka=b40sjqm3nPTN=P{`x5{))jZJL?UbRc$ zCAJ|^vx(xz--F$2_1;-QU>X787&mnfGh<5xb^zV%I3oq553EL+T1HDp%UMLXQ`{5{ z-R)Kg9LXQJJgyPn8sG1Y&T-%qUr@8nTNkjeS^{?i8}boH=}=BJBS{kk!GOPg#!Wp@ zPTq0^9xH6qUia6~fZD7~(--nxgchc_seLOE2m)K#u_mvP$D5um%kJ;F)7>i(xTA2v zB~H2}ccR$@;C~ZbSlwEPj3&ukZ-IbEJa2o|%ZOl6a+qY1^a=PYq6P?(K@uWUmMboV zZowt|V(7-roQ&gU8ra6E&R*oXL=KQ8avWk)ZTgXQ28oWa93~&(+J0MQqf7jgJMA$u zA(6}P4XH(%wY<^-o=%E9WfB}bxb|F5JJz)@$DLkhMxd2T*kVvy#(G9(DtvLh7R_|)blV`1EGK6v0i8Q`{g*lq1~G(XkHCaLjJv^Svq>{m z#dm(TOh8{IPt1%!gtb2?1BKSH*0V5#%91B^l>zz5CzdTUlMQjFvz0coLbyV`y52H@ zb>u9{kU^8H8j;9R(qUZ#Akf8~{!xj*ME(Hf-4xJ2JK?~1kO~U^P2AMEN*ef~;5b?1 zrgS15YIX!tm(;#oWG@PztwccM7z(MS)Ll&tpN1jmDZQVY`m~yQ;2o}`)}K)`fo2g% zTuy8Ui7qZmep3wr>LmHjv7ES)*xwA{bL~h~+sjQ2TB(WO=0aFsd?DV~Xae#1_(86J zO1sx5tq`Eje7xWoN$ntQ0X9nb=xp@GL&5T$K<^ke-Ag+M2@qrgW zTm!@lfEN5kmS93tCZnbSXOU2I44KZNMBE6ho!waPzu=~EoW(2 zgaVO}97IOOAQmzNv5^!cLSjga^fd{PD>9QH{)_4ed2taBb;va6mB^QSla*V>8u_^1 z-0fAvh5EW7D~A?t!&zAP*IKtXA}l^fVk~vCMcB|?V`F2tpM~Y#BQi#LAxRkZ&CX(+ wDU!r6j%vwVi?KWMkWN~U9g)4CYd--70EJIjo*vsG^8f$<07*qoM6N<$f`iM6IsgCw literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/tooltip.png b/TMessagesProj/src/main/res/drawable-xhdpi/tooltip.png new file mode 100755 index 0000000000000000000000000000000000000000..4e2da555cc3a88885090c2bca3543f1e47eff794 GIT binary patch literal 466 zcmeAS@N?(olHy`uVBq!ia0vp^Hb5N3!3HFs1e%%xDVB6cUq=Rp^(V|(yIunMk|nMY zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ-LNhrg*wIhE&{ob0?SYkOEIbV%G~+ zo3`)v)5HtpPfRkB6sza1o^7HdEG*BRvtP^5t$f*scPWCOT07oM@7*}PqxWL&YRizx zu9vgDChkorUN?)qBqKrp*UhC(-y7~u@IDp!UjD+t8<(X&&HA(~S>wWgzBo~<(Ea6^ zp*LRdjgESMejfkw61BUlSGm8QGHubJy~jg!FIDMyhCUUX=MewA|9_!Z|A#)s{aP`L ztNAY%?lRXs(7?#d#v@^nKoR4^|EA*d$@dPI{5ZCxl;K0x!dhl)eujCvTS6bMHNI%V zP!V)`;h}|xp1tzwYX5EW;pNJ`A$m7fulgi@BeeSa()f2vv!ib6=4xuySoEuu+g_Ph zp}ei*_V*1}h4rWJ`;*?5$&{h{^4XEVB=-4B7e0?}U}TW3%(&!P*{}o{DGZ*jelF{r G5}E*)bG~^1 literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_send.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_send.png index d55fade396f114a01e3c5f80d3ea8df613e1b0db..eec17c5470f1060b24eb730617bcd731959c66f9 100755 GIT binary patch delta 1014 zcmV>c zL_t(|+T>c_XBc4X#ZmwvzAGIo13!=nx6>fL>GB(z z@E-UrsU`^V9pFF3IYr5YyGay(Mg|N5n(!t`CS;`VEsxg-6TVZDOqFT1`-iZ!z;AOu(M-e@ur7HnhAGI2t@86IQQn{+jT6qcJL7yFK8yCCgK+LTv(T z`<3aQj+V!zZP$`z`8Z9Ij-eN<0jGkBH#P2unSg7CL$4;Nx;uYt=D z4kx3E7b~$P`d*?OP@uKCZ6;t&I2uL#Fw9YM#4dM*j?K_~9Z{C%fA2Ffn}C(7ZP|pN z;{5n1}oVFy^{F|>fZIr7=#H6r9h55n!Un?9e9_nF5bk{cYW zrI6ZE?agvHc9741ye~7!~u}Zu}qI*$0&RvpS zK0?u$y-(wcF&=TN{{gN8U&JX|B|fZpKPAEIWPlOiArEssisvmB>;=cr#b-5k`x6qq zO_|(CaLviCUn*ki_xFtyAEed$r6QW0S%IM94`{fmGYi3eX8I1bw3VWr;b$S+gRGg k&zbEv_haYI zY+JOty2?!DR~G(J-}5E`f3-kRe{=%!!_bJ;^o3n4K_4H2j6m)|zCx}-$_f4R|;u|e1P;BOn=1J9zRn8Im=;I8a8Ldw7}2WnDYkH8F2|R zC-^(X9OeL|${=PcmF4duW*g)kVOASNu`cb0c{7ffJmORU+dD&;w;(lfe?**ttOT^( zR+{l+fypB-LtX{EeH(LBXUxR9&(k68r-j3;(;YDt^8OHW8uCCV%t9mDb(Ap2Aq~1A z&eGUesA`71P#tqU;!23)q6p@sikJz9ucab95FvLjA0xvP%DY%wh{f6URSG2IYTOs%pp z@%;RREQntqpA|fLz?GsA!fcHiQKc^JhKXyLZwrCxg_vUM*G|I3)%G@nm|x*_p8*cD zxRBj>h3Qo`=6sNtUWiL{_M&4|a+r7xy(BC|-MX|JW?#OTeu%h=fAX8@UloLj_Zzim zi|L1mGuunZFBXr&JKuUa%+eqbe?m?{p2i50$NVIMX$fo)&t8r}wnMhC%=KgJ0%bJA z)za6tF}?0%EF;XzkZ#Cz=5Q;vCAhIaEOXbG=n79Q0hbZD{p1&q>SmU?o2-gHmmRYQ za?uIX3USBT-c3jFyqsD(e}JOG?0}qy+=l$MBs$v5-1&Msi+_|zE9VbT5VaL@ z4YCJYr=|Fqwa7B}n|s0UW5F*p1NHS)C3)J#F@k9|% zL-w*f*V`1t!)`0QdgX_BFeZR_W%CMTkma!S=ZBco9PcBwyqxev5c#JeI^yAkF~kD6 zj^u?{7x5F?FlvrkULbfPh)E6c2E*96?ufHAVGM`46Tj}PYRyo~8!p7oERVC43Ptg7 z!nj+=om!f7f8{iF+p#EiZYkcCBgOMWY)}C266BE#4C8dpfXE*0Ut5DV3nAJS~A^2M>?=5rP*5G-Ry^1r**514U z3tx(cGp{1r!Pfr!d;=)h5}!a$GV?ANIaQH)KbHLKp8x{@j79=*L4<8t00000NkvXX Hu0mjf8@)!K diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/ic_send_disabled.png b/TMessagesProj/src/main/res/drawable-xxhdpi/ic_send_disabled.png index 3108f5bb34dfbeca0a2dc0236e87b28c2be0f2fe..633bad32f47d122772bb4473132621aa61abd0a8 100755 GIT binary patch delta 1044 zcmV+v1nc{r3cCm)iBL{Q4GJ0x0000DNk~Le0000#0000#2nGNE09RiS-;p6Ve*?`) zL_t(|+T>c_XIw!Pp6rfgDItUqmav3Y2utaI(5KSqQ~Q#qQ2JC#39Uv}S1WxKu% z8lv3jQ$wldX^HMjqmc-WKICbMmQqS=&uPC|)7!nfckj=cy&uQjy?gK6{k}7E&dixR z)M~Ya<2X}7h}&eFGcz;Wq7Tr$eVgDQd|zeo1V*0(pfIvWF01o@_Wvy~hp@=oZ z5jk#|Y&?wT%XD~6HZAM~SEFB&EqL_=Px26891{%z5%P$#6Bde4f5XDfANgJ&aV8Oc zi&}grVZP3&X!G|{$|9U;G2u8gc?i(&EnNC8Aa9p(u8i*m{I1sjV=;-aPF9sEctGDA zdGGTcHRMze;_b7`0iVz9%yM>+29MS0nCBUJ*+=)*$Fi&Yo z<6rh5%wzBa*(G5=e>lk1IPz1#`?D!4yAZ#I3QNgy(uW|uj=hOCx_N%ia^ibPYa3n{ z$Q}X5M(~QXN~E`XT`2nqZl4Ef13FB19Yv^~!+K&^swGik+ThhR6HAZd=?r)iBbnXuQtldX_FNt3h}`MAdW zDG0AX03#q{PIFC+=T#Bz1;=>F(|FHE*zHe1dJU1>Na&uABG|P{MZ)|2eIv<-@p`*d zgxQ%D2upq+YUyf2ScqJ}NY9Im8?=_bU@P&q=Mn7uM0(%#F`K$Q2fJ=B|MguTvx&Ud zrbOWSm`&u}+eO>(bX^QE6nW0eyl2Mc>c>R7Au@o_`|~yp O0000su#A~1RgkLsZa?4_sbxgZFHz#e+3 zUJ67@68d)$6onBeMbXhgBuSzu1wl~+K_ms2eINHNPPThH=l+|Soe#cNJ#Y8=-8VD4 zvvXB9H#f&j1PdGgC=?17Y`P#(e{?vX&;JnJ0X#R=fMCTe`g$kiEyz2Np8@6q)(E!{&}y68K#Q zbJAeCAi@aq9X1n!&&hC@{jgnQ5a**<<8u+S8}c0_Vy-iYVoll)^I94)e|f|gA?)uk zVP1#akVeEY$Z|;gJxC*dDljeX@?{n_uS4FygIUlS(^~L-DWc4as z-q#5;ZbT>c5avV3Hr)`%A!lPeekbH})iM7;gu$g8VZSJXIiez_pf9l+&3?lT4Q?U6a+3Uo9a+okXRj5SSs&OIaLpnX`lpXN} zlRL5;W|yhN;FyJ^F})CrOs#M*@%nt96o@dme1gpf1uq_Orsx)79!$CtRcg{+m^hdD zHV({ch()G;tS3yIZSOXS!2<95^mCY*IGb~s=@ky4;3G^;G)st*$pN4A%czgRi~SH4~3FdG6RLcjPEHU}U_ zQv}Ilz8Ar)6IrueC=`~!W;J9Zo4l>Ap#)6TxFG&e+Wxix&rZfAp2OAg0 zUW}z!CE^tSZph8D0J>Hp3@Uh)(kLO`8OX;nzO%OERrrjW?WoLsizVsEjA$LKGK(ZN zpjRd462?2l52^ZC(S_p!;sH8A( zri}xE5Qw5E22+9{2tz_32!fCVlG%6QJQ^PF+<>K%*=cDp8I|8p8M{3@7_rq z92{8xveksCoX_Xo4xa%!fd(MS$2NftU=mof3#QZQ!xiALo4_L$AlVQLcnrLYqS2}d ze9)C%2HsX6U*1Dhb{Rd`aM%J;Oujil-DVb%3PT_fbP%KAMkaqEZ#U=pc zYw__FXpCt5tOHFD@iVRv>Wh%P#lHGt=>c4Q3Yl*agpOM%okB0{=qESFg90HOW1tjATP^(cuZ5#rQlo7y(9r5nu!u0Y-okU<4Qe zM!^3npeCQsyS`gYr_+g}L{#EwLdXa>1OeL``TPhXU{@G4$Avl@8U?KT7YJ+W;mGSA=?VB!E^0S6ogW1G*HA-cpPo zKeo!gBPAk%iracc#3zaj{EsU2#+d|%zsBW9Iw90K~gu0)~C$dS!?JJ`oK-*|q zf#<-U58``VrxoNk73DIDTj*rDfCYt!Ufvg>BGV6?1xA4_McFMbLuLCxeov8oQk%UJ zdDLBSrV_(KVd};yUZGc63Frpufs6e89;2I&)${jKh18kan&P(IY}hRqngK=>6UhV| zS9=VRB46nd#;9VxagwJtCKQv`2(JE&{Uedf<(#wc7fasBMPYV3$gUEPRqmN+)_0ZII*nD0W)8kSCw>mU_QLPdRxhogM=Q6;3F{SVV(&BP37078U6W z*&xV|IS)ERsN7xwcRWl&58${EkoZ-6JX0i&w!KChu9K=tfF1KUJaG9AeBktATFPm7 zfYOc>B7Pk>>A`mn{=~4VB!KJ*@G!trTdC|>gC?WXib+YbryxI|e68Za$pFu|FMKpy z7l5qO?t}cxN`<`ifNivwXUNjK;mOX+vQm!a-0kbkY|iAd#4Kw`_7VLv??ylZ+?K9V^Fy4hv~K`Hh&X=j||(S zy`0ri$DMh#Xat0`MAUyl;eT~VP7_i5Yr}+)5nu%TwLbz305-7@tYU_CiU0rr07*qo IM6N<$f;ti2{{R30 literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/mic_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/mic_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..c972af2bd7a41276360dbc43506fb5641a1fdf90 GIT binary patch literal 996 zcmV7~$9K@jm^AVsQp(2M3Eq)0EN zR4PQg7!(4cG*}D@g*?j`I2YRNW(Ln`1@JW9H zt+xr@`G(f1PQ2FuNeW~tK;0>-o9q=p0`v+r(uI!^@-Z4VKos;EG^C&l4iQ8&On@8o z22@#se1-ymPb{k;rDub@(I5aBMP0}1!DPY!NNXHe(_sKiYXq1M10bdmAQlEdR3kt% z41m5e17H9QfB`T72EYIq00UqE41fVJ00!WH1;7msZjWi8&th$vE@S}u1YlodJU@c~ z957`knCp8Q0d{@Haxf@7(P8F#RU<&vXDs^&P-hCqnd>Eu0Gw}b6L1Fswwc07p^;rp z-G>9 zIUxW%Mw0?P0v-AY{E+IT$cH67K&~4Xod8<`#C1#FmB(OifyO{jK@H*F2Hi78btLEn zwao>;^>@mAy3lZb>F>CF3pyjbYOfTGTc#05=Bf%Q+ike%{ZYvO=Roi6^?+@7p#WN7 z29gcXB_Y+hKd|k9-$jJl;({;O!Fx^So`8;+iWkYT?rqi$YRk)xC`NWRT4&yXZFrX9 zgQei4m50U2w{%`&!JWFjrqe5+r8fN=`{%sE;OEKbq;Ss?#n#b!;4WxYa26wHUqG{6 z1U^eXRxJ6L!79b%xu};$KpaS}2>0NO6;E4z1HGgvL0S0FBQ!<9z%PSNb^!T8nh`kD zC}$5qw*#!&zB<+#I2fH01|`lOT5S$jb#iVrOQYpu9}N!&2-a;kL3vec;yevx~x7_ zXO<)>B|Mnlw1BFR${kKkv;a7lm~Tk7wS+X<^B-G%Wd^_i7ytuc0J^e20t^5+%BGU? SoIwfz00001Gin) zU%09i+|B!+J-fktQ`@3M|HwO~8xQwx`|)>z=1)#}#hF1_YtPOr&M1>>*m}8c_KvUD zRi`JdvmUzxJI56W1C$o>I?0Ny9J-EODGltwCb)EvBS1pWj ko^$&}?6)!elldT)z%XZvPmdKI;Vst0LjHySO5S3 literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause1_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause1_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..e86f91f938b403560e03e32e7a40691232397fa4 GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^20(1a!3HE7qorm6DVB6cUq=Rp^(V|(yIunMk|nMY zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5XW_Y?dhE&{ob892-Ap-%{fSyai z8=N9kWU~z-JOkw4!Qkg8rrSw#<>(joH-lzJnW8Vg4+vZrZGAM8q>@6=&d2BL$ zWzopr0K!32od5s; literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause2.png new file mode 100755 index 0000000000000000000000000000000000000000..d18cf420d7fa06eea3c51d04607b4b7b67a4626e GIT binary patch literal 261 zcmeAS@N?(olHy`uVBq!ia0vp^20(1a!3HE7qorm6DVB6cUq=Rp^(V|(yIunMk|nMY zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5XrhB?LhE&{ob4!u$kb?kgz_csM z1y{c)$+R;U&WUSzWyLZtWhJ}glsGHz z+3o&Jz5j9V@^gQd*2QE9K40P|@nGr|PUbz6tr8oqG6Ai@1vdCPOa>~PGYP0LJ?Odi gKW@f1vL6H!7z%3DJ=ujtEkPPRUHx3vIVCg!0I`5m`2YX_ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/pause2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/pause2_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..f7259a3144cf4037e5a53d356f25e6e6496d007d GIT binary patch literal 261 zcmeAS@N?(olHy`uVBq!ia0vp^20(1a!3HE7qorm6DVB6cUq=Rp^(V|(yIunMk|nMY zCBgY=CFO}lsSJ)O`AMk?p1FzXsX?iUDV2pMQ*D5XrhB?LhE&{ob892lAp;TC3vRot zzlfJy>=&`Wr1wo+W|w$Ux!{`*anUy7Myk^bI8IIp%Fg{Z$9eN-hBb4)-9Ga4Uqq2* zTjd<_cYo&`e|i1r-?vdp{tLWcwJ;uA;wO>d^{S#3XbDcRjWJ`gRboSOkUGx?uU9oo her#m@@VsF=12e-a6QOj!hM3hLt)8xaF6*2UngAftS#SUV literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play1.png new file mode 100755 index 0000000000000000000000000000000000000000..1c0924fdf5433568ac6229969470310eb5ce02ce GIT binary patch literal 644 zcmV-~0(#ZbAQ+(FxVem z*%0XjOrmZs8f+9$ZY~;Z78vQGfk9yCMFWe#-;0JD^ESvP&;z?54Zig+9Fl?S!JdN? z@HzMg27wyb2OYLms14SH4%0Gf>U>e2{?IrKb*sM$T1+axGkX{P?kFH*>H$* zBT$p4-nQxN?TlFGdf<09zV%FwSm#y_{=XTpuFl;ESXt+z2()G3HANOjUZBfTI*L)? eLJ{XzfB^sx6Kx}1bYblP0000@4oNu-Z_!3tZpl%DvC4_a0#A3id8^o5!AsA zh%*Y9tb-Of15ri+ohgt5Zy*aM*#t~xK?yvAG^2n?0^AD;Mgfzw%tlFOhfY9AKV+pp za?&qU0wz&esampf(FvHuW$&oVUP32el9JtA@9Hr|0TX?$>ViC&W)#qw0R`{^GK>Ny zbD#1G``oeCuC0Bm>ujJqIV? zbMOxg0yVG?I&7;@8>|T(re)OB`GP$Cp>Y`MoI&6g9D-M`{}{GFQyR3v_CD|dcEFOK z!G|#eU!Vw1K|hEO9H+n)I0ElMe&{#^9>Jdc3_PRk0sW-6!1AaDKdKq%ON|$$=0}>K z1@sr@$Fj0{{v!Y~;^ZrC$I5002ovPDHLkV1k_R20Z`( literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/play2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/play2.png new file mode 100755 index 0000000000000000000000000000000000000000..d34c303719f77deaa3693966b3a60d46e359a6d3 GIT binary patch literal 644 zcmV-~0(ZUkL{JvpbB-FVCfore}UcCNe*a8hUXkFU-z&qFg z^MedNj2ZX@MQ{Xqeth6C1unoIc=Pi^haqqew)oTc1la@nNw0y0pavh*4D?vzMb>qk`=L%R1WAK9r=(EZWnBg%Xw0JF{Z%}5PcWpRC zc@e1cskdx8dpj=cTo3%l#gFKP)XP!ztm(*Xo#6nQ9WPz>M$M1z`$i7O);6IU$#2QE$AnUMGcT=@qKD_2HW%F=}< zK0`Euq5+Y&fHJ&hrqk;=nL|JW+IG&Jwv&Fzmxg58-hTHyZ*P%GDG_037?A{#K#@R^ zK#@R^K%s?M=moAkJr-&K26~1=WkCsGyreA115ki^U>&fU#!$7*SXO`*9hZUAz+a^* zPzA6N*g)b)2j^7LaW@^?fa}01;4E+ssKPGLE_$#gMxmRDtH2@PL>L7^H);hQ0md}l zqY~H!Yyo}%{s@aeXe}p;7;KZkZgRVKz;saSAdD*=z|%fuB8+dBfO~={P@HD5+2^c` zG4CSqK#*JvYnSs|vX0-tn0J<3Z^&1Hpo+$dm(|_@Mt|p;jlkMD;DRxW>&+4I2eq>*1}_UJ6GaOnE`tQLK5y)`! zEKajg4X{kxkd-v$YOF)pMm_<=>rm+;pr_u9!E$dUnc;37Ii&$i?z*JCVIBOq+AT-lUr7Cs&Y8 zvZ`Yl3AqyQ`-eE2uw|FA6u*sb$UH}m`pEwZ1OYn6-LV_X@#|pRjV@wTn@q*81yUgP z(Ic8 zR8_VQAKXFAdSfTEci^`cvp~z70xhujspYvTyC!{0#O;3gGqarwK(l%ee$&U?hauLS z3gooEnbp$GLrk%gnFmlCJ%sMP6OOg{X|rckr8USLb39GBBY_|etwK+>#NM=Sx={vP z?v*GWCLIsW#YM+OX&~dqBisly;k394IL6(SEsg~OH^X}9Tgb&hVO$lPZX3}r%>7WY zF(&dmW%URIR2y`m97N>E5eHQtti4KZbo3%8ue6x zcsU6~S`ZBT|B8e)novngp?Q}(Q`~vELo1_KAOddOo(ZTpm~F8-FQ|_zNFcBx z9K>sp{}Lxiz?$uaVE8g>Q$p8X>O$rhVSC}M| zKLo^m)k$WH!4hLccU7-DRw#12aS{l3cHsteFFW+Hrdc52rv>->I-dmkJG(5AJCp-) zOK}x(yZy`9b#HvoyF{_A=AN3Xd+SYmEr>*Lc5x;uCG?%mn#ZtY1q|$ z&Y5%0%#=@mrIG!gW&P|e8P6MRKn4KnPXL_&tu3VnS$*)#kDzUQ(ZF#R`oiu={hTel;-wzB9XM6FpE5J8L zK$qhAE1{E0&cx8D3-GZBTJPiXE@sGW(v;hoDSyZBaT#*{#P7YR$h*y9O!6w0~nz7m<9N_J_|G20mK$*iLG)_;(Popv)xx#I)@!_ zi+k}}9yjY*w>g13d^{7pq36zOA(I=6wmk@tzUwQ1j)p!0ZOj17et@!8hU1^ET(4&-RP5GIqNxTk#qQwZXWL2^ix|~PsaWKI- zFy64@vfBZY0Vb_1G=oloKIj3Cl(tv}*1T9OaC$wwCb13vZ5i0K%j1&U0MeX70#pfI z0v)9|7ll)O4Cd6_mw6M+ZojCDKZ*fiQwES`6bhh4ZZeq`yHL+Tca%o@Eferf&w<5n zd0g=TKx)(t(B;reRl_%^-E_o|Trwh#ioXkx-t=(X_mWT21yG&Ko^-RSPAu;kdP`IQ z(mD(p8$8M?_viwMz_Atfz5>kov(yV-A{<}s;kd5?i;l?`AgbG3s2IvWfN6cl%0Ud7 zl!KDb5aT^MKQDl8RrsAYQ5%1om66csh7d&Mw5QHkHDPK{fF`>PN)7i{rzVzV0a9)J zV{~qd;?&%r0HufLT<(NvZDr*sNRYv#H7X*2MGd(EG>jc8kET#ntYu{^h}C}baM)Mm z3eYu*9%#h_eq?1XG9=b{lt*#`DBbs1;uF>n1lj*q#fVk|Gn?oaAX`z!kgO{Cb$+zf(gz35}T@$fce@y0E4IHKZN5zhu2jwv8||bN%csPBs%u z%XHDBziA(JyR6Yhd*up%GoPvF&B82cEH?I6H1W7mul3;+$8o8OiN^ZzL1WVMU$I9k zI0+*RfSSOdhV0$Vr}0SWaWX^Aagi#IJT4nRR1_PUax9@cW9JcjKEN5BgDEx`o%U?a z&iZ%7(c`lLG*ngd(aj#P;j)~+=onX%&(h|H47Oi!ctkdU6a{aox4ztEQI1y;z@d-e zQTtSVJtgCcqt)2}QrUS@D}$u6rtOnOe3V8X)ExvMp}dt|ar)@Hsf;xJ+&>kdhytG~ ztuY8^e*_<%#a@j7g;Wk806}iBSz}MB-Yb#~pnVFY>oDqQTWE!p zr2|83?$NO&UWKs>UG0D`9I80nnhhYb!R@K{LRdaRtC`g#p2W-U&cUI6FKQF3H0OV+ zclymwB%PjCB>70K0D(owhZfL_`{?S8vL=29O$h2u$dvE&s~>>AO>3iLj443ExLeeB z-+;V`R*A%v{=gDT#o*Wi84`Mf)tEULu48!0;dUgdEq=23%?dh>D@ZmEpCamFFCe~o znMbY3T<~FTa=b?lN^H{Ub9zZ!+2Nc0mOFO3-n`}rM?D0|q#zA8WlYv4K9#IE z!jpz-9pjhEl+EKF8x^!Xll22ZE?5hT81I)sUH~;K%xsGGl0#nr6t$))ET;wjXJ;+| z(JIkIN<0+i5&NvNMu*}#$2ebc=2OqANQT?3Q`qhdok3&K*%PtPMn|ivlg|eM=qW{1 zJ=o~9FDgrPl14TBk7|PO9hg@~5P;fLEW(2>rX{%LhK~`kXEAl{`-zO-%W~i4ier`h zZ|g>|iIAG?47td2lO3ZszbWb0dUkCVBP~b>wWny zpO4$-0g?s!&I3rNmy}t>jj(YbbO&6)SjFjGULBqfKqPNKUsD{ndqC<4ol_FvbXlW# zB(wK)% z-U&9{RPcZju^8_RQKu{cNEdosMN`c40M1829uYLJqFKosq29X<1)mJ(eFiB1xvX?P z)LKgdW5CiW$PH&wtt!Kn6rS7%aDEN?sC*O8CYMQzp(?xQWOh_yfb-nLO!1YhaJ~(+ zLZ4!qZgUKL-DKQzLIzVhEN{87PF|CZI<|#38~Z8=BwDq8eg<g zxr3B0p>5Fy?EqMg_H^jFp^jb8NB;RaJG6C*d=qV-DpuA{1wPu3(V}Qm-!sU=H<8l% z<4d4t7vLF`g*QWAg6=I8@>UUgmbAw8Ph?iKNOW`%OJ;`(>l@jB{|TnxQmpc6#Ptt{ zcL~v+`w1>kw7*@I7LZ?~>1i6GcY4NM{RE&?`3w4CInQpy1oGRQvy Z3;?8)3)kRq7FGZN002ovPDHLkV1nzVWkdh~ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/player2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/player2.png new file mode 100755 index 0000000000000000000000000000000000000000..7a099f7cbc6b3b37f9fde3d8991ebc8dfd7f54c8 GIT binary patch literal 1290 zcmV+l1@-!gP)xa#EWr`qimBzPd%svMh?QJK~W9kwB3^kwB3^ zkwBqEVXl9m`&lZ~0`&J3hRT8}!J@pfAQxZ(>c9k8A!{h#VJ*MGlpL4A32-4_1u6v_ z!FmxVX5*T2IabTD9b5y)!6|SWw8AdXKDn`GMWI`SE8qY)8b*QGjoQGYV1c3EDB~Of zwt^qQnXm}N)^f_jU|R)V61RH?CWBfBaa`#H&-j#yIKEv0_Xkm+B6*4}K4)c)d6&Va zAh{UVE*G-0j^E&zcTQYyz*m7#MRQiUDA^@wXcM>pcDb@++c@yiHr%u|C1W-()R6`#OThWlFKnWwuwD2^?={l`}R#wZxyU_ErL}l~<<>*rb zzg#O>&C;|WT`W^0&Co4aPx~(7RYItnG>m?)f$l**%?At;;A^Nz-mf}h* zErE2$Hwx0PjNPFap<1&%RT5}JRiJ8m@5&NlG>dD$E3RZiOQ58xX_ufvE_P8Jm(r6Q z-Sc0ps0h^GQ)p3U{6w=8>Pt5n_TBOe!t-P0I%r9AyJJapX>3JxsHFME_fE$B=*RBj zT z9-(dQ!m(CL(@Cm9FxQPat{%88fe?pQ(335yH?4nHu}%qkP2OS5?ukySl>?Hy^~$cTt^noJEm?2MEYf$Bx9 zi6_k)Q7miE88T+ z{Q{3z^#6m*7mATUkw6h5fg*t-fg*tdi9Z4i03sp*7@m4q%>V!Z07*qoM6N<$g1rk{ AZ~y=R literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/player2_pressed.png b/TMessagesProj/src/main/res/drawable-xxhdpi/player2_pressed.png new file mode 100755 index 0000000000000000000000000000000000000000..8fb075f6f02e2a44448241afc99f5f22f5d2a99a GIT binary patch literal 2436 zcmV-~348X5P)^Q05ILxH@;DEeGmWK_G^!jEkLV-(^Ik{pOn4wX~nBtlw?Wu zhP?0N-|4GQQsZieuIWK7t99r(y%Xp&bYrp2wR?fzqdilqhGz8UuWW7SfFFaTH%ndf=p7!{2;w>V z&w;)YE;H`&d!B9TXnI+6Y)}*G{PgAYdMImEsnfW!LDHd02~t|`*<4BcJ`MZ=SZd`) zAZA~>qL?Ix9XUg=-&!hK_5h*2xS;6;%qb7DT23^8%!jHZ75b6T}m|9l*!P z)Z8P#+|=Im8#L$?;*0Kjp5ph!0+GP0h*v{!J)Sf9iPJsozr-kcqy$N8-6CjzNcg!ZupGCsN?9#KJi^^)DAt1BgFrKzB4hM2Rbu&`_d zL-P6}Ty2>6>cL70`dX2CWN8qtb%HgZ@d6o2+Di!mD(WqlpbmCrdbChI&$0$K#G+Fc z0sEeE3A&$9&j`}_|F8x(Z@{~sIDBWh1g#*d=YO(UV95AS5F=U`ZnnZHL0)2x$3VsW zuh~ElANDP3wOh#90w?N$d;J%c`jl&}&Y@GrSU*0y%`O^36g=wI?J(q@P;=_7cF*mY zAf)T3TxqEe1)s+-tLBH=kCujb5)o%H=4X$Avb~~j>yP-~Q4^}crcc@S!OfI28y9GH zF4jlHpR>ZrnPiLDFxw4szQw3@GlNAy)K+$u?PLi@ivf;}=W!M}(NZTdsE3IB=Y^7! z#KVu{!FAT_KxG3g%68=4P-HBUC8~~GY)25E6l*UymK9#HUxI3k50{pmxMDARk9}1z zK^KX`AGaf@oyb5ZgsUgV?kf9tD!VTqvjiDNvT@>YmmNV7YTidEL6`G2p1Tzb$_9bR`5)%QTYU@k^Vb-xdvKPOe`_P@Ir< zP$)eL!fnM9JM4m}W*EBRBMzs^CdgohSc2xUBNj@v2^tveTiM$JlT_-t;&ud$5ejNC zG(+5rRv~1_o(i&KANvbhD{Q4!*Y$Sd@Q58j+y_q=_FX2C=@P1-I-Po^vraPUqLZ|YwooWRTt#i{;rxT!lXDn_>myKIRwzNbK|RwMCj=cUA}Fi% z2(QIjprFNUBV6ZkM!&flM_&IZyADJ)F`>z-npE2i!{{tHJb^-W*hw{ioRH&vp_GUiN6rxMv7C(ms~(4ZO3!ygi481vJy^fiG}pFJAW$X?jomz4-b@Actqo zQ2c`ymHQ6+T-M(B+w^Y}<;S|jrNpXwCI<;&Fg2cf2(Cr#V&r={k|GGoUyMxIQ`rsh z?Pc{kq{pN2hb%V08~{1bIje@8*+~nMJdp3Eh# zff-8#M-%<2$<$_^uUVY3*=~DOwFGhFYw7={zQ9mAfYEI$0^`z}24i%})Wxa$vdQcw zc4c+gik!pcUc1u$Vths5cdW!-G5!utpe69MugN#&S|CO)26tMMO=rKHf7yXHzQoKo z){*Zwxw)Z^o84|hL-xq8_`<#yM9w8~#UNQ-h1zf(pQ|cyDo;;-2y)I?onS{$wS_0& zj?H~~D?s_Z#N94EnI1s!Jg<0_*S#U{u}a?1=Q&MgD$|2fXC%PhTkE$uyDN*yvOFlsQWUNjRs6~b5zit3-`LxQ5vd!v-swPQ665HN zT(=eU0ZIH#iSL1^H6~G|S3wXj@ZkM?kqPd?5JNloX-#%5Vs-I8rJpl|8ZL+Zi?kmV5HgWLxkG}}q?7}^G6@C!-6L7&T<~=p&S&pz7 zpZID5e@S#v1=r$BKzw5TF#G6#f^pqyHGLX4RNy*3wT`P%hP;_9yCYG zn%hjxFOLE*G{St++<(zEM^JSpAyuDap@jWUfB^u%Ivn7*IAxju0000wy literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/rec.png b/TMessagesProj/src/main/res/drawable-xxhdpi/rec.png new file mode 100755 index 0000000000000000000000000000000000000000..e95a6252bc555acda25fabd9e468d3b1f19f46b2 GIT binary patch literal 573 zcmV-D0>b@?P)s+`L@cq9SoV-5mF>_*qC`a5Lptm!x)iY_ibS2FOKC}~RYatS z=+duKL@Z0h64Cd`Gi;gNd1rQJ-oAXyVO~3*dFS1EpJ!?whx>(%G=T&Nfo9Oa$6N3P zZonlt0(qGqY}w0*T8VK1Fa?sJwal0hA3J$0HHm^NFm6Xe2Pa@!xEH#qZLr3Vx1t0MKIEzNA~RsgN`f9ikMK70V62=3 zt?EhPPqskEUkTl8nmkO9*T*l3B#Zc&2>)|_Bm%r(R3vFg#1zbek=RpWKqrYlp)RyG zq9hR(Y7^ER>dAA|csJN6AcWtJH?g+T^ zSYQ{dRI0XVUG#b=LIM*q&CeU?1{s$H7Qra(6|!w0?I(k5{Zj24k#B0jX+yjN+Q6Ix z8Tw2x1iGxY2ANL-MShQ@xnYTOm!|Xg3Y`V>d^`gO;8oW4TYv!o?>|#aN~LK(00000 LNkvXXu0mjfS0L`j literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/slidearrow.png b/TMessagesProj/src/main/res/drawable-xxhdpi/slidearrow.png new file mode 100755 index 0000000000000000000000000000000000000000..f60d77275b598b712b1d8a468ea19a4996b86d1c GIT binary patch literal 416 zcmV;R0bl-!P)M800004XF*Lt006JZ zHwB960000PbVXQnQ*UN;cVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzGD$>1RCwCF z*v$#TFcbjb+JgfK1vl_4F5t;6y!KjWZ~}X52XF%~o?O6_C!IhL9Ke$=;DZ>_wC}GE z9&OXY2T7a0WYRQECW*2v|E}A%ox`Dk_FMRDo+-u;y10b%l^Nzi3&yJR7?a>FflX~Q-OwHNeHs040t{kl)}`Z zF3bdqVP;T>nL<8h4k0!j148Tr`N&2H>_%52JtOxIkd-#(prtkFzl{jqt-$a*bKu$v zjLjfsVAtGe0;Xntb+GS$rV0-EQzt>Fh$~G@4Wv|8Vvt%lia<_9%m%r&J`0qrV#uI$ zeMSPsD^IA1`@pU0nmPeqAy4YN1rg9e8)$!=YHGG22x2~E%kaYF4^c+=!IMLJ#?A=VB@)o z&f8_~)ci>`tGk?g)@Ax;+g)yKwY*i`+wRIJ$Y0Rj|MD8!t~q-puU+Q(W%cFSZM$g) zw{5ig7uy!M_-!)hGE3RZbAo<-+q38Gi{qy6zu);<61e|S|Lgv9A^#cWeZO2h|F?48 zGMl?KRoQQ(Z0EcP@v8KFn|yqA_`5Z~p7%>PO@DCV!a5`4i+hV7vQ>7(Dm}P2JMrd6 zujiHB*{?e;%5PrYCs)=|eSK|m%e}qjGykO8eXqDLpUA=cgZY>00fy5N$6f3C+Qop0 OkHOQ`&t;ucLK6Vdj7Vqz literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable/mic_button_states.xml b/TMessagesProj/src/main/res/drawable/mic_button_states.xml new file mode 100644 index 000000000..791017eda --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/mic_button_states.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/pause1_states.xml b/TMessagesProj/src/main/res/drawable/pause1_states.xml new file mode 100644 index 000000000..79b4a63da --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/pause1_states.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/pause2_states.xml b/TMessagesProj/src/main/res/drawable/pause2_states.xml new file mode 100644 index 000000000..741d3cb33 --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/pause2_states.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/photo_progress_chat.xml b/TMessagesProj/src/main/res/drawable/photo_progress_chat.xml index e0d1896b5..37f07f523 100644 --- a/TMessagesProj/src/main/res/drawable/photo_progress_chat.xml +++ b/TMessagesProj/src/main/res/drawable/photo_progress_chat.xml @@ -1,11 +1,3 @@ - - @@ -15,7 +7,7 @@ - + diff --git a/TMessagesProj/src/main/res/drawable/play1_states.xml b/TMessagesProj/src/main/res/drawable/play1_states.xml new file mode 100644 index 000000000..cdb37c5f3 --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/play1_states.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/play2_states.xml b/TMessagesProj/src/main/res/drawable/play2_states.xml new file mode 100644 index 000000000..c53194ad6 --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/play2_states.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/progress_chat.xml b/TMessagesProj/src/main/res/drawable/progress_chat.xml new file mode 100644 index 000000000..0656b5e83 --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/progress_chat.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/drawable/send_button_states.xml b/TMessagesProj/src/main/res/drawable/send_button_states.xml index 92132f986..90174a3d0 100644 --- a/TMessagesProj/src/main/res/drawable/send_button_states.xml +++ b/TMessagesProj/src/main/res/drawable/send_button_states.xml @@ -1,6 +1,6 @@ + - + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/chat_group_incoming_document_layout.xml b/TMessagesProj/src/main/res/layout/chat_group_incoming_document_layout.xml index 92c29c4db..c186aa4c6 100644 --- a/TMessagesProj/src/main/res/layout/chat_group_incoming_document_layout.xml +++ b/TMessagesProj/src/main/res/layout/chat_group_incoming_document_layout.xml @@ -106,7 +106,7 @@ android:layout_width="fill_parent" android:layout_height="3dp" android:layout_gravity="left|center_vertical" - android:progressDrawable="@drawable/photo_progress_chat" + android:progressDrawable="@drawable/progress_chat" style="?android:attr/progressBarStyleHorizontal" android:progress="50" android:layout_marginLeft="12dp" diff --git a/TMessagesProj/src/main/res/layout/chat_group_incoming_video_layout.xml b/TMessagesProj/src/main/res/layout/chat_group_incoming_video_layout.xml index 417f0d089..8af12c8d0 100644 --- a/TMessagesProj/src/main/res/layout/chat_group_incoming_video_layout.xml +++ b/TMessagesProj/src/main/res/layout/chat_group_incoming_video_layout.xml @@ -130,7 +130,7 @@ android:layout_width="fill_parent" android:layout_height="3dp" android:layout_gravity="left|center_vertical" - android:progressDrawable="@drawable/photo_progress_chat" + android:progressDrawable="@drawable/progress_chat" style="?android:attr/progressBarStyleHorizontal" android:progress="50" android:layout_marginLeft="12dp" diff --git a/TMessagesProj/src/main/res/layout/chat_incoming_document_layout.xml b/TMessagesProj/src/main/res/layout/chat_incoming_document_layout.xml index 5fa9cefa6..6911ef16a 100644 --- a/TMessagesProj/src/main/res/layout/chat_incoming_document_layout.xml +++ b/TMessagesProj/src/main/res/layout/chat_incoming_document_layout.xml @@ -96,7 +96,7 @@ android:layout_width="fill_parent" android:layout_height="3dp" android:layout_gravity="left|center_vertical" - android:progressDrawable="@drawable/photo_progress_chat" + android:progressDrawable="@drawable/progress_chat" style="?android:attr/progressBarStyleHorizontal" android:progress="50" android:layout_marginLeft="12dp" diff --git a/TMessagesProj/src/main/res/layout/chat_incoming_video_layout.xml b/TMessagesProj/src/main/res/layout/chat_incoming_video_layout.xml index 4f8802627..24a0d6a11 100644 --- a/TMessagesProj/src/main/res/layout/chat_incoming_video_layout.xml +++ b/TMessagesProj/src/main/res/layout/chat_incoming_video_layout.xml @@ -121,7 +121,7 @@ android:layout_width="fill_parent" android:layout_height="3dp" android:layout_gravity="left|center_vertical" - android:progressDrawable="@drawable/photo_progress_chat" + android:progressDrawable="@drawable/progress_chat" style="?android:attr/progressBarStyleHorizontal" android:progress="50" android:layout_marginLeft="12dp" diff --git a/TMessagesProj/src/main/res/layout/chat_layout.xml b/TMessagesProj/src/main/res/layout/chat_layout.xml index 3098d1a7d..6f7cc65f6 100644 --- a/TMessagesProj/src/main/res/layout/chat_layout.xml +++ b/TMessagesProj/src/main/res/layout/chat_layout.xml @@ -132,7 +132,7 @@ android:layout_alignBottom="@+id/chat_text_edit"/> + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/layout/chat_outgoing_document_layout.xml b/TMessagesProj/src/main/res/layout/chat_outgoing_document_layout.xml index 1845e7a8f..f948b65a5 100644 --- a/TMessagesProj/src/main/res/layout/chat_outgoing_document_layout.xml +++ b/TMessagesProj/src/main/res/layout/chat_outgoing_document_layout.xml @@ -37,7 +37,7 @@ android:layout_marginLeft="36dp" android:layout_marginRight="12dp" android:layout_gravity="right|center_vertical" - android:progressDrawable="@drawable/photo_progress_chat" + android:progressDrawable="@drawable/progress_chat" android:id="@+id/chat_view_action_progress" android:max="100"/> diff --git a/TMessagesProj/src/main/res/layout/chat_outgoing_video_layout.xml b/TMessagesProj/src/main/res/layout/chat_outgoing_video_layout.xml index 44a8c978b..ddd7ccc5c 100644 --- a/TMessagesProj/src/main/res/layout/chat_outgoing_video_layout.xml +++ b/TMessagesProj/src/main/res/layout/chat_outgoing_video_layout.xml @@ -31,7 +31,7 @@ android:layout_width="fill_parent" android:layout_height="3dp" android:layout_gravity="right|center_vertical" - android:progressDrawable="@drawable/photo_progress_chat" + android:progressDrawable="@drawable/progress_chat" style="?android:attr/progressBarStyleHorizontal" android:progress="50" android:layout_marginLeft="36dp" diff --git a/TMessagesProj/src/main/res/values-es/strings.xml b/TMessagesProj/src/main/res/values-es/strings.xml index 32dbcbfde..352603dfd 100644 --- a/TMessagesProj/src/main/res/values-es/strings.xml +++ b/TMessagesProj/src/main/res/values-es/strings.xml @@ -24,20 +24,20 @@ Ingresa tu nombre y apellidos Nombre (requerido) - Apellido/s (opcional) + Apellido(s) (opcional) Cancelar registro Chats Buscar - Mensajes nuevos + Nuevo mensaje Ajustes Contactos Nuevo grupo ayer Sin resultados No tienes conversaciones todavía... - Empieza a chatear presionando el\nbotón de componer en la esquina superior\nderecha o ve a la sección de Contactos. + Empieza a chatear presionando el\nbotón de Nuevo mensaje en la esquina superior\nderecha o ve a la sección de Contactos. Esperando red... Conectando... Actualizando... @@ -170,9 +170,9 @@ INGRESA EL NOMBRE DEL GRUPO - Multimedia compartida + Fotos y vídeos Información del grupo - MULTIMEDIA COMPARTIDA + FOTOS Y VÍDEOS AJUSTES Añadir miembro Eliminar y salir del grupo @@ -210,11 +210,11 @@ Restablecer todas las notificaciones Tamaño del texto - Haz una pregunta + Hacer una pregunta Activar animaciones Desbloquear Mantén pulsado un usuario para desbloquearlo - No hay usuarios bloqueados aún + No hay usuarios bloqueados TU NÚMERO DE TELÉFONO NOTIFICACIONES DE MENSAJES Alerta @@ -239,7 +239,7 @@ SOPORTE Fondo de chat MENSAJES - Enviar con enter + Enviar con ‘Enter’ Cerrar todas las otras sesiones DESCARGA AUTOMÁTICA DE FOTOS Grupos