From 368822d20f879f5ca851e4cbf13506eda4e48bfc Mon Sep 17 00:00:00 2001 From: DrKLO Date: Tue, 31 Aug 2021 22:06:39 +0300 Subject: [PATCH] Update to 8.0.0 (2406) --- Dockerfile | 2 +- TMessagesProj/build.gradle | 6 +- TMessagesProj/jni/image.cpp | 2 +- .../jni/tgnet/ConnectionsManager.cpp | 2 +- TMessagesProj/jni/voip/CMakeLists.txt | 4 +- .../org_telegram_messenger_voip_Instance.cpp | 86 +- TMessagesProj/jni/voip/tgcalls/Instance.h | 1 + .../jni/voip/tgcalls/InstanceImpl.cpp | 6 + TMessagesProj/jni/voip/tgcalls/InstanceImpl.h | 1 + TMessagesProj/jni/voip/tgcalls/Manager.cpp | 6 + TMessagesProj/jni/voip/tgcalls/Manager.h | 1 + .../jni/voip/tgcalls/MediaManager.cpp | 33 +- TMessagesProj/jni/voip/tgcalls/MediaManager.h | 1 + .../jni/voip/tgcalls/VideoCaptureInterface.h | 3 +- .../tgcalls/VideoCaptureInterfaceImpl.cpp | 35 +- .../voip/tgcalls/VideoCaptureInterfaceImpl.h | 13 +- ...reamingPart.cpp => AudioStreamingPart.cpp} | 97 +- .../voip/tgcalls/group/AudioStreamingPart.h | 41 + .../tgcalls/group/GroupInstanceCustomImpl.cpp | 445 +++--- .../voip/tgcalls/group/GroupInstanceImpl.h | 9 +- .../tgcalls/group/StreamingMediaContext.cpp | 869 +++++++++++ .../tgcalls/group/StreamingMediaContext.h | 53 + .../jni/voip/tgcalls/group/StreamingPart.h | 39 - .../voip/tgcalls/group/VideoStreamingPart.cpp | 659 ++++++++ .../voip/tgcalls/group/VideoStreamingPart.h | 53 + .../tgcalls/legacy/InstanceImplLegacy.cpp | 3 + .../voip/tgcalls/legacy/InstanceImplLegacy.h | 1 + .../tgcalls/platform/android/AndroidContext.h | 3 +- .../reference/InstanceImplReference.cpp | 9 +- .../tgcalls/reference/InstanceImplReference.h | 2 + .../jni/voip/tgcalls/v2/InstanceV2Impl.h | 2 + .../widget/ChatListItemAnimator.java | 4 +- .../widget/LinearLayoutManager.java | 12 + .../widget/LinearSmoothScrollerCustom.java | 5 +- .../telegram/messenger/AndroidUtilities.java | 41 +- .../telegram/messenger/ApplicationLoader.java | 2 + .../org/telegram/messenger/BuildVars.java | 4 +- .../org/telegram/messenger/ChatObject.java | 13 +- .../org/telegram/messenger/DispatchQueue.java | 2 + .../telegram/messenger/DispatchQueuePool.java | 21 +- .../java/org/telegram/messenger/Emoji.java | 4 + .../messenger/ForwardingMessagesParams.java | 178 +++ .../messenger/GcmPushListenerService.java | 38 + .../org/telegram/messenger/ImageLocation.java | 3 +- .../messenger/MediaDataController.java | 1 - .../org/telegram/messenger/MessageObject.java | 75 +- .../messenger/MessagesController.java | 117 +- .../telegram/messenger/MessagesStorage.java | 15 +- .../messenger/NotificationCenter.java | 4 +- .../messenger/SendMessagesHelper.java | 169 +- .../org/telegram/messenger/SharedConfig.java | 17 + .../org/telegram/messenger/SvgHelper.java | 19 +- .../org/telegram/messenger/UserConfig.java | 7 - .../messenger/voip/NativeInstance.java | 12 +- .../telegram/messenger/voip/VoIPService.java | 75 +- .../telegram/tgnet/ConnectionsManager.java | 19 +- .../main/java/org/telegram/tgnet/TLRPC.java | 968 +++++++++++- .../ui/ActionBar/ActionBarLayout.java | 16 +- .../ui/ActionBar/ActionBarMenuSubItem.java | 6 + .../ui/ActionBar/ActionBarPopupWindow.java | 22 +- .../telegram/ui/ActionBar/SimpleTextView.java | 68 + .../java/org/telegram/ui/ActionBar/Theme.java | 269 +++- .../telegram/ui/Adapters/DialogsAdapter.java | 4 + .../ui/Adapters/DialogsSearchAdapter.java | 36 +- .../telegram/ui/Adapters/MentionsAdapter.java | 342 +++- .../telegram/ui/Adapters/StickersAdapter.java | 366 +---- .../java/org/telegram/ui/ArticleViewer.java | 5 +- .../ui/CancelAccountDeletionActivity.java | 2 +- .../org/telegram/ui/Cells/BotHelpCell.java | 4 +- .../telegram/ui/Cells/ChatMessageCell.java | 407 ++++- .../org/telegram/ui/Cells/DialogCell.java | 40 +- .../org/telegram/ui/Cells/HintDialogCell.java | 45 +- .../org/telegram/ui/Cells/LanguageCell.java | 3 + .../telegram/ui/Cells/SharedAudioCell.java | 6 +- .../org/telegram/ui/Cells/StickerCell.java | 15 +- .../telegram/ui/Cells/StickerEmojiCell.java | 6 +- .../org/telegram/ui/ChangePhoneActivity.java | 2 +- .../telegram/ui/ChannelAdminLogActivity.java | 8 +- .../telegram/ui/ChannelCreateActivity.java | 8 +- .../java/org/telegram/ui/ChatActivity.java | 1385 ++++++++++++----- .../org/telegram/ui/ChatEditActivity.java | 15 +- .../telegram/ui/ChatPullingDownDrawable.java | 671 ++++++++ .../ui/Components/AudioPlayerAlert.java | 4 +- .../ui/Components/AvatarDrawable.java | 3 + .../ui/Components/BlurBehindDrawable.java | 78 +- .../telegram/ui/Components/BluredView.java | 36 + .../ui/Components/ChatActivityEnterView.java | 262 ++-- .../ui/Components/ChatAvatarContainer.java | 19 +- .../telegram/ui/Components/CheckBoxBase.java | 8 +- .../ChoosingStickerStatusDrawable.java | 134 ++ .../telegram/ui/Components/ColorPicker.java | 126 +- .../telegram/ui/Components/CounterView.java | 618 ++++---- .../ui/Components/EditTextBoldCursor.java | 4 +- .../telegram/ui/Components/EditTextEmoji.java | 2 +- .../org/telegram/ui/Components/EmojiView.java | 506 +++++- .../ui/Components/ForwardingPreviewView.java | 998 ++++++++++++ .../ui/Components/FragmentContextView.java | 25 +- .../ui/Components/GroupCallPipAlertView.java | 8 +- .../ui/Components/GroupCallPipButton.java | 8 +- .../ui/Components/GroupCallRecordAlert.java | 346 ++++ .../ui/Components/GroupedPhotosListView.java | 6 +- .../telegram/ui/Components/ImageUpdater.java | 7 +- .../ui/Components/InstantCameraView.java | 2 +- .../telegram/ui/Components/JoinCallAlert.java | 42 +- .../ui/Components/JoinCallByUrlAlert.java | 6 +- .../ui/Components/MediaActionDrawable.java | 33 +- .../Components/MotionBackgroundDrawable.java | 30 +- .../PhotoViewerCaptionEnterView.java | 2 +- .../ui/Components/PopupAudioView.java | 4 +- .../ui/Components/RLottieDrawable.java | 21 +- .../ui/Components/RecordStatusDrawable.java | 3 + .../ui/Components/RecyclerListView.java | 4 + .../ui/Components/ScrollSlidingTabStrip.java | 710 +++++++-- .../ui/Components/SearchViewPager.java | 2 +- .../telegram/ui/Components/ShareAlert.java | 591 +++++-- .../ui/Components/SharedMediaLayout.java | 2 +- .../Components/SizeNotifierFrameLayout.java | 8 +- .../ui/Components/StickerMasksAlert.java | 3 +- .../ui/Components/StickerTabView.java | 171 ++ .../ui/Components/ThemePreviewDrawable.java | 7 +- .../ui/Components/TrendingStickersLayout.java | 44 + .../org/telegram/ui/Components/UndoView.java | 54 +- .../voip/GroupCallRenderersContainer.java | 10 +- .../voip/PrivateVideoPreviewDialog.java | 4 +- .../ui/Components/voip/VoIPHelper.java | 4 +- .../java/org/telegram/ui/DialogsActivity.java | 71 +- .../org/telegram/ui/GroupCallActivity.java | 271 ++-- .../telegram/ui/GroupCreateFinalActivity.java | 8 +- .../java/org/telegram/ui/LaunchActivity.java | 44 +- .../java/org/telegram/ui/LoginActivity.java | 10 +- .../java/org/telegram/ui/MediaActivity.java | 2 +- .../ui/MessageEnterTransitionContainer.java | 5 - .../org/telegram/ui/PassportActivity.java | 2 +- .../java/org/telegram/ui/PhotoViewer.java | 29 +- .../telegram/ui/PrivacyControlActivity.java | 4 +- .../java/org/telegram/ui/ProfileActivity.java | 23 +- .../org/telegram/ui/SecretMediaViewer.java | 22 +- .../org/telegram/ui/StickersActivity.java | 41 +- .../ui/TextMessageEnterTransition.java | 66 +- .../java/org/telegram/ui/ThemeActivity.java | 4 +- .../org/telegram/ui/ThemePreviewActivity.java | 970 ++++++++---- .../org/telegram/ui/ThemeSetUrlActivity.java | 4 +- .../telegram/ui/WallpapersListActivity.java | 14 +- .../res/drawable-hdpi/emoji_tabs_faves.png | Bin 0 -> 1465 bytes .../res/drawable-hdpi/emoji_tabs_new1.png | Bin 0 -> 1495 bytes .../res/drawable-hdpi/emoji_tabs_new2.png | Bin 0 -> 474 bytes .../res/drawable-hdpi/emoji_tabs_new3.png | Bin 0 -> 1644 bytes .../res/drawable-hdpi/emoji_tabs_recent.png | Bin 0 -> 1564 bytes .../res/drawable-hdpi/msg_forward_check.png | Bin 0 -> 538 bytes .../res/drawable-hdpi/msg_forward_replace.png | Bin 0 -> 1174 bytes .../res/drawable-hdpi/msg_forward_send.png | Bin 0 -> 935 bytes .../res/drawable-hdpi/msg_panel_forward.png | Bin 222 -> 470 bytes .../res/drawable-mdpi/emoji_tabs_faves.png | Bin 0 -> 991 bytes .../res/drawable-mdpi/emoji_tabs_new1.png | Bin 0 -> 1056 bytes .../res/drawable-mdpi/emoji_tabs_new2.png | Bin 0 -> 367 bytes .../res/drawable-mdpi/emoji_tabs_new3.png | Bin 0 -> 1142 bytes .../res/drawable-mdpi/emoji_tabs_recent.png | Bin 0 -> 1051 bytes .../res/drawable-mdpi/msg_forward_check.png | Bin 0 -> 404 bytes .../res/drawable-mdpi/msg_forward_replace.png | Bin 0 -> 706 bytes .../res/drawable-mdpi/msg_forward_send.png | Bin 0 -> 644 bytes .../res/drawable-mdpi/msg_panel_forward.png | Bin 134 -> 390 bytes .../res/drawable-xhdpi/emoji_tabs_faves.png | Bin 0 -> 2038 bytes .../res/drawable-xhdpi/emoji_tabs_new1.png | Bin 0 -> 2050 bytes .../res/drawable-xhdpi/emoji_tabs_new2.png | Bin 0 -> 594 bytes .../res/drawable-xhdpi/emoji_tabs_new3.png | Bin 0 -> 2236 bytes .../res/drawable-xhdpi/emoji_tabs_recent.png | Bin 0 -> 2098 bytes .../res/drawable-xhdpi/msg_forward_check.png | Bin 0 -> 669 bytes .../drawable-xhdpi/msg_forward_replace.png | Bin 0 -> 1399 bytes .../res/drawable-xhdpi/msg_forward_send.png | Bin 0 -> 1219 bytes .../res/drawable-xhdpi/msg_panel_forward.png | Bin 262 -> 593 bytes .../res/drawable-xxhdpi/emoji_tabs_faves.png | Bin 0 -> 3157 bytes .../res/drawable-xxhdpi/emoji_tabs_new1.png | Bin 0 -> 3040 bytes .../res/drawable-xxhdpi/emoji_tabs_new2.png | Bin 0 -> 915 bytes .../res/drawable-xxhdpi/emoji_tabs_new3.png | Bin 0 -> 3296 bytes .../res/drawable-xxhdpi/emoji_tabs_recent.png | Bin 0 -> 3157 bytes .../res/drawable-xxhdpi/msg_forward_check.png | Bin 0 -> 997 bytes .../drawable-xxhdpi/msg_forward_replace.png | Bin 0 -> 2078 bytes .../res/drawable-xxhdpi/msg_forward_send.png | Bin 0 -> 1834 bytes .../res/drawable-xxhdpi/msg_panel_forward.png | Bin 384 -> 767 bytes .../src/main/res/raw/record_audio.svg | 23 + .../src/main/res/raw/record_video_l.svg | 42 + .../src/main/res/raw/record_video_p.svg | 42 + .../src/main/res/values-ar/strings.xml | 2 +- .../src/main/res/values-de/strings.xml | 2 +- .../src/main/res/values-es/strings.xml | 2 +- .../src/main/res/values-it/strings.xml | 2 +- .../src/main/res/values-ko/strings.xml | 2 +- .../src/main/res/values-nl/strings.xml | 2 +- .../src/main/res/values-pt-rBR/strings.xml | 2 +- TMessagesProj/src/main/res/values/strings.xml | 192 ++- gradle/wrapper/gradle-wrapper.properties | 2 +- 191 files changed, 12004 insertions(+), 2919 deletions(-) rename TMessagesProj/jni/voip/tgcalls/group/{StreamingPart.cpp => AudioStreamingPart.cpp} (82%) create mode 100644 TMessagesProj/jni/voip/tgcalls/group/AudioStreamingPart.h create mode 100644 TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.cpp create mode 100644 TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.h delete mode 100644 TMessagesProj/jni/voip/tgcalls/group/StreamingPart.h create mode 100644 TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.cpp create mode 100644 TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.h create mode 100644 TMessagesProj/src/main/java/org/telegram/messenger/ForwardingMessagesParams.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/ChatPullingDownDrawable.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/BluredView.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/ChoosingStickerStatusDrawable.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/ForwardingPreviewView.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallRecordAlert.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/StickerTabView.java create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/emoji_tabs_faves.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/emoji_tabs_new1.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/emoji_tabs_new2.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/emoji_tabs_new3.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/emoji_tabs_recent.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/msg_forward_check.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/msg_forward_replace.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/msg_forward_send.png mode change 100755 => 100644 TMessagesProj/src/main/res/drawable-hdpi/msg_panel_forward.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/emoji_tabs_faves.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/emoji_tabs_new1.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/emoji_tabs_new2.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/emoji_tabs_new3.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/emoji_tabs_recent.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/msg_forward_check.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/msg_forward_replace.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/msg_forward_send.png mode change 100755 => 100644 TMessagesProj/src/main/res/drawable-mdpi/msg_panel_forward.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/emoji_tabs_faves.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/emoji_tabs_new1.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/emoji_tabs_new2.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/emoji_tabs_new3.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/emoji_tabs_recent.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/msg_forward_check.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/msg_forward_replace.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/msg_forward_send.png mode change 100755 => 100644 TMessagesProj/src/main/res/drawable-xhdpi/msg_panel_forward.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_faves.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_new1.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_new2.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_new3.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_recent.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/msg_forward_check.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/msg_forward_replace.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/msg_forward_send.png mode change 100755 => 100644 TMessagesProj/src/main/res/drawable-xxhdpi/msg_panel_forward.png create mode 100644 TMessagesProj/src/main/res/raw/record_audio.svg create mode 100644 TMessagesProj/src/main/res/raw/record_video_l.svg create mode 100644 TMessagesProj/src/main/res/raw/record_video_p.svg diff --git a/Dockerfile b/Dockerfile index 91e717381..5a5658e8e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM gradle:6.7.1-jdk11 +FROM gradle:7.0.2-jdk11 ENV ANDROID_SDK_URL https://dl.google.com/android/repository/commandlinetools-linux-7302050_latest.zip ENV ANDROID_API_LEVEL android-31 diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle index 040fd1fe3..150328ff7 100644 --- a/TMessagesProj/build.gradle +++ b/TMessagesProj/build.gradle @@ -25,7 +25,7 @@ dependencies { compileOnly 'org.checkerframework:checker-qual:2.5.2' compileOnly 'org.checkerframework:checker-compat-qual:2.5.0' implementation 'com.google.firebase:firebase-messaging:22.0.0' - implementation 'com.google.firebase:firebase-config:21.0.0' + implementation 'com.google.firebase:firebase-config:21.0.1' implementation 'com.google.firebase:firebase-datatransport:18.0.1' implementation 'com.google.firebase:firebase-appindexing:20.0.0' implementation 'com.google.android.gms:play-services-maps:17.0.1' @@ -299,7 +299,7 @@ android { } } - defaultConfig.versionCode = 2390 + defaultConfig.versionCode = 2406 applicationVariants.all { variant -> variant.outputs.all { output -> @@ -318,7 +318,7 @@ android { defaultConfig { minSdkVersion 16 targetSdkVersion 29 - versionName "7.9.3" + versionName "8.0.0" vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi'] diff --git a/TMessagesProj/jni/image.cpp b/TMessagesProj/jni/image.cpp index 78aed909f..7171bafe5 100644 --- a/TMessagesProj/jni/image.cpp +++ b/TMessagesProj/jni/image.cpp @@ -1224,7 +1224,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_generateGradient(JNIEnv *en float directPixelY; float centerDistanceY; float centerDistanceY2; - int32_t colorsCount = colorsArray[12] == 0 ? 3 : 4; + int32_t colorsCount = colorsArray[12] == 0 && colorsArray[13] == 0 && colorsArray[14] == 0 && colorsArray[15] == 0 ? 3 : 4; for (int y = 0; y < height; y++) { if (pixelCache == nullptr) { diff --git a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp index 4da0ceff0..882af401a 100644 --- a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp +++ b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp @@ -2499,7 +2499,7 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t } else { currentCount = 0; } - if (!networkAvailable || currentCount >= 12) { + if (!networkAvailable || currentCount >= 16) { iter++; continue; } diff --git a/TMessagesProj/jni/voip/CMakeLists.txt b/TMessagesProj/jni/voip/CMakeLists.txt index ddba61d8e..15a3c9681 100644 --- a/TMessagesProj/jni/voip/CMakeLists.txt +++ b/TMessagesProj/jni/voip/CMakeLists.txt @@ -918,7 +918,9 @@ add_library(tgcalls STATIC voip/tgcalls/group/GroupNetworkManager.cpp voip/tgcalls/group/GroupInstanceCustomImpl.cpp voip/tgcalls/group/GroupJoinPayloadInternal.cpp - voip/tgcalls/group/StreamingPart.cpp + voip/tgcalls/group/AudioStreamingPart.cpp + voip/tgcalls/group/VideoStreamingPart.cpp + voip/tgcalls/group/StreamingMediaContext.cpp voip/tgcalls/third-party/json11.cpp voip/webrtc/rtc_base/async_invoker.cc diff --git a/TMessagesProj/jni/voip/org_telegram_messenger_voip_Instance.cpp b/TMessagesProj/jni/voip/org_telegram_messenger_voip_Instance.cpp index 20140b711..d08dcead3 100644 --- a/TMessagesProj/jni/voip/org_telegram_messenger_voip_Instance.cpp +++ b/TMessagesProj/jni/voip/org_telegram_messenger_voip_Instance.cpp @@ -76,10 +76,12 @@ class BroadcastPartTaskJava : public BroadcastPartTask { public: BroadcastPartTaskJava(std::shared_ptr platformContext, std::function callback, - int64_t timestamp) : + int64_t timestamp, int32_t videoChannel, VideoChannelDescription::Quality quality) : _platformContext(std::move(platformContext)), _callback(std::move(callback)), - _timestamp(timestamp) { + _timestamp(timestamp), + _videoChannel(videoChannel), + _quality(quality) { } void call(int64_t ts, int64_t responseTs, BroadcastPart::Status status, uint8_t *data, int32_t len) { @@ -91,22 +93,48 @@ public: part.responseTimestamp = responseTs / 1000.0; part.status = status; if (data != nullptr) { - part.oggData = std::vector(data, data + len); + part.data = std::vector(data, data + len); } _callback(std::move<>(part)); } + bool isValidTaskFor(int64_t timestamp, int32_t videoChannel, VideoChannelDescription::Quality quality) { + if (_videoChannel == 0) { + return _timestamp == timestamp; + } else { + return _timestamp == timestamp && _videoChannel == videoChannel && _quality == quality; + } + } + private: void cancel() override { tgvoip::jni::DoWithJNI([&](JNIEnv *env) { - jobject globalRef = ((AndroidContext *) _platformContext.get())->getJavaInstance(); - env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onCancelRequestBroadcastPart", "(J)V"), _timestamp); + auto context = (AndroidContext *) _platformContext.get(); + jobject globalRef = context->getJavaInstance(); + env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onCancelRequestBroadcastPart", "(JII)V"), _timestamp, _videoChannel, (jint) _quality); + if (_videoChannel != 0) { + for (auto videoTaskIter = context->videoStreamTasks.begin(); videoTaskIter != context->videoStreamTasks.end(); videoTaskIter++) { + if (((BroadcastPartTaskJava *) videoTaskIter->get())->isValidTaskFor(_timestamp, _videoChannel, _quality)) { + context->videoStreamTasks.erase(videoTaskIter); + break; + } + } + } else { + for (auto audioTaskIter = context->audioStreamTasks.begin(); audioTaskIter != context->audioStreamTasks.end(); audioTaskIter++) { + if (((BroadcastPartTaskJava *) audioTaskIter->get())->isValidTaskFor(_timestamp, _videoChannel, _quality)) { + context->audioStreamTasks.erase(audioTaskIter); + break; + } + } + } }); } std::shared_ptr _platformContext; std::function _callback; int64_t _timestamp; + int32_t _videoChannel; + VideoChannelDescription::Quality _quality; }; class JavaObject { @@ -399,12 +427,21 @@ JNIEXPORT jlong JNICALL Java_org_telegram_messenger_voip_NativeInstance_makeGrou .platformContext = platformContext }; if (!screencast) { - descriptor.requestBroadcastPart = [](std::shared_ptr platformContext, int64_t timestamp, int64_t duration, std::function callback) -> std::shared_ptr { - std::shared_ptr task = std::make_shared(platformContext, callback, timestamp); - ((AndroidContext *) platformContext.get())->streamTask = task; + descriptor.requestAudioBroadcastPart = [](std::shared_ptr platformContext, int64_t timestamp, int64_t duration, std::function callback) -> std::shared_ptr { + std::shared_ptr task = std::make_shared(platformContext, callback, timestamp, 0, VideoChannelDescription::Quality::Full); + ((AndroidContext *) platformContext.get())->audioStreamTasks.push_back(task); tgvoip::jni::DoWithJNI([platformContext, timestamp, duration, task](JNIEnv *env) { jobject globalRef = ((AndroidContext *) platformContext.get())->getJavaInstance(); - env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onRequestBroadcastPart", "(JJ)V"), timestamp, duration); + env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onRequestBroadcastPart", "(JJII)V"), timestamp, duration, 0, 0); + }); + return task; + }; + descriptor.requestVideoBroadcastPart = [](std::shared_ptr platformContext, int64_t timestamp, int64_t duration, int32_t video_channel, VideoChannelDescription::Quality quality, std::function callback) -> std::shared_ptr { + std::shared_ptr task = std::make_shared(platformContext, callback, timestamp, video_channel, quality); + ((AndroidContext *) platformContext.get())->videoStreamTasks.push_back(task); + tgvoip::jni::DoWithJNI([platformContext, timestamp, duration, task, video_channel, quality](JNIEnv *env) { + jobject globalRef = ((AndroidContext *) platformContext.get())->getJavaInstance(); + env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onRequestBroadcastPart", "(JJII)V"), timestamp, duration, video_channel, (jint) quality); }); return task; }; @@ -812,20 +849,37 @@ JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_stopGroup delete instance; } -JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_onStreamPartAvailable(JNIEnv *env, jobject obj, jlong ts, jobject byteBuffer, jint size, jlong responseTs) { +JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_onStreamPartAvailable(JNIEnv *env, jobject obj, jlong ts, jobject byteBuffer, jint size, jlong responseTs, jint videoChannel, jint quality) { InstanceHolder *instance = getInstanceHolder(env, obj); if (instance->groupNativeInstance == nullptr) { return; } auto context = (AndroidContext *) instance->_platformContext.get(); - std::shared_ptr streamTask = context->streamTask; - auto task = (BroadcastPartTaskJava *) streamTask.get(); + std::shared_ptr task; + auto q = (VideoChannelDescription::Quality) quality; + if (videoChannel != 0) { + for (auto videoTaskIter = context->videoStreamTasks.begin(); videoTaskIter != context->videoStreamTasks.end(); videoTaskIter++) { + if (((BroadcastPartTaskJava *) videoTaskIter->get())->isValidTaskFor(ts, videoChannel, q)) { + task = *videoTaskIter; + context->videoStreamTasks.erase(videoTaskIter); + break; + } + } + } else { + for (auto audioTaskIter = context->audioStreamTasks.begin(); audioTaskIter != context->audioStreamTasks.end(); audioTaskIter++) { + if (((BroadcastPartTaskJava *) audioTaskIter->get())->isValidTaskFor(ts, 0, q)) { + task = *audioTaskIter; + context->audioStreamTasks.erase(audioTaskIter); + break; + } + } + } if (task != nullptr) { if (byteBuffer != nullptr) { auto buf = (uint8_t *) env->GetDirectBufferAddress(byteBuffer); - task->call(ts, responseTs, BroadcastPart::Status::Success, buf, size); + ((BroadcastPartTaskJava *) task.get())->call(ts, responseTs, BroadcastPart::Status::Success, buf, size); } else { - task->call(ts, responseTs, size == 0 ? BroadcastPart::Status::NotReady : BroadcastPart::Status::ResyncNeeded, nullptr, 0); + ((BroadcastPartTaskJava *) task.get())->call(ts, responseTs, size == 0 ? BroadcastPart::Status::NotReady : BroadcastPart::Status::ResyncNeeded, nullptr, 0); } } } @@ -886,7 +940,7 @@ JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_destroyVi JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_switchCameraCapturer(JNIEnv *env, jclass clazz, jlong videoCapturer, jboolean front) { auto capturer = reinterpret_cast(videoCapturer); - capturer->switchToDevice(front ? "front" : "back"); + capturer->switchToDevice(front ? "front" : "back", false); } JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_setVideoStateCapturer(JNIEnv *env, jclass clazz, jlong videoCapturer, jint videoState) { @@ -899,7 +953,7 @@ JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_switchCam if (instance->_videoCapture == nullptr) { return; } - instance->_videoCapture->switchToDevice(front ? "front" : "back"); + instance->_videoCapture->switchToDevice(front ? "front" : "back", false); } JNIEXPORT jboolean JNICALL Java_org_telegram_messenger_voip_NativeInstance_hasVideoCapturer(JNIEnv *env, jobject obj) { diff --git a/TMessagesProj/jni/voip/tgcalls/Instance.h b/TMessagesProj/jni/voip/tgcalls/Instance.h index 62419f554..7d87919ad 100644 --- a/TMessagesProj/jni/voip/tgcalls/Instance.h +++ b/TMessagesProj/jni/voip/tgcalls/Instance.h @@ -204,6 +204,7 @@ public: virtual void receiveSignalingData(const std::vector &data) = 0; virtual void setVideoCapture(std::shared_ptr videoCapture) = 0; + virtual void sendVideoDeviceUpdated() = 0; virtual void setRequestedVideoAspect(float aspect) = 0; virtual void stop(std::function completion) = 0; diff --git a/TMessagesProj/jni/voip/tgcalls/InstanceImpl.cpp b/TMessagesProj/jni/voip/tgcalls/InstanceImpl.cpp index 2e0054017..aff9033f6 100644 --- a/TMessagesProj/jni/voip/tgcalls/InstanceImpl.cpp +++ b/TMessagesProj/jni/voip/tgcalls/InstanceImpl.cpp @@ -58,6 +58,12 @@ void InstanceImpl::setVideoCapture(std::shared_ptr videoC }); } +void InstanceImpl::sendVideoDeviceUpdated() { + _manager->perform(RTC_FROM_HERE, [](Manager *manager) { + manager->sendVideoDeviceUpdated(); + }); +} + void InstanceImpl::setRequestedVideoAspect(float aspect) { _manager->perform(RTC_FROM_HERE, [aspect](Manager *manager) { manager->setRequestedVideoAspect(aspect); diff --git a/TMessagesProj/jni/voip/tgcalls/InstanceImpl.h b/TMessagesProj/jni/voip/tgcalls/InstanceImpl.h index 599cb1d10..89103f3b5 100644 --- a/TMessagesProj/jni/voip/tgcalls/InstanceImpl.h +++ b/TMessagesProj/jni/voip/tgcalls/InstanceImpl.h @@ -21,6 +21,7 @@ public: void receiveSignalingData(const std::vector &data) override; void setVideoCapture(std::shared_ptr videoCapture) override; + void sendVideoDeviceUpdated() override; void setRequestedVideoAspect(float aspect) override; void setNetworkType(NetworkType networkType) override; void setMuteMicrophone(bool muteMicrophone) override; diff --git a/TMessagesProj/jni/voip/tgcalls/Manager.cpp b/TMessagesProj/jni/voip/tgcalls/Manager.cpp index ff12cedad..1244e82a2 100644 --- a/TMessagesProj/jni/voip/tgcalls/Manager.cpp +++ b/TMessagesProj/jni/voip/tgcalls/Manager.cpp @@ -316,6 +316,12 @@ void Manager::setVideoCapture(std::shared_ptr videoCaptur }); } +void Manager::sendVideoDeviceUpdated() { + _mediaManager->perform(RTC_FROM_HERE, [](MediaManager *mediaManager) { + mediaManager->sendVideoDeviceUpdated(); + }); +} + void Manager::setRequestedVideoAspect(float aspect) { _mediaManager->perform(RTC_FROM_HERE, [aspect](MediaManager *mediaManager) { mediaManager->setRequestedVideoAspect(aspect); diff --git a/TMessagesProj/jni/voip/tgcalls/Manager.h b/TMessagesProj/jni/voip/tgcalls/Manager.h index e14841913..ceab976ad 100644 --- a/TMessagesProj/jni/voip/tgcalls/Manager.h +++ b/TMessagesProj/jni/voip/tgcalls/Manager.h @@ -29,6 +29,7 @@ public: void start(); void receiveSignalingData(const std::vector &data); void setVideoCapture(std::shared_ptr videoCapture); + void sendVideoDeviceUpdated(); void setRequestedVideoAspect(float aspect); void setMuteOutgoingAudio(bool mute); void setIncomingVideoOutput(std::shared_ptr> sink); diff --git a/TMessagesProj/jni/voip/tgcalls/MediaManager.cpp b/TMessagesProj/jni/voip/tgcalls/MediaManager.cpp index 28702c497..cd03d3b63 100644 --- a/TMessagesProj/jni/voip/tgcalls/MediaManager.cpp +++ b/TMessagesProj/jni/voip/tgcalls/MediaManager.cpp @@ -641,17 +641,18 @@ void MediaManager::setSendVideo(std::shared_ptr videoCapt _videoCapture = videoCapture; if (_videoCapture) { _videoCapture->setPreferredAspectRatio(_preferredAspectRatio); - _isScreenCapture = _videoCapture->isScreenCapture(); - const auto thread = _thread; - const auto weak = std::weak_ptr(shared_from_this()); - GetVideoCaptureAssumingSameThread(_videoCapture.get())->setStateUpdated([=](VideoState state) { - thread->PostTask(RTC_FROM_HERE, [=] { - if (const auto strong = weak.lock()) { - strong->setOutgoingVideoState(state); - } - }); - }); + const auto thread = _thread; + const auto weak = std::weak_ptr(shared_from_this()); + const auto object = GetVideoCaptureAssumingSameThread(_videoCapture.get()); + _isScreenCapture = object->isScreenCapture(); + object->setStateUpdated([=](VideoState state) { + thread->PostTask(RTC_FROM_HERE, [=] { + if (const auto strong = weak.lock()) { + strong->setOutgoingVideoState(state); + } + }); + }); setOutgoingVideoState(VideoState::Active); } else { _isScreenCapture = false; @@ -681,6 +682,18 @@ void MediaManager::setSendVideo(std::shared_ptr videoCapt checkIsReceivingVideoChanged(wasReceiving); } +void MediaManager::sendVideoDeviceUpdated() { + if (!computeIsSendingVideo()) { + return; + } + const auto wasScreenCapture = _isScreenCapture; + const auto object = GetVideoCaptureAssumingSameThread(_videoCapture.get()); + _isScreenCapture = object->isScreenCapture(); + if (_isScreenCapture != wasScreenCapture) { + adjustBitratePreferences(true); + } +} + void MediaManager::setRequestedVideoAspect(float aspect) { if (_localPreferredVideoAspectRatio != aspect) { _localPreferredVideoAspectRatio = aspect; diff --git a/TMessagesProj/jni/voip/tgcalls/MediaManager.h b/TMessagesProj/jni/voip/tgcalls/MediaManager.h index 5db162686..baaec2729 100644 --- a/TMessagesProj/jni/voip/tgcalls/MediaManager.h +++ b/TMessagesProj/jni/voip/tgcalls/MediaManager.h @@ -58,6 +58,7 @@ public: void setIsConnected(bool isConnected); void notifyPacketSent(const rtc::SentPacket &sentPacket); void setSendVideo(std::shared_ptr videoCapture); + void sendVideoDeviceUpdated(); void setRequestedVideoAspect(float aspect); void setMuteOutgoingAudio(bool mute); void setIncomingVideoOutput(std::shared_ptr> sink); diff --git a/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterface.h b/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterface.h index 4b46096b8..b488d6691 100644 --- a/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterface.h +++ b/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterface.h @@ -39,8 +39,7 @@ public: virtual ~VideoCaptureInterface(); - virtual bool isScreenCapture() = 0; - virtual void switchToDevice(std::string deviceId) = 0; + virtual void switchToDevice(std::string deviceId, bool isScreenCapture) = 0; virtual void setState(VideoState state) = 0; virtual void setPreferredAspectRatio(float aspectRatio) = 0; virtual void setOutput(std::shared_ptr> sink) = 0; diff --git a/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterfaceImpl.cpp b/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterfaceImpl.cpp index d980f51b8..04a8f9255 100644 --- a/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterfaceImpl.cpp +++ b/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterfaceImpl.cpp @@ -9,11 +9,11 @@ namespace tgcalls { -VideoCaptureInterfaceObject::VideoCaptureInterfaceObject(std::string deviceId, std::shared_ptr platformContext, Threads &threads) -: _videoSource(PlatformInterface::SharedInstance()->makeVideoSource(threads.getMediaThread(), threads.getWorkerThread(), deviceId == "screen")) { +VideoCaptureInterfaceObject::VideoCaptureInterfaceObject(std::string deviceId, bool isScreenCapture, std::shared_ptr platformContext, Threads &threads) +: _videoSource(PlatformInterface::SharedInstance()->makeVideoSource(threads.getMediaThread(), threads.getWorkerThread(), isScreenCapture)) { _platformContext = platformContext; - - switchToDevice(deviceId); + + switchToDevice(deviceId, isScreenCapture); } VideoCaptureInterfaceObject::~VideoCaptureInterfaceObject() { @@ -34,13 +34,18 @@ int VideoCaptureInterfaceObject::getRotation() { } } -void VideoCaptureInterfaceObject::switchToDevice(std::string deviceId) { - if (_videoCapturer && _currentUncroppedSink != nullptr) { +bool VideoCaptureInterfaceObject::isScreenCapture() { + return _isScreenCapture; +} + +void VideoCaptureInterfaceObject::switchToDevice(std::string deviceId, bool isScreenCapture) { + if (_videoCapturer) { _videoCapturer->setUncroppedOutput(nullptr); } + _isScreenCapture = isScreenCapture; if (_videoSource) { //this should outlive the capturer - _videoCapturer = NULL; + _videoCapturer = nullptr; _videoCapturer = PlatformInterface::SharedInstance()->makeVideoCapturer(_videoSource, deviceId, [this](VideoState state) { if (this->_stateUpdated) { this->_stateUpdated(state); @@ -164,23 +169,19 @@ void VideoCaptureInterfaceObject::setRotationUpdated(std::function ro VideoCaptureInterfaceImpl::VideoCaptureInterfaceImpl(std::string deviceId, bool isScreenCapture, std::shared_ptr platformContext, std::shared_ptr threads) : _platformContext(platformContext), -_impl(threads->getMediaThread(), [deviceId, platformContext, threads]() { - return new VideoCaptureInterfaceObject(deviceId, platformContext, *threads); -}), _isScreenCapture(isScreenCapture) { +_impl(threads->getMediaThread(), [deviceId, isScreenCapture, platformContext, threads]() { + return new VideoCaptureInterfaceObject(deviceId, isScreenCapture, platformContext, *threads); +}) { } VideoCaptureInterfaceImpl::~VideoCaptureInterfaceImpl() = default; -void VideoCaptureInterfaceImpl::switchToDevice(std::string deviceId) { - _impl.perform(RTC_FROM_HERE, [deviceId](VideoCaptureInterfaceObject *impl) { - impl->switchToDevice(deviceId); +void VideoCaptureInterfaceImpl::switchToDevice(std::string deviceId, bool isScreenCapture) { + _impl.perform(RTC_FROM_HERE, [deviceId, isScreenCapture](VideoCaptureInterfaceObject *impl) { + impl->switchToDevice(deviceId, isScreenCapture); }); } -bool VideoCaptureInterfaceImpl::isScreenCapture() { - return _isScreenCapture; -} - void VideoCaptureInterfaceImpl::withNativeImplementation(std::function completion) { _impl.perform(RTC_FROM_HERE, [completion](VideoCaptureInterfaceObject *impl) { impl->withNativeImplementation(completion); diff --git a/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterfaceImpl.h b/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterfaceImpl.h index a2a3eef70..a296c2cec 100644 --- a/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterfaceImpl.h +++ b/TMessagesProj/jni/voip/tgcalls/VideoCaptureInterfaceImpl.h @@ -14,10 +14,10 @@ class Threads; class VideoCaptureInterfaceObject { public: - VideoCaptureInterfaceObject(std::string deviceId, std::shared_ptr platformContext, Threads &threads); + VideoCaptureInterfaceObject(std::string deviceId, bool isScreenCapture, std::shared_ptr platformContext, Threads &threads); ~VideoCaptureInterfaceObject(); - void switchToDevice(std::string deviceId); + void switchToDevice(std::string deviceId, bool isScreenCapture); void withNativeImplementation(std::function completion); void setState(VideoState state); void setPreferredAspectRatio(float aspectRatio); @@ -29,10 +29,11 @@ public: void setOnIsActiveUpdated(std::function onIsActiveUpdated); webrtc::VideoTrackSourceInterface *source(); int getRotation(); + bool isScreenCapture(); private: void updateAspectRateAdaptation(); - + rtc::scoped_refptr _videoSource; std::shared_ptr> _currentUncroppedSink; std::shared_ptr _platformContext; @@ -46,6 +47,7 @@ private: VideoState _state = VideoState::Active; float _preferredAspectRatio = 0.0f; bool _shouldBeAdaptedToReceiverAspectRate = true; + bool _isScreenCapture = false; }; class VideoCaptureInterfaceImpl : public VideoCaptureInterface { @@ -53,8 +55,7 @@ public: VideoCaptureInterfaceImpl(std::string deviceId, bool isScreenCapture, std::shared_ptr platformContext, std::shared_ptr threads); virtual ~VideoCaptureInterfaceImpl(); - bool isScreenCapture() override; - void switchToDevice(std::string deviceId) override; + void switchToDevice(std::string deviceId, bool isScreenCapture) override; void withNativeImplementation(std::function completion) override; void setState(VideoState state) override; void setPreferredAspectRatio(float aspectRatio) override; @@ -68,8 +69,6 @@ public: private: ThreadLocalObject _impl; - - bool _isScreenCapture = false; std::shared_ptr _platformContext; diff --git a/TMessagesProj/jni/voip/tgcalls/group/StreamingPart.cpp b/TMessagesProj/jni/voip/tgcalls/group/AudioStreamingPart.cpp similarity index 82% rename from TMessagesProj/jni/voip/tgcalls/group/StreamingPart.cpp rename to TMessagesProj/jni/voip/tgcalls/group/AudioStreamingPart.cpp index 1847e2a35..f10c6104f 100644 --- a/TMessagesProj/jni/voip/tgcalls/group/StreamingPart.cpp +++ b/TMessagesProj/jni/voip/tgcalls/group/AudioStreamingPart.cpp @@ -1,4 +1,4 @@ -#include "StreamingPart.h" +#include "AudioStreamingPart.h" #include "rtc_base/logging.h" #include "rtc_base/third_party/base64/base64.h" @@ -10,6 +10,7 @@ extern "C" { } #include +#include #include #include @@ -17,6 +18,28 @@ namespace tgcalls { namespace { +uint32_t stringToUInt32(std::string const &string) { + std::stringstream stringStream(string); + uint32_t value = 0; + stringStream >> value; + return value; +} + +template +void splitString(const std::string &s, char delim, Out result) { + std::istringstream iss(s); + std::string item; + while (std::getline(iss, item, delim)) { + *result++ = item; + } +} + +std::vector splitString(const std::string &s, char delim) { + std::vector elems; + splitString(s, delim, std::back_inserter(elems)); + return elems; +} + static absl::optional readInt32(std::string const &data, int &offset) { if (offset + 4 > data.length()) { return absl::nullopt; @@ -139,9 +162,9 @@ struct ReadPcmResult { int numChannels = 0; }; -class StreamingPartInternal { +class AudioStreamingPartInternal { public: - StreamingPartInternal(std::vector &&fileData) : + AudioStreamingPartInternal(std::vector &&fileData) : _avIoContext(std::move(fileData)) { int ret = 0; @@ -201,6 +224,31 @@ public: _channelUpdates = parseChannelUpdates(result, offset); } } + + uint32_t videoChannelMask = 0; + entry = av_dict_get(inStream->metadata, "ACTIVE_MASK", nullptr, 0); + if (entry && entry->value) { + std::string sourceString = (const char *)entry->value; + videoChannelMask = stringToUInt32(sourceString); + } + + std::vector endpointList; + entry = av_dict_get(inStream->metadata, "ENDPOINTS", nullptr, 0); + if (entry && entry->value) { + std::string sourceString = (const char *)entry->value; + endpointList = splitString(sourceString, ' '); + } + + std::bitset<32> videoChannels(videoChannelMask); + size_t endpointIndex = 0; + if (videoChannels.count() == endpointList.size()) { + for (size_t i = 0; i < videoChannels.size(); i++) { + if (videoChannels[i]) { + _endpointMapping.insert(std::make_pair(endpointList[endpointIndex], i)); + endpointIndex++; + } + } + } } break; @@ -233,7 +281,7 @@ public: } } - ~StreamingPartInternal() { + ~AudioStreamingPartInternal() { if (_frame) { av_frame_unref(_frame); } @@ -283,10 +331,14 @@ public: return _channelCount; } - std::vector const &getChannelUpdates() { + std::vector const &getChannelUpdates() const { return _channelUpdates; } + std::map getEndpointMapping() const { + return _endpointMapping; + } + private: static int16_t sampleFloatToInt16(float sample) { return av_clip_int16 (static_cast(lrint(sample*32767))); @@ -399,13 +451,14 @@ private: int _channelCount = 0; std::vector _channelUpdates; + std::map _endpointMapping; std::vector _pcmBuffer; int _pcmBufferSampleOffset = 0; int _pcmBufferSampleSize = 0; }; -class StreamingPartState { +class AudioStreamingPartState { struct ChannelMapping { uint32_t ssrc = 0; int channelIndex = 0; @@ -416,7 +469,7 @@ class StreamingPartState { }; public: - StreamingPartState(std::vector &&data) : + AudioStreamingPartState(std::vector &&data) : _parsedPart(std::move(data)) { if (_parsedPart.getChannelUpdates().size() == 0) { _didReadToEnd = true; @@ -431,14 +484,18 @@ public: } } - ~StreamingPartState() { + ~AudioStreamingPartState() { + } + + std::map getEndpointMapping() const { + return _parsedPart.getEndpointMapping(); } int getRemainingMilliseconds() const { return _remainingMilliseconds; } - std::vector get10msPerChannel() { + std::vector get10msPerChannel() { if (_didReadToEnd) { return {}; } @@ -455,9 +512,9 @@ public: return {}; } - std::vector resultChannels; + std::vector resultChannels; for (const auto ssrc : _allSsrcs) { - StreamingPart::StreamingPartChannel emptyPart; + AudioStreamingPart::StreamingPartChannel emptyPart; emptyPart.ssrc = ssrc; resultChannels.push_back(emptyPart); } @@ -509,7 +566,7 @@ private: } private: - StreamingPartInternal _parsedPart; + AudioStreamingPartInternal _parsedPart; std::set _allSsrcs; std::vector _pcm10ms; @@ -520,26 +577,30 @@ private: bool _didReadToEnd = false; }; -StreamingPart::StreamingPart(std::vector &&data) { +AudioStreamingPart::AudioStreamingPart(std::vector &&data) { if (!data.empty()) { - _state = new StreamingPartState(std::move(data)); + _state = new AudioStreamingPartState(std::move(data)); } } -StreamingPart::~StreamingPart() { +AudioStreamingPart::~AudioStreamingPart() { if (_state) { delete _state; } } -int StreamingPart::getRemainingMilliseconds() const { +std::map AudioStreamingPart::getEndpointMapping() const { + return _state ? _state->getEndpointMapping() : std::map(); +} + +int AudioStreamingPart::getRemainingMilliseconds() const { return _state ? _state->getRemainingMilliseconds() : 0; } -std::vector StreamingPart::get10msPerChannel() { +std::vector AudioStreamingPart::get10msPerChannel() { return _state ? _state->get10msPerChannel() - : std::vector(); + : std::vector(); } } diff --git a/TMessagesProj/jni/voip/tgcalls/group/AudioStreamingPart.h b/TMessagesProj/jni/voip/tgcalls/group/AudioStreamingPart.h new file mode 100644 index 000000000..34b463931 --- /dev/null +++ b/TMessagesProj/jni/voip/tgcalls/group/AudioStreamingPart.h @@ -0,0 +1,41 @@ +#ifndef TGCALLS_AUDIO_STREAMING_PART_H +#define TGCALLS_AUDIO_STREAMING_PART_H + +#include "absl/types/optional.h" +#include +#include +#include + +namespace tgcalls { + +class AudioStreamingPartState; + +class AudioStreamingPart { +public: + struct StreamingPartChannel { + uint32_t ssrc = 0; + std::vector pcmData; + }; + + explicit AudioStreamingPart(std::vector &&data); + ~AudioStreamingPart(); + + AudioStreamingPart(const AudioStreamingPart&) = delete; + AudioStreamingPart(AudioStreamingPart&& other) { + _state = other._state; + other._state = nullptr; + } + AudioStreamingPart& operator=(const AudioStreamingPart&) = delete; + AudioStreamingPart& operator=(AudioStreamingPart&&) = delete; + + std::map getEndpointMapping() const; + int getRemainingMilliseconds() const; + std::vector get10msPerChannel(); + +private: + AudioStreamingPartState *_state = nullptr; +}; + +} + +#endif diff --git a/TMessagesProj/jni/voip/tgcalls/group/GroupInstanceCustomImpl.cpp b/TMessagesProj/jni/voip/tgcalls/group/GroupInstanceCustomImpl.cpp index 6db325929..3ba311d88 100644 --- a/TMessagesProj/jni/voip/tgcalls/group/GroupInstanceCustomImpl.cpp +++ b/TMessagesProj/jni/voip/tgcalls/group/GroupInstanceCustomImpl.cpp @@ -35,6 +35,7 @@ #include "modules/audio_coding/neteq/default_neteq_factory.h" #include "modules/audio_coding/include/audio_coding_module.h" #include "common_audio/include/audio_util.h" +#include "modules/audio_device/include/audio_device_data_observer.h" #include "AudioFrame.h" #include "ThreadLocalObject.h" @@ -44,9 +45,11 @@ #include "platform/PlatformInterface.h" #include "LogSinkImpl.h" #include "CodecSelectHelper.h" -#include "StreamingPart.h" +#include "AudioStreamingPart.h" +#include "VideoStreamingPart.h" #include "AudioDeviceHelper.h" #include "FakeAudioDeviceModule.h" +#include "StreamingMediaContext.h" #include #include @@ -1238,6 +1241,83 @@ std::function videoCaptureToGetVideoSource }; } +class AudioDeviceDataObserverShared { +public: + AudioDeviceDataObserverShared() { + } + + ~AudioDeviceDataObserverShared() { + } + + void setStreamingContext(std::shared_ptr streamingContext) { + _mutex.Lock(); + _streamingContext = streamingContext; + _mutex.Unlock(); + } + + void mixAudio(int16_t *audio_samples, const size_t num_samples, const size_t num_channels, const uint32_t samples_per_sec) { + const auto numSamplesOut = num_samples * num_channels; + const auto numBytesOut = sizeof(int16_t) * numSamplesOut; + if (samples_per_sec != 48000) { + return; + } + + if (_buffer.size() < numSamplesOut) { + _buffer.resize(numSamplesOut); + } + + _mutex.Lock(); + const auto context = _streamingContext; + _mutex.Unlock(); + + if (context) { + context->getAudio(_buffer.data(), num_samples, num_channels, samples_per_sec); + memcpy(audio_samples, _buffer.data(), numBytesOut); + } + } + +private: + webrtc::Mutex _mutex; + std::vector _buffer; + std::shared_ptr _streamingContext; +}; + +class AudioDeviceDataObserverImpl : public webrtc::AudioDeviceDataObserver { +public: + AudioDeviceDataObserverImpl(std::shared_ptr shared) : + _shared(shared) { + } + + virtual ~AudioDeviceDataObserverImpl() { + } + + virtual void OnCaptureData(const void* audio_samples, + const size_t num_samples, + const size_t bytes_per_sample, + const size_t num_channels, + const uint32_t samples_per_sec) override { + } + + virtual void OnRenderData(const void* audio_samples, + const size_t num_samples, + const size_t bytes_per_sample, + const size_t num_channels, + const uint32_t samples_per_sec) override { + if (samples_per_sec != 48000) { + return; + } + if (bytes_per_sample != num_channels * 2) { + return; + } + if (_shared) { + _shared->mixAudio((int16_t *)audio_samples, num_samples, num_channels, samples_per_sec); + } + } + +private: + std::shared_ptr _shared; +}; + } // namespace class GroupInstanceCustomInternal : public sigslot::has_slots<>, public std::enable_shared_from_this { @@ -1248,7 +1328,9 @@ public: _audioLevelsUpdated(descriptor.audioLevelsUpdated), _onAudioFrame(descriptor.onAudioFrame), _requestMediaChannelDescriptions(descriptor.requestMediaChannelDescriptions), - _requestBroadcastPart(descriptor.requestBroadcastPart), + _requestCurrentTime(descriptor.requestCurrentTime), + _requestAudioBroadcastPart(descriptor.requestAudioBroadcastPart), + _requestVideoBroadcastPart(descriptor.requestVideoBroadcastPart), _videoCapture(descriptor.videoCapture), _videoCaptureSink(new VideoSinkImpl("VideoCapture")), _getVideoSource(descriptor.getVideoSource), @@ -1407,6 +1489,8 @@ public: } #endif + _audioDeviceDataObserverShared = std::make_shared(); + _audioDeviceModule = createAudioDeviceModule(); if (!_audioDeviceModule) { return; @@ -1763,8 +1847,14 @@ public: } void updateSsrcAudioLevel(uint32_t ssrc, uint8_t audioLevel, bool isSpeech) { - float mappedLevel = ((float)audioLevel) / (float)(0x7f); - mappedLevel = (fabs(1.0f - mappedLevel)) * 1.0f; + float mappedLevelDb = ((float)audioLevel) / (float)(0x7f); + + //mappedLevelDb = fabs(1.0f - mappedLevelDb); + //float mappedLevel = pow(10.0f, mappedLevelDb * 0.1f); + + //printf("mappedLevelDb: %f, mappedLevel: %f\n", mappedLevelDb, mappedLevel); + + float mappedLevel = (fabs(1.0f - mappedLevelDb)) * 1.0f; auto it = _audioLevels.find(ChannelId(ssrc)); if (it != _audioLevels.end()) { @@ -1795,18 +1885,18 @@ public: return; } - int64_t timestamp = rtc::TimeMillis(); - int64_t maxSampleTimeout = 400; + //int64_t timestamp = rtc::TimeMillis(); + //int64_t maxSampleTimeout = 400; GroupLevelsUpdate levelsUpdate; levelsUpdate.updates.reserve(strong->_audioLevels.size() + 1); for (auto &it : strong->_audioLevels) { - if (it.second.value.level < 0.001f) { + /*if (it.second.value.level < 0.001f) { continue; } if (it.second.timestamp <= timestamp - maxSampleTimeout) { continue; - } + }*/ uint32_t effectiveSsrc = it.first.actualSsrc; if (std::find_if(levelsUpdate.updates.begin(), levelsUpdate.updates.end(), [&](GroupLevelUpdate const &item) { @@ -1825,10 +1915,12 @@ public: } } - it.second.value.level *= 0.5f; - it.second.value.voice = false; + //it.second.value.level *= 0.5f; + //it.second.value.voice = false; } + strong->_audioLevels.clear(); + auto myAudioLevel = strong->_myAudioLevel; myAudioLevel.isMuted = strong->_isMuted; levelsUpdate.updates.push_back(GroupLevelUpdate{ 0, myAudioLevel }); @@ -1906,26 +1998,7 @@ public: } void updateBroadcastNetworkStatus() { - auto timestamp = rtc::TimeMillis(); - bool isBroadcastConnected = true; - if (_lastBroadcastPartReceivedTimestamp < timestamp - 3000) { - isBroadcastConnected = false; - } - - if (_broadcastEnabledUntilRtcIsConnectedAtTimestamp) { - auto timestamp = rtc::TimeMillis(); - if (std::abs(timestamp - _broadcastEnabledUntilRtcIsConnectedAtTimestamp.value()) > 3000) { - _broadcastEnabledUntilRtcIsConnectedAtTimestamp = absl::nullopt; - if (_currentRequestedBroadcastPart) { - if (_currentRequestedBroadcastPart->task) { - _currentRequestedBroadcastPart->task->cancel(); - } - _currentRequestedBroadcastPart.reset(); - } - isBroadcastConnected = false; - } - } if (isBroadcastConnected != _isBroadcastConnected) { _isBroadcastConnected = isBroadcastConnected; @@ -1933,214 +2006,6 @@ public: } } - absl::optional getNextBroadcastPart() { - while (true) { - if (_sourceBroadcastParts.size() != 0) { - auto readChannels = _sourceBroadcastParts[0]->get10msPerChannel(); - if (readChannels.size() == 0 || readChannels[0].pcmData.size() == 0) { - _sourceBroadcastParts.erase(_sourceBroadcastParts.begin()); - } else { - std::vector channels; - - int numSamples = (int)readChannels[0].pcmData.size(); - - for (auto &readChannel : readChannels) { - DecodedBroadcastPart::DecodedBroadcastPartChannel channel; - channel.ssrc = readChannel.ssrc; - channel.pcmData = std::move(readChannel.pcmData); - channels.push_back(channel); - } - - absl::optional decodedPart; - decodedPart.emplace(numSamples, std::move(channels)); - - return decodedPart; - } - } else { - return absl::nullopt; - } - } - - return absl::nullopt; - } - - void commitBroadcastPackets() { - int numMillisecondsInQueue = 0; - for (const auto &part : _sourceBroadcastParts) { - numMillisecondsInQueue += part->getRemainingMilliseconds(); - } - - int commitMilliseconds = 20; - if (numMillisecondsInQueue > 1000) { - commitMilliseconds = numMillisecondsInQueue - 1000; - } - - std::set channelsWithActivity; - - for (int msIndex = 0; msIndex < commitMilliseconds; msIndex += 10) { - auto packetData = getNextBroadcastPart(); - if (!packetData) { - break; - } - - for (const auto &decodedChannel : packetData->channels) { - if (decodedChannel.ssrc == _outgoingAudioSsrc) { - continue; - } - - ChannelId channelSsrc = ChannelId(decodedChannel.ssrc + 1000, decodedChannel.ssrc); - if (_incomingAudioChannels.find(channelSsrc) == _incomingAudioChannels.end()) { - addIncomingAudioChannel(channelSsrc, true); - } - - webrtc::RtpPacket packet(nullptr, 12 + decodedChannel.pcmData.size() * 2); - - packet.SetMarker(false); - packet.SetPayloadType(112); - - uint16_t packetSeq = 0; - - auto it = _broadcastSeqBySsrc.find(channelSsrc.networkSsrc); - if (it == _broadcastSeqBySsrc.end()) { - packetSeq = 1000; - _broadcastSeqBySsrc.insert(std::make_pair(channelSsrc.networkSsrc, packetSeq)); - } else { - it->second++; - packetSeq = it->second; - } - - packet.SetSequenceNumber(packetSeq); - - packet.SetTimestamp(_broadcastTimestamp); - - packet.SetSsrc(channelSsrc.networkSsrc); - - uint8_t *payload = packet.SetPayloadSize(decodedChannel.pcmData.size() * 2); - memcpy(payload, decodedChannel.pcmData.data(), decodedChannel.pcmData.size() * 2); - - for (int i = 0; i < decodedChannel.pcmData.size() * 2; i += 2) { - auto temp = payload[i]; - payload[i] = payload[i + 1]; - payload[i + 1] = temp; - } - - auto buffer = packet.Buffer(); - _threads->getWorkerThread()->Invoke(RTC_FROM_HERE, [this, buffer]() { - _call->Receiver()->DeliverPacket(webrtc::MediaType::AUDIO, buffer, -1); - }); - - channelsWithActivity.insert(ChannelId(channelSsrc)); - } - - for (auto channelId : channelsWithActivity) { - const auto it = _incomingAudioChannels.find(channelId); - if (it != _incomingAudioChannels.end()) { - it->second->updateActivity(); - } - } - - _broadcastTimestamp += packetData->numSamples; - } - } - - void requestNextBroadcastPart() { - const auto weak = std::weak_ptr(shared_from_this()); - auto requestedPartId = _nextBroadcastTimestampMilliseconds; - auto task = _requestBroadcastPart(_platformContext, requestedPartId, _broadcastPartDurationMilliseconds, [weak, threads = _threads, requestedPartId](BroadcastPart &&part) { - threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, part = std::move(part), requestedPartId]() mutable { - auto strong = weak.lock(); - if (!strong) { - return; - } - if (strong->_currentRequestedBroadcastPart && strong->_currentRequestedBroadcastPart->timestamp == requestedPartId) { - strong->onReceivedNextBroadcastPart(std::move(part)); - } - }); - }); - if (_currentRequestedBroadcastPart) { - if (_currentRequestedBroadcastPart->task) { - _currentRequestedBroadcastPart->task->cancel(); - } - _currentRequestedBroadcastPart.reset(); - } - _currentRequestedBroadcastPart.emplace(requestedPartId, task); - } - - void requestNextBroadcastPartWithDelay(int timeoutMs) { - const auto weak = std::weak_ptr(shared_from_this()); - _threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() { - auto strong = weak.lock(); - if (!strong) { - return; - } - - strong->requestNextBroadcastPart(); - }, timeoutMs); - } - - void onReceivedNextBroadcastPart(BroadcastPart &&part) { - _currentRequestedBroadcastPart.reset(); - - if (_connectionMode != GroupConnectionMode::GroupConnectionModeBroadcast && !_broadcastEnabledUntilRtcIsConnectedAtTimestamp) { - return; - } - - int64_t responseTimestampMilliseconds = (int64_t)(part.responseTimestamp * 1000.0); - - int64_t responseTimestampBoundary = (responseTimestampMilliseconds / _broadcastPartDurationMilliseconds) * _broadcastPartDurationMilliseconds; - - switch (part.status) { - case BroadcastPart::Status::Success: { - _lastBroadcastPartReceivedTimestamp = rtc::TimeMillis(); - updateBroadcastNetworkStatus(); - - if (std::abs((int64_t)(part.responseTimestamp * 1000.0) - part.timestampMilliseconds) > 2000) { - _nextBroadcastTimestampMilliseconds = std::max(part.timestampMilliseconds + _broadcastPartDurationMilliseconds, responseTimestampBoundary); - } else { - _nextBroadcastTimestampMilliseconds = part.timestampMilliseconds + _broadcastPartDurationMilliseconds; - } - _sourceBroadcastParts.emplace_back(new StreamingPart(std::move(part.oggData))); - break; - } - case BroadcastPart::Status::NotReady: { - _nextBroadcastTimestampMilliseconds = part.timestampMilliseconds; - break; - } - case BroadcastPart::Status::ResyncNeeded: { - _nextBroadcastTimestampMilliseconds = responseTimestampBoundary; - break; - } - default: { - //RTC_FATAL() << "Unknown part.status"; - break; - } - } - - int64_t nextDelay = _nextBroadcastTimestampMilliseconds - responseTimestampMilliseconds; - int clippedDelay = std::max((int)nextDelay, 100); - - //RTC_LOG(LS_INFO) << "requestNextBroadcastPartWithDelay(" << clippedDelay << ") (from " << nextDelay << ")"; - - requestNextBroadcastPartWithDelay(clippedDelay); - } - - void beginBroadcastPartsDecodeTimer(int timeoutMs) { - const auto weak = std::weak_ptr(shared_from_this()); - _threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() { - auto strong = weak.lock(); - if (!strong) { - return; - } - - if (strong->_connectionMode != GroupConnectionMode::GroupConnectionModeBroadcast && !strong->_broadcastEnabledUntilRtcIsConnectedAtTimestamp) { - return; - } - - strong->commitBroadcastPackets(); - - strong->beginBroadcastPartsDecodeTimer(20); - }, timeoutMs); - } void configureVideoParams() { if (!_sharedVideoInformation) { @@ -2320,11 +2185,10 @@ public: if (_broadcastEnabledUntilRtcIsConnectedAtTimestamp) { _broadcastEnabledUntilRtcIsConnectedAtTimestamp = absl::nullopt; - if (_currentRequestedBroadcastPart) { - if (_currentRequestedBroadcastPart->task) { - _currentRequestedBroadcastPart->task->cancel(); - } - _currentRequestedBroadcastPart.reset(); + + if (_streamingContext) { + _streamingContext.reset(); + _audioDeviceDataObserverShared->setStreamingContext(nullptr); } } @@ -2710,11 +2574,9 @@ public: if (keepBroadcastIfWasEnabled) { _broadcastEnabledUntilRtcIsConnectedAtTimestamp = rtc::TimeMillis(); } else { - if (_currentRequestedBroadcastPart) { - if (_currentRequestedBroadcastPart->task) { - _currentRequestedBroadcastPart->task->cancel(); - } - _currentRequestedBroadcastPart.reset(); + if (_streamingContext) { + _streamingContext.reset(); + _audioDeviceDataObserverShared->setStreamingContext(nullptr); } } } @@ -2743,12 +2605,50 @@ public: break; } case GroupConnectionMode::GroupConnectionModeBroadcast: { - _broadcastTimestamp = 100001; - _isBroadcastConnected = false; - beginBroadcastPartsDecodeTimer(0); - requestNextBroadcastPart(); + if (!_streamingContext) { + StreamingMediaContext::StreamingMediaContextArguments arguments; + const auto weak = std::weak_ptr(shared_from_this()); + arguments.threads = _threads; + arguments.platformContext = _platformContext; + arguments.requestCurrentTime = _requestCurrentTime; + arguments.requestAudioBroadcastPart = _requestAudioBroadcastPart; + arguments.requestVideoBroadcastPart = _requestVideoBroadcastPart; + arguments.updateAudioLevel = [weak, threads = _threads](uint32_t ssrc, float level, bool isSpeech) { + assert(threads->getMediaThread()->IsCurrent()); + + auto strong = weak.lock(); + if (!strong) { + return; + } + + InternalGroupLevelValue updated; + updated.value.level = level; + updated.value.voice = isSpeech; + updated.timestamp = rtc::TimeMillis(); + strong->_audioLevels.insert(std::make_pair(ChannelId(ssrc), std::move(updated))); + }; + _streamingContext = std::make_shared(std::move(arguments)); + + for (const auto &it : _pendingVideoSinks) { + for (const auto &sink : it.second) { + _streamingContext->addVideoSink(it.first.endpointId, sink); + } + } + + for (const auto &it : _volumeBySsrc) { + _streamingContext->setVolume(it.first, it.second); + } + + std::vector streamingVideoChannels; + for (const auto &it : _pendingRequestedVideo) { + streamingVideoChannels.emplace_back(it.maxQuality, it.endpointId); + } + _streamingContext->setActiveVideoChannels(streamingVideoChannels); + + _audioDeviceDataObserverShared->setStreamingContext(_streamingContext); + } break; } @@ -3033,6 +2933,10 @@ public: } else { _pendingVideoSinks[VideoChannelId(endpointId)].push_back(sink); } + + if (_streamingContext) { + _streamingContext->addVideoSink(endpointId, sink); + } } } @@ -3233,9 +3137,21 @@ public: if (it != _incomingAudioChannels.end()) { it->second->setVolume(volume); } + + if (_streamingContext) { + _streamingContext->setVolume(ssrc, volume); + } } void setRequestedVideoChannels(std::vector &&requestedVideoChannels) { + if (_streamingContext) { + std::vector streamingVideoChannels; + for (const auto &it : requestedVideoChannels) { + streamingVideoChannels.emplace_back(it.maxQuality, it.endpointId); + } + _streamingContext->setActiveVideoChannels(streamingVideoChannels); + } + if (!_sharedVideoInformation) { _pendingRequestedVideo = std::move(requestedVideoChannels); return; @@ -3312,14 +3228,22 @@ public: private: rtc::scoped_refptr createAudioDeviceModule() { + auto audioDeviceDataObserverShared = _audioDeviceDataObserverShared; const auto create = [&](webrtc::AudioDeviceModule::AudioLayer layer) { return webrtc::AudioDeviceModule::Create( layer, _taskQueueFactory.get()); }; const auto check = [&](const rtc::scoped_refptr &result) -> rtc::scoped_refptr { - if (result && result->Init() == 0) { - return PlatformInterface::SharedInstance()->wrapAudioDeviceModule(result); + if (!result) { + return nullptr; + } + + auto audioDeviceObserver = std::make_unique(audioDeviceDataObserverShared); + auto module = webrtc::CreateAudioDeviceWithDataObserver(result, std::move(audioDeviceObserver)); + + if (module->Init() == 0) { + return PlatformInterface::SharedInstance()->wrapAudioDeviceModule(module); } else { return nullptr; } @@ -3348,7 +3272,9 @@ private: std::function _audioLevelsUpdated; std::function _onAudioFrame; std::function(std::vector const &, std::function &&)>)> _requestMediaChannelDescriptions; - std::function(std::shared_ptr, int64_t, int64_t, std::function)> _requestBroadcastPart; + std::function(std::function)> _requestCurrentTime; + std::function(std::shared_ptr, int64_t, int64_t, std::function)> _requestAudioBroadcastPart; + std::function(std::shared_ptr, int64_t, int64_t, int32_t, VideoChannelDescription::Quality, std::function)> _requestVideoBroadcastPart; std::shared_ptr _videoCapture; std::shared_ptr _videoCaptureSink; std::function _getVideoSource; @@ -3371,6 +3297,7 @@ private: std::unique_ptr _call; webrtc::FieldTrialBasedConfig _fieldTrials; webrtc::LocalAudioSinkAdapter _audioSource; + std::shared_ptr _audioDeviceDataObserverShared; rtc::scoped_refptr _audioDeviceModule; std::function(webrtc::TaskQueueFactory*)> _createAudioDeviceModule; std::string _initialInputDeviceId; @@ -3419,14 +3346,6 @@ private: absl::optional _sharedVideoInformation; - int64_t _broadcastPartDurationMilliseconds = 500; - std::vector> _sourceBroadcastParts; - std::map _broadcastSeqBySsrc; - uint32_t _broadcastTimestamp = 0; - int64_t _nextBroadcastTimestampMilliseconds = 0; - absl::optional _currentRequestedBroadcastPart; - int64_t _lastBroadcastPartReceivedTimestamp = 0; - std::vector _externalAudioSamples; webrtc::Mutex _externalAudioSamplesMutex; std::shared_ptr _externalAudioRecorder; @@ -3437,6 +3356,8 @@ private: bool _isDataChannelOpen = false; GroupNetworkState _effectiveNetworkState; + std::shared_ptr _streamingContext; + rtc::scoped_refptr _workerThreadSafery; rtc::scoped_refptr _networkThreadSafery; diff --git a/TMessagesProj/jni/voip/tgcalls/group/GroupInstanceImpl.h b/TMessagesProj/jni/voip/tgcalls/group/GroupInstanceImpl.h index 0484eb30d..a0c006421 100644 --- a/TMessagesProj/jni/voip/tgcalls/group/GroupInstanceImpl.h +++ b/TMessagesProj/jni/voip/tgcalls/group/GroupInstanceImpl.h @@ -57,6 +57,9 @@ public: }; struct BroadcastPart { + struct VideoParams { + }; + enum class Status { Success, NotReady, @@ -66,7 +69,7 @@ struct BroadcastPart { int64_t timestampMilliseconds = 0; double responseTimestamp = 0; Status status = Status::NotReady; - std::vector oggData; + std::vector data; }; enum class GroupConnectionMode { @@ -150,7 +153,9 @@ struct GroupInstanceDescriptor { std::function(webrtc::TaskQueueFactory*)> createAudioDeviceModule; std::shared_ptr videoCapture; // deprecated std::function getVideoSource; - std::function(std::shared_ptr, int64_t, int64_t, std::function)> requestBroadcastPart; + std::function(std::function)> requestCurrentTime; + std::function(std::shared_ptr, int64_t, int64_t, std::function)> requestAudioBroadcastPart; + std::function(std::shared_ptr, int64_t, int64_t, int32_t, VideoChannelDescription::Quality, std::function)> requestVideoBroadcastPart; int outgoingAudioBitrateKbit{32}; bool disableOutgoingAudioProcessing{false}; VideoContentType videoContentType{VideoContentType::None}; diff --git a/TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.cpp b/TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.cpp new file mode 100644 index 000000000..eedd00098 --- /dev/null +++ b/TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.cpp @@ -0,0 +1,869 @@ +#include "StreamingMediaContext.h" + +#include "AudioStreamingPart.h" +#include "VideoStreamingPart.h" + +#include "absl/types/optional.h" +#include "rtc_base/thread.h" +#include "rtc_base/time_utils.h" +#include "absl/types/variant.h" +#include "rtc_base/logging.h" +#include "rtc_base/synchronization/mutex.h" +#include "common_audio/ring_buffer.h" +#include "modules/audio_mixer/frame_combiner.h" +#include "modules/audio_processing/agc2/vad_with_level.h" +#include "modules/audio_processing/audio_buffer.h" +#include "api/video/video_sink_interface.h" +#include "audio/utility/audio_frame_operations.h" + +namespace tgcalls { + +namespace { + +struct PendingAudioSegmentData { +}; + +struct PendingVideoSegmentData { + int32_t channelId = 0; + VideoChannelDescription::Quality quality = VideoChannelDescription::Quality::Thumbnail; + + PendingVideoSegmentData(int32_t channelId_, VideoChannelDescription::Quality quality_) : + channelId(channelId_), + quality(quality_) { + } +}; + +struct PendingMediaSegmentPartResult { + std::vector data; + + explicit PendingMediaSegmentPartResult(std::vector &&data_) : + data(std::move(data_)) { + } +}; + +struct PendingMediaSegmentPart { + absl::variant typeData; + + int64_t minRequestTimestamp = 0; + + std::shared_ptr task; + std::shared_ptr result; +}; + +struct PendingMediaSegment { + int64_t timestamp = 0; + std::vector> parts; +}; + +struct VideoSegment { + VideoChannelDescription::Quality quality; + std::shared_ptr part; + double lastFramePts = -1.0; + int _displayedFrames = 0; + bool isPlaying = false; + std::shared_ptr pendingVideoQualityUpdatePart; +}; + +struct MediaSegment { + int64_t timestamp = 0; + int64_t duration = 0; + std::shared_ptr audio; + std::vector> video; +}; + +class SampleRingBuffer { +public: + SampleRingBuffer(size_t size) { + _buffer = WebRtc_CreateBuffer(size, sizeof(int16_t)); + } + + ~SampleRingBuffer() { + if (_buffer) { + WebRtc_FreeBuffer(_buffer); + } + } + + size_t availableForWriting() { + return WebRtc_available_write(_buffer); + } + + size_t write(int16_t const *samples, size_t count) { + return WebRtc_WriteBuffer(_buffer, samples, count); + } + + size_t read(int16_t *samples, size_t count) { + return WebRtc_ReadBuffer(_buffer, nullptr, samples, count); + } + +private: + RingBuffer *_buffer = nullptr; +}; + +static const int kVadResultHistoryLength = 8; + +class VadHistory { +private: + float _vadResultHistory[kVadResultHistoryLength]; + +public: + VadHistory() { + for (int i = 0; i < kVadResultHistoryLength; i++) { + _vadResultHistory[i] = 0.0f; + } + } + + ~VadHistory() { + } + + bool update(float vadProbability) { + for (int i = 1; i < kVadResultHistoryLength; i++) { + _vadResultHistory[i - 1] = _vadResultHistory[i]; + } + _vadResultHistory[kVadResultHistoryLength - 1] = vadProbability; + + float movingAverage = 0.0f; + for (int i = 0; i < kVadResultHistoryLength; i++) { + movingAverage += _vadResultHistory[i]; + } + movingAverage /= (float)kVadResultHistoryLength; + + bool vadResult = false; + if (movingAverage > 0.8f) { + vadResult = true; + } + + return vadResult; + } +}; + +class CombinedVad { +private: + std::unique_ptr _vadWithLevel; + VadHistory _history; + +public: + CombinedVad() { + _vadWithLevel = std::make_unique(500, webrtc::GetAvailableCpuFeatures()); + } + + ~CombinedVad() { + } + + bool update(webrtc::AudioBuffer *buffer) { + if (buffer->num_channels() <= 0) { + return _history.update(0.0f); + } + webrtc::AudioFrameView frameView(buffer->channels(), buffer->num_channels(), buffer->num_frames()); + float peak = 0.0f; + for (const auto &x : frameView.channel(0)) { + peak = std::max(std::fabs(x), peak); + } + if (peak <= 0.01f) { + return _history.update(false); + } + + auto result = _vadWithLevel->AnalyzeFrame(frameView); + + return _history.update(result.speech_probability); + } + + bool update() { + return _history.update(0.0f); + } +}; + +class SparseVad { +public: + SparseVad() { + } + + std::pair update(webrtc::AudioBuffer *buffer) { + _sampleCount += buffer->num_frames(); + if (_sampleCount >= 400) { + _sampleCount = 0; + _currentValue = _vad.update(buffer); + } + + float currentPeak = 0.0; + float *samples = buffer->channels()[0]; + for (int i = 0; i < buffer->num_frames(); i++) { + float sample = samples[i]; + if (sample < 0.0f) { + sample = -sample; + } + if (_peak < sample) { + _peak = sample; + } + if (currentPeak < sample) { + currentPeak = sample; + } + _peakCount += 1; + } + + if (_peakCount >= 4400) { + float norm = 8000.0f; + _currentLevel = ((float)(_peak)) / norm; + _peak = 0; + _peakCount = 0; + } + + return std::make_pair(_currentLevel, _currentValue); + } + +private: + CombinedVad _vad; + bool _currentValue = false; + size_t _sampleCount = 0; + + int _peakCount = 0; + float _peak = 0.0; + float _currentLevel = 0.0; +}; + +} + +class StreamingMediaContextPrivate : public std::enable_shared_from_this { +public: + StreamingMediaContextPrivate(StreamingMediaContext::StreamingMediaContextArguments &&arguments) : + _threads(arguments.threads), + _requestCurrentTime(arguments.requestCurrentTime), + _requestAudioBroadcastPart(arguments.requestAudioBroadcastPart), + _requestVideoBroadcastPart(arguments.requestVideoBroadcastPart), + _updateAudioLevel(arguments.updateAudioLevel), + _audioRingBuffer(_audioDataRingBufferMaxSize), + _audioFrameCombiner(false), + _platformContext(arguments.platformContext) { + } + + ~StreamingMediaContextPrivate() { + } + + void start() { + beginRenderTimer(0); + } + + void beginRenderTimer(int timeoutMs) { + const auto weak = std::weak_ptr(shared_from_this()); + _threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() { + auto strong = weak.lock(); + if (!strong) { + return; + } + + strong->render(); + + strong->beginRenderTimer((int)(1.0 * 1000.0 / 120.0)); + }, timeoutMs); + } + + void render() { + int64_t absoluteTimestamp = rtc::TimeMillis(); + + while (true) { + if (_waitForBufferredMillisecondsBeforeRendering) { + if (getAvailableBufferDuration() < _waitForBufferredMillisecondsBeforeRendering.value()) { + break; + } else { + _waitForBufferredMillisecondsBeforeRendering = absl::nullopt; + } + } + + if (_availableSegments.empty()) { + _playbackReferenceTimestamp = 0; + + _waitForBufferredMillisecondsBeforeRendering = _segmentBufferDuration + _segmentDuration; + + break; + } + + if (_playbackReferenceTimestamp == 0) { + _playbackReferenceTimestamp = absoluteTimestamp; + } + + double relativeTimestamp = ((double)(absoluteTimestamp - _playbackReferenceTimestamp)) / 1000.0; + + auto segment = _availableSegments[0]; + double segmentDuration = ((double)segment->duration) / 1000.0; + + for (auto &videoSegment : segment->video) { + videoSegment->isPlaying = true; + cancelPendingVideoQualityUpdate(videoSegment); + + auto frame = videoSegment->part->getFrameAtRelativeTimestamp(relativeTimestamp); + if (frame) { + if (videoSegment->lastFramePts != frame->pts) { + videoSegment->lastFramePts = frame->pts; + videoSegment->_displayedFrames += 1; + + auto sinkList = _videoSinks.find(frame->endpointId); + if (sinkList != _videoSinks.end()) { + for (const auto &weakSink : sinkList->second) { + auto sink = weakSink.lock(); + if (sink) { + sink->OnFrame(frame->frame); + } + } + } + } + } + } + + if (segment->audio) { + const auto available = [&] { + _audioDataMutex.Lock(); + const auto result = (_audioRingBuffer.availableForWriting() >= 480); + _audioDataMutex.Unlock(); + + return result; + }; + while (available()) { + auto audioChannels = segment->audio->get10msPerChannel(); + if (audioChannels.empty()) { + break; + } + + std::vector audioFrames; + + for (const auto &audioChannel : audioChannels) { + webrtc::AudioFrame *frame = new webrtc::AudioFrame(); + frame->UpdateFrame(0, audioChannel.pcmData.data(), audioChannel.pcmData.size(), 48000, webrtc::AudioFrame::SpeechType::kNormalSpeech, webrtc::AudioFrame::VADActivity::kVadActive); + + auto volumeIt = _volumeBySsrc.find(audioChannel.ssrc); + if (volumeIt != _volumeBySsrc.end()) { + double outputGain = volumeIt->second; + if (outputGain < 0.99f || outputGain > 1.01f) { + webrtc::AudioFrameOperations::ScaleWithSat(outputGain, frame); + } + } + + audioFrames.push_back(frame); + processAudioLevel(audioChannel.ssrc, audioChannel.pcmData); + } + + webrtc::AudioFrame frameOut; + _audioFrameCombiner.Combine(audioFrames, 1, 48000, audioFrames.size(), &frameOut); + + for (webrtc::AudioFrame *frame : audioFrames) { + delete frame; + } + + _audioDataMutex.Lock(); + _audioRingBuffer.write(frameOut.data(), frameOut.samples_per_channel()); + _audioDataMutex.Unlock(); + } + } + + if (relativeTimestamp >= segmentDuration) { + _playbackReferenceTimestamp += segment->duration; + + if (segment->audio && segment->audio->getRemainingMilliseconds() > 0) { + RTC_LOG(LS_INFO) << "render: discarding " << segment->audio->getRemainingMilliseconds() << " ms of audio at the end of a segment"; + } + if (!segment->video.empty()) { + if (segment->video[0]->part->getActiveEndpointId()) { + RTC_LOG(LS_INFO) << "render: discarding video frames at the end of a segment (displayed " << segment->video[0]->_displayedFrames << " frames)"; + } + } + + _availableSegments.erase(_availableSegments.begin()); + } + + break; + } + + requestSegmentsIfNeeded(); + checkPendingSegments(); + } + + void processAudioLevel(uint32_t ssrc, std::vector const &samples) { + if (!_updateAudioLevel) { + return; + } + + webrtc::AudioBuffer buffer(48000, 1, 48000, 1, 48000, 1); + webrtc::StreamConfig config(48000, 1); + buffer.CopyFrom(samples.data(), config); + + std::pair vadResult = std::make_pair(0.0f, false); + auto vad = _audioVadMap.find(ssrc); + if (vad == _audioVadMap.end()) { + auto newVad = std::make_unique(); + vadResult = newVad->update(&buffer); + _audioVadMap.insert(std::make_pair(ssrc, std::move(newVad))); + } else { + vadResult = vad->second->update(&buffer); + } + + _updateAudioLevel(ssrc, vadResult.first, vadResult.second); + } + + void getAudio(int16_t *audio_samples, const size_t num_samples, const size_t num_channels, const uint32_t samples_per_sec) { + int16_t *buffer = nullptr; + + if (num_channels == 1) { + buffer = audio_samples; + } else { + if (_tempAudioBuffer.size() < num_samples) { + _tempAudioBuffer.resize(num_samples); + } + buffer = _tempAudioBuffer.data(); + } + + _audioDataMutex.Lock(); + size_t readSamples = _audioRingBuffer.read(buffer, num_samples); + _audioDataMutex.Unlock(); + + if (num_channels != 1) { + for (size_t sampleIndex = 0; sampleIndex < readSamples; sampleIndex++) { + for (size_t channelIndex = 0; channelIndex < num_channels; channelIndex++) { + audio_samples[sampleIndex * num_channels + channelIndex] = _tempAudioBuffer[sampleIndex]; + } + } + } + if (readSamples < num_samples) { + memset(audio_samples + readSamples * num_channels, 0, (num_samples - readSamples) * num_channels * sizeof(int16_t)); + } + } + + int64_t getAvailableBufferDuration() { + int64_t result = 0; + + for (const auto &segment : _availableSegments) { + result += segment->duration; + } + + return (int)result; + } + + void discardAllPendingSegments() { + for (size_t i = 0; i < _pendingSegments.size(); i++) { + for (const auto &it : _pendingSegments[i]->parts) { + if (it->task) { + it->task->cancel(); + } + } + } + + _pendingSegments.clear(); + } + + void requestSegmentsIfNeeded() { + while (true) { + if (_nextSegmentTimestamp == 0) { + if (_pendingSegments.size() >= 1) { + break; + } + } else { + int64_t availableAndRequestedSegmentsDuration = 0; + availableAndRequestedSegmentsDuration += getAvailableBufferDuration(); + availableAndRequestedSegmentsDuration += _pendingSegments.size() * _segmentDuration; + + if (availableAndRequestedSegmentsDuration > _segmentBufferDuration) { + break; + } + } + + auto pendingSegment = std::make_shared(); + pendingSegment->timestamp = _nextSegmentTimestamp; + + if (_nextSegmentTimestamp != 0) { + _nextSegmentTimestamp += _segmentDuration; + } + + auto audio = std::make_shared(); + audio->typeData = PendingAudioSegmentData(); + audio->minRequestTimestamp = 0; + pendingSegment->parts.push_back(audio); + + for (const auto &videoChannel : _activeVideoChannels) { + auto channelIdIt = _currentEndpointMapping.find(videoChannel.endpoint); + if (channelIdIt == _currentEndpointMapping.end()) { + continue; + } + + int32_t channelId = channelIdIt->second + 1; + + auto video = std::make_shared(); + video->typeData = PendingVideoSegmentData(channelId, videoChannel.quality); + video->minRequestTimestamp = 0; + pendingSegment->parts.push_back(video); + } + + _pendingSegments.push_back(pendingSegment); + + if (_nextSegmentTimestamp == 0) { + break; + } + } + } + + void requestPendingVideoQualityUpdate(std::shared_ptr segment, int64_t timestamp) { + if (segment->isPlaying) { + return; + } + auto segmentEndpointId = segment->part->getActiveEndpointId(); + if (!segmentEndpointId) { + return; + } + + absl::optional updatedChannelId; + absl::optional updatedQuality; + + for (const auto &videoChannel : _activeVideoChannels) { + auto channelIdIt = _currentEndpointMapping.find(videoChannel.endpoint); + if (channelIdIt == _currentEndpointMapping.end()) { + continue; + } + + updatedChannelId = channelIdIt->second + 1; + updatedQuality = videoChannel.quality; + } + + if (updatedChannelId && updatedQuality) { + if (segment->pendingVideoQualityUpdatePart) { + const auto typeData = &segment->pendingVideoQualityUpdatePart->typeData; + if (const auto videoData = absl::get_if(typeData)) { + if (videoData->channelId == updatedChannelId.value() && videoData->quality == updatedQuality.value()) { + return; + } + } + cancelPendingVideoQualityUpdate(segment); + } + + auto video = std::make_shared(); + + video->typeData = PendingVideoSegmentData(updatedChannelId.value(), updatedQuality.value()); + video->minRequestTimestamp = 0; + + segment->pendingVideoQualityUpdatePart = video; + + const auto weak = std::weak_ptr(shared_from_this()); + const auto weakSegment = std::weak_ptr(segment); + beginPartTask(video, timestamp, [weak, weakSegment]() { + auto strong = weak.lock(); + if (!strong) { + return; + } + + auto strongSegment = weakSegment.lock(); + if (!strongSegment) { + return; + } + + if (!strongSegment->pendingVideoQualityUpdatePart) { + return; + } + + auto result = strongSegment->pendingVideoQualityUpdatePart->result; + if (result) { + strongSegment->part = std::make_shared(std::move(result->data)); + } + + strongSegment->pendingVideoQualityUpdatePart.reset(); + }); + } + } + + void cancelPendingVideoQualityUpdate(std::shared_ptr segment) { + if (!segment->pendingVideoQualityUpdatePart) { + return; + } + + if (segment->pendingVideoQualityUpdatePart->task) { + segment->pendingVideoQualityUpdatePart->task->cancel(); + } + + segment->pendingVideoQualityUpdatePart.reset(); + } + + void checkPendingSegments() { + const auto weak = std::weak_ptr(shared_from_this()); + + int64_t absoluteTimestamp = rtc::TimeMillis(); + int64_t minDelayedRequestTimeout = INT_MAX; + + bool shouldRequestMoreSegments = false; + + for (int i = 0; i < _pendingSegments.size(); i++) { + auto pendingSegment = _pendingSegments[i]; + auto segmentTimestamp = pendingSegment->timestamp; + + bool allPartsDone = true; + + for (auto &part : pendingSegment->parts) { + if (!part->result) { + allPartsDone = false; + } + + if (!part->result && !part->task) { + if (part->minRequestTimestamp != 0) { + if (i != 0) { + continue; + } + if (part->minRequestTimestamp > absoluteTimestamp) { + minDelayedRequestTimeout = std::min(minDelayedRequestTimeout, part->minRequestTimestamp - absoluteTimestamp); + + continue; + } + } + + const auto weakSegment = std::weak_ptr(pendingSegment); + const auto weakPart = std::weak_ptr(part); + + std::function handleResult = [weak, weakSegment, weakPart, threads = _threads, segmentTimestamp](BroadcastPart &&part) { + threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, weakSegment, weakPart, part = std::move(part), segmentTimestamp]() mutable { + auto strong = weak.lock(); + if (!strong) { + return; + } + auto strongSegment = weakSegment.lock(); + if (!strongSegment) { + return; + } + + auto pendingPart = weakPart.lock(); + if (!pendingPart) { + return; + } + + pendingPart->task.reset(); + + switch (part.status) { + case BroadcastPart::Status::Success: { + pendingPart->result = std::make_shared(std::move(part.data)); + if (strong->_nextSegmentTimestamp == 0) { + strong->_nextSegmentTimestamp = part.timestampMilliseconds + strong->_segmentDuration; + } + strong->checkPendingSegments(); + break; + } + case BroadcastPart::Status::NotReady: { + if (segmentTimestamp == 0) { + int64_t responseTimestampMilliseconds = (int64_t)(part.responseTimestamp * 1000.0); + int64_t responseTimestampBoundary = (responseTimestampMilliseconds / strong->_segmentDuration) * strong->_segmentDuration; + + strong->_nextSegmentTimestamp = responseTimestampBoundary; + strong->discardAllPendingSegments(); + strong->requestSegmentsIfNeeded(); + strong->checkPendingSegments(); + } else { + pendingPart->minRequestTimestamp = rtc::TimeMillis() + 100; + strong->checkPendingSegments(); + } + break; + } + case BroadcastPart::Status::ResyncNeeded: { + int64_t responseTimestampMilliseconds = (int64_t)(part.responseTimestamp * 1000.0); + int64_t responseTimestampBoundary = (responseTimestampMilliseconds / strong->_segmentDuration) * strong->_segmentDuration; + + strong->_nextSegmentTimestamp = responseTimestampBoundary; + strong->discardAllPendingSegments(); + strong->requestSegmentsIfNeeded(); + strong->checkPendingSegments(); + + break; + } + default: { + RTC_FATAL() << "Unknown part.status"; + break; + } + } + }); + }; + + const auto typeData = &part->typeData; + if (const auto audioData = absl::get_if(typeData)) { + part->task = _requestAudioBroadcastPart(_platformContext, segmentTimestamp, _segmentDuration, handleResult); + } else if (const auto videoData = absl::get_if(typeData)) { + part->task = _requestVideoBroadcastPart(_platformContext, segmentTimestamp, _segmentDuration, videoData->channelId, videoData->quality, handleResult); + } + } + } + + if (allPartsDone && i == 0) { + std::shared_ptr segment = std::make_shared(); + segment->timestamp = pendingSegment->timestamp; + segment->duration = _segmentDuration; + for (auto &part : pendingSegment->parts) { + const auto typeData = &part->typeData; + if (const auto audioData = absl::get_if(typeData)) { + segment->audio = std::make_shared(std::move(part->result->data)); + _currentEndpointMapping = segment->audio->getEndpointMapping(); + } else if (const auto videoData = absl::get_if(typeData)) { + auto videoSegment = std::make_shared(); + videoSegment->quality = videoData->quality; + if (part->result->data.empty()) { + RTC_LOG(LS_INFO) << "Video part " << segment->timestamp << " is empty"; + } + videoSegment->part = std::make_shared(std::move(part->result->data)); + segment->video.push_back(videoSegment); + } + } + _availableSegments.push_back(segment); + + shouldRequestMoreSegments = true; + + _pendingSegments.erase(_pendingSegments.begin() + i); + i--; + } + } + + if (minDelayedRequestTimeout < INT32_MAX) { + const auto weak = std::weak_ptr(shared_from_this()); + _threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() { + auto strong = weak.lock(); + if (!strong) { + return; + } + strong->checkPendingSegments(); + }, std::max((int32_t)minDelayedRequestTimeout, 10)); + } + + if (shouldRequestMoreSegments) { + requestSegmentsIfNeeded(); + } + } + + void beginPartTask(std::shared_ptr part, int64_t segmentTimestamp, std::function completion) { + const auto weak = std::weak_ptr(shared_from_this()); + const auto weakPart = std::weak_ptr(part); + + std::function handleResult = [weak, weakPart, threads = _threads, completion](BroadcastPart &&part) { + threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, weakPart, part = std::move(part), completion]() mutable { + auto strong = weak.lock(); + if (!strong) { + return; + } + + auto pendingPart = weakPart.lock(); + if (!pendingPart) { + return; + } + + pendingPart->task.reset(); + + switch (part.status) { + case BroadcastPart::Status::Success: { + pendingPart->result = std::make_shared(std::move(part.data)); + break; + } + case BroadcastPart::Status::NotReady: { + break; + } + case BroadcastPart::Status::ResyncNeeded: { + break; + } + default: { + RTC_FATAL() << "Unknown part.status"; + break; + } + } + + completion(); + }); + }; + + const auto typeData = &part->typeData; + if (const auto audioData = absl::get_if(typeData)) { + part->task = _requestAudioBroadcastPart(_platformContext, segmentTimestamp, _segmentDuration, handleResult); + } else if (const auto videoData = absl::get_if(typeData)) { + part->task = _requestVideoBroadcastPart(_platformContext, segmentTimestamp, _segmentDuration, videoData->channelId, videoData->quality, handleResult); + } + } + + void setVolume(uint32_t ssrc, double volume) { + _volumeBySsrc[ssrc] = volume; + } + + void setActiveVideoChannels(std::vector const &videoChannels) { + _activeVideoChannels = videoChannels; + +/*#if DEBUG + for (auto &updatedVideoChannel : _activeVideoChannels) { + if (updatedVideoChannel.quality == VideoChannelDescription::Quality::Medium) { + updatedVideoChannel.quality = VideoChannelDescription::Quality::Thumbnail; + } + } +#endif*/ + + for (const auto &updatedVideoChannel : _activeVideoChannels) { + for (const auto &segment : _availableSegments) { + for (const auto &video : segment->video) { + if (video->part->getActiveEndpointId() == updatedVideoChannel.endpoint) { + if (video->quality != updatedVideoChannel.quality) { + requestPendingVideoQualityUpdate(video, segment->timestamp); + } + } + } + } + } + } + + void addVideoSink(std::string const &endpointId, std::weak_ptr> sink) { + auto it = _videoSinks.find(endpointId); + if (it == _videoSinks.end()) { + _videoSinks.insert(std::make_pair(endpointId, std::vector>>())); + } + _videoSinks[endpointId].push_back(sink); + } + +private: + std::shared_ptr _threads; + std::function(std::function)> _requestCurrentTime; + std::function(std::shared_ptr, int64_t, int64_t, std::function)> _requestAudioBroadcastPart; + std::function(std::shared_ptr, int64_t, int64_t, int32_t, VideoChannelDescription::Quality, std::function)> _requestVideoBroadcastPart; + std::function _updateAudioLevel; + + const int _segmentDuration = 1000; + const int _segmentBufferDuration = 2000; + + int64_t _nextSegmentTimestamp = 0; + + absl::optional _waitForBufferredMillisecondsBeforeRendering; + std::vector> _availableSegments; + + std::vector> _pendingSegments; + + int64_t _playbackReferenceTimestamp = 0; + + const size_t _audioDataRingBufferMaxSize = 4800; + webrtc::Mutex _audioDataMutex; + SampleRingBuffer _audioRingBuffer; + std::vector _tempAudioBuffer; + webrtc::FrameCombiner _audioFrameCombiner; + std::map> _audioVadMap; + + std::map _volumeBySsrc; + std::vector _activeVideoChannels; + std::map>>> _videoSinks; + + std::map _currentEndpointMapping; + + std::shared_ptr _platformContext; +}; + +StreamingMediaContext::StreamingMediaContext(StreamingMediaContextArguments &&arguments) { + _private = std::make_shared(std::move(arguments)); + _private->start(); +} + +StreamingMediaContext::~StreamingMediaContext() { +} + +void StreamingMediaContext::setActiveVideoChannels(std::vector const &videoChannels) { + _private->setActiveVideoChannels(videoChannels); +} + +void StreamingMediaContext::setVolume(uint32_t ssrc, double volume) { + _private->setVolume(ssrc, volume); +} + +void StreamingMediaContext::addVideoSink(std::string const &endpointId, std::weak_ptr> sink) { + _private->addVideoSink(endpointId, sink); +} + +void StreamingMediaContext::getAudio(int16_t *audio_samples, const size_t num_samples, const size_t num_channels, const uint32_t samples_per_sec) { + _private->getAudio(audio_samples, num_samples, num_channels, samples_per_sec); +} + +} diff --git a/TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.h b/TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.h new file mode 100644 index 000000000..928eddcc8 --- /dev/null +++ b/TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.h @@ -0,0 +1,53 @@ +#ifndef TGCALLS_STREAMING_MEDIA_CONTEXT_H +#define TGCALLS_STREAMING_MEDIA_CONTEXT_H + +#include "GroupInstanceImpl.h" +#include +#include "../StaticThreads.h" + +namespace tgcalls { + +class StreamingMediaContextPrivate; + +class StreamingMediaContext { +public: + struct VideoChannel { + VideoChannelDescription::Quality quality = VideoChannelDescription::Quality::Thumbnail; + std::string endpoint; + + VideoChannel(VideoChannelDescription::Quality quality_, std::string endpoint_) : + quality(quality_), + endpoint(endpoint_) { + } + }; + +public: + struct StreamingMediaContextArguments { + std::shared_ptr threads; + std::function(std::function)> requestCurrentTime; + std::function(std::shared_ptr, int64_t, int64_t, std::function)> requestAudioBroadcastPart; + std::function(std::shared_ptr, int64_t, int64_t, int32_t, VideoChannelDescription::Quality, std::function)> requestVideoBroadcastPart; + std::function updateAudioLevel; + std::shared_ptr platformContext; + }; + +public: + StreamingMediaContext(StreamingMediaContextArguments &&arguments); + ~StreamingMediaContext(); + + StreamingMediaContext& operator=(const StreamingMediaContext&) = delete; + StreamingMediaContext& operator=(StreamingMediaContext&&) = delete; + + void setActiveVideoChannels(std::vector const &videoChannels); + void setVolume(uint32_t ssrc, double volume); + void addVideoSink(std::string const &endpointId, std::weak_ptr> sink); + + void getAudio(int16_t *audio_samples, const size_t num_samples, const size_t num_channels, const uint32_t samples_per_sec); + +private: + std::shared_ptr _private; +}; + +} + +#endif diff --git a/TMessagesProj/jni/voip/tgcalls/group/StreamingPart.h b/TMessagesProj/jni/voip/tgcalls/group/StreamingPart.h deleted file mode 100644 index 6e0812cb5..000000000 --- a/TMessagesProj/jni/voip/tgcalls/group/StreamingPart.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef TGCALLS_STREAMING_PART_H -#define TGCALLS_STREAMING_PART_H - -#include "absl/types/optional.h" -#include -#include - -namespace tgcalls { - -class StreamingPartState; - -class StreamingPart { -public: - struct StreamingPartChannel { - uint32_t ssrc = 0; - std::vector pcmData; - }; - - explicit StreamingPart(std::vector &&data); - ~StreamingPart(); - - StreamingPart(const StreamingPart&) = delete; - StreamingPart(StreamingPart&& other) { - _state = other._state; - other._state = nullptr; - } - StreamingPart& operator=(const StreamingPart&) = delete; - StreamingPart& operator=(StreamingPart&&) = delete; - - int getRemainingMilliseconds() const; - std::vector get10msPerChannel(); - -private: - StreamingPartState *_state = nullptr; -}; - -} - -#endif diff --git a/TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.cpp b/TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.cpp new file mode 100644 index 000000000..591a523e0 --- /dev/null +++ b/TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.cpp @@ -0,0 +1,659 @@ +#include "VideoStreamingPart.h" + +#include "rtc_base/logging.h" +#include "rtc_base/third_party/base64/base64.h" +#include "api/video/i420_buffer.h" + +extern "C" { +#include +#include +#include +} + +#include +#include +#include + +namespace tgcalls { + +namespace { + +class AVIOContextImpl { +public: + AVIOContextImpl(std::vector &&fileData) : + _fileData(std::move(fileData)) { + _buffer.resize(4 * 1024); + _context = avio_alloc_context(_buffer.data(), (int)_buffer.size(), 0, this, &AVIOContextImpl::read, NULL, &AVIOContextImpl::seek); + } + + ~AVIOContextImpl() { + av_free(_context); + } + + static int read(void *opaque, unsigned char *buffer, int bufferSize) { + AVIOContextImpl *instance = static_cast(opaque); + + int bytesToRead = std::min(bufferSize, ((int)instance->_fileData.size()) - instance->_fileReadPosition); + if (bytesToRead < 0) { + bytesToRead = 0; + } + + if (bytesToRead > 0) { + memcpy(buffer, instance->_fileData.data() + instance->_fileReadPosition, bytesToRead); + instance->_fileReadPosition += bytesToRead; + + return bytesToRead; + } else { + return AVERROR_EOF; + } + } + + static int64_t seek(void *opaque, int64_t offset, int whence) { + AVIOContextImpl *instance = static_cast(opaque); + + if (whence == 0x10000) { + return (int64_t)instance->_fileData.size(); + } else { + int64_t seekOffset = std::min(offset, (int64_t)instance->_fileData.size()); + if (seekOffset < 0) { + seekOffset = 0; + } + instance->_fileReadPosition = (int)seekOffset; + return seekOffset; + } + } + + AVIOContext *getContext() { + return _context; + } + +private: + std::vector _fileData; + int _fileReadPosition = 0; + + std::vector _buffer; + AVIOContext *_context = nullptr; +}; + +class MediaDataPacket { +public: + MediaDataPacket() : _packet(av_packet_alloc()) { + } + + MediaDataPacket(MediaDataPacket &&other) : _packet(other._packet) { + other._packet = nullptr; + } + + ~MediaDataPacket() { + if (_packet) { + av_packet_free(&_packet); + } + } + + AVPacket *packet() { + return _packet; + } + +private: + AVPacket *_packet = nullptr; +}; + +class DecodableFrame { +public: + DecodableFrame(MediaDataPacket packet, int64_t pts, int64_t dts): + _packet(std::move(packet)), + _pts(pts), + _dts(dts) { + } + + ~DecodableFrame() { + } + + MediaDataPacket &packet() { + return _packet; + } + + int64_t pts() { + return _pts; + } + + int64_t dts() { + return _dts; + } + +private: + MediaDataPacket _packet; + int64_t _pts = 0; + int64_t _dts = 0; +}; + +class Frame { +public: + Frame() { + _frame = av_frame_alloc(); + } + + Frame(Frame &&other) { + _frame = other._frame; + other._frame = nullptr; + } + + ~Frame() { + if (_frame) { + av_frame_unref(_frame); + } + } + + AVFrame *frame() { + return _frame; + } + + double pts(AVStream *stream) { + int64_t framePts = _frame->pts; + double spf = av_q2d(stream->time_base); + return ((double)framePts) * spf; + } + + double duration(AVStream *stream) { + int64_t frameDuration = _frame->pkt_duration; + double spf = av_q2d(stream->time_base); + if (frameDuration != 0) { + return ((double)frameDuration) * spf; + } else { + return spf; + } + } + +private: + AVFrame *_frame = nullptr; +}; + +struct VideoStreamEvent { + int32_t offset = 0; + std::string endpointId; + int32_t rotation = 0; + int32_t extra = 0; +}; + +struct VideoStreamInfo { + std::string container; + int32_t activeMask = 0; + std::vector events; +}; + +absl::optional readInt32(std::vector const &data, int &offset) { + if (offset + 4 > data.size()) { + return absl::nullopt; + } + + int32_t value = 0; + memcpy(&value, data.data() + offset, 4); + offset += 4; + + return value; +} + +absl::optional readBytesAsInt32(std::vector const &data, int &offset, int count) { + if (offset + count > data.size()) { + return absl::nullopt; + } + + if (count == 0) { + return absl::nullopt; + } + + if (count <= 4) { + int32_t value = 0; + memcpy(&value, data.data() + offset, count); + offset += count; + return value; + } else { + return absl::nullopt; + } +} + +int32_t roundUp(int32_t numToRound, int32_t multiple) { + if (multiple == 0) { + return numToRound; + } + + int32_t remainder = numToRound % multiple; + if (remainder == 0) { + return numToRound; + } + + return numToRound + multiple - remainder; +} + +absl::optional readSerializedString(std::vector const &data, int &offset) { + if (const auto tmp = readBytesAsInt32(data, offset, 1)) { + int paddingBytes = 0; + int length = 0; + if (tmp.value() == 254) { + if (const auto len = readBytesAsInt32(data, offset, 3)) { + length = len.value(); + paddingBytes = roundUp(length, 4) - length; + } else { + return absl::nullopt; + } + } + else { + length = tmp.value(); + paddingBytes = roundUp(length + 1, 4) - (length + 1); + } + + if (offset + length > data.size()) { + return absl::nullopt; + } + + std::string result(data.data() + offset, data.data() + offset + length); + + offset += length; + offset += paddingBytes; + + return result; + } else { + return absl::nullopt; + } +} + +absl::optional readVideoStreamEvent(std::vector const &data, int &offset) { + VideoStreamEvent event; + + if (const auto offsetValue = readInt32(data, offset)) { + event.offset = offsetValue.value(); + } else { + return absl::nullopt; + } + + if (const auto endpointId = readSerializedString(data, offset)) { + event.endpointId = endpointId.value(); + } else { + return absl::nullopt; + } + + if (const auto rotation = readInt32(data, offset)) { + event.rotation = rotation.value(); + } else { + return absl::nullopt; + } + + if (const auto extra = readInt32(data, offset)) { + event.extra = extra.value(); + } else { + return absl::nullopt; + } + + return event; +} + +absl::optional consumeVideoStreamInfo(std::vector &data) { + int offset = 0; + if (const auto signature = readInt32(data, offset)) { + if (signature.value() != 0xa12e810d) { + return absl::nullopt; + } + } else { + return absl::nullopt; + } + + VideoStreamInfo info; + + if (const auto container = readSerializedString(data, offset)) { + info.container = container.value(); + } else { + return absl::nullopt; + } + + if (const auto activeMask = readInt32(data, offset)) { + info.activeMask = activeMask.value(); + } else { + return absl::nullopt; + } + + if (const auto eventCount = readInt32(data, offset)) { + if (const auto event = readVideoStreamEvent(data, offset)) { + info.events.push_back(event.value()); + } else { + return absl::nullopt; + } + } else { + return absl::nullopt; + } + + data.erase(data.begin(), data.begin() + offset); + + return info; +} + +} + +class VideoStreamingPartInternal { +public: + VideoStreamingPartInternal(std::string endpointId, webrtc::VideoRotation rotation, std::vector &&fileData, std::string const &container) : + _endpointId(endpointId), + _rotation(rotation) { + _avIoContext = std::make_unique(std::move(fileData)); + + int ret = 0; + + AVInputFormat *inputFormat = av_find_input_format(container.c_str()); + if (!inputFormat) { + _didReadToEnd = true; + return; + } + + _inputFormatContext = avformat_alloc_context(); + if (!_inputFormatContext) { + _didReadToEnd = true; + return; + } + + _inputFormatContext->pb = _avIoContext->getContext(); + + if ((ret = avformat_open_input(&_inputFormatContext, "", inputFormat, nullptr)) < 0) { + _didReadToEnd = true; + return; + } + + if ((ret = avformat_find_stream_info(_inputFormatContext, nullptr)) < 0) { + _didReadToEnd = true; + + avformat_close_input(&_inputFormatContext); + _inputFormatContext = nullptr; + return; + } + + AVCodecParameters *videoCodecParameters = nullptr; + AVStream *videoStream = nullptr; + for (int i = 0; i < _inputFormatContext->nb_streams; i++) { + AVStream *inStream = _inputFormatContext->streams[i]; + + AVCodecParameters *inCodecpar = inStream->codecpar; + if (inCodecpar->codec_type != AVMEDIA_TYPE_VIDEO) { + continue; + } + videoCodecParameters = inCodecpar; + videoStream = inStream; + + break; + } + + if (videoCodecParameters && videoStream) { + AVCodec *codec = avcodec_find_decoder(videoCodecParameters->codec_id); + if (codec) { + _codecContext = avcodec_alloc_context3(codec); + ret = avcodec_parameters_to_context(_codecContext, videoCodecParameters); + if (ret < 0) { + _didReadToEnd = true; + + avcodec_free_context(&_codecContext); + _codecContext = nullptr; + } else { + _codecContext->pkt_timebase = videoStream->time_base; + + ret = avcodec_open2(_codecContext, codec, nullptr); + if (ret < 0) { + _didReadToEnd = true; + + avcodec_free_context(&_codecContext); + _codecContext = nullptr; + } else { + _videoStream = videoStream; + } + } + } + } + } + + ~VideoStreamingPartInternal() { + if (_codecContext) { + avcodec_close(_codecContext); + avcodec_free_context(&_codecContext); + } + if (_inputFormatContext) { + avformat_close_input(&_inputFormatContext); + } + } + + std::string endpointId() { + return _endpointId; + } + + absl::optional readPacket() { + if (_didReadToEnd) { + return absl::nullopt; + } + if (!_inputFormatContext) { + return absl::nullopt; + } + + MediaDataPacket packet; + int result = av_read_frame(_inputFormatContext, packet.packet()); + if (result < 0) { + return absl::nullopt; + } + + return packet; + } + + std::shared_ptr readNextDecodableFrame() { + while (true) { + absl::optional packet = readPacket(); + if (packet) { + if (_videoStream && packet->packet()->stream_index == _videoStream->index) { + return std::make_shared(std::move(packet.value()), packet->packet()->pts, packet->packet()->dts); + } + } else { + return nullptr; + } + } + } + + absl::optional convertCurrentFrame() { + rtc::scoped_refptr i420Buffer = webrtc::I420Buffer::Copy( + _frame.frame()->width, + _frame.frame()->height, + _frame.frame()->data[0], + _frame.frame()->linesize[0], + _frame.frame()->data[1], + _frame.frame()->linesize[1], + _frame.frame()->data[2], + _frame.frame()->linesize[2] + ); + if (i420Buffer) { + auto videoFrame = webrtc::VideoFrame::Builder() + .set_video_frame_buffer(i420Buffer) + .set_rotation(_rotation) + .build(); + + return VideoStreamingPartFrame(_endpointId, videoFrame, _frame.pts(_videoStream), _frame.duration(_videoStream), _frameIndex); + } else { + return absl::nullopt; + } + } + + absl::optional getNextFrame() { + if (!_codecContext) { + return {}; + } + + while (true) { + if (_didReadToEnd) { + if (!_finalFrames.empty()) { + auto frame = _finalFrames[0]; + _finalFrames.erase(_finalFrames.begin()); + return frame; + } else { + break; + } + } else { + const auto frame = readNextDecodableFrame(); + if (frame) { + auto status = avcodec_send_packet(_codecContext, frame->packet().packet()); + if (status == 0) { + auto status = avcodec_receive_frame(_codecContext, _frame.frame()); + if (status == 0) { + auto convertedFrame = convertCurrentFrame(); + if (convertedFrame) { + _frameIndex++; + return convertedFrame; + } + } else if (status == -35) { + // more data needed + } else { + _didReadToEnd = true; + break; + } + } else { + _didReadToEnd = true; + return {}; + } + } else { + _didReadToEnd = true; + int status = avcodec_send_packet(_codecContext, nullptr); + if (status == 0) { + while (true) { + auto status = avcodec_receive_frame(_codecContext, _frame.frame()); + if (status == 0) { + auto convertedFrame = convertCurrentFrame(); + if (convertedFrame) { + _frameIndex++; + _finalFrames.push_back(convertedFrame.value()); + } + } else { + break; + } + } + } + } + } + } + + return {}; + } + +private: + std::string _endpointId; + webrtc::VideoRotation _rotation = webrtc::VideoRotation::kVideoRotation_0; + + std::unique_ptr _avIoContext; + + AVFormatContext *_inputFormatContext = nullptr; + AVCodecContext *_codecContext = nullptr; + AVStream *_videoStream = nullptr; + Frame _frame; + + std::vector _finalFrames; + + int _frameIndex = 0; + bool _didReadToEnd = false; +}; + +class VideoStreamingPartState { +public: + VideoStreamingPartState(std::vector &&data) { + _videoStreamInfo = consumeVideoStreamInfo(data); + if (!_videoStreamInfo) { + return; + } + + for (size_t i = 0; i < _videoStreamInfo->events.size(); i++) { + std::vector dataSlice(data.begin() + _videoStreamInfo->events[i].offset, i == (_videoStreamInfo->events.size() - 1) ? data.end() : (data.begin() + _videoStreamInfo->events[i + 1].offset)); + webrtc::VideoRotation rotation = webrtc::VideoRotation::kVideoRotation_0; + switch (_videoStreamInfo->events[i].rotation) { + case 0: { + rotation = webrtc::VideoRotation::kVideoRotation_0; + break; + } + case 90: { + rotation = webrtc::VideoRotation::kVideoRotation_90; + break; + } + case 180: { + rotation = webrtc::VideoRotation::kVideoRotation_180; + break; + } + case 270: { + rotation = webrtc::VideoRotation::kVideoRotation_270; + break; + } + default: { + break; + } + } + auto part = std::make_unique(_videoStreamInfo->events[i].endpointId, rotation, std::move(dataSlice), _videoStreamInfo->container); + _parsedParts.push_back(std::move(part)); + } + } + + ~VideoStreamingPartState() { + } + + absl::optional getFrameAtRelativeTimestamp(double timestamp) { + while (true) { + if (!_currentFrame) { + if (!_parsedParts.empty()) { + auto result = _parsedParts[0]->getNextFrame(); + if (result) { + _currentFrame = result; + _relativeTimestamp += result->duration; + } else { + _parsedParts.erase(_parsedParts.begin()); + continue; + } + } + } + + if (_currentFrame) { + if (timestamp <= _relativeTimestamp) { + return _currentFrame; + } else { + _currentFrame = absl::nullopt; + } + } else { + return absl::nullopt; + } + } + } + + absl::optional getActiveEndpointId() const { + if (!_parsedParts.empty()) { + return _parsedParts[0]->endpointId(); + } else { + return absl::nullopt; + } + } + +private: + absl::optional _videoStreamInfo; + std::vector> _parsedParts; + absl::optional _currentFrame; + double _relativeTimestamp = 0.0; +}; + +VideoStreamingPart::VideoStreamingPart(std::vector &&data) { + if (!data.empty()) { + _state = new VideoStreamingPartState(std::move(data)); + } +} + +VideoStreamingPart::~VideoStreamingPart() { + if (_state) { + delete _state; + } +} + +absl::optional VideoStreamingPart::getFrameAtRelativeTimestamp(double timestamp) { + return _state + ? _state->getFrameAtRelativeTimestamp(timestamp) + : absl::nullopt; +} + +absl::optional VideoStreamingPart::getActiveEndpointId() const { + return _state + ? _state->getActiveEndpointId() + : absl::nullopt; +} + +} diff --git a/TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.h b/TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.h new file mode 100644 index 000000000..330b1fdc0 --- /dev/null +++ b/TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.h @@ -0,0 +1,53 @@ +#ifndef TGCALLS_VIDEO_STREAMING_PART_H +#define TGCALLS_VIDEO_STREAMING_PART_H + +#include "absl/types/optional.h" +#include +#include + +#include "api/video/video_frame.h" +#include "absl/types/optional.h" + +namespace tgcalls { + +class VideoStreamingPartState; + +struct VideoStreamingPartFrame { + std::string endpointId; + webrtc::VideoFrame frame; + double pts = 0; + double duration = 0.0; + int index = 0; + + VideoStreamingPartFrame(std::string endpointId_, webrtc::VideoFrame const &frame_, double pts_, double duration_, int index_) : + endpointId(endpointId_), + frame(frame_), + pts(pts_), + duration(duration_), + index(index_) { + } +}; + +class VideoStreamingPart { +public: + explicit VideoStreamingPart(std::vector &&data); + ~VideoStreamingPart(); + + VideoStreamingPart(const VideoStreamingPart&) = delete; + VideoStreamingPart(VideoStreamingPart&& other) { + _state = other._state; + other._state = nullptr; + } + VideoStreamingPart& operator=(const VideoStreamingPart&) = delete; + VideoStreamingPart& operator=(VideoStreamingPart&&) = delete; + + absl::optional getFrameAtRelativeTimestamp(double timestamp); + absl::optional getActiveEndpointId() const; + +private: + VideoStreamingPartState *_state = nullptr; +}; + +} + +#endif diff --git a/TMessagesProj/jni/voip/tgcalls/legacy/InstanceImplLegacy.cpp b/TMessagesProj/jni/voip/tgcalls/legacy/InstanceImplLegacy.cpp index 99108f69a..bf9bd35db 100644 --- a/TMessagesProj/jni/voip/tgcalls/legacy/InstanceImplLegacy.cpp +++ b/TMessagesProj/jni/voip/tgcalls/legacy/InstanceImplLegacy.cpp @@ -209,6 +209,9 @@ void InstanceImplLegacy::receiveSignalingData(const std::vector &data) void InstanceImplLegacy::setVideoCapture(std::shared_ptr videoCapture) { } +void InstanceImplLegacy::sendVideoDeviceUpdated() { +} + void InstanceImplLegacy::setRequestedVideoAspect(float aspect) { } diff --git a/TMessagesProj/jni/voip/tgcalls/legacy/InstanceImplLegacy.h b/TMessagesProj/jni/voip/tgcalls/legacy/InstanceImplLegacy.h index 554239d71..3a3a6647e 100644 --- a/TMessagesProj/jni/voip/tgcalls/legacy/InstanceImplLegacy.h +++ b/TMessagesProj/jni/voip/tgcalls/legacy/InstanceImplLegacy.h @@ -20,6 +20,7 @@ public: void setNetworkType(NetworkType networkType) override; void setMuteMicrophone(bool muteMicrophone) override; void setVideoCapture(std::shared_ptr videoCapture) override; + void sendVideoDeviceUpdated() override; void setRequestedVideoAspect(float aspect) override; bool supportsVideo() override { return false; diff --git a/TMessagesProj/jni/voip/tgcalls/platform/android/AndroidContext.h b/TMessagesProj/jni/voip/tgcalls/platform/android/AndroidContext.h index effdb0ecf..5c3fa0b51 100644 --- a/TMessagesProj/jni/voip/tgcalls/platform/android/AndroidContext.h +++ b/TMessagesProj/jni/voip/tgcalls/platform/android/AndroidContext.h @@ -19,7 +19,8 @@ public: void setJavaInstance(JNIEnv *env, jobject instance); - std::shared_ptr streamTask; + std::vector> audioStreamTasks; + std::vector> videoStreamTasks; std::vector> descriptionTasks; private: diff --git a/TMessagesProj/jni/voip/tgcalls/reference/InstanceImplReference.cpp b/TMessagesProj/jni/voip/tgcalls/reference/InstanceImplReference.cpp index 999d5616f..7569c6ce5 100644 --- a/TMessagesProj/jni/voip/tgcalls/reference/InstanceImplReference.cpp +++ b/TMessagesProj/jni/voip/tgcalls/reference/InstanceImplReference.cpp @@ -406,7 +406,10 @@ public: } beginSendingVideo(); } - + + void sendVideoDeviceUpdated() { + } + void setRequestedVideoAspect(float aspect) { } @@ -1015,12 +1018,12 @@ PersistentState InstanceImplReference::getPersistentState() { void InstanceImplReference::stop(std::function completion) { auto result = FinalState(); - + result.persistentState = getPersistentState(); result.debugLog = logSink_->result(); result.trafficStats = getTrafficStats(); result.isRatingSuggested = false; - + completion(result); } diff --git a/TMessagesProj/jni/voip/tgcalls/reference/InstanceImplReference.h b/TMessagesProj/jni/voip/tgcalls/reference/InstanceImplReference.h index b8f0ff119..e4808d92a 100644 --- a/TMessagesProj/jni/voip/tgcalls/reference/InstanceImplReference.h +++ b/TMessagesProj/jni/voip/tgcalls/reference/InstanceImplReference.h @@ -18,6 +18,8 @@ public: void setNetworkType(NetworkType networkType) override; void setMuteMicrophone(bool muteMicrophone) override; void setVideoCapture(std::shared_ptr videoCapture) override; + void sendVideoDeviceUpdated() override { + } void setRequestedVideoAspect(float aspect) override; bool supportsVideo() override { return true; diff --git a/TMessagesProj/jni/voip/tgcalls/v2/InstanceV2Impl.h b/TMessagesProj/jni/voip/tgcalls/v2/InstanceV2Impl.h index 1a36a24c6..690a17353 100644 --- a/TMessagesProj/jni/voip/tgcalls/v2/InstanceV2Impl.h +++ b/TMessagesProj/jni/voip/tgcalls/v2/InstanceV2Impl.h @@ -44,6 +44,8 @@ public: TrafficStats getTrafficStats() override; PersistentState getPersistentState() override; void stop(std::function completion) override; + void sendVideoDeviceUpdated() override { + } private: std::shared_ptr _threads; diff --git a/TMessagesProj/src/main/java/androidx/recyclerview/widget/ChatListItemAnimator.java b/TMessagesProj/src/main/java/androidx/recyclerview/widget/ChatListItemAnimator.java index e7d53ffa2..432635de1 100644 --- a/TMessagesProj/src/main/java/androidx/recyclerview/widget/ChatListItemAnimator.java +++ b/TMessagesProj/src/main/java/androidx/recyclerview/widget/ChatListItemAnimator.java @@ -62,9 +62,7 @@ public class ChatListItemAnimator extends DefaultItemAnimator { this.activity = activity; this.recyclerListView = listView; translationInterpolator = DEFAULT_INTERPOLATOR; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - listView.getElevation(); - } + setSupportsChangeAnimations(false); } @Override diff --git a/TMessagesProj/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java b/TMessagesProj/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java index ebc44fa91..b9a468324 100644 --- a/TMessagesProj/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java +++ b/TMessagesProj/src/main/java/androidx/recyclerview/widget/LinearLayoutManager.java @@ -153,6 +153,8 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements // time. private int[] mReusableIntPair = new int[2]; + private boolean needFixGap = true; + /** * Creates a vertical LinearLayoutManager * @@ -945,6 +947,9 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements */ private int fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler, RecyclerView.State state, boolean canOffsetChildren) { + if (!needFixGap) { + return 0; + } int gap = mOrientationHelper.getEndAfterPadding() - endOffset; int fixOffset = 0; if (gap > 0) { @@ -974,6 +979,9 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements */ private int fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler, RecyclerView.State state, boolean canOffsetChildren) { + if (!needFixGap) { + return 0; + } int gap = startOffset - getStarForFixGap(); int fixOffset = 0; if (gap > 0) { @@ -2578,4 +2586,8 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements mFocusable = false; } } + + public void setNeedFixGap(boolean needFixGap) { + this.needFixGap = needFixGap; + } } diff --git a/TMessagesProj/src/main/java/androidx/recyclerview/widget/LinearSmoothScrollerCustom.java b/TMessagesProj/src/main/java/androidx/recyclerview/widget/LinearSmoothScrollerCustom.java index 50b9ab759..455b2ade1 100644 --- a/TMessagesProj/src/main/java/androidx/recyclerview/widget/LinearSmoothScrollerCustom.java +++ b/TMessagesProj/src/main/java/androidx/recyclerview/widget/LinearSmoothScrollerCustom.java @@ -38,6 +38,7 @@ public class LinearSmoothScrollerCustom extends RecyclerView.SmoothScroller { public static final int POSITION_MIDDLE = 0; public static final int POSITION_END = 1; + public static final int POSITION_TOP = 2; public LinearSmoothScrollerCustom(Context context, int position) { MILLISECONDS_PER_PX = MILLISECONDS_PER_INCH / context.getResources().getDisplayMetrics().densityDpi; @@ -125,7 +126,9 @@ public class LinearSmoothScrollerCustom extends RecyclerView.SmoothScroller { int boxSize = end - start; int viewSize = bottom - top; - if (viewSize > boxSize) { + if (scrollPosition == POSITION_TOP) { + start = layoutManager.getPaddingTop(); + } else if (viewSize > boxSize) { start = 0; } else if (scrollPosition == POSITION_MIDDLE) { start = (boxSize - viewSize) / 2; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java index d7bc57d0e..f783298b0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java @@ -3717,38 +3717,27 @@ public class AndroidUtilities { animated = false; } - if (show && view.getTag() == null) { - view.animate().setListener(null).cancel(); - if (animated) { - if (view.getVisibility() != View.VISIBLE) { - view.setVisibility(View.VISIBLE); - view.setAlpha(0f); - view.setScaleX(scaleFactor); - view.setScaleY(scaleFactor); - } - view.animate().alpha(1f).scaleY(1f).scaleX(1f).setDuration(150).start(); - } else { - view.setVisibility(View.VISIBLE); - view.setAlpha(1f); - view.setScaleX(1f); - view.setScaleY(1f); - } - view.setTag(1); - } else if (!show && view.getTag() != null){ - view.animate().setListener(null).cancel(); - if (animated) { - view.animate().alpha(0).scaleY(scaleFactor).scaleX(scaleFactor).setListener(new HideViewAfterAnimation(view)).setDuration(150).start(); - } else { - view.setVisibility(View.GONE); - } - view.setTag(null); - } else if (!animated) { + if (!animated) { view.animate().setListener(null).cancel(); view.setVisibility(show ? View.VISIBLE : View.GONE); view.setTag(show ? 1 : null); view.setAlpha(1f); view.setScaleX(1f); view.setScaleY(1f); + } else if (show && view.getTag() == null) { + view.animate().setListener(null).cancel(); + if (view.getVisibility() != View.VISIBLE) { + view.setVisibility(View.VISIBLE); + view.setAlpha(0f); + view.setScaleX(scaleFactor); + view.setScaleY(scaleFactor); + } + view.animate().alpha(1f).scaleY(1f).scaleX(1f).setDuration(150).start(); + view.setTag(1); + } else if (!show && view.getTag() != null) { + view.animate().setListener(null).cancel(); + view.animate().alpha(0).scaleY(scaleFactor).scaleX(scaleFactor).setListener(new HideViewAfterAnimation(view)).setDuration(150).start(); + view.setTag(null); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java index 4cd5a98bc..cb3e67061 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ApplicationLoader.java @@ -265,8 +265,10 @@ public class ApplicationLoader extends Application { } Utilities.globalQueue.postRunnable(() -> { try { + SharedConfig.pushStringGetTimeStart = SystemClock.elapsedRealtime(); FirebaseMessaging.getInstance().getToken() .addOnCompleteListener(task -> { + SharedConfig.pushStringGetTimeEnd = SystemClock.elapsedRealtime(); if (!task.isSuccessful()) { if (BuildVars.LOGS_ENABLED) { FileLog.d("Failed to get regid"); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java index c8ac62558..835cad3ca 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java @@ -19,8 +19,8 @@ public class BuildVars { public static boolean USE_CLOUD_STRINGS = true; public static boolean CHECK_UPDATES = true; public static boolean NO_SCOPED_STORAGE = true/* || Build.VERSION.SDK_INT <= 28*/; - public static int BUILD_VERSION = 2390; - public static String BUILD_VERSION_STRING = "7.9.3"; + public static int BUILD_VERSION = 2406; + public static String BUILD_VERSION_STRING = "8.0.0"; public static int APP_ID = 4; public static String APP_HASH = "014b35b6184100b085b0d0572f9b5103"; public static String APPCENTER_HASH = "a5b5c4f5-51da-dedc-9918-d9766a22ca7c"; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java index f70937ea4..8d58fe29d 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java @@ -1035,7 +1035,7 @@ public class ChatObject { int selfId = getSelfId(); VoIPService service = VoIPService.getSharedInstance(); TLRPC.TL_groupCallParticipant selfParticipant = participants.get(selfId); - canStreamVideo = selfParticipant != null && selfParticipant.video_joined; + canStreamVideo = true;//selfParticipant != null && selfParticipant.video_joined || BuildVars.DEBUG_PRIVATE_VERSION; boolean allowedVideoCount; boolean hasAnyVideo = false; activeVideos = 0; @@ -1243,7 +1243,7 @@ public class ChatObject { } } - public void toggleRecord(String title) { + public void toggleRecord(String title, int type) { recording = !recording; TLRPC.TL_phone_toggleGroupCallRecord req = new TLRPC.TL_phone_toggleGroupCallRecord(); req.call = getInputGroupCall(); @@ -1252,6 +1252,11 @@ public class ChatObject { req.title = title; req.flags |= 2; } + if (type == 1 || type == 2) { + req.flags |= 4; + req.video = true; + req.video_portrait = type == 1; + } currentAccount.getConnectionsManager().sendRequest(req, (response, error) -> { if (response != null) { final TLRPC.Updates res = (TLRPC.Updates) response; @@ -1434,6 +1439,10 @@ public class ChatObject { return chat instanceof TLRPC.TL_channel || chat instanceof TLRPC.TL_channelForbidden; } + public static boolean isChannelOrGiga(TLRPC.Chat chat) { + return (chat instanceof TLRPC.TL_channel || chat instanceof TLRPC.TL_channelForbidden) && (!chat.megagroup || chat.gigagroup); + } + public static boolean isMegagroup(TLRPC.Chat chat) { return (chat instanceof TLRPC.TL_channel || chat instanceof TLRPC.TL_channelForbidden) && chat.megagroup; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java index bd2294d69..4c0804b77 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java @@ -20,6 +20,8 @@ public class DispatchQueue extends Thread { private volatile Handler handler = null; private CountDownLatch syncLatch = new CountDownLatch(1); private long lastTaskTime; + private static int indexPointer = 0; + public final int index = indexPointer++; public DispatchQueue(final String threadName) { this(threadName, true); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePool.java b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePool.java index afee8d9a7..58479eecd 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePool.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueuePool.java @@ -1,16 +1,16 @@ package org.telegram.messenger; import android.os.SystemClock; - -import java.util.HashMap; -import java.util.LinkedList; +import android.util.SparseIntArray; import androidx.annotation.UiThread; +import java.util.LinkedList; + public class DispatchQueuePool { private LinkedList queues = new LinkedList<>(); - private HashMap busyQueuesMap = new HashMap<>(); + private SparseIntArray busyQueuesMap = new SparseIntArray(); private LinkedList busyQueues = new LinkedList<>(); private int maxCount; private int createdCount; @@ -66,22 +66,19 @@ public class DispatchQueuePool { } totalTasksCount++; busyQueues.add(queue); - Integer count = busyQueuesMap.get(queue); - if (count == null) { - count = 0; - } - busyQueuesMap.put(queue, count + 1); + int count = busyQueuesMap.get(queue.index, 0); + busyQueuesMap.put(queue.index, count + 1); queue.postRunnable(() -> { runnable.run(); AndroidUtilities.runOnUIThread(() -> { totalTasksCount--; - int remainingTasksCount = busyQueuesMap.get(queue) - 1; + int remainingTasksCount = busyQueuesMap.get(queue.index) - 1; if (remainingTasksCount == 0) { - busyQueuesMap.remove(queue); + busyQueuesMap.delete(queue.index); busyQueues.remove(queue); queues.add(queue); } else { - busyQueuesMap.put(queue, remainingTasksCount); + busyQueuesMap.put(queue.index, remainingTasksCount); } }); }); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java b/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java index 98668cd12..2a22f07dc 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java @@ -27,6 +27,7 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.text.Spannable; import android.text.Spanned; +import android.text.TextUtils; import android.text.style.DynamicDrawableSpan; import android.text.style.ImageSpan; import android.view.View; @@ -189,6 +190,9 @@ public class Emoji { } public static boolean isValidEmoji(CharSequence code) { + if (TextUtils.isEmpty(code)) { + return false; + } DrawableInfo info = rects.get(code); if (info == null) { CharSequence newCode = EmojiData.emojiAliasMap.get(code); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ForwardingMessagesParams.java b/TMessagesProj/src/main/java/org/telegram/messenger/ForwardingMessagesParams.java new file mode 100644 index 000000000..8690f97f1 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ForwardingMessagesParams.java @@ -0,0 +1,178 @@ +package org.telegram.messenger; + +import android.text.TextUtils; +import android.util.LongSparseArray; +import android.util.SparseBooleanArray; + +import org.telegram.tgnet.TLRPC; + +import java.util.ArrayList; + +public class ForwardingMessagesParams { + + public LongSparseArray groupedMessagesMap = new LongSparseArray<>(); + public ArrayList messages; + public ArrayList previewMessages = new ArrayList<>(); + public SparseBooleanArray selectedIds = new SparseBooleanArray(); + public boolean hideForwardSendersName; + public boolean hideCaption; + public boolean hasCaption; + public boolean hasSenders; + public boolean isSecret; + public boolean willSeeSenders; + public boolean multiplyUsers; + + public ArrayList pollChoosenAnswers = new ArrayList(); + + public ForwardingMessagesParams(ArrayList messages, long newDialogId) { + this.messages = messages; + hasCaption = false; + hasSenders = false; + isSecret = DialogObject.isSecretDialogId(newDialogId); + ArrayList hiddenSendersName = new ArrayList<>(); + for (int i = 0; i < messages.size(); i++) { + MessageObject messageObject = messages.get(i); + if (!TextUtils.isEmpty(messageObject.caption)) { + hasCaption = true; + } + selectedIds.put(messageObject.getId(), true); + + TLRPC.Message message = new TLRPC.TL_message(); + message.id = messageObject.messageOwner.id; + message.grouped_id = messageObject.messageOwner.grouped_id; + message.peer_id = messageObject.messageOwner.peer_id; + message.from_id = messageObject.messageOwner.from_id; + message.message = messageObject.messageOwner.message; + message.media = messageObject.messageOwner.media; + message.action = messageObject.messageOwner.action; + message.edit_date = 0; + + message.out = true; + message.unread = false; + message.via_bot_id = messageObject.messageOwner.via_bot_id; + message.reply_markup = messageObject.messageOwner.reply_markup; + message.post = messageObject.messageOwner.post; + message.legacy = messageObject.messageOwner.legacy; + + TLRPC.MessageFwdHeader header = null; + + int clientUserId = UserConfig.getInstance(messageObject.currentAccount).clientUserId; + if (!isSecret) { + if (messageObject.messageOwner.fwd_from != null) { + header = messageObject.messageOwner.fwd_from; + if (!messageObject.isDice()) { + hasSenders = true; + } else { + willSeeSenders = true; + } + if (header.from_id == null && !hiddenSendersName.contains(header.from_name)) { + hiddenSendersName.add(header.from_name); + } + } else if (messageObject.messageOwner.from_id.user_id == 0 || messageObject.messageOwner.dialog_id != clientUserId || messageObject.messageOwner.from_id.user_id != clientUserId) { + header = new TLRPC.TL_messageFwdHeader(); + header.from_id = messageObject.messageOwner.from_id; + if (!messageObject.isDice()) { + hasSenders = true; + } else { + willSeeSenders = true; + } + } + } + + if (header != null) { + message.fwd_from = header; + message.flags |= TLRPC.MESSAGE_FLAG_FWD; + } + message.dialog_id = newDialogId; + + MessageObject previewMessage = new MessageObject(messageObject.currentAccount, message, true, false) { + @Override + public boolean needDrawForwarded() { + if (hideForwardSendersName) { + return false; + } + return super.needDrawForwarded(); + } + }; + previewMessage.preview = true; + if (previewMessage.getGroupId() != 0) { + MessageObject.GroupedMessages groupedMessages = groupedMessagesMap.get(previewMessage.getGroupId(), null); + if (groupedMessages == null) { + groupedMessages = new MessageObject.GroupedMessages(); + groupedMessagesMap.put(previewMessage.getGroupId(), groupedMessages); + } + groupedMessages.messages.add(previewMessage); + } + previewMessages.add(0, previewMessage); + + if (messageObject.isPoll()) { + TLRPC.TL_messageMediaPoll mediaPoll = (TLRPC.TL_messageMediaPoll) messageObject.messageOwner.media; + PreviewMediaPoll newMediaPoll = new PreviewMediaPoll(); + newMediaPoll.poll = mediaPoll.poll; + newMediaPoll.provider = mediaPoll.provider; + newMediaPoll.results = new TLRPC.TL_pollResults(); + newMediaPoll.totalVotersCached = newMediaPoll.results.total_voters = mediaPoll.results.total_voters; + + previewMessage.messageOwner.media = newMediaPoll; + + if (messageObject.canUnvote()) { + for (int a = 0, N = mediaPoll.results.results.size(); a < N; a++) { + TLRPC.TL_pollAnswerVoters answer = mediaPoll.results.results.get(a); + if (answer.chosen) { + TLRPC.TL_pollAnswerVoters newAnswer = new TLRPC.TL_pollAnswerVoters(); + newAnswer.chosen = answer.chosen; + newAnswer.correct = answer.correct; + newAnswer.flags = answer.flags; + newAnswer.option = answer.option; + newAnswer.voters = answer.voters; + pollChoosenAnswers.add(newAnswer); + newMediaPoll.results.results.add(newAnswer); + } else { + newMediaPoll.results.results.add(answer); + } + } + } + } + } + + ArrayList uids = new ArrayList<>(); + for (int a = 1; a < messages.size(); a++) { + MessageObject object = messages.get(a); + int uid; + if (object.isFromUser()) { + uid = object.messageOwner.from_id.user_id; + } else { + TLRPC.Chat chat = MessagesController.getInstance(object.currentAccount).getChat(object.messageOwner.peer_id.channel_id); + if (ChatObject.isChannel(chat) && chat.megagroup && object.isForwardedChannelPost()) { + uid = -object.messageOwner.fwd_from.from_id.channel_id; + } else { + uid = -object.messageOwner.peer_id.channel_id; + } + } + if (!uids.contains(uid)) { + uids.add(uid); + } + } + if (uids.size() + hiddenSendersName.size() > 1) { + multiplyUsers = true; + } + for (int i = 0; i < groupedMessagesMap.size(); i++) { + groupedMessagesMap.valueAt(i).calculate(); + } + } + + public void getSelectedMessages(ArrayList messagesToForward) { + messagesToForward.clear(); + for (int i = 0; i < messages.size(); i++) { + MessageObject messageObject = messages.get(i); + int id = messageObject.getId(); + if (selectedIds.get(id, false)) { + messagesToForward.add(messageObject); + } + } + } + + public class PreviewMediaPoll extends TLRPC.TL_messageMediaPoll { + public int totalVotersCached; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/GcmPushListenerService.java b/TMessagesProj/src/main/java/org/telegram/messenger/GcmPushListenerService.java index 8c3739424..902fa3802 100755 --- a/TMessagesProj/src/main/java/org/telegram/messenger/GcmPushListenerService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/GcmPushListenerService.java @@ -134,12 +134,21 @@ public class GcmPushListenerService extends FirebaseMessagingService { } } int account = UserConfig.selectedAccount; + boolean foundAccount = false; for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) { if (UserConfig.getInstance(a).getClientUserId() == accountUserId) { account = a; + foundAccount = true; break; } } + if (!foundAccount) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("GCM ACCOUNT NOT FOUND"); + } + countDownLatch.countDown(); + return; + } final int accountFinal = currentAccount = account; if (!UserConfig.getInstance(currentAccount).isClientActivated()) { if (BuildVars.LOGS_ENABLED) { @@ -1109,6 +1118,11 @@ public class GcmPushListenerService extends FirebaseMessagingService { if (token == null) { return; } + boolean sendStat = false; + if (SharedConfig.pushStringGetTimeStart != 0 && SharedConfig.pushStringGetTimeEnd != 0 && (!SharedConfig.pushStatSent || !TextUtils.equals(SharedConfig.pushString, token))) { + sendStat = true; + SharedConfig.pushStatSent = false; + } SharedConfig.pushString = token; for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) { UserConfig userConfig = UserConfig.getInstance(a); @@ -1116,6 +1130,30 @@ public class GcmPushListenerService extends FirebaseMessagingService { userConfig.saveConfig(false); if (userConfig.getClientUserId() != 0) { final int currentAccount = a; + if (sendStat) { + TLRPC.TL_help_saveAppLog req = new TLRPC.TL_help_saveAppLog(); + TLRPC.TL_inputAppEvent event = new TLRPC.TL_inputAppEvent(); + event.time = SharedConfig.pushStringGetTimeStart; + event.type = "fcm_token_request"; + event.peer = 0; + event.data = new TLRPC.TL_jsonNull(); + req.events.add(event); + + event = new TLRPC.TL_inputAppEvent(); + event.time = SharedConfig.pushStringGetTimeEnd; + event.type = "fcm_token_response"; + event.peer = SharedConfig.pushStringGetTimeEnd - SharedConfig.pushStringGetTimeStart; + event.data = new TLRPC.TL_jsonNull(); + req.events.add(event); + + sendStat = false; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error != null) { + SharedConfig.pushStatSent = true; + SharedConfig.saveConfig(); + } + })); + } AndroidUtilities.runOnUIThread(() -> MessagesController.getInstance(currentAccount).registerForPush(token)); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLocation.java b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLocation.java index a5f82259c..70d696261 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLocation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLocation.java @@ -342,7 +342,8 @@ public class ImageLocation { } else if (document != null) { if (!url && document instanceof DocumentObject.ThemeDocument) { DocumentObject.ThemeDocument themeDocument = (DocumentObject.ThemeDocument) document; - return document.dc_id + "_" + document.id + "_" + Theme.getBaseThemeKey(themeDocument.themeSettings) + "_" + themeDocument.themeSettings.accent_color + "_" + themeDocument.themeSettings.message_top_color + "_" + themeDocument.themeSettings.message_bottom_color; + return document.dc_id + "_" + document.id + "_" + Theme.getBaseThemeKey(themeDocument.themeSettings) + "_" + themeDocument.themeSettings.accent_color + "_" + + (themeDocument.themeSettings.message_colors.size() > 1 ? themeDocument.themeSettings.message_colors.get(1) : 0) + "_" + (themeDocument.themeSettings.message_colors.size() > 0 ? themeDocument.themeSettings.message_colors.get(0) : 0); } else if (document.id != 0 && document.dc_id != 0) { return document.dc_id + "_" + document.id; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java index f1e0db2a3..51d55edee 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java @@ -203,7 +203,6 @@ public class MediaDataController extends BaseController { loadingStickers[a] = false; stickersLoaded[a] = false; } - featuredStickerSets.clear(); loadingPinnedMessages.clear(); loadFeaturedDate = 0; loadFeaturedHash = 0; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java index 15e429bcf..862b2579a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java @@ -122,6 +122,9 @@ public class MessageObject { public boolean isRestrictedMessage; public long loadedFileSize; + public byte[] sponsoredId; + public String botStartParam; + public boolean animateComments; public boolean loadingCancelled; @@ -135,6 +138,7 @@ public class MessageObject { public boolean cancelEditing; public boolean scheduled; + public boolean preview; public ArrayList checkedVotes; @@ -179,6 +183,7 @@ public class MessageObject { public ArrayList highlightedWords; public String messageTrimmedToHighlight; + public int parentWidth; static final String[] excludeWords = new String[] { " vs. ", @@ -1579,6 +1584,27 @@ public class MessageObject { } else { message.media = new TLRPC.TL_messageMediaEmpty(); } + } else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionChangeTheme) { + messageText = replaceWithLink(chat.megagroup ? LocaleController.getString("EventLogEditedGroupTheme", R.string.EventLogEditedGroupTheme) : LocaleController.getString("EventLogEditedChannelTheme", R.string.EventLogEditedChannelTheme), "un1", fromUser); + message = new TLRPC.TL_message(); + message.out = false; + message.unread = false; + message.from_id = new TLRPC.TL_peerUser(); + message.from_id.user_id = event.user_id; + message.peer_id = peer_id; + message.date = event.date; + message.message = ((TLRPC.TL_channelAdminLogEventActionChangeTheme) event.action).new_value; + if (!TextUtils.isEmpty(((TLRPC.TL_channelAdminLogEventActionChangeTheme) event.action).prev_value)) { + message.media = new TLRPC.TL_messageMediaWebPage(); + message.media.webpage = new TLRPC.TL_webPage(); + message.media.webpage.flags = 10; + message.media.webpage.display_url = ""; + message.media.webpage.url = ""; + message.media.webpage.site_name = LocaleController.getString("EventLogPreviousGroupTheme", R.string.EventLogPreviousGroupTheme); + message.media.webpage.description = ((TLRPC.TL_channelAdminLogEventActionChangeTheme) event.action).prev_value; + } else { + message.media = new TLRPC.TL_messageMediaEmpty(); + } } else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionChangeUsername) { String newLink = ((TLRPC.TL_channelAdminLogEventActionChangeUsername) event.action).new_value; if (!TextUtils.isEmpty(newLink)) { @@ -1710,9 +1736,17 @@ public class MessageObject { messageText = replaceWithLink(LocaleController.formatString("EventLogToggledSlowmodeOn", R.string.EventLogToggledSlowmodeOn, string), "un1", fromUser); } } else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionStartGroupCall) { - messageText = replaceWithLink(LocaleController.getString("EventLogStartedVoiceChat", R.string.EventLogStartedVoiceChat), "un1", fromUser); + if (ChatObject.isChannel(chat) && (!chat.megagroup || chat.gigagroup)) { + messageText = replaceWithLink(LocaleController.getString("EventLogStartedLiveStream", R.string.EventLogStartedLiveStream), "un1", fromUser); + } else { + messageText = replaceWithLink(LocaleController.getString("EventLogStartedVoiceChat", R.string.EventLogStartedVoiceChat), "un1", fromUser); + } } else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionDiscardGroupCall) { - messageText = replaceWithLink(LocaleController.getString("EventLogEndedVoiceChat", R.string.EventLogEndedVoiceChat), "un1", fromUser); + if (ChatObject.isChannel(chat) && (!chat.megagroup || chat.gigagroup)) { + messageText = replaceWithLink(LocaleController.getString("EventLogEndedLiveStream", R.string.EventLogEndedLiveStream), "un1", fromUser); + } else { + messageText = replaceWithLink(LocaleController.getString("EventLogEndedVoiceChat", R.string.EventLogEndedVoiceChat), "un1", fromUser); + } } else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionParticipantMute) { TLRPC.TL_channelAdminLogEventActionParticipantMute action = (TLRPC.TL_channelAdminLogEventActionParticipantMute) event.action; int id = getPeerId(action.participant.peer); @@ -2211,6 +2245,10 @@ public class MessageObject { return false; } + public boolean isSponsored() { + return sponsoredId != null; + } + public long getPollId() { if (type != TYPE_POLL) { return 0; @@ -2466,7 +2504,11 @@ public class MessageObject { if (messageOwner.action != null) { if (messageOwner.action instanceof TLRPC.TL_messageActionGroupCallScheduled) { TLRPC.TL_messageActionGroupCallScheduled action = (TLRPC.TL_messageActionGroupCallScheduled) messageOwner.action; - messageText = LocaleController.formatString("ActionGroupCallScheduled", R.string.ActionGroupCallScheduled, LocaleController.formatStartsTime(action.schedule_date, 3, false)); + if (messageOwner.peer_id instanceof TLRPC.TL_peerChat || isSupergroup()) { + messageText = LocaleController.formatString("ActionGroupCallScheduled", R.string.ActionGroupCallScheduled, LocaleController.formatStartsTime(action.schedule_date, 3, false)); + } else { + messageText = LocaleController.formatString("ActionChannelCallScheduled", R.string.ActionChannelCallScheduled, LocaleController.formatStartsTime(action.schedule_date, 3, false)); + } } else if (messageOwner.action instanceof TLRPC.TL_messageActionGroupCall) { if (messageOwner.action.duration != 0) { String time; @@ -2494,7 +2536,7 @@ public class MessageObject { messageText = replaceWithLink(LocaleController.formatString("ActionGroupCallEndedBy", R.string.ActionGroupCallEndedBy, time), "un1", fromObject); } } else { - messageText = LocaleController.formatString("ActionGroupCallEnded", R.string.ActionGroupCallEnded, time); + messageText = LocaleController.formatString("ActionChannelCallEnded", R.string.ActionChannelCallEnded, time); } } else { if (messageOwner.peer_id instanceof TLRPC.TL_peerChat || isSupergroup()) { @@ -2504,7 +2546,7 @@ public class MessageObject { messageText = replaceWithLink(LocaleController.getString("ActionGroupCallStarted", R.string.ActionGroupCallStarted), "un1", fromObject); } } else { - messageText = LocaleController.getString("ActionGroupCallJustStarted", R.string.ActionGroupCallJustStarted); + messageText = LocaleController.getString("ActionChannelCallJustStarted", R.string.ActionChannelCallJustStarted); } } } else if (messageOwner.action instanceof TLRPC.TL_messageActionInviteToGroupCall) { @@ -4113,7 +4155,9 @@ public class MessageObject { } public boolean needDrawShareButton() { - if (scheduled) { + if (preview) { + return false; + } else if (scheduled) { return false; } else if (eventId != 0) { return false; @@ -4160,7 +4204,7 @@ public class MessageObject { if (AndroidUtilities.isTablet() && eventId != 0) { generatedWithMinSize = AndroidUtilities.dp(530); } else { - generatedWithMinSize = AndroidUtilities.isTablet() ? AndroidUtilities.getMinTabletSide() : AndroidUtilities.displaySize.x; + generatedWithMinSize = AndroidUtilities.isTablet() ? AndroidUtilities.getMinTabletSide() : getParentWidth(); } generatedWithDensity = AndroidUtilities.density; if (messageOwner.media instanceof TLRPC.TL_messageMediaWebPage && messageOwner.media.webpage != null && "telegram_background".equals(messageOwner.media.webpage.type)) { @@ -4464,6 +4508,9 @@ public class MessageObject { } public boolean isOutOwner() { + if (preview) { + return true; + } TLRPC.Chat chat = messageOwner.peer_id != null && messageOwner.peer_id.channel_id != 0 ? getChat(null, null, messageOwner.peer_id.channel_id) : null; if (!messageOwner.out || !(messageOwner.from_id instanceof TLRPC.TL_peerUser) && (!(messageOwner.from_id instanceof TLRPC.TL_peerChannel) || ChatObject.isChannel(chat) && !chat.megagroup) || messageOwner.post) { return false; @@ -4480,11 +4527,11 @@ public class MessageObject { } public boolean needDrawAvatar() { - return isFromUser() || isFromGroup() || eventId != 0 || messageOwner.fwd_from != null && messageOwner.fwd_from.saved_from_peer != null; + return !isSponsored() && (isFromUser() || isFromGroup() || eventId != 0 || messageOwner.fwd_from != null && messageOwner.fwd_from.saved_from_peer != null); } private boolean needDrawAvatarInternal() { - return isFromChat() && isFromUser() || isFromGroup() || eventId != 0 || messageOwner.fwd_from != null && messageOwner.fwd_from.saved_from_peer != null; + return !isSponsored() && (isFromChat() && isFromUser() || isFromGroup() || eventId != 0 || messageOwner.fwd_from != null && messageOwner.fwd_from.saved_from_peer != null); } public boolean isFromChat() { @@ -4541,7 +4588,7 @@ public class MessageObject { } public boolean isForwardedChannelPost() { - return messageOwner.from_id instanceof TLRPC.TL_peerChannel && messageOwner.fwd_from != null && messageOwner.fwd_from.channel_post != 0; + return messageOwner.from_id instanceof TLRPC.TL_peerChannel && messageOwner.fwd_from != null && messageOwner.fwd_from.channel_post != 0 && messageOwner.fwd_from.saved_from_peer instanceof TLRPC.TL_peerChannel && messageOwner.from_id.channel_id == messageOwner.fwd_from.saved_from_peer.channel_id; } public boolean isUnread() { @@ -5202,6 +5249,10 @@ public class MessageObject { } } + private int getParentWidth() { + return (preview && parentWidth > 0) ? parentWidth : AndroidUtilities.displaySize.x; + } + public String getStickerEmoji() { TLRPC.Document document = getDocument(); if (document == null) { @@ -5604,7 +5655,7 @@ public class MessageObject { } public boolean canForwardMessage() { - return !(messageOwner instanceof TLRPC.TL_message_secret) && !needDrawBluredPreview() && !isLiveLocation() && type != 16; + return !(messageOwner instanceof TLRPC.TL_message_secret) && !needDrawBluredPreview() && !isLiveLocation() && type != 16 && !isSponsored(); } public boolean canEditMedia() { @@ -5712,7 +5763,7 @@ public class MessageObject { } public boolean canDeleteMessage(boolean inScheduleMode, TLRPC.Chat chat) { - return eventId == 0 && canDeleteMessage(currentAccount, inScheduleMode, messageOwner, chat); + return eventId == 0 && sponsoredId == null && canDeleteMessage(currentAccount, inScheduleMode, messageOwner, chat); } public static boolean canDeleteMessage(int currentAccount, boolean inScheduleMode, TLRPC.Message message, TLRPC.Chat chat) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index d6ca3d5ca..33f96003a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -78,7 +78,7 @@ public class MessagesController extends BaseController implements NotificationCe private SparseArray exportedChats = new SparseArray<>(); public ArrayList hintDialogs = new ArrayList<>(); - private SparseArray> dialogsByFolder = new SparseArray<>(); + public SparseArray> dialogsByFolder = new SparseArray<>(); protected ArrayList allDialogs = new ArrayList<>(); public ArrayList dialogsForward = new ArrayList<>(); public ArrayList dialogsServerOnly = new ArrayList<>(); @@ -100,7 +100,7 @@ public class MessagesController extends BaseController implements NotificationCe public ConcurrentHashMap>> printingUsers = new ConcurrentHashMap<>(20, 1.0f, 2); public LongSparseArray> printingStrings = new LongSparseArray<>(); public LongSparseArray> printingStringsTypes = new LongSparseArray<>(); - public LongSparseArray>[] sendingTypings = new LongSparseArray[10]; + public LongSparseArray>[] sendingTypings = new LongSparseArray[11]; public ConcurrentHashMap onlinePrivacy = new ConcurrentHashMap<>(20, 1.0f, 2); private int lastPrintingStringCount; @@ -171,6 +171,8 @@ public class MessagesController extends BaseController implements NotificationCe private SparseIntArray migratedChats = new SparseIntArray(); + private LongSparseArray sponsoredMessages = new LongSparseArray<>(); + private HashMap> reloadingWebpages = new HashMap<>(); private LongSparseArray> reloadingWebpagesPending = new LongSparseArray<>(); private HashMap> reloadingScheduledWebpages = new HashMap<>(); @@ -322,6 +324,12 @@ public class MessagesController extends BaseController implements NotificationCe public volatile boolean ignoreSetOnline; + private class SponsoredMessagesInfo { + private ArrayList messages; + private long loadTime; + private boolean loading; + } + public static class FaqSearchResult { public String title; @@ -2287,14 +2295,18 @@ public class MessagesController extends BaseController implements NotificationCe settings.base_theme = Theme.getBaseThemeByKey(themeInfo.name); settings.accent_color = accent.accentColor; if (accent.myMessagesAccentColor != 0) { - settings.message_bottom_color = accent.myMessagesAccentColor; + settings.message_colors.add(accent.myMessagesAccentColor); settings.flags |= 1; - } - if (accent.myMessagesGradientAccentColor != 0) { - settings.message_top_color = accent.myMessagesGradientAccentColor; - settings.flags |= 1; - } else if (settings.message_bottom_color != 0) { - settings.message_top_color = settings.message_bottom_color; + if (accent.myMessagesGradientAccentColor1 != 0) { + settings.message_colors.add(accent.myMessagesGradientAccentColor1); + if (accent.myMessagesGradientAccentColor2 != 0) { + settings.message_colors.add(accent.myMessagesGradientAccentColor2); + if (accent.myMessagesGradientAccentColor3 != 0) { + settings.message_colors.add(accent.myMessagesGradientAccentColor3); + } + } + } + settings.message_colors_animated = accent.myMessagesAnimated; } settings.flags |= 2; settings.wallpaper_settings = new TLRPC.TL_wallPaperSettings(); @@ -2538,6 +2550,7 @@ public class MessagesController extends BaseController implements NotificationCe reloadingWebpagesPending.clear(); reloadingScheduledWebpages.clear(); reloadingScheduledWebpagesPending.clear(); + sponsoredMessages.clear(); dialogs_dict.clear(); dialogs_read_inbox_max.clear(); loadingPinnedDialogs.clear(); @@ -3865,7 +3878,7 @@ public class MessagesController extends BaseController implements NotificationCe if (currentDeletingTaskMedia) { getMessagesStorage().emptyMessagesMedia(mids); } else { - deleteMessages(mids, null, null, 0, 0, false, false, !mids.isEmpty() && mids.get(0) > 0); + deleteMessages(mids, null, null, 0, 0, true, false, !mids.isEmpty() && mids.get(0) > 0); } Utilities.stageQueue.postRunnable(() -> { getNewDeleteTask(mids, currentDeletingTaskChannelId, currentDeletingTaskMedia); @@ -5945,6 +5958,13 @@ public class MessagesController extends BaseController implements NotificationCe newPrintingStrings.put(threadId, LocaleController.getString("SelectingContact", R.string.SelectingContact)); } newPrintingStringsTypes.put(threadId, 0); + } else if (pu.action instanceof TLRPC.TL_sendMessageChooseStickerAction) { + if (lower_id < 0) { + newPrintingStrings.put(threadId, LocaleController.formatString("IsChoosingSticker", R.string.IsChoosingSticker, getUserNameForTyping(user))); + } else { + newPrintingStrings.put(threadId, LocaleController.getString("ChoosingSticker", R.string.ChoosingSticker)); + } + newPrintingStringsTypes.put(threadId, 5); } else { if (lower_id < 0) { newPrintingStrings.put(threadId, LocaleController.formatString("IsTypingGroup", R.string.IsTypingGroup, getUserNameForTyping(user))); @@ -6084,6 +6104,8 @@ public class MessagesController extends BaseController implements NotificationCe req.action = new TLRPC.TL_sendMessageUploadRoundAction(); } else if (action == 9) { req.action = new TLRPC.TL_sendMessageUploadAudioAction(); + } else if (action == 10) { + req.action = new TLRPC.TL_sendMessageChooseStickerAction(); } threads.put(threadMsgId, true); int reqId = getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> cancelTyping(action, dialogId, threadMsgId)), ConnectionsManager.RequestFlagFailOnServerErrors); @@ -13753,6 +13775,81 @@ public class MessagesController extends BaseController implements NotificationCe return false; } + public ArrayList getSponsoredMessages(long dialogId) { + SponsoredMessagesInfo info = sponsoredMessages.get(dialogId); + if (info != null && (info.loading || Math.abs(SystemClock.elapsedRealtime() - info.loadTime) <= 5 * 60 * 1000)) { + return info.messages; + } + TLRPC.Chat chat = getChat((int) -dialogId); //TODO long + if (!ChatObject.isChannel(chat) || chat.megagroup || chat.gigagroup) { + return null; + } + info = new SponsoredMessagesInfo(); + info.loading = true; + sponsoredMessages.put(dialogId, info); + SponsoredMessagesInfo infoFinal = info; + TLRPC.TL_channels_getSponsoredMessages req = new TLRPC.TL_channels_getSponsoredMessages(); + req.channel = getInputChannel(chat); + getConnectionsManager().sendRequest(req, (response, error) -> { + ArrayList result; + if (response != null) { + TLRPC.TL_messages_sponsoredMessages res = (TLRPC.TL_messages_sponsoredMessages) response; + if (res.messages.isEmpty()) { + result = null; + } else { + result = new ArrayList<>(); + AndroidUtilities.runOnUIThread(() -> { + putUsers(res.users, false); + putChats(res.chats, false); + }); + final SparseArray usersDict = new SparseArray<>(); + final SparseArray chatsDict = new SparseArray<>(); + + for (int a = 0; a < res.users.size(); a++) { + TLRPC.User u = res.users.get(a); + usersDict.put(u.id, u); + } + for (int a = 0; a < res.chats.size(); a++) { + TLRPC.Chat c = res.chats.get(a); + chatsDict.put(c.id, c); + } + + int messageId = -10000000; + for (int a = 0, N = res.messages.size(); a < N; a++) { + TLRPC.TL_sponsoredMessage sponsoredMessage = res.messages.get(a); + TLRPC.TL_message message = new TLRPC.TL_message(); + message.message = sponsoredMessage.message; + if (!sponsoredMessage.entities.isEmpty()) { + message.entities = sponsoredMessage.entities; + message.flags |= 128; + } + message.peer_id = getPeer((int) dialogId); //TODO long + message.from_id = sponsoredMessage.from_id; + message.flags |= 256; + message.date = getConnectionsManager().getCurrentTime(); + message.id = messageId--; + MessageObject messageObject = new MessageObject(currentAccount, message, usersDict, chatsDict, true, true); + messageObject.sponsoredId = sponsoredMessage.random_id; + messageObject.botStartParam = sponsoredMessage.start_param; + result.add(messageObject); + } + } + } else { + result = null; + } + AndroidUtilities.runOnUIThread(() -> { + if (result == null) { + sponsoredMessages.remove(dialogId); + } else { + infoFinal.loadTime = SystemClock.elapsedRealtime(); + infoFinal.messages = result; + getNotificationCenter().postNotificationName(NotificationCenter.didLoadSponsoredMessages, dialogId, result); + } + }); + }); + return null; + } + public CharSequence getPrintingString(long dialogId, int threadId, boolean isDialog) { if (isDialog && (int) dialogId > 0) { TLRPC.User user = getUser((int) dialogId); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java index f5e592dac..6805b6654 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java @@ -6743,7 +6743,11 @@ public class MessagesStorage extends BaseController { } if (MessageObject.isSecretMedia(message)) { try { - SQLiteCursor cursor2 = database.queryFinalized(String.format(Locale.US, "SELECT date FROM enc_tasks_v3 WHERE mid = %d", message.id)); + long messageId = message.id; + if (message.peer_id.channel_id != 0) { + messageId |= ((long) message.peer_id.channel_id) << 32; + } + SQLiteCursor cursor2 = database.queryFinalized(String.format(Locale.US, "SELECT date FROM enc_tasks_v3 WHERE mid = %d AND media = 1", messageId)); if (cursor2.next()) { message.destroyTime = cursor2.intValue(0); } @@ -10732,6 +10736,15 @@ public class MessagesStorage extends BaseController { chatsToLoad.add(-message.ttl); } } + if (message.params != null) { + String peerIdStr = message.params.get("fwd_peer"); + if (peerIdStr != null) { + int peerId = Utilities.parseInt(peerIdStr); + if (peerId < 0) { + chatsToLoad.add(-peerId); + } + } + } } public void getDialogs(final int folderId, final int offset, final int count, final boolean loadDraftsPeersAndFolders) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java index 83bb4f38b..057b417a5 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java @@ -32,6 +32,7 @@ public class NotificationCenter { public static final int commentsRead = totalEvents++; public static final int changeRepliesCounter = totalEvents++; public static final int messagesDidLoad = totalEvents++; + public static final int didLoadSponsoredMessages = totalEvents++; public static final int messagesDidLoadWithoutProcess = totalEvents++; public static final int loadingMessagesFailed = totalEvents++; public static final int messageReceivedByAck = totalEvents++; @@ -188,6 +189,7 @@ public class NotificationCenter { public static final int didReceiveSmsCode = totalEvents++; public static final int didReceiveCall = totalEvents++; public static final int emojiLoaded = totalEvents++; + public static final int invalidateMotionBackground = totalEvents++; public static final int closeOtherAppActivities = totalEvents++; public static final int cameraInitied = totalEvents++; public static final int didReplacedPhotoInMemCache = totalEvents++; @@ -411,7 +413,7 @@ public class NotificationCenter { } public void postNotificationName(int id, Object... args) { - boolean allowDuringAnimation = id == startAllHeavyOperations || id == stopAllHeavyOperations || id == didReplacedPhotoInMemCache || id == closeChats; + boolean allowDuringAnimation = id == startAllHeavyOperations || id == stopAllHeavyOperations || id == didReplacedPhotoInMemCache || id == closeChats || id == invalidateMotionBackground; ArrayList expiredIndices = null; if (!allowDuringAnimation && !allowedNotifications.isEmpty()) { int size = allowedNotifications.size(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java index 7dcb7e034..5ea5e7770 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java @@ -1448,7 +1448,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe } else if ((int) did != 0) { ArrayList arrayList = new ArrayList<>(); arrayList.add(messageObject); - sendMessage(arrayList, did, true, 0); + sendMessage(arrayList, did, true, false, true, 0); } } else if (messageObject.messageOwner.message != null) { TLRPC.WebPage webPage = null; @@ -1475,7 +1475,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe } else if ((int) did != 0) { ArrayList arrayList = new ArrayList<>(); arrayList.add(messageObject); - sendMessage(arrayList, did, true, 0); + sendMessage(arrayList, did, true, false, true, 0); } } @@ -1633,7 +1633,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe } } - public int sendMessage(ArrayList messages, final long peer, boolean notify, int scheduleDate) { + public int sendMessage(ArrayList messages, final long peer, boolean forwardFromMyName, boolean hideCaption, boolean notify, int scheduleDate) { if (messages == null || messages.isEmpty()) { return 0; } @@ -1716,68 +1716,74 @@ public class SendMessagesHelper extends BaseController implements NotificationCe } final TLRPC.Message newMsg = new TLRPC.TL_message(); - boolean forwardFromSaved = msgObj.getDialogId() == myId && msgObj.isFromUser() && msgObj.messageOwner.from_id.user_id == myId; - if (msgObj.isForwarded()) { - newMsg.fwd_from = new TLRPC.TL_messageFwdHeader(); - if ((msgObj.messageOwner.fwd_from.flags & 1) != 0) { - newMsg.fwd_from.flags |= 1; - newMsg.fwd_from.from_id = msgObj.messageOwner.fwd_from.from_id; - } - if ((msgObj.messageOwner.fwd_from.flags & 32) != 0) { - newMsg.fwd_from.flags |= 32; - newMsg.fwd_from.from_name = msgObj.messageOwner.fwd_from.from_name; - } - if ((msgObj.messageOwner.fwd_from.flags & 4) != 0) { - newMsg.fwd_from.flags |= 4; - newMsg.fwd_from.channel_post = msgObj.messageOwner.fwd_from.channel_post; - } - if ((msgObj.messageOwner.fwd_from.flags & 8) != 0) { - newMsg.fwd_from.flags |= 8; - newMsg.fwd_from.post_author = msgObj.messageOwner.fwd_from.post_author; - } - if ((peer == myId || isChannel) && (msgObj.messageOwner.fwd_from.flags & 16) != 0 && !UserObject.isReplyUser(msgObj.getDialogId())) { - newMsg.fwd_from.flags |= 16; - newMsg.fwd_from.saved_from_peer = msgObj.messageOwner.fwd_from.saved_from_peer; - newMsg.fwd_from.saved_from_msg_id = msgObj.messageOwner.fwd_from.saved_from_msg_id; - } - newMsg.fwd_from.date = msgObj.messageOwner.fwd_from.date; - newMsg.flags = TLRPC.MESSAGE_FLAG_FWD; - } else if (!forwardFromSaved) { //if (!toMyself || !msgObj.isOutOwner()) - int fromId = msgObj.getFromChatId(); - newMsg.fwd_from = new TLRPC.TL_messageFwdHeader(); - newMsg.fwd_from.channel_post = msgObj.getId(); - newMsg.fwd_from.flags |= 4; - if (msgObj.isFromUser()) { - newMsg.fwd_from.from_id = msgObj.messageOwner.from_id; - newMsg.fwd_from.flags |= 1; - } else { - newMsg.fwd_from.from_id = new TLRPC.TL_peerChannel(); - newMsg.fwd_from.from_id.channel_id = msgObj.messageOwner.peer_id.channel_id; - newMsg.fwd_from.flags |= 1; - if (msgObj.messageOwner.post && fromId > 0) { - newMsg.fwd_from.from_id = msgObj.messageOwner.from_id != null ? msgObj.messageOwner.from_id : msgObj.messageOwner.peer_id; + if (!forwardFromMyName) { + boolean forwardFromSaved = msgObj.getDialogId() == myId && msgObj.isFromUser() && msgObj.messageOwner.from_id.user_id == myId; + if (msgObj.isForwarded()) { + newMsg.fwd_from = new TLRPC.TL_messageFwdHeader(); + if ((msgObj.messageOwner.fwd_from.flags & 1) != 0) { + newMsg.fwd_from.flags |= 1; + newMsg.fwd_from.from_id = msgObj.messageOwner.fwd_from.from_id; } - } - if (msgObj.messageOwner.post_author != null) { + if ((msgObj.messageOwner.fwd_from.flags & 32) != 0) { + newMsg.fwd_from.flags |= 32; + newMsg.fwd_from.from_name = msgObj.messageOwner.fwd_from.from_name; + } + if ((msgObj.messageOwner.fwd_from.flags & 4) != 0) { + newMsg.fwd_from.flags |= 4; + newMsg.fwd_from.channel_post = msgObj.messageOwner.fwd_from.channel_post; + } + if ((msgObj.messageOwner.fwd_from.flags & 8) != 0) { + newMsg.fwd_from.flags |= 8; + newMsg.fwd_from.post_author = msgObj.messageOwner.fwd_from.post_author; + } + if ((peer == myId || isChannel) && (msgObj.messageOwner.fwd_from.flags & 16) != 0 && !UserObject.isReplyUser(msgObj.getDialogId())) { + newMsg.fwd_from.flags |= 16; + newMsg.fwd_from.saved_from_peer = msgObj.messageOwner.fwd_from.saved_from_peer; + newMsg.fwd_from.saved_from_msg_id = msgObj.messageOwner.fwd_from.saved_from_msg_id; + } + newMsg.fwd_from.date = msgObj.messageOwner.fwd_from.date; + newMsg.flags = TLRPC.MESSAGE_FLAG_FWD; + } else if (!forwardFromSaved) { //if (!toMyself || !msgObj.isOutOwner()) + int fromId = msgObj.getFromChatId(); + newMsg.fwd_from = new TLRPC.TL_messageFwdHeader(); + newMsg.fwd_from.channel_post = msgObj.getId(); + newMsg.fwd_from.flags |= 4; + if (msgObj.isFromUser()) { + newMsg.fwd_from.from_id = msgObj.messageOwner.from_id; + newMsg.fwd_from.flags |= 1; + } else { + newMsg.fwd_from.from_id = new TLRPC.TL_peerChannel(); + newMsg.fwd_from.from_id.channel_id = msgObj.messageOwner.peer_id.channel_id; + newMsg.fwd_from.flags |= 1; + if (msgObj.messageOwner.post && fromId > 0) { + newMsg.fwd_from.from_id = msgObj.messageOwner.from_id != null ? msgObj.messageOwner.from_id : msgObj.messageOwner.peer_id; + } + } + if (msgObj.messageOwner.post_author != null) { /*newMsg.fwd_from.post_author = msgObj.messageOwner.post_author; newMsg.fwd_from.flags |= 8;*/ - } else if (!msgObj.isOutOwner() && fromId > 0 && msgObj.messageOwner.post) { - TLRPC.User signUser = getMessagesController().getUser(fromId); - if (signUser != null) { - newMsg.fwd_from.post_author = ContactsController.formatName(signUser.first_name, signUser.last_name); - newMsg.fwd_from.flags |= 8; + } else if (!msgObj.isOutOwner() && fromId > 0 && msgObj.messageOwner.post) { + TLRPC.User signUser = getMessagesController().getUser(fromId); + if (signUser != null) { + newMsg.fwd_from.post_author = ContactsController.formatName(signUser.first_name, signUser.last_name); + newMsg.fwd_from.flags |= 8; + } + } + newMsg.date = msgObj.messageOwner.date; + newMsg.flags = TLRPC.MESSAGE_FLAG_FWD; + } + if (peer == myId && newMsg.fwd_from != null) { + newMsg.fwd_from.flags |= 16; + newMsg.fwd_from.saved_from_msg_id = msgObj.getId(); + newMsg.fwd_from.saved_from_peer = msgObj.messageOwner.peer_id; + if (newMsg.fwd_from.saved_from_peer.user_id == myId) { + newMsg.fwd_from.saved_from_peer.user_id = (int) msgObj.getDialogId(); } } - newMsg.date = msgObj.messageOwner.date; - newMsg.flags = TLRPC.MESSAGE_FLAG_FWD; - } - if (peer == myId && newMsg.fwd_from != null) { - newMsg.fwd_from.flags |= 16; - newMsg.fwd_from.saved_from_msg_id = msgObj.getId(); - newMsg.fwd_from.saved_from_peer = msgObj.messageOwner.peer_id; - if (newMsg.fwd_from.saved_from_peer.user_id == myId) { - newMsg.fwd_from.saved_from_peer.user_id = (int) msgObj.getDialogId(); - } + } else { + newMsg.params = new HashMap<>(); + newMsg.params.put("fwd_id", "" + msgObj.getId()); + newMsg.params.put("fwd_peer", "" + msgObj.getDialogId()); } if (!msgObj.messageOwner.restriction_reason.isEmpty()) { newMsg.restriction_reason = msgObj.messageOwner.restriction_reason; @@ -1803,7 +1809,9 @@ public class SendMessagesHelper extends BaseController implements NotificationCe newMsg.flags |= 8388608; } - newMsg.message = msgObj.messageOwner.message; + if (!hideCaption || newMsg.media == null) { + newMsg.message = msgObj.messageOwner.message; + } if (newMsg.message == null) { newMsg.message = ""; } @@ -1960,6 +1968,8 @@ public class SendMessagesHelper extends BaseController implements NotificationCe } req.random_id = randomIds; req.id = ids; + req.drop_author = forwardFromMyName; + req.drop_media_captions = hideCaption; req.with_my_score = messages.size() == 1 && messages.get(0).messageOwner.with_my_score; final ArrayList newMsgObjArr = arr; @@ -3135,7 +3145,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe if (parentObject == null && params != null && params.containsKey("parentObject")) { parentObject = params.get("parentObject"); } - if (retryMessageObject.isForwarded()) { + if (retryMessageObject.isForwarded() || params != null && params.containsKey("fwd_id")) { type = 4; } else { if (retryMessageObject.isDice()) { @@ -4331,15 +4341,34 @@ public class SendMessagesHelper extends BaseController implements NotificationCe TLRPC.TL_messages_forwardMessages reqSend = new TLRPC.TL_messages_forwardMessages(); reqSend.to_peer = sendToPeer; reqSend.with_my_score = retryMessageObject.messageOwner.with_my_score; - if (retryMessageObject.messageOwner.ttl != 0) { - TLRPC.Chat chat = getMessagesController().getChat(-retryMessageObject.messageOwner.ttl); - reqSend.from_peer = new TLRPC.TL_inputPeerChannel(); - reqSend.from_peer.channel_id = -retryMessageObject.messageOwner.ttl; - if (chat != null) { - reqSend.from_peer.access_hash = chat.access_hash; + if (params != null && params.containsKey("fwd_id")) { + int fwdId = Utilities.parseInt(params.get("fwd_id")); + reqSend.drop_author = true; + long peerId = Utilities.parseLong(params.get("fwd_peer")); + if (peerId < 0) { + TLRPC.Chat chat = getMessagesController().getChat((int) -peerId); + if (ChatObject.isChannel(chat)) { + reqSend.from_peer = new TLRPC.TL_inputPeerChannel(); + reqSend.from_peer.channel_id = chat.id; + reqSend.from_peer.access_hash = chat.access_hash; + } else { + reqSend.from_peer = new TLRPC.TL_inputPeerEmpty(); + } + } else { + reqSend.from_peer = new TLRPC.TL_inputPeerEmpty(); } + reqSend.id.add(fwdId); } else { - reqSend.from_peer = new TLRPC.TL_inputPeerEmpty(); + if (retryMessageObject.messageOwner.ttl != 0) { + TLRPC.Chat chat = getMessagesController().getChat(-retryMessageObject.messageOwner.ttl); + reqSend.from_peer = new TLRPC.TL_inputPeerChannel(); + reqSend.from_peer.channel_id = -retryMessageObject.messageOwner.ttl; + if (chat != null) { + reqSend.from_peer.access_hash = chat.access_hash; + } + } else { + reqSend.from_peer = new TLRPC.TL_inputPeerEmpty(); + } } reqSend.silent = newMsg.silent; if (scheduleDate != 0) { @@ -6921,7 +6950,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe return String.format(Locale.US, blur ? "%d_%d@%d_%d_b" : "%d_%d@%d_%d", photoSize.location.volume_id, photoSize.location.local_id, (int) (point.x / AndroidUtilities.density), (int) (point.y / AndroidUtilities.density)); } - private static boolean shouldSendWebPAsSticker(String path, Uri uri) { + public static boolean shouldSendWebPAsSticker(String path, Uri uri) { BitmapFactory.Options bmOptions = new BitmapFactory.Options(); bmOptions.inJustDecodeBounds = true; try { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java index bf8cec20b..48ec3daf9 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java @@ -38,6 +38,9 @@ public class SharedConfig { public static String pushString = ""; public static String pushStringStatus = ""; + public static long pushStringGetTimeStart; + public static long pushStringGetTimeEnd; + public static boolean pushStatSent; public static byte[] pushAuthKey; public static byte[] pushAuthKeyId; @@ -66,6 +69,7 @@ public class SharedConfig { public static int textSelectionHintShows; public static int scheduledOrNoSoundHintShows; public static int lockRecordAudioVideoHint; + public static boolean forwardingOptionsHintShown; public static boolean searchMessagesAsListUsed; public static boolean stickersReorderingHintUsed; public static boolean disableVoiceAudioEffects; @@ -189,6 +193,7 @@ public class SharedConfig { editor.putBoolean("useFingerprint", useFingerprint); editor.putBoolean("allowScreenCapture", allowScreenCapture); editor.putString("pushString2", pushString); + editor.putBoolean("pushStatSent", pushStatSent); editor.putString("pushAuthKey", pushAuthKey != null ? Base64.encodeToString(pushAuthKey, Base64.DEFAULT) : ""); editor.putInt("lastLocalId", lastLocalId); editor.putString("passportConfigJson", passportConfigJson); @@ -197,6 +202,7 @@ public class SharedConfig { editor.putBoolean("sortFilesByName", sortFilesByName); editor.putInt("textSelectionHintShows", textSelectionHintShows); editor.putInt("scheduledOrNoSoundHintShows", scheduledOrNoSoundHintShows); + editor.putBoolean("forwardingOptionsHintShown", forwardingOptionsHintShown); editor.putInt("lockRecordAudioVideoHint", lockRecordAudioVideoHint); editor.putString("storageCacheDir", !TextUtils.isEmpty(storageCacheDir) ? storageCacheDir : ""); @@ -252,6 +258,7 @@ public class SharedConfig { allowScreenCapture = preferences.getBoolean("allowScreenCapture", false); lastLocalId = preferences.getInt("lastLocalId", -210000); pushString = preferences.getString("pushString2", ""); + pushStatSent = preferences.getBoolean("pushStatSent", false); passportConfigJson = preferences.getString("passportConfigJson", ""); passportConfigHash = preferences.getInt("passportConfigHash", 0); storageCacheDir = preferences.getString("storageCacheDir", null); @@ -352,6 +359,7 @@ public class SharedConfig { stickersReorderingHintUsed = preferences.getBoolean("stickersReorderingHintUsed", false); textSelectionHintShows = preferences.getInt("textSelectionHintShows", 0); scheduledOrNoSoundHintShows = preferences.getInt("scheduledOrNoSoundHintShows", 0); + forwardingOptionsHintShown = preferences.getBoolean("forwardingOptionsHintShown", false); lockRecordAudioVideoHint = preferences.getInt("lockRecordAudioVideoHint", 0); disableVoiceAudioEffects = preferences.getBoolean("disableVoiceAudioEffects", false); noiseSupression = preferences.getBoolean("noiseSupression", false); @@ -513,6 +521,7 @@ public class SharedConfig { textSelectionHintShows = 0; scheduledOrNoSoundHintShows = 0; lockRecordAudioVideoHint = 0; + forwardingOptionsHintShown = false; saveConfig(); } @@ -561,6 +570,14 @@ public class SharedConfig { editor.commit(); } + public static void forwardingOptionsHintHintShowed() { + SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + SharedPreferences.Editor editor = preferences.edit(); + forwardingOptionsHintShown = true; + editor.putBoolean("forwardingOptionsHintShown", forwardingOptionsHintShown); + editor.commit(); + } + public static void removeScheduledOrNoSuoundHint() { SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SvgHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SvgHelper.java index d3f4718a0..a09986ad8 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SvgHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SvgHelper.java @@ -122,6 +122,8 @@ public class SvgHelper { private float colorAlpha; private float crossfadeAlpha = 1.0f; + private boolean aspectFill = true; + @Override public int getIntrinsicHeight() { return width; @@ -132,6 +134,16 @@ public class SvgHelper { return height; } + public void setAspectFill(boolean value) { + aspectFill = value; + } + + public void overrideWidthAndHeight(int w, int h) { + width = w; + height = h; + } + + @Override public void draw(Canvas canvas) { if (currentColorKey != null) { @@ -140,9 +152,12 @@ public class SvgHelper { Rect bounds = getBounds(); float scaleX = bounds.width() / (float) width; float scaleY = bounds.height() / (float) height; - float scale = Math.max(scaleX, scaleY); + float scale = aspectFill ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY); canvas.save(); canvas.translate(bounds.left, bounds.top); + if (!aspectFill) { + canvas.translate((bounds.width() - width * scale) / 2, (bounds.height() - height * scale) / 2); + } canvas.scale(scale, scale); for (int a = 0, N = commands.size(); a < N; a++) { Object object = commands.get(a); @@ -1719,7 +1734,7 @@ public class SvgHelper { int num = encoded[i] & 0xff; if (num >= 128 + 64) { int start = num - 128 - 64; - path.append("AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,".substring(start, start + 1)); + path.append("AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,".charAt(start)); } else { if (num >= 128) { path.append(','); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java index e3cc31a17..cce6fa3b2 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserConfig.java @@ -103,10 +103,6 @@ public class UserConfig extends BaseController { } public void saveConfig(boolean withFile) { - saveConfig(withFile, null); - } - - public void saveConfig(boolean withFile, File oldFile) { NotificationCenter.getInstance(currentAccount).doOnIdle(() -> { synchronized (sync) { try { @@ -184,9 +180,6 @@ public class UserConfig extends BaseController { } editor.commit(); - if (oldFile != null) { - oldFile.delete(); - } } catch (Exception e) { FileLog.e(e); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/NativeInstance.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/NativeInstance.java index 3b32b68e9..08dd74477 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/NativeInstance.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/NativeInstance.java @@ -47,7 +47,7 @@ public class NativeInstance { } public interface RequestBroadcastPartCallback { - void run(long timestamp, long duration); + void run(long timestamp, long duration, int videoChannel, int quality); } public static NativeInstance make(String version, Instance.Config config, String path, Instance.Endpoint[] endpoints, Instance.Proxy proxy, int networkType, Instance.EncryptionKey encryptionKey, VideoSink remoteSink, long videoCapturer, AudioLevelsCallback audioLevelsCallback) { @@ -152,12 +152,12 @@ public class NativeInstance { } } - private void onRequestBroadcastPart(long timestamp, long duration) { - requestBroadcastPartCallback.run(timestamp, duration); + private void onRequestBroadcastPart(long timestamp, long duration, int videoChannel, int quality) { + requestBroadcastPartCallback.run(timestamp, duration, videoChannel, quality); } - private void onCancelRequestBroadcastPart(long timestamp) { - cancelRequestBroadcastPartCallback.run(timestamp, 0); + private void onCancelRequestBroadcastPart(long timestamp, int videoChannel, int quality) { + cancelRequestBroadcastPartCallback.run(timestamp, 0, 0, 0); } public native void setJoinResponsePayload(String payload); @@ -222,6 +222,6 @@ public class NativeInstance { public native void switchCamera(boolean front); public native void setVideoState(int videoState); public native void onSignalingDataReceive(byte[] data); - public native void onStreamPartAvailable(long ts, ByteBuffer buffer, int size, long timestamp); + public native void onStreamPartAvailable(long ts, ByteBuffer buffer, int size, long timestamp, int videoChannel, int quality); public native boolean hasVideoCapturer(); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java index bd214afb7..5bcf94226 100755 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java @@ -187,8 +187,6 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa private boolean reconnectScreenCapture; - private int currentStreamRequestId; - private TLRPC.Chat chat; private boolean isVideoAvailable; @@ -306,7 +304,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa private int classGuid; - private long currentStreamRequestTimestamp; + private HashMap currentStreamRequestTimestamp = new HashMap<>(); public boolean micSwitching; private Runnable afterSoundRunnable = new Runnable() { @@ -1272,32 +1270,15 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa return result; } - public void requestFullScreen(TLRPC.TL_groupCallParticipant participant, boolean screencast) { - if (currentBackgroundSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] != null) { - currentBackgroundSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA].setBackground(null); - } - if (participant == null) { - currentBackgroundSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = null; - currentBackgroundEndpointId[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = null; - return; - } + public void requestFullScreen(TLRPC.TL_groupCallParticipant participant, boolean full, boolean screencast) { String endpointId = screencast ? participant.presentationEndpoint : participant.videoEndpoint; if (endpointId == null) { return; } - ProxyVideoSink sink = remoteSinks.get(endpointId); - if (sink == null) { - sink = addRemoteSink(participant, screencast, null, null); - } - if (sink != null) { - sink.setBackground(remoteSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA]); - //tgVoip[CAPTURE_DEVICE_CAMERA].setVideoEndpointQuality(endpointId, QUALITY_FULL); TODO - currentBackgroundSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = sink; - currentBackgroundEndpointId[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = endpointId; + if (full) { + tgVoip[CAPTURE_DEVICE_CAMERA].setVideoEndpointQuality(endpointId, QUALITY_FULL); } else { - //tgVoip[CAPTURE_DEVICE_CAMERA].setVideoEndpointQuality(endpointId, QUALITY_MEDIUM); TODO - currentBackgroundSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = null; - currentBackgroundEndpointId[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = null; + tgVoip[CAPTURE_DEVICE_CAMERA].setVideoEndpointQuality(endpointId, QUALITY_MEDIUM); } } @@ -2084,7 +2065,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa } broadcastUnknownParticipants(taskPtr, unknown); }); - }, (timestamp, duration) -> { + }, (timestamp, duration, videoChannel, quality) -> { if (type != CAPTURE_DEVICE_CAMERA) { return; } @@ -2096,15 +2077,21 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa if (duration == 500) { inputGroupCallStream.scale = 1; } + if (videoChannel != 0) { + inputGroupCallStream.flags |= 1; + inputGroupCallStream.video_channel = videoChannel; + inputGroupCallStream.video_quality = quality; + } req.location = inputGroupCallStream; - currentStreamRequestTimestamp = timestamp; - currentStreamRequestId = AccountInstance.getInstance(currentAccount).getConnectionsManager().sendRequest(req, (response, error, responseTime) -> { + String key = videoChannel == 0 ? ("" + timestamp) : (videoChannel + "_" + timestamp + "_" + quality); + int reqId = AccountInstance.getInstance(currentAccount).getConnectionsManager().sendRequest(req, (response, error, responseTime) -> { + AndroidUtilities.runOnUIThread(() -> currentStreamRequestTimestamp.remove(key)); if (tgVoip[type] == null) { return; } if (response != null) { TLRPC.TL_upload_file res = (TLRPC.TL_upload_file) response; - tgVoip[type].onStreamPartAvailable(timestamp, res.bytes.buffer, res.bytes.limit(), responseTime); + tgVoip[type].onStreamPartAvailable(timestamp, res.bytes.buffer, res.bytes.limit(), responseTime, videoChannel, quality); } else { if ("GROUPCALL_JOIN_MISSING".equals(error.text)) { AndroidUtilities.runOnUIThread(() -> createGroupInstance(type, false)); @@ -2115,18 +2102,23 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa } else { status = -1; } - tgVoip[type].onStreamPartAvailable(timestamp, null, status, responseTime); + tgVoip[type].onStreamPartAvailable(timestamp, null, status, responseTime, videoChannel, quality); } } }, ConnectionsManager.RequestFlagFailOnServerErrors, ConnectionsManager.ConnectionTypeDownload, groupCall.call.stream_dc_id); - }, (timestamp, duration) -> { + AndroidUtilities.runOnUIThread(() -> currentStreamRequestTimestamp.put(key, reqId)); + }, (timestamp, duration, videoChannel, quality) -> { if (type != CAPTURE_DEVICE_CAMERA) { return; } - if (currentStreamRequestTimestamp == timestamp) { - AccountInstance.getInstance(currentAccount).getConnectionsManager().cancelRequest(currentStreamRequestId, true); - currentStreamRequestId = 0; - } + AndroidUtilities.runOnUIThread(() -> { + String key = videoChannel == 0 ? ("" + timestamp) : (videoChannel + "_" + timestamp + "_" + quality); + Integer reqId = currentStreamRequestTimestamp.get(key); + if (reqId != null) { + AccountInstance.getInstance(currentAccount).getConnectionsManager().cancelRequest(reqId, true); + currentStreamRequestTimestamp.remove(key); + } + }); }); tgVoip[type].setOnStateUpdatedListener((state, inTransition) -> updateConnectionState(type, state, inTransition)); } @@ -2819,18 +2811,23 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa intent.putExtra("currentAccount", currentAccount); } Notification.Builder builder = new Notification.Builder(this) - .setContentTitle(groupCall != null ? LocaleController.getString("VoipVoiceChat", R.string.VoipVoiceChat) : LocaleController.getString("VoipOutgoingCall", R.string.VoipOutgoingCall)) .setContentText(name) .setContentIntent(PendingIntent.getActivity(this, 50, intent, 0)); if (groupCall != null) { + builder.setContentTitle(ChatObject.isChannelOrGiga(chat) ? LocaleController.getString("VoipLiveStream", R.string.VoipLiveStream) : LocaleController.getString("VoipVoiceChat", R.string.VoipVoiceChat)); builder.setSmallIcon(isMicMute() ? R.drawable.voicechat_muted : R.drawable.voicechat_active); } else { + builder.setContentTitle(LocaleController.getString("VoipOutgoingCall", R.string.VoipOutgoingCall)); builder.setSmallIcon(R.drawable.notification); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { Intent endIntent = new Intent(this, VoIPActionsReceiver.class); endIntent.setAction(getPackageName() + ".END_CALL"); - builder.addAction(R.drawable.ic_call_end_white_24dp, groupCall != null ? LocaleController.getString("VoipGroupLeaveAlertTitle", R.string.VoipGroupLeaveAlertTitle) : LocaleController.getString("VoipEndCall", R.string.VoipEndCall), PendingIntent.getBroadcast(this, 0, endIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + if (groupCall != null) { + builder.addAction(R.drawable.ic_call_end_white_24dp, ChatObject.isChannelOrGiga(chat) ? LocaleController.getString("VoipChannelLeaveAlertTitle", R.string.VoipChannelLeaveAlertTitle) : LocaleController.getString("VoipGroupLeaveAlertTitle", R.string.VoipGroupLeaveAlertTitle), PendingIntent.getBroadcast(this, 0, endIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + } else { + builder.addAction(R.drawable.ic_call_end_white_24dp, LocaleController.getString("VoipEndCall", R.string.VoipEndCall), PendingIntent.getBroadcast(this, 0, endIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + } builder.setPriority(Notification.PRIORITY_MAX); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { @@ -2954,8 +2951,10 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa if (tgVoip[CAPTURE_DEVICE_CAMERA].isGroup()) { NativeInstance instance = tgVoip[CAPTURE_DEVICE_CAMERA]; Utilities.globalQueue.postRunnable(instance::stopGroup); - AccountInstance.getInstance(currentAccount).getConnectionsManager().cancelRequest(currentStreamRequestId, true); - currentStreamRequestId = 0; + for (HashMap.Entry entry : currentStreamRequestTimestamp.entrySet()) { + AccountInstance.getInstance(currentAccount).getConnectionsManager().cancelRequest(entry.getValue(), true); + } + currentStreamRequestTimestamp.clear(); } else { Instance.FinalState state = tgVoip[CAPTURE_DEVICE_CAMERA].stop(); updateTrafficStats(tgVoip[CAPTURE_DEVICE_CAMERA], state.trafficStats); diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java b/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java index 558f7f9f2..be1960813 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java @@ -201,10 +201,7 @@ public class ConnectionsManager extends BaseController { systemVersion = "SDK Unknown"; } getUserConfig().loadConfig(); - String pushString = SharedConfig.pushString; - if (TextUtils.isEmpty(pushString) && !TextUtils.isEmpty(SharedConfig.pushStringStatus)) { - pushString = SharedConfig.pushStringStatus; - } + String pushString = getRegId(); String fingerprint = AndroidUtilities.getCertificateSHA256Fingerprint(); int timezoneOffset = (TimeZone.getDefault().getRawOffset() + TimeZone.getDefault().getDSTSavings()) / 1000; @@ -212,6 +209,17 @@ public class ConnectionsManager extends BaseController { init(BuildVars.BUILD_VERSION, TLRPC.LAYER, BuildVars.APP_ID, deviceModel, systemVersion, appVersion, langCode, systemLangCode, configPath, FileLog.getNetworkLogPath(), pushString, fingerprint, timezoneOffset, getUserConfig().getClientUserId(), enablePushConnection); } + private String getRegId() { + String pushString = SharedConfig.pushString; + if (TextUtils.isEmpty(pushString) && !TextUtils.isEmpty(SharedConfig.pushStringStatus)) { + pushString = SharedConfig.pushStringStatus; + } + if (TextUtils.isEmpty(pushString)) { + pushString = SharedConfig.pushStringStatus = "__FIREBASE_GENERATING_SINCE_" + getCurrentTime() + "__"; + } + return pushString; + } + public boolean isPushConnectionEnabled() { SharedPreferences preferences = MessagesController.getGlobalNotificationsSettings(); if (preferences.contains("pushConnection")) { @@ -402,6 +410,9 @@ public class ConnectionsManager extends BaseController { if (TextUtils.isEmpty(pushString) && !TextUtils.isEmpty(status)) { pushString = status; } + if (TextUtils.isEmpty(pushString)) { + pushString = SharedConfig.pushStringStatus = "__FIREBASE_GENERATING_SINCE_" + getInstance(0).getCurrentTime() + "__"; + } for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) { native_setRegId(a, pushString); } diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java index b0a82d04e..4f7021bcf 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java @@ -64,7 +64,7 @@ public class TLRPC { public static final int MESSAGE_FLAG_HAS_BOT_ID = 0x00000800; public static final int MESSAGE_FLAG_EDITED = 0x00008000; - public static final int LAYER = 131; + public static final int LAYER = 132; public static class TL_stats_megagroupStats extends TLObject { public static int constructor = 0xef7ff916; @@ -2004,6 +2004,7 @@ public class TLRPC { public boolean join_date_asc; public boolean schedule_start_subscribed; public boolean can_start_video; + public boolean record_video_active; public long id; public long access_hash; public int participants_count; @@ -2056,7 +2057,7 @@ public class TLRPC { public static class TL_groupCall extends GroupCall { public static int constructor = 0xd597650c; - + public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -2065,6 +2066,7 @@ public class TLRPC { join_date_asc = (flags & 64) != 0; schedule_start_subscribed = (flags & 256) != 0; can_start_video = (flags & 512) != 0; + record_video_active = (flags & 2048) != 0; id = stream.readInt64(exception); access_hash = stream.readInt64(exception); participants_count = stream.readInt32(exception); @@ -2094,6 +2096,7 @@ public class TLRPC { flags = join_date_asc ? (flags | 64) : (flags &~ 64); flags = schedule_start_subscribed ? (flags | 256) : (flags &~ 256); flags = can_start_video ? (flags | 512) : (flags &~ 512); + flags = record_video_active ? (flags | 2048) : (flags &~ 2048); stream.writeInt32(flags); stream.writeInt64(id); stream.writeInt64(access_hash); @@ -5592,6 +5595,9 @@ public class TLRPC { case 0x628cbc6f: result = new TL_sendMessageChooseContactAction(); break; + case 0xb05ac6b1: + result = new TL_sendMessageChooseStickerAction(); + break; case 0x88f27fbc: result = new TL_sendMessageRecordRoundAction(); break; @@ -5750,6 +5756,15 @@ public class TLRPC { } } + public static class TL_sendMessageChooseStickerAction extends SendMessageAction { + public static int constructor = 0xb05ac6b1; + + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_sendMessageRecordRoundAction extends SendMessageAction { public static int constructor = 0x88f27fbc; @@ -6315,13 +6330,13 @@ public class TLRPC { } public static class TL_themeSettings extends TLObject { - public static int constructor = 0x9c14984a; + public static int constructor = 0x8db4e76c; public int flags; + public boolean message_colors_animated; public BaseTheme base_theme; public int accent_color; - public int message_top_color; - public int message_bottom_color; + public ArrayList message_colors = new ArrayList<>(); public WallPaper wallpaper; public static TL_themeSettings TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { @@ -6339,13 +6354,21 @@ public class TLRPC { public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); + message_colors_animated = (flags & 4) != 0; base_theme = BaseTheme.TLdeserialize(stream, stream.readInt32(exception), exception); accent_color = stream.readInt32(exception); if ((flags & 1) != 0) { - message_top_color = stream.readInt32(exception); - } - if ((flags & 1) != 0) { - message_bottom_color = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + message_colors.add(stream.readInt32(exception)); + } } if ((flags & 2) != 0) { wallpaper = WallPaper.TLdeserialize(stream, stream.readInt32(exception), exception); @@ -6354,14 +6377,17 @@ public class TLRPC { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); + flags = message_colors_animated ? (flags | 4) : (flags &~ 4); stream.writeInt32(flags); base_theme.serializeToStream(stream); stream.writeInt32(accent_color); if ((flags & 1) != 0) { - stream.writeInt32(message_top_color); - } - if ((flags & 1) != 0) { - stream.writeInt32(message_bottom_color); + stream.writeInt32(0x1cb5c415); + int count = message_colors.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(message_colors.get(a)); + } } if ((flags & 2) != 0) { wallpaper.serializeToStream(stream); @@ -7887,6 +7913,40 @@ public class TLRPC { } } + public static class TL_chatTheme extends TLObject { + public static int constructor = 0xed0b5c33; + + public String emoticon; + public Theme theme; + public Theme dark_theme; + + public static TL_chatTheme TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_chatTheme.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_chatTheme", constructor)); + } else { + return null; + } + } + TL_chatTheme result = new TL_chatTheme(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + emoticon = stream.readString(exception); + theme = Theme.TLdeserialize(stream, stream.readInt32(exception), exception); + dark_theme = Theme.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(emoticon); + theme.serializeToStream(stream); + dark_theme.serializeToStream(stream); + } + } + public static abstract class messages_DhConfig extends TLObject { public byte[] random; public int g; @@ -8910,6 +8970,97 @@ public class TLRPC { } } + public static class TL_messages_sponsoredMessages extends TLObject { + public static int constructor = 0x65a4c7d5; + + public ArrayList messages = new ArrayList<>(); + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_messages_sponsoredMessages TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_messages_sponsoredMessages.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_messages_sponsoredMessages", constructor)); + } else { + return null; + } + } + TL_messages_sponsoredMessages result = new TL_messages_sponsoredMessages(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_sponsoredMessage object = TL_sponsoredMessage.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + messages.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + Chat object = Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + User object = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = messages.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + messages.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + public static class TL_messageViews extends TLObject { public static int constructor = 0x455b853d; @@ -9856,6 +10007,7 @@ public class TLRPC { public int ttl_period; public ArrayList pending_suggestions = new ArrayList<>(); public Peer groupcall_default_join_as; + public String theme_emoticon; public int inviterId; //custom public int invitesCount; //custom @@ -9917,9 +10069,15 @@ public class TLRPC { result = new TL_channelFull_layer124(); break; case 0x8a1e2983: - result = new TL_chatFull(); + result = new TL_chatFull_layer131(); break; case 0x548c3f93: + result = new TL_channelFull_layer131(); + break; + case 0x49a0a5d9: + result = new TL_chatFull(); + break; + case 0x2f532f3c: result = new TL_channelFull(); break; case 0x1b7c9db3: @@ -10656,7 +10814,7 @@ public class TLRPC { } } - public static class TL_chatFull extends ChatFull { + public static class TL_chatFull_layer131 extends TL_chatFull { public static int constructor = 0x8a1e2983; @@ -10749,7 +10907,7 @@ public class TLRPC { } } - public static class TL_channelFull extends ChatFull { + public static class TL_channelFull_layer131 extends TL_channelFull { public static int constructor = 0x548c3f93; @@ -10957,6 +11115,319 @@ public class TLRPC { } } + public static class TL_chatFull extends ChatFull { + public static int constructor = 0x49a0a5d9; + + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + can_set_username = (flags & 128) != 0; + has_scheduled = (flags & 256) != 0; + id = stream.readInt32(exception); + about = stream.readString(exception); + participants = ChatParticipants.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 4) != 0) { + chat_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 8192) != 0) { + exported_invite = (TLRPC.TL_chatInviteExported) ExportedChatInvite.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 8) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + BotInfo object = BotInfo.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + bot_info.add(object); + } + } + if ((flags & 64) != 0) { + pinned_msg_id = stream.readInt32(exception); + } + if ((flags & 2048) != 0) { + folder_id = stream.readInt32(exception); + } + if ((flags & 4096) != 0) { + call = TL_inputGroupCall.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 16384) != 0) { + ttl_period = stream.readInt32(exception); + } + if ((flags & 32768) != 0) { + groupcall_default_join_as = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 65536) != 0) { + theme_emoticon = stream.readString(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = can_set_username ? (flags | 128) : (flags &~ 128); + flags = has_scheduled ? (flags | 256) : (flags &~ 256); + stream.writeInt32(flags); + stream.writeInt32(id); + stream.writeString(about); + participants.serializeToStream(stream); + if ((flags & 4) != 0) { + chat_photo.serializeToStream(stream); + } + notify_settings.serializeToStream(stream); + if ((flags & 8192) != 0) { + exported_invite.serializeToStream(stream); + } + if ((flags & 8) != 0) { + stream.writeInt32(0x1cb5c415); + int count = bot_info.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + bot_info.get(a).serializeToStream(stream); + } + } + if ((flags & 64) != 0) { + stream.writeInt32(pinned_msg_id); + } + if ((flags & 2048) != 0) { + stream.writeInt32(folder_id); + } + if ((flags & 4096) != 0) { + call.serializeToStream(stream); + } + if ((flags & 16384) != 0) { + stream.writeInt32(ttl_period); + } + if ((flags & 32768) != 0) { + groupcall_default_join_as.serializeToStream(stream); + } + if ((flags & 65536) != 0) { + stream.writeString(theme_emoticon); + } + } + } + + public static class TL_channelFull extends ChatFull { + public static int constructor = 0x2f532f3c; + + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + can_view_participants = (flags & 8) != 0; + can_set_username = (flags & 64) != 0; + can_set_stickers = (flags & 128) != 0; + hidden_prehistory = (flags & 1024) != 0; + can_set_location = (flags & 65536) != 0; + has_scheduled = (flags & 524288) != 0; + can_view_stats = (flags & 1048576) != 0; + blocked = (flags & 4194304) != 0; + id = stream.readInt32(exception); + about = stream.readString(exception); + if ((flags & 1) != 0) { + participants_count = stream.readInt32(exception); + } + if ((flags & 2) != 0) { + admins_count = stream.readInt32(exception); + } + if ((flags & 4) != 0) { + kicked_count = stream.readInt32(exception); + } + if ((flags & 4) != 0) { + banned_count = stream.readInt32(exception); + } + if ((flags & 8192) != 0) { + online_count = stream.readInt32(exception); + } + read_inbox_max_id = stream.readInt32(exception); + read_outbox_max_id = stream.readInt32(exception); + unread_count = stream.readInt32(exception); + chat_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 8388608) != 0) { + exported_invite = (TLRPC.TL_chatInviteExported) ExportedChatInvite.TLdeserialize(stream, stream.readInt32(exception), exception); + } + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + BotInfo object = BotInfo.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + bot_info.add(object); + } + if ((flags & 16) != 0) { + migrated_from_chat_id = stream.readInt32(exception); + } + if ((flags & 16) != 0) { + migrated_from_max_id = stream.readInt32(exception); + } + if ((flags & 32) != 0) { + pinned_msg_id = stream.readInt32(exception); + } + if ((flags & 256) != 0) { + stickerset = StickerSet.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 512) != 0) { + available_min_id = stream.readInt32(exception); + } + if ((flags & 2048) != 0) { + folder_id = stream.readInt32(exception); + } + if ((flags & 16384) != 0) { + linked_chat_id = stream.readInt32(exception); + } + if ((flags & 32768) != 0) { + location = ChannelLocation.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 131072) != 0) { + slowmode_seconds = stream.readInt32(exception); + } + if ((flags & 262144) != 0) { + slowmode_next_send_date = stream.readInt32(exception); + } + if ((flags & 4096) != 0) { + stats_dc = stream.readInt32(exception); + } + pts = stream.readInt32(exception); + if ((flags & 2097152) != 0) { + call = TL_inputGroupCall.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 16777216) != 0) { + ttl_period = stream.readInt32(exception); + } + if ((flags & 33554432) != 0) { + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + pending_suggestions.add(stream.readString(exception)); + } + } + if ((flags & 67108864) != 0) { + groupcall_default_join_as = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 134217728) != 0) { + theme_emoticon = stream.readString(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = can_view_participants ? (flags | 8) : (flags &~ 8); + flags = can_set_username ? (flags | 64) : (flags &~ 64); + flags = can_set_stickers ? (flags | 128) : (flags &~ 128); + flags = hidden_prehistory ? (flags | 1024) : (flags &~ 1024); + flags = can_set_location ? (flags | 65536) : (flags &~ 65536); + flags = has_scheduled ? (flags | 524288) : (flags &~ 524288); + flags = can_view_stats ? (flags | 1048576) : (flags &~ 1048576); + flags = blocked ? (flags | 4194304) : (flags &~ 4194304); + stream.writeInt32(flags); + stream.writeInt32(id); + stream.writeString(about); + if ((flags & 1) != 0) { + stream.writeInt32(participants_count); + } + if ((flags & 2) != 0) { + stream.writeInt32(admins_count); + } + if ((flags & 4) != 0) { + stream.writeInt32(kicked_count); + } + if ((flags & 4) != 0) { + stream.writeInt32(banned_count); + } + if ((flags & 8192) != 0) { + stream.writeInt32(online_count); + } + stream.writeInt32(read_inbox_max_id); + stream.writeInt32(read_outbox_max_id); + stream.writeInt32(unread_count); + chat_photo.serializeToStream(stream); + notify_settings.serializeToStream(stream); + if ((flags & 8388608) != 0) { + exported_invite.serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + int count = bot_info.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + bot_info.get(a).serializeToStream(stream); + } + if ((flags & 16) != 0) { + stream.writeInt32(migrated_from_chat_id); + } + if ((flags & 16) != 0) { + stream.writeInt32(migrated_from_max_id); + } + if ((flags & 32) != 0) { + stream.writeInt32(pinned_msg_id); + } + if ((flags & 256) != 0) { + stickerset.serializeToStream(stream); + } + if ((flags & 512) != 0) { + stream.writeInt32(available_min_id); + } + if ((flags & 2048) != 0) { + stream.writeInt32(folder_id); + } + if ((flags & 16384) != 0) { + stream.writeInt32(linked_chat_id); + } + if ((flags & 32768) != 0) { + location.serializeToStream(stream); + } + if ((flags & 131072) != 0) { + stream.writeInt32(slowmode_seconds); + } + if ((flags & 262144) != 0) { + stream.writeInt32(slowmode_next_send_date); + } + if ((flags & 4096) != 0) { + stream.writeInt32(stats_dc); + } + stream.writeInt32(pts); + if ((flags & 2097152) != 0) { + call.serializeToStream(stream); + } + if ((flags & 16777216) != 0) { + stream.writeInt32(ttl_period); + } + if ((flags & 33554432) != 0) { + stream.writeInt32(0x1cb5c415); + count = pending_suggestions.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeString(pending_suggestions.get(a)); + } + } + if ((flags & 67108864) != 0) { + groupcall_default_join_as.serializeToStream(stream); + } + if ((flags & 134217728) != 0) { + stream.writeString(theme_emoticon); + } + } + } + public static class TL_channelFull_layer122 extends TL_channelFull { public static int constructor = 0xef3a6acd; @@ -14821,13 +15292,14 @@ public class TLRPC { } public static class TL_messages_discussionMessage extends TLObject { - public static int constructor = 0xf5dd8f9d; + public static int constructor = 0xa6341782; public int flags; public ArrayList messages = new ArrayList<>(); public int max_id; public int read_inbox_max_id; public int read_outbox_max_id; + public int unread_count; public ArrayList chats = new ArrayList<>(); public ArrayList users = new ArrayList<>(); @@ -14870,6 +15342,7 @@ public class TLRPC { if ((flags & 4) != 0) { read_outbox_max_id = stream.readInt32(exception); } + unread_count = stream.readInt32(exception); magic = stream.readInt32(exception); if (magic != 0x1cb5c415) { if (exception) { @@ -14920,6 +15393,7 @@ public class TLRPC { if ((flags & 4) != 0) { stream.writeInt32(read_outbox_max_id); } + stream.writeInt32(unread_count); stream.writeInt32(0x1cb5c415); count = chats.size(); stream.writeInt32(count); @@ -16383,6 +16857,74 @@ public class TLRPC { } } + public static abstract class account_ChatThemes extends TLObject { + + public static account_ChatThemes TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + account_ChatThemes result = null; + switch (constructor) { + case 0xe011e1c4: + result = new TL_account_chatThemesNotModified(); + break; + case 0xfe4cbebd: + result = new TL_account_chatThemes(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in account_ChatThemes", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_account_chatThemesNotModified extends account_ChatThemes { + public static int constructor = 0xe011e1c4; + + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_account_chatThemes extends account_ChatThemes { + public static int constructor = 0xfe4cbebd; + + public int hash; + public ArrayList themes = new ArrayList<>(); + + public void readParams(AbstractSerializedData stream, boolean exception) { + hash = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_chatTheme object = TL_chatTheme.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + themes.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(hash); + stream.writeInt32(0x1cb5c415); + int count = themes.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + themes.get(a).serializeToStream(stream); + } + } + } + public static class TL_auth_passwordRecovery extends TLObject { public static int constructor = 0x137948a5; @@ -16541,7 +17083,7 @@ public class TLRPC { case 0x9d84f3db: result = new TL_inputStickerSetThumb(); break; - case 0xbba51639: + case 0x598a92a: result = new TL_inputGroupCallStream(); break; case 0xdfdaabe1: @@ -16659,23 +17201,40 @@ public class TLRPC { } public static class TL_inputGroupCallStream extends InputFileLocation { - public static int constructor = 0xbba51639; + public static int constructor = 0x598a92a; + public int flags; public TL_inputGroupCall call; public long time_ms; public int scale; + public int video_channel; + public int video_quality; public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); call = TL_inputGroupCall.TLdeserialize(stream, stream.readInt32(exception), exception); time_ms = stream.readInt64(exception); scale = stream.readInt32(exception); + if ((flags & 1) != 0) { + video_channel = stream.readInt32(exception); + } + if ((flags & 1) != 0) { + video_quality = stream.readInt32(exception); + } } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); + stream.writeInt32(flags); call.serializeToStream(stream); stream.writeInt64(time_ms); stream.writeInt32(scale); + if ((flags & 1) != 0) { + stream.writeInt32(video_channel); + } + if ((flags & 1) != 0) { + stream.writeInt32(video_quality); + } } } @@ -18657,6 +19216,9 @@ public class TLRPC { case 0x7a0d7f42: result = new TL_messageActionGroupCall(); break; + case 0xaa786345: + result = new TL_messageActionSetChatTheme(); + break; case 0x4792929b: result = new TL_messageActionScreenshotTaken(); break; @@ -18846,6 +19408,21 @@ public class TLRPC { } } + public static class TL_messageActionSetChatTheme extends MessageAction { + public static int constructor = 0xaa786345; + + public String emoticon; + + public void readParams(AbstractSerializedData stream, boolean exception) { + emoticon = stream.readString(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(emoticon); + } + } + public static class TL_messageActionChatMigrateTo extends MessageAction { public static int constructor = 0x51bdb021; @@ -29978,7 +30555,7 @@ public class TLRPC { Theme result = null; switch (constructor) { case 0x28f1114: - result = new TL_theme(); + result = new TL_theme_layer131(); break; case 0xf7d90ce0: result = new TL_theme_layer106(); @@ -29986,6 +30563,9 @@ public class TLRPC { case 0x483d270c: result = new TL_themeDocumentNotModified_layer106(); break; + case 0xe802b8dc: + result = new TL_theme(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in Theme", constructor)); @@ -29998,11 +30578,12 @@ public class TLRPC { } public static class TL_theme extends Theme { - public static int constructor = 0x28f1114; + public static int constructor = 0xe802b8dc; public int flags; public boolean creator; public boolean isDefault; + public boolean for_chat; public long id; public long access_hash; public String slug; @@ -30011,6 +30592,51 @@ public class TLRPC { public TL_themeSettings settings; public int installs_count; + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + creator = (flags & 1) != 0; + isDefault = (flags & 2) != 0; + for_chat = (flags & 32) != 0; + id = stream.readInt64(exception); + access_hash = stream.readInt64(exception); + slug = stream.readString(exception); + title = stream.readString(exception); + if ((flags & 4) != 0) { + document = Document.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 8) != 0) { + settings = TL_themeSettings.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 16) != 0) { + installs_count = stream.readInt32(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = creator ? (flags | 1) : (flags &~ 1); + flags = isDefault ? (flags | 2) : (flags &~ 2); + flags = for_chat ? (flags | 32) : (flags &~ 32); + stream.writeInt32(flags); + stream.writeInt64(id); + stream.writeInt64(access_hash); + stream.writeString(slug); + stream.writeString(title); + if ((flags & 4) != 0) { + document.serializeToStream(stream); + } + if ((flags & 8) != 0) { + settings.serializeToStream(stream); + } + if ((flags & 16) != 0) { + stream.writeInt32(installs_count); + } + } + } + + public static class TL_theme_layer131 extends TL_theme { + public static int constructor = 0x28f1114; + public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); @@ -30371,6 +30997,76 @@ public class TLRPC { } } + public static class TL_sponsoredMessage extends TLObject { + public static int constructor = 0x2a3c381f; + + public int flags; + public byte[] random_id; + public Peer from_id; + public String start_param; + public String message; + public ArrayList entities = new ArrayList<>(); + + public static TL_sponsoredMessage TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_sponsoredMessage.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_sponsoredMessage", constructor)); + } else { + return null; + } + } + TL_sponsoredMessage result = new TL_sponsoredMessage(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + random_id = stream.readByteArray(exception); + from_id = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 1) != 0) { + start_param = stream.readString(exception); + } + message = stream.readString(exception); + if ((flags & 2) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + MessageEntity object = MessageEntity.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + entities.add(object); + } + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeByteArray(random_id); + from_id.serializeToStream(stream); + if ((flags & 1) != 0) { + stream.writeString(start_param); + } + stream.writeString(message); + if ((flags & 2) != 0) { + stream.writeInt32(0x1cb5c415); + int count = entities.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + entities.get(a).serializeToStream(stream); + } + } + } + } + public static class TL_account_authorizationForm extends TLObject { public static int constructor = 0xad2e1cd8; @@ -33030,6 +33726,9 @@ public class TLRPC { case 0x2df5fc0a: result = new TL_channelAdminLogEventActionDefaultBannedRights(); break; + case 0xfe69018d: + result = new TL_channelAdminLogEventActionChangeTheme(); + break; case 0xf89777f2: result = new TL_channelAdminLogEventActionParticipantLeave(); break; @@ -33191,6 +33890,24 @@ public class TLRPC { } } + public static class TL_channelAdminLogEventActionChangeTheme extends ChannelAdminLogEventAction { + public static int constructor = 0xfe69018d; + + public String prev_value; + public String new_value; + + public void readParams(AbstractSerializedData stream, boolean exception) { + prev_value = stream.readString(exception); + new_value = stream.readString(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(prev_value); + stream.writeString(new_value); + } + } + public static class TL_channelAdminLogEventActionParticipantLeave extends ChannelAdminLogEventAction { public static int constructor = 0xf89777f2; @@ -37333,13 +38050,17 @@ public class TLRPC { public int folder_id; public int ttl_period; public TL_peerSettings settings; + public String theme_emoticon; public static UserFull TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { UserFull result = null; switch (constructor) { - case 0x139a9a77: + case 0xd697ff05: result = new TL_userFull(); break; + case 0x139a9a77: + result = new TL_userFull_layer131(); + break; case 0x745559cc: result = new TL_userFull_layer101(); break; @@ -37360,6 +38081,82 @@ public class TLRPC { } } + public static class TL_userFull extends UserFull { + public static int constructor = 0xd697ff05; + + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + blocked = (flags & 1) != 0; + phone_calls_available = (flags & 16) != 0; + phone_calls_private = (flags & 32) != 0; + can_pin_message = (flags & 128) != 0; + has_scheduled = (flags & 4096) != 0; + video_calls_available = (flags & 8192) != 0; + user = User.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 2) != 0) { + about = stream.readString(exception); + } + settings = TL_peerSettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 4) != 0) { + profile_photo = Photo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + notify_settings = PeerNotifySettings.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 8) != 0) { + bot_info = BotInfo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 64) != 0) { + pinned_msg_id = stream.readInt32(exception); + } + common_chats_count = stream.readInt32(exception); + if ((flags & 2048) != 0) { + folder_id = stream.readInt32(exception); + } + if ((flags & 16384) != 0) { + ttl_period = stream.readInt32(exception); + } + if ((flags & 32768) != 0) { + theme_emoticon = stream.readString(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = blocked ? (flags | 1) : (flags &~ 1); + flags = phone_calls_available ? (flags | 16) : (flags &~ 16); + flags = phone_calls_private ? (flags | 32) : (flags &~ 32); + flags = can_pin_message ? (flags | 128) : (flags &~ 128); + flags = has_scheduled ? (flags | 4096) : (flags &~ 4096); + flags = video_calls_available ? (flags | 8192) : (flags &~ 8192); + stream.writeInt32(flags); + user.serializeToStream(stream); + if ((flags & 2) != 0) { + stream.writeString(about); + } + settings.serializeToStream(stream); + if ((flags & 4) != 0) { + profile_photo.serializeToStream(stream); + } + notify_settings.serializeToStream(stream); + if ((flags & 8) != 0) { + bot_info.serializeToStream(stream); + } + if ((flags & 64) != 0) { + stream.writeInt32(pinned_msg_id); + } + stream.writeInt32(common_chats_count); + if ((flags & 2048) != 0) { + stream.writeInt32(folder_id); + } + if ((flags & 16384) != 0) { + stream.writeInt32(ttl_period); + } + if ((flags & 32768) != 0) { + stream.writeString(theme_emoticon); + } + } + } + public static class TL_userFull_layer101 extends TL_userFull { public static int constructor = 0x745559cc; @@ -37474,7 +38271,7 @@ public class TLRPC { } } - public static class TL_userFull extends UserFull { + public static class TL_userFull_layer131 extends TL_userFull { public static int constructor = 0x139a9a77; @@ -40386,6 +41183,21 @@ public class TLRPC { } } + public static class TL_account_getChatThemes extends TLObject { + public static int constructor = 0xd6d71d7b; + + public int hash; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return account_ChatThemes.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(hash); + } + } + public static class TL_users_getFullUser extends TLObject { public static int constructor = 0xca30a5b1; @@ -41380,6 +42192,8 @@ public class TLRPC { public boolean silent; public boolean background; public boolean with_my_score; + public boolean drop_author; + public boolean drop_media_captions; public InputPeer from_peer; public ArrayList id = new ArrayList<>(); public ArrayList random_id = new ArrayList<>(); @@ -41395,6 +42209,8 @@ public class TLRPC { flags = silent ? (flags | 32) : (flags &~ 32); flags = background ? (flags | 64) : (flags &~ 64); flags = with_my_score ? (flags | 256) : (flags &~ 256); + flags = drop_author ? (flags | 2048) : (flags &~ 2048); + flags = drop_media_captions ? (flags | 4096) : (flags &~ 4096); stream.writeInt32(flags); from_peer.serializeToStream(stream); stream.writeInt32(0x1cb5c415); @@ -45226,6 +46042,23 @@ public class TLRPC { } } + public static class TL_messages_setChatTheme extends TLObject { + public static int constructor = 0xe63be13f; + + public InputPeer peer; + public String emoticon; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Updates.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeString(emoticon); + } + } + public static class TL_help_getAppChangelog extends TLObject { public static int constructor = 0x9010ef6f; @@ -46072,6 +46905,38 @@ public class TLRPC { } } + public static class TL_channels_viewSponsoredMessage extends TLObject { + public static int constructor = 0xbeaedb94; + + public InputChannel channel; + public byte[] random_id; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + channel.serializeToStream(stream); + stream.writeByteArray(random_id); + } + } + + public static class TL_channels_getSponsoredMessages extends TLObject { + public static int constructor = 0xec210fbf; + + public InputChannel channel; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_messages_sponsoredMessages.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + channel.serializeToStream(stream); + } + } + public static class TL_stickers_createStickerSet extends TLObject { public static int constructor = 0x9021ab67; @@ -46528,12 +47393,14 @@ public class TLRPC { } public static class TL_phone_toggleGroupCallRecord extends TLObject { - public static int constructor = 0xc02a66d7; + public static int constructor = 0xf128c708; public int flags; public boolean start; + public boolean video; public TL_inputGroupCall call; public String title; + public boolean video_portrait; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { return Updates.TLdeserialize(stream, constructor, exception); @@ -46542,11 +47409,15 @@ public class TLRPC { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = start ? (flags | 1) : (flags &~ 1); + flags = video ? (flags | 4) : (flags &~ 4); stream.writeInt32(flags); call.serializeToStream(stream); if ((flags & 2) != 0) { stream.writeString(title); } + if ((flags & 4) != 0) { + stream.writeBool(video_portrait); + } } } @@ -46852,13 +47723,13 @@ public class TLRPC { } public static class TL_inputThemeSettings extends TLObject { - public static int constructor = 0xbd507cd1; + public static int constructor = 0xff38f912; public int flags; + public boolean message_colors_animated; public BaseTheme base_theme; public int accent_color; - public int message_top_color; - public int message_bottom_color; + public ArrayList message_colors = new ArrayList<>(); public InputWallPaper wallpaper; public WallPaperSettings wallpaper_settings; @@ -46877,13 +47748,21 @@ public class TLRPC { public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); + message_colors_animated = (flags & 4) != 0; base_theme = BaseTheme.TLdeserialize(stream, stream.readInt32(exception), exception); accent_color = stream.readInt32(exception); if ((flags & 1) != 0) { - message_top_color = stream.readInt32(exception); - } - if ((flags & 1) != 0) { - message_bottom_color = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + message_colors.add(stream.readInt32(exception)); + } } if ((flags & 2) != 0) { wallpaper = InputWallPaper.TLdeserialize(stream, stream.readInt32(exception), exception); @@ -46895,14 +47774,17 @@ public class TLRPC { public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); + flags = message_colors_animated ? (flags | 4) : (flags &~ 4); stream.writeInt32(flags); base_theme.serializeToStream(stream); stream.writeInt32(accent_color); if ((flags & 1) != 0) { - stream.writeInt32(message_top_color); - } - if ((flags & 1) != 0) { - stream.writeInt32(message_bottom_color); + stream.writeInt32(0x1cb5c415); + int count = message_colors.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stream.writeInt32(message_colors.get(a)); + } } if ((flags & 2) != 0) { wallpaper.serializeToStream(stream); @@ -47950,22 +48832,22 @@ public class TLRPC { result = new TL_message_layer104_2(); break; case 0xa367e716: - result = new TL_messageForwarded_old2(); //custom + result = new TL_messageForwarded_old2(); break; case 0x5f46804: - result = new TL_messageForwarded_old(); //custom + result = new TL_messageForwarded_old(); break; case 0x567699b3: - result = new TL_message_old2(); //custom + result = new TL_message_old2(); break; case 0x9f8d60bb: - result = new TL_messageService_old(); //custom + result = new TL_messageService_old(); break; case 0x22eb6aba: - result = new TL_message_old(); //custom + result = new TL_message_old(); break; case 0x555555F8: - result = new TL_message_secret_old(); //custom + result = new TL_message_secret_old(); break; case 0x9789dac4: result = new TL_message_layer104_3(); @@ -47992,7 +48874,7 @@ public class TLRPC { result = new TL_messageService(); break; case 0xf07814c8: - result = new TL_message_old5(); //custom + result = new TL_message_old5(); break; } if (result == null && exception) { @@ -48845,7 +49727,7 @@ public class TLRPC { if ((flags & 33554432) != 0) { stream.writeInt32(ttl_period); } - writeAttachPath(stream); + writeAttachPath(stream); //custom } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java index 7b26e005c..4af6fec46 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java @@ -40,6 +40,8 @@ import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; +import com.google.android.exoplayer2.util.Log; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.FileLog; @@ -207,6 +209,12 @@ public class ActionBarLayout extends FrameLayout { this.fragmentPanTranslationOffset = fragmentPanTranslationOffset; invalidate(); } + + @Override + public void setTranslationX(float translationX) { + Log.d("kek", "set translationX" + translationX); + super.setTranslationX(translationX); + } } private static Drawable headerShadowDrawable; @@ -982,7 +990,7 @@ public class ActionBarLayout extends FrameLayout { return false; } fragment.setInPreviewMode(preview); - if (parentActivity.getCurrentFocus() != null && fragment.hideKeyboardOnShow()) { + if (parentActivity.getCurrentFocus() != null && fragment.hideKeyboardOnShow() && !preview) { AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus()); } boolean needAnimation = preview || !forceWithoutAnimation && MessagesController.getGlobalMainSettings().getBoolean("view_animations", true); @@ -1158,7 +1166,7 @@ public class ActionBarLayout extends FrameLayout { containerView.setScaleY(1.0f); } if (containerView.isKeyboardVisible || containerViewBack.isKeyboardVisible) { - if (currentFragment != null) { + if (currentFragment != null && !preview) { currentFragment.saveKeyboardPositionBeforeTransition(); } waitingForKeyboardCloseRunnable = new Runnable() { @@ -1218,7 +1226,7 @@ public class ActionBarLayout extends FrameLayout { startLayoutAnimation(true, true, preview); } } else { - if (containerView.isKeyboardVisible || containerViewBack.isKeyboardVisible && currentFragment != null) { + if (!preview && containerView.isKeyboardVisible || containerViewBack.isKeyboardVisible && currentFragment != null) { currentFragment.saveKeyboardPositionBeforeTransition(); } currentAnimation = animation; @@ -1443,7 +1451,7 @@ public class ActionBarLayout extends FrameLayout { animation = currentFragment.onCustomTransitionAnimation(false, () -> onAnimationEndCheck(false)); } if (animation == null) { - if (containerView.isKeyboardVisible || containerViewBack.isKeyboardVisible) { + if (!inPreviewMode && (containerView.isKeyboardVisible || containerViewBack.isKeyboardVisible)) { waitingForKeyboardCloseRunnable = new Runnable() { @Override public void run() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java index b1ec3bef3..1eef0fe36 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuSubItem.java @@ -107,6 +107,12 @@ public class ActionBarMenuSubItem extends FrameLayout { setTextAndIcon(text, icon, null); } + public void setMultiline() { + textView.setLines(2); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + textView.setSingleLine(false); + } + public void setTextAndIcon(CharSequence text, int icon, Drawable iconDrawable) { textView.setText(text); if (icon != 0 || iconDrawable != null || checkView != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java index 8f14e4287..571feb664 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarPopupWindow.java @@ -92,8 +92,8 @@ public class ActionBarPopupWindow extends PopupWindow { private boolean animationEnabled = allowAnimation; private ArrayList itemAnimators; private HashMap positions = new HashMap<>(); - private int gapStartY = Integer.MIN_VALUE; - private int gapEndY = Integer.MIN_VALUE; + private int gapStartY = -1000000; + private int gapEndY = -1000000; private Rect bgPaddings = new Rect(); private ScrollView scrollView; @@ -105,9 +105,13 @@ public class ActionBarPopupWindow extends PopupWindow { private boolean fitItems; public ActionBarPopupWindowLayout(Context context) { + this(context, R.drawable.popup_fixed_alert2); + } + + public ActionBarPopupWindowLayout(Context context, int resId) { super(context); - backgroundDrawable = getResources().getDrawable(R.drawable.popup_fixed_alert2).mutate(); + backgroundDrawable = getResources().getDrawable(resId).mutate(); if (backgroundDrawable != null) { backgroundDrawable.getPadding(bgPaddings); } @@ -130,8 +134,8 @@ public class ActionBarPopupWindow extends PopupWindow { if (fitItems) { int maxWidth = 0; int fixWidth = 0; - gapStartY = Integer.MIN_VALUE; - gapEndY = Integer.MIN_VALUE; + gapStartY = -1000000; + gapEndY = -1000000; ArrayList viewsToFix = null; for (int a = 0, N = getChildCount(); a < N; a++) { View view = getChildAt(a); @@ -326,7 +330,7 @@ public class ActionBarPopupWindow extends PopupWindow { if (a == 1 && start < -AndroidUtilities.dp(16)) { break; } - if (gapStartY != Integer.MIN_VALUE) { + if (gapStartY != -1000000) { canvas.save(); canvas.clipRect(0, bgPaddings.top, getMeasuredWidth(), getMeasuredHeight()); } @@ -338,10 +342,10 @@ public class ActionBarPopupWindow extends PopupWindow { if (start > -AndroidUtilities.dp(16)) { int h = (int) (getMeasuredHeight() * backScaleY); if (a == 0) { - backgroundDrawable.setBounds(0, -scrollView.getScrollY(), (int) (getMeasuredWidth() * backScaleX), Math.min(h, start + AndroidUtilities.dp(16))); + backgroundDrawable.setBounds(0, -scrollView.getScrollY() + (gapStartY != -1000000 ? AndroidUtilities.dp(1) : 0), (int) (getMeasuredWidth() * backScaleX), (gapStartY != -1000000 ? Math.min(h, start + AndroidUtilities.dp(16)) : h)); } else { if (h < end) { - if (gapStartY != Integer.MIN_VALUE) { + if (gapStartY != -1000000) { canvas.restore(); } continue; @@ -353,7 +357,7 @@ public class ActionBarPopupWindow extends PopupWindow { } } backgroundDrawable.draw(canvas); - if (gapStartY != Integer.MIN_VALUE) { + if (gapStartY != -1000000) { canvas.restore(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/SimpleTextView.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/SimpleTextView.java index ad3d277ab..1fb192bc0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/SimpleTextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/SimpleTextView.java @@ -28,6 +28,7 @@ import android.view.View; import android.view.accessibility.AccessibilityNodeInfo; import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.Cells.DialogCell; import org.telegram.ui.Components.EmptyStubSpan; import org.telegram.ui.Components.StaticLayoutEx; @@ -44,6 +45,10 @@ public class SimpleTextView extends View implements Drawable.Callback { private SpannableStringBuilder spannableStringBuilder; private Drawable leftDrawable; private Drawable rightDrawable; + private Drawable replacedDrawable; + private String replacedText; + private int replacingDrawableTextIndex; + private float replacingDrawableTextOffset; private float rightDrawableScale = 1.0f; private int drawablePadding = AndroidUtilities.dp(4); private int leftDrawableTopPadding; @@ -211,9 +216,17 @@ public class SimpleTextView extends View implements Drawable.Callback { fullLayoutLeftCharactersOffset = fullLayout.getPrimaryHorizontal(0) - firstLineLayout.getPrimaryHorizontal(0); } } + + if (replacingDrawableTextIndex >= 0) { + replacingDrawableTextOffset = layout.getPrimaryHorizontal(replacingDrawableTextIndex); + } else { + replacingDrawableTextOffset = 0; + } } protected boolean createLayout(int width) { + CharSequence text = this.text; + replacingDrawableTextIndex = -1; if (text != null) { try { if (leftDrawable != null) { @@ -225,6 +238,17 @@ public class SimpleTextView extends View implements Drawable.Callback { width -= dw; width -= drawablePadding; } + if (replacedText != null && replacedDrawable != null) { + replacingDrawableTextIndex = text.toString().indexOf(replacedText); + if (replacingDrawableTextIndex >= 0) { + SpannableStringBuilder builder = SpannableStringBuilder.valueOf(text); + builder.setSpan(new DialogCell.FixedWidthSpan(replacedDrawable.getIntrinsicWidth()), replacingDrawableTextIndex, replacingDrawableTextIndex + replacedText.length(), 0); + text = builder; + } else { + width -= replacedDrawable.getIntrinsicWidth(); + width -= drawablePadding; + } + } if (buildFullLayout) { CharSequence string = TextUtils.ellipsize(text, textPaint, width, TextUtils.TruncateAt.END); if (!string.equals(text)) { @@ -379,6 +403,23 @@ public class SimpleTextView extends View implements Drawable.Callback { } } + public void replaceTextWithDrawable(Drawable drawable, String replacedText) { + if (replacedDrawable == drawable) { + return; + } + if (replacedDrawable != null) { + replacedDrawable.setCallback(null); + } + replacedDrawable = drawable; + if (drawable != null) { + drawable.setCallback(this); + } + if (!recreateLayoutMaybe()) { + invalidate(); + } + this.replacedText = replacedText; + } + public void setMinusWidth(int value) { if (value == minusWidth) { return; @@ -486,6 +527,11 @@ public class SimpleTextView extends View implements Drawable.Callback { textOffsetX += drawablePadding + leftDrawable.getIntrinsicWidth(); } } + if (replacedDrawable != null && replacingDrawableTextIndex < 0) { + if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT) { + textOffsetX += drawablePadding + replacedDrawable.getIntrinsicWidth(); + } + } return (int) getX() + offsetX + textOffsetX; } @@ -530,6 +576,26 @@ public class SimpleTextView extends View implements Drawable.Callback { } totalWidth += drawablePadding + leftDrawable.getIntrinsicWidth(); } + if (replacedDrawable != null && replacedText != null) { + int x = (int) (-scrollingOffset + replacingDrawableTextOffset); + if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL) { + x += offsetX; + } + int y; + if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.CENTER_VERTICAL) { + y = (getMeasuredHeight() - replacedDrawable.getIntrinsicHeight()) / 2 + leftDrawableTopPadding; + } else { + y = (textHeight - replacedDrawable.getIntrinsicHeight()) / 2 + leftDrawableTopPadding; + } + replacedDrawable.setBounds(x, y, x + replacedDrawable.getIntrinsicWidth(), y + replacedDrawable.getIntrinsicHeight()); + replacedDrawable.draw(canvas); + if (replacingDrawableTextIndex < 0) { + if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT || (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL) { + textOffsetX += drawablePadding + replacedDrawable.getIntrinsicWidth(); + } + totalWidth += drawablePadding + replacedDrawable.getIntrinsicWidth(); + } + } if (rightDrawable != null) { int x = textOffsetX + textWidth + drawablePadding + (int) -scrollingOffset; if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL) { @@ -681,6 +747,8 @@ public class SimpleTextView extends View implements Drawable.Callback { invalidate(leftDrawable.getBounds()); } else if (who == rightDrawable) { invalidate(rightDrawable.getBounds()); + } else if (who == replacedDrawable) { + invalidate(replacedDrawable.getBounds()); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java index 340b51bbb..bd4c1cc34 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java @@ -85,6 +85,7 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.Cells.ThemesHorizontalListCell; import org.telegram.ui.Components.AudioVisualizerDrawable; import org.telegram.ui.Components.BackgroundGradientDrawable; +import org.telegram.ui.Components.ChoosingStickerStatusDrawable; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.FragmentContextViewWavesDrawable; import org.telegram.ui.Components.MotionBackgroundDrawable; @@ -132,12 +133,15 @@ public class Theme { public static class MessageDrawable extends Drawable { - private LinearGradient gradientShader; + private Shader gradientShader; private int currentBackgroundHeight; private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private Paint selectedPaint; private int currentColor; - private int currentGradientColor; + private int currentGradientColor1; + private int currentGradientColor2; + private int currentGradientColor3; + private boolean currentAnimateGradient; private RectF rect = new RectF(); private Matrix matrix = new Matrix(); @@ -153,6 +157,8 @@ public class Theme { private boolean isTopNear; private boolean isBottomNear; + public static MotionBackgroundDrawable[] motionBackground = new MotionBackgroundDrawable[2]; + private int[] currentShadowDrawableRadius = new int[]{-1, -1, -1, -1}; private Drawable[] shadowDrawable = new Drawable[4]; private int[] shadowDrawableColor = new int[]{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}; @@ -172,6 +178,7 @@ public class Theme { Drawable transitionDrawable; int transitionDrawableColor; private int alpha; + private boolean drawFullBubble; public MessageDrawable(int type, boolean out, boolean selected) { super(); @@ -187,7 +194,18 @@ public class Theme { return gradientShader != null && shouldDrawGradientIcons; } - public LinearGradient getGradientShader() { + public void applyMatrixScale() { + if (gradientShader instanceof BitmapShader) { + int num = currentType == TYPE_PREVIEW ? 1 : 0; + Bitmap bitmap = motionBackground[num].getBitmap(); + float scaleW = (bitmap.getWidth() / (float) motionBackground[num].getBounds().width()); + float scaleH = (bitmap.getHeight() / (float) motionBackground[num].getBounds().height()); + float scale = 1.0f / Math.min(scaleW, scaleH); + matrix.postScale(scale, scale); + } + } + + public Shader getGradientShader() { return gradientShader; } @@ -203,38 +221,85 @@ public class Theme { return Theme.currentColors.get(key); } - public void setTop(int top, int backgroundHeight, boolean topNear, boolean bottomNear) { + public void setTop(int top, int backgroundWidth, int backgroundHeight, boolean topNear, boolean bottomNear) { + setTop(top, backgroundWidth, backgroundHeight, backgroundHeight, topNear, bottomNear); + } + + public void setTop(int top, int backgroundWidth, int backgroundHeight, int heightOffset, boolean topNear, boolean bottomNear) { int color; - Integer gradientColor; + Integer gradientColor1; + Integer gradientColor2; + Integer gradientColor3; + boolean animatedGradient; if (isOut) { color = getColor(isSelected ? key_chat_outBubbleSelected : key_chat_outBubble); - gradientColor = getCurrentColor(key_chat_outBubbleGradient); + gradientColor1 = getCurrentColor(key_chat_outBubbleGradient1); + gradientColor2 = getCurrentColor(key_chat_outBubbleGradient2); + gradientColor3 = getCurrentColor(key_chat_outBubbleGradient3); + Integer val = getCurrentColor(key_chat_outBubbleGradientAnimated); + animatedGradient = val != null && val != 0; } else { color = getColor(isSelected ? key_chat_inBubbleSelected : key_chat_inBubble); - gradientColor = null; + gradientColor1 = null; + gradientColor2 = null; + gradientColor3 = null; + animatedGradient = false; } - if (gradientColor != null) { + if (gradientColor1 != null) { color = getColor(key_chat_outBubble); } - if (gradientColor == null) { - gradientColor = 0; + if (gradientColor1 == null) { + gradientColor1 = 0; } - if (gradientColor != 0 && (gradientShader == null || backgroundHeight != currentBackgroundHeight || currentColor != color || currentGradientColor != gradientColor)) { - gradientShader = new LinearGradient(0, 0, 0, backgroundHeight, new int[]{gradientColor, color}, null, Shader.TileMode.CLAMP); + if (gradientColor2 == null) { + gradientColor2 = 0; + } + if (gradientColor3 == null) { + gradientColor3 = 0; + } + int num = currentType == TYPE_PREVIEW ? 1 : 0; + if (gradientColor1 != 0 && (gradientShader == null || backgroundHeight != currentBackgroundHeight || currentColor != color || currentGradientColor1 != gradientColor1 || currentGradientColor2 != gradientColor2 || currentGradientColor3 != gradientColor3 || currentAnimateGradient != animatedGradient)) { + if (gradientColor2 != 0 && animatedGradient) { + if (motionBackground[num] == null) { + motionBackground[num] = new MotionBackgroundDrawable(); + if (currentType != TYPE_PREVIEW) { + motionBackground[num].setPostInvalidateParent(true); + } + motionBackground[num].setRoundRadius(AndroidUtilities.dp(1)); + } + motionBackground[num].setColors(color, gradientColor1, gradientColor2, gradientColor3); + gradientShader = motionBackground[num].getBitmapShader(); + } else { + if (gradientColor2 != 0) { + if (gradientColor3 != 0) { + gradientShader = new LinearGradient(0, 0, 0, backgroundHeight, new int[]{gradientColor3, gradientColor2, gradientColor1, color}, null, Shader.TileMode.CLAMP); + } else { + gradientShader = new LinearGradient(0, 0, 0, backgroundHeight, new int[]{gradientColor2, gradientColor1, color}, null, Shader.TileMode.CLAMP); + } + } else { + gradientShader = new LinearGradient(0, 0, 0, backgroundHeight, new int[]{gradientColor1, color}, null, Shader.TileMode.CLAMP); + } + } paint.setShader(gradientShader); currentColor = color; - currentGradientColor = gradientColor; + currentAnimateGradient = animatedGradient; + currentGradientColor1 = gradientColor1; + currentGradientColor2 = gradientColor2; + currentGradientColor3 = gradientColor3; paint.setColor(0xffffffff); - } else if (gradientColor == 0) { + } else if (gradientColor1 == 0) { if (gradientShader != null) { gradientShader = null; paint.setShader(null); } paint.setColor(color); } + if (gradientShader instanceof BitmapShader) { + motionBackground[num].setBounds(0, 0, backgroundWidth, backgroundHeight - (gradientShader instanceof BitmapShader ? heightOffset : 0)); + } currentBackgroundHeight = backgroundHeight; - topY = top; + topY = top - (gradientShader instanceof BitmapShader ? heightOffset : 0); isTopNear = topNear; isBottomNear = bottomNear; } @@ -358,6 +423,10 @@ public class Theme { return transitionDrawable; } + public MotionBackgroundDrawable getMotionBackgroundDrawable() { + return motionBackground[currentType == TYPE_PREVIEW ? 1 : 0]; + } + public Drawable getShadowDrawable() { if (gradientShader == null && !isSelected) { return null; @@ -482,6 +551,7 @@ public class Theme { if (paintToUse == null && gradientShader != null) { matrix.reset(); + applyMatrixScale(); matrix.postTranslate(0, -topY); gradientShader.setLocalMatrix(matrix); } @@ -489,7 +559,7 @@ public class Theme { int top = Math.max(bounds.top, 0); path.reset(); if (isOut) { - if (currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - rad < currentBackgroundHeight) { + if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - rad < currentBackgroundHeight) { if (currentType == TYPE_MEDIA) { path.moveTo(bounds.right - dp(8) - rad, bounds.bottom - padding); } else { @@ -502,7 +572,7 @@ public class Theme { path.moveTo(bounds.right - dp(8), top - topY + currentBackgroundHeight); path.lineTo(bounds.left + padding, top - topY + currentBackgroundHeight); } - if (currentType == TYPE_PREVIEW || paintToUse != null || topY + rad * 2 >= 0) { + if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + rad * 2 >= 0) { path.lineTo(bounds.left + padding, bounds.top + padding + rad); rect.set(bounds.left + padding, bounds.top + padding, bounds.left + padding + rad * 2, bounds.top + padding + rad * 2); path.arcTo(rect, 180, 90, false); @@ -535,7 +605,7 @@ public class Theme { path.lineTo(bounds.right - padding, top - topY + currentBackgroundHeight); } } else { - if (currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - smallRad * 2 < currentBackgroundHeight) { + if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - smallRad * 2 < currentBackgroundHeight) { path.lineTo(bounds.right - dp(8), bounds.bottom - padding - smallRad - dp(3)); rect.set(bounds.right - dp(8), bounds.bottom - padding - smallRad * 2 - dp(9), bounds.right - dp(7) + smallRad * 2, bounds.bottom - padding - dp(1)); path.arcTo(rect, 180, -83, false); @@ -544,7 +614,7 @@ public class Theme { } } } else { - if (currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - rad < currentBackgroundHeight) { + if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - rad < currentBackgroundHeight) { if (currentType == TYPE_MEDIA) { path.moveTo(bounds.left + dp(8) + rad, bounds.bottom - padding); } else { @@ -557,7 +627,7 @@ public class Theme { path.moveTo(bounds.left + dp(8), top - topY + currentBackgroundHeight); path.lineTo(bounds.right - padding, top - topY + currentBackgroundHeight); } - if (currentType == TYPE_PREVIEW || paintToUse != null || topY + rad * 2 >= 0) { + if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + rad * 2 >= 0) { path.lineTo(bounds.right - padding, bounds.top + padding + rad); rect.set(bounds.right - padding - rad * 2, bounds.top + padding, bounds.right - padding, bounds.top + padding + rad * 2); path.arcTo(rect, 0, -90, false); @@ -590,7 +660,7 @@ public class Theme { path.lineTo(bounds.left + padding, top - topY + currentBackgroundHeight); } } else { - if (currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - smallRad * 2 < currentBackgroundHeight) { + if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - smallRad * 2 < currentBackgroundHeight) { path.lineTo(bounds.left + dp(8), bounds.bottom - padding - smallRad - dp(3)); rect.set(bounds.left + dp(7) - smallRad * 2, bounds.bottom - padding - smallRad * 2 - dp(9), bounds.left + dp(8), bounds.bottom - padding - dp(1)); path.arcTo(rect, 0, 83, false); @@ -609,6 +679,10 @@ public class Theme { } } + public void setDrawFullBubble(boolean drawFullBuble) { + this.drawFullBubble = drawFullBuble; + } + @Override public void setAlpha(int alpha) { if (this.alpha != alpha) { @@ -949,7 +1023,10 @@ public class Theme { public int accentColor; public int myMessagesAccentColor; - public int myMessagesGradientAccentColor; + public int myMessagesGradientAccentColor1; + public int myMessagesGradientAccentColor2; + public int myMessagesGradientAccentColor3; + public boolean myMessagesAnimated; public long backgroundOverrideColor; public long backgroundGradientOverrideColor1; public long backgroundGradientOverrideColor2; @@ -1005,7 +1082,7 @@ public class Theme { } } int myMessagesAccent = myMessagesAccentColor; - if ((myMessagesAccentColor != 0 || accentColor != 0) && myMessagesGradientAccentColor != 0) { + if ((myMessagesAccentColor != 0 || accentColor != 0) && myMessagesGradientAccentColor1 != 0) { int firstColor = myMessagesAccentColor != 0 ? myMessagesAccentColor : accentColor; Integer color = currentColorsNoAccent.get(key_chat_outBubble); if (color == null) { @@ -1013,7 +1090,7 @@ public class Theme { } int newColor = changeColorAccent(hsvTemp1, hsvTemp2, color, isDarkTheme); int distance1 = AndroidUtilities.getColorDistance(firstColor, newColor); - int distance2 = AndroidUtilities.getColorDistance(firstColor, myMessagesGradientAccentColor); + int distance2 = AndroidUtilities.getColorDistance(firstColor, myMessagesGradientAccentColor1); isMyMessagesGradientColorsNear = distance1 <= 35000 && distance2 <= 35000; myMessagesAccent = getAccentColor(hsvTemp1, color, firstColor); } @@ -1041,11 +1118,22 @@ public class Theme { } } if (!isMyMessagesGradientColorsNear) { - if (myMessagesGradientAccentColor != 0) { + if (myMessagesGradientAccentColor1 != 0) { int textColor; int subTextColor; int seekbarColor; - if (useBlackText(myMessagesAccentColor, myMessagesGradientAccentColor)) { + boolean useBlackText; + if (myMessagesGradientAccentColor2 != 0) { + int color = AndroidUtilities.getAverageColor(myMessagesAccentColor, myMessagesGradientAccentColor1); + color = AndroidUtilities.getAverageColor(color, myMessagesGradientAccentColor2); + if (myMessagesGradientAccentColor3 != 0) { + color = AndroidUtilities.getAverageColor(color, myMessagesGradientAccentColor3); + } + useBlackText = AndroidUtilities.computePerceivedBrightness(color) > 0.705f; + } else { + useBlackText = useBlackText(myMessagesAccentColor, myMessagesGradientAccentColor1); + } + if (useBlackText) { textColor = 0xff212121; subTextColor = 0xff555555; seekbarColor = 0x4d000000; @@ -1135,9 +1223,16 @@ public class Theme { isMyMessagesGradientColorsNear = false; } } - if (myMessagesAccentColor != 0 && myMessagesGradientAccentColor != 0) { + if (myMessagesAccentColor != 0 && myMessagesGradientAccentColor1 != 0) { currentColors.put(key_chat_outBubble, myMessagesAccentColor); - currentColors.put(key_chat_outBubbleGradient, myMessagesGradientAccentColor); + currentColors.put(key_chat_outBubbleGradient1, myMessagesGradientAccentColor1); + if (myMessagesGradientAccentColor2 != 0) { + currentColors.put(key_chat_outBubbleGradient2, myMessagesGradientAccentColor2); + if (myMessagesGradientAccentColor3 != 0) { + currentColors.put(key_chat_outBubbleGradient3, myMessagesGradientAccentColor3); + } + } + currentColors.put(key_chat_outBubbleGradientAnimated, myMessagesAnimated ? 1 : 0); } int backgroundOverride = (int) backgroundOverrideColor; if (backgroundOverride != 0) { @@ -1613,7 +1708,11 @@ public class Theme { if (defaultAccent == null || accent == null) { return false; } - return defaultAccent.myMessagesAccentColor == accent.myMessagesAccentColor && defaultAccent.myMessagesGradientAccentColor == accent.myMessagesGradientAccentColor; + return defaultAccent.myMessagesAccentColor == accent.myMessagesAccentColor && + defaultAccent.myMessagesGradientAccentColor1 == accent.myMessagesGradientAccentColor1 && + defaultAccent.myMessagesGradientAccentColor2 == accent.myMessagesGradientAccentColor2 && + defaultAccent.myMessagesGradientAccentColor3 == accent.myMessagesGradientAccentColor3 && + defaultAccent.myMessagesAnimated == accent.myMessagesAnimated; } private boolean isDefaultMainAccent() { @@ -1708,7 +1807,7 @@ public class Theme { themeAccent.myMessagesAccentColor = myMessages[a]; } if (myMessagesGradient != null) { - themeAccent.myMessagesGradientAccentColor = myMessagesGradient[a]; + themeAccent.myMessagesGradientAccentColor1 = myMessagesGradient[a]; } if (background != null) { themeAccent.backgroundOverrideColor = background[a]; @@ -1781,10 +1880,13 @@ public class Theme { } public static boolean accentEquals(ThemeAccent accent, TLRPC.TL_themeSettings settings) { - int myMessagesGradientAccentColor = settings.message_top_color; - if (settings.message_bottom_color == myMessagesGradientAccentColor) { - myMessagesGradientAccentColor = 0; + int bottomColor = settings.message_colors.size() > 0 ? settings.message_colors.get(0) | 0xff000000 : 0; + int myMessagesGradientAccentColor1 = settings.message_colors.size() > 1 ? settings.message_colors.get(1) | 0xff000000 : 0; + if (bottomColor == myMessagesGradientAccentColor1) { + myMessagesGradientAccentColor1 = 0; } + int myMessagesGradientAccentColor2 = settings.message_colors.size() > 2 ? settings.message_colors.get(2) | 0xff000000 : 0; + int myMessagesGradientAccentColor3 = settings.message_colors.size() > 3 ? settings.message_colors.get(3) | 0xff000000 : 0; int backgroundOverrideColor = 0; long backgroundGradientOverrideColor1 = 0; long backgroundGradientOverrideColor2 = 0; @@ -1816,8 +1918,11 @@ public class Theme { } } return settings.accent_color == accent.accentColor && - settings.message_bottom_color == accent.myMessagesAccentColor && - myMessagesGradientAccentColor == accent.myMessagesGradientAccentColor && + bottomColor == accent.myMessagesAccentColor && + myMessagesGradientAccentColor1 == accent.myMessagesGradientAccentColor1 && + myMessagesGradientAccentColor2 == accent.myMessagesGradientAccentColor2 && + myMessagesGradientAccentColor3 == accent.myMessagesGradientAccentColor3 && + settings.message_colors_animated == accent.myMessagesAnimated && backgroundOverrideColor == accent.backgroundOverrideColor && backgroundGradientOverrideColor1 == accent.backgroundGradientOverrideColor1 && backgroundGradientOverrideColor2 == accent.backgroundGradientOverrideColor2 && @@ -1829,11 +1934,14 @@ public class Theme { public static void fillAccentValues(ThemeAccent themeAccent, TLRPC.TL_themeSettings settings) { themeAccent.accentColor = settings.accent_color; - themeAccent.myMessagesAccentColor = settings.message_bottom_color; - themeAccent.myMessagesGradientAccentColor = settings.message_top_color; - if (themeAccent.myMessagesAccentColor == themeAccent.myMessagesGradientAccentColor) { - themeAccent.myMessagesGradientAccentColor = 0; + themeAccent.myMessagesAccentColor = settings.message_colors.size() > 0 ? settings.message_colors.get(0) | 0xff000000 : 0; + themeAccent.myMessagesGradientAccentColor1 = settings.message_colors.size() > 1 ? settings.message_colors.get(1) | 0xff000000 : 0; + if (themeAccent.myMessagesAccentColor == themeAccent.myMessagesGradientAccentColor1) { + themeAccent.myMessagesGradientAccentColor1 = 0; } + themeAccent.myMessagesGradientAccentColor2 = settings.message_colors.size() > 2 ? settings.message_colors.get(2) | 0xff000000 : 0; + themeAccent.myMessagesGradientAccentColor3 = settings.message_colors.size() > 3 ? settings.message_colors.get(3) | 0xff000000 : 0; + themeAccent.myMessagesAnimated = settings.message_colors_animated; if (settings.wallpaper != null && settings.wallpaper.settings != null) { themeAccent.backgroundOverrideColor = getWallpaperColor(settings.wallpaper.settings.background_color); if ((settings.wallpaper.settings.flags & 16) != 0 && settings.wallpaper.settings.second_background_color == 0) { @@ -1896,7 +2004,10 @@ public class Theme { ThemeAccent themeAccent = new ThemeAccent(); themeAccent.accentColor = accent.accentColor; themeAccent.myMessagesAccentColor = accent.myMessagesAccentColor; - themeAccent.myMessagesGradientAccentColor = accent.myMessagesGradientAccentColor; + themeAccent.myMessagesGradientAccentColor1 = accent.myMessagesGradientAccentColor1; + themeAccent.myMessagesGradientAccentColor2 = accent.myMessagesGradientAccentColor2; + themeAccent.myMessagesGradientAccentColor3 = accent.myMessagesGradientAccentColor3; + themeAccent.myMessagesAnimated = accent.myMessagesAnimated; themeAccent.backgroundOverrideColor = accent.backgroundOverrideColor; themeAccent.backgroundGradientOverrideColor1 = accent.backgroundGradientOverrideColor1; themeAccent.backgroundGradientOverrideColor2 = accent.backgroundGradientOverrideColor2; @@ -2256,6 +2367,7 @@ public class Theme { public static Drawable chat_msgNoSoundDrawable; public static Drawable chat_composeShadowDrawable; + public static Drawable chat_composeShadowRoundDrawable; public static Drawable chat_roundVideoShadow; public static MessageDrawable chat_msgInDrawable; public static MessageDrawable chat_msgInSelectedDrawable; @@ -2265,7 +2377,7 @@ public class Theme { public static MessageDrawable chat_msgInMediaSelectedDrawable; public static MessageDrawable chat_msgOutMediaDrawable; public static MessageDrawable chat_msgOutMediaSelectedDrawable; - private static StatusDrawable[] chat_status_drawables = new StatusDrawable[5]; + private static StatusDrawable[] chat_status_drawables = new StatusDrawable[6]; public static PathAnimator playPauseAnimator; public static Drawable chat_msgOutCheckDrawable; @@ -2670,7 +2782,10 @@ public class Theme { public static final String key_chat_inBubbleSelected = "chat_inBubbleSelected"; public static final String key_chat_inBubbleShadow = "chat_inBubbleShadow"; public static final String key_chat_outBubble = "chat_outBubble"; - public static final String key_chat_outBubbleGradient = "chat_outBubbleGradient"; + public static final String key_chat_outBubbleGradient1 = "chat_outBubbleGradient"; + public static final String key_chat_outBubbleGradient2 = "chat_outBubbleGradient2"; + public static final String key_chat_outBubbleGradient3 = "chat_outBubbleGradient3"; + public static final String key_chat_outBubbleGradientAnimated = "chat_outBubbleGradientAnimated"; public static final String key_chat_outBubbleGradientSelectedOverlay = "chat_outBubbleGradientSelectedOverlay"; public static final String key_chat_outBubbleSelected = "chat_outBubbleSelected"; public static final String key_chat_outBubbleShadow = "chat_outBubbleShadow"; @@ -4162,7 +4277,7 @@ public class Theme { myMessagesColorKeys.add(key_chat_outBubble); myMessagesColorKeys.add(key_chat_outBubbleSelected); myMessagesColorKeys.add(key_chat_outBubbleShadow); - myMessagesColorKeys.add(key_chat_outBubbleGradient); + myMessagesColorKeys.add(key_chat_outBubbleGradient1); myMessagesColorKeys.add(key_chat_outSentCheck); myMessagesColorKeys.add(key_chat_outSentCheckSelected); myMessagesColorKeys.add(key_chat_outSentCheckRead); @@ -4456,7 +4571,14 @@ public class Theme { accent.accentColor = data.readInt32(true); accent.parentTheme = info; accent.myMessagesAccentColor = data.readInt32(true); - accent.myMessagesGradientAccentColor = data.readInt32(true); + accent.myMessagesGradientAccentColor1 = data.readInt32(true); + if (version >= 7) { + accent.myMessagesGradientAccentColor2 = data.readInt32(true); + accent.myMessagesGradientAccentColor3 = data.readInt32(true); + } + if (version >= 8) { + accent.myMessagesAnimated = data.readBool(true); + } if (version >= 3) { accent.backgroundOverrideColor = data.readInt64(true); } else { @@ -4527,13 +4649,16 @@ public class Theme { info.lastAccentId = 101; SerializedData data = new SerializedData(4 * (15 + 2)); - data.writeInt32(6); + data.writeInt32(8); data.writeInt32(1); data.writeInt32(accent.id); data.writeInt32(accent.accentColor); data.writeInt32(accent.myMessagesAccentColor); - data.writeInt32(accent.myMessagesGradientAccentColor); + data.writeInt32(accent.myMessagesGradientAccentColor1); + data.writeInt32(accent.myMessagesGradientAccentColor2); + data.writeInt32(accent.myMessagesGradientAccentColor3); + data.writeBool(accent.myMessagesAnimated); data.writeInt64(accent.backgroundOverrideColor); data.writeInt64(accent.backgroundGradientOverrideColor1); data.writeInt64(accent.backgroundGradientOverrideColor2); @@ -5644,10 +5769,10 @@ public class Theme { } public static void refreshThemeColors() { - refreshThemeColors(false); + refreshThemeColors(false, false); } - public static void refreshThemeColors(boolean bg) { + public static void refreshThemeColors(boolean bg, boolean messages) { currentColors.clear(); currentColors.putAll(currentColorsNoAccent); shouldDrawGradientIcons = true; @@ -5655,7 +5780,9 @@ public class Theme { if (accent != null) { shouldDrawGradientIcons = accent.fillAccentColors(currentColorsNoAccent, currentColors); } - reloadWallpaper(); + if (!messages) { + reloadWallpaper(); + } applyCommonTheme(); applyDialogsTheme(); applyProfileTheme(); @@ -5814,7 +5941,7 @@ public class Theme { int N = theme.themeAccents.size(); int count = Math.max(0, N - theme.defaultAccentCount); SerializedData data = new SerializedData(4 * (count * 15 + 2)); - data.writeInt32(6); + data.writeInt32(8); data.writeInt32(count); for (int a = 0; a < N; a++) { ThemeAccent accent = theme.themeAccents.get(a); @@ -5824,7 +5951,10 @@ public class Theme { data.writeInt32(accent.id); data.writeInt32(accent.accentColor); data.writeInt32(accent.myMessagesAccentColor); - data.writeInt32(accent.myMessagesGradientAccentColor); + data.writeInt32(accent.myMessagesGradientAccentColor1); + data.writeInt32(accent.myMessagesGradientAccentColor2); + data.writeInt32(accent.myMessagesGradientAccentColor3); + data.writeBool(accent.myMessagesAnimated); data.writeInt64(accent.backgroundOverrideColor); data.writeInt64(accent.backgroundGradientOverrideColor1); data.writeInt64(accent.backgroundGradientOverrideColor2); @@ -5969,6 +6099,10 @@ public class Theme { return currentTheme == currentNightTheme; } + public static boolean isCurrentThemeDark() { + return currentTheme.isDark(); + } + public static ThemeInfo getActiveTheme() { return currentTheme; } @@ -6290,10 +6424,19 @@ public class Theme { StringBuilder result = new StringBuilder(); if (colorsMap != defaultColors) { int outBubbleColor = accent != null ? accent.myMessagesAccentColor : 0; - int outBubbleGradient = accent != null ? accent.myMessagesGradientAccentColor : 0; - if (outBubbleColor != 0 && outBubbleGradient != 0) { + int outBubbleGradient1 = accent != null ? accent.myMessagesGradientAccentColor1 : 0; + int outBubbleGradient2 = accent != null ? accent.myMessagesGradientAccentColor2 : 0; + int outBubbleGradient3 = accent != null ? accent.myMessagesGradientAccentColor3 : 0; + if (outBubbleColor != 0 && outBubbleGradient1 != 0) { colorsMap.put(key_chat_outBubble, outBubbleColor); - colorsMap.put(key_chat_outBubbleGradient, outBubbleGradient); + colorsMap.put(key_chat_outBubbleGradient1, outBubbleGradient1); + if (outBubbleGradient2 != 0) { + colorsMap.put(key_chat_outBubbleGradient2, outBubbleGradient2); + if (outBubbleGradient3 != 0) { + colorsMap.put(key_chat_outBubbleGradient3, outBubbleGradient3); + } + } + colorsMap.put(key_chat_outBubbleGradientAnimated, accent != null && accent.myMessagesAnimated ? 1 : 0); } } for (HashMap.Entry entry : colorsMap.entrySet()) { @@ -6737,7 +6880,7 @@ public class Theme { int messageFieldIconColor = getPreviewColor(colors, key_chat_messagePanelIcons); int messageInColor = getPreviewColor(colors, key_chat_inBubble); int messageOutColor = getPreviewColor(colors, key_chat_outBubble); - Integer messageOutGradientColor = colors.get(key_chat_outBubbleGradient); + Integer messageOutGradientColor = colors.get(key_chat_outBubbleGradient1); Integer backgroundColor = colors.get(key_chat_wallpaper); Integer gradientToColor1 = colors.get(key_chat_wallpaper_gradient_to1); Integer gradientToColor2 = colors.get(key_chat_wallpaper_gradient_to2); @@ -6960,15 +7103,15 @@ public class Theme { otherDrawable.draw(canvas); } msgDrawable[1].setBounds(161, 216, bitmap.getWidth() - 20, 216 + 92); - msgDrawable[1].setTop(0, 522, false, false); + msgDrawable[1].setTop(0, 560, 522, false, false); msgDrawable[1].draw(canvas); msgDrawable[1].setBounds(161, 430, bitmap.getWidth() - 20, 430 + 92); - msgDrawable[1].setTop(430, 522, false, false); + msgDrawable[1].setTop(430, 560, 522, false, false); msgDrawable[1].draw(canvas); msgDrawable[0].setBounds(20, 323, 399, 323 + 92); - msgDrawable[0].setTop(323, 522, false, false); + msgDrawable[0].setTop(323, 560, 522, false, false); msgDrawable[0].draw(canvas); paint.setColor(messageFieldColor); @@ -7695,7 +7838,8 @@ public class Theme { chat_locationDrawable[0] = resources.getDrawable(R.drawable.msg_location).mutate(); chat_locationDrawable[1] = resources.getDrawable(R.drawable.msg_location).mutate(); - chat_composeShadowDrawable = context.getResources().getDrawable(R.drawable.compose_panel_shadow); + chat_composeShadowDrawable = context.getResources().getDrawable(R.drawable.compose_panel_shadow).mutate(); + chat_composeShadowRoundDrawable = context.getResources().getDrawable(R.drawable.sheet_shadow_round).mutate(); try { int bitmapSize = AndroidUtilities.roundMessageSize + AndroidUtilities.dp(6); @@ -7750,7 +7894,6 @@ public class Theme { chat_instantViewPaint.setTextSize(AndroidUtilities.dp(13)); chat_instantViewRectPaint.setStrokeWidth(AndroidUtilities.dp(1)); chat_pollTimerPaint.setStrokeWidth(AndroidUtilities.dp(1.1f)); - chat_statusRecordPaint.setStrokeWidth(AndroidUtilities.dp(2)); chat_actionTextPaint.setTextSize(AndroidUtilities.dp(Math.max(16, SharedConfig.fontSize) - 2)); chat_contextResult_titleTextPaint.setTextSize(AndroidUtilities.dp(15)); chat_contextResult_descriptionTextPaint.setTextSize(AndroidUtilities.dp(13)); @@ -7961,6 +8104,7 @@ public class Theme { setDrawableColor(chat_psaHelpDrawable[1], getColor(key_chat_outViews)); setDrawableColorByKey(chat_composeShadowDrawable, key_chat_messagePanelShadow); + setDrawableColorByKey(chat_composeShadowRoundDrawable, key_chat_messagePanelBackground); int color = getColor(key_chat_outAudioSeekbarFill); if (color == 0xffffffff) { @@ -8993,7 +9137,7 @@ public class Theme { } public static StatusDrawable getChatStatusDrawable(int type) { - if (type < 0 || type > 4) { + if (type < 0 || type > 5) { return null; } StatusDrawable statusDrawable = chat_status_drawables[type]; @@ -9016,6 +9160,9 @@ public class Theme { case 4: chat_status_drawables[4] = new RoundStatusDrawable(true); break; + case 5: + chat_status_drawables[5] = new ChoosingStickerStatusDrawable(true); + break; } statusDrawable = chat_status_drawables[type]; statusDrawable.start(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java index 744aa8e7e..ef4eaff3c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsAdapter.java @@ -144,6 +144,10 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter { notifyDataSetChanged(); } + public int getDialogsType() { + return dialogsType; + } + @Override public int getItemCount() { MessagesController messagesController = MessagesController.getInstance(currentAccount); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java index ce8ef9051..1d2b79c6f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java @@ -106,10 +106,10 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { public CharSequence name; } - protected static class RecentSearchObject { - TLObject object; - int date; - long did; + public static class RecentSearchObject { + public TLObject object; + public int date; + public long did; } public interface DialogsSearchAdapterDelegate { @@ -121,7 +121,17 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { boolean isSelected(long dialogId); } - private class CategoryAdapterRecycler extends RecyclerListView.SelectionAdapter { + public static class CategoryAdapterRecycler extends RecyclerListView.SelectionAdapter { + + private final Context mContext; + private final int currentAccount; + private boolean drawChecked; + + public CategoryAdapterRecycler(Context context, int account, boolean drawChecked) { + this.drawChecked = drawChecked; + mContext = context; + currentAccount = account; + } public void setIndex(int value) { notifyDataSetChanged(); @@ -129,7 +139,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - View view = new HintDialogCell(mContext); + View view = new HintDialogCell(mContext, drawChecked); view.setLayoutParams(new RecyclerView.LayoutParams(AndroidUtilities.dp(80), AndroidUtilities.dp(86))); return new RecyclerListView.Holder(view); } @@ -371,6 +381,12 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { } public void loadRecentSearch() { + loadRecentSearch(currentAccount, dialogsType, (arrayList, hashMap) -> { + DialogsSearchAdapter.this.setRecentSearch(arrayList, hashMap); + }); + } + + public static void loadRecentSearch(int currentAccount, int dialogsType, OnRecentSearchLoaded callback) { MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> { try { SQLiteCursor cursor = MessagesStorage.getInstance(currentAccount).getDatabase().queryFinalized("SELECT did, date FROM search_recent WHERE 1"); @@ -465,7 +481,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { return 0; } }); - AndroidUtilities.runOnUIThread(() -> setRecentSearch(arrayList, hashMap)); + AndroidUtilities.runOnUIThread(() -> callback.setRecentSearch(arrayList, hashMap)); } catch (Exception e) { FileLog.e(e); } @@ -934,7 +950,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); horizontalListView.setLayoutManager(layoutManager); //horizontalListView.setDisallowInterceptTouchEvents(true); - horizontalListView.setAdapter(new CategoryAdapterRecycler()); + horizontalListView.setAdapter(new CategoryAdapterRecycler(mContext, currentAccount, false)); horizontalListView.setOnItemClickListener((view1, position) -> { if (delegate != null) { delegate.didPressedOnSubDialog((Integer) view1.getTag()); @@ -1223,4 +1239,8 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter { public int getCurrentItemCount() { return currentItemCount; } + + public interface OnRecentSearchLoaded { + void setRecentSearch(ArrayList arrayList, LongSparseArray hashMap); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java index 524c9346f..a5595a586 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java @@ -24,13 +24,18 @@ import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.ImageLocation; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.SendMessagesHelper; +import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; import org.telegram.tgnet.ConnectionsManager; @@ -42,9 +47,11 @@ import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.BotSwitchCell; import org.telegram.ui.Cells.ContextLinkCell; import org.telegram.ui.Cells.MentionCell; +import org.telegram.ui.Cells.StickerCell; import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.RecyclerListView; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -53,7 +60,7 @@ import java.util.HashMap; import androidx.recyclerview.widget.RecyclerView; -public class MentionsAdapter extends RecyclerListView.SelectionAdapter { +public class MentionsAdapter extends RecyclerListView.SelectionAdapter implements NotificationCenter.NotificationCenterDelegate { public interface MentionsAdapterDelegate { void needChangePanelVisibility(boolean show); @@ -95,6 +102,8 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { private int channelReqId; private boolean isSearchingMentions; + private boolean visibleByStickersSearch; + private final static String punctuationsChars = " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\n"; private Runnable cancelDelayRunnable; @@ -110,8 +119,25 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { private Runnable contextQueryRunnable; private Location lastKnownLocation; + private ArrayList stickers; + private HashMap stickersMap; + private ArrayList stickersToLoad = new ArrayList<>(); + private String lastSticker; + private int lastReqId; + private boolean delayLocalResults; + private ChatActivity parentFragment; + private static class StickerResult { + public TLRPC.Document sticker; + public Object parent; + + public StickerResult(TLRPC.Document s, Object p) { + sticker = s; + parent = p; + } + } + private SendMessagesHelper.LocationProvider locationProvider = new SendMessagesHelper.LocationProvider(new SendMessagesHelper.LocationProvider.LocationProviderDelegate() { @Override public void onLocationAcquired(Location location) { @@ -152,6 +178,134 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { } } }); + if (!darkTheme) { + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileLoaded); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileLoadFailed); + } + } + + @Override + public void didReceivedNotification(int id, int account, final Object... args) { + if (id == NotificationCenter.fileLoaded || id == NotificationCenter.fileLoadFailed) { + if (stickers != null && !stickers.isEmpty() && !stickersToLoad.isEmpty() && visibleByStickersSearch) { + String fileName = (String) args[0]; + stickersToLoad.remove(fileName); + if (stickersToLoad.isEmpty()) { + delegate.needChangePanelVisibility(stickers != null && !stickers.isEmpty()); + } + } + } + } + + private void addStickerToResult(TLRPC.Document document, Object parent) { + if (document == null) { + return; + } + String key = document.dc_id + "_" + document.id; + if (stickersMap != null && stickersMap.containsKey(key)) { + return; + } + if (stickers == null) { + stickers = new ArrayList<>(); + stickersMap = new HashMap<>(); + } + stickers.add(new StickerResult(document, parent)); + stickersMap.put(key, document); + } + + private void addStickersToResult(ArrayList documents, Object parent) { + if (documents == null || documents.isEmpty()) { + return; + } + for (int a = 0, size = documents.size(); a < size; a++) { + TLRPC.Document document = documents.get(a); + String key = document.dc_id + "_" + document.id; + if (stickersMap != null && stickersMap.containsKey(key)) { + continue; + } + if (stickers == null) { + stickers = new ArrayList<>(); + stickersMap = new HashMap<>(); + } + for (int b = 0, size2 = document.attributes.size(); b < size2; b++) { + TLRPC.DocumentAttribute attribute = document.attributes.get(b); + if (attribute instanceof TLRPC.TL_documentAttributeSticker) { + parent = attribute.stickerset; + break; + } + } + stickers.add(new StickerResult(document, parent)); + stickersMap.put(key, document); + } + } + + private boolean checkStickerFilesExistAndDownload() { + if (stickers == null) { + return false; + } + stickersToLoad.clear(); + int size = Math.min(6, stickers.size()); + for (int a = 0; a < size; a++) { + StickerResult result = stickers.get(a); + TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(result.sticker.thumbs, 90); + if (thumb instanceof TLRPC.TL_photoSize || thumb instanceof TLRPC.TL_photoSizeProgressive) { + File f = FileLoader.getPathToAttach(thumb, "webp", true); + if (!f.exists()) { + stickersToLoad.add(FileLoader.getAttachFileName(thumb, "webp")); + FileLoader.getInstance(currentAccount).loadFile(ImageLocation.getForDocument(thumb, result.sticker), result.parent, "webp", 1, 1); + } + } + } + return stickersToLoad.isEmpty(); + } + + private boolean isValidSticker(TLRPC.Document document, String emoji) { + for (int b = 0, size2 = document.attributes.size(); b < size2; b++) { + TLRPC.DocumentAttribute attribute = document.attributes.get(b); + if (attribute instanceof TLRPC.TL_documentAttributeSticker) { + if (attribute.alt != null && attribute.alt.contains(emoji)) { + return true; + } + break; + } + } + return false; + } + + private void searchServerStickers(final String emoji, final String originalEmoji) { + TLRPC.TL_messages_getStickers req = new TLRPC.TL_messages_getStickers(); + req.emoticon = originalEmoji; + req.hash = 0; + lastReqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + lastReqId = 0; + if (!emoji.equals(lastSticker) || !(response instanceof TLRPC.TL_messages_stickers)) { + return; + } + delayLocalResults = false; + TLRPC.TL_messages_stickers res = (TLRPC.TL_messages_stickers) response; + int oldCount = stickers != null ? stickers.size() : 0; + addStickersToResult(res.stickers, "sticker_search_" + emoji); + int newCount = stickers != null ? stickers.size() : 0; + if (!visibleByStickersSearch && stickers != null && !stickers.isEmpty()) { + checkStickerFilesExistAndDownload(); + delegate.needChangePanelVisibility(stickersToLoad.isEmpty()); + visibleByStickersSearch = true; + } + if (oldCount != newCount) { + notifyDataSetChanged(); + } + })); + } + + private void clearStickers() { + lastSticker = null; + stickers = null; + stickersMap = null; + notifyDataSetChanged(); + if (lastReqId != 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(lastReqId, true); + lastReqId = 0; + } } public void onDestroy() { @@ -175,6 +329,10 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { searchingContextUsername = null; searchingContextQuery = null; noUserName = false; + if (!isDarkTheme) { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileLoaded); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileLoadFailed); + } } public void setParentFragment(ChatActivity fragment) { @@ -510,6 +668,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { cancelDelayRunnable = null; } searchResultHashtags = null; + stickers = null; searchResultUsernames = null; searchResultUsernamesMap = null; searchResultCommands = null; @@ -568,6 +727,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { searchForContextBot(null, null); delegate.needChangePanelVisibility(false); lastText = null; + clearStickers(); return; } int searchPostion = position; @@ -579,7 +739,136 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { lastForSearch = forSearch; StringBuilder result = new StringBuilder(); int foundType = -1; - if (!usernameOnly && needBotContext && text.charAt(0) == '@') { + + boolean searchEmoji = !usernameOnly && text != null && text.length() > 0 && text.length() <= 14; + String originalEmoji = ""; + if (searchEmoji) { + CharSequence emoji = originalEmoji = text; + int length = emoji.length(); + for (int a = 0; a < length; a++) { + char ch = emoji.charAt(a); + char nch = a < length - 1 ? emoji.charAt(a + 1) : 0; + if (a < length - 1 && ch == 0xD83C && nch >= 0xDFFB && nch <= 0xDFFF) { + emoji = TextUtils.concat(emoji.subSequence(0, a), emoji.subSequence(a + 2, emoji.length())); + length -= 2; + a--; + } else if (ch == 0xfe0f) { + emoji = TextUtils.concat(emoji.subSequence(0, a), emoji.subSequence(a + 1, emoji.length())); + length--; + a--; + } + } + lastSticker = emoji.toString().trim(); + } + boolean isValidEmoji = searchEmoji && (Emoji.isValidEmoji(originalEmoji) || Emoji.isValidEmoji(lastSticker)); + + if (isValidEmoji && parentFragment != null && (parentFragment.getCurrentChat() == null || ChatObject.canSendStickers(parentFragment.getCurrentChat()))) { + stickersToLoad.clear(); + if (SharedConfig.suggestStickers == 2 || !isValidEmoji) { + if (visibleByStickersSearch && SharedConfig.suggestStickers == 2) { + visibleByStickersSearch = false; + delegate.needChangePanelVisibility(false); + notifyDataSetChanged(); + } + return; + } + stickers = null; + stickersMap = null; + foundType = 4; + if (lastReqId != 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(lastReqId, true); + lastReqId = 0; + } + boolean serverStickersOnly = MessagesController.getInstance(currentAccount).suggestStickersApiOnly; + + delayLocalResults = false; + if (!serverStickersOnly) { + final ArrayList recentStickers = MediaDataController.getInstance(currentAccount).getRecentStickersNoCopy(MediaDataController.TYPE_IMAGE); + final ArrayList favsStickers = MediaDataController.getInstance(currentAccount).getRecentStickersNoCopy(MediaDataController.TYPE_FAVE); + int recentsAdded = 0; + for (int a = 0, size = Math.min(20, recentStickers.size()); a < size; a++) { + TLRPC.Document document = recentStickers.get(a); + if (isValidSticker(document, lastSticker)) { + addStickerToResult(document, "recent"); + recentsAdded++; + if (recentsAdded >= 5) { + break; + } + } + } + for (int a = 0, size = favsStickers.size(); a < size; a++) { + TLRPC.Document document = favsStickers.get(a); + if (isValidSticker(document, lastSticker)) { + addStickerToResult(document, "fav"); + } + } + + HashMap> allStickers = MediaDataController.getInstance(currentAccount).getAllStickers(); + ArrayList newStickers = allStickers != null ? allStickers.get(lastSticker) : null; + if (newStickers != null && !newStickers.isEmpty()) { + addStickersToResult(newStickers, null); + } + if (stickers != null) { + Collections.sort(stickers, new Comparator() { + private int getIndex(StickerResult result) { + for (int a = 0; a < favsStickers.size(); a++) { + if (favsStickers.get(a).id == result.sticker.id) { + return a + 2000000; + } + } + for (int a = 0; a < Math.min(20, recentStickers.size()); a++) { + if (recentStickers.get(a).id == result.sticker.id) { + return recentStickers.size() - a + 1000000; + } + } + return -1; + } + + @Override + public int compare(StickerResult lhs, StickerResult rhs) { + boolean isAnimated1 = MessageObject.isAnimatedStickerDocument(lhs.sticker, true); + boolean isAnimated2 = MessageObject.isAnimatedStickerDocument(rhs.sticker, true); + if (isAnimated1 == isAnimated2) { + int idx1 = getIndex(lhs); + int idx2 = getIndex(rhs); + if (idx1 > idx2) { + return -1; + } else if (idx1 < idx2) { + return 1; + } + return 0; + } else { + if (isAnimated1) { + return -1; + } else { + return 1; + } + } + } + }); + } + } + if (SharedConfig.suggestStickers == 0 || serverStickersOnly) { + searchServerStickers(lastSticker, originalEmoji); + } + + if (stickers != null && !stickers.isEmpty()) { + if (SharedConfig.suggestStickers == 0 && stickers.size() < 5) { + delayLocalResults = true; + delegate.needChangePanelVisibility(false); + visibleByStickersSearch = false; + } else { + checkStickerFilesExistAndDownload(); + boolean show = stickersToLoad.isEmpty(); + delegate.needChangePanelVisibility(show); + visibleByStickersSearch = true; + } + notifyDataSetChanged(); + } else if (visibleByStickersSearch) { + delegate.needChangePanelVisibility(false); + visibleByStickersSearch = false; + } + } else if (!usernameOnly && needBotContext && text.charAt(0) == '@') { int index = text.indexOf(' '); int len = text.length(); String username = null; @@ -801,6 +1090,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { } }); searchResultHashtags = null; + stickers = null; searchResultCommands = null; searchResultCommandsHelp = null; searchResultCommandsUsers = null; @@ -887,6 +1177,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { } } searchResultHashtags = newResult; + stickers = null; searchResultUsernames = null; searchResultUsernamesMap = null; searchResultCommands = null; @@ -912,6 +1203,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { } } searchResultHashtags = null; + stickers = null; searchResultUsernames = null; searchResultUsernamesMap = null; searchResultSuggestions = null; @@ -929,6 +1221,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { MediaDataController.getInstance(currentAccount).getEmojiSuggestions(lastSearchKeyboardLanguage, result.toString(), false, (param, alias) -> { searchResultSuggestions = param; searchResultHashtags = null; + stickers = null; searchResultUsernames = null; searchResultUsernamesMap = null; searchResultCommands = null; @@ -937,6 +1230,14 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { notifyDataSetChanged(); delegate.needChangePanelVisibility(searchResultSuggestions != null && !searchResultSuggestions.isEmpty()); }); + } else if (foundType == 4) { + searchResultHashtags = null; + searchResultUsernames = null; + searchResultUsernamesMap = null; + searchResultSuggestions = null; + searchResultCommands = null; + searchResultCommandsHelp = null; + searchResultCommandsUsers = null; } } @@ -970,7 +1271,9 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { if (foundContextBot != null && !inlineMediaEnabled) { return 1; } - if (searchResultBotContext != null) { + if (stickers != null) { + return stickers.size(); + }else if (searchResultBotContext != null) { return searchResultBotContext.size() + (searchResultBotContextSwitch != null ? 1 : 0); } else if (searchResultUsernames != null) { return searchResultUsernames.size(); @@ -986,7 +1289,9 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { @Override public int getItemViewType(int position) { - if (foundContextBot != null && !inlineMediaEnabled) { + if (stickers != null) { + return 4; + } else if (foundContextBot != null && !inlineMediaEnabled) { return 3; } else if (searchResultBotContext != null) { if (position == 0 && searchResultBotContextSwitch != null) { @@ -1009,8 +1314,14 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { return i; } + public Object getItemParent(int i) { + return stickers != null && i >= 0 && i < stickers.size() ? stickers.get(i).parent : null; + } + public Object getItem(int i) { - if (searchResultBotContext != null) { + if (stickers != null) { + return i >= 0 && i < stickers.size() ? stickers.get(i).sticker : null; + } else if (searchResultBotContext != null) { if (searchResultBotContextSwitch != null) { if (i == 0) { return searchResultBotContextSwitch; @@ -1061,6 +1372,10 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { return searchResultCommands != null; } + public boolean isStickers() { + return stickers != null; + } + public boolean isBotContext() { return searchResultBotContext != null; } @@ -1070,12 +1385,12 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { } public boolean isMediaLayout() { - return contextMedia; + return contextMedia || stickers != null; } @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { - return foundContextBot == null || inlineMediaEnabled; + return (foundContextBot == null || inlineMediaEnabled) && stickers == null; } @Override @@ -1094,20 +1409,29 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter { view = new BotSwitchCell(mContext); break; case 3: - default: TextView textView = new TextView(mContext); textView.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2)); view = textView; break; + case 4: + default: + view = new StickerCell(mContext); + break; } return new RecyclerListView.Holder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { - if (holder.getItemViewType() == 3) { + int type = holder.getItemViewType(); + if (type == 4) { + StickerCell stickerCell = (StickerCell) holder.itemView; + StickerResult result = stickers.get(position); + stickerCell.setSticker(result.sticker, result.parent); + stickerCell.setClearsInputField(true); + } else if (type == 3) { TextView textView = (TextView) holder.itemView; TLRPC.Chat chat = parentFragment.getCurrentChat(); if (chat != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java index 972116f28..dede2f497 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/StickersAdapter.java @@ -10,57 +10,36 @@ package org.telegram.ui.Adapters; import android.content.Context; import android.text.TextUtils; -import android.view.View; import android.view.ViewGroup; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.Emoji; import org.telegram.messenger.ImageLocation; -import org.telegram.messenger.MessageObject; -import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; -import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.FileLoader; -import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.Cells.EmojiReplacementCell; -import org.telegram.ui.Cells.StickerCell; import org.telegram.ui.Components.RecyclerListView; import java.io.File; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; import androidx.recyclerview.widget.RecyclerView; public class StickersAdapter extends RecyclerListView.SelectionAdapter implements NotificationCenter.NotificationCenterDelegate { - private static class StickerResult { - public TLRPC.Document sticker; - public Object parent; - - public StickerResult(TLRPC.Document s, Object p) { - sticker = s; - parent = p; - } - } - private int currentAccount = UserConfig.selectedAccount; private Context mContext; private ArrayList keywordResults; - private ArrayList stickers; - private HashMap stickersMap; - private ArrayList stickersToLoad = new ArrayList<>(); private StickersAdapterDelegate delegate; - private String lastSticker; + private boolean visible; - private int lastReqId; - private boolean delayLocalResults; + + private String lastSearch; + private String[] lastSearchKeyboardLanguage; private Runnable searchRunnable; @@ -74,114 +53,23 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_IMAGE); MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_MASK); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.newEmojiSuggestionsAvailable); - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileLoaded); - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileLoadFailed); } public void onDestroy() { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.newEmojiSuggestionsAvailable); - NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileLoaded); - NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileLoadFailed); } @Override public void didReceivedNotification(int id, int account, final Object... args) { - if (id == NotificationCenter.fileLoaded || id == NotificationCenter.fileLoadFailed) { - if (stickers != null && !stickers.isEmpty() && !stickersToLoad.isEmpty() && visible) { - String fileName = (String) args[0]; - stickersToLoad.remove(fileName); - if (stickersToLoad.isEmpty()) { - boolean show = stickers != null && !stickers.isEmpty(); - if (show) { - keywordResults = null; - } - delegate.needChangePanelVisibility(show); - } - } - } else if (id == NotificationCenter.newEmojiSuggestionsAvailable) { - if ((keywordResults == null || keywordResults.isEmpty()) && !TextUtils.isEmpty(lastSticker) && getItemCount() == 0) { + if (id == NotificationCenter.newEmojiSuggestionsAvailable) { + if ((keywordResults == null || keywordResults.isEmpty()) && !TextUtils.isEmpty(lastSearch) && getItemCount() == 0) { searchEmojiByKeyword(); } } } - private boolean checkStickerFilesExistAndDownload() { - if (stickers == null) { - return false; - } - stickersToLoad.clear(); - int size = Math.min(6, stickers.size()); - for (int a = 0; a < size; a++) { - StickerResult result = stickers.get(a); - TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(result.sticker.thumbs, 90); - if (thumb instanceof TLRPC.TL_photoSize || thumb instanceof TLRPC.TL_photoSizeProgressive) { - File f = FileLoader.getPathToAttach(thumb, "webp", true); - if (!f.exists()) { - stickersToLoad.add(FileLoader.getAttachFileName(thumb, "webp")); - FileLoader.getInstance(currentAccount).loadFile(ImageLocation.getForDocument(thumb, result.sticker), result.parent, "webp", 1, 1); - } - } - } - return stickersToLoad.isEmpty(); - } - - private boolean isValidSticker(TLRPC.Document document, String emoji) { - for (int b = 0, size2 = document.attributes.size(); b < size2; b++) { - TLRPC.DocumentAttribute attribute = document.attributes.get(b); - if (attribute instanceof TLRPC.TL_documentAttributeSticker) { - if (attribute.alt != null && attribute.alt.contains(emoji)) { - return true; - } - break; - } - } - return false; - } - - private void addStickerToResult(TLRPC.Document document, Object parent) { - if (document == null) { - return; - } - String key = document.dc_id + "_" + document.id; - if (stickersMap != null && stickersMap.containsKey(key)) { - return; - } - if (stickers == null) { - stickers = new ArrayList<>(); - stickersMap = new HashMap<>(); - } - stickers.add(new StickerResult(document, parent)); - stickersMap.put(key, document); - } - - private void addStickersToResult(ArrayList documents, Object parent) { - if (documents == null || documents.isEmpty()) { - return; - } - for (int a = 0, size = documents.size(); a < size; a++) { - TLRPC.Document document = documents.get(a); - String key = document.dc_id + "_" + document.id; - if (stickersMap != null && stickersMap.containsKey(key)) { - continue; - } - if (stickers == null) { - stickers = new ArrayList<>(); - stickersMap = new HashMap<>(); - } - for (int b = 0, size2 = document.attributes.size(); b < size2; b++) { - TLRPC.DocumentAttribute attribute = document.attributes.get(b); - if (attribute instanceof TLRPC.TL_documentAttributeSticker) { - parent = attribute.stickerset; - break; - } - } - stickers.add(new StickerResult(document, parent)); - stickersMap.put(key, document); - } - } - public void hide() { - if (visible && (stickers != null || keywordResults != null && !keywordResults.isEmpty())) { + if (visible && keywordResults != null && !keywordResults.isEmpty()) { visible = false; delegate.needChangePanelVisibility(false); } @@ -200,10 +88,10 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement MediaDataController.getInstance(currentAccount).fetchNewEmojiKeywords(newLanguage); } lastSearchKeyboardLanguage = newLanguage; - String query = lastSticker; + String query = lastSearch; cancelEmojiSearch(); searchRunnable = () -> MediaDataController.getInstance(currentAccount).getEmojiSuggestions(lastSearchKeyboardLanguage, query, true, (param, alias) -> { - if (query.equals(lastSticker)) { + if (query.equals(lastSearch)) { if (!param.isEmpty()) { keywordResults = param; } @@ -218,7 +106,7 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement } } - public void loadStikersForEmoji(CharSequence emoji, boolean emojiOnly) { + public void searchEmojiByKeyword(CharSequence emoji) { boolean searchEmoji = emoji != null && emoji.length() > 0 && emoji.length() <= 14; String originalEmoji = ""; @@ -239,9 +127,8 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement } } } - lastSticker = emoji.toString().trim(); - stickersToLoad.clear(); - boolean isValidEmoji = searchEmoji && (Emoji.isValidEmoji(originalEmoji) || Emoji.isValidEmoji(lastSticker)); + lastSearch = emoji.toString().trim(); + boolean isValidEmoji = searchEmoji && (Emoji.isValidEmoji(originalEmoji) || Emoji.isValidEmoji(lastSearch)); if (isValidEmoji) { TLRPC.Document animatedSticker = MediaDataController.getInstance(currentAccount).getEmojiAnimatedSticker(emoji); if (animatedSticker != null) { @@ -252,166 +139,28 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement } } } - if (emojiOnly || SharedConfig.suggestStickers == 2 || !isValidEmoji) { - if (visible && (emojiOnly || SharedConfig.suggestStickers == 2 || keywordResults == null || keywordResults.isEmpty())) { - visible = false; - delegate.needChangePanelVisibility(false); - notifyDataSetChanged(); - } - if (!isValidEmoji) { - searchEmojiByKeyword(); - } - return; - } - cancelEmojiSearch(); - stickers = null; - stickersMap = null; - if (lastReqId != 0) { - ConnectionsManager.getInstance(currentAccount).cancelRequest(lastReqId, true); - lastReqId = 0; - } - boolean serverStickersOnly = MessagesController.getInstance(currentAccount).suggestStickersApiOnly; - delayLocalResults = false; - if (!serverStickersOnly) { - final ArrayList recentStickers = MediaDataController.getInstance(currentAccount).getRecentStickersNoCopy(MediaDataController.TYPE_IMAGE); - final ArrayList favsStickers = MediaDataController.getInstance(currentAccount).getRecentStickersNoCopy(MediaDataController.TYPE_FAVE); - int recentsAdded = 0; - for (int a = 0, size = Math.min(20, recentStickers.size()); a < size; a++) { - TLRPC.Document document = recentStickers.get(a); - if (isValidSticker(document, lastSticker)) { - addStickerToResult(document, "recent"); - recentsAdded++; - if (recentsAdded >= 5) { - break; - } - } - } - for (int a = 0, size = favsStickers.size(); a < size; a++) { - TLRPC.Document document = favsStickers.get(a); - if (isValidSticker(document, lastSticker)) { - addStickerToResult(document, "fav"); - } - } - - HashMap> allStickers = MediaDataController.getInstance(currentAccount).getAllStickers(); - ArrayList newStickers = allStickers != null ? allStickers.get(lastSticker) : null; - if (newStickers != null && !newStickers.isEmpty()) { - addStickersToResult(newStickers, null); - } - if (stickers != null) { - Collections.sort(stickers, new Comparator() { - private int getIndex(StickerResult result) { - for (int a = 0; a < favsStickers.size(); a++) { - if (favsStickers.get(a).id == result.sticker.id) { - return a + 2000000; - } - } - for (int a = 0; a < Math.min(20, recentStickers.size()); a++) { - if (recentStickers.get(a).id == result.sticker.id) { - return recentStickers.size() - a + 1000000; - } - } - return -1; - } - - @Override - public int compare(StickerResult lhs, StickerResult rhs) { - boolean isAnimated1 = MessageObject.isAnimatedStickerDocument(lhs.sticker, true); - boolean isAnimated2 = MessageObject.isAnimatedStickerDocument(rhs.sticker, true); - if (isAnimated1 == isAnimated2) { - int idx1 = getIndex(lhs); - int idx2 = getIndex(rhs); - if (idx1 > idx2) { - return -1; - } else if (idx1 < idx2) { - return 1; - } - return 0; - } else { - if (isAnimated1) { - return -1; - } else { - return 1; - } - } - } - }); - } - } - if (SharedConfig.suggestStickers == 0 || serverStickersOnly) { - searchServerStickers(lastSticker, originalEmoji); - } - - if (stickers != null && !stickers.isEmpty()) { - if (SharedConfig.suggestStickers == 0 && stickers.size() < 5) { - delayLocalResults = true; - delegate.needChangePanelVisibility(false); - visible = false; - } else { - checkStickerFilesExistAndDownload(); - boolean show = stickersToLoad.isEmpty(); - if (show) { - keywordResults = null; - } - delegate.needChangePanelVisibility(show); - visible = true; - } - notifyDataSetChanged(); - } else if (visible) { - delegate.needChangePanelVisibility(false); + if (visible && (keywordResults == null || keywordResults.isEmpty())) { visible = false; + delegate.needChangePanelVisibility(false); + notifyDataSetChanged(); + } + if (!isValidEmoji) { + searchEmojiByKeyword(); + } else { + clearSearch(); + delegate.needChangePanelVisibility(false); } } - private void searchServerStickers(final String emoji, final String originalEmoji) { - TLRPC.TL_messages_getStickers req = new TLRPC.TL_messages_getStickers(); - req.emoticon = originalEmoji; - req.hash = 0; - lastReqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - lastReqId = 0; - if (!emoji.equals(lastSticker) || !(response instanceof TLRPC.TL_messages_stickers)) { - return; - } - delayLocalResults = false; - TLRPC.TL_messages_stickers res = (TLRPC.TL_messages_stickers) response; - int oldCount = stickers != null ? stickers.size() : 0; - addStickersToResult(res.stickers, "sticker_search_" + emoji); - int newCount = stickers != null ? stickers.size() : 0; - if (!visible && stickers != null && !stickers.isEmpty()) { - checkStickerFilesExistAndDownload(); - boolean show = stickersToLoad.isEmpty(); - if (show) { - keywordResults = null; - } - delegate.needChangePanelVisibility(show); - visible = true; - } - if (oldCount != newCount) { - notifyDataSetChanged(); - } - })); - } - - public void clearStickers() { - if (delayLocalResults || lastReqId != 0) { - return; - } - if (stickersToLoad.isEmpty()) { - lastSticker = null; - stickers = null; - stickersMap = null; - } + public void clearSearch() { + lastSearch = null; keywordResults = null; notifyDataSetChanged(); - if (lastReqId != 0) { - ConnectionsManager.getInstance(currentAccount).cancelRequest(lastReqId, true); - lastReqId = 0; - } } public String getQuery() { - return lastSticker; + return lastSearch; } public boolean isShowingKeywords() { @@ -423,21 +172,14 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement if (keywordResults != null && !keywordResults.isEmpty()) { return keywordResults.size(); } - return !delayLocalResults && stickers != null ? stickers.size() : 0; + return 0; } public Object getItem(int i) { if (keywordResults != null && !keywordResults.isEmpty()) { return i >= 0 && i < keywordResults.size() ? keywordResults.get(i).emoji : null; } - return stickers != null && i >= 0 && i < stickers.size() ? stickers.get(i).sticker : null; - } - - public Object getItemParent(int i) { - if (keywordResults != null && !keywordResults.isEmpty()) { - return null; - } - return stickers != null && i >= 0 && i < stickers.size() ? stickers.get(i).parent : null; + return null; } @Override @@ -447,61 +189,27 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { - View view; - switch (viewType) { - case 0: - view = new StickerCell(mContext); - break; - case 1: - default: - view = new EmojiReplacementCell(mContext); - } - return new RecyclerListView.Holder(view); + return new RecyclerListView.Holder(new EmojiReplacementCell(mContext)); } @Override public int getItemViewType(int position) { - if (keywordResults != null && !keywordResults.isEmpty()) { - return 1; - } return 0; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { - switch (holder.getItemViewType()) { - case 0: { - int side = 0; - if (position == 0) { - if (stickers.size() == 1) { - side = 2; - } else { - side = -1; - } - } else if (position == stickers.size() - 1) { - side = 1; - } - StickerCell stickerCell = (StickerCell) holder.itemView; - StickerResult result = stickers.get(position); - stickerCell.setSticker(result.sticker, result.parent, side); - stickerCell.setClearsInputField(true); - break; - } - case 1: { - int side = 0; - if (position == 0) { - if (keywordResults.size() == 1) { - side = 2; - } else { - side = -1; - } - } else if (position == keywordResults.size() - 1) { - side = 1; - } - EmojiReplacementCell cell = (EmojiReplacementCell) holder.itemView; - cell.setEmoji(keywordResults.get(position).emoji, side); - break; + int side = 0; + if (position == 0) { + if (keywordResults.size() == 1) { + side = 2; + } else { + side = -1; } + } else if (position == keywordResults.size() - 1) { + side = 1; } + EmojiReplacementCell cell = (EmojiReplacementCell) holder.itemView; + cell.setEmoji(keywordResults.get(position).emoji, side); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java index f3464151a..f8b99b10d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java @@ -626,6 +626,8 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg } }; + private boolean closeAnimationInProgress; + private class WindowView extends FrameLayout { private final Paint blackPaint = new Paint(); @@ -640,7 +642,6 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg private int startedTrackingX; private int startedTrackingY; private VelocityTracker tracker; - private boolean closeAnimationInProgress; private float innerTranslationX; private float alpha; @@ -4491,7 +4492,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg } public void close(boolean byBackPress, boolean force) { - if (parentActivity == null || !isVisible || checkAnimation()) { + if (parentActivity == null || closeAnimationInProgress || !isVisible || checkAnimation()) { return; } if (fullscreenVideoContainer.getVisibility() == View.VISIBLE) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CancelAccountDeletionActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CancelAccountDeletionActivity.java index 8daa702c2..e189298ce 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CancelAccountDeletionActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CancelAccountDeletionActivity.java @@ -556,7 +556,7 @@ public class CancelAccountDeletionActivity extends BaseFragment { Intent mailer = new Intent(Intent.ACTION_SENDTO); mailer.setData(Uri.parse("mailto:")); - mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"sms@stel.com"}); + mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"reports@stel.com"}); mailer.putExtra(Intent.EXTRA_SUBJECT, "Android cancel account deletion issue " + version + " " + phone); mailer.putExtra(Intent.EXTRA_TEXT, "Phone: " + phone + "\nApp version: " + version + "\nOS version: SDK " + Build.VERSION.SDK_INT + "\nDevice Name: " + Build.MANUFACTURER + Build.MODEL + "\nLocale: " + Locale.getDefault() + "\nError: " + lastError); getContext().startActivity(Intent.createChooser(mailer, "Send email...")); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java index ea255ba44..2a87117f1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BotHelpCell.java @@ -207,12 +207,14 @@ public class BotHelpCell extends View { shadowDrawable.setBounds(x, y, width + x, height + y); shadowDrawable.draw(canvas); } + int w = AndroidUtilities.displaySize.x; int h = AndroidUtilities.displaySize.y; if (getParent() instanceof View) { View view = (View) getParent(); + w = view.getMeasuredWidth(); h = view.getMeasuredHeight(); } - Theme.chat_msgInMediaDrawable.setTop((int) getY(), h, false, false); + Theme.chat_msgInMediaDrawable.setTop((int) getY(), w, h, false, false); Theme.chat_msgInMediaDrawable.setBounds(x, y, width + x, height + y); Theme.chat_msgInMediaDrawable.draw(canvas); Theme.chat_msgTextPaint.setColor(Theme.getColor(Theme.key_chat_messageTextIn)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java index 8baeb13b2..f7abc9a11 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -68,6 +68,8 @@ import android.widget.Toast; import androidx.core.graphics.ColorUtils; +import com.google.android.exoplayer2.util.Log; + import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ChatObject; @@ -348,6 +350,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private boolean needNewVisiblePart; private boolean fullyDraw; + private int parentWidth; private int parentHeight; public float parentViewTopOffset; @@ -1518,7 +1521,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } private boolean checkInstantButtonMotionEvent(MotionEvent event) { - if (!drawInstantView || currentMessageObject.type == 0) { + if (!currentMessageObject.isSponsored() && (!drawInstantView || currentMessageObject.type == 0)) { return false; } int x = (int) event.getX(); @@ -2555,7 +2558,18 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate fullyDraw = draw; } - public void setVisiblePart(int position, int height, int parent, float parentOffset, float visibleTop, int parentH) { + public void setParentViewSize(int parentW, int parentH) { + parentWidth = parentW; + parentHeight = parentH; + backgroundHeight = parentH; + + if (currentMessageObject != null && (Theme.hasGradientService() && currentMessageObject.shouldDrawWithoutBackground() || drawSideButton != 0 || !botButtons.isEmpty()) || currentBackgroundDrawable != null && currentBackgroundDrawable.getGradientShader() != null) { + invalidate(); + } + } + + public void setVisiblePart(int position, int height, int parent, float parentOffset, float visibleTop, int parentW, int parentH) { + parentWidth = parentW; parentHeight = parentH; backgroundHeight = parentH; viewTop = visibleTop; @@ -3032,9 +3046,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (messageObject.checkLayout() || currentPosition != null && lastHeight != AndroidUtilities.displaySize.y) { currentMessageObject = null; } - boolean widthChanged = lastWidth != AndroidUtilities.displaySize.x; + boolean widthChanged = lastWidth != getParentWidth(); lastHeight = AndroidUtilities.displaySize.y; - lastWidth = AndroidUtilities.displaySize.x; + lastWidth = getParentWidth(); isRoundVideo = messageObject != null && messageObject.isRoundVideo(); TLRPC.Message newReply = messageObject.hasValidReplyMessageObject() ? messageObject.replyMessageObject.messageOwner : null; boolean messageIdChanged = currentMessageObject == null || currentMessageObject.getId() != messageObject.getId(); @@ -3272,7 +3286,6 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (currentMessageObject != null && currentMessageObject.textLayoutBlocks != null && currentMessageObject.textLayoutBlocks.size() > 1) { needNewVisiblePart = true; } - } boolean linked = false; @@ -3383,14 +3396,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { maxWidth = AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(122); } else { - maxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(122); + maxWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(122); } drawName = true; } else { if (AndroidUtilities.isTablet()) { maxWidth = AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(80); } else { - maxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(80); + maxWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(80); } drawName = isPinnedChat || messageObject.messageOwner.peer_id.channel_id != 0 && (!messageObject.isOutOwner() || messageObject.isSupergroup()) || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null; } @@ -3417,7 +3430,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate TLRPC.Document androidThemeDocument = null; TLRPC.TL_themeSettings androidThemeSettings = null; if (!drawInstantView) { - if ("telegram_voicechat".equals(webpageType)) { + if ("telegram_livestream".equals(webpageType)) { + drawInstantView = true; + drawInstantViewType = 11; + } else if ("telegram_voicechat".equals(webpageType)) { drawInstantView = true; drawInstantViewType = 9; } else if ("telegram_channel".equals(webpageType)) { @@ -3583,9 +3599,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } else { if (drawAvatar) { - linkPreviewMaxWidth = AndroidUtilities.displaySize.x - AndroidUtilities.dp(132); + linkPreviewMaxWidth = getParentWidth() - AndroidUtilities.dp(132); } else { - linkPreviewMaxWidth = AndroidUtilities.displaySize.x - AndroidUtilities.dp(80); + linkPreviewMaxWidth = getParentWidth() - AndroidUtilities.dp(80); } } if (drawSideButton != 0) { @@ -3625,8 +3641,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } boolean isSmallImageType = "app".equals(type) || "profile".equals(type) || "article".equals(type) || "telegram_bot".equals(type) || "telegram_user".equals(type) || "telegram_channel".equals(type) || "telegram_megagroup".equals(type) || "telegram_voicechat".equals(type); - smallImage = !slideshow && (!drawInstantView || drawInstantViewType == 9) && document == null && isSmallImageType; - isSmallImage = !slideshow && (!drawInstantView || drawInstantViewType == 9) && document == null && description != null && type != null && isSmallImageType && currentMessageObject.photoThumbs != null; + smallImage = !slideshow && (!drawInstantView || drawInstantViewType == 9 || drawInstantViewType == 11) && document == null && isSmallImageType; + isSmallImage = !slideshow && (!drawInstantView || drawInstantViewType == 9 || drawInstantViewType == 11) && document == null && description != null && type != null && isSmallImageType && currentMessageObject.photoThumbs != null; } else if (hasInvoicePreview) { TLRPC.TL_messageMediaInvoice invoice = (TLRPC.TL_messageMediaInvoice) messageObject.messageOwner.media; site_name = messageObject.messageOwner.media.title; @@ -3658,9 +3674,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate isSmallImage = false; smallImage = false; } - if (drawInstantViewType == 9) { + if (drawInstantViewType == 11) { + site_name = LocaleController.getString("VoipChannelVoiceChat", R.string.VoipChannelVoiceChat); + } else if (drawInstantViewType == 9) { site_name = LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat); - TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) messageObject.messageOwner.media.webpage; } else if (drawInstantViewType == 6) { site_name = LocaleController.getString("ChatBackground", R.string.ChatBackground); } else if ("telegram_theme".equals(webpageType)) { @@ -4001,7 +4018,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { maxChildWidth = Math.max(maxChildWidth, Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 52 : 0), AndroidUtilities.dp(220)) - AndroidUtilities.dp(30) + additinalWidth); } else { - maxChildWidth = Math.max(maxChildWidth, Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 52 : 0), AndroidUtilities.dp(220)) - AndroidUtilities.dp(30) + additinalWidth); + maxChildWidth = Math.max(maxChildWidth, Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 52 : 0), AndroidUtilities.dp(220)) - AndroidUtilities.dp(30) + additinalWidth); } calcBackgroundWidth(maxWidth, timeMore, maxChildWidth); } else if (MessageObject.isMusicDocument(document)) { @@ -4072,7 +4089,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { maxPhotoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.5f); } else { - maxPhotoWidth = (int) (AndroidUtilities.displaySize.x * 0.5f); + maxPhotoWidth = (int) (getParentWidth() * 0.5f); } } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND) { maxPhotoWidth = AndroidUtilities.roundMessageSize; @@ -4314,7 +4331,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); } else { - backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); + backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); } availableTimeWidth = backgroundWidth - AndroidUtilities.dp(31); @@ -4384,7 +4401,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); } else { - backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); + backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); } availableTimeWidth = backgroundWidth - AndroidUtilities.dp(31); @@ -4463,7 +4480,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); } else { - backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); + backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); } createDocumentLayout(backgroundWidth, messageObject); @@ -4478,7 +4495,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); } else { - backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); + backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); } createDocumentLayout(backgroundWidth, messageObject); @@ -4768,7 +4785,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } } else { - drawForwardedName = messageObject.messageOwner.fwd_from != null && !messageObject.isAnyKindOfSticker(); + drawForwardedName = messageObject.messageOwner.fwd_from != null && !(messageObject.isAnyKindOfSticker() && messageObject.isDice()); if (!messageObject.isAnyKindOfSticker() && messageObject.type != MessageObject.TYPE_ROUND_VIDEO) { drawName = (messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null) && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_TOP) != 0); } @@ -4799,7 +4816,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(300)); } else { - backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(300)); + backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(300)); } if (checkNeedDrawShareButton(messageObject)) { backgroundWidth -= AndroidUtilities.dp(20); @@ -4896,7 +4913,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37)); } else { - backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37)); + backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37)); } backgroundWidth -= AndroidUtilities.dp(4); if (checkNeedDrawShareButton(messageObject)) { @@ -4946,7 +4963,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37)); } else { - backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37)); + backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37)); } backgroundWidth -= AndroidUtilities.dp(4); if (checkNeedDrawShareButton(messageObject)) { @@ -4980,7 +4997,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37)); } else { - backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37)); + backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37)); } backgroundWidth -= AndroidUtilities.dp(4); if (checkNeedDrawShareButton(messageObject)) { @@ -5042,7 +5059,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { maxHeight = maxWidth = AndroidUtilities.getMinTabletSide() * 0.4f; } else { - maxHeight = maxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f; + maxHeight = maxWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y) * 0.5f; } String filter; if (messageObject.isAnimatedEmoji() || messageObject.isDice()) { @@ -5130,10 +5147,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate photoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.7f); } else { if (currentPhotoObject != null && (messageObject.type == MessageObject.TYPE_PHOTO || messageObject.type == MessageObject.TYPE_VIDEO || messageObject.type == 8) && currentPhotoObject.w >= currentPhotoObject.h) { - photoWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(64 + (checkNeedDrawShareButton(messageObject) ? 10 : 0)); + photoWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(64 + (checkNeedDrawShareButton(messageObject) ? 10 : 0)); useFullWidth = true; } else { - photoWidth = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.7f); + photoWidth = (int) (Math.min(getParentWidth(), AndroidUtilities.displaySize.y) * 0.7f); } } } @@ -5289,14 +5306,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { w = h = (int) (AndroidUtilities.getMinTabletSide() * 0.5f); } else { - w = h = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f); + w = h = (int) (Math.min(getParentWidth(), AndroidUtilities.displaySize.y) * 0.5f); } } int widthForCaption = 0; boolean fixPhotoWidth = false; if (currentMessagesGroup != null) { - float maxHeight = Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f; + float maxHeight = Math.max(getParentWidth(), AndroidUtilities.displaySize.y) * 0.5f; int dWidth = getGroupPhotosWidth(); w = (int) Math.ceil(currentPosition.pw / 1000.0f * dWidth); if (currentPosition.minY != 0 && (messageObject.isOutOwner() && (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0 || !messageObject.isOutOwner() && (currentPosition.flags & MessageObject.POSITION_FLAG_RIGHT) != 0)) { @@ -5407,7 +5424,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { minCaptionWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.65f); } else { - minCaptionWidth = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.65f); + minCaptionWidth = (int) (Math.min(getParentWidth(), AndroidUtilities.displaySize.y) * 0.65f); } if (!messageObject.needDrawBluredPreview() && currentCaption != null && photoWidth < minCaptionWidth) { widthForCaption = minCaptionWidth; @@ -5801,6 +5818,19 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } + if (messageObject.isSponsored()) { + drawInstantView = true; + drawInstantViewType = 1; + long id = MessageObject.getPeerId(messageObject.messageOwner.from_id); + if (id > 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser((int) id); //TODO long + if (user != null && user.bot) { + drawInstantViewType = 10; + } + } + createInstantViewButton(); + } + botButtons.clear(); if (messageIdChanged) { botButtonsByData.clear(); @@ -5823,7 +5853,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { maxButtonWidth += AndroidUtilities.getMinTabletSide(); } else { - maxButtonWidth += Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(5); + maxButtonWidth += Math.min(getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(5); } widthForButtons = Math.max(backgroundWidth, Math.min(messageObject.wantedBotKeyboardWidth, maxButtonWidth)); } @@ -6804,6 +6834,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate instantWidth = AndroidUtilities.dp(12 + 9 + 12); if (drawInstantViewType == 1) { str = LocaleController.getString("OpenChannel", R.string.OpenChannel); + } else if (drawInstantViewType == 10) { + str = LocaleController.getString("OpenBot", R.string.OpenBot); } else if (drawInstantViewType == 2) { str = LocaleController.getString("OpenGroup", R.string.OpenGroup); } else if (drawInstantViewType == 3) { @@ -6820,7 +6852,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else { str = LocaleController.getString("PollSubmitVotes", R.string.PollSubmitVotes); } - } else if (drawInstantViewType == 9) { + } else if (drawInstantViewType == 9 || drawInstantViewType == 11) { TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) currentMessageObject.messageOwner.media.webpage; if (webPage != null && webPage.url.contains("voicechat=")) { str = LocaleController.getString("VoipGroupJoinAsSpeaker", R.string.VoipGroupJoinAsSpeaker); @@ -6877,14 +6909,18 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } private int getGroupPhotosWidth() { + int width = getParentWidth(); + if (currentMessageObject != null && currentMessageObject.preview) { + width = parentWidth; + } if (!AndroidUtilities.isInMultiwindow && AndroidUtilities.isTablet() && (!AndroidUtilities.isSmallTablet() || getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)) { - int leftWidth = AndroidUtilities.displaySize.x / 100 * 35; + int leftWidth = width / 100 * 35; if (leftWidth < AndroidUtilities.dp(320)) { leftWidth = AndroidUtilities.dp(320); } - return AndroidUtilities.displaySize.x - leftWidth; + return width - leftWidth; } else { - return AndroidUtilities.displaySize.x; + return width; } } @@ -7309,7 +7345,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private void drawContent(Canvas canvas) { if (needNewVisiblePart && currentMessageObject.type == 0) { getLocalVisibleRect(scrollRect); - setVisiblePart(scrollRect.top, scrollRect.bottom - scrollRect.top, parentHeight, parentViewTopOffset, viewTop, backgroundHeight); + setVisiblePart(scrollRect.top, scrollRect.bottom - scrollRect.top, parentHeight, parentViewTopOffset, viewTop, parentWidth, backgroundHeight); needNewVisiblePart = false; } @@ -7387,7 +7423,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate textX += diff - getExtraTimeX(); } } - if (!enterTransitionInPorgress) { + if (!enterTransitionInPorgress && currentMessageObject != null && !currentMessageObject.preview) { if (transitionParams.animateChangeProgress != 1.0f && transitionParams.animateMessageText) { canvas.save(); if (currentBackgroundDrawable != null) { @@ -7687,7 +7723,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (captionLayout != null) { updateCaptionLayout(); } - if ((currentPosition == null || currentMessagesGroup != null && currentMessagesGroup.isDocuments) && !transitionParams.transformGroupToSingleMessage && !(enterTransitionInPorgress && currentMessageObject.isVoice())) { + if (!currentMessageObject.preview && (currentPosition == null || currentMessagesGroup != null && currentMessagesGroup.isDocuments) && !transitionParams.transformGroupToSingleMessage && !(enterTransitionInPorgress && currentMessageObject.isVoice())) { drawCaptionLayout(canvas, false, 1f); } @@ -7864,7 +7900,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } public void drawLinkPreview(Canvas canvas, float alpha) { - if (!hasLinkPreview && !hasGamePreview && !hasInvoicePreview) { + if (!currentMessageObject.isSponsored() && !hasLinkPreview && !hasGamePreview && !hasInvoicePreview) { return; } int startY; @@ -7875,6 +7911,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else if (hasInvoicePreview) { startY = AndroidUtilities.dp(14) + namesOffset; linkX = unmovedTextX + AndroidUtilities.dp(1); + } else if (currentMessageObject.isSponsored()) { + startY = textY + currentMessageObject.textHeight - AndroidUtilities.dp(2); + linkX = unmovedTextX + AndroidUtilities.dp(1); } else { startY = textY + currentMessageObject.textHeight + AndroidUtilities.dp(8); linkX = unmovedTextX + AndroidUtilities.dp(1); @@ -7882,7 +7921,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate int linkPreviewY = startY; int smallImageStartY = 0; - if (!hasInvoicePreview) { + if (!hasInvoicePreview && !currentMessageObject.isSponsored()) { Theme.chat_replyLinePaint.setColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outPreviewLine : Theme.key_chat_inPreviewLine)); if (alpha != 1f) { Theme.chat_replyLinePaint.setAlpha((int) (alpha * Theme.chat_replyLinePaint.getAlpha())); @@ -8048,7 +8087,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate linkPreviewY += descriptionLayout.getLineBottom(descriptionLayout.getLineCount() - 1); } - if (drawPhotoImage && (!drawInstantView || drawInstantViewType == 9)) { + if (drawPhotoImage && (!drawInstantView || drawInstantViewType == 9 || drawInstantViewType == 11)) { if (linkPreviewY != startY) { linkPreviewY += AndroidUtilities.dp(2); } @@ -8267,8 +8306,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate canvas.restore(); } + @SuppressLint("Range") public void drawMessageText(Canvas canvas, ArrayList textLayoutBlocks, boolean origin, float alpha, boolean drawOnlyText) { - if (textLayoutBlocks == null || textLayoutBlocks.isEmpty()) { + if (textLayoutBlocks == null || textLayoutBlocks.isEmpty() || alpha == 0) { return; } int firstVisibleBlockNum; @@ -8285,12 +8325,19 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate lastVisibleBlockNum = textLayoutBlocks.size(); } + float textY = this.textY; + if (transitionParams.animateText) { + textY = transitionParams.animateFromTextY * (1f - transitionParams.animateChangeProgress) + this.textY * transitionParams.animateChangeProgress; + } + textY += transitionYOffsetForDrawables; if (firstVisibleBlockNum >= 0) { int restore = Integer.MIN_VALUE; - int oldAlpha = -1; - int oldLinkAlpha = -1; + int oldAlpha = 0; + int oldLinkAlpha = 0; + boolean needRestoreColor = false; if (alpha != 1.0f) { if (drawOnlyText) { + needRestoreColor = true; oldAlpha = Theme.chat_msgTextPaint.getAlpha(); oldLinkAlpha = Color.alpha(Theme.chat_msgTextPaint.linkColor); Theme.chat_msgTextPaint.setAlpha((int) (oldAlpha * alpha)); @@ -8344,7 +8391,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } canvas.restore(); } - if (oldAlpha >= 0) { + if (needRestoreColor) { Theme.chat_msgTextPaint.setAlpha(oldAlpha); Theme.chat_msgTextPaint.linkColor = ColorUtils.setAlphaComponent(Theme.chat_msgTextPaint.linkColor, oldLinkAlpha); } @@ -8478,9 +8525,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } else { if (isChat && !isThreadPost && !currentMessageObject.isOutOwner() && currentMessageObject.needDrawAvatar()) { - maxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(42); + maxWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(42); } else { - maxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y); + maxWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y); } } if (isPlayingRound) { @@ -8494,7 +8541,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (AndroidUtilities.isTablet()) { dWidth = AndroidUtilities.getMinTabletSide(); } else { - dWidth = AndroidUtilities.displaySize.x; + dWidth = getParentWidth(); } int firstLineWidth = 0; for (int a = 0; a < currentMessagesGroup.posArray.size(); a++) { @@ -9445,7 +9492,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } } - if (currentMessageObject.scheduled && currentMessageObject.messageOwner.date == 0x7FFFFFFE) { + if (currentMessageObject.isSponsored()) { + timeString = LocaleController.getString("SponsoredMessage", R.string.SponsoredMessage); + } else if (currentMessageObject.scheduled && currentMessageObject.messageOwner.date == 0x7FFFFFFE) { timeString = ""; } else if (edited) { timeString = LocaleController.getString("EditedMessage", R.string.EditedMessage) + " " + LocaleController.getInstance().formatterDay.format((long) (messageObject.messageOwner.date) * 1000); @@ -9583,7 +9632,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } private void setMessageObjectInternal(MessageObject messageObject) { - if (((messageObject.messageOwner.flags & TLRPC.MESSAGE_FLAG_HAS_VIEWS) != 0 || messageObject.messageOwner.replies != null) && !currentMessageObject.scheduled) { + if (((messageObject.messageOwner.flags & TLRPC.MESSAGE_FLAG_HAS_VIEWS) != 0 || messageObject.messageOwner.replies != null) && !currentMessageObject.scheduled && !currentMessageObject.isSponsored()) { if (!currentMessageObject.viewsReloaded) { MessagesController.getInstance(currentAccount).addToViewsQueue(currentMessageObject); currentMessageObject.viewsReloaded = true; @@ -9657,7 +9706,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else if (isMegagroup && currentChat != null && currentMessageObject.isForwardedChannelPost()) { adminString = LocaleController.getString("DiscussChannel", R.string.DiscussChannel); adminWidth = (int) Math.ceil(Theme.chat_adminPaint.measureText(adminString)); - nameWidth -= adminWidth; + nameWidth -= adminWidth; //TODO } else if (currentUser != null && !currentMessageObject.isOutOwner() && !currentMessageObject.isAnyKindOfSticker() && currentMessageObject.type != 5 && delegate != null && (adminLabel = delegate.getAdminRank(currentUser.id)) != null) { if (adminLabel.length() == 0) { adminLabel = LocaleController.getString("ChatAdmin", R.string.ChatAdmin); @@ -9808,7 +9857,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } forwardNameOffsetX[0] = forwardedNameLayout[0].getLineLeft(0); forwardNameOffsetX[1] = forwardedNameLayout[1].getLineLeft(0); - if (messageObject.type != 5) { + if (messageObject.type != 5 && !messageObject.isAnyKindOfSticker()) { namesOffset += AndroidUtilities.dp(36); } } catch (Exception e) { @@ -10076,7 +10125,13 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate public void drawCheckBox(Canvas canvas) { if (currentMessageObject != null && !currentMessageObject.isSending() && !currentMessageObject.isSendError() && checkBox != null && (checkBoxVisible || checkBoxAnimationInProgress) && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) != 0 && (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0)) { canvas.save(); - canvas.translate(0, getTop()); + float y = getY(); + if (currentMessagesGroup != null && currentMessagesGroup.messages.size() > 1) { + y = getTop() + currentMessagesGroup.transitionParams.offsetTop - getTranslationY(); + } else { + y += transitionParams.deltaTop; + } + canvas.translate(0, y + transitionYOffsetForDrawables); checkBox.draw(canvas); canvas.restore(); } @@ -10091,20 +10146,41 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate if (drawable == null) { continue; } + int w = parentWidth; int h = parentHeight; if (h == 0) { + w = getParentWidth(); h = AndroidUtilities.displaySize.y; if (getParent() instanceof View) { View view = (View) getParent(); + w = view.getMeasuredWidth(); h = view.getMeasuredHeight(); } } - drawable.setTop((int) ((fromParent ? getY() : getTop()) + parentViewTopOffset), h, pinnedTop, pinnedBottom || transitionParams.changePinnedBottomProgress != 1); + drawable.setTop((int) ((fromParent ? getY() : getTop()) + parentViewTopOffset), w, h, (int) parentViewTopOffset, pinnedTop, pinnedBottom || transitionParams.changePinnedBottomProgress != 1); } } + public void setBackgroundTopY(int offset) { + Theme.MessageDrawable drawable = currentBackgroundDrawable; + int w = parentWidth; + int h = parentHeight; + if (h == 0) { + w = getParentWidth(); + h = AndroidUtilities.displaySize.y; + if (getParent() instanceof View) { + View view = (View) getParent(); + w = view.getMeasuredWidth(); + h = view.getMeasuredHeight(); + } + } + drawable.setTop((int) (getTop() + parentViewTopOffset + offset), w, h, (int) parentViewTopOffset, pinnedTop, pinnedBottom || transitionParams.changePinnedBottomProgress != 1); + } + + float transitionYOffsetForDrawables; public void setDrawableBoundsInner(Drawable drawable, int x, int y, int w, int h) { if (drawable != null) { + transitionYOffsetForDrawables = (y + h + transitionParams.deltaBottom) - ((int) (y + h + transitionParams.deltaBottom)); drawable.setBounds((int) (x + transitionParams.deltaLeft), (int) (y + transitionParams.deltaTop), (int) (x + w + transitionParams.deltaRight), (int) (y + h + transitionParams.deltaBottom)); } } @@ -10384,6 +10460,12 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } + boolean needRestore = false; + if (transitionYOffsetForDrawables != 0) { + needRestore = true; + canvas.save(); + canvas.translate(0, transitionYOffsetForDrawables); + } if (drawBackground && currentBackgroundDrawable != null && (currentPosition == null || isDrawSelectionBackground() && (currentMessageObject.isMusic() || currentMessageObject.isDocument())) && !(enterTransitionInPorgress && !currentMessageObject.isVoice())) { if (isHighlightedAnimated) { currentBackgroundDrawable.setAlpha((int) (255 * alphaInternal)); @@ -10433,15 +10515,18 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate drawable.setBounds(rect.left, rect.top, rect.right + AndroidUtilities.dp(6), rect.bottom); canvas.save(); canvas.clipRect(rect.right - AndroidUtilities.dp(12), rect.bottom - AndroidUtilities.dp(16), rect.right + AndroidUtilities.dp(12), rect.bottom); + int w = parentWidth; int h = parentHeight; if (h == 0) { + w = getParentWidth(); h = AndroidUtilities.displaySize.y; if (getParent() instanceof View) { View view = (View) getParent(); + w = view.getMeasuredWidth(); h = view.getMeasuredHeight(); } } - drawable.setTop((int) (getY() + parentViewTopOffset), h, pinnedTop, pinnedBottom); + drawable.setTop((int) (getY() + parentViewTopOffset), w, h, (int) parentViewTopOffset, pinnedTop, pinnedBottom); float alpha = !mediaBackground && !pinnedBottom ? transitionParams.changePinnedBottomProgress : (1f - transitionParams.changePinnedBottomProgress); drawable.setAlpha((int) (255 * alpha)); drawable.draw(canvas); @@ -10466,6 +10551,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } } + if (needRestore) { + canvas.restore(); + } if (isHighlightedAnimated) { long newTime = System.currentTimeMillis(); long dt = Math.abs(newTime - lastHighlightProgressTime); @@ -10802,7 +10890,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate return getBackgroundDrawableTop() + layoutHeight - offsetBottom + additionalBottom; } - public void drawBackground(Canvas canvas, int left, int top, int right, int bottom, boolean pinnedTop, boolean pinnedBottom, boolean selected) { + public void drawBackground(Canvas canvas, int left, int top, int right, int bottom, boolean pinnedTop, boolean pinnedBottom, boolean selected, int keyboardHeight) { if (currentMessageObject.isOutOwner()) { if (!mediaBackground && !pinnedBottom) { currentBackgroundDrawable = selected ? Theme.chat_msgOutSelectedDrawable : Theme.chat_msgOutDrawable; @@ -10817,17 +10905,20 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } + int w = parentWidth; int h = parentHeight; if (h == 0) { + w = getParentWidth(); h = AndroidUtilities.displaySize.y; if (getParent() instanceof View) { View view = (View) getParent(); + w = view.getMeasuredWidth(); h = view.getMeasuredHeight(); } } if (currentBackgroundDrawable != null) { - currentBackgroundDrawable.setTop(0, h, pinnedTop, pinnedBottom); + currentBackgroundDrawable.setTop(keyboardHeight, w, h, (int) parentViewTopOffset, pinnedTop, pinnedBottom); Drawable currentBackgroundShadowDrawable = currentBackgroundDrawable.getShadowDrawable(); if (currentBackgroundShadowDrawable != null) { currentBackgroundShadowDrawable.setAlpha((int) (getAlpha() * 255)); @@ -11006,24 +11097,62 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } } - if (drawForwardedName && forwardedNameLayout[0] != null && forwardedNameLayout[1] != null && (currentPosition == null || currentPosition.minY == 0 && currentPosition.minX == 0)) { - if (currentMessageObject.type == MessageObject.TYPE_ROUND_VIDEO) { + boolean drawForwardedNameLocal = drawForwardedName; + StaticLayout[] forwardedNameLayoutLocal = forwardedNameLayout; + float animatingAlpha = 1f; + int forwardedNameWidthLocal = forwardedNameWidth; + if (transitionParams.animateForwardedLayout) { + if (!currentMessageObject.needDrawForwarded()) { + drawForwardedNameLocal = true; + forwardedNameLayoutLocal = transitionParams.animatingForwardedNameLayout; + animatingAlpha = 1f - transitionParams.animateChangeProgress; + forwardedNameWidthLocal = transitionParams.animateForwardNameWidth; + } else { + animatingAlpha = transitionParams.animateChangeProgress; + } + } + + float forwardNameXLocal; + if (drawForwardedNameLocal && forwardedNameLayoutLocal[0] != null && forwardedNameLayoutLocal[1] != null && (currentPosition == null || currentPosition.minY == 0 && currentPosition.minX == 0)) { + if (currentMessageObject.type == MessageObject.TYPE_ROUND_VIDEO || currentMessageObject.isAnyKindOfSticker()) { Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_stickerReplyNameText)); - if (currentMessageObject.isOutOwner()) { - forwardNameX = AndroidUtilities.dp(23); + if (currentMessageObject.needDrawForwarded()) { + if (currentMessageObject.isOutOwner()) { + forwardNameXLocal = forwardNameX = AndroidUtilities.dp(23); + } else { + forwardNameXLocal = forwardNameX = backgroundDrawableLeft + backgroundDrawableRight + AndroidUtilities.dp(17); + } } else { - forwardNameX = backgroundDrawableLeft + backgroundDrawableRight + AndroidUtilities.dp(17); + forwardNameXLocal = transitionParams.animateForwardNameX; + } + if (currentMessageObject.isOutOwner() && currentMessageObject.type == MessageObject.TYPE_ROUND_VIDEO && transitionParams.animatePlayingRound || isPlayingRound) { + forwardNameXLocal -= AndroidUtilities.dp (78) * (isPlayingRound ? transitionParams.animateChangeProgress : (1f - transitionParams.animateChangeProgress)); } forwardNameY = AndroidUtilities.dp(12); - int backWidth = forwardedNameWidth + AndroidUtilities.dp(14); + int backWidth = forwardedNameWidthLocal + AndroidUtilities.dp(14); - rect.set((int) forwardNameX - AndroidUtilities.dp(7), forwardNameY - AndroidUtilities.dp(6), (int) forwardNameX - AndroidUtilities.dp(7) + backWidth, forwardNameY + AndroidUtilities.dp(38)); + rect.set((int) forwardNameXLocal - AndroidUtilities.dp(7), forwardNameY - AndroidUtilities.dp(6), (int) forwardNameXLocal - AndroidUtilities.dp(7) + backWidth, forwardNameY + AndroidUtilities.dp(38)); Theme.applyServiceShaderMatrix(getMeasuredWidth(), backgroundHeight, getX(), viewTop); + int oldAlpha1 = -1, oldAlpha2 = -1; + if (animatingAlpha != 1f) { + oldAlpha1 = Theme.chat_actionBackgroundPaint.getAlpha(); + Theme.chat_actionBackgroundPaint.setAlpha((int) (oldAlpha1 * animatingAlpha)); + } canvas.drawRoundRect(rect, AndroidUtilities.dp(6), AndroidUtilities.dp(6), Theme.chat_actionBackgroundPaint); if (Theme.hasGradientService()) { + if (animatingAlpha != 1f) { + oldAlpha2 = Theme.chat_actionBackgroundGradientDarkenPaint.getAlpha(); + Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha((int) (oldAlpha2 * animatingAlpha)); + } canvas.drawRoundRect(rect, AndroidUtilities.dp(6), AndroidUtilities.dp(6), Theme.chat_actionBackgroundGradientDarkenPaint); } + if (oldAlpha1 >= 0) { + Theme.chat_actionBackgroundPaint.setAlpha(oldAlpha1); + } + if (oldAlpha2 >= 0) { + Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha(oldAlpha2); + } } else { forwardNameY = AndroidUtilities.dp(10 + (drawNameLayout ? 19 : 0)); if (currentMessageObject.isOutOwner()) { @@ -11032,24 +11161,62 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else { Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_outForwardedNameText)); } - forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(11) + getExtraTextX(); + if (currentMessageObject.needDrawForwarded()) { + forwardNameXLocal = forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(11) + getExtraTextX(); + } else { + forwardNameXLocal = transitionParams.animateForwardNameX; + } } else { if (hasPsaHint) { Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_inPsaNameText)); } else { Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_inForwardedNameText)); } - if (mediaBackground) { - forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(11) + getExtraTextX(); + if (currentMessageObject.needDrawForwarded()) { + if (mediaBackground) { + forwardNameXLocal = forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(11) + getExtraTextX(); + } else { + forwardNameXLocal = forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(drawPinnedBottom ? 11 : 17) + getExtraTextX(); + } } else { - forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(drawPinnedBottom ? 11 : 17) + getExtraTextX(); + forwardNameXLocal = transitionParams.animateForwardNameX; } } } + boolean clipContent = false; + if (transitionParams.animateForwardedLayout) { + if (currentBackgroundDrawable != null && currentMessagesGroup == null && currentMessageObject.type != MessageObject.TYPE_ROUND_VIDEO && !currentMessageObject.isAnyKindOfSticker()) { + Rect r = currentBackgroundDrawable.getBounds(); + canvas.save(); + if (currentMessageObject.isOutOwner() && !mediaBackground && !pinnedBottom) { + canvas.clipRect( + r.left + AndroidUtilities.dp(4), r.top + AndroidUtilities.dp(4), + r.right - AndroidUtilities.dp(10), r.bottom - AndroidUtilities.dp(4) + ); + } else { + canvas.clipRect( + r.left + AndroidUtilities.dp(4), r.top + AndroidUtilities.dp(4), + r.right - AndroidUtilities.dp(4), r.bottom - AndroidUtilities.dp(4) + ); + } + clipContent = true; + } + } + for (int a = 0; a < 2; a++) { canvas.save(); - canvas.translate(forwardNameX - forwardNameOffsetX[a], forwardNameY + AndroidUtilities.dp(16) * a); - forwardedNameLayout[a].draw(canvas); + canvas.translate(forwardNameXLocal - forwardNameOffsetX[a], forwardNameY + AndroidUtilities.dp(16) * a); + if (animatingAlpha != 1f) { + int oldAlpha = forwardedNameLayoutLocal[a].getPaint().getAlpha(); + forwardedNameLayoutLocal[a].getPaint().setAlpha((int) (oldAlpha * animatingAlpha)); + forwardedNameLayoutLocal[a].draw(canvas); + forwardedNameLayoutLocal[a].getPaint().setAlpha(oldAlpha); + } else { + forwardedNameLayoutLocal[a].draw(canvas); + } + canvas.restore(); + } + if (clipContent) { canvas.restore(); } @@ -11209,7 +11376,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate checkBox.onAttachedToWindow(); } } - if (visible && mediaCheckBox == null && currentMessagesGroup != null && currentMessagesGroup.messages.size() > 1) { + if (visible && mediaCheckBox == null && ((currentMessagesGroup != null && currentMessagesGroup.messages.size() > 1) || (groupedMessagesToSet != null && groupedMessagesToSet.messages.size() > 1))) { mediaCheckBox = new CheckBoxBase(this, 21); mediaCheckBox.setUseDefaultCheck(true); if (attachedToWindow) { @@ -11780,7 +11947,11 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate canvas.restore(); Theme.chat_timePaint.setAlpha(255); } else { - timeYOffset = -(drawCommentButton ? AndroidUtilities.dp(43) : 0); + if (currentMessageObject.isSponsored()) { + timeYOffset = -AndroidUtilities.dp(48); + } else { + timeYOffset = -(drawCommentButton ? AndroidUtilities.dp(43) : 0); + } float additionalX = -timeLayout.getLineLeft(0); if (ChatObject.isChannel(currentChat) && !currentChat.megagroup || (currentMessageObject.messageOwner.flags & TLRPC.MESSAGE_FLAG_HAS_VIEWS) != 0 || (repliesLayout != null || transitionParams.animateReplies) || (isPinned || transitionParams.animatePinned)) { additionalX += this.timeWidth - timeLayout.getLineWidth(0); @@ -11870,6 +12041,12 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate boolean drawClock = (currentStatus & 4) != 0; boolean drawError = (currentStatus & 8) != 0; boolean isBroadcast = (currentStatus & 16) != 0; + boolean needRestore = false; + if (transitionYOffsetForDrawables != 0) { + needRestore = true; + canvas.save(); + canvas.translate(0, transitionYOffsetForDrawables); + } if (statusDrawableAnimationInProgress) { boolean outDrawCheck1 = (animateFromStatusDrawableParams & 1) != 0; boolean outDrawCheck2 = (animateFromStatusDrawableParams & 2) != 0; @@ -11885,6 +12062,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate } else { drawStatusDrawable(canvas, drawCheck1, drawCheck2, drawClock, drawError, isBroadcast, alpha, bigRadius, timeYOffset, layoutHeight, 1, false, drawSelectionBackground); } + if (needRestore) { + canvas.restore(); + } transitionParams.lastStatusDrawableParams = transitionParams.createStatusDrawableParams(); if (fromParent && drawClock && getParent() != null) { ((View) getParent()).invalidate(); @@ -12787,6 +12967,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate Theme.chat_livePaint.setColor(color2); Theme.chat_locationAddressPaint.setColor(color2); + canvas.save(); + if (transitionParams.animateForwardedLayout) { + float y = namesOffset * transitionParams.animateChangeProgress + transitionParams.animateForwardedNamesOffset * (1f - transitionParams.animateChangeProgress); + if (currentMessageObject.needDrawForwarded()) { + y -= namesOffset; + } + canvas.translate(0, y); + } int x; if (currentMessageObject.isOutOwner()) { x = layoutWidth - backgroundWidth + AndroidUtilities.dp(11); @@ -13060,6 +13248,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate canvas.restore(); } updatePollAnimations(dt); + canvas.restore(); } else if (currentMessageObject.type == 12) { if (currentMessageObject.isOutOwner()) { Theme.chat_contactNamePaint.setColor(Theme.getColor(Theme.key_chat_outContactNameText)); @@ -14236,6 +14425,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate return isRoundVideo && isPlayingRound; } + public int getParentWidth() { + MessageObject object = currentMessageObject == null ? messageObjectToSet : currentMessageObject; + if (object != null && object.preview && parentWidth > 0) { + return parentWidth; + } + return AndroidUtilities.displaySize.x; + } + public class TransitionParams { public float lastDrawingImageX, lastDrawingImageY, lastDrawingImageW, lastDrawingImageH; @@ -14354,8 +14551,24 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate private StaticLayout lastTimeLayout; private boolean lastIsPlayingRound; public boolean animatePlayingRound; + public boolean animateText; + + public float lastDrawingTextY; + public float lastDrawingTextX; + + public float animateFromTextY; public int lastTopOffset; + public boolean animateForwardedLayout; + public int animateForwardedNamesOffset; + public int lastForwardedNamesOffset; + public boolean lastDrawnForwardedName; + public StaticLayout[] lastDrawnForwardedNameLayout = new StaticLayout[2]; + public StaticLayout[] animatingForwardedNameLayout = new StaticLayout[2]; + float animateForwardNameX; + float lastForwardNameX; + int animateForwardNameWidth; + int lastForwardNameWidth; public void recordDrawingState() { wasDraw = true; @@ -14414,8 +14627,26 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate lastLocatinIsExpired = locationExpired; lastIsPlayingRound = isPlayingRound; + + lastDrawingTextY = textY; + lastDrawingTextX = textX; + + lastDrawnForwardedNameLayout[0] = forwardedNameLayout[0]; + lastDrawnForwardedNameLayout[1] = forwardedNameLayout[1]; + lastDrawnForwardedName = currentMessageObject.needDrawForwarded(); + lastForwardNameX = forwardNameX; + lastForwardedNamesOffset = namesOffset; + lastForwardNameWidth = forwardedNameWidth; } + public void recordDrawingStatePreview() { + lastDrawnForwardedNameLayout[0] = forwardedNameLayout[0]; + lastDrawnForwardedNameLayout[1] = forwardedNameLayout[1]; + lastDrawnForwardedName = currentMessageObject.needDrawForwarded(); + lastForwardNameX = forwardNameX; + lastForwardedNamesOffset = namesOffset; + lastForwardNameWidth = forwardedNameWidth; + } public boolean animateChange() { if (!wasDraw) { return false; @@ -14473,6 +14704,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate animateReplaceCaptionLayout = true; animateOutCaptionLayout = lastDrawingCaptionLayout; changed = true; + } else { + updateCaptionLayout(); + if (lastDrawingCaptionX != captionX || lastDrawingCaptionY != captionY) { + moveCaption = true; + captionFromX = lastDrawingCaptionX; + captionFromY = lastDrawingCaptionY; + changed = true; + } } } else if (captionLayout != null && lastDrawingCaptionLayout != null) { updateCaptionLayout(); @@ -14571,6 +14810,24 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate animatePlayingRound = true; changed = true; } + + if (lastDrawingTextY != textY) { + animateText = true; + animateFromTextY = lastDrawingTextY; + changed = true; + } + + if (currentMessageObject != null) { + if (lastDrawnForwardedName != currentMessageObject.needDrawForwarded()) { + animateForwardedLayout = true; + animatingForwardedNameLayout[0] = lastDrawnForwardedNameLayout[0]; + animatingForwardedNameLayout[1] = lastDrawnForwardedNameLayout[1]; + animateForwardNameX = lastForwardNameX; + animateForwardedNamesOffset = lastForwardedNamesOffset; + animateForwardNameWidth = lastForwardNameWidth; + changed = true; + } + } return changed; } @@ -14627,6 +14884,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate animateDrawingTimeAlpha = false; animateLocationIsExpired = false; animatePlayingRound = false; + animateText = false; + animateForwardedLayout = false; + animatingForwardedNameLayout[0] = null; + animatingForwardedNameLayout[1] = null; } public boolean supportChangeAnimation() { 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 a48178bc9..f5343de38 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -377,10 +377,25 @@ public class DialogCell extends BaseCell { user = newUser; } } - boolean isOnline = user != null && !user.self && (user.status != null && user.status.expires > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || MessagesController.getInstance(currentAccount).onlinePrivacy.containsKey(user.id)); + boolean isOnline = isOnline(); onlineProgress = isOnline ? 1.0f : 0.0f; } + private boolean isOnline() { + if (user == null || user.self) { + return false; + } + if (user.status != null && user.status.expires <= 0) { + if (MessagesController.getInstance(currentAccount).onlinePrivacy.containsKey(user.id)) { + return true; + } + } + if (user.status != null && user.status.expires > ConnectionsManager.getInstance(currentAccount).getCurrentTime()) { + return true; + } + return false; + } + private void checkGroupCall() { hasCall = chat != null && chat.call_active && chat.call_not_empty; chatCallProgress = hasCall ? 1.0f : 0.0f; @@ -598,6 +613,7 @@ public class DialogCell extends BaseCell { boolean showChecks = !UserObject.isUserSelf(user) && !useMeForMyMessages; boolean drawTime = true; printingStringType = -1; + int printigStingReplaceIndex = -1; String messageFormat; boolean hasNameInMessage; @@ -875,7 +891,15 @@ public class DialogCell extends BaseCell { startPadding = statusDrawable.getIntrinsicWidth() + AndroidUtilities.dp(3); } SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); - spannableStringBuilder.append(" ").append(TextUtils.replace(printingString, new String[]{"..."}, new String[]{""})).setSpan(new FixedWidthSpan(startPadding), 0, 1, 0); + + if (printingStringType == 5) { + printigStingReplaceIndex = printingString.toString().indexOf("**oo**"); + } + if (printigStingReplaceIndex > 0) { + spannableStringBuilder.append(TextUtils.replace(printingString, new String[]{"..."}, new String[]{""})).setSpan(new FixedWidthSpan(Theme.getChatStatusDrawable(printingStringType).getIntrinsicWidth()), printigStingReplaceIndex, printigStingReplaceIndex + 6, 0); + } else { + spannableStringBuilder.append(" ").append(TextUtils.replace(printingString, new String[]{"..."}, new String[]{""})).setSpan(new FixedWidthSpan(startPadding), 0, 1, 0); + } messageString = spannableStringBuilder; currentMessagePaint = Theme.dialogs_messagePrintingPaint[paintIndex]; @@ -1725,8 +1749,14 @@ public class DialogCell extends BaseCell { } } if (messageLayout != null && printingStringType >= 0) { - float x1 = messageLayout.getPrimaryHorizontal(0); - float x2 = messageLayout.getPrimaryHorizontal(1); + float x1, x2; + if (printigStingReplaceIndex >= 0){ + x1 = messageLayout.getPrimaryHorizontal(printigStingReplaceIndex); + x2 = messageLayout.getPrimaryHorizontal(printigStingReplaceIndex + 1); + } else { + x1 = messageLayout.getPrimaryHorizontal(0); + x2 = messageLayout.getPrimaryHorizontal(1); + } if (x1 < x2) { statusDrawableLeft = (int) (messageLeft + x1); } else { @@ -2802,7 +2832,7 @@ public class DialogCell extends BaseCell { if (isDialogCell && currentDialogFolderId == 0) { if (user != null && !MessagesController.isSupportUser(user) && !user.bot) { - boolean isOnline = !user.self && (user.status != null && user.status.expires > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || MessagesController.getInstance(currentAccount).onlinePrivacy.containsKey(user.id)); + boolean isOnline = isOnline(); if (isOnline || onlineProgress != 0) { int top = (int) (avatarImage.getImageY2() - AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 6 : 8)); int left; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HintDialogCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HintDialogCell.java index 6e4cc382b..5373097b1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HintDialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HintDialogCell.java @@ -27,6 +27,7 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.CheckBox2; import org.telegram.ui.Components.CounterView; import org.telegram.ui.Components.LayoutHelper; @@ -46,9 +47,12 @@ public class HintDialogCell extends FrameLayout { boolean wasDraw; CounterView counterView; + CheckBox2 checkBox; + private final boolean drawCheckbox; - public HintDialogCell(Context context) { + public HintDialogCell(Context context, boolean drawCheckbox) { super(context); + this.drawCheckbox = drawCheckbox; imageView = new BackupImageView(context); imageView.setRoundRadius(AndroidUtilities.dp(27)); @@ -67,12 +71,28 @@ public class HintDialogCell extends FrameLayout { addView(counterView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 28, Gravity.TOP,0 ,4,0,0)); counterView.setColors(Theme.key_chats_unreadCounterText, Theme.key_chats_unreadCounter); counterView.setGravity(Gravity.RIGHT); + + if (drawCheckbox) { + checkBox = new CheckBox2(context, 21); + checkBox.setColor(Theme.key_dialogRoundCheckBox, Theme.key_dialogBackground, Theme.key_dialogRoundCheckBoxCheck); + checkBox.setDrawUnchecked(false); + checkBox.setDrawBackgroundAsArc(4); + checkBox.setProgressDelegate(progress -> { + float scale = 1.0f - (1.0f - 0.857f) * checkBox.getProgress(); + imageView.setScaleX(scale); + imageView.setScaleY(scale); + invalidate(); + }); + addView(checkBox, LayoutHelper.createFrame(24, 24, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 19, 42, 0, 0)); + checkBox.setChecked(true, false); + setWillNotDraw(false); + } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(86), MeasureSpec.EXACTLY)); - counterView.horizontalPadding = AndroidUtilities.dp(13); + counterView.counterDrawable.horizontalPadding = AndroidUtilities.dp(13); } public void update(int mask) { @@ -181,4 +201,25 @@ public class HintDialogCell extends FrameLayout { } return result; } + + @Override + protected void onDraw(Canvas canvas) { + if (drawCheckbox) { + int cx = imageView.getLeft() + imageView.getMeasuredWidth() / 2; + int cy = imageView.getTop() + imageView.getMeasuredHeight() / 2; + Theme.checkboxSquare_checkPaint.setColor(Theme.getColor(Theme.key_dialogRoundCheckBox)); + Theme.checkboxSquare_checkPaint.setAlpha((int) (checkBox.getProgress() * 255)); + canvas.drawCircle(cx, cy, AndroidUtilities.dp(28), Theme.checkboxSquare_checkPaint); + } + } + + public void setChecked(boolean checked, boolean animated) { + if (drawCheckbox) { + checkBox.setChecked(checked, animated); + } + } + + public long getDialogId() { + return dialog_id; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LanguageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LanguageCell.java index 2f8054d74..7914d4d27 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LanguageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LanguageCell.java @@ -36,6 +36,9 @@ public class LanguageCell extends FrameLayout { public LanguageCell(Context context, boolean dialog) { super(context); + if (Theme.dividerPaint == null) { + Theme.createCommonResources(context); + } setWillNotDraw(false); isDialog = dialog; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedAudioCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedAudioCell.java index ee6679bdd..398622afc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedAudioCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedAudioCell.java @@ -9,16 +9,12 @@ import android.text.SpannableStringBuilder; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; -import android.text.style.ReplacementSpan; import android.view.Gravity; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.DownloadController; import org.telegram.messenger.Emoji; @@ -206,7 +202,7 @@ public class SharedAudioCell extends FrameLayout implements DownloadController.F currentMessageObject = messageObject; TLRPC.Document document = messageObject.getDocument(); - TLRPC.PhotoSize thumb = document != null ? FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 240) : null; + TLRPC.PhotoSize thumb = document != null ? FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 360) : null; if (thumb instanceof TLRPC.TL_photoSize || thumb instanceof TLRPC.TL_photoSizeProgressive) { radialProgress.setImageOverlay(thumb, document, messageObject); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java index 96b2ced06..a9675910b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerCell.java @@ -77,7 +77,7 @@ public class StickerCell extends FrameLayout { return clearsInputField; } - public void setSticker(TLRPC.Document document, Object parent, int side) { + public void setSticker(TLRPC.Document document, Object parent) { parentObject = parent; if (document != null) { TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90); @@ -103,19 +103,6 @@ public class StickerCell extends FrameLayout { } } sticker = document; - if (side == -1) { - setBackgroundResource(R.drawable.stickers_back_left); - setPadding(AndroidUtilities.dp(7), 0, 0, 0); - } else if (side == 0) { - setBackgroundResource(R.drawable.stickers_back_center); - setPadding(0, 0, 0, 0); - } else if (side == 1) { - setBackgroundResource(R.drawable.stickers_back_right); - setPadding(0, 0, AndroidUtilities.dp(7), 0); - } else if (side == 2) { - setBackgroundResource(R.drawable.stickers_back_all); - setPadding(AndroidUtilities.dp(3), 0, AndroidUtilities.dp(3), 0); - } Drawable background = getBackground(); if (background != null) { background.setAlpha(230); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java index 83073f715..40a99b9df 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/StickerEmojiCell.java @@ -140,11 +140,11 @@ public class StickerEmojiCell extends FrameLayout { SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(document, fromEmojiPanel ? Theme.key_emptyListPlaceholder : Theme.key_windowBackgroundGray, fromEmojiPanel ? 0.2f : 1.0f); if (MessageObject.canAutoplayAnimatedSticker(document)) { if (svgThumb != null) { - imageView.setImage(ImageLocation.getForDocument(document), "80_80", null, svgThumb, parentObject); + imageView.setImage(ImageLocation.getForDocument(document), "66_66", null, svgThumb, parentObject); } else if (thumb != null) { - imageView.setImage(ImageLocation.getForDocument(document), "80_80", ImageLocation.getForDocument(thumb, document), null, 0, parentObject); + imageView.setImage(ImageLocation.getForDocument(document), "66_66", ImageLocation.getForDocument(thumb, document), null, 0, parentObject); } else { - imageView.setImage(ImageLocation.getForDocument(document), "80_80", null, null, parentObject); + imageView.setImage(ImageLocation.getForDocument(document), "66_66", null, null, parentObject); } } else { if (svgThumb != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java index 213ee4b8b..dfa7543b9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChangePhoneActivity.java @@ -985,7 +985,7 @@ public class ChangePhoneActivity extends BaseFragment { Intent mailer = new Intent(Intent.ACTION_SENDTO); mailer.setData(Uri.parse("mailto:")); - mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"sms@stel.com"}); + mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"reports@stel.com"}); mailer.putExtra(Intent.EXTRA_SUBJECT, "Android registration/login issue " + version + " " + emailPhone); mailer.putExtra(Intent.EXTRA_TEXT, "Phone: " + requestPhone + "\nApp version: " + version + "\nOS version: SDK " + Build.VERSION.SDK_INT + "\nDevice Name: " + Build.MANUFACTURER + Build.MODEL + "\nLocale: " + Locale.getDefault() + "\nError: " + lastError); getContext().startActivity(Intent.createChooser(mailer, "Send email...")); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java index d1a789a37..d0e8cbcaa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelAdminLogActivity.java @@ -1755,7 +1755,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio if (viewBottom > height) { viewBottom = viewTop + height; } - messageCell.setVisiblePart(viewTop, viewBottom - viewTop, contentView.getHeightWithKeyboard() - AndroidUtilities.dp(48) - chatListView.getTop(), 0, view.getY() + actionBar.getMeasuredHeight() - contentView.getBackgroundTranslationY(), contentView.getBackgroundSizeY()); + messageCell.setVisiblePart(viewTop, viewBottom - viewTop, contentView.getHeightWithKeyboard() - AndroidUtilities.dp(48) - chatListView.getTop(), 0, view.getY() + actionBar.getMeasuredHeight() - contentView.getBackgroundTranslationY(), contentView.getMeasuredWidth(), contentView.getBackgroundSizeY()); MessageObject messageObject = messageCell.getMessageObject(); if (roundVideoContainer != null && messageObject.isRoundVideo() && MediaController.getInstance().isPlayingMessage(messageObject)) { @@ -2518,7 +2518,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio } if (holder.itemView instanceof ChatMessageCell) { - ((ChatMessageCell) view).setVisiblePart(viewTop, viewBottom - viewTop, contentView.getHeightWithKeyboard() - AndroidUtilities.dp(48) - chatListView.getTop(), 0, view.getY() + actionBar.getMeasuredHeight() - contentView.getBackgroundTranslationY(), contentView.getBackgroundSizeY()); + ((ChatMessageCell) view).setVisiblePart(viewTop, viewBottom - viewTop, contentView.getHeightWithKeyboard() - AndroidUtilities.dp(48) - chatListView.getTop(), 0, view.getY() + actionBar.getMeasuredHeight() - contentView.getBackgroundTranslationY(), contentView.getMeasuredWidth(), contentView.getBackgroundSizeY()); } else if (holder.itemView instanceof ChatActionCell) { if (actionBar != null && contentView != null) { ((ChatActionCell) view).setVisiblePart(view.getY() + actionBar.getMeasuredHeight() - contentView.getBackgroundTranslationY(), contentView.getBackgroundSizeY()); @@ -2750,7 +2750,9 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgOutDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgOutMediaDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubble)); - themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient)); + themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient1)); + themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient2)); + themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient3)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutSelectedDrawable, Theme.chat_msgOutMediaSelectedDrawable}, null, Theme.key_chat_outBubbleSelected)); themeDescriptions.add(new ThemeDescription(chatListView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{ChatActionCell.class}, Theme.chat_actionTextPaint, null, null, Theme.key_chat_serviceText)); themeDescriptions.add(new ThemeDescription(chatListView, ThemeDescription.FLAG_LINKCOLOR, new Class[]{ChatActionCell.class}, Theme.chat_actionTextPaint, null, null, Theme.key_chat_serviceLink)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java index 470a6c109..023f5ab34 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelCreateActivity.java @@ -499,8 +499,12 @@ public class ChannelCreateActivity extends BaseFragment implements NotificationC avatarEditor.setAnimation(cameraDrawable); cameraDrawable.setCurrentFrame(0); }, dialog -> { - cameraDrawable.setCustomEndFrame(86); - avatarEditor.playAnimation(); + if (!imageUpdater.isUploadingImage()) { + cameraDrawable.setCustomEndFrame(86); + avatarEditor.playAnimation(); + } else { + cameraDrawable.setCurrentFrame(0, false); + } }); cameraDrawable.setCurrentFrame(0); cameraDrawable.setCustomEndFrame(43); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 8bd3ee3a1..caa336601 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -37,7 +37,6 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Typeface; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; @@ -79,7 +78,6 @@ import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ScrollView; import android.widget.Space; import android.widget.TextView; import android.widget.Toast; @@ -107,6 +105,7 @@ import org.telegram.messenger.Emoji; import org.telegram.messenger.EmojiData; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; +import org.telegram.messenger.ForwardingMessagesParams; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.LocaleController; @@ -164,6 +163,7 @@ import org.telegram.ui.Components.AnimatedFileDrawable; import org.telegram.ui.Components.AnimationProperties; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.BlurBehindDrawable; +import org.telegram.ui.Components.BluredView; import org.telegram.ui.Components.BotCommandsMenuView; import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.BulletinFactory; @@ -186,8 +186,10 @@ import org.telegram.ui.Components.EmbedBottomSheet; import org.telegram.ui.Components.EmojiView; import org.telegram.ui.Components.ExtendedGridLayoutManager; import org.telegram.ui.Components.FireworksOverlay; +import org.telegram.ui.Components.ForwardingPreviewView; import org.telegram.ui.Components.FragmentContextView; import org.telegram.ui.Components.GigagroupConvertAlert; +import org.telegram.ui.Components.HideViewAfterAnimation; import org.telegram.ui.Components.HintView; import org.telegram.ui.Components.ImportingAlert; import org.telegram.ui.Components.InstantCameraView; @@ -306,6 +308,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private BackupImageView replyImageView; private SimpleTextView replyNameTextView; private SimpleTextView replyObjectTextView; + private SimpleTextView replyObjectHintTextView; + private boolean showTapForForwardingOptionsHit; + private Runnable tapForForwardingOptionsHitRunnable; private ImageView replyIconImageView; private ImageView replyCloseImageView; private MentionsAdapter mentionsAdapter; @@ -342,7 +347,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private ChecksHintView checksHintView; private View emojiButtonRed; private FrameLayout pinnedMessageView; - private View blurredView; + private BluredView blurredView; private PinnedLineView pinnedLineView; private boolean setPinnedTextTranslationX; private AnimatorSet pinnedMessageViewAnimator; @@ -416,10 +421,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private boolean scrollToThreadMessage; private int threadMaxInboxReadId; private int threadMaxOutboxReadId; - private int replyMaxReadId = 0; + private int replyMaxReadId; private Runnable delayedReadRunnable; private SparseArray pendingSendMessagesDict = new SparseArray<>(); private ArrayList pendingSendMessages = new ArrayList<>(); + private int threadUnreadMessagesCount; public ArrayList animatingMessageObjects = new ArrayList<>(); private HashMap animatingDocuments = new HashMap<>(); @@ -446,6 +452,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private int mentionListViewLastViewTop; private int mentionListViewLastViewPosition; private boolean mentionListViewIsScrolling; + private boolean mentionListViewIsDragging; private ArrayList pinnedMessageIds = new ArrayList<>(); private HashMap pinnedMessageObjects = new HashMap<>(); @@ -481,7 +488,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private MessageObject selectedObjectToEditCaption; private MessageObject selectedObject; private MessageObject.GroupedMessages selectedObjectGroup; - private ArrayList forwardingMessages; + private ForwardingMessagesParams forwardingMessages; + private CharSequence formwardingNameText; private MessageObject forwardingMessage; private MessageObject.GroupedMessages forwardingMessageGroup; private MessageObject replyingMessageObject; @@ -620,12 +628,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private boolean scrollToVideo; private Path aspectPath; private Paint aspectPaint; - private Runnable destroyTextureViewRunnable = new Runnable() { - @Override - public void run() { - destroyTextureView(); - } - }; + private Runnable destroyTextureViewRunnable = this::destroyTextureView; private Paint scrimPaint; private View scrimView; @@ -686,6 +689,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private boolean openImport; private float chatListViewPaddingTop; + private float chatListViewPaddingTopOnlyTopViews; private int chatListViewPaddingVisibleOffset; private int contentPaddingTop; @@ -707,6 +711,22 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private boolean showSearchAsIcon; public MessageEnterTransitionContainer messageEnterTransitionContainer; + private float pullingDownOffset; + private ChatPullingDownDrawable pullingDownDrawable; + private ValueAnimator pullingDownBackAnimator; + private boolean fromPullingDownTransition; + private boolean toPullingDownTransition; + private ChatActivity pullingDownAnimateToActivity; + private float pullingDownAnimateProgress; + private AnimatorSet fragmentTransition; + private Runnable fragmentTransitionRunnable = new Runnable() { + @Override + public void run() { + if (fragmentTransition != null && !fragmentTransition.isRunning()) { + fragmentTransition.start(); + } + } + }; private final static int[] allowedNotificationsDuringChatListAnimations = new int[]{ NotificationCenter.messagesRead, @@ -721,7 +741,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not NotificationCenter.didUpdateConnectionState, NotificationCenter.updateInterfaces, NotificationCenter.closeChats, - // NotificationCenter.contactsDidLoad, NotificationCenter.chatInfoCantLoad, NotificationCenter.userInfoDidLoad, NotificationCenter.pinnedInfoDidLoad, @@ -755,6 +774,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private Runnable cancelFixedPositionRunnable; private boolean invalidateMessagesVisiblePart; private boolean scrollByTouch; + int dialogFolderId; + int dialogFilterId; private PinchToZoomHelper pinchToZoomHelper; @@ -784,6 +805,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } + ForwardingPreviewView forwardingPreviewView; + private class UnreadCounterTextView extends View { private int currentCounter; @@ -1013,6 +1036,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { return ChatActivity.this.getPlaceForPhoto(messageObject, fileLocation, needPreview, false); } + + @Override + public boolean validateGroupId(long groupId) { + MessageObject.GroupedMessages groupedMessages = groupedMessagesMap.get(groupId); + if (groupedMessages != null && groupedMessages.messages.size() > 1) { + return true; + } + return false; + } }; private ArrayList botContextResults; @@ -1203,8 +1235,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not MessageObject messageObject = messages.get(i); if (messageObject.contentType == 0 && messageObject.hasValidGroupId()) { MessageObject.GroupedMessages groupedMessages = groupedMessagesMap.get(messageObject.getGroupId()); - MessageObject messageObject1 = groupedMessages.messages.get(selectionTop ? 0 : groupedMessages.messages.size() - 1); - return chatAdapter.messagesStartRow + messages.indexOf(messageObject1); + if (groupedMessages != null) { + MessageObject messageObject1 = groupedMessages.messages.get(selectionTop ? 0 : groupedMessages.messages.size() - 1); + return chatAdapter.messagesStartRow + messages.indexOf(messageObject1); + } } } return position; @@ -1266,7 +1300,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not scheduledOrNoSoundHint.setText(LocaleController.getString("ScheduledOrNoSoundHint", R.string.ScheduledOrNoSoundHint)); contentView.addView(scheduledOrNoSoundHint, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 10, 0, 10, 0)); } - scheduledOrNoSoundHint.showForView(chatActivityEnterView.getSendButton(), true); + scheduledOrNoSoundHint.showForView(anchor, true); }; public ChatActivity(Bundle args) { @@ -1278,12 +1312,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not final int chatId = arguments.getInt("chat_id", 0); final int userId = arguments.getInt("user_id", 0); final int encId = arguments.getInt("enc_id", 0); + dialogFolderId = arguments.getInt("dialog_folder_id", 0); + dialogFilterId = arguments.getInt("dialog_filter_id", 0); chatMode = arguments.getInt("chatMode", 0); voiceChatHash = arguments.getString("voicechat", null); inlineReturn = arguments.getLong("inline_return", 0); String inlineQuery = arguments.getString("inline_query"); startLoadFromMessageId = arguments.getInt("message_id", 0); startFromVideoTimestamp = arguments.getInt("video_timestamp", -1); + threadUnreadMessagesCount = arguments.getInt("unread_count", 0); if (startFromVideoTimestamp >= 0) { startFromVideoMessageId = startLoadFromMessageId; } @@ -1426,11 +1463,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } getNotificationCenter().addObserver(this, NotificationCenter.messagesDidLoad); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.invalidateMotionBackground); getNotificationCenter().addObserver(this, NotificationCenter.didUpdateConnectionState); getNotificationCenter().addObserver(this, NotificationCenter.updateInterfaces); if (chatMode != MODE_PINNED) { getNotificationCenter().addObserver(this, NotificationCenter.didReceiveNewMessages); } + if (chatMode == 0) { + getNotificationCenter().addObserver(this, NotificationCenter.didLoadSponsoredMessages); + } getNotificationCenter().addObserver(this, NotificationCenter.closeChats); getNotificationCenter().addObserver(this, NotificationCenter.messagesDeleted); getNotificationCenter().addObserver(this, NotificationCenter.historyCleared); @@ -1622,7 +1663,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), (dialogInterface, i) -> finishFragment()); showDialog(builder.create()); - }, timeout * 1000); + }, timeout * 1000L); } return true; } @@ -1693,6 +1734,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not getMessagesController().setLastCreatedDialogId(dialog_id, chatMode == MODE_SCHEDULED, false); getNotificationCenter().removeObserver(this, NotificationCenter.messagesDidLoad); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.invalidateMotionBackground); getNotificationCenter().removeObserver(this, NotificationCenter.didUpdateConnectionState); getNotificationCenter().removeObserver(this, NotificationCenter.updateInterfaces); getNotificationCenter().removeObserver(this, NotificationCenter.didReceiveNewMessages); @@ -1753,6 +1795,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not getNotificationCenter().removeObserver(this, NotificationCenter.scheduledMessagesUpdated); getNotificationCenter().removeObserver(this, NotificationCenter.diceStickersDidLoad); getNotificationCenter().removeObserver(this, NotificationCenter.dialogDeleted); + getNotificationCenter().removeObserver(this, NotificationCenter.didLoadSponsoredMessages); if (currentEncryptedChat != null) { getNotificationCenter().removeObserver(this, NotificationCenter.didVerifyMessagesStickers); } @@ -2474,6 +2517,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not progressView.setTranslationY(y / 2); contentView.setBackgroundTranslation((int) y); instantCameraView.onPanTranslationUpdate(y); + if (blurredView != null) { + blurredView.drawable.onPanTranslationUpdate(y); + } setFragmentPanTranslationOffset((int) y); invalidateChatListViewTopPadding(); invalidateMessagesVisiblePart(); @@ -2519,12 +2565,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (messageObject != null && (messageObject.isRoundVideo() || messageObject.isVideo()) && messageObject.eventId == 0 && messageObject.getDialogId() == dialog_id) { MediaController.getInstance().setTextureView(createTextureView(false), aspectRatioFrameLayout, videoPlayerContainer, true); } + if (pullingDownDrawable != null) { + pullingDownDrawable.onAttach(); + } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); adjustPanLayoutHelper.onDetach(); + if (pullingDownDrawable != null) { + pullingDownDrawable.onDetach(); + pullingDownDrawable = null; + } } @Override @@ -2578,6 +2631,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (getTag(BlurBehindDrawable.TAG_DRAWING_AS_BACKGROUND) != null) { return; } + if (getTag(BlurBehindDrawable.TAG_DRAWING_AS_BACKGROUND) == null && (instantCameraView.blurFullyDrawing() || (blurredView != null && blurredView.fullyDrawing() && blurredView.getTag() != null))) { + return; + } super.onDraw(canvas); } @@ -2592,17 +2648,20 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (child == undoView && PhotoViewer.getInstance().isVisible()) { return true; } - if (getTag(BlurBehindDrawable.TAG_DRAWING_AS_BACKGROUND) != null ) { + if (toPullingDownTransition && child == chatListView) { + return true; + } + if (getTag(BlurBehindDrawable.TAG_DRAWING_AS_BACKGROUND) != null) { boolean needBlur; if (((int) getTag(BlurBehindDrawable.TAG_DRAWING_AS_BACKGROUND)) == BlurBehindDrawable.STATIC_CONTENT) { needBlur = child == actionBar; } else { - needBlur = child == chatListView || child == pinnedMessageView || child == fragmentContextView; + needBlur = child == chatListView || child == pinnedMessageView || child == fragmentContextView || child == chatActivityEnterView || chatActivityEnterView.isPopupView(child); } if (!needBlur) { return false; } - } else if (getTag(BlurBehindDrawable.TAG_DRAWING_AS_BACKGROUND) == null && instantCameraView.blurFullyDrawing()) { + } else if (getTag(BlurBehindDrawable.TAG_DRAWING_AS_BACKGROUND) == null && (instantCameraView.blurFullyDrawing() || (blurredView != null && blurredView.fullyDrawing() && blurredView.getTag() != null))) { boolean needBlur = child == actionBar || child == chatListView || child == pinnedMessageView || child == fragmentContextView; if (needBlur) { return false; @@ -2613,6 +2672,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not boolean isRoundVideo = false; boolean isVideo = messageObject != null && messageObject.eventId == 0 && ((isRoundVideo = messageObject.isRoundVideo()) || messageObject.isVideo()); if (child == videoPlayerContainer) { + canvas.save(); + float transitionOffset = 0; + if (pullingDownAnimateProgress != 0) { + transitionOffset = (chatListView.getMeasuredHeight() - pullingDownOffset) * pullingDownAnimateProgress; + } + canvas.translate(0, -pullingDownOffset - transitionOffset); if (messageObject != null && messageObject.type == MessageObject.TYPE_ROUND_VIDEO) { if (Theme.chat_roundVideoShadow != null && aspectRatioFrameLayout.isDrawingReady()) { int x = (int) child.getX() - AndroidUtilities.dp(3); @@ -2635,9 +2700,16 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not result = false; } } + canvas.restore(); } else { result = super.drawChild(canvas, child, drawingTime); if (isVideo && child == chatListView && messageObject.type != 5 && videoPlayerContainer != null && videoPlayerContainer.getTag() != null) { + canvas.save(); + float transitionOffset = 0; + if (pullingDownAnimateProgress != 0) { + transitionOffset = (chatListView.getMeasuredHeight() - pullingDownOffset) * pullingDownAnimateProgress; + } + canvas.translate(0, -pullingDownOffset - transitionOffset); super.drawChild(canvas, videoPlayerContainer, drawingTime); if (drawLaterRoundProgressCell != null) { canvas.save(); @@ -2646,7 +2718,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not drawLaterRoundProgressCell.drawRoundProgress(canvas); invalidate(); drawLaterRoundProgressCell.invalidate(); - // drawLaterRoundProgressCell.drawOverlays(canvas); } else { drawLaterRoundProgressCell.drawOverlays(canvas); if (drawLaterRoundProgressCell.needDrawTime()) { @@ -2655,6 +2726,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } canvas.restore(); } + canvas.restore(); } } if (child == actionBar && parentLayout != null) { @@ -2698,12 +2770,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not updateTextureViewPosition(false); super.dispatchDraw(canvas); if (fragmentContextView != null && fragmentContextView.isCallStyle()) { - canvas.save(); - canvas.translate(fragmentContextView.getX(), fragmentContextView.getY()); - fragmentContextView.setDrawOverlay(true); - fragmentContextView.draw(canvas); - fragmentContextView.setDrawOverlay(false); - canvas.restore(); + float alpha = (blurredView != null && blurredView.getVisibility() == View.VISIBLE) ? 1f - blurredView.getAlpha() : 1f; + if (alpha > 0) { + if (alpha == 1f) { + canvas.save(); + } else { + canvas.saveLayerAlpha(fragmentContextView.getX(), fragmentContextView.getY() - AndroidUtilities.dp(30), fragmentContextView.getX() + fragmentContextView.getMeasuredWidth(), fragmentContextView.getY() + fragmentContextView.getMeasuredHeight(), (int) (255 * alpha), Canvas.ALL_SAVE_FLAG); + } + canvas.translate(fragmentContextView.getX(), fragmentContextView.getY()); + fragmentContextView.setDrawOverlay(true); + fragmentContextView.draw(canvas); + fragmentContextView.setDrawOverlay(false); + canvas.restore(); + } } if (chatActivityEnterView != null) { if (chatActivityEnterView.pannelAniamationInProgress() && chatActivityEnterView.getEmojiPadding() < bottomPanelTranslationY) { @@ -2798,7 +2877,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not canvas.save(); canvas.clipRect(0, listTop, getMeasuredWidth(), chatListView.getY() + chatListView.getHeight()); canvas.translate(0, chatListView.getY()); - scrimGroup.transitionParams.cell.drawBackground(canvas, (int) l, (int) t, (int) r, (int) b, scrimGroup.transitionParams.pinnedTop, scrimGroup.transitionParams.pinnedBotton, selected); + scrimGroup.transitionParams.cell.drawBackground(canvas, (int) l, (int) t, (int) r, (int) b, scrimGroup.transitionParams.pinnedTop, scrimGroup.transitionParams.pinnedBotton, selected, contentView.getKeyboardHeight()); canvas.restore(); groupedBackgroundWasDraw = true; } @@ -2895,6 +2974,22 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } canvas.drawRect(0,getMeasuredHeight() - fixedKeyboardHeight, getMeasuredWidth(), getMeasuredHeight(), backgroundPaint); } + if (pullingDownDrawable != null && pullingDownDrawable.needDrawBottomPanel()) { + int top, bottom; + if (chatActivityEnterView != null && chatActivityEnterView.getVisibility() == View.VISIBLE) { + top = chatActivityEnterView.getTop() + AndroidUtilities.dp2(2); + bottom = chatActivityEnterView.getBottom(); + } else { + top = bottomOverlayChat.getTop() + AndroidUtilities.dp2(2); + bottom = bottomOverlayChat.getBottom(); + } + pullingDownDrawable.drawBottomPanel(canvas, top, bottom, getMeasuredWidth()); + } + if (pullingDownAnimateToActivity != null) { + canvas.saveLayerAlpha(0, 0, getMeasuredWidth(), getMeasuredHeight(), (int) (255 * pullingDownAnimateProgress), Canvas.ALL_SAVE_FLAG); + pullingDownAnimateToActivity.fragmentView.draw(canvas); + canvas.restore(); + } } @Override @@ -3013,8 +3108,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not continue; } if (child == blurredView) { + int h = allHeight; + if (keyboardSize > AndroidUtilities.dp(20) && getLayoutParams().height < 0) { + h += keyboardSize; + } int contentWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY); - int contentHeightSpec = MeasureSpec.makeMeasureSpec(allHeight, MeasureSpec.EXACTLY); + int contentHeightSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY); child.measure(contentWidthSpec, contentHeightSpec); } else if (child == chatListView) { int contentWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY); @@ -3059,7 +3158,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else { int height; mentionListViewIgnoreLayout = true; - if (mentionsAdapter.isBotContext() && mentionsAdapter.isMediaLayout()) { + if ((mentionsAdapter.isStickers() || mentionsAdapter.isBotContext()) && mentionsAdapter.isMediaLayout()) { int size = mentionGridLayoutManager.getRowsCount(widthSize); int maxHeight = size * 102; if (mentionsAdapter.isBotContext()) { @@ -3111,6 +3210,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not textSelectionHelper.setKeyboardSize(0); } child.measure(contentWidthSpec, MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY)); + } else if (child == forwardingPreviewView) { + int contentWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY); + int h = allHeight - AndroidUtilities.statusBarHeight; + if (keyboardSize > AndroidUtilities.dp(20) && getLayoutParams().height < 0) { + h += keyboardSize; + } + int contentHeightSpec = MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY); + child.measure(contentWidthSpec, contentHeightSpec); } else { measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); } @@ -3266,6 +3373,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } else if (chatActivityEnterView != null && child == chatActivityEnterView.botCommandsMenuContainer) { childTop -= inputFieldHeight; + } else if (child == forwardingPreviewView) { + childTop = AndroidUtilities.statusBarHeight; } child.layout(childLeft, childTop, childLeft + width, childTop + height); } @@ -3288,6 +3397,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not contentPanTranslation = 0; contentView.setBackgroundTranslation(0); instantCameraView.onPanTranslationUpdate(0); + if (blurredView != null) { + blurredView.drawable.onPanTranslationUpdate(0); + } setFragmentPanTranslationOffset(0); invalidateChatListViewTopPadding(); } @@ -3298,6 +3410,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not invalidateChatListViewTopPadding(); invalidateMessagesVisiblePart(); } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == 1 && forwardingPreviewView != null && forwardingPreviewView.isShowing()) { + forwardingPreviewView.dismiss(true); + return true; + } + return super.dispatchKeyEvent(event); + } }; contentView = (SizeNotifierFrameLayout) fragmentView; @@ -3392,9 +3513,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private int lastWidth; - private ArrayList drawTimeAfter = new ArrayList<>(); - private ArrayList drawNamesAfter = new ArrayList<>(); - private ArrayList drawCaptionAfter = new ArrayList<>(); + private final ArrayList drawTimeAfter = new ArrayList<>(); + private final ArrayList drawNamesAfter = new ArrayList<>(); + private final ArrayList drawCaptionAfter = new ArrayList<>(); + private final ArrayList drawingGroups = new ArrayList<>(10); private boolean slideAnimationInProgress; private int startedTrackingX; @@ -3513,7 +3635,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not super.setItemAnimator(animator); } - private RectF rect = new RectF(); private void drawReplyButton(Canvas canvas) { if (slidingView == null) { return; @@ -3564,12 +3685,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not Theme.chat_replyIconDrawable.setAlpha(alpha); float x = getMeasuredWidth() + slidingView.getNonAnimationTranslationX(false) / 2; float y = slidingView.getTop() + slidingView.getMeasuredHeight() / 2; - rect.set((int) (x - AndroidUtilities.dp(16) * scale), (int) (y - AndroidUtilities.dp(16) * scale), (int) (x + AndroidUtilities.dp(16) * scale), (int) (y + AndroidUtilities.dp(16) * scale)); + AndroidUtilities.rectTmp.set((int) (x - AndroidUtilities.dp(16) * scale), (int) (y - AndroidUtilities.dp(16) * scale), (int) (x + AndroidUtilities.dp(16) * scale), (int) (y + AndroidUtilities.dp(16) * scale)); - Theme.applyServiceShaderMatrix(getMeasuredWidth(), AndroidUtilities.displaySize.y, 0, getY() + rect.top); - canvas.drawRoundRect(rect, AndroidUtilities.dp(16), AndroidUtilities.dp(16), Theme.chat_actionBackgroundPaint); + Theme.applyServiceShaderMatrix(getMeasuredWidth(), AndroidUtilities.displaySize.y, 0, getY() + AndroidUtilities.rectTmp.top); + canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(16), AndroidUtilities.dp(16), Theme.chat_actionBackgroundPaint); if (Theme.hasGradientService()) { - canvas.drawRoundRect(rect, AndroidUtilities.dp(16), AndroidUtilities.dp(16), Theme.chat_actionBackgroundGradientDarkenPaint); + canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(16), AndroidUtilities.dp(16), Theme.chat_actionBackgroundGradientDarkenPaint); } Theme.chat_replyIconDrawable.setBounds((int) (x - AndroidUtilities.dp(7) * scale), (int) (y - AndroidUtilities.dp(6) * scale), (int) (x + AndroidUtilities.dp(7) * scale), (int) (y + AndroidUtilities.dp(5) * scale)); @@ -3664,6 +3785,36 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (e.getAction() == MotionEvent.ACTION_DOWN) { scrollByTouch = true; } + if (pullingDownOffset != 0 && (e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_CANCEL)) { + float progress = Math.min(1f, pullingDownOffset / AndroidUtilities.dp(110)); + if (e.getAction() == MotionEvent.ACTION_UP && progress == 1 && pullingDownDrawable != null && !pullingDownDrawable.emptyStub) { + if (pullingDownDrawable.animationIsRunning()) { + pullingDownBackAnimator = ValueAnimator.ofFloat(pullingDownOffset, pullingDownOffset + AndroidUtilities.dp(8)); + pullingDownBackAnimator.addUpdateListener(valueAnimator -> { + pullingDownOffset = (float) valueAnimator.getAnimatedValue(); + chatListView.invalidate(); + }); + pullingDownBackAnimator.setDuration(200); + pullingDownBackAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + pullingDownBackAnimator.start(); + pullingDownDrawable.runOnAnimationFinish(() -> { + animateToNextChat(); + }); + } else { + animateToNextChat(); + } + } else { + pullingDownBackAnimator = ValueAnimator.ofFloat(pullingDownOffset, 0); + pullingDownDrawable.showBottomPanel(false); + pullingDownBackAnimator.addUpdateListener(valueAnimator -> { + pullingDownOffset = (float) valueAnimator.getAnimatedValue(); + chatListView.invalidate(); + }); + pullingDownBackAnimator.setDuration(ChatListItemAnimator.DEFAULT_DURATION); + pullingDownBackAnimator.setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR); + pullingDownBackAnimator.start(); + } + } if (isFastScrollAnimationRunning()) { return false; } @@ -3741,18 +3892,66 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } drawReplyButton(c); } + + if (pullingDownOffset != 0) { + c.save(); + float transitionOffset = 0; + if (pullingDownAnimateProgress != 0) { + transitionOffset = (chatListView.getMeasuredHeight() - pullingDownOffset) * pullingDownAnimateProgress; + } + c.translate(0, getMeasuredHeight() - transitionOffset); + if (pullingDownDrawable == null) { + pullingDownDrawable = new ChatPullingDownDrawable(currentAccount, fragmentView, dialog_id, dialogFolderId, dialogFilterId); + pullingDownDrawable.showBottomPanel(true); + pullingDownDrawable.onAttach(); + } + pullingDownDrawable.setWidth(getMeasuredWidth()); + float progress = Math.min(1f, pullingDownOffset / AndroidUtilities.dp(110)); + pullingDownDrawable.draw(c, chatListView, progress, 1f - pullingDownAnimateProgress); + + c.restore(); + + if (pullingDownAnimateToActivity != null) { + c.saveLayerAlpha(0, 0, pullingDownAnimateToActivity.chatListView.getMeasuredWidth(), pullingDownAnimateToActivity.chatListView.getMeasuredHeight(), (int) (255 * pullingDownAnimateProgress), Canvas.ALL_SAVE_FLAG); + c.translate(0, getMeasuredHeight() - pullingDownOffset - transitionOffset); + pullingDownAnimateToActivity.chatListView.draw(c); + c.restore(); + } + } else if (pullingDownDrawable != null) { + pullingDownDrawable.reset(); + } } - ArrayList drawingGroups = new ArrayList<>(10); @Override protected void dispatchDraw(Canvas canvas) { drawLaterRoundProgressCell = null; - MessageObject.GroupedMessages lastDrawnGroup = null; - int count = getChildCount(); canvas.save(); - canvas.clipRect(0, chatListViewPaddingTop - chatListViewPaddingVisibleOffset - AndroidUtilities.dp(4), getMeasuredWidth(), getMeasuredHeight()); + if (fromPullingDownTransition && !toPullingDownTransition) { + canvas.clipRect(0, chatListViewPaddingTop - chatListViewPaddingVisibleOffset - AndroidUtilities.dp(4), getMeasuredWidth(), getMeasuredHeight()); + } + + if (pullingDownOffset != 0) { + canvas.save(); + float transitionOffset = 0; + if (pullingDownAnimateProgress != 0) { + transitionOffset = (chatListView.getMeasuredHeight() - pullingDownOffset) * pullingDownAnimateProgress; + } + canvas.translate(0, -pullingDownOffset - transitionOffset); + drawChatBackgroundElements(canvas); + super.dispatchDraw(canvas); + canvas.restore(); + } else { + drawChatBackgroundElements(canvas); + super.dispatchDraw(canvas); + } + canvas.restore(); + } + + private void drawChatBackgroundElements(Canvas canvas) { + int count = getChildCount(); + MessageObject.GroupedMessages lastDrawnGroup = null; for (int a = 0; a < count; a++) { View child = getChildAt(a); @@ -3830,7 +4029,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } } - MessageObject.GroupedMessages scrimGroup = null; if (scrimView instanceof ChatMessageCell) { scrimGroup = ((ChatMessageCell) scrimView).getCurrentMessagesGroup(); @@ -3943,7 +4141,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not break; } } - group.transitionParams.cell.drawBackground(canvas, (int) l, (int) t, (int) r, (int) b, group.transitionParams.pinnedTop, group.transitionParams.pinnedBotton, selected); + group.transitionParams.cell.drawBackground(canvas, (int) l, (int) t, (int) r, (int) b, group.transitionParams.pinnedTop, group.transitionParams.pinnedBotton, selected, contentView.getKeyboardHeight()); group.transitionParams.cell = null; group.transitionParams.drawCaptionLayout = group.hasCaption; if (useScale) { @@ -3961,8 +4159,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } } - super.dispatchDraw(canvas); - canvas.restore(); } @Override @@ -4294,7 +4490,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not progressLocal = 1f - progressLocal; } int fromY = y; - int toY = y > maxY ? maxY : y; + int toY = Math.min(y, maxY); y = (int) (fromY * progressLocal + toY * (1f - progressLocal)); } } else { @@ -4521,7 +4717,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not }); } }; - chatListItemAnimator.setSupportsChangeAnimations(false); } chatLayoutManager = new GridLayoutManagerFixed(context, 1000, LinearLayoutManager.VERTICAL, true) { @@ -4659,7 +4854,20 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { + if (dy < 0 && pullingDownOffset != 0) { + pullingDownOffset += dy; + if (pullingDownOffset < 0) { + dy = (int) pullingDownOffset; + pullingDownOffset = 0; + chatListView.invalidate(); + } else { + dy = 0; + } + } + int n = chatListView.getChildCount(); + int scrolled = 0; + boolean foundTopView = false; for (int i = 0; i < n; i++) { View child = chatListView.getChildAt(i); float padding = chatListViewPaddingTop; @@ -4667,13 +4875,50 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not padding -= Math.max(0, AndroidUtilities.dp(48) + pinnedMessageEnterOffset); } if (chatListView.getChildAdapterPosition(child) == chatAdapter.getItemCount() - 1) { + int dyLocal = dy; if (child.getTop() - dy > padding) { - dy = (int) (child.getTop() - padding); + dyLocal = (int) (child.getTop() - padding); } - return super.scrollVerticallyBy(dy, recycler, state); + scrolled = super.scrollVerticallyBy(dyLocal, recycler, state); + foundTopView = true; + break; } } - return super.scrollVerticallyBy(dy, recycler, state); + if (!foundTopView) { + scrolled = super.scrollVerticallyBy(dy, recycler, state); + } + if (dy > 0 && scrolled == 0 && ChatObject.isChannel(currentChat) && !currentChat.megagroup && chatListView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING && !chatListView.isFastScrollAnimationRunning() && !chatListView.isMultiselect()) { + if (pullingDownOffset == 0 && pullingDownDrawable != null) { + pullingDownDrawable.updateDialog(); + } + if (pullingDownBackAnimator != null) { + pullingDownBackAnimator.removeAllListeners(); + pullingDownBackAnimator.cancel(); + } + + float k; + if (pullingDownOffset < AndroidUtilities.dp(110)) { + float progress = pullingDownOffset / AndroidUtilities.dp(110); + k = 0.65f * (1f - progress) + 0.45f * progress; + } else if (pullingDownOffset < AndroidUtilities.dp(160)) { + float progress = (pullingDownOffset - AndroidUtilities.dp(110)) / AndroidUtilities.dp(50); + k = 0.45f * (1f - progress) + 0.05f * progress; + } else { + k = 0.05f; + } + + pullingDownOffset += dy * k; + chatListView.invalidate(); + } + if (pullingDownOffset == 0) { + chatListView.setOverScrollMode(View.OVER_SCROLL_ALWAYS); + } else { + chatListView.setOverScrollMode(View.OVER_SCROLL_NEVER); + } + if (pullingDownDrawable != null && chatListView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) { + pullingDownDrawable.showBottomPanel(pullingDownOffset > 0); + } + return scrolled; } }; chatLayoutManager.setSpanSizeLookup(new GridLayoutManagerFixed.SpanSizeLookup() { @@ -5438,6 +5683,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not mentionContainer = new FrameLayout(context) { + private Rect padding; + @Override public void onDraw(Canvas canvas) { if (mentionListView.getChildCount() <= 0) { @@ -5451,15 +5698,25 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not canvas.drawRect(0, 0, getMeasuredWidth(), top, Theme.chat_composeBackgroundPaint); } else { int top = (int) mentionListView.getY(); - if (mentionsAdapter.isBotContext() && mentionsAdapter.isMediaLayout() && mentionsAdapter.getBotContextSwitch() == null) { + if ((mentionsAdapter.isStickers() || mentionsAdapter.isBotContext()) && mentionsAdapter.isMediaLayout() && mentionsAdapter.getBotContextSwitch() == null) { top += mentionListViewScrollOffsetY - AndroidUtilities.dp(4); } else { top += mentionListViewScrollOffsetY - AndroidUtilities.dp(2); } - int bottom = top + Theme.chat_composeShadowDrawable.getIntrinsicHeight(); - Theme.chat_composeShadowDrawable.setBounds(0, top, getMeasuredWidth(), bottom); - Theme.chat_composeShadowDrawable.draw(canvas); - canvas.drawRect(0, bottom, getMeasuredWidth(), bottomPanelTranslationYReverse + getMeasuredHeight(), Theme.chat_composeBackgroundPaint); + if (mentionsAdapter.isMediaLayout()) { + if (padding == null) { + padding = new Rect(); + Theme.chat_composeShadowRoundDrawable.getPadding(padding); + } + int bottom = top + Theme.chat_composeShadowRoundDrawable.getIntrinsicHeight(); + Theme.chat_composeShadowRoundDrawable.setBounds(-padding.left, top - padding.top - AndroidUtilities.dp(8), getMeasuredWidth() + padding.right, (int) (bottomPanelTranslationYReverse + getMeasuredHeight())); + Theme.chat_composeShadowRoundDrawable.draw(canvas); + } else { + int bottom = top + Theme.chat_composeShadowDrawable.getIntrinsicHeight(); + Theme.chat_composeShadowDrawable.setBounds(0, top, getMeasuredWidth(), bottom); + Theme.chat_composeShadowDrawable.draw(canvas); + canvas.drawRect(0, bottom, getMeasuredWidth(), bottomPanelTranslationYReverse + getMeasuredHeight(), Theme.chat_composeBackgroundPaint); + } } } @@ -5476,6 +5733,47 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not mentionContainer.setWillNotDraw(false); contentView.addView(mentionContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 110, Gravity.LEFT | Gravity.BOTTOM)); + final ContentPreviewViewer.ContentPreviewViewerDelegate contentPreviewViewerDelegate = new ContentPreviewViewer.ContentPreviewViewerDelegate() { + @Override + public void sendSticker(TLRPC.Document sticker, String query, Object parent, boolean notify, int scheduleDate) { + chatActivityEnterView.onStickerSelected(sticker, query, parent, null, true, notify, scheduleDate); + } + + @Override + public boolean needSend() { + return false; + } + + @Override + public boolean canSchedule() { + return ChatActivity.this.canScheduleMessage(); + } + + @Override + public boolean isInScheduleMode() { + return chatMode == MODE_SCHEDULED; + } + + @Override + public void openSet(TLRPC.InputStickerSet set, boolean clearsInputField) { + if (set == null || getParentActivity() == null) { + return; + } + TLRPC.TL_inputStickerSetID inputStickerSet = new TLRPC.TL_inputStickerSetID(); + inputStickerSet.access_hash = set.access_hash; + inputStickerSet.id = set.id; + StickersAlert alert = new StickersAlert(getParentActivity(), ChatActivity.this, inputStickerSet, null, chatActivityEnterView); + alert.setCalcMandatoryInsets(isKeyboardVisible()); + alert.setClearsInputField(clearsInputField); + showDialog(alert); + } + + @Override + public long getDialogId() { + return dialog_id; + } + }; + mentionListView = new RecyclerListView(context) { private int lastWidth; @@ -5484,26 +5782,26 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public boolean onInterceptTouchEvent(MotionEvent event) { if (mentionLayoutManager.getReverseLayout()) { - if (!mentionListViewIsScrolling && mentionListViewScrollOffsetY != 0 && event.getY() > mentionListViewScrollOffsetY) { + if (!mentionListViewIsDragging && mentionListViewScrollOffsetY != 0 && event.getY() > mentionListViewScrollOffsetY) { return false; } } else { - if (!mentionListViewIsScrolling && mentionListViewScrollOffsetY != 0 && event.getY() < mentionListViewScrollOffsetY) { + if (!mentionListViewIsDragging && mentionListViewScrollOffsetY != 0 && event.getY() < mentionListViewScrollOffsetY) { return false; } } - boolean result = ContentPreviewViewer.getInstance().onInterceptTouchEvent(event, mentionListView, 0, null); + boolean result = !mentionListViewIsScrolling && ContentPreviewViewer.getInstance().onInterceptTouchEvent(event, mentionListView, 0, null); return super.onInterceptTouchEvent(event) || result; } @Override public boolean onTouchEvent(MotionEvent event) { if (mentionLayoutManager.getReverseLayout()) { - if (!mentionListViewIsScrolling && mentionListViewScrollOffsetY != 0 && event.getY() > mentionListViewScrollOffsetY) { + if (!mentionListViewIsDragging && mentionListViewScrollOffsetY != 0 && event.getY() > mentionListViewScrollOffsetY) { return false; } } else { - if (!mentionListViewIsScrolling && mentionListViewScrollOffsetY != 0 && event.getY() < mentionListViewScrollOffsetY) { + if (!mentionListViewIsDragging && mentionListViewScrollOffsetY != 0 && event.getY() < mentionListViewScrollOffsetY) { return false; } } @@ -5535,7 +5833,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (newPosition != -1) { mentionListViewIgnoreLayout = true; - if (mentionsAdapter.isBotContext() && mentionsAdapter.isMediaLayout()) { + if ((mentionsAdapter.isStickers() || mentionsAdapter.isBotContext()) && mentionsAdapter.isMediaLayout()) { mentionGridLayoutManager.scrollToPositionWithOffset(newPosition, newTop); } else { mentionLayoutManager.scrollToPositionWithOffset(newPosition, newTop); @@ -5555,7 +5853,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not mentionContainer.invalidate(); } }; - mentionListView.setOnTouchListener((v, event) -> ContentPreviewViewer.getInstance().onTouch(event, mentionListView, 0, mentionsOnItemClickListener, null)); + mentionListView.setOnTouchListener((v, event) -> ContentPreviewViewer.getInstance().onTouch(event, mentionListView, 0, mentionsOnItemClickListener, mentionsAdapter.isStickers() ? contentPreviewViewerDelegate : null)); mentionListView.setTag(2); mentionLayoutManager = new LinearLayoutManager(context) { @Override @@ -5639,6 +5937,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not Object object = mentionsAdapter.getItem(position); if (object instanceof TLRPC.TL_inlineBotSwitchPM) { return 100; + } else if (object instanceof TLRPC.Document) { + return 20; } else { if (mentionsAdapter.getBotContextSwitch() != null) { position--; @@ -5656,7 +5956,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not outRect.bottom = 0; if (parent.getLayoutManager() == mentionGridLayoutManager) { int position = parent.getChildAdapterPosition(view); - if (mentionsAdapter.getBotContextSwitch() != null) { + if (mentionsAdapter.isStickers()) { + return; + } else if (mentionsAdapter.getBotContextSwitch() != null) { if (position == 0) { return; } @@ -5681,7 +5983,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not mentionListView.setAdapter(mentionsAdapter = new MentionsAdapter(context, false, dialog_id, new MentionsAdapter.MentionsAdapterDelegate() { @Override public void needChangePanelVisibility(boolean show) { - if (mentionsAdapter.isBotContext() && mentionsAdapter.isMediaLayout()) { + if ((mentionsAdapter.isStickers() || mentionsAdapter.isBotContext()) && mentionsAdapter.isMediaLayout()) { mentionListView.setLayoutManager(mentionGridLayoutManager); } else { mentionListView.setLayoutManager(mentionLayoutManager); @@ -5699,7 +6001,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not mentionContainer.setAlpha(1.0f); return; } - if (mentionsAdapter.isBotContext() && mentionsAdapter.isMediaLayout()) { + if ((mentionsAdapter.isStickers() || mentionsAdapter.isBotContext()) && mentionsAdapter.isMediaLayout()) { mentionGridLayoutManager.scrollToPositionWithOffset(0, 10000); } else if (!mentionLayoutManager.getReverseLayout()) { mentionLayoutManager.scrollToPositionWithOffset(0, mentionLayoutManager.getReverseLayout() ? -10000 : 10000); @@ -5823,7 +6125,26 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not Object object = mentionsAdapter.getItem(position); int start = mentionsAdapter.getResultStartPosition(); int len = mentionsAdapter.getResultLength(); - if (object instanceof TLRPC.Chat) { + if (object instanceof TLRPC.TL_document) { + if (chatMode == 0 && checkSlowMode(view)) { + return; + } + MessageObject.SendAnimationData sendAnimationData = null; + if (view instanceof StickerCell) { + sendAnimationData = ((StickerCell) view).getSendAnimationData(); + } + TLRPC.TL_document document = (TLRPC.TL_document) object; + Object parent = mentionsAdapter.getItemParent(position); + if (chatMode == MODE_SCHEDULED) { + String query = stickersAdapter.getQuery(); + AlertsCreator.createScheduleDatePickerDialog(getParentActivity(), dialog_id, (notify, scheduleDate) -> SendMessagesHelper.getInstance(currentAccount).sendSticker(document, query, dialog_id, replyingMessageObject, getThreadMessage(), parent, null, notify, scheduleDate)); + } else { + getSendMessagesHelper().sendSticker(document, stickersAdapter.getQuery(), dialog_id, replyingMessageObject, getThreadMessage(), parent, sendAnimationData, true, 0); + } + hideFieldPanel(false); + chatActivityEnterView.addStickerToRecent(document); + chatActivityEnterView.setFieldText(""); + } else if (object instanceof TLRPC.Chat) { TLRPC.Chat chat = (TLRPC.Chat) object; if (searchingForUser && searchContainer.getVisibility() == View.VISIBLE) { searchUserMessages(null, chat); @@ -5942,13 +6263,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { - mentionListViewIsScrolling = newState == RecyclerView.SCROLL_STATE_DRAGGING; + mentionListViewIsScrolling = newState != RecyclerView.SCROLL_STATE_IDLE; + mentionListViewIsDragging = newState == RecyclerView.SCROLL_STATE_DRAGGING; } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { int lastVisibleItem; - if (mentionsAdapter.isBotContext() && mentionsAdapter.isMediaLayout()) { + if ((mentionsAdapter.isStickers() || mentionsAdapter.isBotContext()) && mentionsAdapter.isMediaLayout()) { lastVisibleItem = mentionGridLayoutManager.findLastVisibleItemPosition(); } else { lastVisibleItem = mentionLayoutManager.findLastVisibleItemPosition(); @@ -6291,12 +6613,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (message != null) { scheduledMessagesCount++; } - if (forwardingMessages != null && !forwardingMessages.isEmpty()) { - scheduledMessagesCount += forwardingMessages.size(); + if (forwardingMessages != null && !forwardingMessages.messages.isEmpty()) { + scheduledMessagesCount += forwardingMessages.messages.size(); } updateScheduledInterface(false); } hideFieldPanel(notify, scheduleDate, true); + if (chatActivityEnterView != null && chatActivityEnterView.getEmojiView() != null) { + chatActivityEnterView.getEmojiView().onMessageSend(); + } } @Override @@ -6353,7 +6678,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public void onTextChanged(final CharSequence text, boolean bigChange) { MediaController.getInstance().setInputFieldHasText(!TextUtils.isEmpty(text) || chatActivityEnterView.isEditingMessage()); if (stickersAdapter != null && chatActivityEnterView != null && chatActivityEnterView.getVisibility() == View.VISIBLE && (bottomOverlay == null || bottomOverlay.getVisibility() != View.VISIBLE)) { - stickersAdapter.loadStikersForEmoji(text, currentChat != null && !ChatObject.canSendStickers(currentChat) || chatActivityEnterView.isEditingMessage()); + stickersAdapter.searchEmojiByKeyword(text); } if (mentionsAdapter != null) { mentionsAdapter.searchUsernameOrHashtag(text.toString(), chatActivityEnterView.getCursorPosition(), messages, false, false); @@ -6543,6 +6868,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not checkRaiseSensors(); if (chatActivityEnterView.isStickersExpanded()) { AndroidUtilities.setAdjustResizeToNothing(getParentActivity(), classGuid); + if (Bulletin.getVisibleBulletin() != null && Bulletin.getVisibleBulletin().isShowing()) { + Bulletin.getVisibleBulletin().hide(); + } } else { AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); } @@ -6621,7 +6949,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public boolean hasForwardingMessages() { - return forwardingMessages != null && !forwardingMessages.isEmpty(); + return forwardingMessages != null && !forwardingMessages.messages.isEmpty(); } }); chatActivityEnterView.setDialogId(dialog_id, currentAccount); @@ -6708,8 +7036,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatActivityEnterTopView.addReplyView(replyLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.NO_GRAVITY, 0, 0, 52, 0)); replyLayout.setOnClickListener(v -> { - if (forwardingMessages != null && !forwardingMessages.isEmpty()) { - openAnotherForward(); + if (forwardingMessages != null && !forwardingMessages.messages.isEmpty()) { + SharedConfig.forwardingOptionsHintHintShowed(); + openForwardingPreview(); } else if (replyingMessageObject != null && (!isThreadChat() || replyingMessageObject.getId() != threadMessageId)) { scrollToMessageId(replyingMessageObject.getId(), 0, true, 0, true, 0); } else if (editingMessageObject != null) { @@ -6739,25 +7068,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } chatActivityEnterTopView.addView(replyCloseImageView, LayoutHelper.createFrame(52, 46, Gravity.RIGHT | Gravity.TOP, 0, 0.5f, 0, 0)); replyCloseImageView.setOnClickListener(v -> { - if (forwardingMessages == null || forwardingMessages.isEmpty()) { + if (forwardingMessages == null || forwardingMessages.messages.isEmpty()) { showFieldPanel(false, null, null, null, foundWebPage, true, 0, true, true); } else { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setButtonsVertical(true); - builder.setTitle(LocaleController.getString("CancelForward", R.string.CancelForward)); - builder.setMessage(LocaleController.getString("CancelForwardMessage", R.string.CancelForwardMessage)); - builder.setPositiveButton(LocaleController.getString("CancelForwarding", R.string.CancelForwarding), (dialogInterface, i) -> { - if (forwardingMessages != null) { - forwardingMessages.clear(); - } - showFieldPanel(false, null, null, null, foundWebPage, true, 0, true, true); - }); - builder.setNegativeButton(LocaleController.getString("SelectOtherChat", R.string.SelectOtherChat), (dialogInterface, i) -> { - if (forwardingMessages != null && !forwardingMessages.isEmpty()) { - openAnotherForward(); - } - }); - builder.show(); + openAnotherForward(); } }); @@ -6769,9 +7083,17 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not replyObjectTextView = new SimpleTextView(context); replyObjectTextView.setTextSize(14); - replyObjectTextView.setTextColor(Theme.getColor(Theme.key_chat_replyPanelMessage)); + replyObjectTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText)); replyLayout.addView(replyObjectTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 18, Gravity.TOP | Gravity.LEFT, 52, 24, 0, 0)); + replyObjectHintTextView = new SimpleTextView(context); + replyObjectHintTextView.setTextSize(14); + replyObjectHintTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText)); + replyObjectHintTextView.setText(LocaleController.getString("TapForForwardingOptions", R.string.TapForForwardingOptions)); + replyObjectHintTextView.setAlpha(0f); + replyLayout.addView(replyObjectHintTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 18, Gravity.TOP | Gravity.LEFT, 52, 24, 0, 0)); + + replyImageView = new BackupImageView(context); replyImageView.setRoundRadius(AndroidUtilities.dp(2)); replyLayout.addView(replyImageView, LayoutHelper.createFrame(34, 34, Gravity.TOP | Gravity.LEFT, 52, 6, 0, 0)); @@ -6843,46 +7165,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not }); } - final ContentPreviewViewer.ContentPreviewViewerDelegate contentPreviewViewerDelegate = new ContentPreviewViewer.ContentPreviewViewerDelegate() { - @Override - public void sendSticker(TLRPC.Document sticker, String query, Object parent, boolean notify, int scheduleDate) { - chatActivityEnterView.onStickerSelected(sticker, query, parent, null, true, notify, scheduleDate); - } - - @Override - public boolean needSend() { - return false; - } - - @Override - public boolean canSchedule() { - return ChatActivity.this.canScheduleMessage(); - } - - @Override - public boolean isInScheduleMode() { - return chatMode == MODE_SCHEDULED; - } - - @Override - public void openSet(TLRPC.InputStickerSet set, boolean clearsInputField) { - if (set == null || getParentActivity() == null) { - return; - } - TLRPC.TL_inputStickerSetID inputStickerSet = new TLRPC.TL_inputStickerSetID(); - inputStickerSet.access_hash = set.access_hash; - inputStickerSet.id = set.id; - StickersAlert alert = new StickersAlert(getParentActivity(), ChatActivity.this, inputStickerSet, null, chatActivityEnterView); - alert.setCalcMandatoryInsets(isKeyboardVisible()); - alert.setClearsInputField(clearsInputField); - showDialog(alert); - } - - @Override - public long getDialogId() { - return dialog_id; - } - }; stickersListView = new RecyclerListView(context) { @Override public boolean onInterceptTouchEvent(MotionEvent event) { @@ -7079,7 +7361,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not bottomOverlayChatText = new UnreadCounterTextView(context); bottomOverlayChat.addView(bottomOverlayChatText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 0, 1.5f, 0, 0)); bottomOverlayChatText.setOnClickListener(view -> { - if (getParentActivity() == null) { + if (getParentActivity() == null || pullingDownOffset != 0) { return; } if (reportType >= 0) { @@ -7424,6 +7706,96 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not return fragmentView; } + private void openForwardingPreview() { + boolean keyboardVisible = chatActivityEnterView.isKeyboardVisible(); + forwardingPreviewView = new ForwardingPreviewView(contentView.getContext(), forwardingMessages, currentUser, currentChat, currentAccount) { + @Override + protected void onDismiss(boolean canShowKeyboard) { + checkShowBlur(true); + ArrayList selectedMessage = new ArrayList<>(); + forwardingMessages.getSelectedMessages(selectedMessage); + showFieldPanelForForward(true, selectedMessage); + + if (keyboardVisible && canShowKeyboard) { + AndroidUtilities.runOnUIThread(() -> chatActivityEnterView.openKeyboard(), 50); + } + + AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); + } + + @Override + protected void selectAnotherChat() { + super.selectAnotherChat(); + dismiss(false); + if (forwardingMessages != null) { + int hasPoll = 0; + boolean hasInvoice = false; + for (int a = 0, N = forwardingMessages.messages.size(); a < N; a++) { + MessageObject messageObject = forwardingMessages.messages.get(a); + if (messageObject.isPoll()) { + if (hasPoll != 2) { + hasPoll = messageObject.isPublicPoll() ? 2 : 1; + } + } else if (messageObject.isInvoice()) { + hasInvoice = true; + } + selectedMessagesIds[0].put(messageObject.getId(), messageObject); + } + Bundle args = new Bundle(); + args.putBoolean("onlySelect", true); + args.putInt("dialogsType", 3); + args.putInt("hasPoll", hasPoll); + args.putBoolean("hasInvoice", hasInvoice); + args.putInt("messagesCount", forwardingMessages.messages.size()); + DialogsActivity fragment = new DialogsActivity(args); + fragment.setDelegate(ChatActivity.this); + presentFragment(fragment); + } + } + + @Override + protected void didSendPressed() { + super.didSendPressed(); + dismiss(true); + chatActivityEnterView.getSendButton().callOnClick(); + } + }; + checkShowBlur(true); + contentView.addView(forwardingPreviewView); + + if (keyboardVisible) { + chatActivityEnterView.showEmojiView(); + openKeyboardOnAttachMenuClose = true; + } + AndroidUtilities.setAdjustResizeToNothing(getParentActivity(), classGuid); + fragmentView.requestLayout(); + } + + private void animateToNextChat() { + if (pullingDownDrawable == null) { + return; + } + if (getParentLayout().fragmentsStack.size() > 1) { + BaseFragment previousFragment = getParentLayout().fragmentsStack.get(getParentLayout().fragmentsStack.size() - 2); + if (previousFragment instanceof ChatActivity) { + getParentLayout().fragmentsStack.remove(getParentLayout().fragmentsStack.size() - 2); + } + } + Bundle bundle = new Bundle(); + bundle.putInt("chat_id", pullingDownDrawable.getChatId()); + bundle.putInt("dialog_folder_id", pullingDownDrawable.dialogFolderId); + bundle.putInt("dialog_filter_id", pullingDownDrawable.dialogFilterId); + SharedPreferences sharedPreferences = MessagesController.getNotificationsSettings(currentAccount); + sharedPreferences.edit().remove("diditem" + pullingDownDrawable.nextDialogId).apply(); + ChatActivity chatActivity = new ChatActivity(bundle); + chatActivity.setPullingDownTransition(true); + presentFragment(chatActivity); + } + + private void setPullingDownTransition(boolean fromPullingDownTransition) { + this.fromPullingDownTransition = fromPullingDownTransition; + } + private void updateBulletinLayout() { Bulletin bulletin = Bulletin.getVisibleBulletin(); if (bulletin != null && bulletinDelegate != null) { @@ -7538,28 +7910,100 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } private void openAnotherForward() { - int hasPoll = 0; - boolean hasInvoice = false; - for (int a = 0, N = forwardingMessages.size(); a < N; a++) { - MessageObject messageObject = forwardingMessages.get(a); - if (messageObject.isPoll()) { - if (hasPoll != 2) { - hasPoll = messageObject.isPublicPoll() ? 2 : 1; - } - } else if (messageObject.isInvoice()) { - hasInvoice = true; + boolean fewSenders = false; + long lastPeerId = 0; + long dialogId = 0; + for (int a = 0, N = forwardingMessages.messages.size(); a < N; a++) { + MessageObject message = forwardingMessages.messages.get(a); + if (lastPeerId == 0) { + dialogId = message.getDialogId(); + lastPeerId = message.getFromChatId(); + } else if (lastPeerId != message.getFromChatId()) { + fewSenders = true; + break; } - selectedMessagesIds[0].put(messageObject.getId(), messageObject); } - Bundle args = new Bundle(); - args.putBoolean("onlySelect", true); - args.putInt("dialogsType", 3); - args.putInt("hasPoll", hasPoll); - args.putBoolean("hasInvoice", hasInvoice); - args.putInt("messagesCount", forwardingMessages.size()); - DialogsActivity fragment = new DialogsActivity(args); - fragment.setDelegate(ChatActivity.this); - presentFragment(fragment); + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setButtonsVertical(true); + String message; + if (dialogId > 0) { + TLRPC.User user = getMessagesController().getUser((int) dialogId); + message = LocaleController.formatString("CancelForwardPrivate", R.string.CancelForwardPrivate, LocaleController.formatPluralString("MessagesBold", forwardingMessages.messages.size()), ContactsController.formatName(user.first_name, user.last_name)); + } else { + TLRPC.Chat chat = getMessagesController().getChat(-(int) dialogId); + message = LocaleController.formatString("CancelForwardChat", R.string.CancelForwardChat, LocaleController.formatPluralString("MessagesBold", forwardingMessages.messages.size()), chat.title); + } + builder.setMessage(AndroidUtilities.replaceTags(message)); + builder.setTitle(LocaleController.formatPluralString("messages", forwardingMessages.messages.size())); + builder.setPositiveButton(LocaleController.getString("CancelForwarding", R.string.CancelForwarding), (dialogInterface, i) -> { + if (forwardingMessages != null) { + forwardingMessages = null; + } + showFieldPanel(false, null, null, null, foundWebPage, true, 0, true, true); + }); +// String sendersNameString; +// if (hideForwardSendersName) { +// if (fewSenders) { +// sendersNameString = LocaleController.getString("ShowSendersName", R.string.ShowSendersName); +// } else { +// sendersNameString = LocaleController.getString("ShowSenderName", R.string.ShowSenderName); +// } +// } else { +// if (fewSenders) { +// sendersNameString = LocaleController.getString("HideSendersName", R.string.HideSendersName); +// } else { +// sendersNameString = LocaleController.getString("HideSenderName", R.string.HideSenderName); +// } +// } +// boolean fewSendersFinal = fewSenders; +// builder.setNeutralButton(sendersNameString, (dialogInterface, i) -> { +// hideForwardSendersName = !hideForwardSendersName; +// if (hideForwardSendersName) { +// if (fewSendersFinal) { +// replyNameTextView.setText(LocaleController.getString("YouSendersNameHidden", R.string.YouSendersNameHidden)); +// } else { +// replyNameTextView.setText(LocaleController.getString("YouSenderNameHidden", R.string.YouSenderNameHidden)); +// } +// } else { +// replyNameTextView.setText(formwardingNameText); +// } +// }); +// builder.setNegativeButton(LocaleController.getString("ForwardAnotherChat", R.string.ForwardAnotherChat), (dialogInterface, i) -> { +// if (forwardingMessages != null && !forwardingMessages.isEmpty()) { +// int hasPoll = 0; +// boolean hasInvoice = false; +// for (int a = 0, N = forwardingMessages.size(); a < N; a++) { +// MessageObject messageObject = forwardingMessages.get(a); +// if (messageObject.isPoll()) { +// if (hasPoll != 2) { +// hasPoll = messageObject.isPublicPoll() ? 2 : 1; +// } +// } else if (messageObject.isInvoice()) { +// hasInvoice = true; +// } +// selectedMessagesIds[0].put(messageObject.getId(), messageObject); +// } +// Bundle args = new Bundle(); +// args.putBoolean("onlySelect", true); +// args.putInt("dialogsType", 3); +// args.putInt("hasPoll", hasPoll); +// args.putBoolean("hasInvoice", hasInvoice); +// args.putInt("messagesCount", forwardingMessages.size()); +// DialogsActivity fragment = new DialogsActivity(args); +// fragment.setDelegate(ChatActivity.this); +// presentFragment(fragment); +// } +// }); + + builder.setNegativeButton(LocaleController.getString("ShowForwardingOptions", R.string.ShowForwardingOptions), (dialogInterface, i) -> { + openForwardingPreview(); + }); + AlertDialog dialog = builder.create(); + showDialog(dialog); + TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); + if (button != null) { + button.setTextColor(Theme.getColor(Theme.key_dialogTextRed2)); + } } private void openPinnedMessagesList(boolean preview) { @@ -7655,16 +8099,32 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }; if (preview) { - int w = (int) (fragmentView.getMeasuredWidth() / 6.0f); - int h = (int) (fragmentView.getMeasuredHeight() / 6.0f); - Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - canvas.scale(1.0f / 6.0f, 1.0f / 6.0f); - fragmentView.draw(canvas); - Utilities.stackBlurBitmap(bitmap, Math.max(7, Math.max(w, h) / 180)); + presentFragmentAsPreview(fragment); + checkShowBlur(true); + } else { + presentFragment(fragment, false); + } + } + + private void checkShowBlur(boolean animated) { + boolean show = (parentLayout != null && parentLayout.isInPreviewMode() && !inPreviewMode) || (forwardingPreviewView != null && forwardingPreviewView.isShowing()); + + if (show && (blurredView == null || blurredView.getTag() == null)) { if (blurredView == null) { - blurredView = new View(getParentActivity()); + blurredView = new BluredView(fragmentView.getContext(), fragmentView) { + @Override + public void setAlpha(float alpha) { + super.setAlpha(alpha); + fragmentView.invalidate(); + } + + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + fragmentView.invalidate(); + } + }; contentView.addView(blurredView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); } else { int idx = contentView.indexOfChild(blurredView); @@ -7672,15 +8132,28 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not contentView.removeView(blurredView); contentView.addView(blurredView); } + blurredView.update(); blurredView.setVisibility(View.VISIBLE); } - blurredView.setBackground(new BitmapDrawable(bitmap)); blurredView.setAlpha(0.0f); + blurredView.animate().setListener(null).cancel(); + blurredView.animate().alpha(1f).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + chatListView.invalidate(); + fragmentView.invalidate(); + } + }).start(); - presentFragmentAsPreview(fragment); - } else { - presentFragment(fragment, false); + blurredView.setTag(1); + } else if (!show && blurredView != null && blurredView.getTag() != null) { + blurredView.animate().setListener(null).cancel(); + blurredView.animate().setListener(new HideViewAfterAnimation(blurredView)).alpha(0).start(); + blurredView.setTag(null); + chatListView.invalidate(); + fragmentView.invalidate(); } } @@ -7778,6 +8251,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not pinnedViewH = Math.max(0, AndroidUtilities.dp(48) + pinnedMessageEnterOffset); } float oldPadding = chatListViewPaddingTop; + chatListViewPaddingTopOnlyTopViews = topPanelViewH + pinnedViewH; chatListViewPaddingTop = AndroidUtilities.dp(4) + contentPaddingTop + topPanelViewH + pinnedViewH; chatListViewPaddingVisibleOffset = 0; chatListViewPaddingTop += contentPanTranslation + bottomPanelTranslationY; @@ -7901,11 +8375,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (topChatPanelView != null) { topChatPanelView.setTranslationY(contentPanTranslation + contentPaddingTop + topChatPanelViewOffset); } - if (mentionListView != null && mentionLayoutManager != null && mentionsAdapter != null && !mentionsAdapter.isBotCommands()) { + if (mentionListView != null && mentionLayoutManager != null && mentionsAdapter != null && !mentionsAdapter.isBotCommands() && !mentionsAdapter.isStickers()) { if (mentionLayoutManager.getReverseLayout()) { mentionListView.setTranslationY(contentPanTranslation + contentPaddingTop); } else { - mentionListView.setTranslationY(contentPanTranslation); + mentionListView.setTranslationY(bottomPanelTranslationYReverse); } } else if (mentionListView != null) { mentionListView.setTranslationY(bottomPanelTranslationYReverse); @@ -8621,7 +9095,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public void onAnimationEnd(Animator animation) { if (runningAnimation != null && runningAnimation.equals(animation)) { if (!show) { - stickersAdapter.clearStickers(); + stickersAdapter.clearSearch(); stickersPanel.setVisibility(View.GONE); if (ContentPreviewViewer.getInstance().isVisible()) { ContentPreviewViewer.getInstance().close(); @@ -8646,26 +9120,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not })); stickersListView.setOnItemClickListener(stickersOnItemClickListener = (view, position) -> { Object item = stickersAdapter.getItem(position); - Object parent = stickersAdapter.getItemParent(position); - if (item instanceof TLRPC.TL_document) { - if (chatMode == 0 && checkSlowMode(view)) { - return; - } - MessageObject.SendAnimationData sendAnimationData = null; - if (view instanceof StickerCell) { - sendAnimationData = ((StickerCell) view).getSendAnimationData(); - } - TLRPC.TL_document document = (TLRPC.TL_document) item; - if (chatMode == MODE_SCHEDULED) { - String query = stickersAdapter.getQuery(); - AlertsCreator.createScheduleDatePickerDialog(getParentActivity(), dialog_id, (notify, scheduleDate) -> SendMessagesHelper.getInstance(currentAccount).sendSticker(document, query, dialog_id, replyingMessageObject, getThreadMessage(), parent, null, notify, scheduleDate)); - } else { - getSendMessagesHelper().sendSticker(document, stickersAdapter.getQuery(), dialog_id, replyingMessageObject, getThreadMessage(), parent, sendAnimationData, true, 0); - } - hideFieldPanel(false); - chatActivityEnterView.addStickerToRecent(document); - chatActivityEnterView.setFieldText(""); - } else if (item instanceof String) { + if (item instanceof String) { String emoji = (String) item; SpannableString string = new SpannableString(emoji); Emoji.replaceEmoji(string, chatActivityEnterView.getEditField().getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false); @@ -9520,7 +9975,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not SendMessagesHelper.getInstance(currentAccount).sendMessage(caption, dialog_id, null, null, null, true, null, null, null, true, 0, null); caption = null; } - getSendMessagesHelper().sendMessage(fmessages, dialog_id, true, 0); + getSendMessagesHelper().sendMessage(fmessages, dialog_id, false, false,true, 0); SendMessagesHelper.prepareSendingDocuments(getAccountInstance(), files, files, null, caption, null, dialog_id, replyingMessageObject, getThreadMessage(), null, editingMessageObject, notify, scheduleDate); afterMessageSend(); } @@ -9750,20 +10205,14 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not }); } - private void forwardMessages(ArrayList arrayList, boolean fromMyName, boolean notify, int scheduleDate) { + private void forwardMessages(ArrayList arrayList, boolean fromMyName, boolean hideCaption, boolean notify, int scheduleDate) { if (arrayList == null || arrayList.isEmpty()) { return; } if ((scheduleDate != 0) == (chatMode == MODE_SCHEDULED)) { waitingForSendingMessageLoad = true; } - if (!fromMyName) { - AlertsCreator.showSendMediaAlert(getSendMessagesHelper().sendMessage(arrayList, dialog_id, notify, scheduleDate), this); - } else { - for (MessageObject object : arrayList) { - getSendMessagesHelper().processForwardFromMyName(object, dialog_id); - } - } + AlertsCreator.showSendMediaAlert(getSendMessagesHelper().sendMessage(arrayList, dialog_id, fromMyName, hideCaption, notify, scheduleDate), this); } public boolean shouldShowImport() { @@ -9822,6 +10271,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (chatActivityEnterView == null) { return; } + boolean showHint = false; if (show) { if (messageObjectToReply == null && messageObjectsToForward == null && messageObjectToEdit == null && webPage == null) { return; @@ -9968,7 +10418,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } editingMessageObject = null; chatActivityEnterView.setEditingMessageObject(null, false); - forwardingMessages = messageObjectsToForward; + if (forwardingMessages == null) { + forwardingMessages = new ForwardingMessagesParams(messageObjectsToForward, dialog_id); + } if (foundWebPage != null) { return; } @@ -10050,64 +10502,63 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not break; } } - replyNameTextView.setText(userNames); + formwardingNameText = userNames; if (type == -1 || type == 0 || type == 10 || type == 11) { - if (messageObjectsToForward.size() == 1 && messageObjectsToForward.get(0).messageText != null) { - MessageObject messageObject = messageObjectsToForward.get(0); - if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGame) { - replyObjectTextView.setText(Emoji.replaceEmoji(messageObject.messageOwner.media.game.title, replyObjectTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false)); - } else { - String mess = messageObject.messageText.toString(); - if (mess.length() > 150) { - mess = mess.substring(0, 150); - } - mess = mess.replace('\n', ' '); - replyObjectTextView.setText(Emoji.replaceEmoji(mess, replyObjectTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false)); - } + replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardMessagesCount", messageObjectsToForward.size())); + } else if (type == 1) { + replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardPhoto", messageObjectsToForward.size())); + if (messageObjectsToForward.size() == 1) { + messageObjectToReply = messageObjectsToForward.get(0); + } + } else if (type == 4) { + replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardLocation", messageObjectsToForward.size())); + } else if (type == 3) { + replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardVideo", messageObjectsToForward.size())); + if (messageObjectsToForward.size() == 1) { + messageObjectToReply = messageObjectsToForward.get(0); + } + } else if (type == 12) { + replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardContact", messageObjectsToForward.size())); + } else if (type == 2) { + replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardAudio", messageObjectsToForward.size())); + } else if (type == MessageObject.TYPE_ROUND_VIDEO) { + replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardRound", messageObjectsToForward.size())); + } else if (type == 14) { + replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardMusic", messageObjectsToForward.size())); + } else if (type == MessageObject.TYPE_STICKER || type == MessageObject.TYPE_ANIMATED_STICKER) { + replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardSticker", messageObjectsToForward.size())); + } else if (type == 17) { + replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardPoll", messageObjectsToForward.size())); + } else if (type == 8 || type == 9) { + if (messageObjectsToForward.size() == 1 & type == 9) { + messageObjectToReply = messageObjectsToForward.get(0); + } + if (messageObjectsToForward.size() == 1 && type == 8) { + replyNameTextView.setText(LocaleController.getString("AttachGif", R.string.AttachGif)); } else { - replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedMessageCount", messageObjectsToForward.size())); + replyNameTextView.setText(LocaleController.formatPluralString("PreviewForwardFile", messageObjectsToForward.size())); } + } + + if (forwardingMessages.hideForwardSendersName) { + replyObjectTextView.setText(LocaleController.getString("HiddenSendersNameDescription", R.string.HiddenSendersNameDescription)); } else { - if (type == 1) { - replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedPhoto", messageObjectsToForward.size())); - if (messageObjectsToForward.size() == 1) { - messageObjectToReply = messageObjectsToForward.get(0); - } - } else if (type == 4) { - replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedLocation", messageObjectsToForward.size())); - } else if (type == 3) { - replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedVideo", messageObjectsToForward.size())); - if (messageObjectsToForward.size() == 1) { - messageObjectToReply = messageObjectsToForward.get(0); - } - } else if (type == 12) { - replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedContact", messageObjectsToForward.size())); - } else if (type == 2) { - replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedAudio", messageObjectsToForward.size())); - } else if (type == MessageObject.TYPE_ROUND_VIDEO) { - replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedRound", messageObjectsToForward.size())); - } else if (type == 14) { - replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedMusic", messageObjectsToForward.size())); - } else if (type == MessageObject.TYPE_STICKER || type == MessageObject.TYPE_ANIMATED_STICKER) { - replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedSticker", messageObjectsToForward.size())); - } else if (type == 17) { - replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedPoll", messageObjectsToForward.size())); - } else if (type == 8 || type == 9) { - if (messageObjectsToForward.size() == 1) { - if (type == 8) { - replyObjectTextView.setText(LocaleController.getString("AttachGif", R.string.AttachGif)); - } else { - String name; - if ((name = FileLoader.getDocumentFileName(messageObjectsToForward.get(0).getDocument())).length() != 0) { - replyObjectTextView.setText(name); - } - messageObjectToReply = messageObjectsToForward.get(0); - } - } else { - replyObjectTextView.setText(LocaleController.formatPluralString("ForwardedFile", messageObjectsToForward.size())); + if ((type == -1 || type == 0 || type == 10 || type == 11) && (messageObjectsToForward.size() == 1 && messageObjectsToForward.get(0).messageText != null)) { + MessageObject messageObject = messageObjectsToForward.get(0); + String mess = messageObject.messageText.toString(); + if (mess.length() > 150) { + mess = mess.substring(0, 150); } + mess = mess.replace('\n', ' '); + String finalString = LocaleController.formatString("ForwardingFromNameAndMessage", R.string.ForwardingFromNameAndMessage, userNames, mess); + replyObjectTextView.setText(Emoji.replaceEmoji(finalString, replyObjectTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false)); + } else { + replyObjectTextView.setText(LocaleController.formatString("ForwardingFromNames", R.string.ForwardingFromNames, userNames)); } } + if (!SharedConfig.forwardingOptionsHintShown) { + showHint = true; + } } else { replyIconImageView.setImageResource(R.drawable.msg_link); if (webPage instanceof TLRPC.TL_webPagePending) { @@ -10185,7 +10636,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (thumbMediaMessageObject != null && thumbMediaMessageObject.isRoundVideo()) { replyImageView.setRoundRadius(AndroidUtilities.dp(17)); } else { - replyImageView.setRoundRadius(0); + replyImageView.setRoundRadius(AndroidUtilities.dp(2)); } replyImageSize = size; replyImageCacheType = cacheType; @@ -10211,14 +10662,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not foundWebPage = null; chatActivityEnterView.setWebPage(null, !cancel); if (webPage != null && (replyingMessageObject != null || forwardingMessages != null || editingMessageObject != null)) { - showFieldPanel(true, replyingMessageObject, editingMessageObject, forwardingMessages, null, notify, scheduleDate, false, true); + showFieldPanel(true, replyingMessageObject, editingMessageObject, forwardingMessages != null ? forwardingMessages.messages : null, null, notify, scheduleDate, false, true); return; } } if (forwardingMessages != null) { - ArrayList messagesToForward = forwardingMessages; + ArrayList messagesToForward = new ArrayList<>(); + forwardingMessages.getSelectedMessages(messagesToForward); + forwardMessages(messagesToForward, forwardingMessages.hideForwardSendersName, forwardingMessages.hideCaption, notify, scheduleDate != 0 && scheduleDate != 0x7ffffffe ? scheduleDate + 1 : scheduleDate); forwardingMessages = null; - forwardMessages(messagesToForward, false, notify, scheduleDate != 0 && scheduleDate != 0x7ffffffe ? scheduleDate + 1 : scheduleDate); } chatActivityEnterView.setForceShowSendButton(false, animated); if (!waitingForSendingMessageLoad) { @@ -10232,6 +10684,31 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not replyImageLocation = null; replyImageLocationObject = null; } + + if (showHint) { + if (tapForForwardingOptionsHitRunnable == null) { + AndroidUtilities.runOnUIThread(tapForForwardingOptionsHitRunnable = () -> { + showTapForForwardingOptionsHit = !showTapForForwardingOptionsHit; + replyObjectTextView.setPivotX(0); + replyObjectHintTextView.setPivotX(0); + if (showTapForForwardingOptionsHit) { + replyObjectTextView.animate().alpha(0f).scaleX(0.98f).scaleY(0.98f).setDuration(150).start(); + replyObjectHintTextView.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(150).start(); + } else { + replyObjectTextView.animate().alpha(1f).scaleX(1f).scaleY(1f).setDuration(150).start(); + replyObjectHintTextView.animate().alpha(0f).scaleX(0.98f).scaleY(0.98f).setDuration(150).start(); + } + AndroidUtilities.runOnUIThread(tapForForwardingOptionsHitRunnable, 6000); + }, 6000); + } + } else { + if (tapForForwardingOptionsHitRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(tapForForwardingOptionsHitRunnable); + tapForForwardingOptionsHitRunnable = null; + } + replyObjectTextView.setAlpha(1f); + replyObjectHintTextView.setAlpha(0); + } } private void moveScrollToLastMessage() { @@ -10548,8 +11025,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not maxVisibleMessageObject = messageObject; } - messageCell.setVisiblePart(viewTop, viewBottom - viewTop, recyclerChatViewHeight, keyboardOffset, view.getY() + (isKeyboardVisible() ? chatListView.getTop() : actionBar.getMeasuredHeight()) - contentView.getBackgroundTranslationY(), contentView.getBackgroundSizeY()); - + messageCell.setVisiblePart(viewTop, viewBottom - viewTop, recyclerChatViewHeight, keyboardOffset, view.getY() + (isKeyboardVisible() ? chatListView.getTop() : actionBar.getMeasuredHeight()) - contentView.getBackgroundTranslationY(), contentView.getMeasuredWidth(), contentView.getBackgroundSizeY()); + markSponsoredAsRead(messageObject); if (!threadMessageVisible && threadMessageObject != null && messageObject == threadMessageObject && messageCell.getBottom() > chatListViewPaddingTop) { threadMessageVisible = true; } @@ -10779,6 +11256,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else if (previousPinnedMessageId != 0 && !pinnedMessageIds.isEmpty()) { currentPinnedMessageId = previousPinnedMessageId; } + boolean animated = (fromPullingDownTransition && fragmentView.getVisibility() == View.VISIBLE) || (openAnimationStartTime != 0 && SystemClock.elapsedRealtime() >= openAnimationStartTime + 150); if (previousPinnedMessageId != currentPinnedMessageId) { int animateToNext; if (previousPinnedMessageId == 0) { @@ -10788,9 +11266,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else { animateToNext = 2; } - updatePinnedMessageView(openAnimationStartTime != 0 && SystemClock.elapsedRealtime() >= openAnimationStartTime + 150, animateToNext); + + updatePinnedMessageView(animated, animateToNext); } else { - updatePinnedListButton(openAnimationStartTime != 0 && SystemClock.elapsedRealtime() >= openAnimationStartTime + 150); + updatePinnedListButton(animated); } } if (floatingDateView != null) { @@ -10821,14 +11300,12 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } } - if (!isThreadChat()) { - if (forwardEndReached[0] && maxPositiveUnreadId == minMessageId[0] || maxNegativeUnreadId == minMessageId[0]) { + if (forwardEndReached[0] && maxPositiveUnreadId == minMessageId[0] || maxNegativeUnreadId == minMessageId[0]) { + newUnreadMessageCount = 0; + } else { + newUnreadMessageCount -= counterDecrement; + if (newUnreadMessageCount < 0) { newUnreadMessageCount = 0; - } else { - newUnreadMessageCount -= counterDecrement; - if (newUnreadMessageCount < 0) { - newUnreadMessageCount = 0; - } } } if (inLayout) { @@ -11309,7 +11786,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (currentEncryptedChat == null || bigEmptyView == null) { bottomOverlay.setVisibility(View.INVISIBLE); if (stickersAdapter != null && chatActivityEnterView != null && chatActivityEnterView.hasText()) { - stickersAdapter.loadStikersForEmoji(chatActivityEnterView.getFieldText(), false); + stickersAdapter.searchEmojiByKeyword(chatActivityEnterView.getFieldText()); } return; } @@ -12423,7 +12900,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not resumeDelayedFragmentAnimation(); MessageObject messageObject = messArr.get(0); getMessagesController().markDialogAsRead(dialog_id, messageObject.getId(), messageObject.getId(), messageObject.messageOwner.date, false, 0, 0, true, 0); - + AndroidUtilities.cancelRunOnUIThread(fragmentTransitionRunnable); + fragmentTransitionRunnable.run(); return; } if (chatMode != mode) { @@ -12590,6 +13068,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else if (!chatWasReset && startLoadFromMessageId != 0 && (load_type == 3 || load_type == 4)) { last_message_id = (Integer) args[5]; } + if (isThreadChat() && threadUnreadMessagesCount != 0) { + unread_to_load = threadUnreadMessagesCount; + threadUnreadMessagesCount = 0; + } int newRowsCount = 0; if (load_type != 0 && (isThreadChat() && first_unread_id != 0 || startLoadFromMessageId != 0 || last_message_id != 0)) { @@ -12608,7 +13090,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (loadsCount == 1 && messArr.size() > 20) { loadsCount++; } - + boolean isFirstLoading = firstLoading; if (firstLoading) { if (!forwardEndReached[loadIndex]) { messages.clear(); @@ -12632,6 +13114,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not AndroidUtilities.runOnUIThread(() -> { getNotificationCenter().runDelayedNotifications(); resumeDelayedFragmentAnimation(); + AndroidUtilities.cancelRunOnUIThread(fragmentTransitionRunnable); + fragmentTransitionRunnable.run(); }); } @@ -13262,6 +13746,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (showDateAfter) { showFloatingDateView(false); } + addSponsoredMessages(!isFirstLoading); checkScrollForLoad(false); if (postponedScroll) { @@ -13320,6 +13805,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } chatWasReset = false; + } else if (id == NotificationCenter.invalidateMotionBackground) { + if (chatListView != null) { + chatListView.invalidateViews(); + } + if (messageEnterTransitionContainer != null) { + messageEnterTransitionContainer.invalidate(); + } } else if (id == NotificationCenter.emojiLoaded) { if (chatListView != null) { chatListView.invalidateViews(); @@ -13439,6 +13931,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } checkWaitingForReplies(); } + } else if (id == NotificationCenter.didLoadSponsoredMessages) { + addSponsoredMessages(true); } else if (id == NotificationCenter.closeChats) { if (args != null && args.length > 0) { long did = (Long) args[0]; @@ -14576,9 +15070,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } if (updated) { updateVisibleRows(); - /*if (chatLayoutManager != null && chatLayoutManager.findFirstVisibleItemPosition() == 0) { - moveScrollToLastMessage(); - }*/ } } else if (id == NotificationCenter.didReceivedWebpagesInUpdates) { if (foundWebPage != null) { @@ -15059,6 +15550,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not }, true); } + private boolean sponsoredMessagesAdded; + private void addSponsoredMessages(boolean animated) { + if (sponsoredMessagesAdded || chatMode != 0 || !ChatObject.isChannel(currentChat) || currentChat.megagroup || currentChat.gigagroup || !forwardEndReached[0]) { + return; + } + ArrayList arrayList = getMessagesController().getSponsoredMessages(dialog_id); + if (arrayList == null) { + return; + } + sponsoredMessagesAdded = true; + processNewMessages(arrayList); + } + private void checkGroupCallJoin(boolean fromServer) { if (groupCall == null || voiceChatHash == null || !openAnimationEnded) { if (voiceChatHash != null && fromServer && chatInfo != null && chatInfo.call == null && fragmentView != null && getParentActivity() != null) { @@ -15349,11 +15853,22 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } + private void rotateMotionBackgroundDrawable() { + Drawable wallpaper = Theme.getCachedWallpaperNonBlocking(); + if (wallpaper instanceof MotionBackgroundDrawable) { + ((MotionBackgroundDrawable) wallpaper).switchToNextPosition(); + } + MotionBackgroundDrawable drawable = Theme.chat_msgOutDrawable != null ? Theme.chat_msgOutDrawable.getMotionBackgroundDrawable() : null; + if (drawable instanceof MotionBackgroundDrawable) { + drawable.switchToNextPosition(); + } + } + private void processNewMessages(ArrayList arr) { int currentUserId = getUserConfig().getClientUserId(); boolean updateChat = false; boolean hasFromMe = false; - + boolean isAd = false; if (chatListItemAnimator != null) { chatListItemAnimator.setShouldAnimateEnterFromBottom(true); @@ -15363,6 +15878,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not LongSparseArray scheduledGroupReplacement = null; for (int a = 0, N = arr.size(); a < N; a++) { MessageObject messageObject = arr.get(a); + if (!isAd) { + isAd = messageObject.isSponsored(); + } int messageId = messageObject.getId(); if (threadMessageId != 0) { if (messageId > 0 && messageId <= (messageObject.isOut() ? threadMaxOutboxReadId : threadMaxInboxReadId)) { @@ -15473,10 +15991,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not for (int a = 0; a < arr.size(); a++) { MessageObject obj = arr.get(a); if (obj.isOut()) { - Drawable wallpaper = Theme.getCachedWallpaperNonBlocking(); - if (wallpaper instanceof MotionBackgroundDrawable) { - ((MotionBackgroundDrawable) wallpaper).switchToNextPosition(); - } + rotateMotionBackgroundDrawable(); } if (threadMessageId != 0 && threadMessageId != obj.getReplyTopMsgId() && threadMessageId != obj.getReplyMsgId()) { continue; @@ -15542,6 +16057,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (obj.messageOwner.mentioned && obj.isContentUnread()) { newMentionsCount++; } + } + if (!isAd) { newUnreadMessageCount++; } if (obj.type == 10 || obj.type == 11) { @@ -15574,10 +16091,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not continue; } if (obj.isOut()) { - Drawable wallpaper = Theme.getCachedWallpaperNonBlocking(); - if (wallpaper instanceof MotionBackgroundDrawable) { - ((MotionBackgroundDrawable) wallpaper).switchToNextPosition(); - } + rotateMotionBackgroundDrawable(); } int placeToPaste = -1; int messageId = obj.getId(); @@ -15813,7 +16327,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } obj.stableId = lastStableId++; messages.add(placeToPaste, obj); - if (placeToPaste == 0) { + if (placeToPaste == 0 && !obj.isSponsored()) { needMoveScrollToLastMessage = true; } if (chatAdapter != null) { @@ -15833,6 +16347,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (!obj.isOut() && obj.messageOwner.mentioned && obj.isContentUnread()) { newMentionsCount++; } + } + if (!isAd) { newUnreadMessageCount++; } if (obj.type == 10 || obj.type == 11) { @@ -15874,25 +16390,27 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else { diff = 0; } - if (lastVisible == 0 && diff <= AndroidUtilities.dp(5) || hasFromMe) { - newUnreadMessageCount = 0; - if (!firstLoading && chatMode != MODE_SCHEDULED) { - if (paused) { - scrollToTopOnResume = true; - } else { - forceScrollToTop = true; - moveScrollToLastMessage(); + if (!isAd) { + if (lastVisible == 0 && diff <= AndroidUtilities.dp(5) || hasFromMe) { + newUnreadMessageCount = 0; + if (!firstLoading && chatMode != MODE_SCHEDULED) { + if (paused) { + scrollToTopOnResume = true; + } else { + forceScrollToTop = true; + moveScrollToLastMessage(); + } } - } - } else { - if (newUnreadMessageCount != 0 && pagedownButtonCounter != null) { - if (prevSetUnreadCount != newUnreadMessageCount) { - prevSetUnreadCount = newUnreadMessageCount; - pagedownButtonCounter.setCount(newUnreadMessageCount, true); + } else { + if (newUnreadMessageCount != 0 && pagedownButtonCounter != null) { + if (prevSetUnreadCount != newUnreadMessageCount) { + prevSetUnreadCount = newUnreadMessageCount; + pagedownButtonCounter.setCount(newUnreadMessageCount, true); + } } + canShowPagedownButton = true; + updatePagedownButtonVisibility(true); } - canShowPagedownButton = true; - updatePagedownButtonVisibility(true); } if (newMentionsCount != 0 && mentiondownButtonCounter != null) { mentiondownButtonCounter.setVisibility(View.VISIBLE); @@ -16529,20 +17047,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatActivityEnterView.onBeginHide(); } } + checkShowBlur(true); transitionAnimationIndex = getNotificationCenter().setAnimationInProgress(transitionAnimationIndex, alowedNotifications); } - @Override - protected void onTransitionAnimationProgress(boolean isOpen, float progress) { - if (blurredView != null && blurredView.getVisibility() == View.VISIBLE) { - if (isOpen) { - blurredView.setAlpha(1.0f - progress); - } else { - blurredView.setAlpha(progress); - } - } - } - @Override public void onTransitionAnimationEnd(boolean isOpen, boolean backward) { if (isOpen) { @@ -16562,10 +17070,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } fixedKeyboardHeight = -1; if (isOpen) { - if (blurredView != null && blurredView.getVisibility() == View.VISIBLE) { - blurredView.setVisibility(View.GONE); - blurredView.setBackground(null); - } + checkShowBlur(false); openAnimationEnded = true; getNotificationCenter().onAnimationFinish(transitionAnimationIndex); if (Build.VERSION.SDK_INT >= 21) { @@ -17996,10 +18501,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public void onResume() { super.onResume(); - if (parentLayout != null && !parentLayout.isInPreviewMode() && blurredView != null && blurredView.getVisibility() == View.VISIBLE) { - blurredView.setVisibility(View.GONE); - blurredView.setBackground(null); - } + checkShowBlur(false); activityResumeTime = System.currentTimeMillis(); if (openImport && getSendMessagesHelper().getImportingHistory(dialog_id) != null) { ImportingAlert alert = new ImportingAlert(getParentActivity(), null, this); @@ -18120,6 +18622,11 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not checkBotCommands(); updateTitle(); showGigagroupConvertAlert(); + + if (pullingDownOffset != 0) { + pullingDownOffset = 0; + chatListView.invalidate(); + } } public void checkAdjustResize() { @@ -18411,15 +18918,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (AndroidUtilities.isTablet()) { if (AndroidUtilities.isSmallTablet() && ApplicationLoader.applicationContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { actionBar.setBackButtonDrawable(new BackDrawable(false)); - /*if (fragmentContextView != null && fragmentContextView.getParent() == null) { - ((ViewGroup) fragmentView).addView(fragmentContextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 38, Gravity.TOP | Gravity.LEFT, 0, -36, 0, 0)); - }*/ } else { actionBar.setBackButtonDrawable(new BackDrawable(parentLayout == null || parentLayout.fragmentsStack.isEmpty() || parentLayout.fragmentsStack.get(0) == ChatActivity.this || parentLayout.fragmentsStack.size() == 1)); - /*if (fragmentContextView != null && fragmentContextView.getParent() != null) { - fragmentView.setPadding(0, 0, 0, 0); - ((ViewGroup) fragmentView).removeView(fragmentContextView); - }*/ } return false; } @@ -18702,7 +19202,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } } - if (threadMessageObjects != null && threadMessageObjects.contains(message)) { + if (message.isSponsored() || threadMessageObjects != null && threadMessageObjects.contains(message)) { single = true; } @@ -18760,7 +19260,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not allowEdit = captionsCount < 2; } if (chatMode == MODE_SCHEDULED || threadMessageObjects != null && threadMessageObjects.contains(message) || - type == 1 && message.getDialogId() == mergeDialogId || + message.isSponsored() || type == 1 && message.getDialogId() == mergeDialogId || message.messageOwner.action instanceof TLRPC.TL_messageActionSecureValuesSent || currentEncryptedChat == null && message.getId() < 0 || bottomOverlayChat != null && bottomOverlayChat.getVisibility() == View.VISIBLE || @@ -18798,11 +19298,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not icons.add(selectedObject.messageOwner.ttl_period != 0 ? R.drawable.msg_delete_auto : R.drawable.msg_delete); } else if (type == 1) { if (currentChat != null) { - /*if (selectedObject.messageOwner.action instanceof TLRPC.TL_messageActionGroupCall) { - items.add(LocaleController.getString("VoipGroupJoinCall", R.string.VoipGroupJoinCall)); - options.add(29); - icons.add(R.drawable.msg_callback); - }*/ if (allowChatActions) { items.add(LocaleController.getString("Reply", R.string.Reply)); options.add(8); @@ -18891,7 +19386,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not options.add(27); icons.add(R.drawable.msg_viewreplies); } - if (chatMode != MODE_SCHEDULED && ChatObject.isChannel(currentChat) && selectedObject.getDialogId() != mergeDialogId) { + if (!selectedObject.isSponsored() && chatMode != MODE_SCHEDULED && ChatObject.isChannel(currentChat) && selectedObject.getDialogId() != mergeDialogId) { items.add(LocaleController.getString("CopyLink", R.string.CopyLink)); options.add(22); icons.add(R.drawable.msg_link); @@ -19048,7 +19543,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not icons.add(R.drawable.msg_unfave); } } - if (chatMode != MODE_SCHEDULED && !selectedObject.needDrawBluredPreview() && !selectedObject.isLiveLocation() && selectedObject.type != 16) { + if (!selectedObject.isSponsored() && chatMode != MODE_SCHEDULED && !selectedObject.needDrawBluredPreview() && !selectedObject.isLiveLocation() && selectedObject.type != 16) { items.add(LocaleController.getString("Forward", R.string.Forward)); options.add(2); icons.add(R.drawable.msg_forward); @@ -19183,7 +19678,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not Rect rect = new Rect(); - ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(getParentActivity()); + ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(getParentActivity(), R.drawable.popup_fixed_alert); + popupLayout.setMinimumWidth(AndroidUtilities.dp(200)); popupLayout.setOnTouchListener(new View.OnTouchListener() { private int[] pos = new int[2]; @@ -19215,30 +19711,48 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not Rect backgroundPaddings = new Rect(); Drawable shadowDrawable = getParentActivity().getResources().getDrawable(R.drawable.popup_fixed_alert).mutate(); shadowDrawable.getPadding(backgroundPaddings); - popupLayout.setBackgroundDrawable(shadowDrawable); popupLayout.setBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground)); - LinearLayout linearLayout = new LinearLayout(getParentActivity()); - ScrollView scrollView; - if (Build.VERSION.SDK_INT >= 21) { - scrollView = new ScrollView(getParentActivity(), null, 0, R.style.scrollbarShapeStyle) { - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - setMeasuredDimension(linearLayout.getMeasuredWidth(), getMeasuredHeight()); - } - }; - } else { - scrollView = new ScrollView(getParentActivity()); - } - scrollView.setClipToPadding(false); - popupLayout.addView(scrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); - - linearLayout.setMinimumWidth(AndroidUtilities.dp(200)); - linearLayout.setOrientation(LinearLayout.VERTICAL); - scrimPopupWindowItems = new ActionBarMenuSubItem[items.size()]; + scrimPopupWindowItems = new ActionBarMenuSubItem[items.size() + (selectedObject.isSponsored() ? 1 : 0)]; for (int a = 0, N = items.size(); a < N; a++) { + if (a == 0 && selectedObject.isSponsored()) { + ActionBarMenuSubItem cell = new ActionBarMenuSubItem(getParentActivity(), true, true); + cell.setTextAndIcon(LocaleController.getString("SponsoredMessageInfo", R.string.SponsoredMessageInfo), R.drawable.menu_info); + cell.setItemHeight(56); + cell.setTag(R.id.width_tag, 240); + cell.setMultiline(); + scrimPopupWindowItems[scrimPopupWindowItems.length - 1] = cell; + popupLayout.addView(cell); + cell.setOnClickListener(v1 -> { + AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); + builder.setTitle(LocaleController.getString("SponsoredMessageAlertTitle", R.string.SponsoredMessageAlertTitle)); + builder.setMessage(LocaleController.getString("SponsoredMessageAlertText", R.string.SponsoredMessageAlertText)); + builder.setNegativeButton(LocaleController.getString("SponsoredMessageAlertLearnMore", R.string.SponsoredMessageAlertLearnMore), (dialogInterface, i) -> Browser.openUrl(getParentActivity(), LocaleController.getString("SponsoredMessageAlertLearnMoreUrl", R.string.SponsoredMessageAlertLearnMoreUrl))); + builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null); + showDialog(builder.create()); + scrimView = null; + contentView.invalidate(); + chatListView.invalidate(); + if (scrimPopupWindow != null) { + scrimPopupWindow.dismiss(); + } + }); + + View gap = new View(getParentActivity()); + gap.setMinimumWidth(AndroidUtilities.dp(196)); + gap.setTag(1000); + gap.setTag(R.id.object_tag, 1); + popupLayout.addView(gap); + LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) cell.getLayoutParams(); + if (LocaleController.isRTL) { + layoutParams.gravity = Gravity.RIGHT; + } + layoutParams.width = LayoutHelper.MATCH_PARENT; + layoutParams.height = AndroidUtilities.dp(6); + gap.setLayoutParams(layoutParams); + } ActionBarMenuSubItem cell = new ActionBarMenuSubItem(getParentActivity(), a == 0, a == N - 1); + cell.setMinimumWidth(AndroidUtilities.dp(200)); cell.setTextAndIcon(items.get(a), icons.get(a)); Integer option = options.get(a); if (option == 1 && selectedObject.messageOwner.ttl_period != 0) { @@ -19247,7 +19761,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not cell.setSubtextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText6)); } scrimPopupWindowItems[a] = cell; - linearLayout.addView(cell); + popupLayout.addView(cell); final int i = a; cell.setOnClickListener(v1 -> { if (selectedObject == null || i >= options.size()) { @@ -19262,7 +19776,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } }); } - scrollView.addView(linearLayout, LayoutHelper.createScroll(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP)); scrimPopupWindow = new ActionBarPopupWindow(popupLayout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT) { @Override public void dismiss() { @@ -19317,6 +19830,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not scrimPopupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED); scrimPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); scrimPopupWindow.getContentView().setFocusableInTouchMode(true); + popupLayout.setFitItems(true); int popupX = v.getLeft() + (int) x - popupLayout.getMeasuredWidth() + backgroundPaddings.left - AndroidUtilities.dp(28); if (popupX < AndroidUtilities.dp(6)) { popupX = AndroidUtilities.dp(6); @@ -20276,7 +20790,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (message != null) { getSendMessagesHelper().sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null); } - getSendMessagesHelper().sendMessage(fmessages, did, true, 0); + getSendMessagesHelper().sendMessage(fmessages, did, false, false,true, 0); } fragment.finishFragment(); if (dids.size() == 1) { @@ -20355,7 +20869,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not @Override public boolean onBackPressed() { - if (messagesSearchListView.getTag() != null) { + if (forwardingPreviewView != null && forwardingPreviewView.isShowing()) { + forwardingPreviewView.dismiss(true); + return false; + } else if (messagesSearchListView.getTag() != null) { showMessagesSearchListView(false); return false; } else if (scrimPopupWindow != null) { @@ -20532,9 +21049,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not if (messageObject.getDialogId() == mergeDialogId && startMessageObject.getDialogId() != mergeDialogId) { continue; } - /*if (startDialogId == mergeDialogId && messageId != 0 && messageObject.getDialogId() != mergeDialogId) { - messageId = 0; - }*/ if ((currentEncryptedChat == null && messageObject.getId() > messageId || currentEncryptedChat != null && messageObject.getId() < messageId) && (messageObject.isVoice() || messageObject.isRoundVideo()) && (!playingUnreadMedia || messageObject.isContentUnread() && !messageObject.isOut())) { messageObjects.add(messageObject); } @@ -20789,6 +21303,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not long dialogId = arrayList.get(0).getDialogId(); args.putInt("chat_id", (int) -dialogId); args.putInt("message_id", Math.max(1, discussionMessage.read_inbox_max_id)); + args.putInt("unread_count", discussionMessage.unread_count); args.putBoolean("historyPreloaded", history != null); ChatActivity chatActivity = new ChatActivity(args); chatActivity.setThreadMessages(arrayList, originalChat, req.msg_id, discussionMessage.read_inbox_max_id, discussionMessage.read_outbox_max_id); @@ -21313,8 +21828,13 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not String link = null; if (messageObject1.messageOwner.fwd_from != null) { - lower_id = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.saved_from_peer); - messageId = messageObject1.messageOwner.fwd_from.saved_from_msg_id; + if (messageObject1.messageOwner.fwd_from.saved_from_peer != null) { + lower_id = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.saved_from_peer); + messageId = messageObject1.messageOwner.fwd_from.saved_from_msg_id; + } else if (messageObject1.messageOwner.fwd_from.from_id != null) { + lower_id = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.from_id); + messageId = messageObject1.messageOwner.fwd_from.channel_post; + } } if (lower_id < 0) { @@ -21406,7 +21926,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } } - void openPhotoViewerForMessage(ChatMessageCell cell, MessageObject message) { + void openPhotoViewerForMessage(ChatMessageCell cell, MessageObject message) { if (cell == null) { int count = chatListView.getChildCount(); for (int a = 0; a < count; a++) { @@ -21470,10 +21990,23 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not chatListView.setImportantForAccessibility(mentionContainer.getVisibility() == View.VISIBLE || (scrimPopupWindow != null && scrimPopupWindow.isShowing()) ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); } } + + private void markSponsoredAsRead(MessageObject object) { + if (!object.isSponsored() || object.viewsReloaded) { + return; + } + object.viewsReloaded = true; + TLRPC.TL_channels_viewSponsoredMessage req = new TLRPC.TL_channels_viewSponsoredMessage(); + req.channel = MessagesController.getInputChannel(currentChat); + req.random_id = object.sponsoredId; + getConnectionsManager().sendRequest(req, (response, error) -> { + + }); + } @Override public boolean canBeginSlide() { - return swipeBackEnabled && chatActivityEnterView.swipeToBackEnabled(); + return swipeBackEnabled && chatActivityEnterView.swipeToBackEnabled() && pullingDownOffset == 0; } @Override @@ -22158,7 +22691,21 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not } else if (type == 5) { openVCard(getMessagesController().getUser(messageObject.messageOwner.media.user_id), messageObject.messageOwner.media.vcard, messageObject.messageOwner.media.first_name, messageObject.messageOwner.media.last_name); } else { - if (messageObject.messageOwner.media != null && messageObject.messageOwner.media.webpage != null) { + if (messageObject.isSponsored()) { + Bundle args = new Bundle(); + long peerId = MessageObject.getPeerId(messageObject.messageOwner.from_id); + if (peerId < 0) { + args.putInt("chat_id", -(int) peerId); //TODO long + } else { + args.putInt("user_id", (int) peerId); + } + if (messageObject.botStartParam != null) { + args.putString("inline_query", messageObject.botStartParam); + } + if (getMessagesController().checkCanOpenChat(args, ChatActivity.this)) { + presentFragment(new ChatActivity(args)); + } + } else if (messageObject.messageOwner.media != null && messageObject.messageOwner.media.webpage != null) { if (!openLinkInternally(messageObject.messageOwner.media.webpage.url, messageObject.getId())) { Browser.openUrl(getParentActivity(), messageObject.messageOwner.media.webpage.url); } @@ -22759,7 +23306,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not keyboardOffset = chatActivityEnterView.getEmojiPadding(); } if (holder.itemView instanceof ChatMessageCell) { - ((ChatMessageCell) view).setVisiblePart(viewTop, viewBottom - viewTop, recyclerChatViewHeight, keyboardOffset, view.getY() + (isKeyboardVisible() ? chatListView.getTop() : actionBar.getMeasuredHeight()) - contentView.getBackgroundTranslationY(), contentView.getBackgroundSizeY()); + ChatMessageCell chatMessageCell = (ChatMessageCell) view; + chatMessageCell.setVisiblePart(viewTop, viewBottom - viewTop, recyclerChatViewHeight, keyboardOffset, view.getY() + (isKeyboardVisible() ? chatListView.getTop() : actionBar.getMeasuredHeight()) - contentView.getBackgroundTranslationY(), contentView.getMeasuredWidth(), contentView.getBackgroundSizeY()); + markSponsoredAsRead(chatMessageCell.getMessageObject()); } else if (holder.itemView instanceof ChatActionCell) { if (actionBar != null && contentView != null) { ((ChatActionCell) view).setVisiblePart(view.getY() + (isKeyboardVisible() ? chatListView.getTop() : actionBar.getMeasuredHeight()) - contentView.getBackgroundTranslationY(), contentView.getBackgroundSizeY()); @@ -23379,7 +23928,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgOutDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgOutMediaDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubble)); - themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient)); + themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient1)); + themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient2)); + themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient3)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutSelectedDrawable, Theme.chat_msgOutMediaSelectedDrawable}, null, Theme.key_chat_outBubbleSelected)); themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutSelectedDrawable, Theme.chat_msgOutSelectedDrawable}, null, Theme.key_chat_outBubbleGradientSelectedOverlay)); themeDescriptions.add(new ThemeDescription(chatListView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{ChatActionCell.class}, Theme.chat_actionTextPaint, null, null, Theme.key_chat_serviceText)); @@ -23586,6 +24137,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not themeDescriptions.add(new ThemeDescription(mentionContainer, 0, null, Theme.chat_composeBackgroundPaint, null, null, Theme.key_chat_messagePanelBackground)); themeDescriptions.add(new ThemeDescription(mentionContainer, 0, null, null, new Drawable[]{Theme.chat_composeShadowDrawable}, null, Theme.key_chat_messagePanelShadow)); + themeDescriptions.add(new ThemeDescription(mentionContainer, 0, null, null, new Drawable[]{Theme.chat_composeShadowRoundDrawable}, null, Theme.key_chat_messagePanelBackground)); themeDescriptions.add(new ThemeDescription(searchContainer, 0, null, Theme.chat_composeBackgroundPaint, null, null, Theme.key_chat_messagePanelBackground)); themeDescriptions.add(new ThemeDescription(searchContainer, 0, null, null, new Drawable[]{Theme.chat_composeShadowDrawable}, null, Theme.key_chat_messagePanelShadow)); themeDescriptions.add(new ThemeDescription(bottomOverlay, 0, null, Theme.chat_composeBackgroundPaint, null, null, Theme.key_chat_messagePanelBackground)); @@ -23714,7 +24266,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not themeDescriptions.add(new ThemeDescription(replyLineView, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_chat_replyPanelLine)); themeDescriptions.add(new ThemeDescription(replyNameTextView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_chat_replyPanelName)); - themeDescriptions.add(new ThemeDescription(replyObjectTextView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_chat_replyPanelMessage)); + themeDescriptions.add(new ThemeDescription(replyObjectTextView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteGrayText)); + themeDescriptions.add(new ThemeDescription(replyObjectHintTextView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteGrayText)); themeDescriptions.add(new ThemeDescription(replyIconImageView, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_chat_replyPanelIcons)); themeDescriptions.add(new ThemeDescription(replyCloseImageView, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_chat_replyPanelClose)); themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, selectedBackgroundDelegate, Theme.key_chat_replyPanelName)); @@ -23868,4 +24421,102 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public ChatAvatarContainer getAvatarContainer() { return avatarContainer; } + + + @Override + protected AnimatorSet onCustomTransitionAnimation(boolean isOpen, Runnable callback) { + if (isOpen && fromPullingDownTransition && getParentLayout().fragmentsStack.size() > 1) { + BaseFragment previousFragment = getParentLayout().fragmentsStack.get(getParentLayout().fragmentsStack.size() - 2); + if (previousFragment instanceof ChatActivity) { + ChatActivity previousChat = (ChatActivity) previousFragment; + previousChat.setTransitionToChatActivity(this); + fragmentView.setAlpha(0); + contentView.setSkipBackgroundDrawing(true); + avatarContainer.setTranslationY(AndroidUtilities.dp(8)); + avatarContainer.getAvatarImageView().setAlpha(0); + avatarContainer.getAvatarImageView().setTranslationY(-AndroidUtilities.dp(8)); + toPullingDownTransition = true; + ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1f); + + boolean useAlphaForContextView = previousChat.fragmentContextView.getMeasuredHeight() != fragmentContextView.getMeasuredHeight(); + valueAnimator.addUpdateListener(valueAnimator1 -> { + float progress = (float) valueAnimator1.getAnimatedValue(); + previousChat.setTransitionToChatProgress(progress); + float y = AndroidUtilities.dp(8) * (1f - progress); + avatarContainer.setTranslationY(y); + avatarContainer.getAvatarImageView().setTranslationY(-y); + y = -AndroidUtilities.dp(8) * progress; + previousChat.avatarContainer.setTranslationY(y); + previousChat.avatarContainer.getAvatarImageView().setTranslationY(-y); + avatarContainer.getAvatarImageView().setScaleX(0.8f + 0.2f * progress); + avatarContainer.getAvatarImageView().setScaleY(0.8f + 0.2f * progress); + avatarContainer.getAvatarImageView().setAlpha(progress); + previousChat.avatarContainer.getAvatarImageView().setScaleX(0.8f + 0.2f * (1f - progress)); + previousChat.avatarContainer.getAvatarImageView().setScaleY(0.8f + 0.2f * (1f - progress)); + previousChat.avatarContainer.getAvatarImageView().setAlpha(1f - progress); + + if (useAlphaForContextView) { + previousChat.fragmentContextView.setAlpha(1f - progress); + } + previousChat.pinnedMessageView.setAlpha(1f - progress); + previousChat.topChatPanelView.setAlpha(1f - progress); + }); + + updateChatListViewTopPadding(); + fragmentTransition = new AnimatorSet(); + fragmentTransition.addListener(new AnimatorListenerAdapter() { + + int index; + + @Override + public void onAnimationStart(Animator animation) { + super.onAnimationStart(animation); + index = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(index, null); + } + + @Override + public void onAnimationEnd(Animator animation) { + fragmentTransition = null; + NotificationCenter.getInstance(currentAccount).onAnimationFinish(index); + super.onAnimationEnd(animation); + contentView.invalidate(); + contentView.setSkipBackgroundDrawing(false); + toPullingDownTransition = false; + previousChat.setTransitionToChatProgress(0); + previousChat.setTransitionToChatActivity(null); + fragmentView.setAlpha(1f); + callback.run(); + avatarContainer.setTranslationY(0); + previousChat.avatarContainer.setTranslationY(0); + previousChat.avatarContainer.getAvatarImageView().setTranslationY(0); + avatarContainer.getAvatarImageView().setScaleX(1f); + avatarContainer.getAvatarImageView().setScaleY(1f); + avatarContainer.getAvatarImageView().setAlpha(1f); + previousChat.avatarContainer.getAvatarImageView().setScaleX(1f); + previousChat.avatarContainer.getAvatarImageView().setScaleY(1f); + previousChat.avatarContainer.getAvatarImageView().setAlpha(1f); + + previousChat.pinnedMessageView.setAlpha(1f); + previousChat.topChatPanelView.setAlpha(1f); + } + }); + fragmentTransition.setDuration(300); + fragmentTransition.setInterpolator(CubicBezierInterpolator.DEFAULT); + fragmentTransition.playTogether(valueAnimator); + AndroidUtilities.runOnUIThread(fragmentTransitionRunnable, 200); + return fragmentTransition; + } + } + return null; + } + + private void setTransitionToChatActivity(ChatActivity chatActivity) { + pullingDownAnimateToActivity = chatActivity; + } + + private void setTransitionToChatProgress(float p) { + pullingDownAnimateProgress = p; + fragmentView.invalidate(); + chatListView.invalidate(); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java index 00873e4b6..dd93185e2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java @@ -592,8 +592,13 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image cameraDrawable.setCurrentFrame(0); setAvatarCell.imageView.playAnimation(); }, dialogInterface -> { - cameraDrawable.setCustomEndFrame(86); - setAvatarCell.imageView.playAnimation(); + if (!imageUpdater.isUploadingImage()) { + cameraDrawable.setCustomEndFrame(86); + setAvatarCell.imageView.playAnimation(); + } else { + cameraDrawable.setCurrentFrame(0, false); + } + }); cameraDrawable.setCurrentFrame(0); cameraDrawable.setCustomEndFrame(43); @@ -1037,6 +1042,12 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image } else { avatarImage.setImage(ImageLocation.getForLocal(avatar), "50_50", avatarDrawable, currentChat); setAvatarCell.setTextAndIcon(LocaleController.getString("ChatSetNewPhoto", R.string.ChatSetNewPhoto), R.drawable.menu_camera2, true); + if (cameraDrawable == null) { + cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); + } + setAvatarCell.imageView.setTranslationY(-AndroidUtilities.dp(9)); + setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); + setAvatarCell.imageView.setAnimation(cameraDrawable); showAvatarProgress(true, false); } }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatPullingDownDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatPullingDownDrawable.java new file mode 100644 index 000000000..ee23fcad7 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatPullingDownDrawable.java @@ -0,0 +1,671 @@ +package org.telegram.ui; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.RectF; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.View; + +import com.google.android.exoplayer2.util.Log; + +import org.telegram.messenger.AccountInstance; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DialogObject; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AvatarDrawable; +import org.telegram.ui.Components.CounterView; +import org.telegram.ui.Components.CubicBezierInterpolator; + +import java.util.ArrayList; + +public class ChatPullingDownDrawable implements NotificationCenter.NotificationCenterDelegate { + + public int dialogFolderId; + public int dialogFilterId; + int lastWidth; + float circleRadius; + + Paint arrowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + TextPaint textPaint2 = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private Paint xRefPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + Path path = new Path(); + + StaticLayout chatNameLayout; + StaticLayout layout1; + StaticLayout layout2; + + int chatNameWidth; + int layout1Width; + int layout2Width; + + ImageReceiver imageReceiver = new ImageReceiver(); + TLRPC.Chat nextChat; + + AnimatorSet showReleaseAnimator; + + float swipeToReleaseProgress; + float bounceProgress; + + boolean animateSwipeToRelease; + boolean animateCheck; + float checkProgress; + long lastHapticTime; + float lastProgress; + boolean emptyStub; + float progressToBottomPannel; + private final View fragmentView; + + boolean drawFolderBackground; + Runnable onAnimationFinishRunnable; + public long nextDialogId; + View parentView; + + CounterView.CounterDrawable counterDrawable = new CounterView.CounterDrawable(null); + int params[] = new int[3]; + private final int currentAccount; + private final int folderId; + private final int filterId; + private final long currentDialog; + + public ChatPullingDownDrawable(int currentAccount, View fragmentView, long currentDialog, int folderId, int filterId) { + this.fragmentView = fragmentView; + this.currentAccount = currentAccount; + this.currentDialog = currentDialog; + this.folderId = folderId; + this.filterId = filterId; + + arrowPaint.setStrokeWidth(AndroidUtilities.dpf2(2.8f)); + arrowPaint.setStrokeCap(Paint.Cap.ROUND); + counterDrawable.gravity = Gravity.LEFT; + counterDrawable.setType(CounterView.CounterDrawable.TYPE_CHAT_PULLING_DOWN); + counterDrawable.circlePaint = Theme.chat_actionBackgroundPaint; + counterDrawable.textPaint = textPaint; + + textPaint.setTextSize(AndroidUtilities.dp(13)); + textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + textPaint2.setTextSize(AndroidUtilities.dp(14)); + + xRefPaint.setColor(0xff000000); + xRefPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + + updateDialog(); + } + + public void updateDialog() { + TLRPC.Dialog dialog = getNextUnreadDialog(currentDialog, folderId, filterId, true, params); + if (dialog != null) { + nextDialogId = dialog.id; + drawFolderBackground = params[0] == 1; + dialogFolderId = params[1]; + dialogFilterId = params[2]; + emptyStub = false; + nextChat = MessagesController.getInstance(currentAccount).getChat((int) -dialog.id); + if (nextChat == null) { + MessagesController.getInstance(currentAccount).getChat((int) dialog.id); + } + AvatarDrawable avatarDrawable = new AvatarDrawable(); + avatarDrawable.setInfo(nextChat); + imageReceiver.setImage(ImageLocation.getForChat(nextChat, ImageLocation.TYPE_SMALL), "50_50", avatarDrawable, null, UserConfig.getInstance(0).getCurrentUser(), 0); + MessagesController.getInstance(currentAccount).ensureMessagesLoaded(dialog.id, 0, null); + counterDrawable.setCount(dialog.unread_count, false); + } else { + drawFolderBackground = false; + emptyStub = true; + } + } + + public void setWidth(int width) { + if (width != lastWidth) { + circleRadius = AndroidUtilities.dp(56) / 2f; + lastWidth = width; + + String nameStr = nextChat != null ? nextChat.title : LocaleController.getString("SwipeToGoNextChannelEnd", R.string.SwipeToGoNextChannelEnd); + chatNameWidth = (int) textPaint.measureText(nameStr); + chatNameWidth = Math.min(chatNameWidth, lastWidth - AndroidUtilities.dp(60)); + chatNameLayout = new StaticLayout(nameStr, textPaint, chatNameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + + String str1 = null; + String str2 = null; + + if (drawFolderBackground && dialogFolderId != folderId && dialogFolderId != 0) { + str1 = LocaleController.getString("SwipeToGoNextArchive", R.string.SwipeToGoNextArchive); + str2 = LocaleController.getString("ReleaseToGoNextArchive", R.string.ReleaseToGoNextArchive); + } else if (drawFolderBackground) { + str1 = LocaleController.getString("SwipeToGoNextFolder", R.string.SwipeToGoNextFolder); + str2 = LocaleController.getString("ReleaseToGoNextFolder", R.string.ReleaseToGoNextFolder); + } else { + str1 = LocaleController.getString("SwipeToGoNextChannel", R.string.SwipeToGoNextChannel); + str2 = LocaleController.getString("ReleaseToGoNextChannel", R.string.ReleaseToGoNextChannel); + } + layout1Width = (int) textPaint2.measureText(str1); + layout1Width = Math.min(layout1Width, lastWidth - AndroidUtilities.dp(60)); + layout1 = new StaticLayout(str1, textPaint2, layout1Width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + + + layout2Width = (int) textPaint2.measureText(str2); + layout2Width = Math.min(layout2Width, lastWidth - AndroidUtilities.dp(60)); + layout2 = new StaticLayout(str2, textPaint2, layout2Width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + + + float cx = lastWidth / 2f; + float cy = AndroidUtilities.dp(12) + circleRadius; + imageReceiver.setImageCoords(cx - AndroidUtilities.dp(40) / 2f, cy - AndroidUtilities.dp(40) / 2f, AndroidUtilities.dp(40), AndroidUtilities.dp(40)); + imageReceiver.setRoundRadius((int) (AndroidUtilities.dp(40) / 2f)); + + counterDrawable.setSize(AndroidUtilities.dp(28), AndroidUtilities.dp(100)); + } + + } + + public void draw(Canvas canvas, View parent, float progress, float alpha) { + this.parentView = parent; + counterDrawable.setParent(parent); + int oldAlpha, oldAlpha1, oldAlpha2, oldAlpha3; + float offset = AndroidUtilities.dp(110) * progress; + if (offset < AndroidUtilities.dp(8)) { + return; + } + + if (progress < 0.2f) { + alpha *= progress * 5f; + } + Theme.applyServiceShaderMatrix(lastWidth, parent.getMeasuredHeight(), 0, parent.getMeasuredHeight() - offset); + + textPaint.setColor(Theme.getColor(Theme.key_chat_serviceText)); + arrowPaint.setColor(Theme.getColor(Theme.key_chat_serviceText)); + textPaint2.setColor(Theme.getColor(Theme.key_chat_messagePanelHint)); + + oldAlpha = Theme.chat_actionBackgroundPaint.getAlpha(); + oldAlpha1 = Theme.chat_actionBackgroundGradientDarkenPaint.getAlpha(); + oldAlpha2 = textPaint.getAlpha(); + oldAlpha3 = arrowPaint.getAlpha(); + Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha((int) (oldAlpha1 * alpha)); + Theme.chat_actionBackgroundPaint.setAlpha((int) (oldAlpha * alpha)); + textPaint.setAlpha((int) (oldAlpha2 * alpha)); + imageReceiver.setAlpha(alpha); + + if ((progress >= 1f && lastProgress < 1f) || (progress < 1f && lastProgress == 1f)) { + long time = System.currentTimeMillis(); + if (time - lastHapticTime > 100) { + parent.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); + lastHapticTime = time; + } + lastProgress = progress; + } + + if (progress == 1f && !animateSwipeToRelease) { + animateSwipeToRelease = true; + animateCheck = true; + showReleaseState(true, parent); + } else if (progress != 1f && animateSwipeToRelease) { + animateSwipeToRelease = false; + showReleaseState(false, parent); + } + + float cx = lastWidth / 2f; + float bounceOffset = bounceProgress * -AndroidUtilities.dp(4); + + if (emptyStub) { + offset -= bounceOffset; + } + + + float widthRadius = Math.max(0, Math.min(circleRadius, offset / 2f - AndroidUtilities.dp(16) * progress - AndroidUtilities.dp(4))); + float widthRadius2 = Math.max(0, Math.min(circleRadius * progress, offset / 2f - AndroidUtilities.dp(8) * progress)); + float size = (widthRadius2 * 2 - AndroidUtilities.dp2(16)) * (1f - swipeToReleaseProgress) + AndroidUtilities.dp(56) * swipeToReleaseProgress; + + if (swipeToReleaseProgress < 1f || emptyStub) { + float bottom = -AndroidUtilities.dp(8) * (1f - swipeToReleaseProgress) + (-offset + AndroidUtilities.dp(56)) * swipeToReleaseProgress; + AndroidUtilities.rectTmp.set(cx - widthRadius, -offset, cx + widthRadius, bottom); + if (swipeToReleaseProgress > 0 && !emptyStub) { + float inset = AndroidUtilities.dp(16) * swipeToReleaseProgress; + AndroidUtilities.rectTmp.inset(inset, inset); + } + drawBackground(canvas, AndroidUtilities.rectTmp); + + float arrowCy = -offset + AndroidUtilities.dp(24) + AndroidUtilities.dp(8) * (1f - progress) - AndroidUtilities.dp(36) * swipeToReleaseProgress; + canvas.save(); + AndroidUtilities.rectTmp.inset(AndroidUtilities.dp(1), AndroidUtilities.dp(1)); + canvas.clipRect(AndroidUtilities.rectTmp); + if (swipeToReleaseProgress > 0f) { + arrowPaint.setAlpha((int) ((1f - swipeToReleaseProgress) * 255)); + } + drawArrow(canvas, cx, arrowCy, AndroidUtilities.dp(24) * progress); + + if (emptyStub) { + float top = (-AndroidUtilities.dp(8) - AndroidUtilities.dp2(8) * progress - size) * (1f - swipeToReleaseProgress) + (-offset - AndroidUtilities.dp(2)) * swipeToReleaseProgress + bounceOffset; + arrowPaint.setAlpha(oldAlpha3); + drawCheck(canvas, cx, top + AndroidUtilities.dp(28)); + } + canvas.restore(); + } + + if (chatNameLayout != null && swipeToReleaseProgress > 0) { + Theme.chat_actionBackgroundPaint.setAlpha((int) (oldAlpha * alpha)); + textPaint.setAlpha((int) (oldAlpha2 * alpha)); + + float y = AndroidUtilities.dp(20) * (1f - swipeToReleaseProgress) - AndroidUtilities.dp(36) * swipeToReleaseProgress + bounceOffset; + AndroidUtilities.rectTmp.set((lastWidth - chatNameWidth) / 2f, y, lastWidth - (lastWidth - chatNameWidth) / 2f, y + chatNameLayout.getHeight()); + AndroidUtilities.rectTmp.inset(-AndroidUtilities.dp(8), -AndroidUtilities.dp(4)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(15), AndroidUtilities.dp(15), Theme.chat_actionBackgroundPaint); + if (Theme.hasGradientService()) { + canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(15), AndroidUtilities.dp(15), Theme.chat_actionBackgroundGradientDarkenPaint); + } + + canvas.save(); + canvas.translate((lastWidth - chatNameWidth) / 2f, y); + chatNameLayout.draw(canvas); + canvas.restore(); + } + + if (!emptyStub && size > 0) { + float top = (-AndroidUtilities.dp(8) - AndroidUtilities.dp2(8) * progress - size) * (1f - swipeToReleaseProgress) + (-offset + AndroidUtilities.dp(4)) * swipeToReleaseProgress + bounceOffset; + imageReceiver.setRoundRadius((int) (size / 2f)); + imageReceiver.setImageCoords(cx - size / 2f, top, size, size); + + if (swipeToReleaseProgress > 0) { + canvas.saveLayerAlpha(imageReceiver.getImageX(), imageReceiver.getImageY(), imageReceiver.getImageX() + imageReceiver.getImageWidth(), imageReceiver.getImageY() + imageReceiver.getImageHeight(), 255, Canvas.ALL_SAVE_FLAG); + imageReceiver.draw(canvas); + canvas.scale(swipeToReleaseProgress, swipeToReleaseProgress, cx + AndroidUtilities.dp(12) + counterDrawable.getCenterX(), top - AndroidUtilities.dp(6) + AndroidUtilities.dp(14)); + canvas.translate(cx + AndroidUtilities.dp(12), top - AndroidUtilities.dp(6)); + counterDrawable.updateBackgroundRect(); + counterDrawable.rectF.inset(-AndroidUtilities.dp(2), -AndroidUtilities.dp(2)); + canvas.drawRoundRect(counterDrawable.rectF, counterDrawable.rectF.height() / 2f, counterDrawable.rectF.height() / 2f, xRefPaint); + canvas.restore(); + + canvas.save(); + canvas.scale(swipeToReleaseProgress, swipeToReleaseProgress, cx + AndroidUtilities.dp(12) + counterDrawable.getCenterX(), top - AndroidUtilities.dp(6) + AndroidUtilities.dp(14)); + canvas.translate(cx + AndroidUtilities.dp(12), top - AndroidUtilities.dp(6)); + counterDrawable.draw(canvas); + canvas.restore(); + } else { + + imageReceiver.draw(canvas); + } + } + + Theme.chat_actionBackgroundPaint.setAlpha(oldAlpha); + Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha(oldAlpha1); + textPaint.setAlpha(oldAlpha2); + arrowPaint.setAlpha(oldAlpha3); + imageReceiver.setAlpha(1f); + + } + + private void drawCheck(Canvas canvas, float cx, float cy) { + if (!animateCheck) { + return; + } + if (checkProgress < 1f) { + checkProgress += 16 / 220f; + if (checkProgress > 1f) { + checkProgress = 1f; + } + } + float p1 = checkProgress > 0.5f ? 1f : checkProgress / 0.5f; + float p2 = checkProgress < 0.5f ? 0 : (checkProgress - 0.5f) / 0.5f; + canvas.save(); + canvas.clipRect(AndroidUtilities.rectTmp); + canvas.translate(cx - AndroidUtilities.dp(24), cy - AndroidUtilities.dp(24)); + float x1 = AndroidUtilities.dp(16); + float y1 = AndroidUtilities.dp(26); + float x2 = AndroidUtilities.dp(22); + float y2 = AndroidUtilities.dp(32); + float x3 = AndroidUtilities.dp(32); + float y3 = AndroidUtilities.dp(20); + canvas.drawLine(x1, y1, x1 * (1f - p1) + x2 * p1, y1 * (1f - p1) + y2 * p1, arrowPaint); + if (p2 > 0) { + canvas.drawLine(x2, y2, x2 * (1f - p2) + x3 * p2, y2 * (1f - p2) + y3 * p2, arrowPaint); + } + canvas.restore(); + } + + private void drawBackground(Canvas canvas, RectF rectTmp) { + if (drawFolderBackground) { + path.reset(); + float roundRadius = rectTmp.width() * 0.2f; + float folderOffset = rectTmp.width() * 0.1f; + float folderOffset2 = rectTmp.width() * 0.03f; + float roundRadius2 = folderOffset / 2f; + + float h = rectTmp.height() - folderOffset; + + path.moveTo(rectTmp.right, rectTmp.top + roundRadius + folderOffset); + path.rQuadTo(0, -roundRadius, -roundRadius, -roundRadius); + path.rLineTo(-(rectTmp.width() - (2 * roundRadius)) / 2 + roundRadius2 * 2 - folderOffset2, 0); + path.rQuadTo(-roundRadius2 / 2, 0, -roundRadius2 * 2, -folderOffset / 2); + path.rQuadTo(-roundRadius2 / 2, -folderOffset / 2, -roundRadius2 * 2, -folderOffset / 2); + path.rLineTo(-(rectTmp.width() - (2 * roundRadius)) / 2 + roundRadius2 * 2 + folderOffset2, 0); + path.rQuadTo(-roundRadius, 0, -roundRadius, roundRadius); + path.rLineTo(0, (h + folderOffset - (2 * roundRadius))); + path.rQuadTo(0, roundRadius, roundRadius, roundRadius); + path.rLineTo((rectTmp.width() - (2 * roundRadius)), 0); + path.rQuadTo(roundRadius, 0, roundRadius, -roundRadius); + path.rLineTo(0, -(h - (2 * roundRadius))); + path.close(); + canvas.drawPath(path, Theme.chat_actionBackgroundPaint); + if (Theme.hasGradientService()) { + canvas.drawPath(path, Theme.chat_actionBackgroundGradientDarkenPaint); + } + } else { + canvas.drawRoundRect(AndroidUtilities.rectTmp, circleRadius, circleRadius, Theme.chat_actionBackgroundPaint); + if (Theme.hasGradientService()) { + canvas.drawRoundRect(AndroidUtilities.rectTmp, circleRadius, circleRadius, Theme.chat_actionBackgroundGradientDarkenPaint); + } + } + } + + private void showReleaseState(boolean show, View parent) { + if (showReleaseAnimator != null) { + showReleaseAnimator.removeAllListeners(); + showReleaseAnimator.cancel(); + } + if (show) { + ValueAnimator out = ValueAnimator.ofFloat(swipeToReleaseProgress, 1f); + out.addUpdateListener(animation -> { + swipeToReleaseProgress = (float) animation.getAnimatedValue(); + parent.invalidate(); + fragmentView.invalidate(); + }); + + out.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + out.setDuration(250); + + bounceProgress = 0; + + ValueAnimator bounceUp = ValueAnimator.ofFloat(0f, 1f); + bounceUp.addUpdateListener(animation -> { + bounceProgress = (float) animation.getAnimatedValue(); + parent.invalidate(); + }); + + bounceUp.setInterpolator(CubicBezierInterpolator.EASE_BOTH); + bounceUp.setDuration(180); + + ValueAnimator bounceDown = ValueAnimator.ofFloat(1f, -0.5f); + bounceDown.addUpdateListener(animation -> { + bounceProgress = (float) animation.getAnimatedValue(); + parent.invalidate(); + }); + + bounceDown.setInterpolator(CubicBezierInterpolator.EASE_BOTH); + bounceDown.setDuration(120); + + ValueAnimator bounceOut = ValueAnimator.ofFloat(-0.5f, 0f); + bounceOut.addUpdateListener(animation -> { + bounceProgress = (float) animation.getAnimatedValue(); + parent.invalidate(); + }); + + bounceOut.setInterpolator(CubicBezierInterpolator.EASE_BOTH); + bounceOut.setDuration(100); + + showReleaseAnimator = new AnimatorSet(); + showReleaseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + bounceProgress = 0f; + swipeToReleaseProgress = 1f; + parent.invalidate(); + fragmentView.invalidate(); + if (onAnimationFinishRunnable != null) { + onAnimationFinishRunnable.run(); + onAnimationFinishRunnable = null; + } + } + }); + + AnimatorSet bounce = new AnimatorSet(); + bounce.playSequentially(bounceUp, bounceDown, bounceOut); + + showReleaseAnimator.playTogether(out, bounce); + showReleaseAnimator.start(); + } else { + ValueAnimator out = ValueAnimator.ofFloat(swipeToReleaseProgress, 0f); + out.addUpdateListener(animation -> { + swipeToReleaseProgress = (float) animation.getAnimatedValue(); + fragmentView.invalidate(); + parent.invalidate(); + }); + + out.setInterpolator(CubicBezierInterpolator.DEFAULT); + out.setDuration(220); + showReleaseAnimator = new AnimatorSet(); + showReleaseAnimator.playTogether(out); + showReleaseAnimator.start(); + } + } + + private void drawArrow(Canvas canvas, float cx, float cy, float size) { + canvas.save(); + float s = size / AndroidUtilities.dpf2(24); + canvas.scale(s, s, cx, cy - AndroidUtilities.dp(20)); + canvas.translate(cx - AndroidUtilities.dp2(12), cy - AndroidUtilities.dp(12)); + canvas.drawLine(AndroidUtilities.dpf2(12.5f), AndroidUtilities.dpf2(4f), AndroidUtilities.dpf2(12.5f), AndroidUtilities.dpf2(22), arrowPaint); + canvas.drawLine(AndroidUtilities.dpf2(3.5f), AndroidUtilities.dpf2(12), AndroidUtilities.dpf2(12.5f), AndroidUtilities.dpf2(3.5f), arrowPaint); + canvas.drawLine(AndroidUtilities.dpf2(25 - 3.5f), AndroidUtilities.dpf2(12), AndroidUtilities.dpf2(12.5f), AndroidUtilities.dpf2(3.5f), arrowPaint); + canvas.restore(); + } + + public void onAttach() { + imageReceiver.onAttachedToWindow(); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.updateInterfaces); + } + + public void onDetach() { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.updateInterfaces); + imageReceiver.onDetachedFromWindow(); + lastProgress = 0; + lastHapticTime = 0; + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (nextDialogId !=0 ) { + TLRPC.Dialog dialog = MessagesController.getInstance(currentAccount).dialogs_dict.get(nextDialogId); + if (dialog != null) { + counterDrawable.setCount(dialog.unread_count, true); + if (parentView != null) { + parentView.invalidate(); + } + } + } + } + + + public static TLRPC.Dialog getNextUnreadDialog(long currentDialogId, int folderId, int filterId) { + return getNextUnreadDialog(currentDialogId, folderId, filterId, true, null); + } + + public static TLRPC.Dialog getNextUnreadDialog(long currentDialogId, int folderId, int filterId, boolean searchNext, int[] params) { + MessagesController messagesController = AccountInstance.getInstance(UserConfig.selectedAccount).getMessagesController(); + if (params != null) { + params[0] = 0; + params[1] = folderId; + params[2] = filterId; + } + ArrayList dialogs = null; + if (filterId != 0) { + dialogs = messagesController.dialogFiltersById.get(filterId).dialogs; + } else { + dialogs = messagesController.getDialogs(folderId); + } + if (dialogs == null) { + return null; + } + for (int i = 0; i < dialogs.size(); i++) { + TLRPC.Dialog dialog = dialogs.get(i); + int lower_id = (int) dialog.id; + TLRPC.Chat chat = messagesController.getChat(-lower_id); + if (chat != null && dialog.id != currentDialogId && dialog.unread_count > 0 && DialogObject.isChannel(dialog) && !chat.megagroup && !messagesController.isPromoDialog(dialog.id, false)) { + return dialog; + } + } + + if (searchNext) { + if (filterId != 0) { + for (int i = 0; i < messagesController.dialogFilters.size(); i++) { + int newFilterId = messagesController.dialogFilters.get(i).id; + if (filterId != newFilterId) { + TLRPC.Dialog dialog = getNextUnreadDialog(currentDialogId, folderId, newFilterId, false, params); + if (dialog != null) { + if (params != null) { + params[0] = 1; + } + return dialog; + } + } + } + } + + for (int i = 0; i < messagesController.dialogsByFolder.size(); i++) { + int newFolderId = messagesController.dialogsByFolder.keyAt(i); + if (folderId != newFolderId) { + TLRPC.Dialog dialog = getNextUnreadDialog(currentDialogId, newFolderId, 0, false, params); + if (dialog != null) { + if (params != null) { + params[0] = 1; + } + return dialog; + } + } + } + } + + return null; + } + + public int getChatId() { + return nextChat.id; + } + + public void drawBottomPanel(Canvas canvas, int top, int bottom, int width) { + if (showBottomPanel && progressToBottomPannel != 1f) { + progressToBottomPannel += 16f / 150f; + if (progressToBottomPannel > 1f) { + progressToBottomPannel = 1f; + } else { + fragmentView.invalidate(); + } + } else if (!showBottomPanel && progressToBottomPannel != 0) { + progressToBottomPannel -= 16f / 150f; + if (progressToBottomPannel < 0) { + progressToBottomPannel = 0; + } else { + fragmentView.invalidate(); + } + } + int oldAlpha = Theme.chat_composeBackgroundPaint.getAlpha(); + int oldAlphaText = textPaint2.getAlpha(); + + Theme.chat_composeBackgroundPaint.setAlpha((int) (oldAlpha * progressToBottomPannel)); + canvas.drawRect(0, top, width, bottom, Theme.chat_composeBackgroundPaint); + + + if (layout1 != null && swipeToReleaseProgress < 1f) { + textPaint2.setAlpha((int) (oldAlphaText * (1f - swipeToReleaseProgress) * progressToBottomPannel)); + float y = top + AndroidUtilities.dp(18) - AndroidUtilities.dp(10) * swipeToReleaseProgress; + canvas.save(); + canvas.translate((lastWidth - layout1Width) / 2f, y); + layout1.draw(canvas); + canvas.restore(); + } + + if (layout2 != null && swipeToReleaseProgress > 0) { + textPaint2.setAlpha((int) (oldAlphaText * swipeToReleaseProgress * progressToBottomPannel)); + float y = top + AndroidUtilities.dp(18) + AndroidUtilities.dp(10) * (1f - swipeToReleaseProgress); + canvas.save(); + canvas.translate((lastWidth - layout2Width) / 2f, y); + layout2.draw(canvas); + canvas.restore(); + } + + textPaint2.setAlpha(oldAlpha); + Theme.chat_composeBackgroundPaint.setAlpha(oldAlpha); + } + + boolean showBottomPanel; + + public void showBottomPanel(boolean b) { + showBottomPanel = b; + fragmentView.invalidate(); + } + + public boolean needDrawBottomPanel() { + return (showBottomPanel || progressToBottomPannel > 0) && !emptyStub; + } + + public boolean animationIsRunning() { + return swipeToReleaseProgress != 1f; + } + + public void runOnAnimationFinish(Runnable runnable) { + if (showReleaseAnimator != null) { + showReleaseAnimator.removeAllListeners(); + showReleaseAnimator.cancel(); + } + onAnimationFinishRunnable = runnable; + showReleaseAnimator = new AnimatorSet(); + + ValueAnimator out = ValueAnimator.ofFloat(swipeToReleaseProgress, 1f); + out.addUpdateListener(animation -> { + swipeToReleaseProgress = (float) animation.getAnimatedValue(); + fragmentView.invalidate(); + if (parentView != null) { + parentView.invalidate(); + } + }); + ValueAnimator bounceOut = ValueAnimator.ofFloat(bounceProgress, 0f); + bounceOut.addUpdateListener(animation -> { + bounceProgress = (float) animation.getAnimatedValue(); + if (parentView != null) { + parentView.invalidate(); + } + }); + showReleaseAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + bounceProgress = 0f; + swipeToReleaseProgress = 1f; + if (parentView != null) { + parentView.invalidate(); + } + fragmentView.invalidate(); + if (onAnimationFinishRunnable != null) { + onAnimationFinishRunnable.run(); + onAnimationFinishRunnable = null; + } + } + }); + showReleaseAnimator.playTogether(out, bounceOut); + showReleaseAnimator.setDuration(120); + showReleaseAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + showReleaseAnimator.start(); + } + + public void reset() { + checkProgress = 0; + animateCheck = false; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AudioPlayerAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AudioPlayerAlert.java index cb8bcec19..c09c2a8f4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AudioPlayerAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AudioPlayerAlert.java @@ -1355,7 +1355,7 @@ public class AudioPlayerAlert extends BottomSheet implements NotificationCenter. if (message != null) { SendMessagesHelper.getInstance(currentAccount).sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null); } - SendMessagesHelper.getInstance(currentAccount).sendMessage(fmessages, did, true, 0); + SendMessagesHelper.getInstance(currentAccount).sendMessage(fmessages, did, false, false, true, 0); } fragment1.finishFragment(); } else { @@ -1935,7 +1935,7 @@ public class AudioPlayerAlert extends BottomSheet implements NotificationCenter. private ImageLocation getArtworkThumbImageLocation(MessageObject messageObject) { final TLRPC.Document document = messageObject.getDocument(); - TLRPC.PhotoSize thumb = document != null ? FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 240) : null; + TLRPC.PhotoSize thumb = document != null ? FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 360) : null; if (!(thumb instanceof TLRPC.TL_photoSize) && !(thumb instanceof TLRPC.TL_photoSizeProgressive)) { thumb = null; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java index 5f2a17e93..45cd100f0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java @@ -381,7 +381,10 @@ public class AvatarDrawable extends Drawable { Theme.avatarDrawables[1].draw(canvas); } else { if (textLayout != null) { + float scale = size / (float) AndroidUtilities.dp(50); + canvas.scale(scale, scale, size / 2f, size / 2f) ; canvas.translate((size - textWidth) / 2 - textLeft, (size - textHeight) / 2); + textLayout.draw(canvas); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java index 3f9bce717..63e5faaf7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java @@ -20,6 +20,7 @@ public class BlurBehindDrawable { DispatchQueue queue; + private final int type; public static final int TAG_DRAWING_AS_BACKGROUND = (1 << 26) + 3; public static final int STATIC_CONTENT = 0; @@ -41,6 +42,7 @@ public class BlurBehindDrawable { private float blurAlpha; private boolean show; private boolean error; + private boolean animateAlpha = true; private final float DOWN_SCALE = 6f; private int lastH; @@ -57,7 +59,8 @@ public class BlurBehindDrawable { Paint emptyPaint = new Paint(Paint.FILTER_BITMAP_FLAG); Paint errorBlackoutPaint = new Paint(); - public BlurBehindDrawable(View behindView, View parentView) { + public BlurBehindDrawable(View behindView, View parentView, int type) { + this.type = type; this.behindView = behindView; this.parentView = parentView; @@ -66,8 +69,12 @@ public class BlurBehindDrawable { public void draw(Canvas canvas) { + if (type == 1 && !wasDraw && !animateAlpha) { + generateBlurredBitmaps(); + invalidate = false; + } final Bitmap[] bitmap = renderingBitmap; - if (bitmap != null || error) { + if ((bitmap != null || error) && animateAlpha) { if (show && blurAlpha != 1f) { blurAlpha += 0.09f; if (blurAlpha > 1f) { @@ -83,18 +90,28 @@ public class BlurBehindDrawable { } } + float alpha = animateAlpha ? blurAlpha : 1f; if (bitmap == null && error) { - errorBlackoutPaint.setAlpha((int) (50 * blurAlpha)); + errorBlackoutPaint.setAlpha((int) (50 * alpha)); canvas.drawPaint(errorBlackoutPaint); return; } - canvas.saveLayerAlpha(0, 0, parentView.getMeasuredWidth(), parentView.getMeasuredHeight(), (int) (blurAlpha * 255), ALL_SAVE_FLAG); + if (alpha == 1f) { + canvas.save(); + } else { + canvas.saveLayerAlpha(0, 0, parentView.getMeasuredWidth(), parentView.getMeasuredHeight(), (int) (alpha * 255), ALL_SAVE_FLAG); + } if (bitmap != null) { - emptyPaint.setAlpha((int) (255 * blurAlpha)); + emptyPaint.setAlpha((int) (255 * alpha)); + if (type == 1) { + canvas.translate(0, panTranslationY); + } canvas.drawBitmap(bitmap[1], 0, 0, null); canvas.save(); - canvas.translate(0, panTranslationY); + if (type == 0) { + canvas.translate(0, panTranslationY); + } canvas.drawBitmap(bitmap[0], 0, 0, null); canvas.restore(); wasDraw = true; @@ -126,8 +143,12 @@ public class BlurBehindDrawable { }); return; } + } else { + blurredBitmapTmp[i].eraseColor(Color.TRANSPARENT); + } + if (i == 1) { + blurredBitmapTmp[i].eraseColor(Theme.getColor(Theme.key_windowBackgroundWhite)); } - blurCanvas[i].save(); blurCanvas[i].scale(1f / DOWN_SCALE, 1f / DOWN_SCALE, 0, 0); Drawable backDrawable = behindView.getBackground(); @@ -220,7 +241,7 @@ public class BlurBehindDrawable { } public boolean isFullyDrawing() { - return !skipDraw && wasDraw && blurAlpha == 1f && show; + return !skipDraw && wasDraw && (blurAlpha == 1f || !animateAlpha) && show && parentView.getAlpha() == 1f; } public void checkSizes() { @@ -228,6 +249,22 @@ public class BlurBehindDrawable { if (bitmap == null || parentView.getMeasuredHeight() == 0 || parentView.getMeasuredWidth() == 0) { return; } + generateBlurredBitmaps(); + + lastH = parentView.getMeasuredHeight(); + lastW = parentView.getMeasuredWidth(); + } + + private void generateBlurredBitmaps() { + Bitmap[] bitmap = renderingBitmap; + if (bitmap == null) { + bitmap = renderingBitmap = new Bitmap[2]; + renderingBitmapCanvas = new Canvas[2]; + } + if (blurredBitmapTmp == null) { + blurredBitmapTmp = new Bitmap[2]; + blurCanvas = new Canvas[2]; + } blurBackgroundTask.canceled = true; blurBackgroundTask = new BlurBackgroundTask(); @@ -237,17 +274,20 @@ public class BlurBehindDrawable { toolbarH = AndroidUtilities.statusBarHeight + AndroidUtilities.dp(100); int h = i == 0 ? toolbarH : lastH; - if (bitmap[i].getHeight() != h || bitmap[i].getWidth() != parentView.getMeasuredWidth()) { + if (bitmap[i] == null || bitmap[i].getHeight() != h || bitmap[i].getWidth() != parentView.getMeasuredWidth()) { if (queue != null) { queue.cleanupQueue(); } blurredBitmapTmp[i] = Bitmap.createBitmap((int) (lastW / DOWN_SCALE), (int) (h / DOWN_SCALE), Bitmap.Config.ARGB_8888); + if (i == 1) { + blurredBitmapTmp[i].eraseColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + } blurCanvas[i] = new Canvas(blurredBitmapTmp[i]); renderingBitmap[i] = Bitmap.createBitmap(lastW, i == 0 ? toolbarH : lastH, Bitmap.Config.ARGB_8888); renderingBitmapCanvas[i] = new Canvas(renderingBitmap[i]); - renderingBitmapCanvas[i].scale(DOWN_SCALE, DOWN_SCALE); + renderingBitmapCanvas[i].scale((float) renderingBitmap[i].getWidth() / (float) blurredBitmapTmp[i].getWidth(), (float) renderingBitmap[i].getHeight() / (float) blurredBitmapTmp[i].getHeight()); blurCanvas[i].save(); blurCanvas[i].scale(1f / DOWN_SCALE, 1f / DOWN_SCALE, 0, 0); @@ -274,18 +314,22 @@ public class BlurBehindDrawable { Utilities.stackBlurBitmap(blurredBitmapTmp[i], getBlurRadius()); emptyPaint.setAlpha(255); + if (i == 1) { + renderingBitmap[i].eraseColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + } renderingBitmapCanvas[i].drawBitmap(blurredBitmapTmp[i], 0, 0, emptyPaint); } } - - lastH = parentView.getMeasuredHeight(); - lastW = parentView.getMeasuredWidth(); } public void show(boolean show) { this.show = show; } + public void setAnimateAlpha(boolean animateAlpha) { + this.animateAlpha = animateAlpha; + } + public void onPanTranslationUpdate(float y) { panTranslationY = y; parentView.invalidate(); @@ -317,13 +361,19 @@ public class BlurBehindDrawable { try { backgroundBitmap[i] = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); backgroundBitmapCanvas[i] = new Canvas(backgroundBitmap[i]); - backgroundBitmapCanvas[i].scale(DOWN_SCALE, DOWN_SCALE); + backgroundBitmapCanvas[i].scale(width / (float) blurredBitmapTmp[i].getWidth(), h / (float) blurredBitmapTmp[i].getHeight()); } catch (Throwable e) { FileLog.e(e); } } + if (i == 1) { + backgroundBitmap[i].eraseColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + } else { + backgroundBitmap[i].eraseColor(Color.TRANSPARENT); + } emptyPaint.setAlpha(255); Utilities.stackBlurBitmap(blurredBitmapTmp[i], getBlurRadius()); + if (backgroundBitmapCanvas[i] != null) { backgroundBitmapCanvas[i].drawBitmap(blurredBitmapTmp[i], 0, 0, emptyPaint); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BluredView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BluredView.java new file mode 100644 index 000000000..aee1873f9 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BluredView.java @@ -0,0 +1,36 @@ +package org.telegram.ui.Components; + +import android.content.Context; +import android.graphics.Canvas; +import android.view.View; + +public class BluredView extends View { + + public final BlurBehindDrawable drawable; + + public BluredView(Context context, View parentView) { + super(context); + drawable = new BlurBehindDrawable(parentView, this, 1); + drawable.setAnimateAlpha(false); + drawable.show(true); + } + + @Override + protected void onDraw(Canvas canvas) { + drawable.draw(canvas); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + drawable.checkSizes(); + } + + public void update() { + drawable.invalidate(); + } + + public boolean fullyDrawing() { + return drawable.isFullyDrawing() && getVisibility() == View.VISIBLE; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java index 2eefa5393..6a846bbc1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java @@ -243,6 +243,9 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe public BotCommandsMenuContainer botCommandsMenuContainer; private BotCommandsMenuView.BotCommandsAdapter botCommandsAdapter; + private ValueAnimator searchAnimator; + private float searchToOpenProgress; + private HashMap animationParamsX = new HashMap<>(); private class SeekBarWaveformView extends View { @@ -379,6 +382,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe private ReplaceableIconDrawable botButtonDrawable; private CharSequence draftMessage; + private boolean draftSearchWebpage; private boolean isPaste; @@ -502,7 +506,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe emojiTabOpen = curPage == 0; if (stickersExpanded) { if (searchingType != 0) { - searchingType = curPage == 0 ? 2 : 1; + setSearchingTypeInternal(curPage == 0 ? 2 : 1, true); checkStickresExpandHeight(); } else if (!stickersTabOpen) { setStickersExpanded(false, true, false); @@ -1742,7 +1746,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe emojiView.onOpen(messageEditText.length() > 0); } else { if (searchingType != 0) { - searchingType = 0; + setSearchingTypeInternal(0, true); if (emojiView != null) { emojiView.closeSearch(false); } @@ -1809,11 +1813,14 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe return false; } } - - if (isInScheduleMode()) { - AlertsCreator.createScheduleDatePickerDialog(parentActivity, parentFragment.getDialogId(), (notify, scheduleDate) -> send(inputContentInfo, notify, scheduleDate)); + if (inputContentInfo.getDescription().hasMimeType("image/gif") || SendMessagesHelper.shouldSendWebPAsSticker(null, inputContentInfo.getContentUri())) { + if (isInScheduleMode()) { + AlertsCreator.createScheduleDatePickerDialog(parentActivity, parentFragment.getDialogId(), (notify, scheduleDate) -> send(inputContentInfo, notify, scheduleDate)); + } else { + send(inputContentInfo, true, 0); + } } else { - send(inputContentInfo, true, 0); + editPhoto(inputContentInfo.getContentUri(), inputContentInfo.getDescription().getMimeType(0)); } return true; }; @@ -1832,7 +1839,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe if (isPopupShowing() && event.getAction() == MotionEvent.ACTION_DOWN) { boolean rez = false; if (searchingType != 0) { - searchingType = 0; + setSearchingTypeInternal(0, false); emojiView.closeSearch(false); requestFocus(); rez = true; @@ -1907,70 +1914,76 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe ClipData clipData = clipboard.getPrimaryClip(); if (clipData != null) { if (clipData.getItemCount() == 1 && clipData.getDescription().hasMimeType("image/*")) { - final File file = AndroidUtilities.generatePicturePath(fragment.isSecretChat(), MimeTypeMap.getSingleton().getExtensionFromMimeType(clipData.getDescription().getMimeType(0))); - Uri uri = clipData.getItemAt(0).getUri(); - Utilities.globalQueue.postRunnable(() -> { - try { - InputStream in = context.getContentResolver().openInputStream(uri); - FileOutputStream fos = new FileOutputStream(file); - byte[] buffer = new byte[1024]; - int lengthRead; - while ((lengthRead = in.read(buffer)) > 0) { - fos.write(buffer, 0, lengthRead); - fos.flush(); - } - in.close(); - fos.close(); - MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, -1, 0, file.getAbsolutePath(), 0, false, 0, 0, 0); - ArrayList entries = new ArrayList<>(); - entries.add(photoEntry); - AndroidUtilities.runOnUIThread(() -> { - PhotoViewer.getInstance().setParentActivity(parentActivity); - PhotoViewer.getInstance().openPhotoForSelect(entries, 0, 2, false, new PhotoViewer.EmptyPhotoViewerProvider() { - boolean sending; - @Override - public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, boolean forceDocument) { - ArrayList photos = new ArrayList<>(); - SendMessagesHelper.SendingMediaInfo info = new SendMessagesHelper.SendingMediaInfo(); - if (!photoEntry.isVideo && photoEntry.imagePath != null) { - info.path = photoEntry.imagePath; - } else if (photoEntry.path != null) { - info.path = photoEntry.path; - } - info.thumbPath = photoEntry.thumbPath; - info.isVideo = photoEntry.isVideo; - info.caption = photoEntry.caption != null ? photoEntry.caption.toString() : null; - info.entities = photoEntry.entities; - info.masks = photoEntry.stickers; - info.ttl = photoEntry.ttl; - info.videoEditedInfo = videoEditedInfo; - info.canDeleteAfter = true; - photos.add(info); - photoEntry.reset(); - sending = true; - SendMessagesHelper.prepareSendingMedia(accountInstance, photos, dialog_id, replyingMessageObject, getThreadMessage(), null, false, false, editingMessageObject, notify, scheduleDate); - } - - @Override - public void willHidePhotoViewer() { - if (!sending) { - try { - file.delete(); - } catch (Throwable ignore) { - - } - } - } - }, parentFragment); - }); - } catch (Throwable e) { - e.printStackTrace(); - } - }); + editPhoto(clipData.getItemAt(0).getUri(), clipData.getDescription().getMimeType(0)); } } return super.onTextContextMenuItem(id); } + + private void editPhoto(Uri uri, String mime) { + final File file = AndroidUtilities.generatePicturePath(fragment.isSecretChat(), MimeTypeMap.getSingleton().getExtensionFromMimeType(mime)); + Utilities.globalQueue.postRunnable(() -> { + try { + InputStream in = context.getContentResolver().openInputStream(uri); + FileOutputStream fos = new FileOutputStream(file); + byte[] buffer = new byte[1024]; + int lengthRead; + while ((lengthRead = in.read(buffer)) > 0) { + fos.write(buffer, 0, lengthRead); + fos.flush(); + } + in.close(); + fos.close(); + MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, -1, 0, file.getAbsolutePath(), 0, false, 0, 0, 0); + ArrayList entries = new ArrayList<>(); + entries.add(photoEntry); + AndroidUtilities.runOnUIThread(() -> { + PhotoViewer.getInstance().setParentActivity(parentActivity); + PhotoViewer.getInstance().openPhotoForSelect(entries, 0, 2, false, new PhotoViewer.EmptyPhotoViewerProvider() { + boolean sending; + @Override + public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, boolean forceDocument) { + ArrayList photos = new ArrayList<>(); + SendMessagesHelper.SendingMediaInfo info = new SendMessagesHelper.SendingMediaInfo(); + if (!photoEntry.isVideo && photoEntry.imagePath != null) { + info.path = photoEntry.imagePath; + } else if (photoEntry.path != null) { + info.path = photoEntry.path; + } + info.thumbPath = photoEntry.thumbPath; + info.isVideo = photoEntry.isVideo; + info.caption = photoEntry.caption != null ? photoEntry.caption.toString() : null; + info.entities = photoEntry.entities; + info.masks = photoEntry.stickers; + info.ttl = photoEntry.ttl; + info.videoEditedInfo = videoEditedInfo; + info.canDeleteAfter = true; + photos.add(info); + photoEntry.reset(); + sending = true; + SendMessagesHelper.prepareSendingMedia(accountInstance, photos, dialog_id, replyingMessageObject, getThreadMessage(), null, false, false, editingMessageObject, notify, scheduleDate); + if (delegate != null) { + delegate.onMessageSend(null, true, scheduleDate); + } + } + + @Override + public void willHidePhotoViewer() { + if (!sending) { + try { + file.delete(); + } catch (Throwable ignore) { + + } + } + } + }, parentFragment); + }); + } catch (Throwable e) { + e.printStackTrace(); + } + }); + } }; messageEditText.setDelegate(() -> { if (delegate != null) { @@ -2012,7 +2025,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe preferences.edit().putInt("hidekeyboard_" + dialog_id, botButtonsMessageObject.getId()).commit(); } if (searchingType != 0) { - searchingType = 0; + setSearchingTypeInternal(0, true); if (emojiView != null) { emojiView.closeSearch(true); } @@ -2305,7 +2318,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe attachLayout.addView(botButton, LayoutHelper.createLinear(48, 48)); botButton.setOnClickListener(v -> { if (searchingType != 0) { - searchingType = 0; + setSearchingTypeInternal(0, false); emojiView.closeSearch(false); messageEditText.requestFocus(); } @@ -2921,7 +2934,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe } if (stickersExpanded) { if (searchingType != 0) { - searchingType = 0; + setSearchingTypeInternal(0, true); emojiView.closeSearch(true); emojiView.hideSearchKeyboard(); if (emojiTabOpen) { @@ -3699,7 +3712,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe hideKeyboardRunnable = null; } int visibility = getVisibility(); - if (showKeyboardOnResume) { + if (showKeyboardOnResume && parentFragment.isLastFragment()) { showKeyboardOnResume = false; if (searchingType == 0) { messageEditText.requestFocus(); @@ -4167,7 +4180,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe return; } if (searchingType != 0) { - searchingType = 0; + setSearchingTypeInternal(0, true); emojiView.closeSearch(false); if (stickersExpanded) { @@ -5805,7 +5818,9 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe } if (draftMessage == null && !hadEditingMessage) { draftMessage = messageEditText.length() > 0 ? messageEditText.getText() : null; + draftSearchWebpage = messageWebPageSearch; } + messageWebPageSearch = editingMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage; if (!keyboardVisible) { AndroidUtilities.runOnUIThread(setTextFieldRunnable = () -> { setFieldText(textToSetWithKeyboard); @@ -5896,6 +5911,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe scheduledButton.setVisibility(VISIBLE); } messageEditText.setText(draftMessage); + messageWebPageSearch = draftSearchWebpage; messageEditText.setSelection(messageEditText.length()); if (getVisibility() == VISIBLE) { delegate.onAttachButtonShow(); @@ -6433,7 +6449,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe if (emojiView != null) { return; } - emojiView = new EmojiView(allowStickers, allowGifs, parentActivity, true, info) { + emojiView = new EmojiView(allowStickers, allowGifs, getContext(), true, info, sizeNotifierLayout) { @Override public void setTranslationY(float translationY) { super.setTranslationY(translationY); @@ -6443,6 +6459,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe } }; emojiView.setVisibility(GONE); + emojiView.setShowing(false); emojiView.setDelegate(new EmojiView.EmojiViewDelegate() { @Override @@ -6487,7 +6504,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe } if (stickersExpanded) { if (searchingType != 0) { - searchingType = 0; + setSearchingTypeInternal(0, true); emojiView.closeSearch(true, MessageObject.getStickerSetId(sticker)); emojiView.hideSearchKeyboard(); } @@ -6550,7 +6567,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe SendMessagesHelper.prepareSendingBotContextResult(accountInstance, result, params, dialog_id, replyingMessageObject, getThreadMessage(), notify, scheduleDate); if (searchingType != 0) { - searchingType = 0; + setSearchingTypeInternal(0, true); emojiView.closeSearch(true); emojiView.hideSearchKeyboard(); } @@ -6621,13 +6638,9 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe @Override public void onSearchOpenClose(int type) { - searchingType = type; + setSearchingTypeInternal(type, true); if (type != 0) { -// expandStickersWithKeyboard = true; -// if (expandStickersWithKeyboard) { -// expandStickersWithKeyboard = false; setStickersExpanded(true, true, false); - // } } if (emojiTabOpen && searchingType == 2) { checkStickresExpandHeight(); @@ -6659,6 +6672,11 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe return dialog_id; } + @Override + public int getThreadId() { + return getThreadMessageId(); + } + @Override public void showTrendingStickersAlert(TrendingStickersLayout layout) { if (parentActivity != null && parentFragment != null) { @@ -6680,6 +6698,16 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe trendingStickersAlert.show(); } } + + @Override + public void invalidateEnterView() { + invalidate(); + } + + @Override + public float getProgressToSearchOpened() { + return searchToOpenProgress; + } }); emojiView.setDragListener(new EmojiView.DragListener() { @@ -6752,7 +6780,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe return stickersTabOpen && !(!stickersExpanded && messageEditText.length() > 0) && emojiView.areThereAnyStickers() && !waitingForKeyboardOpen; } }); - sizeNotifierLayout.addView(emojiView, sizeNotifierLayout.getChildCount() - 1); + sizeNotifierLayout.addView(emojiView, sizeNotifierLayout.getChildCount() - 5); checkChannelRights(); } @@ -6768,7 +6796,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe return; } if (searchingType != 0) { - searchingType = 0; + setSearchingTypeInternal(0, true); emojiView.closeSearch(true); emojiView.hideSearchKeyboard(); } @@ -6803,6 +6831,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe if (!emojiViewVisible && emojiView != null && emojiView.getVisibility() != GONE) { sizeNotifierLayout.removeView(emojiView); emojiView.setVisibility(GONE); + emojiView.setShowing(false); } } @@ -6828,7 +6857,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe int previusHeight = 0; if (contentType == 0) { if (emojiView.getParent() == null) { - sizeNotifierLayout.addView(emojiView, sizeNotifierLayout.getChildCount() - 1); + sizeNotifierLayout.addView(emojiView, sizeNotifierLayout.getChildCount() - 5); } samePannelWasVisible = emojiViewVisible && emojiView.getVisibility() == View.VISIBLE; emojiView.setVisibility(VISIBLE); @@ -6839,6 +6868,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe anotherPanelWasVisible = true; previusHeight = botKeyboardView.getMeasuredHeight(); } + emojiView.setShowing(true); currentView = emojiView; animatingContentType = 0; } else if (contentType == 1) { @@ -6847,6 +6877,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe if (emojiView != null && emojiView.getVisibility() != GONE) { sizeNotifierLayout.removeView(emojiView); emojiView.setVisibility(GONE); + emojiView.setShowing(false); emojiViewVisible = false; anotherPanelWasVisible = true; previusHeight = emojiView.getMeasuredHeight(); @@ -6918,6 +6949,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe if (emojiViewVisible = true) { animatingContentType = 0; } + emojiView.setShowing(false); panelAnimation = new AnimatorSet(); panelAnimation.playTogether(ObjectAnimator.ofFloat(emojiView, View.TRANSLATION_Y, emojiView.getMeasuredHeight())); panelAnimation.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); @@ -6955,6 +6987,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe emojiPadding = 0; sizeNotifierLayout.removeView(emojiView); emojiView.setVisibility(GONE); + emojiView.setShowing(false); } } else { removeEmojiViewAfterAnimation = false; @@ -6966,7 +6999,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe } emojiViewVisible = false; } - if (botKeyboardView != null) { + if (botKeyboardView != null && botKeyboardView.getVisibility() == View.VISIBLE) { if (show != 2 || AndroidUtilities.usingHardwareInput || AndroidUtilities.isInMultiwindow) { if (smoothKeyboard && !keyboardVisible) { if (botKeyboardViewVisible) { @@ -7124,7 +7157,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe preferences.edit().putInt("hidekeyboard_" + dialog_id, botButtonsMessageObject.getId()).commit(); } if (byBackButton && searchingType != 0) { - searchingType = 0; + setSearchingTypeInternal(0, true); if (emojiView != null) { emojiView.closeSearch(true); } @@ -7135,7 +7168,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe } } else { if (searchingType != 0) { - searchingType = 0; + setSearchingTypeInternal(0, false); emojiView.closeSearch(false); messageEditText.requestFocus(); } @@ -7144,6 +7177,45 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe } } + private void setSearchingTypeInternal(int searchingType, boolean animated) { + boolean showSearchingNew = searchingType != 0; + boolean showSearchingOld = this.searchingType != 0; + if (showSearchingNew != showSearchingOld) { + if (searchAnimator != null) { + searchAnimator.removeAllListeners(); + searchAnimator.cancel(); + } + if (!animated) { + searchToOpenProgress = showSearchingNew ? 1f : 0f; + if (emojiView != null) { + emojiView.searchProgressChanged(); + } + } else { + searchAnimator = ValueAnimator.ofFloat(searchToOpenProgress, showSearchingNew ? 1f : 0f); + searchAnimator.addUpdateListener(valueAnimator -> { + searchToOpenProgress = (float) valueAnimator.getAnimatedValue(); + if (emojiView != null) { + emojiView.searchProgressChanged(); + } + }); + searchAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + searchToOpenProgress = showSearchingNew ? 1f : 0f; + if (emojiView != null) { + emojiView.searchProgressChanged(); + } + } + }); + searchAnimator.setDuration(220); + searchAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + searchAnimator.start(); + } + } + + this.searchingType = searchingType; + } + private void openKeyboardInternal() { showPopup(AndroidUtilities.usingHardwareInput || AndroidUtilities.isInMultiwindow || parentFragment != null && parentFragment.isInBubbleMode() || isPaused ? 0 : 2, 0); messageEditText.requestFocus(); @@ -7206,7 +7278,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (w != oldw && stickersExpanded) { - searchingType = 0; + setSearchingTypeInternal(0, false); emojiView.closeSearch(false); setStickersExpanded(false, false, false); } @@ -7349,6 +7421,9 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe if (botKeyboardView != null) { botKeyboardView.invalidateViews(); } + if (messageEditText != null) { + messageEditText.postInvalidate(); + } } else if (id == NotificationCenter.recordProgressChanged) { int guid = (Integer) args[0]; if (guid != recordingGuid) { @@ -8404,4 +8479,17 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe AndroidUtilities.cancelRunOnUIThread(runEmojiPanelAnimation); runEmojiPanelAnimation.run(); } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (emojiView == null || emojiView.getVisibility() != View.VISIBLE || emojiView.getStickersExpandOffset() == 0) { + super.dispatchDraw(canvas); + } else { + canvas.save(); + canvas.clipRect(0, AndroidUtilities.dp(2), getMeasuredWidth(), getMeasuredHeight()); + canvas.translate(0, -emojiView.getStickersExpandOffset()); + super.dispatchDraw(canvas); + canvas.restore(); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java index a3bd8adfa..e65337709 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java @@ -13,6 +13,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; +import android.graphics.Canvas; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; @@ -53,7 +54,7 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent private ImageView timeItem; private TimerDrawable timerDrawable; private ChatActivity parentFragment; - private StatusDrawable[] statusDrawables = new StatusDrawable[5]; + private StatusDrawable[] statusDrawables = new StatusDrawable[6]; private AvatarDrawable avatarDrawable = new AvatarDrawable(); private int currentAccount = UserConfig.selectedAccount; private boolean occupyStatusBar = true; @@ -156,6 +157,7 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent statusDrawables[2] = new SendingFileDrawable(false); statusDrawables[3] = new PlayingGameDrawable(false); statusDrawables[4] = new RoundStatusDrawable(false); + statusDrawables[5] = new ChoosingStickerStatusDrawable(false); for (int a = 0; a < statusDrawables.length; a++) { statusDrawables[a].setIsChat(chat != null); } @@ -380,8 +382,14 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent private void setTypingAnimation(boolean start) { if (start) { try { - Integer type = MessagesController.getInstance(currentAccount).getPrintingStringType(parentFragment.getDialogId(), parentFragment.getThreadId()); - subtitleTextView.setLeftDrawable(statusDrawables[type]); + int type = MessagesController.getInstance(currentAccount).getPrintingStringType(parentFragment.getDialogId(), parentFragment.getThreadId()); + if (type == 5) { + subtitleTextView.replaceTextWithDrawable(statusDrawables[type], "**oo**"); + subtitleTextView.setLeftDrawable(null); + } else { + subtitleTextView.replaceTextWithDrawable(null, null); + subtitleTextView.setLeftDrawable(statusDrawables[type]); + } for (int a = 0; a < statusDrawables.length; a++) { if (a == type) { statusDrawables[a].start(); @@ -394,6 +402,7 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent } } else { subtitleTextView.setLeftDrawable(null); + subtitleTextView.replaceTextWithDrawable(null, null); for (int a = 0; a < statusDrawables.length; a++) { statusDrawables[a].stop(); } @@ -745,4 +754,8 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent public SharedMediaLayout.SharedMediaPreloader getSharedMediaPreloader() { return sharedMediaPreloader; } + + public BackupImageView getAvatarImageView() { + return avatarImageView; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxBase.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxBase.java index debc97ddb..401326002 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxBase.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CheckBoxBase.java @@ -6,7 +6,6 @@ import android.animation.ObjectAnimator; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Path; @@ -16,6 +15,7 @@ import android.graphics.Rect; import android.graphics.RectF; import androidx.annotation.Keep; +import android.graphics.Shader; import android.text.TextPaint; import android.view.View; @@ -314,9 +314,11 @@ public class CheckBoxBase { if (backgroundType == 12 || backgroundType == 13) { backgroundPaint.setStyle(Paint.Style.FILL); if (messageDrawable != null && messageDrawable.hasGradient()) { - LinearGradient shader = messageDrawable.getGradientShader(); + Shader shader = messageDrawable.getGradientShader(); Matrix matrix = messageDrawable.getMatrix(); - matrix.setTranslate(0, -messageDrawable.getTopY() + bounds.top); + matrix.reset(); + messageDrawable.applyMatrixScale(); + matrix.postTranslate(0, -messageDrawable.getTopY() + bounds.top); shader.setLocalMatrix(matrix); backgroundPaint.setShader(shader); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChoosingStickerStatusDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChoosingStickerStatusDrawable.java new file mode 100644 index 000000000..3353f3b18 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChoosingStickerStatusDrawable.java @@ -0,0 +1,134 @@ +package org.telegram.ui.Components; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.ui.ActionBar.Theme; + +public class ChoosingStickerStatusDrawable extends StatusDrawable { + + Paint strokePaint; + Paint fillPaint; + + private boolean isChat = false; + private long lastUpdateTime = 0; + private boolean started = false; + float progress; + boolean increment = true; + + public ChoosingStickerStatusDrawable(boolean createPaint) { + if (createPaint) { + strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + strokePaint.setStyle(Paint.Style.STROKE); + strokePaint.setStrokeWidth(AndroidUtilities.dpf2(1.2f)); + } + } + + @Override + public void start() { + lastUpdateTime = System.currentTimeMillis(); + started = true; + invalidateSelf(); + } + + @Override + public void stop() { + started = false; + } + + @Override + public void setIsChat(boolean value) { + this.isChat = value; + } + + @Override + public void setColor(int color) { + fillPaint.setColor(color); + strokePaint.setColor(color); + } + + @Override + public void draw(@NonNull Canvas canvas) { + float animationProgress = Math.min(progress, 1f); + float k = 0.3f; + float p = CubicBezierInterpolator.EASE_IN.getInterpolation(animationProgress < k ? animationProgress / k : 1f); + float p2 = CubicBezierInterpolator.EASE_OUT.getInterpolation(animationProgress < k ? 0 : (animationProgress - k) / (1f - k)); + float cx, xOffset; + if (increment) { + cx = AndroidUtilities.dp(2.1f) * p + (AndroidUtilities.dp(7) - AndroidUtilities.dp(2.1f)) * (1f - p); + xOffset = AndroidUtilities.dpf2(1.5f) * (1f - CubicBezierInterpolator.EASE_OUT.getInterpolation(progress / 2)); + } else { + cx = AndroidUtilities.dp(2.1f) * (1f - p) + (AndroidUtilities.dp(7) - AndroidUtilities.dp(2.1f)) * p; + xOffset = AndroidUtilities.dpf2(1.5f) * CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(progress / 2); + } + float cy = AndroidUtilities.dp(11) / 2f; + float r = AndroidUtilities.dpf2(2f); + + float scaleOffset = AndroidUtilities.dpf2(0.5f) * p - AndroidUtilities.dpf2(0.5f) * p2; + + Paint strokePaint = this.strokePaint != null ? this.strokePaint : Theme.chat_statusRecordPaint; + Paint paint = this.fillPaint != null ? this.fillPaint : Theme.chat_statusPaint; + if (strokePaint.getStrokeWidth() != AndroidUtilities.dp(0.8f)) { + strokePaint.setStrokeWidth(AndroidUtilities.dp(0.8f)); + } + for (int i = 0; i < 2; i++) { + canvas.save(); + canvas.translate(strokePaint.getStrokeWidth() / 2f + xOffset + AndroidUtilities.dp(9) * i + getBounds().left + AndroidUtilities.dpf2(0.2f), strokePaint.getStrokeWidth() / 2f + AndroidUtilities.dpf2(2f) + getBounds().top); + + AndroidUtilities.rectTmp.set(0, scaleOffset, AndroidUtilities.dp(7), AndroidUtilities.dp(11) - scaleOffset); + canvas.drawOval(AndroidUtilities.rectTmp, strokePaint); + canvas.drawCircle(cx, cy, r, paint); + canvas.restore(); + } + + if (started) { + update(); + } + } + + private void update() { + long newTime = System.currentTimeMillis(); + long dt = newTime - lastUpdateTime; + lastUpdateTime = newTime; + if (dt > 16) { + dt = 16; + } + progress += dt / 500f; + if (progress >= 2f) { + progress = 0; + increment = !increment; + } + invalidateSelf(); + } + + @Override + public void setAlpha(int i) { + + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + + } + + @Override + public int getOpacity() { + return 0; + } + + @Override + public int getIntrinsicWidth() { + return AndroidUtilities.dp(20); + } + + @Override + public int getIntrinsicHeight() { + return AndroidUtilities.dp(18); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ColorPicker.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ColorPicker.java index 2143b7d63..b5b18f6b5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ColorPicker.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ColorPicker.java @@ -75,7 +75,6 @@ public class ColorPicker extends FrameLayout { private EditTextBoldCursor[] colorEditText; private ImageView clearButton; private ImageView addButton; - private ImageView exchangeButton; private TextView resetButton; private ActionBarMenuItem menuItem; @@ -389,24 +388,6 @@ public class ColorPicker extends FrameLayout { } } - exchangeButton = new ImageView(getContext()); - exchangeButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_dialogButtonSelector), 1)); - exchangeButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.MULTIPLY)); - exchangeButton.setScaleType(ImageView.ScaleType.CENTER); - exchangeButton.setVisibility(GONE); - exchangeButton.setImageResource(R.drawable.themes_swapcolor); - exchangeButton.setOnClickListener(v -> { - if (exchangeButton.getAlpha() != 1.0f) { - return; - } - int color = radioButton[0].getColor(); - radioButton[0].setColor(radioButton[1].getColor()); - radioButton[1].setColor(color); - delegate.setColor(radioButton[0].getColor(), 0, false); - delegate.setColor(radioButton[1].getColor(), 1, true); - }); - radioContainer.addView(exchangeButton, 1, LayoutHelper.createFrame(30, 30, Gravity.LEFT | Gravity.TOP, 0, 1, 0, 0)); - addButton = new ImageView(getContext()); addButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_dialogButtonSelector), 1)); addButton.setImageResource(R.drawable.themes_addcolor); @@ -432,19 +413,7 @@ public class ColorPicker extends FrameLayout { animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_X, 1.0f)); animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_Y, 1.0f)); animators.add(ObjectAnimator.ofFloat(addButton, View.TRANSLATION_X, AndroidUtilities.dp(30) + AndroidUtilities.dp(13))); - if (myMessagesColor) { - exchangeButton.setVisibility(VISIBLE); - animators.add(ObjectAnimator.ofFloat(addButton, View.ALPHA, 0.0f)); - animators.add(ObjectAnimator.ofFloat(addButton, View.SCALE_X, 0.0f)); - animators.add(ObjectAnimator.ofFloat(addButton, View.SCALE_Y, 0.0f)); - animators.add(ObjectAnimator.ofFloat(exchangeButton, View.ALPHA, 1.0f)); - animators.add(ObjectAnimator.ofFloat(exchangeButton, View.SCALE_X, 1.0f)); - animators.add(ObjectAnimator.ofFloat(exchangeButton, View.SCALE_Y, 1.0f)); - } } else if (colorsCount == 2) { - if (myMessagesColor) { - return; - } colorsCount = 3; if (radioButton[2].getColor() == 0) { int color = radioButton[0].getColor(); @@ -461,9 +430,6 @@ public class ColorPicker extends FrameLayout { animators.add(ObjectAnimator.ofFloat(addButton, View.TRANSLATION_X, AndroidUtilities.dp(30) * 2 + AndroidUtilities.dp(13) * 2)); delegate.setColor(radioButton[2].getColor(), 2, true); } else if (colorsCount == 3) { - if (myMessagesColor) { - return; - } colorsCount = 4; if (radioButton[3].getColor() == 0) { radioButton[3].setColor(generateGradientColors(radioButton[2].getColor())); @@ -486,7 +452,7 @@ public class ColorPicker extends FrameLayout { colorsAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - if (colorsCount == 4 || myMessagesColor && colorsCount == 2) { + if (colorsCount == 4) { addButton.setVisibility(INVISIBLE); } colorsAnimator = null; @@ -524,15 +490,6 @@ public class ColorPicker extends FrameLayout { animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_X, 0.0f)); animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_Y, 0.0f)); animators.add(ObjectAnimator.ofFloat(addButton, View.TRANSLATION_X, 0)); - if (myMessagesColor) { - addButton.setVisibility(VISIBLE); - animators.add(ObjectAnimator.ofFloat(exchangeButton, View.ALPHA, 0.0f)); - animators.add(ObjectAnimator.ofFloat(exchangeButton, View.SCALE_X, 0.0f)); - animators.add(ObjectAnimator.ofFloat(exchangeButton, View.SCALE_Y, 0.0f)); - animators.add(ObjectAnimator.ofFloat(addButton, View.ALPHA, 1.0f)); - animators.add(ObjectAnimator.ofFloat(addButton, View.SCALE_X, 1.0f)); - animators.add(ObjectAnimator.ofFloat(addButton, View.SCALE_Y, 1.0f)); - } } else if (colorsCount == 3) { colorsCount = 2; animators = new ArrayList<>(); @@ -573,9 +530,6 @@ public class ColorPicker extends FrameLayout { public void onAnimationEnd(Animator animation) { if (colorsCount == 1) { clearButton.setVisibility(INVISIBLE); - if (myMessagesColor) { - exchangeButton.setVisibility(INVISIBLE); - } } for (int a = 0; a < radioButton.length; a++) { if (radioButton[a].getTag(R.id.index_tag) == null) { @@ -641,9 +595,6 @@ public class ColorPicker extends FrameLayout { private void updateColorsPosition(ArrayList animators, int hidingIndex, boolean hiding, int width) { int allX = 0; int count = colorsCount; - if (myMessagesColor && colorsCount == 2) { - count++; - } int visibleX = count * AndroidUtilities.dp(30) + (count - 1) * AndroidUtilities.dp(13); int left = radioContainer.getLeft() + visibleX; int w = width - AndroidUtilities.dp(currentResetType == 1 ? 50 : 0); @@ -661,10 +612,6 @@ public class ColorPicker extends FrameLayout { for (int a = 0; a < radioButton.length; a++) { boolean wasVisible = radioButton[a].getTag(R.id.index_tag) != null; if (a < colorsCount) { - if (a == 1 && myMessagesColor) { - exchangeButton.setTranslationX(allX); - allX += AndroidUtilities.dp(30) + AndroidUtilities.dp(13); - } radioButton[a].setVisibility(VISIBLE); if (animators != null) { if (!wasVisible) { @@ -969,7 +916,7 @@ public class ColorPicker extends FrameLayout { addButton.setVisibility(GONE); clearButton.setVisibility(GONE); } else { - if (newColorsCount < (myMessages ? 2 : 4)) { + if (newColorsCount < 4) { addButton.setVisibility(VISIBLE); addButton.setScaleX(1.0f); addButton.setScaleY(1.0f); @@ -986,14 +933,6 @@ public class ColorPicker extends FrameLayout { clearButton.setVisibility(GONE); } } - if (myMessages) { - exchangeButton.setVisibility(newColorsCount == 2 ? VISIBLE : INVISIBLE); - exchangeButton.setAlpha(newColorsCount == 2 ? 1.0f : 0.0f); - exchangeButton.setScaleX(newColorsCount == 2 ? 1.0f : 0.0f); - exchangeButton.setScaleY(newColorsCount == 2 ? 1.0f : 0.0f); - } else { - exchangeButton.setVisibility(GONE); - } linearLayout.invalidate(); updateColorsPosition(null, 0, false, getMeasuredWidth()); @@ -1004,61 +943,6 @@ public class ColorPicker extends FrameLayout { animators = null; } - /*if (!twoColors || !hasSecondColor) { - colorEditText[1].requestFocus(); - } - for (int a = 2; a < colorEditText.length; a++) { - if (animated) { - if (twoColors) { - colorEditText[a].setVisibility(VISIBLE); - } - animators.add(ObjectAnimator.ofFloat(colorEditText[a], View.ALPHA, twoColors && hasSecondColor ? 1.0f : 0.0f)); - } else { - colorEditText[a].setVisibility(twoColors ? VISIBLE : GONE); - colorEditText[a].setAlpha(twoColors && hasSecondColor ? 1.0f : 0.0f); - } - colorEditText[a].setTag(twoColors ? 1 : null); - }*/ - - /*if (animated) { - if (twoColors) { - exchangeButton.setVisibility(VISIBLE); - } - animators.add(ObjectAnimator.ofFloat(exchangeButton, View.ALPHA, twoColors && hasSecondColor ? 1.0f : 0.0f)); - } else { - exchangeButton.setVisibility(twoColors ? VISIBLE : GONE); - exchangeButton.setAlpha(twoColors && hasSecondColor ? 1.0f : 0.0f); - } - if (twoColors) { - clearButton.setTag(hasSecondColor ? 1 : null); - clearButton.setRotation(hasSecondColor ? 0 : 45); - } - if (animated) { - if (twoColors) { - clearButton.setVisibility(VISIBLE); - } - animators.add(ObjectAnimator.ofFloat(clearButton, View.ALPHA, twoColors ? 1.0f : 0.0f)); - } else { - clearButton.setVisibility(twoColors ? VISIBLE : GONE); - clearButton.setAlpha(twoColors ? 1.0f : 0.0f); - }*/ - - /*resetButton.setTag(hasChanges ? 1 : null); - resetButton.setText(resetType == 1 ? LocaleController.getString("ColorPickerResetAll", R.string.ColorPickerResetAll) : LocaleController.getString("ColorPickerReset", R.string.ColorPickerReset)); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) resetButton.getLayoutParams(); - layoutParams.rightMargin = AndroidUtilities.dp(resetType == 1 ? 14 : (14 + 47)); - if (animated) { - if (!hasChanges || resetButton.getVisibility() == VISIBLE && hasSecondColor) { - animators.add(ObjectAnimator.ofFloat(resetButton, View.ALPHA, 0.0f)); - } else if (resetButton.getVisibility() != VISIBLE && !hasSecondColor) { - resetButton.setVisibility(VISIBLE); - animators.add(ObjectAnimator.ofFloat(resetButton, View.ALPHA, 1.0f)); - } - } else { - resetButton.setAlpha(!hasChanges || hasSecondColor ? 0.0f : 1.0f); - resetButton.setVisibility(!hasChanges || hasSecondColor ? GONE : VISIBLE); - }*/ - if (animators != null && !animators.isEmpty()) { AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(animators); @@ -1066,12 +950,8 @@ public class ColorPicker extends FrameLayout { animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - /*if (!hasChanges || hasSecondColor) { - resetButton.setVisibility(GONE); - }*/ if (!fewColors) { clearButton.setVisibility(GONE); - exchangeButton.setVisibility(GONE); } } }); @@ -1135,8 +1015,6 @@ public class ColorPicker extends FrameLayout { } arrayList.add(new ThemeDescription(clearButton, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); arrayList.add(new ThemeDescription(clearButton, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_dialogButtonSelector)); - arrayList.add(new ThemeDescription(exchangeButton, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); - arrayList.add(new ThemeDescription(exchangeButton, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_dialogButtonSelector)); if (menuItem != null) { ThemeDescription.ThemeDescriptionDelegate delegate = () -> { menuItem.setIconColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/CounterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/CounterView.java index cc4e0d59c..f917f5a30 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/CounterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/CounterView.java @@ -23,292 +23,384 @@ import org.telegram.ui.ActionBar.Theme; public class CounterView extends View { - private final static int ANIMATION_TYPE_IN = 0; - private final static int ANIMATION_TYPE_OUT = 1; - private final static int ANIMATION_TYPE_REPLACE = 2; - - int animationType = -1; - - Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - RectF rectF = new RectF(); - - int currentCount; - private boolean countAnimationIncrement; - private ValueAnimator countAnimator; - private float countChangeProgress = 1f; - private StaticLayout countLayout; - private StaticLayout countOldLayout; - private StaticLayout countAnimationStableLayout; - private StaticLayout countAnimationInLayout; - - private int countWidthOld; - private int countWidth; - - private int circleColor; - private int textColor; - private String textColorKey = Theme.key_chat_goDownButtonCounter; - private String circleColorKey = Theme.key_chat_goDownButtonCounterBackground; - - int lastH; - int gravity = Gravity.CENTER; - float countLeft; - float x; - - private boolean reverseAnimation; - public float horizontalPadding; - + public CounterDrawable counterDrawable = new CounterDrawable(this); public CounterView(Context context) { super(context); setVisibility(View.GONE); - circlePaint.setColor(Color.BLACK); - - textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - textPaint.setTextSize(AndroidUtilities.dp(13)); + counterDrawable.updateVisibility = true; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - if (getMeasuredHeight() != lastH) { - int count = currentCount; - currentCount = -1; - setCount(count, animationType == ANIMATION_TYPE_IN); - lastH = getMeasuredHeight(); - } + counterDrawable.setSize(getMeasuredHeight(), getMeasuredWidth()); } - public void setCount(int count, boolean animated) { - if (count == currentCount) { - return; - } - if (countAnimator != null) { - countAnimator.cancel(); - } - if (count > 0) { - setVisibility(View.VISIBLE); - } - if (Math.abs(count - currentCount) > 99) { - animated = false; - } - if (!animated) { - currentCount = count; - if (count == 0) { - setVisibility(View.GONE); - return; - } - String newStr = String.valueOf(count); - countWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(newStr))); - countLayout = new StaticLayout(newStr, textPaint, countWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); - invalidate(); - } - String newStr = String.valueOf(count); - - if (animated) { - if (countAnimator != null) { - countAnimator.cancel(); - } - countChangeProgress = 0f; - countAnimator = ValueAnimator.ofFloat(0, 1f); - countAnimator.addUpdateListener(valueAnimator -> { - countChangeProgress = (float) valueAnimator.getAnimatedValue(); - invalidate(); - }); - countAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - animationType = -1; - countChangeProgress = 1f; - countOldLayout = null; - countAnimationStableLayout = null; - countAnimationInLayout = null; - if (currentCount == 0) { - setVisibility(View.GONE); - } - invalidate(); - } - }); - if (currentCount <= 0) { - animationType = ANIMATION_TYPE_IN; - countAnimator.setDuration(220); - countAnimator.setInterpolator(new OvershootInterpolator()); - } else if (count == 0) { - animationType = ANIMATION_TYPE_OUT; - countAnimator.setDuration(150); - countAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); - } else { - animationType = ANIMATION_TYPE_REPLACE; - countAnimator.setDuration(430); - countAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); - } - if (countLayout != null) { - String oldStr = String.valueOf(currentCount); - - if (oldStr.length() == newStr.length()) { - SpannableStringBuilder oldSpannableStr = new SpannableStringBuilder(oldStr); - SpannableStringBuilder newSpannableStr = new SpannableStringBuilder(newStr); - SpannableStringBuilder stableStr = new SpannableStringBuilder(newStr); - for (int i = 0; i < oldStr.length(); i++) { - if (oldStr.charAt(i) == newStr.charAt(i)) { - oldSpannableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0); - newSpannableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0); - } else { - stableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0); - } - } - - int countOldWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(oldStr))); - countOldLayout = new StaticLayout(oldSpannableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); - countAnimationStableLayout = new StaticLayout(stableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); - countAnimationInLayout = new StaticLayout(newSpannableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); - } else { - countOldLayout = countLayout; - } - } - countWidthOld = countWidth; - countAnimationIncrement = count > currentCount; - countAnimator.start(); - } - if (count > 0) { - countWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(newStr))); - countLayout = new StaticLayout(newStr, textPaint, countWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); - } - - currentCount = count; - invalidate(); - } @Override protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - int textColor = Theme.getColor(textColorKey); - int circleColor = Theme.getColor(circleColorKey); - if (this.textColor != textColor) { - this.textColor = textColor; - textPaint.setColor(textColor); - } - if (this.circleColor != circleColor) { - this.circleColor = circleColor; - circlePaint.setColor(circleColor); - } - if (countChangeProgress != 1f) { - if (animationType == ANIMATION_TYPE_IN || animationType == ANIMATION_TYPE_OUT) { - updateX(countWidth); - float cx = countLeft + countWidth / 2f; - float cy = getMeasuredHeight() / 2f; - canvas.save(); - float progress = animationType == ANIMATION_TYPE_IN ? countChangeProgress : (1f - countChangeProgress); - canvas.scale(progress, progress, cx, cy); - drawInternal(canvas); - canvas.restore(); - } else { - float progressHalf = countChangeProgress * 2; - if (progressHalf > 1f) { - progressHalf = 1f; - } - - float countTop = (getMeasuredHeight() - AndroidUtilities.dp(23)) / 2f; - float countWidth; - if (this.countWidth == this.countWidthOld) { - countWidth = this.countWidth; - } else { - countWidth = this.countWidth * progressHalf + this.countWidthOld * (1f - progressHalf); - } - updateX(countWidth); - - float scale = 1f; - if (countAnimationIncrement) { - if (countChangeProgress <= 0.5f) { - scale += 0.1f * CubicBezierInterpolator.EASE_OUT.getInterpolation(countChangeProgress * 2); - } else { - scale += 0.1f * CubicBezierInterpolator.EASE_IN.getInterpolation((1f - (countChangeProgress - 0.5f) * 2)); - } - } - - rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23)); - canvas.save(); - canvas.scale(scale, scale, rectF.centerX(), rectF.centerY()); - canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, circlePaint); - canvas.clipRect(rectF); - - boolean increment = reverseAnimation != countAnimationIncrement; - if (countAnimationInLayout != null) { - canvas.save(); - canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? AndroidUtilities.dp(13) : -AndroidUtilities.dp(13)) * (1f - progressHalf)); - textPaint.setAlpha((int) (255 * progressHalf)); - countAnimationInLayout.draw(canvas); - canvas.restore(); - } else if (countLayout != null) { - canvas.save(); - canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? AndroidUtilities.dp(13) : -AndroidUtilities.dp(13)) * (1f - progressHalf)); - textPaint.setAlpha((int) (255 * progressHalf)); - countLayout.draw(canvas); - canvas.restore(); - } - - if (countOldLayout != null) { - canvas.save(); - canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? -AndroidUtilities.dp(13) : AndroidUtilities.dp(13)) * (progressHalf)); - textPaint.setAlpha((int) (255 * (1f - progressHalf))); - countOldLayout.draw(canvas); - canvas.restore(); - } - - if (countAnimationStableLayout != null) { - canvas.save(); - canvas.translate(countLeft, countTop + AndroidUtilities.dp(4)); - textPaint.setAlpha(255); - countAnimationStableLayout.draw(canvas); - canvas.restore(); - } - textPaint.setAlpha(255); - canvas.restore(); - } - } else { - drawInternal(canvas); - } + counterDrawable.draw(canvas); } - private void updateX(float countWidth) { - if (gravity == Gravity.RIGHT) { - countLeft = getMeasuredWidth() - AndroidUtilities.dp(5.5f); - if (horizontalPadding != 0) { - countLeft -= Math.max(horizontalPadding + countWidth / 2f, countWidth); - } else { - countLeft -= countWidth; - } - } else if (gravity == Gravity.LEFT) { - countLeft = AndroidUtilities.dp(5.5f); - } else { - countLeft = (int) ((getMeasuredWidth() - countWidth) / 2f); - } - x = countLeft - AndroidUtilities.dp(5.5f); - } - - private void drawInternal(Canvas canvas) { - float countTop = (getMeasuredHeight() - AndroidUtilities.dp(23)) / 2f; - updateX(countWidth); - rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23)); - canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, circlePaint); - if (countLayout != null) { - canvas.save(); - canvas.translate(countLeft, countTop + AndroidUtilities.dp(4)); - countLayout.draw(canvas); - canvas.restore(); - } - } public void setColors(String textKey, String circleKey){ - this.textColorKey = textKey; - this.circleColorKey = circleKey; + counterDrawable.textColorKey = textKey; + counterDrawable.circleColorKey = circleKey; } public void setGravity(int gravity) { - this.gravity = gravity; + counterDrawable.gravity = gravity; } public void setReverse(boolean b) { - reverseAnimation = b; + counterDrawable.reverseAnimation = b; + } + + public void setCount(int count, boolean animated) { + counterDrawable.setCount(count, animated); + } + + public static class CounterDrawable { + + private final static int ANIMATION_TYPE_IN = 0; + private final static int ANIMATION_TYPE_OUT = 1; + private final static int ANIMATION_TYPE_REPLACE = 2; + + int animationType = -1; + + public Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + public TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + public RectF rectF = new RectF(); + + int currentCount; + private boolean countAnimationIncrement; + private ValueAnimator countAnimator; + private float countChangeProgress = 1f; + private StaticLayout countLayout; + private StaticLayout countOldLayout; + private StaticLayout countAnimationStableLayout; + private StaticLayout countAnimationInLayout; + + private int countWidthOld; + private int countWidth; + + private int circleColor; + private int textColor; + private String textColorKey = Theme.key_chat_goDownButtonCounter; + private String circleColorKey = Theme.key_chat_goDownButtonCounterBackground; + + int lastH; + int width; + public int gravity = Gravity.CENTER; + float countLeft; + float x; + + private boolean reverseAnimation; + public float horizontalPadding; + + boolean updateVisibility; + + private View parent; + + public final static int TYPE_DEFAULT = 0; + public final static int TYPE_CHAT_PULLING_DOWN = 1; + + int type = TYPE_DEFAULT; + + public CounterDrawable(View parent) { + this.parent = parent; + circlePaint.setColor(Color.BLACK); + textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + textPaint.setTextSize(AndroidUtilities.dp(13)); + } + + public void setSize(int h, int w) { + if (h != lastH) { + int count = currentCount; + currentCount = -1; + setCount(count, animationType == ANIMATION_TYPE_IN); + lastH = h; + } + width = w; + } + + + private void drawInternal(Canvas canvas) { + float countTop = (lastH - AndroidUtilities.dp(23)) / 2f; + updateX(countWidth); + rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23)); + canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, circlePaint); + if (Theme.hasGradientService()) { + canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, Theme.chat_actionBackgroundGradientDarkenPaint); + } + if (countLayout != null) { + canvas.save(); + canvas.translate(countLeft, countTop + AndroidUtilities.dp(4)); + countLayout.draw(canvas); + canvas.restore(); + } + } + + public void setCount(int count, boolean animated) { + if (count == currentCount) { + return; + } + if (countAnimator != null) { + countAnimator.cancel(); + } + if (count > 0 && updateVisibility && parent != null) { + parent.setVisibility(View.VISIBLE); + } + if (Math.abs(count - currentCount) > 99) { + animated = false; + } + if (!animated) { + currentCount = count; + if (count == 0) { + if (updateVisibility && parent != null) { + parent.setVisibility(View.GONE); + } + return; + } + String newStr = String.valueOf(count); + countWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(newStr))); + countLayout = new StaticLayout(newStr, textPaint, countWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + if (parent != null) { + parent.invalidate(); + } + } + String newStr = String.valueOf(count); + + if (animated) { + if (countAnimator != null) { + countAnimator.cancel(); + } + countChangeProgress = 0f; + countAnimator = ValueAnimator.ofFloat(0, 1f); + countAnimator.addUpdateListener(valueAnimator -> { + countChangeProgress = (float) valueAnimator.getAnimatedValue(); + if (parent != null) { + parent.invalidate(); + } + }); + countAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + animationType = -1; + countChangeProgress = 1f; + countOldLayout = null; + countAnimationStableLayout = null; + countAnimationInLayout = null; + if (parent != null) { + if (currentCount == 0 && updateVisibility) { + parent.setVisibility(View.GONE); + } + parent.invalidate(); + } + } + }); + if (currentCount <= 0) { + animationType = ANIMATION_TYPE_IN; + countAnimator.setDuration(220); + countAnimator.setInterpolator(new OvershootInterpolator()); + } else if (count == 0) { + animationType = ANIMATION_TYPE_OUT; + countAnimator.setDuration(150); + countAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + } else { + animationType = ANIMATION_TYPE_REPLACE; + countAnimator.setDuration(430); + countAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + } + if (countLayout != null) { + String oldStr = String.valueOf(currentCount); + + if (oldStr.length() == newStr.length()) { + SpannableStringBuilder oldSpannableStr = new SpannableStringBuilder(oldStr); + SpannableStringBuilder newSpannableStr = new SpannableStringBuilder(newStr); + SpannableStringBuilder stableStr = new SpannableStringBuilder(newStr); + for (int i = 0; i < oldStr.length(); i++) { + if (oldStr.charAt(i) == newStr.charAt(i)) { + oldSpannableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0); + newSpannableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0); + } else { + stableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0); + } + } + + int countOldWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(oldStr))); + countOldLayout = new StaticLayout(oldSpannableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + countAnimationStableLayout = new StaticLayout(stableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + countAnimationInLayout = new StaticLayout(newSpannableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + } else { + countOldLayout = countLayout; + } + } + countWidthOld = countWidth; + countAnimationIncrement = count > currentCount; + countAnimator.start(); + } + if (count > 0) { + countWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(newStr))); + countLayout = new StaticLayout(newStr, textPaint, countWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); + } + + currentCount = count; + if (parent != null) { + parent.invalidate(); + } + } + + public void draw(Canvas canvas) { + if (type != TYPE_CHAT_PULLING_DOWN) { + int textColor = Theme.getColor(textColorKey); + int circleColor = Theme.getColor(circleColorKey); + if (this.textColor != textColor) { + this.textColor = textColor; + textPaint.setColor(textColor); + } + if (this.circleColor != circleColor) { + this.circleColor = circleColor; + circlePaint.setColor(circleColor); + } + } + if (countChangeProgress != 1f) { + if (animationType == ANIMATION_TYPE_IN || animationType == ANIMATION_TYPE_OUT) { + updateX(countWidth); + float cx = countLeft + countWidth / 2f; + float cy = lastH / 2f; + canvas.save(); + float progress = animationType == ANIMATION_TYPE_IN ? countChangeProgress : (1f - countChangeProgress); + canvas.scale(progress, progress, cx, cy); + drawInternal(canvas); + canvas.restore(); + } else { + float progressHalf = countChangeProgress * 2; + if (progressHalf > 1f) { + progressHalf = 1f; + } + + float countTop = (lastH - AndroidUtilities.dp(23)) / 2f; + float countWidth; + if (this.countWidth == this.countWidthOld) { + countWidth = this.countWidth; + } else { + countWidth = this.countWidth * progressHalf + this.countWidthOld * (1f - progressHalf); + } + updateX(countWidth); + + float scale = 1f; + if (countAnimationIncrement) { + if (countChangeProgress <= 0.5f) { + scale += 0.1f * CubicBezierInterpolator.EASE_OUT.getInterpolation(countChangeProgress * 2); + } else { + scale += 0.1f * CubicBezierInterpolator.EASE_IN.getInterpolation((1f - (countChangeProgress - 0.5f) * 2)); + } + } + + rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23)); + canvas.save(); + canvas.scale(scale, scale, rectF.centerX(), rectF.centerY()); + canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, circlePaint); + if (Theme.hasGradientService()) { + canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, Theme.chat_actionBackgroundGradientDarkenPaint); + } + canvas.clipRect(rectF); + + boolean increment = reverseAnimation != countAnimationIncrement; + if (countAnimationInLayout != null) { + canvas.save(); + canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? AndroidUtilities.dp(13) : -AndroidUtilities.dp(13)) * (1f - progressHalf)); + textPaint.setAlpha((int) (255 * progressHalf)); + countAnimationInLayout.draw(canvas); + canvas.restore(); + } else if (countLayout != null) { + canvas.save(); + canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? AndroidUtilities.dp(13) : -AndroidUtilities.dp(13)) * (1f - progressHalf)); + textPaint.setAlpha((int) (255 * progressHalf)); + countLayout.draw(canvas); + canvas.restore(); + } + + if (countOldLayout != null) { + canvas.save(); + canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? -AndroidUtilities.dp(13) : AndroidUtilities.dp(13)) * (progressHalf)); + textPaint.setAlpha((int) (255 * (1f - progressHalf))); + countOldLayout.draw(canvas); + canvas.restore(); + } + + if (countAnimationStableLayout != null) { + canvas.save(); + canvas.translate(countLeft, countTop + AndroidUtilities.dp(4)); + textPaint.setAlpha(255); + countAnimationStableLayout.draw(canvas); + canvas.restore(); + } + textPaint.setAlpha(255); + canvas.restore(); + } + } else { + drawInternal(canvas); + } + } + + public void updateBackgroundRect() { + if (countChangeProgress != 1f) { + if (animationType == ANIMATION_TYPE_IN || animationType == ANIMATION_TYPE_OUT) { + updateX(countWidth); + float countTop = (lastH - AndroidUtilities.dp(23)) / 2f; + rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23)); + } else { + float progressHalf = countChangeProgress * 2; + if (progressHalf > 1f) { + progressHalf = 1f; + } + float countTop = (lastH - AndroidUtilities.dp(23)) / 2f; + float countWidth; + if (this.countWidth == this.countWidthOld) { + countWidth = this.countWidth; + } else { + countWidth = this.countWidth * progressHalf + this.countWidthOld * (1f - progressHalf); + } + updateX(countWidth); + rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23)); + } + } else { + updateX(countWidth); + float countTop = (lastH - AndroidUtilities.dp(23)) / 2f; + rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23)); + } + } + + private void updateX(float countWidth) { + if (gravity == Gravity.RIGHT) { + countLeft = width - AndroidUtilities.dp(5.5f); + if (horizontalPadding != 0) { + countLeft -= Math.max(horizontalPadding + countWidth / 2f, countWidth); + } else { + countLeft -= countWidth; + } + } else if (gravity == Gravity.LEFT) { + countLeft = AndroidUtilities.dp(5.5f); + } else { + countLeft = (int) ((width - countWidth) / 2f); + } + x = countLeft - AndroidUtilities.dp(5.5f); + } + + public float getCenterX() { + updateX(countWidth); + return countLeft + countWidth / 2f; + } + + public void setType(int type) { + this.type = type; + } + + public void setParent(View parent) { + this.parent = parent; + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java index 27367307f..e778e5600 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextBoldCursor.java @@ -572,9 +572,9 @@ public class EditTextBoldCursor extends EditText { } if (supportRtlHint && LocaleController.isRTL) { float offset = getMeasuredWidth() - hintWidth; - canvas.translate(left + getScrollX() + offset, lineY - hintLayout.getHeight() - AndroidUtilities.dp(6)); + canvas.translate(left + getScrollX() + offset, lineY - hintLayout.getHeight() - AndroidUtilities.dp(7)); } else { - canvas.translate(left + getScrollX(), lineY - hintLayout.getHeight() - AndroidUtilities.dp(6)); + canvas.translate(left + getScrollX(), lineY - hintLayout.getHeight() - AndroidUtilities.dp2(7)); } if (transformHintToHeader) { float scale = 1.0f - 0.3f * headerAnimationProgress; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEmoji.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEmoji.java index 1c8efff62..c73dccd1b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEmoji.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EditTextEmoji.java @@ -470,7 +470,7 @@ public class EditTextEmoji extends FrameLayout implements NotificationCenter.Not if (emojiView != null) { return; } - emojiView = new EmojiView(false, false, getContext(), false, null); + emojiView = new EmojiView(false, false, getContext(), false, null, null); emojiView.setVisibility(GONE); if (AndroidUtilities.isTablet()) { emojiView.setForseMultiwindowLayout(true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java index c4f4792ac..a83b56659 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java @@ -15,6 +15,7 @@ import android.animation.ObjectAnimator; import android.animation.StateListAnimator; import android.annotation.SuppressLint; import android.annotation.TargetApi; +import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.database.DataSetObserver; @@ -24,6 +25,7 @@ import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; @@ -37,10 +39,12 @@ import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearSmoothScroller; +import androidx.recyclerview.widget.LinearSmoothScrollerCustom; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.SimpleItemAnimator; import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; + import android.text.Editable; import android.text.SpannableStringBuilder; import android.text.TextUtils; @@ -67,6 +71,8 @@ import android.widget.LinearLayout; import android.widget.PopupWindow; import android.widget.TextView; +import com.google.android.exoplayer2.util.Log; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BuildVars; import org.telegram.messenger.ChatObject; @@ -139,9 +145,11 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private EmojiSearchAdapter emojiSearchAdapter; private SearchField emojiSearchField; private AnimatorSet emojiTabShadowAnimator; + private RecyclerAnimationScrollHelper scrollHelper; private boolean firstEmojiAttach = true; private boolean needEmojiSearch; private int hasRecentEmoji = -1; + private boolean hasChatStickers; private FrameLayout gifContainer; private RecyclerListView gifGridView; @@ -163,19 +171,34 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private StickersSearchGridAdapter stickersSearchGridAdapter; private RecyclerListView.OnItemClickListener stickersOnItemClickListener; private ScrollSlidingTabStrip stickersTab; + private FrameLayout stickersTabContainer; private RecyclerListView stickersGridView; private GridLayoutManager stickersLayoutManager; + private TrendingAdapter trendingAdapter; private SearchField stickersSearchField; private int stickersMinusDy; private boolean firstStickersAttach = true; + private boolean ignoreStickersScroll; + private boolean stickersContainerAttached; private AnimatorSet searchAnimation; private TextView mediaBanTooltip; private DragListener dragListener; + private boolean showing; private final int[] tabsMinusDy = new int[3]; private ObjectAnimator[] tabsYAnimators = new ObjectAnimator[3]; + private boolean firstTabUpdate; + + public void setShowing(boolean showing) { + this.showing = showing; + updateStickerTabsPosition(); + } + + public void onMessageSend() { + chooseStickerActionTracker.reset(); + } @IntDef({Type.STICKERS, Type.EMOJIS, Type.GIFS}) @Retention(RetentionPolicy.SOURCE) @@ -205,6 +228,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private ArrayList recentGifs = new ArrayList<>(); private ArrayList recentStickers = new ArrayList<>(); private ArrayList favouriteStickers = new ArrayList<>(); + private ArrayList featuredStickerSets = new ArrayList<>(); private Paint dotPaint; @@ -249,6 +273,18 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private float emojiLastY; private float emojiTouchedX; private float emojiTouchedY; + private float lastStickersX; + private boolean expandStickersByDragg; + + private Runnable checkExpandStickerTabsRunnable = new Runnable() { + @Override + public void run() { + if (!stickersTab.isDragging()) { + expandStickersByDragg = false; + updateStickerTabsPosition(); + } + } + }; public interface EmojiViewDelegate { default boolean onBackspace() { @@ -319,15 +355,30 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific return 0; } + default int getThreadId() { + return 0; + } + default void showTrendingStickersAlert(TrendingStickersLayout layout) { } + + default void invalidateEnterView() { + + } + + default float getProgressToSearchOpened() { + return 0f; + } } public interface DragListener { void onDragStart(); + void onDragEnd(float velocity); + void onDragCancel(); + void onDrag(int offset); } @@ -389,6 +440,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific }; private static final Field superListenerField; + static { Field f = null; try { @@ -583,9 +635,15 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific if (!smoothScrolling) { animateTabsY(type); } + if (ignoreStickersScroll) { + ignoreStickersScroll = false; + } smoothScrolling = false; } else { if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { + if (ignoreStickersScroll) { + ignoreStickersScroll = false; + } final SearchField searchField = getSearchFieldForType(type); if (searchField != null) { searchField.hideKeyboard(); @@ -595,6 +653,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific if (!smoothScrolling) { stopAnimatingTabsY(type); } + if (type == Type.STICKERS) { + chooseStickerActionTracker.doSomeAction(); + } } } @@ -642,6 +703,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific @Override public boolean onInterceptTouchEvent(MotionEvent ev) { + if (isDragging()) { + return super.onInterceptTouchEvent(ev); + } if (getParent() != null) { getParent().requestDisallowInterceptTouchEvent(true); } @@ -668,18 +732,27 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific @Override public boolean onTouchEvent(MotionEvent ev) { + if (isDragging()) { + return super.onTouchEvent(ev); + } if (first) { first = false; lastX = ev.getX(); } + if (ev.getAction() == MotionEvent.ACTION_DOWN || ev.getAction() == MotionEvent.ACTION_MOVE) { + lastStickersX = ev.getRawX(); + } if (ev.getAction() == MotionEvent.ACTION_DOWN) { draggingVertically = draggingHorizontally = false; downX = ev.getRawX(); downY = ev.getRawY(); } else { if (!draggingVertically && !draggingHorizontally && dragListener != null) { - if (Math.abs(ev.getRawX() - downX) >= touchSlop) { + if (Math.abs(ev.getRawX() - downX) >= touchSlop && canScrollHorizontally((int) (downX - ev.getRawX()))) { draggingHorizontally = true; + AndroidUtilities.cancelRunOnUIThread(checkExpandStickerTabsRunnable); + expandStickersByDragg = true; + updateStickerTabsPosition(); } else if (Math.abs(ev.getRawY() - downY) >= touchSlop) { draggingVertically = true; downY = ev.getRawY(); @@ -691,6 +764,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } } } + if (expandStickersByDragg && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL)) { + AndroidUtilities.runOnUIThread(checkExpandStickerTabsRunnable, 1500); + } if (draggingVertically) { if (vTracker == null) { vTracker = VelocityTracker.obtain(); @@ -711,6 +787,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } else { dragListener.onDrag(Math.round(ev.getRawY() - downY)); } + cancelLongPress(); return true; } float newTranslationX = getTranslationX(); @@ -999,7 +1076,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } } - public EmojiView(boolean needStickers, boolean needGif, final Context context, boolean needSearch, final TLRPC.ChatFull chatFull) { + public EmojiView(boolean needStickers, boolean needGif, final Context context, boolean needSearch, final TLRPC.ChatFull chatFull, ViewGroup parentView) { super(context); int color = Theme.getColor(Theme.key_chat_emojiBottomPanelIcon); @@ -1027,12 +1104,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific }; stickerIcons = new Drawable[]{ - Theme.createEmojiIconSelectorDrawable(context, R.drawable.stickers_recent, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)), - Theme.createEmojiIconSelectorDrawable(context, R.drawable.stickers_favorites, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)), - Theme.createEmojiIconSelectorDrawable(context, R.drawable.stickers_trending3, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)), + Theme.createEmojiIconSelectorDrawable(context, R.drawable.emoji_tabs_recent, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)), + Theme.createEmojiIconSelectorDrawable(context, R.drawable.emoji_tabs_faves, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)), + Theme.createEmojiIconSelectorDrawable(context, R.drawable.emoji_tabs_new3, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)), new LayerDrawable(new Drawable[]{ - Theme.createEmojiIconSelectorDrawable(context, R.drawable.stickers_trending1, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)), - Theme.createEmojiIconSelectorDrawable(context, R.drawable.stickers_trending2, Theme.getColor(Theme.key_chat_emojiPanelStickerPackSelectorLine), Theme.getColor(Theme.key_chat_emojiPanelStickerPackSelectorLine)) + Theme.createEmojiIconSelectorDrawable(context, R.drawable.emoji_tabs_new1, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)), + Theme.createEmojiIconSelectorDrawable(context, R.drawable.emoji_tabs_new2, Theme.getColor(Theme.key_chat_emojiPanelStickerPackSelectorLine), Theme.getColor(Theme.key_chat_emojiPanelStickerPackSelectorLine)) }) }; @@ -1551,7 +1628,24 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific gifAdapter.loadTrendingGifs(); } - stickersContainer = new FrameLayout(context); + stickersContainer = new FrameLayout(context) { + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + stickersContainerAttached = true; + updateStickerTabsPosition(); + chooseStickerActionTracker.checkVisibility(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + stickersContainerAttached = false; + updateStickerTabsPosition(); + chooseStickerActionTracker.checkVisibility(); + } + }; MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_IMAGE); MediaDataController.getInstance(currentAccount).checkFeaturedStickers(); @@ -1591,7 +1685,29 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } }; - stickersGridView.setLayoutManager(stickersLayoutManager = new GridLayoutManager(context, 5)); + stickersGridView.setLayoutManager(stickersLayoutManager = new GridLayoutManager(context, 5) { + @Override + public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { + try { + LinearSmoothScrollerCustom linearSmoothScroller = new LinearSmoothScrollerCustom(recyclerView.getContext(), LinearSmoothScrollerCustom.POSITION_TOP); + linearSmoothScroller.setTargetPosition(position); + startSmoothScroll(linearSmoothScroller); + } catch (Exception e) { + FileLog.e(e); + } + } + + @Override + public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { + int i = super.scrollVerticallyBy(dy, recycler, state); + if (i != 0 && stickersGridView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) { + expandStickersByDragg = false; + updateStickerTabsPosition(); + } + chooseStickerActionTracker.doSomeAction(); + return i; + } + }); stickersLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { @@ -1647,31 +1763,122 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific stickersGridView.setOnItemClickListener(stickersOnItemClickListener); stickersGridView.setGlowColor(Theme.getColor(Theme.key_chat_emojiPanelBackground)); stickersContainer.addView(stickersGridView); + scrollHelper = new RecyclerAnimationScrollHelper(stickersGridView, stickersLayoutManager); stickersSearchField = new SearchField(context, 0); stickersContainer.addView(stickersSearchField, new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, searchFieldHeight + AndroidUtilities.getShadowHeight())); - stickersTab = new DraggableScrollSlidingTabStrip(context); + stickersTab = new DraggableScrollSlidingTabStrip(context) { + + @Override + protected void updatePosition() { + updateStickerTabsPosition(); + stickersTabContainer.invalidate(); + invalidate(); + if (delegate != null) { + delegate.invalidateEnterView(); + } + } + + @Override + protected void stickerSetPositionChanged(int fromPosition, int toPosition) { + int index1 = fromPosition - stickersTabOffset; + int index2 = toPosition - stickersTabOffset; + + final MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount); + + swapListElements(stickerSets, index1, index2); + if (hasChatStickers) { + swapListElements(mediaDataController.getStickerSets(MediaDataController.TYPE_IMAGE), index1 - 1, index2 - 1); + } else { + swapListElements(mediaDataController.getStickerSets(MediaDataController.TYPE_IMAGE), index1, index2); + } + + reloadStickersAdapter(); + AndroidUtilities.cancelRunOnUIThread(checkExpandStickerTabsRunnable); + AndroidUtilities.runOnUIThread(checkExpandStickerTabsRunnable, 1500); + sendReorder(); + updateStickerTabs(); + } + + private void swapListElements(List list, int index1, int index2) { + final TLRPC.TL_messages_stickerSet set1 = list.remove(index1); + list.add(index2, set1); + } + + private void sendReorder() { + MediaDataController.getInstance(currentAccount).calcNewHash(MediaDataController.TYPE_IMAGE); + TLRPC.TL_messages_reorderStickerSets req = new TLRPC.TL_messages_reorderStickerSets(); + req.masks = false; + for (int a = hasChatStickers ? 1 : 0; a < stickerSets.size(); a++) { + req.order.add(stickerSets.get(a).set.id); + } + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { }); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.stickersDidLoad, MediaDataController.TYPE_IMAGE); + } + + @Override + protected void invalidateOverlays() { + stickersTabContainer.invalidate(); + } + }; + stickersTab.setDragEnabled(true); + stickersTab.setWillNotDraw(false); stickersTab.setType(ScrollSlidingTabStrip.Type.TAB); stickersTab.setUnderlineHeight(AndroidUtilities.getShadowHeight()); stickersTab.setIndicatorColor(Theme.getColor(Theme.key_chat_emojiPanelStickerPackSelectorLine)); stickersTab.setUnderlineColor(Theme.getColor(Theme.key_chat_emojiPanelShadowLine)); - stickersTab.setBackgroundColor(Theme.getColor(Theme.key_chat_emojiPanelBackground)); - stickersContainer.addView(stickersTab, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP)); + + if (parentView != null) { + stickersTabContainer = new FrameLayout(context) { + + Paint paint = new Paint(); + @Override + protected void dispatchDraw(Canvas canvas) { + float searchProgress = delegate.getProgressToSearchOpened(); + float searchProgressOffset = AndroidUtilities.dp(50) * delegate.getProgressToSearchOpened(); + if (searchProgressOffset > getMeasuredHeight()) { + return; + } + canvas.save(); + if (searchProgressOffset != 0) { + canvas.clipRect(0, searchProgressOffset, getMeasuredWidth(), getMeasuredHeight()); + } + paint.setColor(Theme.getColor(Theme.key_chat_emojiPanelBackground)); + canvas.drawRect(0, 0, getMeasuredWidth(), AndroidUtilities.dp(48) + stickersTab.getExpandedOffset(), paint); + super.dispatchDraw(canvas); + stickersTab.drawOverlays(canvas); + canvas.restore(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + updateStickerTabsPosition(); + } + }; + stickersTabContainer.addView(stickersTab, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP)); + parentView.addView(stickersTabContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } else { + stickersContainer.addView(stickersTab, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP)); + } updateStickerTabs(); stickersTab.setDelegate(page -> { + if (firstTabUpdate) { + return; + } if (page == trendingTabNum) { openTrendingStickers(null); return; } else if (page == recentTabBum) { stickersGridView.stopScroll(); - stickersLayoutManager.scrollToPositionWithOffset(stickersGridAdapter.getPositionForPack("recent"), 0); + scrollStickersToPosition(stickersGridAdapter.getPositionForPack("recent"), 0); resetTabsY(Type.STICKERS); stickersTab.onPageScrolled(recentTabBum, recentTabBum > 0 ? recentTabBum : stickersTabOffset); return; } else if (page == favTabBum) { stickersGridView.stopScroll(); - stickersLayoutManager.scrollToPositionWithOffset(stickersGridAdapter.getPositionForPack("fav"), 0); + scrollStickersToPosition(stickersGridAdapter.getPositionForPack("fav"), 0); resetTabsY(Type.STICKERS); stickersTab.onPageScrolled(favTabBum, favTabBum > 0 ? favTabBum : stickersTabOffset); return; @@ -1686,9 +1893,20 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } firstStickersAttach = false; stickersGridView.stopScroll(); - stickersLayoutManager.scrollToPositionWithOffset(stickersGridAdapter.getPositionForPack(stickerSets.get(index)), 0); + scrollStickersToPosition(stickersGridAdapter.getPositionForPack(stickerSets.get(index)), 0); resetTabsY(Type.STICKERS); checkScroll(Type.STICKERS); + int firstTab; + if (favTabBum > 0) { + firstTab = favTabBum; + } else if (recentTabBum > 0) { + firstTab = recentTabBum; + } else { + firstTab = stickersTabOffset; + } + stickersTab.onPageScrolled(page, firstTab); + expandStickersByDragg = false; + updateStickerTabsPosition(); }); stickersGridView.setOnScrollListener(new TypedScrollListener(Type.STICKERS)); @@ -1809,6 +2027,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific typeTabs.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + checkGridVisibility(position, positionOffset); EmojiView.this.onPageScrolled(position, getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), positionOffsetPixels); showBottomTab(true, true); SearchField currentField; @@ -1837,6 +2056,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific field.searchEditText.setSelection(currentFieldText.length()); } startStopVisibleGifs((position == 0 && positionOffset > 0) || position == 1); + updateStickerTabsPosition(); } @Override @@ -1972,6 +2192,31 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } } + private void checkGridVisibility(int position, float positionOffset) { + if (stickersContainer == null || gifContainer == null) { + return; + } + if (position == 0) { + emojiGridView.setVisibility(View.VISIBLE); + gifGridView.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE); + gifTabs.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE); + stickersGridView.setVisibility(View.GONE); + stickersTab.setVisibility(View.GONE); + } else if (position == 1) { + emojiGridView.setVisibility(View.GONE); + gifGridView.setVisibility(View.VISIBLE); + gifTabs.setVisibility(View.VISIBLE); + stickersGridView.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE); + stickersTab.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE); + } else if (position == 2) { + emojiGridView.setVisibility(View.GONE); + gifGridView.setVisibility(View.GONE); + gifTabs.setVisibility(View.GONE); + stickersGridView.setVisibility(View.VISIBLE); + stickersTab.setVisibility(View.VISIBLE); + } + } + private static String addColorToCode(String code, String color) { String end = null; int length = code.length(); @@ -2051,6 +2296,41 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific public void setTranslationY(float translationY) { super.setTranslationY(translationY); updateBottomTabContainerPosition(); + updateStickerTabsPosition(); + } + + Rect rect = new Rect(); + private void updateStickerTabsPosition() { + if (stickersTabContainer == null) { + return; + } + boolean visible = getVisibility() == View.VISIBLE && stickersContainerAttached && delegate.getProgressToSearchOpened() != 1f; + stickersTabContainer.setVisibility(visible ? View.VISIBLE : View.GONE); + + if (visible) { + rect.setEmpty(); + pager.getChildVisibleRect(stickersContainer, rect, null); + float searchProgressOffset = AndroidUtilities.dp(50) * delegate.getProgressToSearchOpened(); + int left = rect.left; + if (left != 0 || searchProgressOffset != 0) { + expandStickersByDragg = false; + } + + stickersTabContainer.setTranslationX(left); + float y = getTop() + getTranslationY() - stickersTabContainer.getTop() - stickersTab.getExpandedOffset() - searchProgressOffset; + if (stickersTabContainer.getTranslationY() != y) { + stickersTabContainer.setTranslationY(y); + stickersTabContainer.invalidate(); + } + } + + if (expandStickersByDragg && (visible && showing)) { + stickersTab.expandStickers(lastStickersX, true); + } else { + expandStickersByDragg = false; + stickersTab.expandStickers(lastStickersX, false); + } + } private void updateBottomTabContainerPosition() { @@ -2059,7 +2339,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific if (parent != null) { float y = getY() - parent.getHeight(); if (getLayoutParams().height > 0) { - y += getLayoutParams().height; + y += getLayoutParams().height; } else { y += getMeasuredHeight(); } @@ -2189,7 +2469,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific if (searchField == currentField && delegate != null && delegate.isExpanded()) { searchAnimation = new AnimatorSet(); - if (tabStrip != null) { + if (tabStrip != null && a != 2) { searchAnimation.playTogether( ObjectAnimator.ofFloat(tabStrip, View.TRANSLATION_Y, -AndroidUtilities.dp(48)), ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, -AndroidUtilities.dp(48)), @@ -2199,8 +2479,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, -AndroidUtilities.dp(48)), ObjectAnimator.ofFloat(currentField, View.TRANSLATION_Y, AndroidUtilities.dp(0))); } - searchAnimation.setDuration(200); - searchAnimation.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + searchAnimation.setDuration(220); + searchAnimation.setInterpolator(CubicBezierInterpolator.DEFAULT); searchAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -2225,7 +2505,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific searchAnimation.start(); } else { currentField.setTranslationY(AndroidUtilities.dp(0)); - if (tabStrip != null) { + if (tabStrip != null && a != 2) { tabStrip.setTranslationY(-AndroidUtilities.dp(48)); } if (gridView == stickersGridView) { @@ -2276,6 +2556,18 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific closeSearch(animated, -1); } + private void scrollStickersToPosition(int p, int offset) { + View view = stickersLayoutManager.findViewByPosition(p); + int firstPosition = stickersLayoutManager.findFirstVisibleItemPosition(); + if (view == null && Math.abs(p - firstPosition) > 40) { + scrollHelper.setScrollDirection(stickersLayoutManager.findFirstVisibleItemPosition() < p ? RecyclerAnimationScrollHelper.SCROLL_DIRECTION_DOWN : RecyclerAnimationScrollHelper.SCROLL_DIRECTION_UP); + scrollHelper.scrollToPosition(p, offset, false, true); + } else { + ignoreStickersScroll = true; + stickersGridView.smoothScrollToPosition(p); + } + } + public void closeSearch(boolean animated, long scrollToSet) { if (searchAnimation != null) { searchAnimation.cancel(); @@ -2287,8 +2579,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific TLRPC.TL_messages_stickerSet set = MediaDataController.getInstance(currentAccount).getStickerSetById(scrollToSet); if (set != null) { int pos = stickersGridAdapter.getPositionForPack(set); - if (pos >= 0) { - stickersLayoutManager.scrollToPositionWithOffset(pos, AndroidUtilities.dp(48 + 12)); + if (pos >= 0 && pos < stickersGridAdapter.getItemCount()) { + scrollStickersToPosition(pos, AndroidUtilities.dp(48 + 12)); } } } @@ -2328,14 +2620,14 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific if (a == currentItem && animated) { searchAnimation = new AnimatorSet(); - if (tabStrip != null) { + if (tabStrip != null && a != 2) { searchAnimation.playTogether( ObjectAnimator.ofFloat(tabStrip, View.TRANSLATION_Y, 0), ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, AndroidUtilities.dp(48) - searchFieldHeight), ObjectAnimator.ofFloat(currentField, View.TRANSLATION_Y, AndroidUtilities.dp(48) - searchFieldHeight)); } else { searchAnimation.playTogether( - ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, -searchFieldHeight), + ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, AndroidUtilities.dp(48) - searchFieldHeight), ObjectAnimator.ofFloat(currentField, View.TRANSLATION_Y, -searchFieldHeight)); } searchAnimation.setDuration(200); @@ -2375,7 +2667,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific searchAnimation.start(); } else { currentField.setTranslationY(AndroidUtilities.dp(48) - searchFieldHeight); - if (tabStrip != null) { + if (tabStrip != null && a != 2) { tabStrip.setTranslationY(0); } if (gridView == stickersGridView) { @@ -2537,7 +2829,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific checkEmojiTabY(emojiGridView, dy); return; } - if (delegate != null && delegate.isSearchOpened()) { + if (delegate != null && delegate.isSearchOpened() || ignoreStickersScroll) { return; } final RecyclerListView listView = getListViewForType(type); @@ -2553,18 +2845,22 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } else if (tabsMinusDy[type] < -AndroidUtilities.dp(48 * 6)) { tabsMinusDy[type] = -AndroidUtilities.dp(48 * 6); } - getTabsForType(type).setTranslationY(Math.max(-AndroidUtilities.dp(48), tabsMinusDy[type])); + if (type == 0) { + updateStickerTabsPosition(); + } else { + getTabsForType(type).setTranslationY(Math.max(-AndroidUtilities.dp(48), tabsMinusDy[type])); + } } private void resetTabsY(@Type int type) { - if (delegate != null && delegate.isSearchOpened()) { + if (delegate != null && delegate.isSearchOpened() || type == Type.STICKERS) { return; } getTabsForType(type).setTranslationY(tabsMinusDy[type] = 0); } private void animateTabsY(@Type int type) { - if (delegate != null && delegate.isSearchOpened()) { + if ((delegate != null && delegate.isSearchOpened()) || type == Type.STICKERS) { return; } final float tabsHeight = AndroidUtilities.dpf2(type == Type.EMOJIS ? 38 : 48); @@ -2635,9 +2931,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private ScrollSlidingTabStrip getTabsForType(@Type int type) { switch (type) { - case Type.STICKERS: return stickersTab; - case Type.EMOJIS: return emojiTabs; - case Type.GIFS: return gifTabs; + case Type.STICKERS: + return stickersTab; + case Type.EMOJIS: + return emojiTabs; + case Type.GIFS: + return gifTabs; default: throw new IllegalArgumentException("Unexpected argument: " + type); } @@ -2645,9 +2944,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private RecyclerListView getListViewForType(@Type int type) { switch (type) { - case Type.STICKERS: return stickersGridView; - case Type.EMOJIS: return emojiGridView; - case Type.GIFS: return gifGridView; + case Type.STICKERS: + return stickersGridView; + case Type.EMOJIS: + return emojiGridView; + case Type.GIFS: + return gifGridView; default: throw new IllegalArgumentException("Unexpected argument: " + type); } @@ -2655,9 +2957,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private GridLayoutManager getLayoutManagerForType(@Type int type) { switch (type) { - case Type.STICKERS: return stickersLayoutManager; - case Type.EMOJIS: return emojiLayoutManager; - case Type.GIFS: return gifLayoutManager; + case Type.STICKERS: + return stickersLayoutManager; + case Type.EMOJIS: + return emojiLayoutManager; + case Type.GIFS: + return gifLayoutManager; default: throw new IllegalArgumentException("Unexpected argument: " + type); } @@ -2665,9 +2970,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private SearchField getSearchFieldForType(@Type int type) { switch (type) { - case Type.STICKERS: return stickersSearchField; - case Type.EMOJIS: return emojiSearchField; - case Type.GIFS: return gifSearchField; + case Type.STICKERS: + return stickersSearchField; + case Type.EMOJIS: + return emojiSearchField; + case Type.GIFS: + return gifSearchField; default: throw new IllegalArgumentException("Unexpected argument: " + type); } @@ -2770,6 +3078,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific private void checkScroll(@Type int type) { if (type == Type.STICKERS) { + if (ignoreStickersScroll) { + return; + } int firstVisibleItem = stickersLayoutManager.findFirstVisibleItemPosition(); if (firstVisibleItem == RecyclerView.NO_POSITION) { return; @@ -2881,12 +3192,13 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } private void updateStickerTabs() { - if (stickersTab == null) { + if (stickersTab == null || stickersTab.isDragging()) { return; } recentTabBum = -2; favTabBum = -2; trendingTabNum = -2; + hasChatStickers = false; stickersTabOffset = 0; int lastPosition = stickersTab.getCurrentPosition(); @@ -2895,10 +3207,22 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific final MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount); SharedPreferences preferences = MessagesController.getEmojiSettings(currentAccount); + featuredStickerSets.clear(); ArrayList featured = mediaDataController.getFeaturedStickerSets(); - if (!featured.isEmpty() && (!BuildVars.DEBUG_PRIVATE_VERSION || (mediaDataController.getUnreadStickerSets().isEmpty() || preferences.getLong("featured_hidden", 0) == featured.get(0).set.id))) { + for (int a = 0, N = featured.size(); a < N; a++) { + TLRPC.StickerSetCovered set = featured.get(a); + if (mediaDataController.isStickerPackInstalled(set.set.id)) { + continue; + } + featuredStickerSets.add(set); + } + if (trendingAdapter != null) { + trendingAdapter.notifyDataSetChanged(); + } + if (!featured.isEmpty() && (featuredStickerSets.isEmpty() || preferences.getLong("featured_hidden", 0) == featured.get(0).set.id)) { final int id = mediaDataController.getUnreadStickerSets().isEmpty() ? 2 : 3; - final ImageView trendingStickersTabView = stickersTab.addIconTab(id, stickerIcons[id]); + final StickerTabView trendingStickersTabView = stickersTab.addStickerIconTab(id, stickerIcons[id]); + trendingStickersTabView.textView.setText(LocaleController.getString("FeaturedStickersShort", R.string.FeaturedStickersShort)); trendingStickersTabView.setContentDescription(LocaleController.getString("FeaturedStickers", R.string.FeaturedStickers)); trendingTabNum = stickersTabOffset; stickersTabOffset++; @@ -2907,13 +3231,17 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific if (!favouriteStickers.isEmpty()) { favTabBum = stickersTabOffset; stickersTabOffset++; - stickersTab.addIconTab(1, stickerIcons[1]).setContentDescription(LocaleController.getString("FavoriteStickers", R.string.FavoriteStickers)); + StickerTabView stickerTabView = stickersTab.addStickerIconTab(1, stickerIcons[1]); + stickerTabView.textView.setText(LocaleController.getString("FavoriteStickersShort", R.string.FavoriteStickersShort)); + stickerTabView.setContentDescription(LocaleController.getString("FavoriteStickers", R.string.FavoriteStickers)); } if (!recentStickers.isEmpty()) { recentTabBum = stickersTabOffset; stickersTabOffset++; - stickersTab.addIconTab(0, stickerIcons[0]).setContentDescription(LocaleController.getString("RecentStickers", R.string.RecentStickers)); + StickerTabView stickerTabView = stickersTab.addStickerIconTab(0, stickerIcons[0]); + stickerTabView.textView.setText(LocaleController.getString("RecentStickersShort", R.string.RecentStickersShort)); + stickerTabView.setContentDescription(LocaleController.getString("RecentStickers", R.string.RecentStickers)); } stickerSets.clear(); @@ -2990,17 +3318,17 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific stickerSets.remove(0); a--; } else { + hasChatStickers = true; stickersTab.addStickerTab(chat); } } else { TLRPC.TL_messages_stickerSet stickerSet = stickerSets.get(a); TLRPC.Document document = stickerSet.documents.get(0); TLObject thumb = FileLoader.getClosestPhotoSizeWithSize(stickerSet.set.thumbs, 90); - SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(stickerSet.set.thumbs, Theme.key_emptyListPlaceholder, 0.2f); if (thumb == null) { thumb = document; } - stickersTab.addStickerTab(thumb, svgThumb, document, stickerSet).setContentDescription(stickerSet.set.title + ", " + LocaleController.getString("AccDescrStickerSet", R.string.AccDescrStickerSet)); + stickersTab.addStickerTab(thumb, document, stickerSet).setContentDescription(stickerSet.set.title + ", " + LocaleController.getString("AccDescrStickerSet", R.string.AccDescrStickerSet)); } } stickersTab.commitUpdate(); @@ -3060,8 +3388,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific final Emoji.EmojiDrawable emojiDrawable = Emoji.getEmojiDrawable(emoji); if (emojiDrawable != null) { gifTabsCount++; - final ImageView iconTab = gifTabs.addIconTab(3 + i, emojiDrawable); - iconTab.setPadding(hPadding, vPadding, hPadding, vPadding); + TLRPC.Document document = MediaDataController.getInstance(currentAccount).getEmojiAnimatedSticker(emoji); + final View iconTab = gifTabs.addEmojiTab(3 + i, emojiDrawable, document); + // iconTab.setPadding(hPadding, vPadding, hPadding, vPadding); iconTab.setContentDescription(emoji); } } @@ -3286,6 +3615,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } } super.onLayout(changed, left, top, right, bottom); + updateStickerTabsPosition(); } private void reloadStickersAdapter() { @@ -3339,6 +3669,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific pager.setCurrentItem(2, false); } if (stickersTab != null) { + firstTabUpdate = true; if (favTabBum >= 0) { stickersTab.selectTab(favTabBum); } else if (recentTabBum >= 0) { @@ -3346,6 +3677,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } else { stickersTab.selectTab(stickersTabOffset); } + firstTabUpdate = false; + stickersLayoutManager.scrollToPositionWithOffset(1, 0); } } else if (currentPage == 2) { showBackspaceButton(false, false); @@ -3397,6 +3730,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific MediaDataController.getInstance(currentAccount).loadRecents(MediaDataController.TYPE_IMAGE, false, true, false); MediaDataController.getInstance(currentAccount).loadRecents(MediaDataController.TYPE_FAVE, false, true, false); } + chooseStickerActionTracker.checkVisibility(); } public int getCurrentPage() { @@ -3684,7 +4018,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { BackupImageView imageView = (BackupImageView) holder.itemView; - TLRPC.StickerSetCovered set = MediaDataController.getInstance(currentAccount).getFeaturedStickerSets().get(position); + TLRPC.StickerSetCovered set = featuredStickerSets.get(position); imageView.setTag(set); TLRPC.Document document = set.cover; @@ -3693,6 +4027,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } TLObject object = FileLoader.getClosestPhotoSizeWithSize(set.set.thumbs, 90); SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(set.set.thumbs, Theme.key_emptyListPlaceholder, 0.2f); + if (svgThumb != null) { + svgThumb.overrideWidthAndHeight(512, 512); + } if (object == null) { object = document; } @@ -3731,7 +4068,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific @Override public int getItemCount() { - return MediaDataController.getInstance(currentAccount).getFeaturedStickerSets().size(); + return featuredStickerSets.size(); } } @@ -3882,12 +4219,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific ((StickerSetNameCell) view).setOnIconClickListener(v -> { MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount); ArrayList featured = mediaDataController.getFeaturedStickerSets(); - if (!featured.isEmpty() && !mediaDataController.getUnreadStickerSets().isEmpty()) { + if (!featured.isEmpty()) { MessagesController.getEmojiSettings(currentAccount).edit().putLong("featured_hidden", featured.get(0).set.id).commit(); - updateStickerTabs(); if (stickersGridAdapter != null) { stickersGridAdapter.notifyItemRangeRemoved(1, 2); } + updateStickerTabs(); } }); break; @@ -3902,6 +4239,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific return super.onInterceptTouchEvent(e); } }; + horizontalListView.setSelectorRadius(AndroidUtilities.dp(4)); + horizontalListView.setSelectorDrawableColor(Theme.getColor(Theme.key_listSelector)); horizontalListView.setTag(9); horizontalListView.setItemAnimator(null); horizontalListView.setLayoutAnimation(null); @@ -3913,7 +4252,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific }; layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); horizontalListView.setLayoutManager(layoutManager); - horizontalListView.setAdapter(new TrendingAdapter()); + horizontalListView.setAdapter(trendingAdapter = new TrendingAdapter()); horizontalListView.setOnItemClickListener((view1, position) -> { openTrendingStickers((TLRPC.StickerSetCovered) view1.getTag()); }); @@ -4037,7 +4376,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount); SharedPreferences preferences = MessagesController.getEmojiSettings(currentAccount); ArrayList featured = mediaDataController.getFeaturedStickerSets(); - if (BuildVars.DEBUG_PRIVATE_VERSION && !featured.isEmpty() && !mediaDataController.getUnreadStickerSets().isEmpty() && preferences.getLong("featured_hidden", 0) != featured.get(0).set.id) { + if (!featuredStickerSets.isEmpty() && preferences.getLong("featured_hidden", 0) != featured.get(0).set.id) { cache.put(totalItems++, "trend1"); cache.put(totalItems++, "trend2"); startRow += 2; @@ -4475,7 +4814,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific } public CharSequence getPageTitle(int position) { - switch(position) { + switch (position) { case 0: return LocaleController.getString("Emoji", R.string.Emoji); case 1: @@ -4505,12 +4844,6 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific return view == object; } - @Override - public void unregisterDataSetObserver(DataSetObserver observer) { - if (observer != null) { - super.unregisterDataSetObserver(observer); - } - } } @@ -5649,4 +5982,51 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific super.notifyDataSetChanged(); } } + + public void searchProgressChanged() { + updateStickerTabsPosition(); + } + + public float getStickersExpandOffset() { + return stickersTab == null ? 0 : stickersTab.getExpandedOffset(); + } + + private final ChooseStickerActionTracker chooseStickerActionTracker = new ChooseStickerActionTracker(); + + private class ChooseStickerActionTracker { + + boolean visible = false; + boolean typingWasSent; + long lastActionTime = -1; + + void doSomeAction() { + if (visible) { + if (lastActionTime == -1) { + lastActionTime = System.currentTimeMillis(); + return; + } + if (System.currentTimeMillis() - lastActionTime > 2000) { + typingWasSent = true; + MessagesController.getInstance(currentAccount).sendTyping(delegate.getDialogId(), delegate.getThreadId(), 10, 0); + } + } + } + + void checkVisibility() { + if (delegate == null) { + return; + } + visible = getVisibility() == View.VISIBLE && stickersContainerAttached; + if (!visible) { + reset(); + } + } + + private void reset() { + if (typingWasSent) { + MessagesController.getInstance(currentAccount).sendTyping(delegate.getDialogId(), delegate.getThreadId(), 2, 0); + } + lastActionTime = -1; + } + } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ForwardingPreviewView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ForwardingPreviewView.java new file mode 100644 index 000000000..86a128505 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ForwardingPreviewView.java @@ -0,0 +1,998 @@ +package org.telegram.ui.Components; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Outline; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.ScrollView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.ChatListItemAnimator; +import androidx.recyclerview.widget.GridLayoutManagerFixed; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.ChatObject; +import org.telegram.messenger.ContactsController; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ForwardingMessagesParams; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.ActionBarMenuSubItem; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.BotHelpCell; +import org.telegram.ui.Cells.ChatMessageCell; + +import java.util.ArrayList; + +public class ForwardingPreviewView extends FrameLayout { + + SizeNotifierFrameLayout chatPreviewContainer; + ActionBar actionBar; + RecyclerListView chatListView; + ChatListItemAnimator itemAnimator; + GridLayoutManagerFixed chatLayoutManager; + + ForwardingMessagesParams forwardingMessagesParams; + Adapter adapter; + + ScrollView menuScrollView; + LinearLayout menuContainer; + LinearLayout buttonsLayout; + LinearLayout buttonsLayout2; + + ActionBarMenuSubItem showSendersNameView; + ActionBarMenuSubItem hideSendersNameView; + + ActionBarMenuSubItem showCaptionView; + ActionBarMenuSubItem hideCaptionView; + + ActionBarMenuSubItem changeRecipientView; + ActionBarMenuSubItem sendMessagesView; + + int chatTopOffset; + float yOffset; + int currentTopOffset; + float currentYOffset; + + ArrayList actionItems = new ArrayList<>(); + + Rect rect = new Rect(); + + private boolean firstLayout = true; + ValueAnimator offsetsAnimator; + TLRPC.User currentUser; + TLRPC.Chat currentChat; + boolean showing; + boolean isLandscapeMode; + private final int currentAccount; + boolean returnSendersNames; + + Runnable changeBoundsRunnable = new Runnable() { + @Override + public void run() { + if (offsetsAnimator != null && !offsetsAnimator.isRunning()) { + offsetsAnimator.start(); + } + } + }; + + private final ArrayList drawingGroups = new ArrayList<>(10); + + @SuppressLint("ClickableViewAccessibility") + public ForwardingPreviewView(@NonNull Context context, ForwardingMessagesParams params, TLRPC.User user, TLRPC.Chat chat, int currentAccount) { + super(context); + this.currentAccount = currentAccount; + currentUser = user; + currentChat = chat; + forwardingMessagesParams = params; + + chatPreviewContainer = new SizeNotifierFrameLayout(context); + chatPreviewContainer.setBackgroundImage(Theme.getCachedWallpaper(), Theme.isWallpaperMotion()); + chatPreviewContainer.setOccupyStatusBar(false); + + if (Build.VERSION.SDK_INT >= 21) { + chatPreviewContainer.setOutlineProvider(new ViewOutlineProvider() { + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, (int) (currentTopOffset + 1), view.getMeasuredWidth(), view.getMeasuredHeight(), AndroidUtilities.dp(6)); + } + }); + chatPreviewContainer.setClipToOutline(true); + chatPreviewContainer.setElevation(AndroidUtilities.dp(4)); + } + + actionBar = new ActionBar(context); + actionBar.setBackgroundColor(Theme.getColor(Theme.key_actionBarDefault)); + actionBar.setOccupyStatusBar(false); + + chatListView = new RecyclerListView(context) { + + @Override + public boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (child instanceof ChatMessageCell) { + ChatMessageCell cell = (ChatMessageCell) child; + boolean r = super.drawChild(canvas, child, drawingTime); + cell.drawCheckBox(canvas); + canvas.save(); + canvas.translate(cell.getX(), cell.getY()); + cell.drawMessageText(canvas, cell.getMessageObject().textLayoutBlocks, true, 1f, false); + + if (cell.getCurrentMessagesGroup() != null || cell.getTransitionParams().animateBackgroundBoundsInner) { + cell.drawNamesLayout(canvas, 1f); + } + if ((cell.getCurrentPosition() != null && cell.getCurrentPosition().last) || cell.getTransitionParams().animateBackgroundBoundsInner) { + cell.drawTime(canvas, 1f, true); + } + if ((cell.getCurrentPosition() != null && cell.getCurrentPosition().last) || cell.getCurrentPosition() == null) { + cell.drawCaptionLayout(canvas, false, 1f); + } + cell.getTransitionParams().recordDrawingStatePreview(); + canvas.restore(); + return r; + } + return true; + } + + @Override + protected void dispatchDraw(Canvas canvas) { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof ChatMessageCell) { + ChatMessageCell cell = (ChatMessageCell) child; + cell.setParentViewSize(chatPreviewContainer.getMeasuredWidth(), chatPreviewContainer.getBackgroundSizeY()); + } + } + drawChatBackgroundElements(canvas); + super.dispatchDraw(canvas); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + updatePositions(); + } + + private void drawChatBackgroundElements(Canvas canvas) { + int count = getChildCount(); + MessageObject.GroupedMessages lastDrawnGroup = null; + + for (int a = 0; a < count; a++) { + View child = getChildAt(a); + if (child instanceof ChatMessageCell) { + ChatMessageCell cell = (ChatMessageCell) child; + MessageObject.GroupedMessages group = cell.getCurrentMessagesGroup(); + if (group != null && group == lastDrawnGroup) { + continue; + } + lastDrawnGroup = group; + MessageObject.GroupedMessagePosition position = cell.getCurrentPosition(); + MessageBackgroundDrawable backgroundDrawable = cell.getBackgroundDrawable(); + } + } + MessageObject.GroupedMessages scrimGroup = null; + for (int k = 0; k < 3; k++) { + drawingGroups.clear(); + if (k == 2 && !chatListView.isFastScrollAnimationRunning()) { + continue; + } + for (int i = 0; i < count; i++) { + View child = chatListView.getChildAt(i); + if (child instanceof ChatMessageCell) { + ChatMessageCell cell = (ChatMessageCell) child; + if (child.getY() > chatListView.getHeight() || child.getY() + child.getHeight() < 0) { + continue; + } + MessageObject.GroupedMessages group = cell.getCurrentMessagesGroup(); + if (group == null || (k == 0 && group.messages.size() == 1) || (k == 1 && !group.transitionParams.drawBackgroundForDeletedItems)) { + continue; + } + if ((k == 0 && cell.getMessageObject().deleted) || (k == 1 && !cell.getMessageObject().deleted)) { + continue; + } + if ((k == 2 && !cell.willRemovedAfterAnimation()) || (k != 2 && cell.willRemovedAfterAnimation())) { + continue; + } + + if (!drawingGroups.contains(group)) { + group.transitionParams.left = 0; + group.transitionParams.top = 0; + group.transitionParams.right = 0; + group.transitionParams.bottom = 0; + + group.transitionParams.pinnedBotton = false; + group.transitionParams.pinnedTop = false; + group.transitionParams.cell = cell; + drawingGroups.add(group); + } + + group.transitionParams.pinnedTop = cell.isPinnedTop(); + group.transitionParams.pinnedBotton = cell.isPinnedBottom(); + + int left = (cell.getLeft() + cell.getBackgroundDrawableLeft()); + int right = (cell.getLeft() + cell.getBackgroundDrawableRight()); + int top = (cell.getTop() + cell.getBackgroundDrawableTop()); + int bottom = (cell.getTop() + cell.getBackgroundDrawableBottom()); + + if ((cell.getCurrentPosition().flags & MessageObject.POSITION_FLAG_TOP) == 0) { + top -= AndroidUtilities.dp(10); + } + + if ((cell.getCurrentPosition().flags & MessageObject.POSITION_FLAG_BOTTOM) == 0) { + bottom += AndroidUtilities.dp(10); + } + + if (cell.willRemovedAfterAnimation()) { + group.transitionParams.cell = cell; + } + + if (group.transitionParams.top == 0 || top < group.transitionParams.top) { + group.transitionParams.top = top; + } + if (group.transitionParams.bottom == 0 || bottom > group.transitionParams.bottom) { + group.transitionParams.bottom = bottom; + } + if (group.transitionParams.left == 0 || left < group.transitionParams.left) { + group.transitionParams.left = left; + } + if (group.transitionParams.right == 0 || right > group.transitionParams.right) { + group.transitionParams.right = right; + } + } + } + + for (int i = 0; i < drawingGroups.size(); i++) { + MessageObject.GroupedMessages group = drawingGroups.get(i); + if (group == scrimGroup) { + continue; + } + float x = group.transitionParams.cell.getNonAnimationTranslationX(true); + float l = (group.transitionParams.left + x + group.transitionParams.offsetLeft); + float t = (group.transitionParams.top + group.transitionParams.offsetTop); + float r = (group.transitionParams.right + x + group.transitionParams.offsetRight); + float b = (group.transitionParams.bottom + group.transitionParams.offsetBottom); + + if (!group.transitionParams.backgroundChangeBounds) { + t += group.transitionParams.cell.getTranslationY(); + b += group.transitionParams.cell.getTranslationY(); + } + + if (t < -AndroidUtilities.dp(20)) { + t = -AndroidUtilities.dp(20); + } + + if (b > chatListView.getMeasuredHeight() + AndroidUtilities.dp(20)) { + b = chatListView.getMeasuredHeight() + AndroidUtilities.dp(20); + } + + boolean useScale = group.transitionParams.cell.getScaleX() != 1f || group.transitionParams.cell.getScaleY() != 1f; + if (useScale) { + canvas.save(); + canvas.scale(group.transitionParams.cell.getScaleX(), group.transitionParams.cell.getScaleY(), l + (r - l) / 2, t + (b - t) / 2); + } + + group.transitionParams.cell.drawBackground(canvas, (int) l, (int) t, (int) r, (int) b, group.transitionParams.pinnedTop, group.transitionParams.pinnedBotton, false, 0); + group.transitionParams.cell = null; + group.transitionParams.drawCaptionLayout = group.hasCaption; + if (useScale) { + canvas.restore(); + for (int ii = 0; ii < count; ii++) { + View child = chatListView.getChildAt(ii); + if (child instanceof ChatMessageCell && ((ChatMessageCell) child).getCurrentMessagesGroup() == group) { + ChatMessageCell cell = ((ChatMessageCell) child); + int left = cell.getLeft(); + int top = cell.getTop(); + child.setPivotX(l - left + (r - l) / 2); + child.setPivotY(t - top + (b - t) / 2); + } + } + } + } + } + } + }; + chatListView.setItemAnimator(itemAnimator = new ChatListItemAnimator(null, chatListView) { + + int scrollAnimationIndex = -1; + + @Override + public void onAnimationStart() { + super.onAnimationStart(); + AndroidUtilities.cancelRunOnUIThread(changeBoundsRunnable); + changeBoundsRunnable.run(); + + if (scrollAnimationIndex == -1) { + scrollAnimationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(scrollAnimationIndex, null, false); + } + if (finishRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(finishRunnable); + finishRunnable = null; + } + } + + + Runnable finishRunnable; + + @Override + protected void onAllAnimationsDone() { + super.onAllAnimationsDone(); + if (finishRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(finishRunnable); + } + AndroidUtilities.runOnUIThread(finishRunnable = () -> { + if (scrollAnimationIndex != -1) { + NotificationCenter.getInstance(currentAccount).onAnimationFinish(scrollAnimationIndex); + scrollAnimationIndex = -1; + } + }); + + if (updateAfterAnimations) { + updateAfterAnimations = false; + AndroidUtilities.runOnUIThread(() -> updateMessages()); + } + } + + @Override + public void endAnimations() { + super.endAnimations(); + if (finishRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(finishRunnable); + } + AndroidUtilities.runOnUIThread(finishRunnable = () -> { + if (scrollAnimationIndex != -1) { + NotificationCenter.getInstance(currentAccount).onAnimationFinish(scrollAnimationIndex); + scrollAnimationIndex = -1; + } + }); + } + }); + chatListView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + + for (int i = 0; i < chatListView.getChildCount(); i++) { + ChatMessageCell cell = (ChatMessageCell) chatListView.getChildAt(i); + cell.setParentViewSize(chatPreviewContainer.getMeasuredWidth(), chatPreviewContainer.getBackgroundSizeY()); + } + } + }); + chatListView.setOnItemClickListener(new RecyclerListView.OnItemClickListener() { + @Override + public void onItemClick(View view, int position) { + if (forwardingMessagesParams.previewMessages.size() <= 1) { + return; + } + int id = params.previewMessages.get(position).getId(); + boolean newSelected = !params.selectedIds.get(id, false); + if (forwardingMessagesParams.selectedIds.size() == 1 && !newSelected) { + return; + } + if (!newSelected) { + params.selectedIds.delete(id); + } else { + params.selectedIds.put(id, newSelected); + } + ChatMessageCell chatMessageCell = (ChatMessageCell) view; + chatMessageCell.setChecked(newSelected, newSelected, true); + actionBar.setTitle(LocaleController.formatPluralString("PreviewForwardMessagesCount", params.selectedIds.size())); + } + }); + + chatListView.setAdapter(adapter = new Adapter()); + chatListView.setPadding(0, AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4)); + chatLayoutManager = new GridLayoutManagerFixed(context, 1000, LinearLayoutManager.VERTICAL, true) { + + @Override + public boolean shouldLayoutChildFromOpositeSide(View child) { + return false; + } + + @Override + protected boolean hasSiblingChild(int position) { + MessageObject message = params.previewMessages.get(position); + MessageObject.GroupedMessages group = getValidGroupedMessage(message); + if (group != null) { + MessageObject.GroupedMessagePosition pos = group.positions.get(message); + if (pos.minX == pos.maxX || pos.minY != pos.maxY || pos.minY == 0) { + return false; + } + int count = group.posArray.size(); + for (int a = 0; a < count; a++) { + MessageObject.GroupedMessagePosition p = group.posArray.get(a); + if (p == pos) { + continue; + } + if (p.minY <= pos.minY && p.maxY >= pos.minY) { + return true; + } + } + } + return false; + } + + @Override + public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { + if (BuildVars.DEBUG_PRIVATE_VERSION) { + super.onLayoutChildren(recycler, state); + } else { + try { + super.onLayoutChildren(recycler, state); + } catch (Exception e) { + FileLog.e(e); + AndroidUtilities.runOnUIThread(() -> adapter.notifyDataSetChanged()); + } + } + } + }; + chatLayoutManager.setSpanSizeLookup(new GridLayoutManagerFixed.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + int idx = position; + if (idx >= 0 && idx < params.previewMessages.size()) { + MessageObject message = params.previewMessages.get(idx); + MessageObject.GroupedMessages groupedMessages = getValidGroupedMessage(message); + if (groupedMessages != null) { + return groupedMessages.positions.get(message).spanSize; + } + } + return 1000; + } + }); + chatListView.setClipToPadding(false); + chatListView.setLayoutManager(chatLayoutManager); + chatListView.addItemDecoration(new RecyclerView.ItemDecoration() { + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + outRect.bottom = 0; + if (view instanceof ChatMessageCell) { + ChatMessageCell cell = (ChatMessageCell) view; + MessageObject.GroupedMessages group = cell.getCurrentMessagesGroup(); + if (group != null) { + MessageObject.GroupedMessagePosition position = cell.getCurrentPosition(); + if (position != null && position.siblingHeights != null) { + float maxHeight = Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f; + int h = cell.getExtraInsetHeight(); + for (int a = 0; a < position.siblingHeights.length; a++) { + h += (int) Math.ceil(maxHeight * position.siblingHeights[a]); + } + h += (position.maxY - position.minY) * Math.round(7 * AndroidUtilities.density); + int count = group.posArray.size(); + for (int a = 0; a < count; a++) { + MessageObject.GroupedMessagePosition pos = group.posArray.get(a); + if (pos.minY != position.minY || pos.minX == position.minX && pos.maxX == position.maxX && pos.minY == position.minY && pos.maxY == position.maxY) { + continue; + } + if (pos.minY == position.minY) { + h -= (int) Math.ceil(maxHeight * pos.ph) - AndroidUtilities.dp(4); + break; + } + } + outRect.bottom = -h; + } + } + } + } + }); + + chatPreviewContainer.addView(chatListView); + addView(chatPreviewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 400, 0, 8, 0, 8, 0)); + chatPreviewContainer.addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + menuScrollView = new ScrollView(context); + addView(menuScrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + + menuContainer = new LinearLayout(context); + menuContainer.setOrientation(LinearLayout.VERTICAL); + menuScrollView.addView(menuContainer); + + buttonsLayout = new LinearLayout(context); + buttonsLayout.setOrientation(LinearLayout.VERTICAL); + Drawable shadowDrawable = getContext().getResources().getDrawable(R.drawable.popup_fixed_alert).mutate(); + shadowDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogBackground), PorterDuff.Mode.MULTIPLY)); + buttonsLayout.setBackground(shadowDrawable); + menuContainer.addView(buttonsLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + showSendersNameView = new ActionBarMenuSubItem(context, true, true, false); + buttonsLayout.addView(showSendersNameView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); + showSendersNameView.setTextAndIcon(forwardingMessagesParams.multiplyUsers ? LocaleController.getString("ShowSenderNames", R.string.ShowSenderNames) : LocaleController.getString("ShowSendersName", R.string.ShowSendersName), 0); + showSendersNameView.setChecked(true); + + hideSendersNameView = new ActionBarMenuSubItem(context, true, false, !params.hasCaption); + buttonsLayout.addView(hideSendersNameView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); + hideSendersNameView.setTextAndIcon(forwardingMessagesParams.multiplyUsers ? LocaleController.getString("HideSenderNames", R.string.HideSenderNames) : LocaleController.getString("HideSendersName", R.string.HideSendersName), 0); + hideSendersNameView.setChecked(false); + + if (forwardingMessagesParams.hasCaption) { + View dividerView = new View(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(2, MeasureSpec.EXACTLY)); + } + }; + dividerView.setBackgroundColor(Theme.getColor(Theme.key_divider)); + buttonsLayout.addView(dividerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + + showCaptionView = new ActionBarMenuSubItem(context, true, false, false); + buttonsLayout.addView(showCaptionView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); + showCaptionView.setTextAndIcon(LocaleController.getString("ShowCaption", R.string.ShowCaption), 0); + showCaptionView.setChecked(true); + + hideCaptionView = new ActionBarMenuSubItem(context, true, false, true); + buttonsLayout.addView(hideCaptionView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); + hideCaptionView.setTextAndIcon(LocaleController.getString("HideCaption", R.string.HideCaption), 0); + hideCaptionView.setChecked(false); + } + + buttonsLayout2 = new LinearLayout(context); + buttonsLayout2.setOrientation(LinearLayout.VERTICAL); + shadowDrawable = getContext().getResources().getDrawable(R.drawable.popup_fixed_alert).mutate(); + shadowDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogBackground), PorterDuff.Mode.MULTIPLY)); + buttonsLayout2.setBackground(shadowDrawable); + menuContainer.addView(buttonsLayout2, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, forwardingMessagesParams.hasSenders ? -8 : 0, 0, 0)); + + changeRecipientView = new ActionBarMenuSubItem(context, true, false); + buttonsLayout2.addView(changeRecipientView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); + changeRecipientView.setTextAndIcon(LocaleController.getString("ChangeRecipient", R.string.ChangeRecipient), R.drawable.msg_forward_replace); + + sendMessagesView = new ActionBarMenuSubItem(context, false, true); + buttonsLayout2.addView(sendMessagesView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); + sendMessagesView.setTextAndIcon(LocaleController.getString("ForwardSendMessages", R.string.ForwardSendMessages), R.drawable.msg_forward_send); + + if (forwardingMessagesParams.hasSenders) { + actionItems.add(showSendersNameView); + actionItems.add(hideSendersNameView); + + if (params.hasCaption) { + actionItems.add(showCaptionView); + actionItems.add(hideCaptionView); + } + } + + actionItems.add(changeRecipientView); + actionItems.add(sendMessagesView); + + showSendersNameView.setOnClickListener(view -> { + if (params.hideForwardSendersName) { + returnSendersNames = false; + showSendersNameView.setChecked(true); + hideSendersNameView.setChecked(false); + if (showCaptionView != null) { + showCaptionView.setChecked(true); + hideCaptionView.setChecked(false); + } + params.hideForwardSendersName = false; + params.hideCaption = false; + updateMessages(); + updateSubtitle(); + } + }); + + hideSendersNameView.setOnClickListener(view -> { + if (!params.hideForwardSendersName) { + returnSendersNames = false; + showSendersNameView.setChecked(false); + hideSendersNameView.setChecked(true); + params.hideForwardSendersName = true; + updateMessages(); + updateSubtitle(); + } + }); + + if (params.hasCaption) { + showCaptionView.setOnClickListener(view -> { + if (params.hideCaption) { + if (returnSendersNames) { + params.hideForwardSendersName = false; + } + returnSendersNames = false; + showCaptionView.setChecked(true); + hideCaptionView.setChecked(false); + showSendersNameView.setChecked(!params.hideForwardSendersName); + hideSendersNameView.setChecked(params.hideForwardSendersName); + params.hideCaption = false; + updateMessages(); + updateSubtitle(); + } + }); + + hideCaptionView.setOnClickListener(view -> { + if (!params.hideCaption) { + showCaptionView.setChecked(false); + hideCaptionView.setChecked(true); + showSendersNameView.setChecked(false); + hideSendersNameView.setChecked(true); + if (!params.hideForwardSendersName) { + params.hideForwardSendersName = true; + returnSendersNames = true; + } + params.hideCaption = true; + updateMessages(); + updateSubtitle(); + } + }); + } + + showSendersNameView.setChecked(!params.hideForwardSendersName); + hideSendersNameView.setChecked(params.hideForwardSendersName); + + if (params.hasCaption) { + showCaptionView.setChecked(!params.hideCaption); + hideCaptionView.setChecked(params.hideCaption); + } + + if (!params.hasSenders) { + buttonsLayout.setVisibility(View.GONE); + } + + sendMessagesView.setOnClickListener(View -> didSendPressed()); + changeRecipientView.setOnClickListener(view -> selectAnotherChat()); + + updateMessages(); + updateSubtitle(); + actionBar.setTitle(LocaleController.formatPluralString("PreviewForwardMessagesCount", params.selectedIds.size())); + + menuScrollView.setOnTouchListener((view, motionEvent) -> { + if (motionEvent.getAction() == MotionEvent.ACTION_UP) { + dismiss(true); + } + return true; + }); + setOnTouchListener((view, motionEvent) -> { + if (motionEvent.getAction() == MotionEvent.ACTION_UP) { + dismiss(true); + } + return true; + }); + showing = true; + setAlpha(0); + setScaleX(0.95f); + setScaleY(0.95f); + animate().alpha(1f).scaleX(1f).setDuration(ChatListItemAnimator.DEFAULT_DURATION).setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR).scaleY(1f); + + updateColors(); + } + + private void updateSubtitle() { + if (!forwardingMessagesParams.hasSenders) { + if (forwardingMessagesParams.willSeeSenders) { + if (currentUser != null) { + actionBar.setSubtitle(LocaleController.formatString("ForwardPreviewSendersNameVisible", R.string.ForwardPreviewSendersNameVisible, ContactsController.formatName(currentUser.first_name, currentUser.last_name))); + } else { + if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) { + actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameVisibleChannel", R.string.ForwardPreviewSendersNameVisibleChannel)); + } else { + actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameVisibleGroup", R.string.ForwardPreviewSendersNameVisibleGroup)); + } + } + } else { + if (currentUser != null) { + actionBar.setSubtitle(LocaleController.formatString("ForwardPreviewSendersNameVisible", R.string.ForwardPreviewSendersNameVisible, ContactsController.formatName(currentUser.first_name, currentUser.last_name))); + } else { + if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) { + actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameHiddenChannel", R.string.ForwardPreviewSendersNameHiddenChannel)); + } else { + actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameHiddenGroup", R.string.ForwardPreviewSendersNameHiddenGroup)); + } + } + } + } else if (!forwardingMessagesParams.hideForwardSendersName) { + if (currentUser != null) { + actionBar.setSubtitle(LocaleController.formatString("ForwardPreviewSendersNameVisible", R.string.ForwardPreviewSendersNameVisible, ContactsController.formatName(currentUser.first_name, currentUser.last_name))); + } else { + if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) { + actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameVisibleChannel", R.string.ForwardPreviewSendersNameVisibleChannel)); + } else { + actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameVisibleGroup", R.string.ForwardPreviewSendersNameVisibleGroup)); + } + } + } else { + if (currentUser != null) { + actionBar.setSubtitle(LocaleController.formatString("ForwardPreviewSendersNameHidden", R.string.ForwardPreviewSendersNameHidden, ContactsController.formatName(currentUser.first_name, currentUser.last_name))); + } else { + if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) { + actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameHiddenChannel", R.string.ForwardPreviewSendersNameHiddenChannel)); + } else { + actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameHiddenGroup", R.string.ForwardPreviewSendersNameHiddenGroup)); + } + } + } + } + + private void updateColors() { + + } + + public void dismiss(boolean canShowKeyboard) { + if (showing) { + showing = false; + animate().alpha(0).scaleX(0.95f).scaleY(0.95f).setDuration(ChatListItemAnimator.DEFAULT_DURATION).setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (getParent() != null) { + ViewGroup parent = (ViewGroup) getParent(); + parent.removeView(ForwardingPreviewView.this); + } + } + }); + onDismiss(canShowKeyboard); + } + } + + protected void onDismiss(boolean canShowKeyboard) { + + } + + boolean updateAfterAnimations; + + private void updateMessages() { + if (itemAnimator.isRunning()) { + updateAfterAnimations = true; + return; + } + for (int i = 0; i < forwardingMessagesParams.previewMessages.size(); i++) { + MessageObject messageObject = forwardingMessagesParams.previewMessages.get(i); + messageObject.forceUpdate = true; + if (!forwardingMessagesParams.hideForwardSendersName) { + messageObject.messageOwner.flags |= TLRPC.MESSAGE_FLAG_FWD; + } else { + messageObject.messageOwner.flags &= ~TLRPC.MESSAGE_FLAG_FWD; + } + if (forwardingMessagesParams.hideCaption) { + messageObject.caption = null; + } else { + messageObject.generateCaption(); + } + + if (messageObject.isPoll()) { + ForwardingMessagesParams.PreviewMediaPoll mediaPoll = (ForwardingMessagesParams.PreviewMediaPoll) messageObject.messageOwner.media; + mediaPoll.results.total_voters = forwardingMessagesParams.hideCaption ? 0 : mediaPoll.totalVotersCached; + } + } + for (int i = 0; i < forwardingMessagesParams.pollChoosenAnswers.size(); i++) { + forwardingMessagesParams.pollChoosenAnswers.get(i).chosen = !forwardingMessagesParams.hideForwardSendersName; + } + for (int i = 0; i < forwardingMessagesParams.groupedMessagesMap.size(); i++) { + itemAnimator.groupWillChanged(forwardingMessagesParams.groupedMessagesMap.valueAt(i)); + } + adapter.notifyItemRangeChanged(0, forwardingMessagesParams.previewMessages.size()); + } + + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int maxActionWidth = 0; + isLandscapeMode = MeasureSpec.getSize(widthMeasureSpec) > MeasureSpec.getSize(heightMeasureSpec); + + int width = MeasureSpec.getSize(widthMeasureSpec); + if (isLandscapeMode) { + width = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.38f); + } + for (int i = 0; i < actionItems.size(); i++) { + actionItems.get(i).measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED)); + if (actionItems.get(i).getMeasuredWidth() > maxActionWidth) { + maxActionWidth = actionItems.get(i).getMeasuredWidth(); + } + } + buttonsLayout.getBackground().getPadding(rect); + int buttonsWidth = maxActionWidth + rect.left + rect.right; + buttonsLayout.getLayoutParams().width = buttonsWidth; + buttonsLayout2.getLayoutParams().width = buttonsWidth; + + buttonsLayout.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED)); + buttonsLayout2.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED)); + + ((MarginLayoutParams) chatListView.getLayoutParams()).topMargin = ActionBar.getCurrentActionBarHeight(); + if (isLandscapeMode) { + chatPreviewContainer.getLayoutParams().height = LayoutHelper.MATCH_PARENT; + ((MarginLayoutParams) chatPreviewContainer.getLayoutParams()).topMargin = AndroidUtilities.dp(8); + ((MarginLayoutParams) chatPreviewContainer.getLayoutParams()).bottomMargin = AndroidUtilities.dp(8); + chatPreviewContainer.getLayoutParams().width = (int) Math.min(MeasureSpec.getSize(widthMeasureSpec), Math.max(AndroidUtilities.dp(340), MeasureSpec.getSize(widthMeasureSpec) * 0.6f)); + menuScrollView.getLayoutParams().height = LayoutHelper.MATCH_PARENT; + } else { + ((MarginLayoutParams) chatPreviewContainer.getLayoutParams()).topMargin = 0; + ((MarginLayoutParams) chatPreviewContainer.getLayoutParams()).bottomMargin = 0; + chatPreviewContainer.getLayoutParams().height = MeasureSpec.getSize(heightMeasureSpec) - AndroidUtilities.dp(16 - 10) - buttonsLayout.getMeasuredHeight() - buttonsLayout2.getMeasuredHeight(); + if (chatPreviewContainer.getLayoutParams().height < MeasureSpec.getSize(heightMeasureSpec) * 0.5f) { + chatPreviewContainer.getLayoutParams().height = (int) (MeasureSpec.getSize(heightMeasureSpec) * 0.5f); + } + chatPreviewContainer.getLayoutParams().width = LayoutHelper.MATCH_PARENT; + menuScrollView.getLayoutParams().height = MeasureSpec.getSize(heightMeasureSpec) - chatPreviewContainer.getLayoutParams().height; + } + + int size = MeasureSpec.getSize(widthMeasureSpec) + MeasureSpec.getSize(heightMeasureSpec) << 16; + if (lastSize != size) { + for (int i = 0; i < forwardingMessagesParams.previewMessages.size(); i++) { + if (isLandscapeMode) { + forwardingMessagesParams.previewMessages.get(i).parentWidth = chatPreviewContainer.getLayoutParams().width; + } else { + forwardingMessagesParams.previewMessages.get(i).parentWidth = MeasureSpec.getSize(widthMeasureSpec) - AndroidUtilities.dp(16); + } + forwardingMessagesParams.previewMessages.get(i).resetLayout(); + forwardingMessagesParams.previewMessages.get(i).forceUpdate = true; + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + } + firstLayout = true; + } + lastSize = size; + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + int lastSize; + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + updatePositions(); + firstLayout = false; + } + + + private void updatePositions() { + int lastTopOffset = chatTopOffset; + float lastYOffset = yOffset; + + if (!isLandscapeMode) { + if (chatListView.getChildCount() == 0 || chatListView.getChildCount() > forwardingMessagesParams.previewMessages.size()) { + chatTopOffset = 0; + } else { + int minTop = chatListView.getChildAt(0).getTop(); + for (int i = 1; i < chatListView.getChildCount(); i++) { + if (chatListView.getChildAt(i).getTop() < minTop) { + minTop = chatListView.getChildAt(i).getTop(); + } + } + minTop -= AndroidUtilities.dp(4); + if (minTop < 0) { + chatTopOffset = 0; + } else { + chatTopOffset = minTop; + } + } + + float totalViewsHeight = buttonsLayout.getMeasuredHeight() + buttonsLayout2.getMeasuredHeight() - AndroidUtilities.dp(8) + (chatPreviewContainer.getMeasuredHeight() - chatTopOffset); + float totalHeight = getMeasuredHeight() - AndroidUtilities.dp(16); + yOffset = AndroidUtilities.dp(8) + (totalHeight - totalViewsHeight) / 2 - chatTopOffset; + if (yOffset > AndroidUtilities.dp(8)) { + yOffset = AndroidUtilities.dp(8); + } + float buttonX = getMeasuredWidth() - menuScrollView.getMeasuredWidth(); + menuScrollView.setTranslationX(buttonX); + } else { + yOffset = 0; + chatTopOffset = 0; + menuScrollView.setTranslationX(chatListView.getMeasuredWidth() + AndroidUtilities.dp(8)); + } + + if (!firstLayout && (chatTopOffset != lastTopOffset || yOffset != lastYOffset)) { + if (offsetsAnimator != null) { + offsetsAnimator.cancel(); + } + offsetsAnimator = ValueAnimator.ofFloat(0, 1f); + offsetsAnimator.addUpdateListener(valueAnimator -> { + float p = (float) valueAnimator.getAnimatedValue(); + currentTopOffset = (int) (lastTopOffset * (1f - p) + chatTopOffset * p); + currentYOffset = lastYOffset * (1f - p) + yOffset * p; + setOffset(currentYOffset, currentTopOffset); + }); + offsetsAnimator.setDuration(ChatListItemAnimator.DEFAULT_DURATION); + offsetsAnimator.setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR); + offsetsAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + offsetsAnimator = null; + setOffset(yOffset, chatTopOffset); + } + }); + + AndroidUtilities.runOnUIThread(changeBoundsRunnable, 50); + + currentTopOffset = lastTopOffset; + currentYOffset = lastYOffset; + setOffset(lastYOffset, lastTopOffset); + } else if (firstLayout) { + setOffset(currentYOffset = yOffset, currentTopOffset = chatTopOffset); + } + } + + private void setOffset(float yOffset, int chatTopOffset) { + if (isLandscapeMode) { + actionBar.setTranslationY(0); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + chatPreviewContainer.invalidateOutline(); + } + chatPreviewContainer.setTranslationY(0); + menuScrollView.setTranslationY(0); + } else { + actionBar.setTranslationY(chatTopOffset); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + chatPreviewContainer.invalidateOutline(); + } + chatPreviewContainer.setTranslationY(yOffset); + menuScrollView.setTranslationY(yOffset + chatPreviewContainer.getMeasuredHeight() - AndroidUtilities.dp(2)); + } + } + + public boolean isShowing() { + return showing; + } + + private class Adapter extends RecyclerView.Adapter { + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + ChatMessageCell chatMessageCell = new ChatMessageCell(parent.getContext()); + return new RecyclerListView.Holder(chatMessageCell); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + ChatMessageCell cell = (ChatMessageCell) holder.itemView; + cell.setParentViewSize(chatListView.getMeasuredWidth(), chatListView.getMeasuredHeight()); + int id = cell.getMessageObject() != null ? cell.getMessageObject().getId() : 0; + cell.setMessageObject(forwardingMessagesParams.previewMessages.get(position), forwardingMessagesParams.groupedMessagesMap.get(forwardingMessagesParams.previewMessages.get(position).getGroupId()), true, true); + cell.setDelegate(new ChatMessageCell.ChatMessageCellDelegate() { + + }); + + if (forwardingMessagesParams.previewMessages.size() > 1) { + cell.setCheckBoxVisible(true, false); + boolean animated = id == forwardingMessagesParams.previewMessages.get(position).getId(); + boolean checked = forwardingMessagesParams.selectedIds.get(forwardingMessagesParams.previewMessages.get(position).getId(), false); + cell.setChecked(checked, checked, animated); + } + + } + + @Override + public int getItemCount() { + return forwardingMessagesParams.previewMessages.size(); + } + } + + protected void selectAnotherChat() { + + } + + protected void didSendPressed() { + + } + + private MessageObject.GroupedMessages getValidGroupedMessage(MessageObject message) { + MessageObject.GroupedMessages groupedMessages = null; + if (message.getGroupId() != 0) { + groupedMessages = forwardingMessagesParams.groupedMessagesMap.get(message.getGroupId()); + if (groupedMessages != null && (groupedMessages.messages.size() <= 1 || groupedMessages.positions.get(message) == null)) { + groupedMessages = null; + } + } + return groupedMessages; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java index 9313d9a78..ecea1f224 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/FragmentContextView.java @@ -122,7 +122,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent private StaticLayout timeLayout; private RectF rect = new RectF(); private boolean scheduleRunnableScheduled; - private Runnable updateScheduleTimeRunnable = new Runnable() { + private final Runnable updateScheduleTimeRunnable = new Runnable() { @Override public void run() { if (gradientTextPaint == null || !(fragment instanceof ChatActivity)) { @@ -1382,6 +1382,9 @@ public class FragmentContextView extends FrameLayout implements NotificationCent return; } if (visible) { + if (playbackSpeedButton != null && playbackSpeedButton.isSubMenuShowing()) { + playbackSpeedButton.toggleSubMenu(); + } visible = false; if (create) { if (getVisibility() != GONE) { @@ -1802,6 +1805,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent updateStyle(4); ChatObject.Call call = ((ChatActivity) fragment).getGroupCall(); + TLRPC.Chat chat = ((ChatActivity) fragment).getCurrentChat(); if (call.isScheduled()) { if (gradientPaint == null) { gradientTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); @@ -1818,7 +1822,11 @@ public class FragmentContextView extends FrameLayout implements NotificationCent if (!TextUtils.isEmpty(call.call.title)) { titleTextView.setText(call.call.title, false); } else { - titleTextView.setText(LocaleController.getString("VoipGroupScheduledVoiceChat", R.string.VoipGroupScheduledVoiceChat), false); + if (ChatObject.isChannelOrGiga(chat)) { + titleTextView.setText(LocaleController.getString("VoipChannelScheduledVoiceChat", R.string.VoipChannelScheduledVoiceChat), false); + } else { + titleTextView.setText(LocaleController.getString("VoipGroupScheduledVoiceChat", R.string.VoipGroupScheduledVoiceChat), false); + } } subtitleTextView.setText(LocaleController.formatStartsTime(call.call.schedule_date, 4), false); if (!scheduleRunnableScheduled) { @@ -1828,7 +1836,11 @@ public class FragmentContextView extends FrameLayout implements NotificationCent } else { timeLayout = null; joinButton.setVisibility(VISIBLE); - titleTextView.setText(LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat), false); + if (ChatObject.isChannelOrGiga(chat)) { + titleTextView.setText(LocaleController.getString("VoipChannelVoiceChat", R.string.VoipChannelVoiceChat), false); + } else { + titleTextView.setText(LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat), false); + } if (call.call.participants_count == 0) { subtitleTextView.setText(LocaleController.getString("MembersTalkingNobody", R.string.MembersTalkingNobody), false); } else { @@ -2080,7 +2092,12 @@ public class FragmentContextView extends FrameLayout implements NotificationCent titleTextView.setText(service.groupCall.call.title, false); } else { if (fragment instanceof ChatActivity && ((ChatActivity) fragment).getCurrentChat() != null && ((ChatActivity) fragment).getCurrentChat().id == service.getChat().id) { - titleTextView.setText(LocaleController.getString("VoipGroupViewVoiceChat", R.string.VoipGroupViewVoiceChat), false); + TLRPC.Chat chat = ((ChatActivity) fragment).getCurrentChat(); + if (ChatObject.isChannelOrGiga(chat)) { + titleTextView.setText(LocaleController.getString("VoipChannelViewVoiceChat", R.string.VoipChannelViewVoiceChat), false); + } else { + titleTextView.setText(LocaleController.getString("VoipGroupViewVoiceChat", R.string.VoipGroupViewVoiceChat), false); + } } else { titleTextView.setText(service.getChat().title, false); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallPipAlertView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallPipAlertView.java index 2b34f808c..fc701a00f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallPipAlertView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallPipAlertView.java @@ -22,6 +22,7 @@ import android.widget.TextView; import androidx.core.graphics.ColorUtils; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ChatObject; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.LocaleController; @@ -75,7 +76,12 @@ public class GroupCallPipAlertView extends LinearLayout implements VoIPService.S public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - info.addAction(new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, LocaleController.getString("VoipGroupOpenVoiceChat", R.string.VoipGroupOpenVoiceChat))); + VoIPService service = VoIPService.getSharedInstance(); + if (service != null && ChatObject.isChannelOrGiga(service.getChat())) { + info.addAction(new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, LocaleController.getString("VoipChannelOpenVoiceChat", R.string.VoipChannelOpenVoiceChat))); + } else { + info.addAction(new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, LocaleController.getString("VoipGroupOpenVoiceChat", R.string.VoipGroupOpenVoiceChat))); + } } } }; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallPipButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallPipButton.java index eafad93d0..c7ee4d0e6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallPipButton.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallPipButton.java @@ -407,7 +407,13 @@ public class GroupCallPipButton extends FrameLayout implements NotificationCente } wavesEnter = showWaves ? 1f : 0f; } - String contentDescription = LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat); + String contentDescription; + VoIPService voIPService = VoIPService.getSharedInstance(); + if (voIPService != null && ChatObject.isChannelOrGiga(voIPService.getChat())) { + contentDescription = LocaleController.getString("VoipChannelVoiceChat", R.string.VoipChannelVoiceChat); + } else { + contentDescription = LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat); + } if (state == MUTE_BUTTON_STATE_UNMUTE) { contentDescription += ", " + LocaleController.getString("VoipTapToMute", R.string.VoipTapToMute); } else if (state == MUTE_BUTTON_STATE_RECONNECT) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallRecordAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallRecordAlert.java new file mode 100644 index 000000000..3207d8e31 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupCallRecordAlert.java @@ -0,0 +1,346 @@ +package org.telegram.ui.Components; + +import android.app.Dialog; +import android.content.Context; +import android.database.DataSetObserver; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.LinearGradient; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Shader; +import android.graphics.drawable.GradientDrawable; +import android.os.Build; +import android.os.Parcelable; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ChatObject; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.R; +import org.telegram.messenger.SvgHelper; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.Theme; + +import androidx.core.graphics.ColorUtils; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +public class GroupCallRecordAlert extends BottomSheet { + + private ViewPager viewPager; + private TextView positiveButton; + private LinearLayout titlesLayout; + private TextView[] titles; + + private float pageOffset; + private int currentPage; + + public GroupCallRecordAlert(Context context, TLRPC.Chat chat) { + super(context, false); + + int color = Theme.getColor(Theme.key_voipgroup_inviteMembersBackground); + shadowDrawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); + + containerView = new FrameLayout(context) { + + boolean ignoreLayout; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + boolean isLandscape = View.MeasureSpec.getSize(widthMeasureSpec) > View.MeasureSpec.getSize(heightMeasureSpec); + ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) positiveButton.getLayoutParams(); + if (isLandscape) { + marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = AndroidUtilities.dp(80); + } else { + marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = AndroidUtilities.dp(16); + } + int width = MeasureSpec.getSize(widthMeasureSpec); + int padding = (width - AndroidUtilities.dp(200)) / 2; + viewPager.setPadding(padding, 0, padding, 0); + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(370), MeasureSpec.EXACTLY)); + measureChildWithMargins(titlesLayout, View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), 0, View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64), View.MeasureSpec.EXACTLY), 0); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + updateTitlesLayout(); + } + + @Override + public void requestLayout() { + if (ignoreLayout) { + return; + } + super.requestLayout(); + } + }; + containerView.setWillNotDraw(false); + containerView.setClipChildren(false); + containerView.setBackgroundDrawable(shadowDrawable); + containerView.setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0); + + TextView titleTextView = new TextView(getContext()); + if (ChatObject.isChannelOrGiga(chat)) { + titleTextView.setText(LocaleController.getString("VoipChannelRecordVoiceChat", R.string.VoipChannelRecordVoiceChat)); + } else { + titleTextView.setText(LocaleController.getString("VoipRecordVoiceChat", R.string.VoipRecordVoiceChat)); + } + titleTextView.setTextColor(0xffffffff); + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + titleTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + containerView.addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 24, 29, 24, 0)); + + TextView infoTextView = new TextView(getContext()); + infoTextView.setText(LocaleController.getString("VoipRecordVoiceChatInfo", R.string.VoipRecordVoiceChatInfo)); + infoTextView.setTextColor(0xffffffff); + infoTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + infoTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + containerView.addView(infoTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 24, 62, 24, 0)); + + titles = new TextView[3]; + + viewPager = new ViewPager(context); + viewPager.setClipChildren(false); + viewPager.setOffscreenPageLimit(4); + viewPager.setClipToPadding(false); + AndroidUtilities.setViewPagerEdgeEffectColor(viewPager, 0x7f000000); + viewPager.setAdapter(new Adapter()); + viewPager.setPageMargin(0); + containerView.addView(viewPager, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER_HORIZONTAL, 0, 100, 0, 130)); + viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + currentPage = position; + pageOffset = positionOffset; + updateTitlesLayout(); + } + + @Override + public void onPageSelected(int i) { + + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + + View leftView = new View(getContext()); + leftView.setBackground(new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, new int[]{color, 0})); + containerView.addView(leftView, LayoutHelper.createFrame(120, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 100, 0, 130)); + + View rightView = new View(getContext()); + rightView.setBackground(new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, new int[]{0, color})); + containerView.addView(rightView, LayoutHelper.createFrame(120, LayoutHelper.MATCH_PARENT, Gravity.RIGHT | Gravity.TOP, 0, 100, 0, 130)); + + positiveButton = new TextView(getContext()) { + + private Paint[] gradientPaint = new Paint[titles.length]; + { + for (int a = 0; a < gradientPaint.length; a++) { + gradientPaint[a] = new Paint(Paint.ANTI_ALIAS_FLAG); + } + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + for (int a = 0; a < gradientPaint.length; a++) { + int color1; + int color2; + int color3; + if (a == 0) { + color1 = 0xff57A4FE; + color2 = 0xff766EE9; + color3 = 0; + } else if (a == 1) { + color1 = 0xff77E55C; + color2 = 0xff56C7FE; + color3 = 0; + } else { + color1 = 0xff766EE9; + color2 = 0xffF05459; + color3 = 0xffE4A756; + } + Shader gradient; + if (color3 != 0) { + gradient = new LinearGradient(0, 0, getMeasuredWidth(), 0, new int[]{color1, color2, color3}, null, Shader.TileMode.CLAMP); + } else { + gradient = new LinearGradient(0, 0, getMeasuredWidth(), 0, new int[]{color1, color2}, null, Shader.TileMode.CLAMP); + } + gradientPaint[a].setShader(gradient); + } + } + + + @Override + protected void onDraw(Canvas canvas) { + AndroidUtilities.rectTmp.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + gradientPaint[currentPage].setAlpha(255); + canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(6), AndroidUtilities.dp(6), gradientPaint[currentPage]); + if (pageOffset > 0 && currentPage + 1 < gradientPaint.length) { + gradientPaint[currentPage + 1].setAlpha((int) (255 * pageOffset)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(6), AndroidUtilities.dp(6), gradientPaint[currentPage + 1]); + } + super.onDraw(canvas); + } + }; + positiveButton.setMinWidth(AndroidUtilities.dp(64)); + positiveButton.setTag(Dialog.BUTTON_POSITIVE); + positiveButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + positiveButton.setTextColor(Theme.getColor(Theme.key_voipgroup_nameText)); + positiveButton.setGravity(Gravity.CENTER); + positiveButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + positiveButton.setText(LocaleController.getString("VoipRecordStart", R.string.VoipRecordStart)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + positiveButton.setForeground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_voipgroup_nameText), (int) (255 * 0.3f)))); + } + positiveButton.setPadding(0, AndroidUtilities.dp(12), 0, AndroidUtilities.dp(12)); + positiveButton.setOnClickListener(view -> { + onStartRecord(currentPage); + dismiss(); + }); + + containerView.addView(positiveButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM, 0, 0, 0, 64)); + + titlesLayout = new LinearLayout(context); + containerView.addView(titlesLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 64, Gravity.BOTTOM)); + + for (int a = 0; a < titles.length; a++) { + titles[a] = new TextView(context); + titles[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + titles[a].setTextColor(0xffffffff); + titles[a].setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + titles[a].setPadding(AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10), 0); + titles[a].setGravity(Gravity.CENTER_VERTICAL); + titles[a].setSingleLine(true); + titlesLayout.addView(titles[a], LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT)); + if (a == 0) { + titles[a].setText(LocaleController.getString("VoipRecordAudio", R.string.VoipRecordAudio)); + } else if (a == 1) { + titles[a].setText(LocaleController.getString("VoipRecordPortrait", R.string.VoipRecordPortrait)); + } else { + titles[a].setText(LocaleController.getString("VoipRecordLandscape", R.string.VoipRecordLandscape)); + } + int num = a; + titles[a].setOnClickListener(view -> viewPager.setCurrentItem(num, true)); + } + } + + private void updateTitlesLayout() { + View current = titles[currentPage]; + View next = currentPage < titles.length - 1 ? titles[currentPage + 1] : null; + float cx = containerView.getMeasuredWidth() / 2; + float currentCx = current.getLeft() + current.getMeasuredWidth() / 2; + float tx = containerView.getMeasuredWidth() / 2 - currentCx; + if (next != null) { + float nextCx = next.getLeft() + next.getMeasuredWidth() / 2; + tx -= (nextCx - currentCx) * pageOffset; + } + for (int a = 0; a < titles.length; a++) { + float alpha; + float scale; + if (a < currentPage || a > currentPage + 1) { + alpha = 0.7f; + scale = 0.9f; + } else if (a == currentPage) { + alpha = 1.0f - 0.3f * pageOffset; + scale = 1.0f - 0.1f * pageOffset; + } else { + alpha = 0.7f + 0.3f * pageOffset; + scale = 0.9f + 0.1f * pageOffset; + } + titles[a].setAlpha(alpha); + titles[a].setScaleX(scale); + titles[a].setScaleY(scale); + } + titlesLayout.setTranslationX(tx); + positiveButton.invalidate(); + } + + protected void onStartRecord(int type) { + + } + + private class Adapter extends PagerAdapter { + @Override + public int getCount() { + return titles.length; + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + View view; + + ImageView imageView = new ImageView(getContext()); + imageView.setTag(position); + imageView.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); + imageView.setScaleType(ImageView.ScaleType.FIT_XY); + imageView.setLayoutParams(new ViewGroup.LayoutParams(AndroidUtilities.dp(200), ViewGroup.LayoutParams.MATCH_PARENT)); + view = imageView; + int res; + if (position == 0) { + res = R.raw.record_audio; + } else if (position == 1) { + res = R.raw.record_video_p; + } else { + res = R.raw.record_video_l; + } + String svg = RLottieDrawable.readRes(null, res); + SvgHelper.SvgDrawable drawable = SvgHelper.getDrawable(svg); + drawable.setAspectFill(false); + imageView.setImageDrawable(drawable); + if (view.getParent() != null) { + ViewGroup parent = (ViewGroup) view.getParent(); + parent.removeView(view); + } + container.addView(view, 0); + return view; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + container.removeView((View) object); + } + + @Override + public void setPrimaryItem(ViewGroup container, int position, Object object) { + super.setPrimaryItem(container, position, object); + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return view.equals(object); + } + + @Override + public void restoreState(Parcelable arg0, ClassLoader arg1) { + } + + @Override + public Parcelable saveState() { + return null; + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + if (observer != null) { + super.unregisterDataSetObserver(observer); + } + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java index 16cd09dc3..659187aaa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java @@ -74,6 +74,7 @@ public class GroupedPhotosListView extends View implements GestureDetector.OnGes void setCurrentIndex(int index); void onShowAnimationStart(); void onStopScrolling(); + boolean validGroupId(long groupId); } public GroupedPhotosListView(Context context) { @@ -122,9 +123,10 @@ public class GroupedPhotosListView extends View implements GestureDetector.OnGes } else if (imagesArr != null && !imagesArr.isEmpty()) { MessageObject messageObject = imagesArr.get(currentIndex); currentObject = messageObject; - if (messageObject.getGroupIdForUse() != currentGroupId) { + long localGroupId = delegate.validGroupId(messageObject.getGroupIdForUse()) ? messageObject.getGroupIdForUse() : 0; + if (localGroupId != currentGroupId) { changed = true; - currentGroupId = messageObject.getGroupIdForUse(); + currentGroupId = localGroupId; } if (currentGroupId != 0) { hasPhotos = true; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ImageUpdater.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ImageUpdater.java index 831332e4b..bd89f0359 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ImageUpdater.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ImageUpdater.java @@ -340,9 +340,6 @@ public class ImageUpdater implements NotificationCenter.NotificationCenterDelega return; } if (button == 8 || button == 7) { - if (button != 8) { - chatAttachAlert.dismiss(); - } HashMap photos = chatAttachAlert.getPhotoLayout().getSelectedPhotos(); ArrayList order = chatAttachAlert.getPhotoLayout().getSelectedPhotosOrder(); @@ -387,6 +384,10 @@ public class ImageUpdater implements NotificationCenter.NotificationCenterDelega } } didSelectPhotos(media); + + if (button != 8) { + chatAttachAlert.dismiss(); + } return; } else { chatAttachAlert.dismissWithButtonClick(button); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java index 9d2cbb240..e3bc56aa8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java @@ -354,7 +354,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter addView(textureOverlayView, new FrameLayout.LayoutParams(AndroidUtilities.roundPlayingMessageSize, AndroidUtilities.roundPlayingMessageSize, Gravity.CENTER)); setVisibility(INVISIBLE); - blurBehindDrawable = new BlurBehindDrawable(parentView, this); + blurBehindDrawable = new BlurBehindDrawable(parentView, this, 0); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallAlert.java index fff2f8ee6..477b24820 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallAlert.java @@ -19,7 +19,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.LinearLayout; -import android.widget.ScrollView; import android.widget.TextView; import org.telegram.messenger.AccountInstance; @@ -421,6 +420,8 @@ public class JoinCallAlert extends BottomSheet { containerView.setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0); } + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat((int) -dialogId); //TODO long + listView = new RecyclerListView(context) { @Override public void requestLayout() { @@ -464,7 +465,7 @@ public class JoinCallAlert extends BottomSheet { } } if (currentType != TYPE_CREATE) { - updateDoneButton(true); + updateDoneButton(true, chat); } }); if (type != TYPE_CREATE) { @@ -493,13 +494,21 @@ public class JoinCallAlert extends BottomSheet { textView.setSingleLine(true); textView.setEllipsize(TextUtils.TruncateAt.END); if (type == TYPE_CREATE) { - textView.setText(LocaleController.getString("StartVoipChatTitle", R.string.StartVoipChatTitle)); + if (ChatObject.isChannelOrGiga(chat)) { + textView.setText(LocaleController.getString("StartVoipChannelTitle", R.string.StartVoipChannelTitle)); + } else { + textView.setText(LocaleController.getString("StartVoipChatTitle", R.string.StartVoipChatTitle)); + } internalLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 23, 16, 23, 0)); } else { if (type == TYPE_DISPLAY) { textView.setText(LocaleController.getString("VoipGroupDisplayAs", R.string.VoipGroupDisplayAs)); } else { - textView.setText(LocaleController.getString("VoipGroupJoinAs", R.string.VoipGroupJoinAs)); + if (ChatObject.isChannelOrGiga(chat)) { + textView.setText(LocaleController.getString("VoipChannelJoinAs", R.string.VoipChannelJoinAs)); + } else { + textView.setText(LocaleController.getString("VoipGroupJoinAs", R.string.VoipGroupJoinAs)); + } } internalLayout.addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 23, 8, 23, 0)); } @@ -515,8 +524,8 @@ public class JoinCallAlert extends BottomSheet { for (int a = 0, N = chats.size(); a < N; a++) { int peerId = MessageObject.getPeerId(chats.get(a)); if (peerId < 0) { - TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-peerId); - if (!ChatObject.isChannel(chat) || chat.megagroup) { + TLRPC.Chat peerChat = MessagesController.getInstance(currentAccount).getChat(-peerId); + if (!ChatObject.isChannel(peerChat) || peerChat.megagroup) { hasGroup = true; break; } @@ -525,7 +534,6 @@ public class JoinCallAlert extends BottomSheet { messageTextView.setMovementMethod(new AndroidUtilities.LinkMovementMethodMy()); messageTextView.setLinkTextColor(Theme.getColor(Theme.key_dialogTextLink)); if (type == TYPE_CREATE) { - TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat((int) -dialogId); StringBuilder builder = new StringBuilder(); if (ChatObject.isChannel(chat) && !chat.megagroup) { builder.append(LocaleController.getString("VoipChannelStart2", R.string.VoipChannelStart2)); @@ -570,7 +578,11 @@ public class JoinCallAlert extends BottomSheet { internalLayout.addView(doneButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); BottomSheetCell scheduleButton = new BottomSheetCell(context, true); - scheduleButton.setText(LocaleController.getString("VoipGroupScheduleVoiceChat", R.string.VoipGroupScheduleVoiceChat), false); + if (ChatObject.isChannelOrGiga(chat)) { + scheduleButton.setText(LocaleController.getString("VoipChannelScheduleVoiceChat", R.string.VoipChannelScheduleVoiceChat), false); + } else { + scheduleButton.setText(LocaleController.getString("VoipGroupScheduleVoiceChat", R.string.VoipGroupScheduleVoiceChat), false); + } scheduleButton.background.setOnClickListener(v -> { selectAfterDismiss = MessagesController.getInstance(currentAccount).getInputPeer(MessageObject.getPeerId(selectedPeer)); schedule = true; @@ -580,20 +592,24 @@ public class JoinCallAlert extends BottomSheet { } else { internalLayout.addView(doneButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 50, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 0)); } - updateDoneButton(false); + updateDoneButton(false, chat); } - private void updateDoneButton(boolean animated) { + private void updateDoneButton(boolean animated, TLRPC.Chat chat) { if (currentType == TYPE_CREATE) { - doneButton.setText(LocaleController.formatString("VoipGroupStartVoiceChat", R.string.VoipGroupStartVoiceChat), animated); + if (ChatObject.isChannelOrGiga(chat)) { + doneButton.setText(LocaleController.formatString("VoipChannelStartVoiceChat", R.string.VoipChannelStartVoiceChat), animated); + } else { + doneButton.setText(LocaleController.formatString("VoipGroupStartVoiceChat", R.string.VoipGroupStartVoiceChat), animated); + } } else { int did = MessageObject.getPeerId(selectedPeer); if (did > 0) { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); doneButton.setText(LocaleController.formatString("VoipGroupContinueAs", R.string.VoipGroupContinueAs, UserObject.getFirstName(user)), animated); } else { - TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); - doneButton.setText(LocaleController.formatString("VoipGroupContinueAs", R.string.VoipGroupContinueAs, chat != null ? chat.title : ""), animated); + TLRPC.Chat peerChat = MessagesController.getInstance(currentAccount).getChat(-did); + doneButton.setText(LocaleController.formatString("VoipGroupContinueAs", R.string.VoipGroupContinueAs, peerChat != null ? peerChat.title : ""), animated); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallByUrlAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallByUrlAlert.java index 2f550761d..e8f388a61 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallByUrlAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/JoinCallByUrlAlert.java @@ -114,7 +114,11 @@ public class JoinCallByUrlAlert extends BottomSheet { BottomSheetCell clearButton = new BottomSheetCell(context); clearButton.setBackground(null); - clearButton.setText(LocaleController.getString("VoipGroupJoinVoiceChatUrl", R.string.VoipGroupJoinVoiceChatUrl)); + if (ChatObject.isChannelOrGiga(chat)) { + clearButton.setText(LocaleController.getString("VoipChannelJoinVoiceChatUrl", R.string.VoipChannelJoinVoiceChatUrl)); + } else { + clearButton.setText(LocaleController.getString("VoipGroupJoinVoiceChatUrl", R.string.VoipGroupJoinVoiceChatUrl)); + } clearButton.background.setOnClickListener(v -> { joinAfterDismiss = true; dismiss(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java index 41275ae30..b23eb9745 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActionDrawable.java @@ -10,6 +10,7 @@ import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.RectF; +import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.text.TextPaint; import android.view.animation.DecelerateInterpolator; @@ -269,15 +270,28 @@ public class MediaActionDrawable extends Drawable { } } + private void applyShaderMatrix(boolean path) { + if (messageDrawable != null && messageDrawable.hasGradient() && !hasOverlayImage) { + android.graphics.Rect bounds = getBounds(); + Shader shader = messageDrawable.getGradientShader(); + Matrix matrix = messageDrawable.getMatrix(); + matrix.reset(); + messageDrawable.applyMatrixScale(); + if (path) { + matrix.postTranslate(-bounds.centerX(), -messageDrawable.getTopY() + bounds.top); + } else { + matrix.postTranslate(0, -messageDrawable.getTopY()); + } + shader.setLocalMatrix(matrix); + } + } + @Override public void draw(Canvas canvas) { android.graphics.Rect bounds = getBounds(); if (messageDrawable != null && messageDrawable.hasGradient() && !hasOverlayImage) { - LinearGradient shader = messageDrawable.getGradientShader(); - Matrix matrix = messageDrawable.getMatrix(); - matrix.setTranslate(0, -messageDrawable.getTopY() + bounds.top); - shader.setLocalMatrix(matrix); + Shader shader = messageDrawable.getGradientShader(); paint.setShader(shader); paint2.setShader(shader); paint3.setShader(shader); @@ -312,6 +326,7 @@ public class MediaActionDrawable extends Drawable { int width = AndroidUtilities.dp(3); if (currentIcon == ICON_DOWNLOAD || nextIcon == ICON_DOWNLOAD) { + applyShaderMatrix(false); float yStart = cy - AndroidUtilities.dp(9) * scale; float yEnd = cy + AndroidUtilities.dp(9) * scale; float yStart2; @@ -433,6 +448,7 @@ public class MediaActionDrawable extends Drawable { } if (currentIcon == ICON_CANCEL || currentIcon == ICON_CANCEL_FILL || currentIcon == ICON_NONE && (nextIcon == ICON_CANCEL_FILL || nextIcon == ICON_CANCEL)) { + applyShaderMatrix(false); float d; float rotation; float iconScale = 1.0f; @@ -550,6 +566,7 @@ public class MediaActionDrawable extends Drawable { } if (alpha != 0) { + applyShaderMatrix(false); paint.setAlpha((int) (alpha * overrideAlpha)); float rad = Math.max(4, 360 * animatedDownloadProgress); int diff = AndroidUtilities.dp(isMini ? 2 : 4); @@ -598,6 +615,7 @@ public class MediaActionDrawable extends Drawable { if (currentIcon == ICON_SECRETCHECK || nextIcon == ICON_SECRETCHECK) { + applyShaderMatrix(false); paint.setAlpha(currentIcon == nextIcon ? 255 : (int) (transitionProgress * 255)); int y = cy + AndroidUtilities.dp(7); int x = cx - AndroidUtilities.dp(3); @@ -612,6 +630,7 @@ public class MediaActionDrawable extends Drawable { } } if (currentIcon == ICON_CANCEL_NOPROFRESS || nextIcon == ICON_CANCEL_NOPROFRESS) { + applyShaderMatrix(false); float transition; if (currentIcon == nextIcon) { transition = 1.0f; @@ -638,6 +657,7 @@ public class MediaActionDrawable extends Drawable { } } if (currentIcon == ICON_CANCEL_PERCENT || nextIcon == ICON_CANCEL_PERCENT) { + applyShaderMatrix(false); float transition; if (currentIcon == nextIcon) { transition = 1.0f; @@ -691,6 +711,8 @@ public class MediaActionDrawable extends Drawable { paint2.setAlpha(255); } + applyShaderMatrix(true); + canvas.save(); canvas.translate(bounds.centerX() + AndroidUtilities.dp(1) * (1.0f - p), bounds.centerY()); float ms = 500.0f * p; @@ -724,6 +746,7 @@ public class MediaActionDrawable extends Drawable { canvas.restore(); } if (currentIcon == ICON_CHECK || nextIcon == ICON_CHECK) { + applyShaderMatrix(false); float progress1; float progress2; if (currentIcon != ICON_CHECK) { @@ -777,6 +800,7 @@ public class MediaActionDrawable extends Drawable { int h = (int) (AndroidUtilities.dp(24) * previowsDrawableScale); paint2.setStyle(Paint.Style.FILL_AND_STROKE); paint2.setAlpha(currentIcon == nextIcon ? 255 : (int) ((1.0f - transitionProgress) * 255)); + applyShaderMatrix(true); canvas.save(); canvas.translate(cx - w / 2, cy - h / 2); if (previousPath[0] != null) { @@ -793,6 +817,7 @@ public class MediaActionDrawable extends Drawable { int alpha = currentIcon == nextIcon ? 255 : (int) (transitionProgress * 255); paint2.setStyle(Paint.Style.FILL_AND_STROKE); paint2.setAlpha(alpha); + applyShaderMatrix(true); canvas.save(); canvas.translate(cx - w / 2, cy - h / 2); if (nextPath[0] != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MotionBackgroundDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MotionBackgroundDrawable.java index db45718b3..70d84c936 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MotionBackgroundDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MotionBackgroundDrawable.java @@ -21,6 +21,8 @@ import android.os.SystemClock; import android.view.View; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.Utilities; import java.lang.ref.WeakReference; @@ -59,6 +61,8 @@ public class MotionBackgroundDrawable extends Drawable { private Canvas gradientCanvas; private Canvas gradientFromCanvas; + private boolean postInvalidateParent; + private Bitmap patternBitmap; private BitmapShader bitmapShader; private BitmapShader gradientShader; @@ -73,6 +77,8 @@ public class MotionBackgroundDrawable extends Drawable { private boolean rotatingPreview; + private Runnable updateAnimationRunnable = this::updateAnimation; + private android.graphics.Rect patternBounds = new android.graphics.Rect(); private int roundRadius; @@ -82,7 +88,6 @@ public class MotionBackgroundDrawable extends Drawable { init(); } - public MotionBackgroundDrawable(int c1, int c2, int c3, int c4, boolean preview) { super(); colors[0] = c1; @@ -117,6 +122,10 @@ public class MotionBackgroundDrawable extends Drawable { invalidateParent(); } + public BitmapShader getBitmapShader() { + return bitmapShader; + } + public Bitmap getBitmap() { return currentBitmap; } @@ -171,6 +180,10 @@ public class MotionBackgroundDrawable extends Drawable { return phase; } + public void setPostInvalidateParent(boolean value) { + postInvalidateParent = value; + } + public void rotatePreview(boolean back) { if (posAnimationProgress < 1.0f) { return; @@ -259,6 +272,12 @@ public class MotionBackgroundDrawable extends Drawable { if (parentView != null && parentView.get() != null) { parentView.get().invalidate(); } + if (postInvalidateParent) { + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.invalidateMotionBackground); + updateAnimation(); + AndroidUtilities.cancelRunOnUIThread(updateAnimationRunnable); + AndroidUtilities.runOnUIThread(updateAnimationRunnable, 16); + } } public boolean hasPattern() { @@ -417,12 +436,19 @@ public class MotionBackgroundDrawable extends Drawable { } canvas.restore(); + updateAnimation(); + } + + private void updateAnimation() { long newTime = SystemClock.elapsedRealtime(); long dt = newTime - lastUpdateTime; if (dt > 20) { dt = 17; } lastUpdateTime = newTime; + if (dt <= 1) { + return; + } if (posAnimationProgress < 1.0f) { float progress; @@ -496,7 +522,7 @@ public class MotionBackgroundDrawable extends Drawable { } } - if (rotatingPreview) { + if (postInvalidateParent || rotatingPreview) { Utilities.generateGradient(currentBitmap, true, phase, progress, currentBitmap.getWidth(), currentBitmap.getHeight(), currentBitmap.getRowBytes(), colors); } else { if (progress != 1f) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java index 7696a5615..56304fa20 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhotoViewerCaptionEnterView.java @@ -537,7 +537,7 @@ public class PhotoViewerCaptionEnterView extends FrameLayout implements Notifica if (emojiView != null) { return; } - emojiView = new EmojiView(false, false, getContext(), false, null); + emojiView = new EmojiView(false, false, getContext(), false, null, null); emojiView.setDelegate(new EmojiView.EmojiViewDelegate() { @Override public boolean onBackspace() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java index be1b913a7..e08cb5a76 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PopupAudioView.java @@ -127,11 +127,13 @@ public class PopupAudioView extends BaseCell implements SeekBar.SeekBarDelegate, } int h = AndroidUtilities.displaySize.y; + int w = AndroidUtilities.displaySize.x; if (getParent() instanceof View) { View view = (View) getParent(); + w = view.getMeasuredWidth(); h = view.getMeasuredHeight(); } - Theme.chat_msgInMediaDrawable.setTop((int) getY(), h, false, false); + Theme.chat_msgInMediaDrawable.setTop((int) getY(), w, h, false, false); setDrawableBounds(Theme.chat_msgInMediaDrawable, 0, 0, getMeasuredWidth(), getMeasuredHeight()); Theme.chat_msgInMediaDrawable.draw(canvas); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java index 94161a8ed..1920e92be 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java @@ -97,6 +97,7 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable { private float scaleX = 1.0f; private float scaleY = 1.0f; private boolean applyTransformation; + private boolean needScale; private final Rect dstRect = new Rect(); protected static final Handler uiHandler = new Handler(Looper.getMainLooper()); protected volatile boolean isRunning; @@ -872,6 +873,9 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable { if (getCallback() != null) { return true; } + if (parentViews.size() <= 1) { + return true; + } for (int a = 0, N = parentViews.size(); a < N; a++) { View view = parentViews.get(a).get(); if (view == null) { @@ -966,15 +970,21 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable { scaleX = (float) dstRect.width() / width; scaleY = (float) dstRect.height() / height; applyTransformation = false; + needScale = !(Math.abs(dstRect.width() - width) < AndroidUtilities.dp(1) && Math.abs(dstRect.width() - width) < AndroidUtilities.dp(1)); } - canvas.save(); - canvas.translate(dstRect.left, dstRect.top); - canvas.scale(scaleX, scaleY); - canvas.drawBitmap(renderingBitmap, 0, 0, getPaint()); + if (!needScale) { + canvas.drawBitmap(renderingBitmap, dstRect.left, dstRect.top, getPaint()); + } else { + canvas.save(); + canvas.translate(dstRect.left, dstRect.top); + canvas.scale(scaleX, scaleY); + canvas.drawBitmap(renderingBitmap, 0, 0, getPaint()); + canvas.restore(); + } + if (isRunning) { invalidateInternal(); } - canvas.restore(); } } @@ -1042,4 +1052,5 @@ public class RLottieDrawable extends BitmapDrawable implements Animatable { public void setInvalidateOnProgressSet(boolean value) { invalidateOnProgressSet = value; } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecordStatusDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecordStatusDrawable.java index 217d336b7..0beb20474 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecordStatusDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecordStatusDrawable.java @@ -74,6 +74,9 @@ public class RecordStatusDrawable extends StatusDrawable { @Override public void draw(Canvas canvas) { Paint paint = currentPaint == null ? Theme.chat_statusRecordPaint : currentPaint; + if (paint.getStrokeWidth() != AndroidUtilities.dp(2)) { + paint.setStrokeWidth(AndroidUtilities.dp(2)); + } canvas.save(); canvas.translate(0, getIntrinsicHeight() / 2 + AndroidUtilities.dp(isChat ? 1 : 2)); for (int a = 0; a < 4; a++) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java index 0a1dde04f..c84f45a1c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java @@ -2083,6 +2083,10 @@ public class RecyclerListView extends RecyclerView { } } + public boolean isMultiselect() { + return multiSelectionGesture; + } + public interface onMultiSelectionChanged { void onSelectionChanged(int position, boolean selected, float x, float y); boolean canSelect(int position); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTabStrip.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTabStrip.java index bfa8d279d..5cb2af266 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTabStrip.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrollSlidingTabStrip.java @@ -9,9 +9,11 @@ package org.telegram.ui.Components; import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.drawable.Drawable; @@ -24,9 +26,13 @@ import android.transition.TransitionManager; import android.transition.TransitionSet; import android.transition.TransitionValues; import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.TypedValue; import android.view.Gravity; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; import android.view.View; +import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.HorizontalScrollView; @@ -34,7 +40,11 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import com.google.android.exoplayer2.util.Log; + import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DocumentObject; +import org.telegram.messenger.Emoji; import org.telegram.messenger.FileLoader; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.MessageObject; @@ -44,7 +54,10 @@ import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; public class ScrollSlidingTabStrip extends HorizontalScrollView { @@ -65,6 +78,10 @@ public class ScrollSlidingTabStrip extends HorizontalScrollView { private HashMap prevTypes = new HashMap<>(); private SparseArray futureTabsPositions = new SparseArray<>(); + View draggingView; + float draggingViewOutProgress; + float draggingViewIndicatorOutProgress; + private boolean shouldExpand; private int tabCount; @@ -74,6 +91,7 @@ public class ScrollSlidingTabStrip extends HorizontalScrollView { private float startAnimationPosition; private float positionAnimationProgress; private long lastAnimationTime; + private float touchSlop; private Paint rectPaint; @@ -88,15 +106,66 @@ public class ScrollSlidingTabStrip extends HorizontalScrollView { private int tabPadding = AndroidUtilities.dp(24); private int lastScrollX = 0; + SparseArray currentPlayingImages = new SparseArray<>(); + SparseArray currentPlayingImagesTmp = new SparseArray<>(); + private boolean dragEnabled; + int startDragFromPosition; + int currentDragPosition; + float startDragFromX; + float dragDx; + float pressedX; + float pressedY; + boolean longClickRunning; + + float draggindViewXOnScreen; + float draggindViewDxOnScreen; + Runnable longClickRunnable = new Runnable() { + @Override + public void run() { + longClickRunning = false; + startDragFromX = getScrollX() + pressedX; + dragDx = 0; + + int p = (int) Math.ceil(startDragFromX / getTabSize()) - 1; + startDragFromPosition = currentDragPosition = p; + if (!canSwap(p)) { + return; + } + if (p >= 0 && p < tabsContainer.getChildCount()) { + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + draggindViewDxOnScreen = 0f; + draggingViewOutProgress = 0f; + draggingView = tabsContainer.getChildAt(p); + draggindViewXOnScreen = draggingView.getX() - getScrollX(); + draggingView.invalidate(); + tabsContainer.invalidate(); + invalidateOverlays(); + invalidate(); + } + } + }; public ScrollSlidingTabStrip(Context context) { super(context); + touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); setFillViewport(true); setWillNotDraw(false); setHorizontalScrollBarEnabled(false); - tabsContainer = new LinearLayout(context); + tabsContainer = new LinearLayout(context) { + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (child instanceof StickerTabView) { + ((StickerTabView) child).updateExpandProgress(expandProgress); + } + if (child == draggingView) { + return true; + } + return super.drawChild(canvas, child, drawingTime); + } + }; tabsContainer.setOrientation(LinearLayout.HORIZONTAL); tabsContainer.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); addView(tabsContainer); @@ -264,65 +333,245 @@ public class ScrollSlidingTabStrip extends HorizontalScrollView { return tab; } + public StickerTabView addStickerIconTab(int id, Drawable drawable) { + String key = "tab" + id; + final int position = tabCount++; + + StickerTabView tab = (StickerTabView) prevTypes.get(key); + if (tab != null) { + checkViewIndex(key, tab, position); + } else { + tab = new StickerTabView(getContext(), StickerTabView.ICON_TYPE); + tab.iconView.setImageDrawable(drawable); + tab.setFocusable(true); + tab.setOnClickListener(v -> delegate.onPageSelected((Integer) v.getTag(R.id.index_tag))); + tab.setExpanded(expanded); + tab.updateExpandProgress(expandProgress); + tabsContainer.addView(tab, position); + } + tab.isChatSticker = false; + tab.setTag(R.id.index_tag, position); + tab.setSelected(position == currentPosition); + + tabTypes.put(key, tab); + return tab; + } + public void addStickerTab(TLRPC.Chat chat) { String key = "chat" + chat.id; final int position = tabCount++; - FrameLayout tab = (FrameLayout) prevTypes.get(key); + StickerTabView tab = (StickerTabView) prevTypes.get(key); if (tab != null) { checkViewIndex(key, tab, position); } else { - tab = new FrameLayout(getContext()); + StickerTabView stickerTabView = new StickerTabView(getContext(), StickerTabView.STICKER_TYPE); + tab = stickerTabView; tab.setFocusable(true); tab.setOnClickListener(v -> delegate.onPageSelected((Integer) v.getTag(R.id.index_tag))); tabsContainer.addView(tab, position); + stickerTabView.setRoundImage(); AvatarDrawable avatarDrawable = new AvatarDrawable(); avatarDrawable.setTextSize(AndroidUtilities.dp(14)); avatarDrawable.setInfo(chat); - BackupImageView imageView = new BackupImageView(getContext()); + BackupImageView imageView = stickerTabView.imageView; imageView.setLayerNum(1); - imageView.setRoundRadius(AndroidUtilities.dp(15)); imageView.setForUserOrChat(chat, avatarDrawable); imageView.setAspectFit(true); - tab.addView(imageView, LayoutHelper.createFrame(30, 30, Gravity.CENTER)); + + stickerTabView.setExpanded(expanded); + stickerTabView.updateExpandProgress(expandProgress); + stickerTabView.textView.setText(chat.title); } + tab.isChatSticker = true; tab.setTag(R.id.index_tag, position); tab.setSelected(position == currentPosition); tabTypes.put(key, tab); } - public View addStickerTab(TLObject thumb, SvgHelper.SvgDrawable svgThumb, TLRPC.Document sticker, TLRPC.TL_messages_stickerSet parentObject) { - String key = "set" + parentObject.set.id; + public View addEmojiTab(int id, Emoji.EmojiDrawable emojiDrawable, TLRPC.Document emojiSticker) { + String key = "tab" + id; final int position = tabCount++; - - FrameLayout tab = (FrameLayout) prevTypes.get(key); + StickerTabView tab = (StickerTabView) prevTypes.get(key); if (tab != null) { checkViewIndex(key, tab, position); } else { - tab = new FrameLayout(getContext()); + tab = new StickerTabView(getContext(), StickerTabView.EMOJI_TYPE); tab.setFocusable(true); tab.setOnClickListener(v -> delegate.onPageSelected((Integer) v.getTag(R.id.index_tag))); - tabsContainer.addView(tab, position); - BackupImageView imageView = new BackupImageView(getContext()); - imageView.setLayerNum(1); - imageView.setAspectFit(true); - tab.addView(imageView, LayoutHelper.createFrame(30, 30, Gravity.CENTER)); + tab.setExpanded(expanded); + tab.updateExpandProgress(expandProgress); + + tabsContainer.addView(tab, position); } - tab.setTag(thumb); + tab.isChatSticker = false; tab.setTag(R.id.index_tag, position); - tab.setTag(R.id.parent_tag, parentObject); - tab.setTag(R.id.object_tag, sticker); - tab.setTag(R.id.svg_tag, svgThumb); + tab.setTag(R.id.parent_tag, emojiDrawable); + tab.setTag(R.id.object_tag, emojiSticker); tab.setSelected(position == currentPosition); tabTypes.put(key, tab); return tab; } + public View addStickerTab(TLObject thumb, TLRPC.Document sticker, TLRPC.TL_messages_stickerSet parentObject) { + String key = "set" + (parentObject == null ? sticker.id : parentObject.set.id); + final int position = tabCount++; + + StickerTabView tab = (StickerTabView) prevTypes.get(key); + if (tab != null) { + checkViewIndex(key, tab, position); + } else { + tab = new StickerTabView(getContext(), StickerTabView.STICKER_TYPE); + tab.setFocusable(true); + tab.setOnClickListener(v -> delegate.onPageSelected((Integer) v.getTag(R.id.index_tag))); + + tab.setExpanded(expanded); + tab.updateExpandProgress(expandProgress); + + tabsContainer.addView(tab, position); + } + tab.isChatSticker = false; + tab.setTag(thumb); + tab.setTag(R.id.index_tag, position); + tab.setTag(R.id.parent_tag, parentObject); + tab.setTag(R.id.object_tag, sticker); + tab.setSelected(position == currentPosition); + + tabTypes.put(key, tab); + return tab; + } + + boolean expanded = false; + boolean animateToExpanded; + ValueAnimator expandStickerAnimator; + float expandProgress; + + private float stickerTabExpandedWidth = AndroidUtilities.dp(86); + private float stickerTabWidth = AndroidUtilities.dp(52); + private float expandOffset; + private int scrollByOnNextMeasure = -1; + + public void expandStickers(float x, boolean expanded) { +// if (expandStickerAnimator != null || draggingView != null) { +// return; +// } + if (this.expanded != expanded) { + this.expanded = expanded; + + if (!expanded) { + fling(0); + } + + if (expandStickerAnimator != null) { + expandStickerAnimator.removeAllListeners(); + expandStickerAnimator.cancel(); + } + expandStickerAnimator = ValueAnimator.ofFloat(expandProgress, expanded ? 1f : 0f); + expandStickerAnimator.addUpdateListener(valueAnimator -> { + if (!expanded) { + float allSize = stickerTabWidth * tabsContainer.getChildCount(); + float totalXRelative = (getScrollX() + x) / (stickerTabExpandedWidth * tabsContainer.getChildCount()); + float maxXRelative = (allSize - getMeasuredWidth()) / allSize; + float additionalX = x; + if (totalXRelative > maxXRelative) { + totalXRelative = maxXRelative; + additionalX = 0; + } + float scrollToX = allSize * totalXRelative; + if (scrollToX - additionalX < 0) { + scrollToX = additionalX; + } + expandOffset = (getScrollX() + additionalX) - scrollToX; + } + expandProgress = (float) valueAnimator.getAnimatedValue(); + for (int i = 0; i < tabsContainer.getChildCount(); i++) { + tabsContainer.getChildAt(i).invalidate(); + } + tabsContainer.invalidate(); + updatePosition(); + }); + expandStickerAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + expandStickerAnimator = null; + expandProgress = expanded ? 1f : 0f; + for (int i = 0; i < tabsContainer.getChildCount(); i++) { + tabsContainer.getChildAt(i).invalidate(); + } + tabsContainer.invalidate(); + updatePosition(); + if (!expanded) { + float allSize = stickerTabWidth * tabsContainer.getChildCount(); + float totalXRelative = (getScrollX() + x) / (stickerTabExpandedWidth * tabsContainer.getChildCount()); + float maxXRelative = (allSize - getMeasuredWidth()) / allSize; + float additionalX = x; + if (totalXRelative > maxXRelative) { + totalXRelative = maxXRelative; + additionalX = 0; + } + float scrollToX = allSize * totalXRelative; + if (scrollToX - additionalX < 0) { + scrollToX = additionalX; + } + expandOffset = (getScrollX() + additionalX) - scrollToX; + scrollByOnNextMeasure = (int) (scrollToX - additionalX); + + if (scrollByOnNextMeasure < 0) { + scrollByOnNextMeasure = 0; + } + + for (int i = 0; i < tabsContainer.getChildCount(); i++) { + View child = tabsContainer.getChildAt(i); + if (child instanceof StickerTabView) { + ((StickerTabView) child).setExpanded(false); + } + child.getLayoutParams().width = AndroidUtilities.dp(52); + } + animateToExpanded = false; + getLayoutParams().height = AndroidUtilities.dp(48); + tabsContainer.requestLayout(); + } + } + }); + expandStickerAnimator.start(); + + if (expanded) { + animateToExpanded = true; + for (int i = 0; i < tabsContainer.getChildCount(); i++) { + View child = tabsContainer.getChildAt(i); + if (child instanceof StickerTabView) { + ((StickerTabView) child).setExpanded(true); + } + child.getLayoutParams().width = AndroidUtilities.dp(86); + } + + tabsContainer.requestLayout(); + getLayoutParams().height = AndroidUtilities.dp(48 + 50); + } + + if (expanded) { + float totalXRelative = (getScrollX() + x) / (stickerTabWidth * tabsContainer.getChildCount()); + float scrollToX = stickerTabExpandedWidth * tabsContainer.getChildCount() * totalXRelative; + + expandOffset = scrollToX - (getScrollX() + x); + scrollByOnNextMeasure = (int) (scrollToX - x); + } + } + } + + protected void updatePosition() { + + } + + public float getExpandedOffset() { + return animateToExpanded ? AndroidUtilities.dp(50) * expandProgress : 0; + } + public void updateTabStyles() { for (int i = 0; i < tabCount; i++) { View v = tabsContainer.getChildAt(i); @@ -358,113 +607,144 @@ public class ScrollSlidingTabStrip extends HorizontalScrollView { protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); setImages(); + if (scrollByOnNextMeasure >= 0) { + scrollTo(scrollByOnNextMeasure, 0); + scrollByOnNextMeasure = -1; + } } public void setImages() { - int tabSize = AndroidUtilities.dp(52); - int start = getScrollX() / tabSize; - int end = Math.min(tabsContainer.getChildCount(), start + (int) Math.ceil(getMeasuredWidth() / (float) tabSize) + 1); + float tabSize = AndroidUtilities.dp( 52) + AndroidUtilities.dp(34) * expandProgress; + float scrollOffset = animateToExpanded ? expandOffset * (1f - expandProgress) : 0; + int start = (int) ((getScrollX() - scrollOffset) / tabSize); + int end = Math.min(tabsContainer.getChildCount(), start + (int) Math.ceil(getMeasuredWidth() / tabSize) + 1); + if (animateToExpanded) { + start -= 2; + end += 2; + if (start < 0) { + start = 0; + } + if (end > tabsContainer.getChildCount()) { + end = tabsContainer.getChildCount(); + } + } + currentPlayingImagesTmp.clear(); + for (int i = 0; i < currentPlayingImages.size(); i++) { + currentPlayingImagesTmp.put(currentPlayingImages.valueAt(i).index, currentPlayingImages.valueAt(i)); + } + currentPlayingImages.clear(); for (int a = start; a < end; a++) { View child = tabsContainer.getChildAt(a); - Object object = child.getTag(); - Object parentObject = child.getTag(R.id.parent_tag); - SvgHelper.SvgDrawable svgThumb = (SvgHelper.SvgDrawable) child.getTag(R.id.svg_tag); - TLRPC.Document sticker = (TLRPC.Document) child.getTag(R.id.object_tag); - ImageLocation imageLocation; - - if (object instanceof TLRPC.Document) { - TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(sticker.thumbs, 90); - imageLocation = ImageLocation.getForDocument(thumb, sticker); - } else if (object instanceof TLRPC.PhotoSize) { - TLRPC.PhotoSize thumb = (TLRPC.PhotoSize) object; - int thumbVersion = 0; - if (parentObject instanceof TLRPC.TL_messages_stickerSet) { - thumbVersion = ((TLRPC.TL_messages_stickerSet) parentObject).set.thumb_version; - } - imageLocation = ImageLocation.getForSticker(thumb, sticker, thumbVersion); - } else { - continue; - } - if (imageLocation == null) { - continue; - } - BackupImageView imageView = (BackupImageView) ((FrameLayout) child).getChildAt(0); - if (object instanceof TLRPC.Document && MessageObject.isAnimatedStickerDocument(sticker, true)) { - if (svgThumb != null) { - imageView.setImage(ImageLocation.getForDocument(sticker), "30_30", svgThumb, 0, parentObject); + if (child instanceof StickerTabView) { + StickerTabView tabView = (StickerTabView) child; + if (tabView.type == StickerTabView.EMOJI_TYPE) { + Object thumb = tabView.getTag(R.id.parent_tag); + Object sticker = tabView.getTag(R.id.object_tag); + Drawable thumbDrawable = null; + if (thumb instanceof Drawable) { + thumbDrawable = (Drawable) thumb; + } + if (sticker instanceof TLRPC.Document) { + // String.format(Locale.US, "%d_%d_nr_%s" + messageObject.emojiAnimatedStickerColor, w, h, messageObject.toString()); + tabView.imageView.setImage(ImageLocation.getForDocument((TLRPC.Document) sticker), "36_36", thumbDrawable, null); + } else { + tabView.imageView.setImageDrawable(thumbDrawable); + } } else { - imageView.setImage(ImageLocation.getForDocument(sticker), "30_30", imageLocation, null, 0, parentObject); + Object object = child.getTag(); + Object parentObject = child.getTag(R.id.parent_tag); + TLRPC.Document sticker = (TLRPC.Document) child.getTag(R.id.object_tag); + ImageLocation imageLocation; + + if (object instanceof TLRPC.Document) { + TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(sticker.thumbs, 90); + if (!tabView.inited) { + tabView.svgThumb = DocumentObject.getSvgThumb((TLRPC.Document) object, Theme.key_emptyListPlaceholder, 0.2f); + } + imageLocation = ImageLocation.getForDocument(thumb, sticker); + } else if (object instanceof TLRPC.PhotoSize) { + TLRPC.PhotoSize thumb = (TLRPC.PhotoSize) object; + int thumbVersion = 0; + if (parentObject instanceof TLRPC.TL_messages_stickerSet) { + thumbVersion = ((TLRPC.TL_messages_stickerSet) parentObject).set.thumb_version; + } + imageLocation = ImageLocation.getForSticker(thumb, sticker, thumbVersion); + } else { + continue; + } + + if (imageLocation == null) { + continue; + } + tabView.inited = true; + SvgHelper.SvgDrawable svgThumb = tabView.svgThumb; + BackupImageView imageView = tabView.imageView; + if (object instanceof TLRPC.Document && MessageObject.isAnimatedStickerDocument(sticker, true)) { + if (svgThumb != null) { + imageView.setImage(ImageLocation.getForDocument(sticker), "40_40", svgThumb, 0, parentObject); + } else { + imageView.setImage(ImageLocation.getForDocument(sticker), "40_40", imageLocation, null, 0, parentObject); + } + } else if (imageLocation.imageType == FileLoader.IMAGE_TYPE_LOTTIE) { + imageView.setImage(imageLocation, "40_40", "tgs", svgThumb, parentObject); + } else { + imageView.setImage(imageLocation, null, "webp", svgThumb, parentObject); + } + String title = null; + if (parentObject instanceof TLRPC.TL_messages_stickerSet) { + title = ((TLRPC.TL_messages_stickerSet) parentObject).set.title; + } + tabView.textView.setText(title); } - } else if (imageLocation.imageType == FileLoader.IMAGE_TYPE_LOTTIE) { - imageView.setImage(imageLocation, "30_30", "tgs", svgThumb, parentObject); - } else { - imageView.setImage(imageLocation, null, "webp", svgThumb, parentObject); + currentPlayingImages.put(tabView.index, tabView); + currentPlayingImagesTmp.remove(tabView.index); } } + + for (int i = 0; i < currentPlayingImagesTmp.size(); i++) { + StickerTabView stickerTabView = currentPlayingImagesTmp.valueAt(i); + if (stickerTabView != draggingView) { + currentPlayingImagesTmp.valueAt(i).imageView.setImageDrawable(null); + } + } + } + + private int getTabSize() { + return AndroidUtilities.dp(animateToExpanded ? 86 : 52); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); - - int tabSize = AndroidUtilities.dp(52); - int oldStart = oldl / tabSize; - int newStart = l / tabSize; - - int count = (int) Math.ceil(getMeasuredWidth() / (float) tabSize) + 1; - int start = Math.max(0, Math.min(oldStart, newStart)); - int end = Math.min(tabsContainer.getChildCount(), Math.max(oldStart, newStart) + count); - - for (int a = start; a < end; a++) { - View child = tabsContainer.getChildAt(a); - if (child == null) { - continue; - } - Object object = child.getTag(); - Object parentObject = child.getTag(R.id.parent_tag); - TLRPC.Document sticker = (TLRPC.Document) child.getTag(R.id.object_tag); - ImageLocation imageLocation; - if (object instanceof TLRPC.Document) { - TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(sticker.thumbs, 90); - imageLocation = ImageLocation.getForDocument(thumb, sticker); - } else if (object instanceof TLRPC.PhotoSize) { - TLRPC.PhotoSize thumb = (TLRPC.PhotoSize) object; - int thumbVersion = 0; - if (parentObject instanceof TLRPC.TL_messages_stickerSet) { - thumbVersion = ((TLRPC.TL_messages_stickerSet) parentObject).set.thumb_version; - } - imageLocation = ImageLocation.getForSticker(thumb, sticker, thumbVersion); - } else { - continue; - } - if (imageLocation == null) { - continue; - } - BackupImageView imageView = (BackupImageView) ((FrameLayout) child).getChildAt(0); - if (a < newStart || a >= newStart + count) { - imageView.setImageDrawable(null); - } else { - if (object instanceof TLRPC.Document && MessageObject.isAnimatedStickerDocument(sticker, true)) { - imageView.setImage(ImageLocation.getForDocument(sticker), "30_30", imageLocation, null, 0, parentObject); - } else if (imageLocation.imageType == FileLoader.IMAGE_TYPE_LOTTIE) { - imageView.setImage(imageLocation, "30_30", "tgs", null, parentObject); - } else { - imageView.setImage(imageLocation, null, "webp", null, parentObject); - } - } - } + setImages(); } @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); + protected void dispatchDraw(Canvas canvas) { + float dif = (stickerTabWidth - stickerTabExpandedWidth); + float offset = expandOffset * (1f - expandProgress); + for (int i = 0; i < tabsContainer.getChildCount(); i++) { + if (tabsContainer.getChildAt(i) instanceof StickerTabView) { + StickerTabView stickerTabView = (StickerTabView) tabsContainer.getChildAt(i); + stickerTabView.animateIfPositionChanged(this); + if (animateToExpanded) { + stickerTabView.setTranslationX(dif * i * (1f - expandProgress) + offset + stickerTabView.dragOffset); + } else { + stickerTabView.setTranslationX(stickerTabView.dragOffset); + } + } + } + super.dispatchDraw(canvas); if (isInEditMode() || tabCount == 0) { return; } - final int height = getHeight(); + float height = getHeight(); + if (animateToExpanded) { + height = getHeight() - AndroidUtilities.dp(50) * (1f - expandProgress); + } if (underlineHeight > 0) { rectPaint.setColor(underlineColor); @@ -474,11 +754,14 @@ public class ScrollSlidingTabStrip extends HorizontalScrollView { if (indicatorHeight >= 0) { View currentTab = tabsContainer.getChildAt(currentPosition); float lineLeft = 0; - int width = 0; + float width = 0; if (currentTab != null) { - lineLeft = currentTab.getLeft(); + lineLeft = currentTab.getX(); width = currentTab.getMeasuredWidth(); } + if (animateToExpanded) { + width = stickerTabWidth + (stickerTabExpandedWidth - stickerTabWidth) * expandProgress; + } if (animateFromPosition) { long newTime = SystemClock.elapsedRealtime(); long dt = newTime - lastAnimationTime; @@ -493,23 +776,54 @@ public class ScrollSlidingTabStrip extends HorizontalScrollView { invalidate(); } + if (draggingView != null && draggingViewIndicatorOutProgress != 1f) { + draggingViewIndicatorOutProgress += 16 / 150f; + if (draggingViewIndicatorOutProgress > 1f) { + draggingViewIndicatorOutProgress = 1f; + } else { + invalidate(); + } + } else if (draggingView == null && draggingViewIndicatorOutProgress != 0){ + draggingViewIndicatorOutProgress -= 16 / 150f; + if (draggingViewIndicatorOutProgress < 0) { + draggingViewIndicatorOutProgress = 0; + } else { + invalidate(); + } + } + switch (type) { case LINE: if (indicatorHeight == 0) { - indicatorDrawable.setBounds((int) lineLeft, 0, (int) lineLeft + width, height); + indicatorDrawable.setBounds((int) lineLeft, 0, (int) (lineLeft + width), (int) height); } else { - indicatorDrawable.setBounds((int) lineLeft, height - indicatorHeight, (int) lineLeft + width, height); + indicatorDrawable.setBounds((int) lineLeft, (int) (height - indicatorHeight), (int) (lineLeft + width), (int) height); } break; case TAB: - indicatorDrawable.setBounds((int) lineLeft + AndroidUtilities.dp(6), height - AndroidUtilities.dp(3), (int) lineLeft + width - AndroidUtilities.dp(6), height); + float yOffset = AndroidUtilities.dp(3) * draggingViewIndicatorOutProgress; + indicatorDrawable.setBounds((int) lineLeft + AndroidUtilities.dp(6), (int) (height - AndroidUtilities.dp(3) + yOffset), (int) (lineLeft + width - AndroidUtilities.dp(6)), (int) (height + yOffset)); break; } + indicatorDrawable.setColor(indicatorColor); indicatorDrawable.draw(canvas); } } + public void drawOverlays(Canvas canvas) { + if (draggingView != null) { + canvas.save(); + float x = draggindViewXOnScreen - draggindViewDxOnScreen; + if (draggingViewOutProgress > 0) { + x = x * (1f - draggingViewOutProgress) + (draggingView.getX() - getScrollX()) * draggingViewOutProgress; + } + canvas.translate(x, 0); + draggingView.draw(canvas); + canvas.restore(); + } + } + public void setShouldExpand(boolean value) { shouldExpand = value; requestLayout(); @@ -546,10 +860,12 @@ public class ScrollSlidingTabStrip extends HorizontalScrollView { for (int a = 0; a < tabsContainer.getChildCount(); a++) { tabsContainer.getChildAt(a).setSelected(a == position); } - if (first == position && position > 1) { - scrollToChild(position - 1); - } else { - scrollToChild(position); + if (expandStickerAnimator == null) { + if (first == position && position > 1) { + scrollToChild(position - 1); + } else { + scrollToChild(position); + } } invalidate(); } @@ -588,4 +904,172 @@ public class ScrollSlidingTabStrip extends HorizontalScrollView { underlineHeight = value; invalidate(); } + + protected void invalidateOverlays() { + + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + return checkLongPress(ev) || super.onInterceptTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + return checkLongPress(ev) || super.onTouchEvent(ev); + } + + public boolean checkLongPress(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN && draggingView == null) { + longClickRunning = true; + AndroidUtilities.runOnUIThread(longClickRunnable, 500); + pressedX = ev.getX(); + pressedY = ev.getY(); + } + if (longClickRunning && ev.getAction() == MotionEvent.ACTION_MOVE) { + if (Math.abs(ev.getX() - pressedX) > touchSlop || Math.abs(ev.getY() - pressedY) > touchSlop) { + longClickRunning = false; + AndroidUtilities.cancelRunOnUIThread(longClickRunnable); + } + } + if (ev.getAction() == MotionEvent.ACTION_MOVE && draggingView != null) { + float x = getScrollX() + ev.getX(); + int p = (int) Math.ceil(x / getTabSize()) - 1; + if (p != currentDragPosition) { + if (p < currentDragPosition) { + while (!canSwap(p) && p != currentDragPosition) { + p++; + } + } else { + while (!canSwap(p) && p != currentDragPosition) { + p--; + } + } + } + if (currentDragPosition != p && canSwap(p)) { + for (int i = 0; i < tabsContainer.getChildCount(); i++) { + if (i == currentDragPosition) { + continue; + } + StickerTabView stickerTabView = (StickerTabView) tabsContainer.getChildAt(i); + stickerTabView.saveXPosition(); + } + + startDragFromX += (p - currentDragPosition) * getTabSize(); + currentDragPosition = p; + tabsContainer.removeView(draggingView); + tabsContainer.addView(draggingView, currentDragPosition); + + invalidate(); + } + dragDx = x - startDragFromX; + draggindViewDxOnScreen = pressedX - ev.getX(); + float viewScreenX = ev.getX(); + if (viewScreenX < draggingView.getMeasuredWidth() / 2f) { + startScroll(false); + } else if (viewScreenX > getMeasuredWidth() - draggingView.getMeasuredWidth() / 2f) { + startScroll(true); + } else { + stopScroll(); + } + tabsContainer.invalidate(); + invalidateOverlays(); + return true; + } + if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + stopScroll(); + AndroidUtilities.cancelRunOnUIThread(longClickRunnable); + if (draggingView != null) { + if (startDragFromPosition != currentDragPosition) { + stickerSetPositionChanged(startDragFromPosition, currentDragPosition); + } + ValueAnimator dragViewOutAnimator = ValueAnimator.ofFloat(0, 1f); + dragViewOutAnimator.addUpdateListener(valueAnimator -> { + draggingViewOutProgress = (float) valueAnimator.getAnimatedValue(); + invalidateOverlays(); + }); + dragViewOutAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (draggingView != null) { + invalidateOverlays(); + draggingView.invalidate(); + tabsContainer.invalidate(); + invalidate(); + draggingView = null; + } + } + }); + dragViewOutAnimator.start(); + } + longClickRunning = false; + invalidateOverlays(); + } + return false; + } + + protected void stickerSetPositionChanged(int fromPosition, int toPosition) { + } + + private boolean canSwap(int p) { + if (!dragEnabled) { + return false; + } + if (p < 0 || p >= tabsContainer.getChildCount()) { + return false; + } + View child = tabsContainer.getChildAt(p); + if (child instanceof StickerTabView && ((StickerTabView) child).type == StickerTabView.STICKER_TYPE && !((StickerTabView) child).isChatSticker) { + return true; + } + return false; + } + + boolean scrollRight; + long scrollStartTime; + Runnable scrollRunnable = new Runnable() { + @Override + public void run() { + long currentTime = System.currentTimeMillis() - scrollStartTime; + int dx; + if (currentTime < 3000) { + dx = Math.max(1, AndroidUtilities.dp(1)) * (scrollRight ? 1 : -1); + } else if (currentTime < 5000) { + dx = Math.max(1, AndroidUtilities.dp(2)) * (scrollRight ? 1 : -1); + } else { + dx = Math.max(1, AndroidUtilities.dp(4)) * (scrollRight ? 1 : -1); + } + + scrollBy(dx, 0); + AndroidUtilities.runOnUIThread(scrollRunnable); + } + }; + + private void startScroll(boolean scrollRight) { + this.scrollRight = scrollRight; + if (scrollStartTime <= 0) { + scrollStartTime = System.currentTimeMillis(); + } + AndroidUtilities.runOnUIThread(scrollRunnable, 16); + } + + private void stopScroll() { + scrollStartTime = -1; + AndroidUtilities.cancelRunOnUIThread(scrollRunnable); + } + + boolean isDragging() { + return draggingView != null; + } + + @Override + public void cancelLongPress() { + super.cancelLongPress(); + longClickRunning = false; + AndroidUtilities.cancelRunOnUIThread(longClickRunnable); + } + + public void setDragEnabled(boolean enabled) { + dragEnabled = enabled; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java index ee28ef081..389194665 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchViewPager.java @@ -405,7 +405,7 @@ public class SearchViewPager extends ViewPagerFixed implements FilteredSearchVie if (message != null) { AccountInstance.getInstance(currentAccount).getSendMessagesHelper().sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null); } - AccountInstance.getInstance(currentAccount).getSendMessagesHelper().sendMessage(fmessages, did, true, 0); + AccountInstance.getInstance(currentAccount).getSendMessagesHelper().sendMessage(fmessages, did, false,false, true, 0); } fragment1.finishFragment(); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ShareAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ShareAlert.java index a698535f2..afe28b8ab 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ShareAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ShareAlert.java @@ -54,6 +54,7 @@ import android.widget.Toast; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.google.android.exoplayer2.util.Log; @@ -66,6 +67,7 @@ import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; @@ -74,6 +76,7 @@ import org.telegram.messenger.R; import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.NativeByteBuffer; @@ -84,7 +87,10 @@ import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Adapters.DialogsSearchAdapter; import org.telegram.ui.Adapters.SearchAdapterHelper; +import org.telegram.ui.Cells.GraySectionCell; +import org.telegram.ui.Cells.HintDialogCell; import org.telegram.ui.Cells.ShareDialogCell; import org.telegram.ui.ChatActivity; import org.telegram.ui.DialogsActivity; @@ -106,7 +112,9 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi private LinearLayout sharesCountLayout; private AnimatorSet animatorSet; private RecyclerListView gridView; + private RecyclerListView searchGridView; private GridLayoutManager layoutManager; + private FillLastGridLayoutManager searchLayoutManager; private ShareDialogsAdapter listAdapter; private ShareSearchAdapter searchAdapter; private ArrayList sendingMessageObjects; @@ -147,9 +155,12 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi private float chatActivityEnterViewAnimateFromTop; private ValueAnimator topBackgroundAnimator; - private int topOffset; - RecyclerItemsEnterAnimator recyclerItemsEnterAnimator; + SearchField searchView; + private boolean updateSearchAdapter; + + private ArrayList recentSearchObjects = new ArrayList<>(); + private LongSparseArray recentSearchObjectsById = new LongSparseArray<>(); public interface ShareAlertDelegate { default void didShare() { @@ -313,23 +324,12 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi clearSearchImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(darkTheme ? Theme.key_voipgroup_searchPlaceholder : Theme.key_dialogSearchIcon), PorterDuff.Mode.MULTIPLY)); addView(clearSearchImageView, LayoutHelper.createFrame(36, 36, Gravity.RIGHT | Gravity.TOP, 14, 11, 14, 0)); clearSearchImageView.setOnClickListener(v -> { + updateSearchAdapter = true; searchEditText.setText(""); AndroidUtilities.showKeyboard(searchEditText); }); - searchEditText = new EditTextBoldCursor(context) { - @Override - public boolean dispatchTouchEvent(MotionEvent event) { - MotionEvent e = MotionEvent.obtain(event); - e.setLocation(e.getRawX(), e.getRawY() - containerView.getTranslationY()); - if (e.getAction() == MotionEvent.ACTION_UP) { - e.setAction(MotionEvent.ACTION_CANCEL); - } - gridView.dispatchTouchEvent(e); - e.recycle(); - return super.dispatchTouchEvent(event); - } - }; + searchEditText = new EditTextBoldCursor(context); searchEditText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); searchEditText.setHintTextColor(Theme.getColor(darkTheme ? Theme.key_voipgroup_searchPlaceholder : Theme.key_dialogSearchHint)); searchEditText.setTextColor(Theme.getColor(darkTheme ? Theme.key_voipgroup_searchText : Theme.key_dialogSearchText)); @@ -367,6 +367,13 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi .scaleY(show ? 1.0f : 0.1f) .start(); } + if (!TextUtils.isEmpty(searchEditText.getText())) { + checkCurrentList(false); + } + + if (!updateSearchAdapter) { + return; + } String text = searchEditText.getText().toString(); if (text.length() != 0) { if (searchEmptyView != null) { @@ -377,7 +384,7 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi int top = getCurrentTop(); searchEmptyView.setText(LocaleController.getString("NoChats", R.string.NoChats)); searchEmptyView.showTextView(); - gridView.setAdapter(listAdapter); + checkCurrentList(false); listAdapter.notifyDataSetChanged(); if (top > 0) { layoutManager.scrollToPositionWithOffset(0, -top); @@ -487,7 +494,7 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi @Override protected void onTransitionStart(boolean keyboardVisible, int contentHeight) { super.onTransitionStart(keyboardVisible, contentHeight); - if (previousScrollOffsetY > 0 && previousScrollOffsetY != scrollOffsetY && keyboardVisible) { + if (previousScrollOffsetY != scrollOffsetY) { fromScrollY = previousScrollOffsetY; toScrollY = scrollOffsetY; panTranslationMoveLayout = true; @@ -520,12 +527,12 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi protected void onTransitionEnd() { super.onTransitionEnd(); panTranslationMoveLayout = false; - updateLayout(); previousScrollOffsetY = scrollOffsetY; gridView.setTopGlowOffset(scrollOffsetY); frameLayout.setTranslationY(scrollOffsetY); searchEmptyView.setTranslationY(scrollOffsetY); gridView.setTranslationY(0); + searchGridView.setTranslationY(0); } @Override @@ -538,9 +545,16 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi } } currentPanTranslationY = y; - if (fromScrollY != -1 && keyboardVisible) { - scrollOffsetY = (int) (fromScrollY * (1f - progress) + toScrollY * progress); - gridView.setTranslationY(currentPanTranslationY + (fromScrollY - toScrollY) * (1f - progress)); + if (fromScrollY != -1) { + float p = keyboardVisible ? progress : (1f - progress); + scrollOffsetY = (int) (fromScrollY * (1f - p) + toScrollY * p); + float translationY = currentPanTranslationY + (fromScrollY - toScrollY) * (1f - p); + gridView.setTranslationY(translationY); + if (keyboardVisible) { + searchGridView.setTranslationY(translationY); + } else { + searchGridView.setTranslationY(translationY + gridView.getPaddingTop()); + } } else if (fromOffsetTop != -1) { scrollOffsetY = (int) (fromOffsetTop * (1f - progress) + toOffsetTop * progress); float p = keyboardVisible ? (1f - progress) : progress; @@ -589,6 +603,8 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi totalHeight = MeasureSpec.getSize(heightMeasureSpec); } + layoutManager.setNeedFixGap(getLayoutParams().height <= 0); + searchLayoutManager.setNeedFixGap(getLayoutParams().height <= 0); if (Build.VERSION.SDK_INT >= 21 && !isFullscreen) { ignoreLayout = true; setPadding(backgroundPaddingLeft, AndroidUtilities.statusBarHeight, backgroundPaddingLeft, 0); @@ -604,10 +620,20 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi gridView.setPadding(0, padding, 0, AndroidUtilities.dp(48)); ignoreLayout = false; } + + if (keyboardVisible && getLayoutParams().height <= 0 && searchGridView.getPaddingTop() != padding) { + ignoreLayout = true; + searchGridView.setPadding(0, 0, 0, AndroidUtilities.dp(48)); + ignoreLayout = false; + } fullHeight = contentSize >= totalHeight; topOffset = (fullHeight || !SharedConfig.smoothKeyboard) ? 0 : totalHeight - contentSize; + ignoreLayout = true; + checkCurrentList(false); + ignoreLayout = false; + setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), totalHeight); - onMeasureInternal(widthMeasureSpec, MeasureSpec.makeMeasureSpec(Math.min(contentSize, totalHeight), MeasureSpec.EXACTLY)); + onMeasureInternal(widthMeasureSpec, MeasureSpec.makeMeasureSpec(totalHeight, MeasureSpec.EXACTLY)); } private void onMeasureInternal(int widthMeasureSpec, int heightMeasureSpec) { @@ -747,6 +773,7 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi notifyHeightChanged(); updateLayout(); + } @Override @@ -862,40 +889,19 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi frameLayout.addView(switchView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.TOP | Gravity.LEFT, 0, 11, 0, 0)); } - SearchField searchView = new SearchField(context); + searchView = new SearchField(context); frameLayout.addView(searchView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 58, Gravity.BOTTOM | Gravity.LEFT)); gridView = new RecyclerListView(context) { - @Override - protected void dispatchDraw(Canvas canvas) { - recyclerItemsEnterAnimator.dispatchDraw(); - super.dispatchDraw(canvas); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - recyclerItemsEnterAnimator.onDetached(); - } - @Override protected boolean allowSelectChildAtPosition(float x, float y) { return y >= AndroidUtilities.dp(darkTheme && linkToCopy[1] != null ? 111 : 58) + (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0); } - - @Override - public void setTranslationY(float translationY) { - super.setTranslationY(translationY); - int[] ii = new int[2]; - getLocationInWindow(ii); - } }; - gridView.setTag(13); gridView.setPadding(0, 0, 0, AndroidUtilities.dp(48)); gridView.setClipToPadding(false); gridView.setLayoutManager(layoutManager = new GridLayoutManager(getContext(), 4)); - recyclerItemsEnterAnimator = new RecyclerItemsEnterAnimator(gridView, true); layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { @@ -924,85 +930,108 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi containerView.addView(gridView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 0, 0, 0, 0)); gridView.setAdapter(listAdapter = new ShareDialogsAdapter(context)); gridView.setGlowColor(Theme.getColor(darkTheme ? Theme.key_voipgroup_inviteMembersBackground : Theme.key_dialogScrollGlow)); - gridView.setOnItemClickListener((view, position) -> { if (position < 0) { return; } - TLRPC.Dialog dialog; - if (gridView.getAdapter() == listAdapter) { - dialog = listAdapter.getItem(position); - } else { - dialog = searchAdapter.getItem(position); - } + TLRPC.Dialog dialog = listAdapter.getItem(position); if (dialog == null) { return; } - if (hasPoll != 0) { - int lowerId = (int) dialog.id; - if (lowerId < 0) { - TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-lowerId); - boolean isChannel = ChatObject.isChannel(chat) && hasPoll == 2 && !chat.megagroup; - if (isChannel || !ChatObject.canSendPolls(chat)) { - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setTitle(LocaleController.getString("SendMessageTitle", R.string.SendMessageTitle)); - if (isChannel) { - builder.setMessage(LocaleController.getString("PublicPollCantForward", R.string.PublicPollCantForward)); - } else if (ChatObject.isActionBannedByDefault(chat, ChatObject.ACTION_SEND_POLLS)) { - builder.setMessage(LocaleController.getString("ErrorSendRestrictedPollsAll", R.string.ErrorSendRestrictedPollsAll)); - } else { - builder.setMessage(LocaleController.getString("ErrorSendRestrictedPolls", R.string.ErrorSendRestrictedPolls)); - } - builder.setNegativeButton(LocaleController.getString("OK", R.string.OK), null); - builder.show(); - return; - } - } - } - ShareDialogCell cell = (ShareDialogCell) view; - if (selectedDialogs.indexOfKey(dialog.id) >= 0) { - selectedDialogs.remove(dialog.id); - cell.setChecked(false, true); - updateSelectedCount(1); - } else { - selectedDialogs.put(dialog.id, dialog); - cell.setChecked(true, true); - updateSelectedCount(2); - int selfUserId = UserConfig.getInstance(currentAccount).clientUserId; - if (gridView.getAdapter() == searchAdapter) { - TLRPC.Dialog existingDialog = listAdapter.dialogsMap.get(dialog.id); - if (existingDialog == null) { - listAdapter.dialogsMap.put(dialog.id, dialog); - listAdapter.dialogs.add(listAdapter.dialogs.isEmpty() ? 0 : 1, dialog); - } else if (existingDialog.id != selfUserId) { - listAdapter.dialogs.remove(existingDialog); - listAdapter.dialogs.add(listAdapter.dialogs.isEmpty() ? 0 : 1, existingDialog); - } - searchView.searchEditText.setText(""); - gridView.setAdapter(listAdapter); - searchView.hideKeyboard(); - } - } + selectDialog((ShareDialogCell) view, dialog); }); gridView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - updateLayout(); if (dy != 0) { + updateLayout(); previousScrollOffsetY = scrollOffsetY; } } }); + + searchGridView = new RecyclerListView(context) { + + @Override + protected void dispatchDraw(Canvas canvas) { + recyclerItemsEnterAnimator.dispatchDraw(); + super.dispatchDraw(canvas); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + recyclerItemsEnterAnimator.onDetached(); + } + + @Override + protected boolean allowSelectChildAtPosition(float x, float y) { + return y >= AndroidUtilities.dp(darkTheme && linkToCopy[1] != null ? 111 : 58) + (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0); + } + }; + searchGridView.setPadding(0, 0, 0, AndroidUtilities.dp(48)); + searchGridView.setClipToPadding(false); + searchGridView.setLayoutManager(searchLayoutManager = new FillLastGridLayoutManager(getContext(), 4, 0, searchGridView)); + searchLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { + @Override + public int getSpanSize(int position) { + return searchAdapter.getSpanSize(4, position); + } + }); + searchGridView.setOnItemClickListener((view, position) -> { + if (position < 0) { + return; + } + TLRPC.Dialog dialog = searchAdapter.getItem(position); + if (dialog == null) { + return; + } + selectDialog((ShareDialogCell) view, dialog); + }); + searchGridView.setHasFixedSize(true); + searchGridView.setItemAnimator(null); + searchGridView.setHorizontalScrollBarEnabled(false); + searchGridView.setVerticalScrollBarEnabled(false); + searchGridView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (dy != 0) { + updateLayout(); + previousScrollOffsetY = scrollOffsetY; + } + } + }); + searchGridView.addItemDecoration(new RecyclerView.ItemDecoration() { + @Override + public void getItemOffsets(android.graphics.Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + RecyclerListView.Holder holder = (RecyclerListView.Holder) parent.getChildViewHolder(view); + if (holder != null) { + int pos = holder.getAdapterPosition(); + outRect.left = pos % 4 == 0 ? 0 : AndroidUtilities.dp(4); + outRect.right = pos % 4 == 3 ? 0 : AndroidUtilities.dp(4); + } else { + outRect.left = AndroidUtilities.dp(4); + outRect.right = AndroidUtilities.dp(4); + } + } + }); + searchGridView.setAdapter(searchAdapter); + searchGridView.setGlowColor(Theme.getColor(darkTheme ? Theme.key_voipgroup_inviteMembersBackground : Theme.key_dialogScrollGlow)); + + recyclerItemsEnterAnimator = new RecyclerItemsEnterAnimator(searchGridView, true); + FlickerLoadingView flickerLoadingView = new FlickerLoadingView(context); flickerLoadingView.setViewType(FlickerLoadingView.SHARE_ALERT_TYPE); searchEmptyView = new EmptyTextProgressView(context, flickerLoadingView); searchEmptyView.setShowAtCenter(true); searchEmptyView.showTextView(); searchEmptyView.setText(LocaleController.getString("NoChats", R.string.NoChats)); - gridView.setEmptyView(searchEmptyView); - gridView.setAnimateEmptyView(true, 0); + searchGridView.setEmptyView(searchEmptyView); + searchGridView.setHideIfEmpty(false); + searchGridView.setAnimateEmptyView(true, 0); containerView.addView(searchEmptyView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 0, 52, 0, 0)); + containerView.addView(searchGridView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, 0, 0, 0, 0)); FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, AndroidUtilities.getShadowHeight(), Gravity.TOP | Gravity.LEFT); frameLayoutParams.topMargin = AndroidUtilities.dp(darkTheme && linkToCopy[1] != null ? 111 : 58); @@ -1244,7 +1273,7 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi if (frameLayout2.getTag() != null && commentTextView.length() > 0) { SendMessagesHelper.getInstance(currentAccount).sendMessage(commentTextView.getText().toString(), key, null, null, null, true, null, null, null, true, 0, null); } - SendMessagesHelper.getInstance(currentAccount).sendMessage(sendingMessageObjects, key, true, 0); + SendMessagesHelper.getInstance(currentAccount).sendMessage(sendingMessageObjects, key, false,false, true, 0); } onSend(selectedDialogs, sendingMessageObjects.size()); } else { @@ -1306,6 +1335,85 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi if (listAdapter.dialogs.isEmpty()) { NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.dialogsNeedReload); } + + DialogsSearchAdapter.loadRecentSearch(currentAccount, 0, new DialogsSearchAdapter.OnRecentSearchLoaded() { + @Override + public void setRecentSearch(ArrayList arrayList, LongSparseArray hashMap) { + recentSearchObjects = arrayList; + recentSearchObjectsById = hashMap; + for (int a = 0; a < recentSearchObjects.size(); a++) { + DialogsSearchAdapter.RecentSearchObject recentSearchObject = recentSearchObjects.get(a); + if (recentSearchObject.object instanceof TLRPC.User) { + MessagesController.getInstance(currentAccount).putUser((TLRPC.User) recentSearchObject.object, true); + } else if (recentSearchObject.object instanceof TLRPC.Chat) { + MessagesController.getInstance(currentAccount).putChat((TLRPC.Chat) recentSearchObject.object, true); + } else if (recentSearchObject.object instanceof TLRPC.EncryptedChat) { + MessagesController.getInstance(currentAccount).putEncryptedChat((TLRPC.EncryptedChat) recentSearchObject.object, true); + } + } + searchAdapter.notifyDataSetChanged(); + } + }); + MediaDataController.getInstance(currentAccount).loadHints(true); + + AndroidUtilities.updateViewVisibilityAnimated(gridView, true, 1f, false); + AndroidUtilities.updateViewVisibilityAnimated(searchGridView, false, 1f, false); + } + + private void selectDialog(ShareDialogCell cell, TLRPC.Dialog dialog) { + if (hasPoll != 0) { + int lowerId = (int) dialog.id; + if (lowerId < 0) { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-lowerId); + boolean isChannel = ChatObject.isChannel(chat) && hasPoll == 2 && !chat.megagroup; + if (isChannel || !ChatObject.canSendPolls(chat)) { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(LocaleController.getString("SendMessageTitle", R.string.SendMessageTitle)); + if (isChannel) { + builder.setMessage(LocaleController.getString("PublicPollCantForward", R.string.PublicPollCantForward)); + } else if (ChatObject.isActionBannedByDefault(chat, ChatObject.ACTION_SEND_POLLS)) { + builder.setMessage(LocaleController.getString("ErrorSendRestrictedPollsAll", R.string.ErrorSendRestrictedPollsAll)); + } else { + builder.setMessage(LocaleController.getString("ErrorSendRestrictedPolls", R.string.ErrorSendRestrictedPolls)); + } + builder.setNegativeButton(LocaleController.getString("OK", R.string.OK), null); + builder.show(); + return; + } + } + } + if (selectedDialogs.indexOfKey(dialog.id) >= 0) { + selectedDialogs.remove(dialog.id); + if (cell != null) { + cell.setChecked(false, true); + } + updateSelectedCount(1); + } else { + selectedDialogs.put(dialog.id, dialog); + if (cell != null) { + cell.setChecked(true, true); + } + updateSelectedCount(2); + int selfUserId = UserConfig.getInstance(currentAccount).clientUserId; + if (searchIsVisible) { + TLRPC.Dialog existingDialog = listAdapter.dialogsMap.get(dialog.id); + if (existingDialog == null) { + listAdapter.dialogsMap.put(dialog.id, dialog); + listAdapter.dialogs.add(listAdapter.dialogs.isEmpty() ? 0 : 1, dialog); + } else if (existingDialog.id != selfUserId) { + listAdapter.dialogs.remove(existingDialog); + listAdapter.dialogs.add(listAdapter.dialogs.isEmpty() ? 0 : 1, existingDialog); + } + listAdapter.notifyDataSetChanged(); + updateSearchAdapter = false; + searchView.searchEditText.setText(""); + checkCurrentList(false); + searchView.hideKeyboard(); + } + } + if (searchAdapter.categoryAdapter != null) { + searchAdapter.categoryAdapter.notifyItemRangeChanged(0, searchAdapter.categoryAdapter.getItemCount()); + } } protected void onSend(LongSparseArray dids, int count) { @@ -1317,7 +1425,7 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi View child = gridView.getChildAt(0); RecyclerListView.Holder holder = (RecyclerListView.Holder) gridView.findContainingViewHolder(child); if (holder != null) { - return gridView.getPaddingTop() - (holder.getAdapterPosition() == 0 && child.getTop() >= 0 ? child.getTop() : 0); + return gridView.getPaddingTop() - (holder.getLayoutPosition() == 0 && child.getTop() >= 0 ? child.getTop() : 0); } } return -1000; @@ -1359,24 +1467,42 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi return false; } + int lastOffset = Integer.MAX_VALUE; + @SuppressLint("NewApi") private void updateLayout() { - if (gridView.getChildCount() <= 0 || panTranslationMoveLayout) { + if (panTranslationMoveLayout) { return; } - View child = gridView.getChildAt(0); - RecyclerListView.Holder holder = (RecyclerListView.Holder) gridView.findContainingViewHolder(child); + View child; + RecyclerListView.Holder holder = null; + RecyclerListView listView = searchIsVisible ? searchGridView : gridView; + + if (listView.getChildCount() <= 0) { + return; + } + child = listView.getChildAt(0); + for (int i = 0; i < listView.getChildCount(); i++) { + if (listView.getChildAt(i).getTop() < child.getTop()) { + child = listView.getChildAt(i); + } + } + holder = (RecyclerListView.Holder) listView.findContainingViewHolder(child); + int top = child.getTop() - AndroidUtilities.dp(8); int newOffset = top > 0 && holder != null && holder.getAdapterPosition() == 0 ? top : 0; if (top >= 0 && holder != null && holder.getAdapterPosition() == 0) { + lastOffset = child.getTop(); newOffset = top; runShadowAnimation(0, false); } else { + lastOffset = Integer.MAX_VALUE; runShadowAnimation(0, true); } if (scrollOffsetY != newOffset) { previousScrollOffsetY = scrollOffsetY; gridView.setTopGlowOffset(scrollOffsetY = (int) (newOffset + currentPanTranslationY)); + searchGridView.setTopGlowOffset(scrollOffsetY = (int) (newOffset + currentPanTranslationY)); frameLayout.setTranslationY(scrollOffsetY + currentPanTranslationY); searchEmptyView.setTranslationY(scrollOffsetY + currentPanTranslationY); containerView.invalidate(); @@ -1676,6 +1802,17 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi private int lastGlobalSearchId; private int lastLocalSearchId; + int hintsCell = -1; + int resentTitleCell = -1; + int firstEmptyViewCell = -1; + int recentDialogsStartRow = -1; + int searchResultsStartRow = -1; + int lastFilledItem = -1; + + int itemsCount; + + DialogsSearchAdapter.CategoryAdapterRecycler categoryAdapter; + public ShareSearchAdapter(Context context) { this.context = context; searchAdapterHelper = new SearchAdapterHelper(false); @@ -1693,6 +1830,7 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi recyclerItemsEnterAnimator.showItemsAnimated(oldItemsCount); } notifyDataSetChanged(); + checkCurrentList(true); } @Override @@ -1937,11 +2075,7 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi recyclerItemsEnterAnimator.showItemsAnimated(oldItemsCount); } notifyDataSetChanged(); - if (!isEmpty && !becomeEmpty && topBeforeSwitch > 0) { - layoutManager.scrollToPositionWithOffset(0, -topBeforeSwitch); - topBeforeSwitch = -1000; - } - + checkCurrentList(true); }); } @@ -1963,14 +2097,13 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi searchAdapterHelper.mergeResults(null); searchAdapterHelper.queryServerSearch(null, true, true, true, true, false, 0, false, 0, 0); notifyDataSetChanged(); + checkCurrentList(true); if (TextUtils.isEmpty(query)) { topBeforeSwitch = getCurrentTop(); lastSearchId = -1; internalDialogsIsSearching = false; - gridView.setAdapter(listAdapter); } else { - gridView.setAdapter(searchAdapter); internalDialogsIsSearching = true; final int searchId = ++lastSearchId; searchEmptyView.showProgress(false); @@ -1986,21 +2119,56 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi }); }, 300); } + checkCurrentList(false); } int lastItemCont; @Override public int getItemCount() { - int count = searchResult.size(); - count += searchAdapterHelper.getLocalServerSearch().size(); - if (count != 0) { - count++; + itemsCount = 0; + hintsCell = -1; + resentTitleCell = -1; + recentDialogsStartRow = -1; + searchResultsStartRow = -1; + lastFilledItem = -1; + + if (TextUtils.isEmpty(lastSearchText)) { + firstEmptyViewCell = itemsCount++; + hintsCell = itemsCount++; + + if (recentSearchObjects.size() > 0) { + resentTitleCell = itemsCount++; + recentDialogsStartRow = itemsCount; + itemsCount += recentSearchObjects.size(); + } + lastFilledItem = itemsCount++; + return itemsCount; + } else { + firstEmptyViewCell = itemsCount++; + searchResultsStartRow = itemsCount; + itemsCount += (searchResult.size() + searchAdapterHelper.getLocalServerSearch().size()); + if (itemsCount == 1) { + firstEmptyViewCell = -1; + return 0; + } + lastFilledItem = itemsCount++; } - return lastItemCont = count; + return lastItemCont = itemsCount; } public TLRPC.Dialog getItem(int position) { + if (position >= recentDialogsStartRow && recentDialogsStartRow >= 0) { + DialogsSearchAdapter.RecentSearchObject recentSearchObject = recentSearchObjects.get(position - recentDialogsStartRow); + TLObject object = recentSearchObject.object; + TLRPC.Dialog dialog = new TLRPC.TL_dialog(); + if (object instanceof TLRPC.User) { + dialog.id = ((TLRPC.User) object).id; + } else { + dialog.id = -((TLRPC.Chat) object).id; + } + return dialog; + } position--; if (position < 0) { return null; @@ -2026,12 +2194,13 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { - if (holder.getItemViewType() == 1) { + if (holder.getItemViewType() == 1 || holder.getItemViewType() == 4) { return false; } return true; } + @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; @@ -2041,12 +2210,99 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, AndroidUtilities.dp(100))); break; } - case 1: - default: { + default: + case 1: { view = new View(context); view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, AndroidUtilities.dp(darkTheme && linkToCopy[1] != null ? 109 : 56))); break; } + case 2: { + RecyclerListView horizontalListView = new RecyclerListView(context) { + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + if (getParent() != null && getParent().getParent() != null) { + getParent().getParent().requestDisallowInterceptTouchEvent(canScrollHorizontally(-1) || canScrollHorizontally(1)); + } + return super.onInterceptTouchEvent(e); + } + }; + horizontalListView.setItemAnimator(null); + horizontalListView.setLayoutAnimation(null); + LinearLayoutManager layoutManager = new LinearLayoutManager(context) { + @Override + public boolean supportsPredictiveItemAnimations() { + return false; + } + }; + layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); + horizontalListView.setLayoutManager(layoutManager); + horizontalListView.setAdapter(categoryAdapter = new DialogsSearchAdapter.CategoryAdapterRecycler(context, currentAccount, true) { + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + HintDialogCell cell = (HintDialogCell) holder.itemView; + + TLRPC.TL_topPeer peer = MediaDataController.getInstance(currentAccount).hints.get(position); + TLRPC.Chat chat = null; + TLRPC.User user = null; + int did = 0; + if (peer.peer.user_id != 0) { + did = peer.peer.user_id; + user = MessagesController.getInstance(currentAccount).getUser(peer.peer.user_id); + } else if (peer.peer.channel_id != 0) { + did = -peer.peer.channel_id; + chat = MessagesController.getInstance(currentAccount).getChat(peer.peer.channel_id); + } else if (peer.peer.chat_id != 0) { + did = -peer.peer.chat_id; + chat = MessagesController.getInstance(currentAccount).getChat(peer.peer.chat_id); + } + boolean animated = did == cell.getDialogId(); + cell.setTag(did); + String name = ""; + if (user != null) { + name = UserObject.getFirstName(user); + } else if (chat != null) { + name = chat.title; + } + cell.setDialog(did, true, name); + cell.setChecked(selectedDialogs.indexOfKey(did) >= 0, animated); + } + }); + horizontalListView.setOnItemClickListener((view1, position) -> { + TLRPC.TL_topPeer peer = MediaDataController.getInstance(currentAccount).hints.get(position); + TLRPC.Dialog dialog = new TLRPC.TL_dialog(); + TLRPC.Chat chat = null; + TLRPC.User user = null; + int did = 0; + if (peer.peer.user_id != 0) { + did = peer.peer.user_id; + } else if (peer.peer.channel_id != 0) { + did = -peer.peer.channel_id; + } else if (peer.peer.chat_id != 0) { + did = -peer.peer.chat_id; + } + dialog.id = did; + selectDialog(null, dialog); + HintDialogCell cell = (HintDialogCell) view1; + cell.setChecked(selectedDialogs.indexOfKey(did) >= 0, true); + }); + view = horizontalListView; + break; + } + case 3: { + GraySectionCell graySectionCell = new GraySectionCell(context); + graySectionCell.setText(LocaleController.getString("Recent", R.string.Recent)); + view = graySectionCell; + break; + } + case 4: { + view = new View(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(searchLayoutManager.lastItemHeight, MeasureSpec.EXACTLY)); + } + }; + break; + } } return new RecyclerListView.Holder(view); } @@ -2054,10 +2310,46 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder.getItemViewType() == 0) { - position--; ShareDialogCell cell = (ShareDialogCell) holder.itemView; - CharSequence name; - long id; + CharSequence name = null; + long id = 0; + + if (TextUtils.isEmpty(lastSearchText)) { + if (recentDialogsStartRow >= 0 && position >= recentDialogsStartRow) { + int p = position - recentDialogsStartRow; + DialogsSearchAdapter.RecentSearchObject recentSearchObject = recentSearchObjects.get(p); + TLObject object = recentSearchObject.object; + if (object instanceof TLRPC.User) { + TLRPC.User user = (TLRPC.User) object; + id = user.id; + name = ContactsController.formatName(user.first_name, user.last_name); + } else if (object instanceof TLRPC.Chat) { + TLRPC.Chat chat = (TLRPC.Chat) object; + id = -chat.id; + name = chat.title; + } else if (object instanceof TLRPC.TL_encryptedChat) { + TLRPC.TL_encryptedChat chat = (TLRPC.TL_encryptedChat) object; + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(chat.user_id); + if (user != null) { + id = user.id; + name = ContactsController.formatName(user.first_name, user.last_name); + } + } + String foundUserName = searchAdapterHelper.getLastFoundUsername(); + if (!TextUtils.isEmpty(foundUserName)) { + int index; + if (name != null && (index = AndroidUtilities.indexOfIgnoreCase(name.toString(), foundUserName)) != -1) { + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(name); + spannableStringBuilder.setSpan(new ForegroundColorSpanThemable(Theme.key_windowBackgroundWhiteBlueText4), index, index + foundUserName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + name = spannableStringBuilder; + } + } + + } + cell.setDialog((int) id, selectedDialogs.indexOfKey(id) >= 0, name); + return; + } + position--; if (position < searchResult.size()) { DialogSearchResult result = (DialogSearchResult) searchResult.get(position); id = result.dialog.id; @@ -2089,12 +2381,65 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi } } + @Override public int getItemViewType(int position) { - if (position == 0) { + if (position == lastFilledItem) { + return 4; + } else if (position == firstEmptyViewCell) { return 1; + } else if (position == hintsCell) { + return 2; + } else if (position == resentTitleCell) { + return 3; } return 0; } + + public boolean isSearching() { + return !TextUtils.isEmpty(lastSearchText); + } + + public int getSpanSize(int spanCount, int position) { + if (position == hintsCell || position == resentTitleCell || position == firstEmptyViewCell || position == lastFilledItem) { + return spanCount; + } + return 1; + } + } + + private boolean searchIsVisible; + + private void checkCurrentList(boolean force) { + boolean searchVisibleLocal = false; + if (!TextUtils.isEmpty(searchView.searchEditText.getText()) || (keyboardVisible && searchView.searchEditText.hasFocus())) { + searchVisibleLocal = true; + updateSearchAdapter = true; + AndroidUtilities.updateViewVisibilityAnimated(gridView, false, 0.98f, true); + AndroidUtilities.updateViewVisibilityAnimated(searchGridView, true); + } else { + AndroidUtilities.updateViewVisibilityAnimated(gridView, true, 0.98f, true); + AndroidUtilities.updateViewVisibilityAnimated(searchGridView, false); + } + + if (searchIsVisible != searchVisibleLocal || force) { + searchIsVisible = searchVisibleLocal; + searchAdapter.notifyDataSetChanged(); + listAdapter.notifyDataSetChanged(); + if (searchIsVisible) { + if (lastOffset == Integer.MAX_VALUE) { + ((LinearLayoutManager) searchGridView.getLayoutManager()).scrollToPositionWithOffset(0, -searchGridView.getPaddingTop()); + } else { + ((LinearLayoutManager) searchGridView.getLayoutManager()).scrollToPositionWithOffset(0, lastOffset - searchGridView.getPaddingTop()); + } + searchAdapter.searchDialogs(searchView.searchEditText.getText().toString()); + } else { + if (lastOffset == Integer.MAX_VALUE) { + layoutManager.scrollToPositionWithOffset(0, 0); + } else { + layoutManager.scrollToPositionWithOffset(0, 0); + } + } + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java index c8c7dfada..ac29406bc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java @@ -1685,7 +1685,7 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter if (message != null) { profileActivity.getSendMessagesHelper().sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null); } - profileActivity.getSendMessagesHelper().sendMessage(fmessages, did, true, 0); + profileActivity.getSendMessagesHelper().sendMessage(fmessages, did, false,false, true, 0); } fragment1.finishFragment(); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java index cecd8dfb3..da6b1f43c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SizeNotifierFrameLayout.java @@ -47,6 +47,7 @@ public class SizeNotifierFrameLayout extends FrameLayout { private int emojiHeight; private float emojiOffset; private boolean animationInProgress; + private boolean skipBackgroundDrawing; public interface SizeNotifierFrameLayoutDelegate { void onSizeChanged(int keyboardHeight, boolean isWidthGreater); @@ -213,7 +214,7 @@ public class SizeNotifierFrameLayout extends FrameLayout { @Override protected void onDraw(Canvas canvas) { - if (backgroundDrawable == null) { + if (backgroundDrawable == null || skipBackgroundDrawing) { super.onDraw(canvas); return; } @@ -335,4 +336,9 @@ public class SizeNotifierFrameLayout extends FrameLayout { protected AdjustPanLayoutHelper createAdjustPanLayoutHelper() { return null; } + + public void setSkipBackgroundDrawing(boolean skipBackgroundDrawing) { + this.skipBackgroundDrawing = skipBackgroundDrawing; + invalidate(); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java index 9fc3ad30e..f937b639d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerMasksAlert.java @@ -826,11 +826,10 @@ public class StickerMasksAlert extends BottomSheet implements NotificationCenter TLRPC.TL_messages_stickerSet stickerSet = stickerSets[currentType].get(a); TLRPC.Document document = stickerSet.documents.get(0); TLObject thumb = FileLoader.getClosestPhotoSizeWithSize(stickerSet.set.thumbs, 90); - SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(stickerSet.set.thumbs, Theme.key_windowBackgroundGray, 1.0f); if (thumb == null) { thumb = document; } - stickersTab.addStickerTab(thumb, svgThumb, document, stickerSet).setContentDescription(stickerSet.set.title + ", " + LocaleController.getString("AccDescrStickerSet", R.string.AccDescrStickerSet)); + stickersTab.addStickerTab(thumb, document, stickerSet).setContentDescription(stickerSet.set.title + ", " + LocaleController.getString("AccDescrStickerSet", R.string.AccDescrStickerSet)); } stickersTab.commitUpdate(); stickersTab.updateTabStyles(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerTabView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerTabView.java new file mode 100644 index 000000000..314d9f4db --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StickerTabView.java @@ -0,0 +1,171 @@ +package org.telegram.ui.Components; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.SvgHelper; +import org.telegram.ui.ActionBar.Theme; + +public class StickerTabView extends FrameLayout { + + public final static int STICKER_TYPE = 0; + public final static int ICON_TYPE = 1; + public final static int EMOJI_TYPE = 2; + + public int type; + public float dragOffset; + public boolean inited; + public boolean isChatSticker; + BackupImageView imageView; + ImageView iconView; + TextView textView; + View visibleView; + + boolean expanded; + public final int index; + private static int indexPointer; + public SvgHelper.SvgDrawable svgThumb; + boolean roundImage; + + ValueAnimator dragOffsetAnimator; + float lastLeft; + boolean hasSavedLeft; + + public StickerTabView(Context context, int type) { + super(context); + this.type = type; + index = indexPointer++; + if (type == EMOJI_TYPE) { + imageView = new BackupImageView(getContext()); + imageView.setLayerNum(1); + imageView.setAspectFit(false); + addView(imageView, LayoutHelper.createFrame(36, 36, Gravity.CENTER)); + visibleView = imageView; + } else if (type == ICON_TYPE) { + iconView = new ImageView(context); + iconView.setScaleType(ImageView.ScaleType.CENTER_CROP); + addView(iconView, LayoutHelper.createFrame(24, 24, Gravity.CENTER)); + visibleView = iconView; + } else { + imageView = new BackupImageView(getContext()); + imageView.setLayerNum(1); + imageView.setAspectFit(true); + addView(imageView, LayoutHelper.createFrame(30, 30, Gravity.CENTER)); + visibleView = imageView; + } + + textView = new TextView(context); + textView.setLines(1); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 11); + textView.setGravity(Gravity.CENTER_HORIZONTAL); + textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM, 8, 0, 8, 10)); + + textView.setVisibility(View.GONE); + } + + public void setExpanded(boolean expanded) { + if (type == EMOJI_TYPE) { + return; + } + this.expanded = expanded; + float size = type == ICON_TYPE ? 24 : 30; + float sizeExpanded = type == ICON_TYPE ? 38 : 56; + + visibleView.getLayoutParams().width = AndroidUtilities.dp(expanded ? sizeExpanded : size); + visibleView.getLayoutParams().height = AndroidUtilities.dp(expanded ? sizeExpanded : size); + + textView.setVisibility(expanded ? View.VISIBLE : View.GONE); + + if (type != ICON_TYPE && roundImage) { + imageView.setRoundRadius(AndroidUtilities.dp(visibleView.getLayoutParams().width / 2f)); + } + } + + public void updateExpandProgress(float expandProgress) { + if (type == EMOJI_TYPE) { + return; + } + if (expanded) { + float size = type == ICON_TYPE ? 24 : 30; + float sizeExpanded = type == ICON_TYPE ? 38 : 56; + float fromX = AndroidUtilities.dp(52 - size) / 2f; + float fromY = AndroidUtilities.dp(48 - size) / 2f; + float toX = AndroidUtilities.dp(86 - sizeExpanded) / 2f; + float toY = AndroidUtilities.dp(48 + 50 - sizeExpanded) / 2f; + + visibleView.setTranslationY((fromY - toY) * (1 - expandProgress) - AndroidUtilities.dp(8) * expandProgress); + visibleView.setTranslationX((fromX - toX) * (1 - expandProgress)); + textView.setAlpha(Math.max(0, (expandProgress - 0.5f) / 0.5f)); + textView.setTranslationY(-AndroidUtilities.dp(40) * (1 - expandProgress)); + textView.setTranslationX(-AndroidUtilities.dp(12) * (1 - expandProgress)); + + float s; + visibleView.setPivotX(0); + visibleView.setPivotY(0); + s = size / sizeExpanded * (1 - expandProgress) + expandProgress; + + visibleView.setScaleX(s); + visibleView.setScaleY(s); + + } else { + visibleView.setTranslationX(0); + visibleView.setTranslationY(0); + visibleView.setScaleX(1f); + visibleView.setScaleY(1f); + } + + } + + public void saveXPosition() { + lastLeft = getLeft(); + hasSavedLeft = true; + invalidate(); + } + + public void animateIfPositionChanged(ViewGroup parent) { + if (getLeft() != lastLeft && hasSavedLeft) { + dragOffset = lastLeft - getLeft(); + if (dragOffsetAnimator != null) { + dragOffsetAnimator.removeAllListeners(); + dragOffsetAnimator.cancel(); + } + dragOffsetAnimator = ValueAnimator.ofFloat(dragOffset, 0); + dragOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + dragOffset = (float) valueAnimator.getAnimatedValue(); + invalidate(); + parent.invalidate(); + } + }); + dragOffsetAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dragOffset = 0; + invalidate(); + parent.invalidate(); + } + }); + dragOffsetAnimator.start(); + } + hasSavedLeft = false; + } + + public void setRoundImage() { + roundImage = true; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemePreviewDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemePreviewDrawable.java index 9bfc4be3e..d45f61cd5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemePreviewDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThemePreviewDrawable.java @@ -53,7 +53,6 @@ public class ThemePreviewDrawable extends BitmapDrawable { int messageInColor = Theme.getPreviewColor(colors, Theme.key_chat_inBubble); int messageOutColor = Theme.getPreviewColor(colors, Theme.key_chat_outBubble); - Integer messageOutGradientColor = colors.get(Theme.key_chat_outBubbleGradient); Integer backgroundColor = colors.get(Theme.key_chat_wallpaper); Integer gradientToColor1 = colors.get(Theme.key_chat_wallpaper_gradient_to1); Integer gradientToColor2 = colors.get(Theme.key_chat_wallpaper_gradient_to2); @@ -190,15 +189,15 @@ public class ThemePreviewDrawable extends BitmapDrawable { } messageDrawable[1].setBounds(161, 216, bitmap.getWidth() - 20, 216 + 92); - messageDrawable[1].setTop(0, 522, false, false); + messageDrawable[1].setTop(0, 560, 522, false, false); messageDrawable[1].draw(canvas); messageDrawable[1].setBounds(161, 430, bitmap.getWidth() - 20, 430 + 92); - messageDrawable[1].setTop(430, 522, false, false); + messageDrawable[1].setTop(430, 560, 522, false, false); messageDrawable[1].draw(canvas); messageDrawable[0].setBounds(20, 323, 399, 323 + 92); - messageDrawable[0].setTop(323, 522, false, false); + messageDrawable[0].setTop(323, 560, 522, false, false); messageDrawable[0].draw(canvas); paint.setColor(messageFieldColor); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java index dbd427169..0c8aac490 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/TrendingStickersLayout.java @@ -5,6 +5,8 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Configuration; +import android.graphics.Canvas; +import android.graphics.Paint; import android.util.LongSparseArray; import android.util.SparseArray; import android.view.Gravity; @@ -358,6 +360,48 @@ public class TrendingStickersLayout extends FrameLayout implements NotificationC } } + private float highlightProgress = 1f; + Paint paint = new Paint(); + + @Override + protected void dispatchDraw(Canvas canvas) { + if (highlightProgress != 0 && scrollToSet != null) { + highlightProgress -= 16f / 3000f; + if (highlightProgress < 0) { + highlightProgress = 0; + } else { + invalidate(); + } + Integer pos = adapter.setsToPosition.get(scrollToSet); + if (pos != null) { + View view1 = layoutManager.findViewByPosition(pos); + int t = -1, b = -1; + if (view1 != null) { + t = (int) view1.getY(); + b = (int) view1.getY() + view1.getMeasuredHeight(); + + } + View view2 = layoutManager.findViewByPosition(pos + 1); + if (view2 != null) { + if (view1 == null) { + t = (int) view2.getY(); + } + b = (int) view2.getY() + view2.getMeasuredHeight(); + } + + if (view1 != null || view2 != null) { + paint.setColor(Theme.getColor(Theme.key_featuredStickers_addButton)); + float p = highlightProgress < 0.06f ? highlightProgress / 0.06f : 1f; + paint.setAlpha((int) (255 * 0.1f * p)); + canvas.drawRect(0, t, getMeasuredWidth(), b, paint); + } + } + + } + + super.dispatchDraw(canvas); + } + @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java index a2c8e3994..d2177ffd3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java @@ -140,6 +140,8 @@ public class UndoView extends FrameLayout { public final static int ACTION_VOIP_SOUND_MUTED = 42; public final static int ACTION_VOIP_SOUND_UNMUTED = 43; public final static int ACTION_VOIP_USER_JOINED = 44; + public final static int ACTION_VOIP_VIDEO_RECORDING_STARTED = 100; + public final static int ACTION_VOIP_VIDEO_RECORDING_FINISHED = 101; public final static int ACTION_IMPORT_NOT_MUTUAL = 45; public final static int ACTION_IMPORT_GROUP_NOT_ADMIN = 46; @@ -315,7 +317,8 @@ public class UndoView extends FrameLayout { currentAction == ACTION_VOIP_LINK_COPIED || currentAction == ACTION_VOIP_INVITED || currentAction == ACTION_VOIP_MUTED_FOR_YOU || currentAction == ACTION_VOIP_UNMUTED_FOR_YOU || currentAction == ACTION_REPORT_SENT || currentAction == ACTION_VOIP_USER_CHANGED || currentAction == ACTION_VOIP_CAN_NOW_SPEAK || currentAction == ACTION_VOIP_RECORDING_STARTED || currentAction == ACTION_VOIP_RECORDING_FINISHED || currentAction == ACTION_VOIP_SOUND_MUTED || currentAction == ACTION_VOIP_SOUND_UNMUTED || currentAction == ACTION_PAYMENT_SUCCESS || - currentAction == ACTION_VOIP_USER_JOINED || currentAction == ACTION_PIN_DIALOGS || currentAction == ACTION_UNPIN_DIALOGS; + currentAction == ACTION_VOIP_USER_JOINED || currentAction == ACTION_PIN_DIALOGS || currentAction == ACTION_UNPIN_DIALOGS || currentAction == ACTION_VOIP_VIDEO_RECORDING_STARTED || + currentAction == ACTION_VOIP_VIDEO_RECORDING_FINISHED; } private boolean hasSubInfo() { @@ -485,7 +488,12 @@ public class UndoView extends FrameLayout { timeLeft = 4000; } else if (action == ACTION_VOIP_INVITED) { TLRPC.User user = (TLRPC.User) infoObject; - infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipGroupInvitedUser", R.string.VoipGroupInvitedUser, UserObject.getFirstName(user))); + TLRPC.Chat chat = (TLRPC.Chat) infoObject2; + if (ChatObject.isChannelOrGiga(chat)) { + infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipChannelInvitedUser", R.string.VoipChannelInvitedUser, UserObject.getFirstName(user))); + } else { + infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipGroupInvitedUser", R.string.VoipGroupInvitedUser, UserObject.getFirstName(user))); + } subInfoText = null; icon = 0; AvatarDrawable avatarDrawable = new AvatarDrawable(); @@ -495,12 +503,21 @@ public class UndoView extends FrameLayout { avatarImageView.setVisibility(VISIBLE); timeLeft = 3000; } else if (action == ACTION_VOIP_USER_JOINED) { + TLRPC.Chat currentChat = (TLRPC.Chat) infoObject2; if (infoObject instanceof TLRPC.User) { TLRPC.User user = (TLRPC.User) infoObject; - infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipChatUserJoined", R.string.VoipChatUserJoined, UserObject.getFirstName(user))); + if (ChatObject.isChannelOrGiga(currentChat)) { + infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipChannelUserJoined", R.string.VoipChannelUserJoined, UserObject.getFirstName(user))); + } else { + infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipChatUserJoined", R.string.VoipChatUserJoined, UserObject.getFirstName(user))); + } } else { TLRPC.Chat chat = (TLRPC.Chat) infoObject; - infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipChatChatJoined", R.string.VoipChatChatJoined, chat.title)); + if (ChatObject.isChannelOrGiga(currentChat)) { + infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipChannelChatJoined", R.string.VoipChannelChatJoined, chat.title)); + } else { + infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipChatChatJoined", R.string.VoipChatChatJoined, chat.title)); + } } subInfoText = null; icon = 0; @@ -525,7 +542,12 @@ public class UndoView extends FrameLayout { avatarImageView.setForUserOrChat(chat, avatarDrawable); name = chat.title; } - infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipGroupUserChanged", R.string.VoipGroupUserChanged, name)); + TLRPC.Chat currentChat = (TLRPC.Chat) infoObject2; + if (ChatObject.isChannelOrGiga(currentChat)) { + infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipChannelUserChanged", R.string.VoipChannelUserChanged, name)); + } else { + infoText = AndroidUtilities.replaceTags(LocaleController.formatString("VoipGroupUserChanged", R.string.VoipGroupUserChanged, name)); + } subInfoText = null; icon = 0; avatarImageView.setVisibility(VISIBLE); @@ -608,22 +630,32 @@ public class UndoView extends FrameLayout { icon = R.raw.voip_allow_talk; timeLeft = 3000; } else if (action == ACTION_VOIP_SOUND_MUTED) { - infoText = AndroidUtilities.replaceTags(LocaleController.getString("VoipGroupSoundMuted", R.string.VoipGroupSoundMuted)); + TLRPC.Chat chat = (TLRPC.Chat) infoObject; + if (ChatObject.isChannelOrGiga(chat)) { + infoText = AndroidUtilities.replaceTags(LocaleController.getString("VoipChannelSoundMuted", R.string.VoipChannelSoundMuted)); + } else { + infoText = AndroidUtilities.replaceTags(LocaleController.getString("VoipGroupSoundMuted", R.string.VoipGroupSoundMuted)); + } subInfoText = null; icon = R.raw.ic_mute; timeLeft = 3000; } else if (action == ACTION_VOIP_SOUND_UNMUTED) { - infoText = AndroidUtilities.replaceTags(LocaleController.getString("VoipGroupSoundUnmuted", R.string.VoipGroupSoundUnmuted)); + TLRPC.Chat chat = (TLRPC.Chat) infoObject; + if (ChatObject.isChannelOrGiga(chat)) { + infoText = AndroidUtilities.replaceTags(LocaleController.getString("VoipChannelSoundUnmuted", R.string.VoipChannelSoundUnmuted)); + } else { + infoText = AndroidUtilities.replaceTags(LocaleController.getString("VoipGroupSoundUnmuted", R.string.VoipGroupSoundUnmuted)); + } subInfoText = null; icon = R.raw.ic_unmute; timeLeft = 3000; - } else if (currentAction == ACTION_VOIP_RECORDING_STARTED) { - infoText = AndroidUtilities.replaceTags(LocaleController.getString("VoipGroupAudioRecordStarted", R.string.VoipGroupAudioRecordStarted)); + } else if (currentAction == ACTION_VOIP_RECORDING_STARTED || currentAction == ACTION_VOIP_VIDEO_RECORDING_STARTED) { + infoText = AndroidUtilities.replaceTags(currentAction == ACTION_VOIP_RECORDING_STARTED ? LocaleController.getString("VoipGroupAudioRecordStarted", R.string.VoipGroupAudioRecordStarted) : LocaleController.getString("VoipGroupVideoRecordStarted", R.string.VoipGroupVideoRecordStarted)); subInfoText = null; icon = R.raw.voip_record_start; timeLeft = 3000; - } else if (currentAction == ACTION_VOIP_RECORDING_FINISHED) { - String text = LocaleController.getString("VoipGroupAudioRecordSaved", R.string.VoipGroupAudioRecordSaved); + } else if (currentAction == ACTION_VOIP_RECORDING_FINISHED || currentAction == ACTION_VOIP_VIDEO_RECORDING_FINISHED) { + String text = currentAction == ACTION_VOIP_RECORDING_FINISHED ? LocaleController.getString("VoipGroupAudioRecordSaved", R.string.VoipGroupAudioRecordSaved) : LocaleController.getString("VoipGroupVideoRecordSaved", R.string.VoipGroupVideoRecordSaved); subInfoText = null; icon = R.raw.voip_record_saved; timeLeft = 4000; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallRenderersContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallRenderersContainer.java index c04c11199..a36d2da13 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallRenderersContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/GroupCallRenderersContainer.java @@ -29,8 +29,6 @@ import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.RecyclerView; -import com.google.android.exoplayer2.util.Log; - import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ChatObject; import org.telegram.messenger.LocaleController; @@ -39,6 +37,7 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.UserObject; +import org.telegram.messenger.voip.VoIPService; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.BackDrawable; @@ -595,7 +594,14 @@ public class GroupCallRenderersContainer extends FrameLayout { if (replaceFullscreenViewAnimator != null) { replaceFullscreenViewAnimator.cancel(); } + VoIPService service = VoIPService.getSharedInstance(); + if (service != null && fullscreenParticipant != null) { + service.requestFullScreen(fullscreenParticipant.participant, false, fullscreenParticipant.presentation); + } fullscreenParticipant = videoParticipant; + if (service != null && fullscreenParticipant != null) { + service.requestFullScreen(fullscreenParticipant.participant, true, fullscreenParticipant.presentation); + } fullscreenPeerId = peerId; boolean oldInFullscreen = inFullscreenMode; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/PrivateVideoPreviewDialog.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/PrivateVideoPreviewDialog.java index 0921a6727..d5c114d6d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/PrivateVideoPreviewDialog.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/PrivateVideoPreviewDialog.java @@ -59,7 +59,6 @@ public abstract class PrivateVideoPreviewDialog extends FrameLayout implements V private ViewPager viewPager; private TextView positiveButton; - private ActionBar actionBar; private LinearLayout titlesLayout; private RLottieImageView micIconView; private TextView[] titles; @@ -136,7 +135,7 @@ public abstract class PrivateVideoPreviewDialog extends FrameLayout implements V textureView.renderer.setUseCameraRotation(true); addView(textureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - actionBar = new ActionBar(context); + ActionBar actionBar = new ActionBar(context); actionBar.setBackButtonDrawable(new BackDrawable(false)); actionBar.setBackgroundColor(Color.TRANSPARENT); actionBar.setItemsColor(Theme.getColor(Theme.key_voipgroup_actionBarItems), false); @@ -529,7 +528,6 @@ public abstract class PrivateVideoPreviewDialog extends FrameLayout implements V frameLayout.addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 21, 28, 21, 0)); } else { ImageView imageView = new ImageView(getContext()); - imageView.setImageResource(R.drawable.icplaceholder); imageView.setTag(position); Bitmap bitmap = null; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java index 33234ad51..24e6d5312 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPHelper.java @@ -277,8 +277,8 @@ public class VoIPHelper { } if (checkAnonymous && !hasFewPeers && peer instanceof TLRPC.TL_inputPeerUser && ChatObject.shouldSendAnonymously(chat) && (!ChatObject.isChannel(chat) || chat.megagroup)) { new AlertDialog.Builder(activity) - .setTitle(LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat)) - .setMessage(LocaleController.getString("VoipGroupJoinAnonymouseAlert", R.string.VoipGroupJoinAnonymouseAlert)) + .setTitle(ChatObject.isChannelOrGiga(chat) ? LocaleController.getString("VoipChannelVoiceChat", R.string.VoipChannelVoiceChat) : LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat)) + .setMessage(ChatObject.isChannelOrGiga(chat) ? LocaleController.getString("VoipChannelJoinAnonymouseAlert", R.string.VoipChannelJoinAnonymouseAlert) : LocaleController.getString("VoipGroupJoinAnonymouseAlert", R.string.VoipGroupJoinAnonymouseAlert)) .setPositiveButton(LocaleController.getString("VoipChatJoin", R.string.VoipChatJoin), (dialog, which) -> doInitiateCall(user, chat, hash, peer, false, videoCall, canVideoCall, createCall, activity, fragment, accountInstance, false, false)) .setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null) .show(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java index 4f75686f1..c64a16ece 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java @@ -77,6 +77,8 @@ import androidx.recyclerview.widget.LinearSmoothScrollerCustom; import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager.widget.ViewPager; +import com.google.android.exoplayer2.util.Log; + import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; @@ -425,6 +427,12 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. private Paint windowBackgroundPaint = new Paint(); private int inputFieldHeight; + @Override + public void setTranslationX(float translationX) { + Log.d("kek", "content view set translationX" + translationX); + super.setTranslationX(translationX); + } + public ContentView(Context context) { super(context); } @@ -4138,9 +4146,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. if (searchIsShowed) { AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); } - if (viewPages != null) { - viewPages[0].dialogsAdapter.notifyDataSetChanged(); - } + updateVisibleRows(0, false); } @Override @@ -4697,13 +4703,21 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. long dialogId = 0; int message_id = 0; boolean isGlobalSearch = false; + int folderId = 0; + int filterId = 0; if (adapter instanceof DialogsAdapter) { DialogsAdapter dialogsAdapter = (DialogsAdapter) adapter; + int dialogsType = dialogsAdapter.getDialogsType(); + if (dialogsType == 7 || dialogsType == 8) { + MessagesController.DialogFilter dialogFilter = getMessagesController().selectedDialogFilter[dialogsType == 7 ? 0 : 1]; + filterId = dialogFilter.id; + } TLObject object = dialogsAdapter.getItem(position); if (object instanceof TLRPC.User) { dialogId = ((TLRPC.User) object).id; } else if (object instanceof TLRPC.Dialog) { TLRPC.Dialog dialog = (TLRPC.Dialog) object; + folderId = dialog.folder_id; if (dialog instanceof TLRPC.TL_dialogFolder) { if (actionBar.isActionModeShowed(null)) { return; @@ -4846,6 +4860,8 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. searchObject = null; } } + args.putInt("dialog_folder_id", folderId); + args.putInt("dialog_filter_id", filterId); if (AndroidUtilities.isTablet()) { if (openedDialogId == dialogId && adapter != searchViewPager.dialogsSearchAdapter) { return; @@ -5346,27 +5362,29 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } } builder.setPositiveButton(action == delete ? LocaleController.getString("Delete", R.string.Delete) - : canClearCacheCount != 0 ? LocaleController.getString("ClearHistoryCache", R.string.ClearHistoryCache) - : LocaleController.getString("ClearHistory", R.string.ClearHistory) - , (dialog1, which) -> { - ArrayList didsCopy = new ArrayList<>(selectedDialogs); - getUndoView().showWithAction(didsCopy, action == delete ? UndoView.ACTION_DELETE_FEW : UndoView.ACTION_CLEAR_FEW, null, null, () -> { - if (action == delete) { - getMessagesController().setDialogsInTransaction(true); - performSelectedDialogsAction(didsCopy, action, false); - getMessagesController().setDialogsInTransaction(false); - getMessagesController().checkIfFolderEmpty(folderId); - if (folderId != 0 && getDialogsArray(currentAccount, viewPages[0].dialogsType, folderId, false).size() == 0) { - viewPages[0].listView.setEmptyView(null); - viewPages[0].progressView.setVisibility(View.INVISIBLE); - finishFragment(); - } - } else { - performSelectedDialogsAction(didsCopy, action, false); - } - }, null); - hideActionMode(action == clear); - }); + : canClearCacheCount != 0 ? LocaleController.getString("ClearHistoryCache", R.string.ClearHistoryCache) + : LocaleController.getString("ClearHistory", R.string.ClearHistory), (dialog1, which) -> { + if (selectedDialogs.isEmpty()) { + return; + } + ArrayList didsCopy = new ArrayList<>(selectedDialogs); + getUndoView().showWithAction(didsCopy, action == delete ? UndoView.ACTION_DELETE_FEW : UndoView.ACTION_CLEAR_FEW, null, null, () -> { + if (action == delete) { + getMessagesController().setDialogsInTransaction(true); + performSelectedDialogsAction(didsCopy, action, false); + getMessagesController().setDialogsInTransaction(false); + getMessagesController().checkIfFolderEmpty(folderId); + if (folderId != 0 && getDialogsArray(currentAccount, viewPages[0].dialogsType, folderId, false).size() == 0) { + viewPages[0].listView.setEmptyView(null); + viewPages[0].progressView.setVisibility(View.INVISIBLE); + finishFragment(); + } + } else { + performSelectedDialogsAction(didsCopy, action, false); + } + }, null); + hideActionMode(action == clear); + }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); AlertDialog alertDialog = builder.create(); showDialog(alertDialog); @@ -6732,6 +6750,9 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. } private void updateVisibleRows(int mask) { + updateVisibleRows(mask, true); + } + private void updateVisibleRows(int mask, boolean animated) { if ((dialogsListFrozen && (mask & MessagesController.UPDATE_MASK_REORDER) == 0) || isPaused) { return; } @@ -6775,7 +6796,7 @@ public class DialogsActivity extends BaseFragment implements NotificationCenter. cell.setDialogSelected(cell.getDialogId() == openedDialogId); } } else { - cell.update(mask); + cell.update(mask, animated); } if (selectedDialogs != null) { cell.setChecked(selectedDialogs.contains(cell.getDialogId()), false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java index 2f768977b..807e9bd67 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java @@ -121,6 +121,7 @@ import org.telegram.ui.Components.EditTextBoldCursor; import org.telegram.ui.Components.FillLastGridLayoutManager; import org.telegram.ui.Components.GroupCallFullscreenAdapter; import org.telegram.ui.Components.GroupCallPip; +import org.telegram.ui.Components.GroupCallRecordAlert; import org.telegram.ui.Components.GroupVoipInviteAlert; import org.telegram.ui.Components.HintView; import org.telegram.ui.Components.ImageUpdater; @@ -223,7 +224,6 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter private HintView recordHintView; private HintView reminderHintView; private int buttonsVisibility; - private TextView speakingMembersSubtitle; public final ArrayList visibleVideoParticipants = new ArrayList<>(); @@ -1144,12 +1144,12 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter if (justJoinedId > 0) { TLRPC.User user = accountInstance.getMessagesController().getUser((int) justJoinedId); if (call.call.participants_count < 250 || UserObject.isContact(user)) { - getUndoView().showWithAction(0, UndoView.ACTION_VOIP_USER_JOINED, user); + getUndoView().showWithAction(0, UndoView.ACTION_VOIP_USER_JOINED, user, currentChat, null, null); } } else { TLRPC.Chat chat = accountInstance.getMessagesController().getChat((int) -justJoinedId); if (call.call.participants_count < 250 || !ChatObject.isNotInChat(chat)) { - getUndoView().showWithAction(0, UndoView.ACTION_VOIP_USER_JOINED, chat); + getUndoView().showWithAction(0, UndoView.ACTION_VOIP_USER_JOINED, chat, currentChat, null, null); } } } @@ -1187,9 +1187,17 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter String text = (String) args[1]; String error; if ("GROUPCALL_PARTICIPANTS_TOO_MUCH".equals(text)) { - error = LocaleController.getString("VoipGroupTooMuch", R.string.VoipGroupTooMuch); + if (ChatObject.isChannelOrGiga(currentChat)) { + error = LocaleController.getString("VoipChannelTooMuch", R.string.VoipChannelTooMuch); + } else { + error = LocaleController.getString("VoipGroupTooMuch", R.string.VoipGroupTooMuch); + } } else if ("ANONYMOUS_CALLS_DISABLED".equals(text) || "GROUPCALL_ANONYMOUS_FORBIDDEN".equals(text)) { - error = LocaleController.getString("VoipGroupJoinAnonymousAdmin", R.string.VoipGroupJoinAnonymousAdmin); + if (ChatObject.isChannelOrGiga(currentChat)) { + error = LocaleController.getString("VoipChannelJoinAnonymousAdmin", R.string.VoipChannelJoinAnonymousAdmin); + } else { + error = LocaleController.getString("VoipGroupJoinAnonymousAdmin", R.string.VoipGroupJoinAnonymousAdmin); + } } else { error = LocaleController.getString("ErrorOccurred", R.string.ErrorOccurred) + "\n" + text; } @@ -1481,7 +1489,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter if (newChat != null) { currentChat = newChat; } - if (ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_INVITE) || (!ChatObject.isChannel(currentChat) || currentChat.megagroup) && ChatObject.canWriteToChat(currentChat) || ChatObject.isChannel(currentChat) && !currentChat.megagroup && !TextUtils.isEmpty(currentChat.username)) { + if (ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_INVITE) || (!ChatObject.isChannel(currentChat) || currentChat.megagroup) && (!TextUtils.isEmpty(currentChat.username) || ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_INVITE)) || ChatObject.isChannel(currentChat) && !currentChat.megagroup && !TextUtils.isEmpty(currentChat.username)) { inviteItem.setVisibility(View.VISIBLE); } else { inviteItem.setVisibility(View.GONE); @@ -1537,7 +1545,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter } else { boolean mutedByAdmin = participant != null && !participant.can_self_unmute && participant.muted && !ChatObject.canManageCalls(currentChat); boolean sharingScreen = VoIPService.getSharedInstance() != null && VoIPService.getSharedInstance().getVideoState(true) == Instance.VIDEO_STATE_ACTIVE; - if (Build.VERSION.SDK_INT >= 21 && !mutedByAdmin && (call.canRecordVideo() || sharingScreen)) { + if (Build.VERSION.SDK_INT >= 21 && !mutedByAdmin && (call.canRecordVideo() || sharingScreen) && !call.isScheduled()) { if (sharingScreen) { screenShareItem.setVisibility(View.GONE); screenItem.setVisibility(View.VISIBLE); @@ -1780,8 +1788,13 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter } else if (id == leave_item) { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setTitle(LocaleController.getString("VoipGroupEndAlertTitle", R.string.VoipGroupEndAlertTitle)); - builder.setMessage(LocaleController.getString("VoipGroupEndAlertText", R.string.VoipGroupEndAlertText)); + if (ChatObject.isChannelOrGiga(currentChat)) { + builder.setTitle(LocaleController.getString("VoipChannelEndAlertTitle", R.string.VoipChannelEndAlertTitle)); + builder.setMessage(LocaleController.getString("VoipChannelEndAlertText", R.string.VoipChannelEndAlertText)); + } else { + builder.setTitle(LocaleController.getString("VoipGroupEndAlertTitle", R.string.VoipGroupEndAlertTitle)); + builder.setMessage(LocaleController.getString("VoipGroupEndAlertText", R.string.VoipGroupEndAlertText)); + } builder.setDialogButtonColorKey(Theme.key_voipgroup_listeningText); builder.setPositiveButton(LocaleController.getString("VoipGroupEnd", R.string.VoipGroupEnd), (dialogInterface, i) -> { @@ -1818,74 +1831,99 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter dialog.setTextColor(Theme.getColor(Theme.key_voipgroup_actionBarItems)); } else if (id == screen_capture_item) { screenShareItem.callOnClick(); - } else if (id == start_record_item) { - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); - builder.setDialogButtonColorKey(Theme.key_voipgroup_listeningText); - EditTextBoldCursor editText; + } else if (id == start_record_item) { if (call.recording) { + boolean video = call.call.record_video_active; + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setDialogButtonColorKey(Theme.key_voipgroup_listeningText); builder.setTitle(LocaleController.getString("VoipGroupStopRecordingTitle", R.string.VoipGroupStopRecordingTitle)); - builder.setMessage(LocaleController.getString("VoipGroupStopRecordingText", R.string.VoipGroupStopRecordingText)); - editText = null; - } else { - enterEventSent = false; - builder.setTitle(LocaleController.getString("VoipGroupStartRecordingTitle", R.string.VoipGroupStartRecordingTitle)); - builder.setMessage(LocaleController.getString("VoipGroupStartRecordingText", R.string.VoipGroupStartRecordingText)); - builder.setCheckFocusable(false); - - editText = new EditTextBoldCursor(getContext()); - editText.setBackgroundDrawable(Theme.createEditTextDrawable(getContext(), true)); - - LinearLayout linearLayout = new LinearLayout(getContext()); - linearLayout.setOrientation(LinearLayout.VERTICAL); - builder.setView(linearLayout); - - editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - editText.setTextColor(Theme.getColor(Theme.key_voipgroup_nameText)); - editText.setMaxLines(1); - editText.setLines(1); - editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); - editText.setGravity(Gravity.LEFT | Gravity.TOP); - editText.setSingleLine(true); - editText.setHint(LocaleController.getString("VoipGroupSaveFileHint", R.string.VoipGroupSaveFileHint)); - editText.setImeOptions(EditorInfo.IME_ACTION_DONE); - editText.setHintTextColor(Theme.getColor(Theme.key_voipgroup_lastSeenText)); - editText.setCursorColor(Theme.getColor(Theme.key_voipgroup_nameText)); - editText.setCursorSize(AndroidUtilities.dp(20)); - editText.setCursorWidth(1.5f); - editText.setPadding(0, AndroidUtilities.dp(4), 0, 0); - linearLayout.addView(editText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, Gravity.TOP | Gravity.LEFT, 24, 0, 24, 12)); - editText.setOnEditorActionListener((textView, i2, keyEvent) -> { - AndroidUtilities.hideKeyboard(textView); - builder.create().getButton(AlertDialog.BUTTON_POSITIVE).callOnClick(); - return false; - }); - - final AlertDialog alertDialog = builder.create(); - alertDialog.setBackgroundColor(Theme.getColor(Theme.key_voipgroup_inviteMembersBackground)); - alertDialog.setOnShowListener(dialog -> makeFocusable(null, alertDialog, editText, true)); - alertDialog.setOnDismissListener(dialog -> AndroidUtilities.hideKeyboard(editText)); - } - - builder.setPositiveButton(call.recording ? LocaleController.getString("Stop", R.string.Stop) : LocaleController.getString("Start", R.string.Start), (dialogInterface, i) -> { - if (editText == null) { - call.toggleRecord(null); - getUndoView().showWithAction(0, UndoView.ACTION_VOIP_RECORDING_FINISHED, null); + if (ChatObject.isChannelOrGiga(currentChat)) { + builder.setMessage(LocaleController.getString("VoipChannelStopRecordingText", R.string.VoipChannelStopRecordingText)); } else { - call.toggleRecord(editText.getText().toString()); - AndroidUtilities.hideKeyboard(editText); - getUndoView().showWithAction(0, UndoView.ACTION_VOIP_RECORDING_STARTED, null); - if (VoIPService.getSharedInstance() != null) { - VoIPService.getSharedInstance().playStartRecordSound(); - } + builder.setMessage(LocaleController.getString("VoipGroupStopRecordingText", R.string.VoipGroupStopRecordingText)); } - }); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), (dialog, which) -> AndroidUtilities.hideKeyboard(editText)); - AlertDialog dialog = builder.create(); - dialog.setBackgroundColor(Theme.getColor(Theme.key_voipgroup_dialogBackground)); - dialog.show(); - dialog.setTextColor(Theme.getColor(Theme.key_voipgroup_nameText)); - if (editText != null) { - editText.requestFocus(); + builder.setPositiveButton(LocaleController.getString("Stop", R.string.Stop), (dialogInterface, i) -> { + call.toggleRecord(null, 0); + getUndoView().showWithAction(0, video ? UndoView.ACTION_VOIP_VIDEO_RECORDING_FINISHED : UndoView.ACTION_VOIP_RECORDING_FINISHED, null); + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + AlertDialog dialog = builder.create(); + dialog.setBackgroundColor(Theme.getColor(Theme.key_voipgroup_dialogBackground)); + dialog.show(); + dialog.setTextColor(Theme.getColor(Theme.key_voipgroup_nameText)); + } else { + /*GroupCallRecordAlert alert = new GroupCallRecordAlert(getContext(), currentChat) { TODO uncomment later + @Override + protected void onStartRecord(int type) {*/ + int type = 0; //TODO remove after uncomment + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setDialogButtonColorKey(Theme.key_voipgroup_listeningText); + + enterEventSent = false; + builder.setTitle(LocaleController.getString("VoipGroupStartRecordingTitle", R.string.VoipGroupStartRecordingTitle)); + if (type == 0) { + builder.setMessage(LocaleController.getString("VoipGroupStartRecordingText", R.string.VoipGroupStartRecordingText)); + } else { + if (ChatObject.isChannelOrGiga(currentChat)) { + builder.setMessage(LocaleController.getString("VoipChannelStartRecordingVideoText", R.string.VoipChannelStartRecordingVideoText)); + } else { + builder.setMessage(LocaleController.getString("VoipGroupStartRecordingVideoText", R.string.VoipGroupStartRecordingVideoText)); + } + } + builder.setCheckFocusable(false); + + EditTextBoldCursor editText = new EditTextBoldCursor(getContext()); + editText.setBackgroundDrawable(Theme.createEditTextDrawable(getContext(), true)); + + LinearLayout linearLayout = new LinearLayout(getContext()); + linearLayout.setOrientation(LinearLayout.VERTICAL); + builder.setView(linearLayout); + + editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + editText.setTextColor(Theme.getColor(Theme.key_voipgroup_nameText)); + editText.setMaxLines(1); + editText.setLines(1); + editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES); + editText.setGravity(Gravity.LEFT | Gravity.TOP); + editText.setSingleLine(true); + editText.setHint(LocaleController.getString("VoipGroupSaveFileHint", R.string.VoipGroupSaveFileHint)); + editText.setImeOptions(EditorInfo.IME_ACTION_DONE); + editText.setHintTextColor(Theme.getColor(Theme.key_voipgroup_lastSeenText)); + editText.setCursorColor(Theme.getColor(Theme.key_voipgroup_nameText)); + editText.setCursorSize(AndroidUtilities.dp(20)); + editText.setCursorWidth(1.5f); + editText.setPadding(0, AndroidUtilities.dp(4), 0, 0); + linearLayout.addView(editText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36, Gravity.TOP | Gravity.LEFT, 24, 0, 24, 12)); + editText.setOnEditorActionListener((textView, i2, keyEvent) -> { + AndroidUtilities.hideKeyboard(textView); + builder.create().getButton(AlertDialog.BUTTON_POSITIVE).callOnClick(); + return false; + }); + + final AlertDialog alertDialog = builder.create(); + alertDialog.setBackgroundColor(Theme.getColor(Theme.key_voipgroup_inviteMembersBackground)); + alertDialog.setOnShowListener(dialog -> makeFocusable(null, alertDialog, editText, true)); + alertDialog.setOnDismissListener(dialog -> AndroidUtilities.hideKeyboard(editText)); + + builder.setPositiveButton(LocaleController.getString("Start", R.string.Start), (dialogInterface, i) -> { + call.toggleRecord(editText.getText().toString(), type); + AndroidUtilities.hideKeyboard(editText); + getUndoView().showWithAction(0, type == 0 ? UndoView.ACTION_VOIP_RECORDING_STARTED : UndoView.ACTION_VOIP_VIDEO_RECORDING_STARTED, null); + if (VoIPService.getSharedInstance() != null) { + VoIPService.getSharedInstance().playStartRecordSound(); + } + }); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), (dialog, which) -> AndroidUtilities.hideKeyboard(editText)); + AlertDialog dialog = builder.create(); + dialog.setBackgroundColor(Theme.getColor(Theme.key_voipgroup_dialogBackground)); + dialog.show(); + dialog.setTextColor(Theme.getColor(Theme.key_voipgroup_nameText)); + if (editText != null) { + editText.requestFocus(); + } + /*} + }; TODO uncomment later + alert.show();*/ } } else if (id == permission_item) { changingPermissions = true; @@ -1910,7 +1948,11 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setDialogButtonColorKey(Theme.key_voipgroup_listeningText); - builder.setTitle(LocaleController.getString("VoipGroupTitle", R.string.VoipGroupTitle)); + if (ChatObject.isChannelOrGiga(currentChat)) { + builder.setTitle(LocaleController.getString("VoipChannelTitle", R.string.VoipChannelTitle)); + } else { + builder.setTitle(LocaleController.getString("VoipGroupTitle", R.string.VoipGroupTitle)); + } builder.setCheckFocusable(false); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), (dialog, which) -> AndroidUtilities.hideKeyboard(editText)); @@ -1997,7 +2039,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter object = accountInstance.getMessagesController().getChat(peer1.channel_id); } if (call.isScheduled()) { - getUndoView().showWithAction(0, UndoView.ACTION_VOIP_USER_CHANGED, object); + getUndoView().showWithAction(0, UndoView.ACTION_VOIP_USER_CHANGED, object, currentChat, null, null); if (peer1 instanceof TLRPC.TL_inputPeerChannel) { selfPeer = new TLRPC.TL_peerChannel(); selfPeer.channel_id = peer1.channel_id; @@ -2529,7 +2571,6 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter shadowDrawable.setBounds(0, (int) top, getMeasuredWidth(), height); shadowDrawable.draw(canvas); - if (rad != 1.0f) { Theme.dialogs_onlineCirclePaint.setColor(backgroundColor); rect.set(backgroundPaddingLeft, backgroundPaddingTop + top, getMeasuredWidth() - backgroundPaddingLeft, backgroundPaddingTop + top + AndroidUtilities.dp(24)); @@ -4328,14 +4369,14 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter soundItemDivider = otherItem.addDivider(ColorUtils.blendARGB(Theme.getColor(Theme.key_voipgroup_actionBar), Color.BLACK, 0.3f)); ((ViewGroup.MarginLayoutParams) soundItemDivider.getLayoutParams()).topMargin = 0; ((ViewGroup.MarginLayoutParams) soundItemDivider.getLayoutParams()).bottomMargin = 0; - editTitleItem = otherItem.addSubItem(edit_item, R.drawable.msg_edit, recordCallDrawable, LocaleController.getString("VoipGroupEditTitle", R.string.VoipGroupEditTitle), true, false); + editTitleItem = otherItem.addSubItem(edit_item, R.drawable.msg_edit, recordCallDrawable, ChatObject.isChannelOrGiga(currentChat) ? LocaleController.getString("VoipChannelEditTitle", R.string.VoipChannelEditTitle) : LocaleController.getString("VoipGroupEditTitle", R.string.VoipGroupEditTitle), true, false); permissionItem = otherItem.addSubItem(permission_item, R.drawable.msg_permissions, recordCallDrawable, LocaleController.getString("VoipGroupEditPermissions", R.string.VoipGroupEditPermissions), false, false); inviteItem = otherItem.addSubItem(share_invite_link_item, R.drawable.msg_link, LocaleController.getString("VoipGroupShareInviteLink", R.string.VoipGroupShareInviteLink)); recordCallDrawable = new RecordCallDrawable(); screenItem = otherItem.addSubItem(screen_capture_item, R.drawable.msg_screencast, LocaleController.getString("VoipChatStartScreenCapture", R.string.VoipChatStartScreenCapture)); recordItem = otherItem.addSubItem(start_record_item, 0, recordCallDrawable, LocaleController.getString("VoipGroupRecordCall", R.string.VoipGroupRecordCall), true, false); recordCallDrawable.setParentView(recordItem.getImageView()); - leaveItem = otherItem.addSubItem(leave_item, R.drawable.msg_endcall, LocaleController.getString("VoipGroupEndChat", R.string.VoipGroupEndChat)); + leaveItem = otherItem.addSubItem(leave_item, R.drawable.msg_endcall, ChatObject.isChannelOrGiga(currentChat) ? LocaleController.getString("VoipChannelEndChat", R.string.VoipChannelEndChat) : LocaleController.getString("VoipGroupEndChat", R.string.VoipGroupEndChat)); otherItem.setPopupItemsSelectorColor(Theme.getColor(Theme.key_voipgroup_listSelector)); otherItem.getPopupLayout().setFitItems(true); @@ -4778,7 +4819,11 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter } }); scheduleAnimator.start(); - titleTextView.setText(LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat), true); + if (ChatObject.isChannelOrGiga(currentChat)) { + titleTextView.setText(LocaleController.getString("VoipChannelVoiceChat", R.string.VoipChannelVoiceChat), true); + } else { + titleTextView.setText(LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat), true); + } Calendar calendar = Calendar.getInstance(); boolean setSeconds = AlertsCreator.checkScheduleDate(null, null, 7 * 24 * 60 * 60, 3, dayPicker, hourPicker, minutePicker); calendar.setTimeInMillis(System.currentTimeMillis() + (long) dayPicker.getValue() * 24 * 3600 * 1000); @@ -5231,7 +5276,11 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter } else { listView.setVisibility(View.VISIBLE); } - leaveItem.setText(LocaleController.getString("VoipGroupCancelChat", R.string.VoipGroupCancelChat)); + if (ChatObject.isChannelOrGiga(currentChat)) { + leaveItem.setText(LocaleController.getString("VoipChannelCancelChat", R.string.VoipChannelCancelChat)); + } else { + leaveItem.setText(LocaleController.getString("VoipGroupCancelChat", R.string.VoipGroupCancelChat)); + } } float scheduleButtonsScale2; float alpha; @@ -5308,7 +5357,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter if (scheduleTimeTextView != null && scheduleTimeTextView.getVisibility() == View.VISIBLE) { leaveButton.setData(R.drawable.calls_decline, 0xffffffff, Theme.getColor(Theme.key_voipgroup_leaveButton), 0.3f, false, LocaleController.getString("VoipGroupLeave", R.string.VoipGroupLeave), false, true); updateSpeakerPhoneIcon(true); - leaveItem.setText(LocaleController.getString("VoipGroupEndChat", R.string.VoipGroupEndChat)); + leaveItem.setText(ChatObject.isChannelOrGiga(currentChat) ? LocaleController.getString("VoipChannelEndChat", R.string.VoipChannelEndChat) : LocaleController.getString("VoipGroupEndChat", R.string.VoipGroupEndChat)); listView.setVisibility(View.VISIBLE); pipItem.setVisibility(View.VISIBLE); AnimatorSet animatorSet = new AnimatorSet(); @@ -5494,7 +5543,11 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter private void updateTitle(boolean animated) { if (call == null) { - titleTextView.setText(LocaleController.getString("VoipGroupScheduleVoiceChat", R.string.VoipGroupScheduleVoiceChat), animated); + if (ChatObject.isChannelOrGiga(currentChat)) { + titleTextView.setText(LocaleController.getString("VoipChannelScheduleVoiceChat", R.string.VoipChannelScheduleVoiceChat), animated); + } else { + titleTextView.setText(LocaleController.getString("VoipGroupScheduleVoiceChat", R.string.VoipGroupScheduleVoiceChat), animated); + } return; } if (!TextUtils.isEmpty(call.call.title)) { @@ -5523,7 +5576,11 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter } else { actionBar.setTitle(currentChat.title); } - titleTextView.setText(LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat), animated); + if (ChatObject.isChannelOrGiga(currentChat)) { + titleTextView.setText(LocaleController.getString("VoipChannelVoiceChat", R.string.VoipChannelVoiceChat), animated); + } else { + titleTextView.setText(LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat), animated); + } } } SimpleTextView textView = actionBar.getTitleTextView(); @@ -5682,7 +5739,11 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter String message; if (urlMuted == null && withMessage) { - message = LocaleController.formatString("VoipGroupInviteText", R.string.VoipGroupInviteText, urlUnmuted); + if (ChatObject.isChannelOrGiga(currentChat)) { + message = LocaleController.formatString("VoipChannelInviteText", R.string.VoipChannelInviteText, urlUnmuted); + } else { + message = LocaleController.formatString("VoipGroupInviteText", R.string.VoipGroupInviteText, urlUnmuted); + } } else { message = urlUnmuted; } @@ -5743,7 +5804,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter } progressDialog[0] = null; - getUndoView().showWithAction(0, UndoView.ACTION_VOIP_INVITED, user, null, null, null); + getUndoView().showWithAction(0, UndoView.ACTION_VOIP_INVITED, user, currentChat, null, null); } }); } else { @@ -5926,7 +5987,7 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter updateMuteButton(MUTE_BUTTON_STATE_CONNECTING, animated); } else { if (userSwitchObject != null) { - getUndoView().showWithAction(0, UndoView.ACTION_VOIP_USER_CHANGED, userSwitchObject); + getUndoView().showWithAction(0, UndoView.ACTION_VOIP_USER_CHANGED, userSwitchObject, currentChat, null, null); userSwitchObject = null; } TLRPC.TL_groupCallParticipant participant = call.participants.get(MessageObject.getPeerId(selfPeer)); @@ -6345,7 +6406,11 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter recordHintView.setVisibility(View.INVISIBLE); recordHintView.setShowingDuration(3000); containerView.addView(recordHintView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 19, 0, 19, 0)); - recordHintView.setText(LocaleController.getString("VoipGroupRecording", R.string.VoipGroupRecording)); + if (ChatObject.isChannelOrGiga(currentChat)) { + recordHintView.setText(LocaleController.getString("VoipChannelRecording", R.string.VoipChannelRecording)); + } else { + recordHintView.setText(LocaleController.getString("VoipGroupRecording", R.string.VoipGroupRecording)); + } recordHintView.setBackgroundColor(0xea272f38, 0xffffffff); } recordHintView.setExtraTranslationY(-AndroidUtilities.statusBarHeight); @@ -6459,8 +6524,13 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter } AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(LocaleController.getString("VoipGroupLeaveAlertTitle", R.string.VoipGroupLeaveAlertTitle)); - builder.setMessage(LocaleController.getString("VoipGroupLeaveAlertText", R.string.VoipGroupLeaveAlertText)); + if (ChatObject.isChannelOrGiga(currentChat)) { + builder.setTitle(LocaleController.getString("VoipChannelLeaveAlertTitle", R.string.VoipChannelLeaveAlertTitle)); + builder.setMessage(LocaleController.getString("VoipChannelLeaveAlertText", R.string.VoipChannelLeaveAlertText)); + } else { + builder.setTitle(LocaleController.getString("VoipGroupLeaveAlertTitle", R.string.VoipGroupLeaveAlertTitle)); + builder.setMessage(LocaleController.getString("VoipGroupLeaveAlertText", R.string.VoipGroupLeaveAlertText)); + } int currentAccount = service.getAccount(); @@ -6479,7 +6549,11 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter checkBox.setColors(Theme.key_voipgroup_mutedIcon, Theme.key_voipgroup_listeningText, Theme.key_voipgroup_nameText); } cells[0].setTag(0); - cells[0].setText(LocaleController.getString("VoipGroupLeaveAlertEndChat", R.string.VoipGroupLeaveAlertEndChat), "", false, false); + if (ChatObject.isChannelOrGiga(currentChat)) { + cells[0].setText(LocaleController.getString("VoipChannelLeaveAlertEndChat", R.string.VoipChannelLeaveAlertEndChat), "", false, false); + } else { + cells[0].setText(LocaleController.getString("VoipGroupLeaveAlertEndChat", R.string.VoipGroupLeaveAlertEndChat), "", false, false); + } cells[0].setPadding(LocaleController.isRTL ? AndroidUtilities.dp(16) : AndroidUtilities.dp(8), 0, LocaleController.isRTL ? AndroidUtilities.dp(8) : AndroidUtilities.dp(16), 0); linearLayout.addView(cells[0], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); @@ -6591,7 +6665,11 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter textView.setEllipsize(TextUtils.TruncateAt.END); if (option == 2) { textView.setText(LocaleController.getString("VoipGroupRemoveMemberAlertTitle2", R.string.VoipGroupRemoveMemberAlertTitle2)); - messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("VoipGroupRemoveMemberAlertText2", R.string.VoipGroupRemoveMemberAlertText2, name, currentChat.title))); + if (ChatObject.isChannelOrGiga(currentChat)) { + messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("VoipChannelRemoveMemberAlertText2", R.string.VoipChannelRemoveMemberAlertText2, name, currentChat.title))); + } else { + messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("VoipGroupRemoveMemberAlertText2", R.string.VoipGroupRemoveMemberAlertText2, name, currentChat.title))); + } } else { textView.setText(LocaleController.getString("VoipGroupAddMemberTitle", R.string.VoipGroupAddMemberTitle)); messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("VoipGroupAddMemberText", R.string.VoipGroupAddMemberText, name, currentChat.title))); @@ -6757,11 +6835,6 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter return false; } - -// if (isLandscapeMode) { -// scrimRenderer = null; -// } - boolean showWithAvatarPreview = !isLandscapeMode && !isTabletMode && !AndroidUtilities.isInMultiwindow; TLRPC.TL_groupCallParticipant participant = view.getParticipant(); @@ -7695,7 +7768,11 @@ public class GroupCallActivity extends BottomSheet implements NotificationCenter textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); textView.setGravity(Gravity.CENTER_HORIZONTAL); textView.setPadding(0, 0, 0, AndroidUtilities.dp(10)); - textView.setText(LocaleController.formatString("VoipVideoNotAvailableAdmin", R.string.VoipVideoNotAvailableAdmin, LocaleController.formatPluralString("Members", accountInstance.getMessagesController().groipCallVideoMaxParticipants))); + if (ChatObject.isChannelOrGiga(currentChat)) { + textView.setText(LocaleController.formatString("VoipChannelVideoNotAvailableAdmin", R.string.VoipChannelVideoNotAvailableAdmin, LocaleController.formatPluralString("Participants", accountInstance.getMessagesController().groipCallVideoMaxParticipants))); + } else { + textView.setText(LocaleController.formatString("VoipVideoNotAvailableAdmin", R.string.VoipVideoNotAvailableAdmin, LocaleController.formatPluralString("Members", accountInstance.getMessagesController().groipCallVideoMaxParticipants))); + } view = textView; break; case 3: diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java index efc3e1523..bba4a4d89 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateFinalActivity.java @@ -461,8 +461,12 @@ public class GroupCreateFinalActivity extends BaseFragment implements Notificati avatarEditor.setAnimation(cameraDrawable); cameraDrawable.setCurrentFrame(0); }, dialog -> { - cameraDrawable.setCustomEndFrame(86); - avatarEditor.playAnimation(); + if (!imageUpdater.isUploadingImage()) { + cameraDrawable.setCustomEndFrame(86); + avatarEditor.playAnimation(); + } else { + cameraDrawable.setCurrentFrame(0, false); + } }); cameraDrawable.setCurrentFrame(0); cameraDrawable.setCustomEndFrame(43); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index f60521203..93ae99e48 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -1577,13 +1577,15 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa wallPaper = new TLRPC.TL_wallPaper(); wallPaper.settings = new TLRPC.TL_wallPaperSettings(); wallPaper.slug = path.replace("bg/", ""); + boolean ok = false; if (wallPaper.slug != null && wallPaper.slug.length() == 6) { try { wallPaper.settings.background_color = Integer.parseInt(wallPaper.slug, 16) | 0xff000000; + wallPaper.slug = null; + ok = true; } catch (Exception ignore) { } - wallPaper.slug = null; } else if (wallPaper.slug != null && wallPaper.slug.length() >= 13 && AndroidUtilities.isValidWallChar(wallPaper.slug.charAt(6))) { try { wallPaper.settings.background_color = Integer.parseInt(wallPaper.slug.substring(0, 6), 16) | 0xff000000; @@ -1594,19 +1596,21 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa if (wallPaper.slug.length() == 27 && AndroidUtilities.isValidWallChar(wallPaper.slug.charAt(20))) { wallPaper.settings.fourth_background_color = Integer.parseInt(wallPaper.slug.substring(21), 16) | 0xff000000; } - } catch (Exception ignore) { + try { + String rotation = data.getQueryParameter("rotation"); + if (!TextUtils.isEmpty(rotation)) { + wallPaper.settings.rotation = Utilities.parseInt(rotation); + } + } catch (Exception ignore) { - } - try { - String rotation = data.getQueryParameter("rotation"); - if (!TextUtils.isEmpty(rotation)) { - wallPaper.settings.rotation = Utilities.parseInt(rotation); } + wallPaper.slug = null; + ok = true; } catch (Exception ignore) { } - wallPaper.slug = null; - } else { + } + if (!ok) { String mode = data.getQueryParameter("mode"); if (mode != null) { mode = mode.toLowerCase(); @@ -1802,13 +1806,15 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa if (wallPaper.slug == null) { wallPaper.slug = data.getQueryParameter("color"); } + boolean ok = false; if (wallPaper.slug != null && wallPaper.slug.length() == 6) { try { wallPaper.settings.background_color = Integer.parseInt(wallPaper.slug, 16) | 0xff000000; + wallPaper.slug = null; + ok = true; } catch (Exception ignore) { } - wallPaper.slug = null; } else if (wallPaper.slug != null && wallPaper.slug.length() >= 13 && AndroidUtilities.isValidWallChar(wallPaper.slug.charAt(6))) { try { wallPaper.settings.background_color = Integer.parseInt(wallPaper.slug.substring(0, 6), 16) | 0xff000000; @@ -1819,19 +1825,21 @@ public class LaunchActivity extends Activity implements ActionBarLayout.ActionBa if (wallPaper.slug.length() == 27 && AndroidUtilities.isValidWallChar(wallPaper.slug.charAt(20))) { wallPaper.settings.fourth_background_color = Integer.parseInt(wallPaper.slug.substring(21), 16) | 0xff000000; } - } catch (Exception ignore) { + try { + String rotation = data.getQueryParameter("rotation"); + if (!TextUtils.isEmpty(rotation)) { + wallPaper.settings.rotation = Utilities.parseInt(rotation); + } + } catch (Exception ignore) { - } - try { - String rotation = data.getQueryParameter("rotation"); - if (!TextUtils.isEmpty(rotation)) { - wallPaper.settings.rotation = Utilities.parseInt(rotation); } + wallPaper.slug = null; + ok = true; } catch (Exception ignore) { } - wallPaper.slug = null; - } else { + } + if (!ok) { String mode = data.getQueryParameter("mode"); if (mode != null) { mode = mode.toLowerCase(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java index 7af7ce361..e587cae32 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java @@ -2021,7 +2021,7 @@ public class LoginActivity extends BaseFragment { Intent mailer = new Intent(Intent.ACTION_SENDTO); mailer.setData(Uri.parse("mailto:")); - mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"sms@stel.com"}); + mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"reports@stel.com"}); mailer.putExtra(Intent.EXTRA_SUBJECT, "Android registration/login issue " + version + " " + emailPhone); mailer.putExtra(Intent.EXTRA_TEXT, "Phone: " + requestPhone + "\nApp version: " + version + "\nOS version: SDK " + Build.VERSION.SDK_INT + "\nDevice Name: " + Build.MANUFACTURER + Build.MODEL + "\nLocale: " + Locale.getDefault() + "\nError: " + lastError); getContext().startActivity(Intent.createChooser(mailer, "Send email...")); @@ -3886,8 +3886,12 @@ public class LoginActivity extends BaseFragment { avatarEditor.setAnimation(cameraDrawable); cameraDrawable.setCurrentFrame(0); }, dialog -> { - cameraDrawable.setCustomEndFrame(86); - avatarEditor.playAnimation(); + if (!imageUpdater.isUploadingImage()) { + cameraDrawable.setCustomEndFrame(86); + avatarEditor.playAnimation(); + } else { + cameraDrawable.setCurrentFrame(0, false); + } }); cameraDrawable.setCurrentFrame(0); cameraDrawable.setCustomEndFrame(43); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java index e7e5537a3..a114ff2cd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MediaActivity.java @@ -460,7 +460,7 @@ public class MediaActivity extends BaseFragment implements NotificationCenter.No if (message != null) { SendMessagesHelper.getInstance(currentAccount).sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null); } - SendMessagesHelper.getInstance(currentAccount).sendMessage(fmessages, did, true, 0); + SendMessagesHelper.getInstance(currentAccount).sendMessage(fmessages, did, false, false, true, 0); } fragment1.finishFragment(); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MessageEnterTransitionContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/MessageEnterTransitionContainer.java index 6358a8e65..048c41780 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MessageEnterTransitionContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MessageEnterTransitionContainer.java @@ -5,8 +5,6 @@ import android.content.Context; import android.graphics.Canvas; import android.view.View; -import com.google.android.exoplayer2.util.Log; - import org.telegram.messenger.NotificationCenter; import java.util.ArrayList; @@ -38,14 +36,11 @@ public class MessageEnterTransitionContainer extends View { checkVisibility(); } - long time; @Override protected void onDraw(Canvas canvas) { if (transitions.isEmpty()) { return; } - long currentTime = System.currentTimeMillis(); - time = currentTime; for (int i = 0; i < transitions.size(); i++) { transitions.get(i).onDraw(canvas); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java index 696f58e85..30a692808 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PassportActivity.java @@ -7385,7 +7385,7 @@ public class PassportActivity extends BaseFragment implements NotificationCenter Intent mailer = new Intent(Intent.ACTION_SENDTO); mailer.setData(Uri.parse("mailto:")); - mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"sms@stel.com"}); + mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"reports@stel.com"}); mailer.putExtra(Intent.EXTRA_SUBJECT, "Android registration/login issue " + version + " " + phone); mailer.putExtra(Intent.EXTRA_TEXT, "Phone: " + phone + "\nApp version: " + version + "\nOS version: SDK " + Build.VERSION.SDK_INT + "\nDevice Name: " + Build.MANUFACTURER + Build.MODEL + "\nLocale: " + Locale.getDefault() + "\nError: " + lastError); getContext().startActivity(Intent.createChooser(mailer, "Send email...")); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index cdf89b9e8..49d0c1717 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -711,8 +711,13 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat int messageId = messageObject1.getId(); if (messageObject1.messageOwner.fwd_from != null) { - lower_id = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.saved_from_peer); - messageId = messageObject1.messageOwner.fwd_from.saved_from_msg_id; + if (messageObject1.messageOwner.fwd_from.saved_from_peer != null) { + lower_id = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.saved_from_peer); + messageId = messageObject1.messageOwner.fwd_from.saved_from_msg_id; + } else if (messageObject1.messageOwner.fwd_from.from_id != null) { + lower_id = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.from_id); + messageId = messageObject1.messageOwner.fwd_from.channel_post; + } } if (lower_id < 0) { @@ -1878,6 +1883,11 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat public boolean closeKeyboard() { return false; } + + @Override + public boolean validateGroupId(long groupId) { + return true; + } } public interface PhotoViewerProvider { @@ -1913,6 +1923,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat MessageObject getEditingMessageObject(); void onCaptionChanged(CharSequence caption); boolean closeKeyboard(); + boolean validateGroupId(long groupId); } private class FrameLayoutDrawer extends SizeNotifierFrameLayoutPhoto { @@ -3879,7 +3890,7 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat if (message != null) { SendMessagesHelper.getInstance(currentAccount).sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null); } - SendMessagesHelper.getInstance(currentAccount).sendMessage(fmessages, did, true, 0); + SendMessagesHelper.getInstance(currentAccount).sendMessage(fmessages, did, false, false, true, 0); } fragment1.finishFragment(); if (parentChatActivityFinal != null) { @@ -4517,6 +4528,14 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat checkProgress(0, false, true); } } + + @Override + public boolean validGroupId(long groupId) { + if (placeProvider != null) { + return placeProvider.validateGroupId(groupId); + } + return true; + } }); for (int a = 0; a < 3; a++) { @@ -6948,8 +6967,8 @@ public class PhotoViewer implements NotificationCenter.NotificationCenterDelegat } } else if (isPlaying || playbackState == ExoPlayer.STATE_ENDED) { if (currentEditMode != 3) { - photoProgressViews[0].setIndexedAlpha(1, 1f, false); - photoProgressViews[0].setBackgroundState(PROGRESS_PLAY, false, isPlaying); + photoProgressViews[0].setIndexedAlpha(1, 1f, playbackState == ExoPlayer.STATE_ENDED); + photoProgressViews[0].setBackgroundState(PROGRESS_PLAY, false, photoProgressViews[0].animAlphas[1] > 0f); } isPlaying = false; AndroidUtilities.cancelRunOnUIThread(updateProgressRunnable); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java index 657683e43..84b92e02d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java @@ -1164,7 +1164,9 @@ public class PrivacyControlActivity extends BaseFragment implements Notification themeDescriptions.add(new ThemeDescription(listView, 0, null, null, Theme.chat_msgInDrawable.getShadowDrawables(), null, Theme.key_chat_inBubbleShadow)); themeDescriptions.add(new ThemeDescription(listView, 0, null, null, Theme.chat_msgInMediaDrawable.getShadowDrawables(), null, Theme.key_chat_inBubbleShadow)); themeDescriptions.add(new ThemeDescription(listView, 0, null, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubble)); - themeDescriptions.add(new ThemeDescription(listView, 0, null, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient)); + themeDescriptions.add(new ThemeDescription(listView, 0, null, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient1)); + themeDescriptions.add(new ThemeDescription(listView, 0, null, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient2)); + themeDescriptions.add(new ThemeDescription(listView, 0, null, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient3)); themeDescriptions.add(new ThemeDescription(listView, 0, null, null, new Drawable[]{Theme.chat_msgOutSelectedDrawable, Theme.chat_msgOutMediaSelectedDrawable}, null, Theme.key_chat_outBubbleSelected)); themeDescriptions.add(new ThemeDescription(listView, 0, null, null, Theme.chat_msgOutDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow)); themeDescriptions.add(new ThemeDescription(listView, 0, null, null, Theme.chat_msgOutMediaDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java index b8025a0fb..8bc25af12 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -1936,7 +1936,11 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. videoCallItem.setContentDescription(LocaleController.getString("VideoCall", R.string.VideoCall)); if (chat_id != 0) { callItem = menu.addItem(call_item, R.drawable.msg_voicechat2); - callItem.setContentDescription(LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat)); + if (ChatObject.isChannelOrGiga(currentChat)) { + callItem.setContentDescription(LocaleController.getString("VoipChannelVoiceChat", R.string.VoipChannelVoiceChat)); + } else { + callItem.setContentDescription(LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat)); + } } else { callItem = menu.addItem(call_item, R.drawable.ic_call); callItem.setContentDescription(LocaleController.getString("Call", R.string.Call)); @@ -2708,11 +2712,12 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. getMessagesStorage().clearSentMedia(); SharedConfig.setNoSoundHintShowed(false); SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); - editor.remove("archivehint").remove("proximityhint").remove("archivehint_l").remove("gifhint").remove("reminderhint").remove("soundHint").remove("themehint").remove("filterhint").commit(); + editor.remove("archivehint").remove("proximityhint").remove("archivehint_l").remove("gifhint").remove("reminderhint").remove("soundHint").remove("themehint").remove("bganimationhint").remove("filterhint").commit(); MessagesController.getEmojiSettings(currentAccount).edit().remove("featured_hidden").commit(); SharedConfig.textSelectionHintShows = 0; SharedConfig.lockRecordAudioVideoHint = 0; SharedConfig.stickersReorderingHintUsed = false; + SharedConfig.forwardingOptionsHintShown = false; } else if (which == 7) { VoIPHelper.showCallDebugSettings(getParentActivity()); } else if (which == 8) { @@ -3393,8 +3398,12 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. MessagesController.getInstance(currentAccount).deleteUserPhoto(null); cameraDrawable.setCurrentFrame(0); }, dialog -> { - cameraDrawable.setCustomEndFrame(86); - writeButton.playAnimation(); + if (!imageUpdater.isUploadingImage()) { + cameraDrawable.setCustomEndFrame(86); + writeButton.playAnimation(); + } else { + cameraDrawable.setCurrentFrame(0, false); + } }); cameraDrawable.setCurrentFrame(0); cameraDrawable.setCustomEndFrame(43); @@ -5991,7 +6000,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. } if (chatInfo != null) { if (ChatObject.canManageCalls(chat) && chatInfo.call == null) { - otherItem.addSubItem(call_item, R.drawable.msg_voicechat, LocaleController.getString("StartVoipChat", R.string.StartVoipChat)); + otherItem.addSubItem(call_item, R.drawable.msg_voicechat, chat.megagroup && !chat.gigagroup ? LocaleController.getString("StartVoipChat", R.string.StartVoipChat) : LocaleController.getString("StartVoipChannel", R.string.StartVoipChannel)); hasVoiceChatItem = true; } if (chatInfo.can_view_stats) { @@ -7337,7 +7346,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. new SearchResult(102, LocaleController.getString("PrivacyLastSeen", R.string.PrivacyLastSeen), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.menu_secret, () -> presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_LASTSEEN, true))), new SearchResult(103, LocaleController.getString("PrivacyProfilePhoto", R.string.PrivacyProfilePhoto), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.menu_secret, () -> presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_PHOTO, true))), new SearchResult(104, LocaleController.getString("PrivacyForwards", R.string.PrivacyForwards), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.menu_secret, () -> presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_FORWARDS, true))), - new SearchResult(105, LocaleController.getString("PrivacyP2P", R.string.PrivacyP2P), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.menu_secret, () -> presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_P2P, true))), + new SearchResult(122, LocaleController.getString("PrivacyP2P", R.string.PrivacyP2P), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.menu_secret, () -> presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_P2P, true))), new SearchResult(106, LocaleController.getString("Calls", R.string.Calls), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.menu_secret, () -> presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_CALLS, true))), new SearchResult(107, LocaleController.getString("GroupsAndChannels", R.string.GroupsAndChannels), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.menu_secret, () -> presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_INVITE, true))), new SearchResult(108, LocaleController.getString("Passcode", R.string.Passcode), LocaleController.getString("PrivacySettings", R.string.PrivacySettings), R.drawable.menu_secret, () -> presentFragment(new PasscodeActivity(SharedConfig.passcodeHash.length() > 0 ? 2 : 0))), @@ -7391,7 +7400,7 @@ public class ProfileActivity extends BaseFragment implements NotificationCenter. new SearchResult(310, LocaleController.getString("RaiseToSpeak", R.string.RaiseToSpeak), "raiseToSpeakRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.menu_chats, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), new SearchResult(311, LocaleController.getString("SendByEnter", R.string.SendByEnter), "sendByEnterRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.menu_chats, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), new SearchResult(312, LocaleController.getString("SaveToGallerySettings", R.string.SaveToGallerySettings), "saveToGalleryRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.menu_chats, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), - new SearchResult(312, LocaleController.getString("DistanceUnits", R.string.DistanceUnits), "distanceRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.menu_chats, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), + new SearchResult(318, LocaleController.getString("DistanceUnits", R.string.DistanceUnits), "distanceRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.menu_chats, () -> presentFragment(new ThemeActivity(ThemeActivity.THEME_TYPE_BASIC))), new SearchResult(313, LocaleController.getString("StickersAndMasks", R.string.StickersAndMasks), LocaleController.getString("ChatSettings", R.string.ChatSettings), R.drawable.menu_chats, () -> presentFragment(new StickersActivity(MediaDataController.TYPE_IMAGE))), new SearchResult(314, LocaleController.getString("SuggestStickers", R.string.SuggestStickers), "suggestRow", LocaleController.getString("ChatSettings", R.string.ChatSettings), LocaleController.getString("StickersAndMasks", R.string.StickersAndMasks), R.drawable.menu_chats, () -> presentFragment(new StickersActivity(MediaDataController.TYPE_IMAGE))), new SearchResult(315, LocaleController.getString("FeaturedStickers", R.string.FeaturedStickers), null, LocaleController.getString("ChatSettings", R.string.ChatSettings), LocaleController.getString("StickersAndMasks", R.string.StickersAndMasks), R.drawable.menu_chats, () -> presentFragment(new FeaturedStickersActivity())), diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java index 9df444a01..0092fa2bc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SecretMediaViewer.java @@ -305,6 +305,8 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD private VelocityTracker velocityTracker; private Scroller scroller; + private boolean closeAfterAnimation; + @SuppressLint("StaticFieldLeak") private static volatile SecretMediaViewer Instance = null; public static SecretMediaViewer getInstance() { @@ -344,7 +346,9 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD if (isVideo && !videoWatchedOneTime) { closeVideoAfterWatch = true; } else { - closePhoto(true, true); + if (!closePhoto(true, true)) { + closeAfterAnimation = true; + } } } } else if (id == NotificationCenter.didCreatedNewDeleteTask) { @@ -379,7 +383,9 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD if (isVideo && !videoWatchedOneTime) { closeVideoAfterWatch = true; } else { - closePhoto(true, true); + if (!closePhoto(true, true)) { + closeAfterAnimation = true; + } } } } @@ -778,9 +784,9 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD isVideo = true; centerImage.setImage(null, null, currentThumb != null ? new BitmapDrawable(currentThumb.bitmap) : null, -1, null, messageObject, 2); long destroyTime = (long) messageObject.messageOwner.destroyTime * 1000; - long currentTime = System.currentTimeMillis() + ConnectionsManager.getInstance(currentAccount).getTimeDifference() * 1000; + long currentTime = System.currentTimeMillis() + ConnectionsManager.getInstance(currentAccount).getTimeDifference() * 1000L; long timeToDestroy = destroyTime - currentTime; - long duration = messageObject.getDuration() * 1000; + long duration = messageObject.getDuration() * 1000L; if (duration > timeToDestroy) { secretDeleteTimer.setDestroyTime(-1, -1, true); } else { @@ -829,6 +835,9 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD containerView.setLayerType(View.LAYER_TYPE_NONE, null); } containerView.invalidate(); + if (closeAfterAnimation) { + closePhoto(true, true); + } }; imageMoveAnimation.setDuration(250); imageMoveAnimation.addListener(new AnimatorListenerAdapter() { @@ -1108,9 +1117,9 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD return currentMessageObject; } - public void closePhoto(boolean animated, boolean byDelete) { + public boolean closePhoto(boolean animated, boolean byDelete) { if (parentActivity == null || !isPhotoVisible || checkPhotoAnimation()) { - return; + return false; } NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagesDeleted); @@ -1262,6 +1271,7 @@ public class SecretMediaViewer implements NotificationCenter.NotificationCenterD } animatorSet.start(); } + return true; } private void onPhotoClosed(PhotoViewer.PlaceProviderObject object) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java index 77fdf34d5..5090c6c0d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/StickersActivity.java @@ -55,6 +55,7 @@ import org.telegram.ui.Components.NumberTextView; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.ReorderingBulletinLayout; import org.telegram.ui.Components.ReorderingHintDrawable; +import org.telegram.ui.Components.ShareAlert; import org.telegram.ui.Components.StickersAlert; import org.telegram.ui.Components.TrendingStickersAlert; import org.telegram.ui.Components.TrendingStickersLayout; @@ -78,6 +79,7 @@ public class StickersActivity extends BaseFragment implements NotificationCenter private static final int MENU_ARCHIVE = 0; private static final int MENU_DELETE = 1; + private static final int MENU_SHARE = 2; private RecyclerListView listView; private ListAdapter listAdapter; @@ -90,6 +92,7 @@ public class StickersActivity extends BaseFragment implements NotificationCenter private ActionBarMenuItem archiveMenuItem; private ActionBarMenuItem deleteMenuItem; + private ActionBarMenuItem shareMenuItem; private int activeReorderingRequests; private boolean needReorder; @@ -205,7 +208,7 @@ public class StickersActivity extends BaseFragment implements NotificationCenter if (onBackPressed()) { finishFragment(); } - } else if (id == MENU_ARCHIVE || id == MENU_DELETE) { + } else if (id == MENU_ARCHIVE || id == MENU_DELETE || id == MENU_SHARE) { if (!needReorder) { if (activeReorderingRequests == 0) { listAdapter.processSelectionMenu(id); @@ -226,9 +229,11 @@ public class StickersActivity extends BaseFragment implements NotificationCenter actionMode.addView(selectedCountTextView, LayoutHelper.createLinear(0, LayoutHelper.MATCH_PARENT, 1.0f, 72, 0, 0, 0)); selectedCountTextView.setOnTouchListener((v, event) -> true); + shareMenuItem = actionMode.addItemWithWidth(MENU_SHARE, R.drawable.msg_share, AndroidUtilities.dp(54)); archiveMenuItem = actionMode.addItemWithWidth(MENU_ARCHIVE, R.drawable.msg_archive, AndroidUtilities.dp(54)); deleteMenuItem = actionMode.addItemWithWidth(MENU_DELETE, R.drawable.msg_delete, AndroidUtilities.dp(54)); + listAdapter = new ListAdapter(context, MediaDataController.getInstance(currentAccount).getStickerSets(currentType)); fragmentView = new FrameLayout(context); @@ -558,7 +563,33 @@ public class StickersActivity extends BaseFragment implements NotificationCenter } private void processSelectionMenu(int which) { - if (which == MENU_ARCHIVE || which == MENU_DELETE) { + if (which == MENU_SHARE) { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0, size = stickerSets.size(); i < size; i++) { + final TLRPC.TL_messages_stickerSet stickerSet = stickerSets.get(i); + if (selectedItems.get(stickerSet.set.id, false)) { + if (stringBuilder.length() != 0) { + stringBuilder.append("\n"); + } + stringBuilder.append(getLinkForSet(stickerSet)); + } + } + String link = stringBuilder.toString(); + ShareAlert shareAlert = ShareAlert.createShareAlert(fragmentView.getContext(), null, link, false, link, false); + shareAlert.setDelegate(new ShareAlert.ShareAlertDelegate() { + @Override + public void didShare() { + clearSelected(); + } + + @Override + public boolean didCopy() { + clearSelected(); + return true; + } + }); + shareAlert.show(); + } else if (which == MENU_ARCHIVE || which == MENU_DELETE) { final ArrayList stickerSetList = new ArrayList<>(selectedItems.size()); for (int i = 0, size = stickerSets.size(); i < size; i++) { @@ -624,7 +655,7 @@ public class StickersActivity extends BaseFragment implements NotificationCenter try { Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_TEXT, String.format(Locale.US, "https://" + MessagesController.getInstance(currentAccount).linkPrefix + "/addstickers/%s", stickerSet.set.short_name)); + intent.putExtra(Intent.EXTRA_TEXT, getLinkForSet(stickerSet)); getParentActivity().startActivityForResult(Intent.createChooser(intent, LocaleController.getString("StickersShare", R.string.StickersShare)), 500); } catch (Exception e) { FileLog.e(e); @@ -970,6 +1001,10 @@ public class StickersActivity extends BaseFragment implements NotificationCenter } } + private String getLinkForSet(TLRPC.TL_messages_stickerSet stickerSet) { + return String.format(Locale.US, "https://" + MessagesController.getInstance(currentAccount).linkPrefix + "/addstickers/%s", stickerSet.set.short_name); + } + @Override public ArrayList getThemeDescriptions() { ArrayList themeDescriptions = new ArrayList<>(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TextMessageEnterTransition.java b/TMessagesProj/src/main/java/org/telegram/ui/TextMessageEnterTransition.java index 290408a0c..c861a17db 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TextMessageEnterTransition.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TextMessageEnterTransition.java @@ -221,11 +221,11 @@ public class TextMessageEnterTransition implements MessageEnterTransitionContain if (Math.abs(ColorUtils.calculateLuminance(Theme.getColor(Theme.key_chat_messageTextOut)) - ColorUtils.calculateLuminance(Theme.getColor(Theme.key_chat_messagePanelText))) > 0.2f) { crossfade = true; changeColor = true; - fromColor = Theme.getColor(Theme.key_chat_messagePanelText); - toColor = Theme.getColor(Theme.key_chat_messageTextOut); - } + fromColor = Theme.getColor(Theme.key_chat_messagePanelText); + toColor = Theme.getColor(Theme.key_chat_messageTextOut); + if (messageTextLayout.getLineCount() == layout.getLineCount()) { n = messageTextLayout.getLineCount(); for (int i = 0; i < n; i++) { @@ -336,7 +336,7 @@ public class TextMessageEnterTransition implements MessageEnterTransitionContain replyNameDx = messageView.replyNameLayout.getWidth() - messageView.replyNameLayout.getLineWidth(0); } } - if (messageView.replyTextLayout != null && messageView.replyTextLayout.getText().length() > 1) { + if (messageView.replyTextLayout != null && messageView.replyTextLayout.getText().length() >= 1) { if (messageView.replyTextLayout.getPrimaryHorizontal(0) != 0) { replyMessageDx = messageView.replyTextLayout.getWidth() - messageView.replyTextLayout.getLineWidth(0); } @@ -359,7 +359,6 @@ public class TextMessageEnterTransition implements MessageEnterTransitionContain animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); container.removeTransition(TextMessageEnterTransition.this); messageView.setEnterTransitionInProgress(false); @@ -371,8 +370,10 @@ public class TextMessageEnterTransition implements MessageEnterTransitionContain }); if (SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_HIGH) { - Theme.MessageDrawable drawable = messageView.getCurrentBackgroundDrawable(false); - fromMessageDrawable = drawable.getTransitionDrawable(Theme.getColor(Theme.key_chat_messagePanelBackground)); + Theme.MessageDrawable drawable = messageView.getCurrentBackgroundDrawable(true); + if (drawable != null) { + fromMessageDrawable = drawable.getTransitionDrawable(Theme.getColor(Theme.key_chat_messagePanelBackground)); + } } } @@ -419,7 +420,7 @@ public class TextMessageEnterTransition implements MessageEnterTransitionContain } float progress = ChatListItemAnimator.DEFAULT_INTERPOLATOR.getInterpolation(this.progress); - float alphaProgress = this.progress > 0.6f ? 1f : this.progress / 0.6f; + float alphaProgress = this.progress > 0.4f ? 1f : this.progress / 0.4f; float p2 = CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(this.progress); float progressX = CubicBezierInterpolator.EASE_OUT.getInterpolation(p2); @@ -446,31 +447,27 @@ public class TextMessageEnterTransition implements MessageEnterTransitionContain Theme.MessageDrawable drawable = messageView.getCurrentBackgroundDrawable(true); if (drawable != null) { - canvas.save(); - canvas.translate(drawableX, drawableTop); - int heightLocal = (int) (drawableBottom - drawableTop); - int widthLocal = (int) (drawableRight - drawableX); - - messageView.setBackgroundTopY(false); + messageView.setBackgroundTopY(listView.getTop() - container.getTop()); Drawable shadowDrawable = drawable.getShadowDrawable(); if (alphaProgress != 1f && fromMessageDrawable != null) { - fromMessageDrawable.setBounds(0, 0, widthLocal, heightLocal); + fromMessageDrawable.setBounds((int) drawableX, (int) drawableTop, drawableRight, (int) drawableBottom); fromMessageDrawable.draw(canvas); } if (shadowDrawable != null) { shadowDrawable.setAlpha((int) (255 * progressX)); - shadowDrawable.setBounds(0, 0, widthLocal, heightLocal); + shadowDrawable.setBounds((int) drawableX, (int) drawableTop, drawableRight, (int) drawableBottom); shadowDrawable.draw(canvas); shadowDrawable.setAlpha(255); } drawable.setAlpha((int) (255 * alphaProgress)); - drawable.setBounds(0, 0, widthLocal, heightLocal); + drawable.setBounds((int) drawableX, (int) drawableTop, drawableRight, (int) drawableBottom); + drawable.setDrawFullBubble(true); drawable.draw(canvas); + drawable.setDrawFullBubble(false); drawable.setAlpha(255); - canvas.restore(); } canvas.restore(); @@ -549,18 +546,22 @@ public class TextMessageEnterTransition implements MessageEnterTransitionContain float replyToMessageX = toReplayX - replyMessageDx; float replyToNameX = toReplayX - replyNameDx; - float replyMessageX = fromReplayX * (1f - progressX) + replyToMessageX * progressX; + float replyMessageX =(fromReplayX - replyMessageDx) * (1f - progressX) + replyToMessageX * progressX; float replyNameX = fromReplayX * (1f - progressX) + replyToNameX * progressX; - canvas.save(); - canvas.translate(replyNameX, replyY); - messageView.replyNameLayout.draw(canvas); - canvas.restore(); + if (messageView.replyNameLayout != null) { + canvas.save(); + canvas.translate(replyNameX, replyY); + messageView.replyNameLayout.draw(canvas); + canvas.restore(); + } - canvas.save(); - canvas.translate(replyMessageX, replyY + AndroidUtilities.dp(19)); - messageView.replyTextLayout.draw(canvas); - canvas.restore(); + if (messageView.replyTextLayout != null) { + canvas.save(); + canvas.translate(replyMessageX, replyY + AndroidUtilities.dp(19)); + messageView.replyTextLayout.draw(canvas); + canvas.restore(); + } canvas.restore(); } @@ -589,7 +590,7 @@ public class TextMessageEnterTransition implements MessageEnterTransitionContain } else { if (crossfade && changeColor) { int oldColor = Theme.chat_msgTextPaint.getColor(); - Theme.chat_msgTextPaint.setColor(ColorUtils.setAlphaComponent(fromColor, (int) (Color.alpha(fromColor) * (1f - alphaProgress)))); + Theme.chat_msgTextPaint.setColor(ColorUtils.blendARGB(fromColor, toColor, alphaProgress)); layout.draw(canvas); Theme.chat_msgTextPaint.setColor(oldColor); } else if (crossfade) { @@ -615,7 +616,7 @@ public class TextMessageEnterTransition implements MessageEnterTransitionContain } else { if (crossfade && changeColor) { int oldColor = Theme.chat_msgTextPaint.getColor(); - Theme.chat_msgTextPaint.setColor(ColorUtils.setAlphaComponent(fromColor, (int) (Color.alpha(fromColor) * (1f - alphaProgress)))); + Theme.chat_msgTextPaint.setColor(ColorUtils.blendARGB(fromColor, toColor, alphaProgress)); rtlLayout.draw(canvas); Theme.chat_msgTextPaint.setColor(oldColor); } else if (crossfade) { @@ -640,7 +641,12 @@ public class TextMessageEnterTransition implements MessageEnterTransitionContain bitmapPaint.setAlpha((int) (255 * alphaProgress)); canvas.drawBitmap(crossfadeTextBitmap, 0, 0, bitmapPaint); } else { - messageView.drawMessageText(canvas, messageView.getMessageObject().textLayoutBlocks, true, alphaProgress, true); + int oldColor = Theme.chat_msgTextPaint.getColor(); + Theme.chat_msgTextPaint.setColor(toColor); + messageView.drawMessageText(canvas, messageView.getMessageObject().textLayoutBlocks, false, alphaProgress, true); + if (Theme.chat_msgTextPaint.getColor() != oldColor) { + Theme.chat_msgTextPaint.setColor(oldColor); + } } canvas.restore(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java index 7606a4e76..9efda1264 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ThemeActivity.java @@ -2172,7 +2172,9 @@ public class ThemeActivity extends BaseFragment implements NotificationCenter.No themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSizeCell.class}, null, Theme.chat_msgInDrawable.getShadowDrawables(), null, Theme.key_chat_inBubbleShadow)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSizeCell.class}, null, Theme.chat_msgInMediaDrawable.getShadowDrawables(), null, Theme.key_chat_inBubbleShadow)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSizeCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubble)); - themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSizeCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient)); + themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSizeCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient1)); + themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSizeCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient2)); + themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSizeCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient3)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSizeCell.class}, null, new Drawable[]{Theme.chat_msgOutSelectedDrawable, Theme.chat_msgOutMediaSelectedDrawable}, null, Theme.key_chat_outBubbleSelected)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSizeCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleShadow)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextSizeCell.class}, null, new Drawable[]{Theme.chat_msgInDrawable, Theme.chat_msgInMediaDrawable}, null, Theme.key_chat_inBubbleShadow)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ThemePreviewActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ThemePreviewActivity.java index b87cab5f4..ffc43bda4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ThemePreviewActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ThemePreviewActivity.java @@ -102,6 +102,7 @@ import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.ColorPicker; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.HintView; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.MediaActionDrawable; import org.telegram.ui.Components.MotionBackgroundDrawable; @@ -139,7 +140,10 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro private boolean removeBackgroundOverride; private int backupAccentColor; private int backupMyMessagesAccentColor; - private int backupMyMessagesGradientAccentColor; + private int backupMyMessagesGradientAccentColor1; + private int backupMyMessagesGradientAccentColor2; + private int backupMyMessagesGradientAccentColor3; + private boolean backupMyMessagesAnimated; private long backupBackgroundOverrideColor; private long backupBackgroundGradientOverrideColor1; private long backupBackgroundGradientOverrideColor2; @@ -190,14 +194,20 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro private RecyclerListView listView2; private MessagesAdapter messagesAdapter; private BackupImageView backgroundImage; - private FrameLayout buttonsContainer; + private FrameLayout backgroundButtonsContainer; + private FrameLayout messagesButtonsContainer; + private HintView animationHint; private AnimatorSet motionAnimation; private RadialProgress2 radialProgress; private FrameLayout bottomOverlayChat; - private FrameLayout playAnimationView; - private ImageView playAnimationImageView; - private AnimatorSet playViewAnimator; - private WallpaperCheckBoxView[] checkBoxView; + private FrameLayout backgroundPlayAnimationView; + private FrameLayout messagesPlayAnimationView; + private ImageView backgroundPlayAnimationImageView; + private ImageView messagesPlayAnimationImageView; + private AnimatorSet backgroundPlayViewAnimator; + private AnimatorSet messagesPlayViewAnimator; + private WallpaperCheckBoxView[] backgroundCheckBoxView; + private WallpaperCheckBoxView[] messagesCheckBoxView; private FrameLayout[] patternLayout = new FrameLayout[2]; private TextView[] patternsCancelButton = new TextView[2]; private TextView[] patternsSaveButton = new TextView[2]; @@ -306,7 +316,10 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro useDefaultThemeForButtons = false; backupAccentColor = accent.accentColor; backupMyMessagesAccentColor = accent.myMessagesAccentColor; - backupMyMessagesGradientAccentColor = accent.myMessagesGradientAccentColor; + backupMyMessagesGradientAccentColor1 = accent.myMessagesGradientAccentColor1; + backupMyMessagesGradientAccentColor2 = accent.myMessagesGradientAccentColor2; + backupMyMessagesGradientAccentColor3 = accent.myMessagesGradientAccentColor3; + backupMyMessagesAnimated = accent.myMessagesAnimated; backupBackgroundOverrideColor = accent.backgroundOverrideColor; backupBackgroundGradientOverrideColor1 = accent.backgroundGradientOverrideColor1; backupBackgroundGradientOverrideColor2 = accent.backgroundGradientOverrideColor2; @@ -680,21 +693,6 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } } }; - int textsCount; - if (screenType == SCREEN_TYPE_ACCENT_COLOR || currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) { - textsCount = 3; - if (currentWallpaper instanceof WallpapersListActivity.ColorWallpaper && Theme.DEFAULT_BACKGROUND_SLUG.equals(((WallpapersListActivity.ColorWallpaper) currentWallpaper).slug)) { - textsCount = 0; - } - } else { - textsCount = 2; - if (currentWallpaper instanceof WallpapersListActivity.FileWallpaper) { - WallpapersListActivity.FileWallpaper fileWallpaper = (WallpapersListActivity.FileWallpaper) currentWallpaper; - if (Theme.THEME_BACKGROUND_SLUG.equals(fileWallpaper.slug)) { - textsCount = 0; - } - } - } page2.addView(backgroundImage, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 48)); if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { @@ -706,9 +704,14 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro Theme.applyChatServiceMessageColor(AndroidUtilities.calcDrawableColor(dr), dr); } listView2.invalidateViews(); - if (buttonsContainer != null) { - for (int a = 0, N = buttonsContainer.getChildCount(); a < N; a++) { - buttonsContainer.getChildAt(a).invalidate(); + if (backgroundButtonsContainer != null) { + for (int a = 0, N = backgroundButtonsContainer.getChildCount(); a < N; a++) { + backgroundButtonsContainer.getChildAt(a).invalidate(); + } + } + if (messagesButtonsContainer != null) { + for (int a = 0, N = messagesButtonsContainer.getChildCount(); a < N; a++) { + messagesButtonsContainer.getChildAt(a).invalidate(); } } if (radialProgress != null) { @@ -866,13 +869,21 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro @Override public void setTranslationY(float translationY) { super.setTranslationY(translationY); - if (checkBoxView != null) { - for (int a = 0; a < checkBoxView.length; a++) { - checkBoxView[a].invalidate(); + if (backgroundCheckBoxView != null) { + for (int a = 0; a < backgroundCheckBoxView.length; a++) { + backgroundCheckBoxView[a].invalidate(); } } - if (playAnimationView != null) { - playAnimationView.invalidate(); + if (messagesCheckBoxView != null) { + for (int a = 0; a < messagesCheckBoxView.length; a++) { + messagesCheckBoxView[a].invalidate(); + } + } + if (backgroundPlayAnimationView != null) { + backgroundPlayAnimationView.invalidate(); + } + if (messagesPlayAnimationView != null) { + messagesPlayAnimationView.invalidate(); } } @@ -911,7 +922,14 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } } }; - ((DefaultItemAnimator) listView2.getItemAnimator()).setDelayAnimations(false); + DefaultItemAnimator itemAnimator = new DefaultItemAnimator() { + @Override + protected void onMoveAnimationUpdate(RecyclerView.ViewHolder holder) { + listView2.invalidateViews(); + } + }; + itemAnimator.setDelayAnimations(false); + listView2.setItemAnimator(itemAnimator); listView2.setVerticalScrollBarEnabled(true); listView2.setOverScrollMode(RecyclerListView.OVER_SCROLL_NEVER); if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { @@ -1231,163 +1249,295 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro sheetDrawable.getPadding(paddings); sheetDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhite), PorterDuff.Mode.MULTIPLY)); - String[] texts = new String[textsCount]; - int[] textSizes = new int[textsCount]; - checkBoxView = new WallpaperCheckBoxView[textsCount]; - int maxTextSize = 0; - if (textsCount != 0) { - buttonsContainer = new FrameLayout(context); + TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + textPaint.setTextSize(AndroidUtilities.dp(14)); + textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + { + int textsCount; if (screenType == SCREEN_TYPE_ACCENT_COLOR || currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) { - texts[0] = LocaleController.getString("BackgroundColors", R.string.BackgroundColors); - texts[1] = LocaleController.getString("BackgroundPattern", R.string.BackgroundPattern); - texts[2] = LocaleController.getString("BackgroundMotion", R.string.BackgroundMotion); + textsCount = 3; + if (currentWallpaper instanceof WallpapersListActivity.ColorWallpaper && Theme.DEFAULT_BACKGROUND_SLUG.equals(((WallpapersListActivity.ColorWallpaper) currentWallpaper).slug)) { + textsCount = 0; + } } else { - texts[0] = LocaleController.getString("BackgroundBlurred", R.string.BackgroundBlurred); - texts[1] = LocaleController.getString("BackgroundMotion", R.string.BackgroundMotion); + textsCount = 2; + if (currentWallpaper instanceof WallpapersListActivity.FileWallpaper) { + WallpapersListActivity.FileWallpaper fileWallpaper = (WallpapersListActivity.FileWallpaper) currentWallpaper; + if (Theme.THEME_BACKGROUND_SLUG.equals(fileWallpaper.slug)) { + textsCount = 0; + } + } } - TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - textPaint.setTextSize(AndroidUtilities.dp(14)); - textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + + String[] texts = new String[textsCount]; + int[] textSizes = new int[textsCount]; + backgroundCheckBoxView = new WallpaperCheckBoxView[textsCount]; + int maxTextSize = 0; + if (textsCount != 0) { + backgroundButtonsContainer = new FrameLayout(context); + if (screenType == SCREEN_TYPE_ACCENT_COLOR || currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) { + texts[0] = LocaleController.getString("BackgroundColors", R.string.BackgroundColors); + texts[1] = LocaleController.getString("BackgroundPattern", R.string.BackgroundPattern); + texts[2] = LocaleController.getString("BackgroundMotion", R.string.BackgroundMotion); + } else { + texts[0] = LocaleController.getString("BackgroundBlurred", R.string.BackgroundBlurred); + texts[1] = LocaleController.getString("BackgroundMotion", R.string.BackgroundMotion); + } + for (int a = 0; a < texts.length; a++) { + textSizes[a] = (int) Math.ceil(textPaint.measureText(texts[a])); + maxTextSize = Math.max(maxTextSize, textSizes[a]); + } + + backgroundPlayAnimationView = new FrameLayout(context) { + + private RectF rect = new RectF(); + + @Override + protected void onDraw(Canvas canvas) { + rect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + Theme.applyServiceShaderMatrixForView(backgroundPlayAnimationView, backgroundImage); + canvas.drawRoundRect(rect, getMeasuredHeight() / 2, getMeasuredHeight() / 2, Theme.chat_actionBackgroundPaint); + if (Theme.hasGradientService()) { + canvas.drawRoundRect(rect, getMeasuredHeight() / 2, getMeasuredHeight() / 2, Theme.chat_actionBackgroundGradientDarkenPaint); + } + } + }; + backgroundPlayAnimationView.setWillNotDraw(false); + backgroundPlayAnimationView.setVisibility(backgroundGradientColor1 != 0 ? View.VISIBLE : View.INVISIBLE); + backgroundPlayAnimationView.setScaleX(backgroundGradientColor1 != 0 ? 1.0f : 0.1f); + backgroundPlayAnimationView.setScaleY(backgroundGradientColor1 != 0 ? 1.0f : 0.1f); + backgroundPlayAnimationView.setAlpha(backgroundGradientColor1 != 0 ? 1.0f : 0.0f); + backgroundPlayAnimationView.setTag(backgroundGradientColor1 != 0 ? 1 : null); + backgroundButtonsContainer.addView(backgroundPlayAnimationView, LayoutHelper.createFrame(48, 48, Gravity.CENTER)); + backgroundPlayAnimationView.setOnClickListener(new View.OnClickListener() { + + int rotation = 0; + + @Override + public void onClick(View v) { + Drawable background = backgroundImage.getBackground(); + backgroundPlayAnimationImageView.setRotation(rotation); + rotation -= 45; + backgroundPlayAnimationImageView.animate().rotationBy(-45).setDuration(300).setInterpolator(CubicBezierInterpolator.EASE_OUT).start(); + if (background instanceof MotionBackgroundDrawable) { + MotionBackgroundDrawable motionBackgroundDrawable = (MotionBackgroundDrawable) background; + motionBackgroundDrawable.switchToNextPosition(); + } else { + onColorsRotate(); + } + } + }); + + backgroundPlayAnimationImageView = new ImageView(context); + backgroundPlayAnimationImageView.setScaleType(ImageView.ScaleType.CENTER); + backgroundPlayAnimationImageView.setImageResource(R.drawable.bg_rotate_large); + backgroundPlayAnimationView.addView(backgroundPlayAnimationImageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + } + + for (int a = 0; a < textsCount; a++) { + final int num = a; + backgroundCheckBoxView[a] = new WallpaperCheckBoxView(context, screenType != SCREEN_TYPE_ACCENT_COLOR && !(currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) || a != 0, backgroundImage); + backgroundCheckBoxView[a].setBackgroundColor(backgroundColor); + backgroundCheckBoxView[a].setText(texts[a], textSizes[a], maxTextSize); + + if (screenType == SCREEN_TYPE_ACCENT_COLOR || currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) { + if (a == 1) { + backgroundCheckBoxView[a].setChecked(selectedPattern != null || accent != null && !TextUtils.isEmpty(accent.patternSlug), false); + } else if (a == 2) { + backgroundCheckBoxView[a].setChecked(isMotion, false); + } + } else { + backgroundCheckBoxView[a].setChecked(a == 0 ? isBlurred : isMotion, false); + } + int width = maxTextSize + AndroidUtilities.dp(14 * 2 + 28); + FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT); + layoutParams.gravity = Gravity.CENTER; + if (textsCount == 3) { + if (a == 0 || a == 2) { + layoutParams.leftMargin = width / 2 + AndroidUtilities.dp(10); + } else { + layoutParams.rightMargin = width / 2 + AndroidUtilities.dp(10); + } + } else { + if (a == 1) { + layoutParams.leftMargin = width / 2 + AndroidUtilities.dp(10); + } else { + layoutParams.rightMargin = width / 2 + AndroidUtilities.dp(10); + } + } + backgroundButtonsContainer.addView(backgroundCheckBoxView[a], layoutParams); + WallpaperCheckBoxView view = backgroundCheckBoxView[a]; + backgroundCheckBoxView[a].setOnClickListener(v -> { + if (backgroundButtonsContainer.getAlpha() != 1.0f || patternViewAnimation != null) { + return; + } + if ((screenType == SCREEN_TYPE_ACCENT_COLOR || currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) && num == 2) { + view.setChecked(!view.isChecked(), true); + isMotion = view.isChecked(); + parallaxEffect.setEnabled(isMotion); + animateMotionChange(); + } else if (num == 1 && (screenType == SCREEN_TYPE_ACCENT_COLOR || currentWallpaper instanceof WallpapersListActivity.ColorWallpaper)) { + if (backgroundCheckBoxView[1].isChecked()) { + lastSelectedPattern = selectedPattern; + backgroundImage.setImageDrawable(null); + selectedPattern = null; + isMotion = false; + updateButtonState(false, true); + animateMotionChange(); + if (patternLayout[1].getVisibility() == View.VISIBLE) { + if (screenType == SCREEN_TYPE_ACCENT_COLOR) { + showPatternsView(0, true, true); + } else { + showPatternsView(num, patternLayout[num].getVisibility() != View.VISIBLE, true); + } + } + } else { + selectPattern(lastSelectedPattern != null ? -1 : 0); + if (screenType == SCREEN_TYPE_ACCENT_COLOR) { + showPatternsView(1, true, true); + } else { + showPatternsView(num, patternLayout[num].getVisibility() != View.VISIBLE, true); + } + } + backgroundCheckBoxView[1].setChecked(selectedPattern != null, true); + updateSelectedPattern(true); + patternsListView.invalidateViews(); + updateMotionButton(); + } else if (currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) { + showPatternsView(num, patternLayout[num].getVisibility() != View.VISIBLE, true); + } else if (screenType != SCREEN_TYPE_ACCENT_COLOR) { + view.setChecked(!view.isChecked(), true); + if (num == 0) { + isBlurred = view.isChecked(); + if (isBlurred) { + backgroundImage.getImageReceiver().setForceCrossfade(true); + } + updateBlurred(); + } else { + isMotion = view.isChecked(); + parallaxEffect.setEnabled(isMotion); + animateMotionChange(); + } + } + }); + if (a == 2) { + backgroundCheckBoxView[a].setAlpha(0.0f); + backgroundCheckBoxView[a].setVisibility(View.INVISIBLE); + } + } + } + + if (screenType == SCREEN_TYPE_ACCENT_COLOR) { + String[] texts = new String[2]; + int[] textSizes = new int[2]; + messagesCheckBoxView = new WallpaperCheckBoxView[2]; + int maxTextSize = 0; + + messagesButtonsContainer = new FrameLayout(context); + + texts[0] = LocaleController.getString("BackgroundAnimate", R.string.BackgroundAnimate); + texts[1] = LocaleController.getString("BackgroundColors", R.string.BackgroundColors); + for (int a = 0; a < texts.length; a++) { textSizes[a] = (int) Math.ceil(textPaint.measureText(texts[a])); maxTextSize = Math.max(maxTextSize, textSizes[a]); } - playAnimationView = new FrameLayout(context) { + messagesPlayAnimationView = new FrameLayout(context) { private RectF rect = new RectF(); @Override protected void onDraw(Canvas canvas) { rect.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); - Theme.applyServiceShaderMatrixForView(playAnimationView, backgroundImage); + Theme.applyServiceShaderMatrixForView(messagesPlayAnimationView, backgroundImage); canvas.drawRoundRect(rect, getMeasuredHeight() / 2, getMeasuredHeight() / 2, Theme.chat_actionBackgroundPaint); if (Theme.hasGradientService()) { canvas.drawRoundRect(rect, getMeasuredHeight() / 2, getMeasuredHeight() / 2, Theme.chat_actionBackgroundGradientDarkenPaint); } } }; - playAnimationView.setWillNotDraw(false); - playAnimationView.setVisibility(backgroundGradientColor1 != 0 ? View.VISIBLE : View.INVISIBLE); - playAnimationView.setScaleX(backgroundGradientColor1 != 0 ? 1.0f : 0.1f); - playAnimationView.setScaleY(backgroundGradientColor1 != 0 ? 1.0f : 0.1f); - playAnimationView.setAlpha(backgroundGradientColor1 != 0 ? 1.0f : 0.0f); - playAnimationView.setTag(backgroundGradientColor1 != 0 ? 1 : null); - buttonsContainer.addView(playAnimationView, LayoutHelper.createFrame(48, 48, Gravity.CENTER)); - playAnimationView.setOnClickListener(new View.OnClickListener() { + messagesPlayAnimationView.setWillNotDraw(false); + messagesPlayAnimationView.setVisibility(accent.myMessagesGradientAccentColor1 != 0 ? View.VISIBLE : View.INVISIBLE); + messagesPlayAnimationView.setScaleX(accent.myMessagesGradientAccentColor1 != 0 ? 1.0f : 0.1f); + messagesPlayAnimationView.setScaleY(accent.myMessagesGradientAccentColor1 != 0 ? 1.0f : 0.1f); + messagesPlayAnimationView.setAlpha(accent.myMessagesGradientAccentColor1 != 0 ? 1.0f : 0.0f); + messagesButtonsContainer.addView(messagesPlayAnimationView, LayoutHelper.createFrame(48, 48, Gravity.CENTER)); + messagesPlayAnimationView.setOnClickListener(new View.OnClickListener() { int rotation = 0; @Override public void onClick(View v) { - Drawable background = backgroundImage.getBackground(); - playAnimationImageView.setRotation(rotation); + messagesPlayAnimationImageView.setRotation(rotation); rotation -= 45; - playAnimationImageView.animate().rotationBy(-45).setDuration(300).setInterpolator(CubicBezierInterpolator.EASE_OUT).start(); - if (background instanceof MotionBackgroundDrawable) { - MotionBackgroundDrawable motionBackgroundDrawable = (MotionBackgroundDrawable) background; - motionBackgroundDrawable.switchToNextPosition(); - } else { - onColorsRotate(); - } - } - }); - - playAnimationImageView = new ImageView(context); - playAnimationImageView.setScaleType(ImageView.ScaleType.CENTER); - playAnimationImageView.setImageResource(R.drawable.bg_rotate_large); - playAnimationView.addView(playAnimationImageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); - } - - for (int a = 0; a < textsCount; a++) { - final int num = a; - checkBoxView[a] = new WallpaperCheckBoxView(context, screenType != SCREEN_TYPE_ACCENT_COLOR && !(currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) || a != 0, backgroundImage); - checkBoxView[a].setBackgroundColor(backgroundColor); - checkBoxView[a].setText(texts[a], textSizes[a], maxTextSize); - - if (screenType == SCREEN_TYPE_ACCENT_COLOR || currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) { - if (a == 1) { - checkBoxView[a].setChecked(selectedPattern != null || accent != null && !TextUtils.isEmpty(accent.patternSlug), false); - } else if (a == 2) { - checkBoxView[a].setChecked(isMotion, false); - } - } else { - checkBoxView[a].setChecked(a == 0 ? isBlurred : isMotion, false); - } - int width = maxTextSize + AndroidUtilities.dp(14 * 2 + 28); - FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT); - layoutParams.gravity = Gravity.CENTER; - if (textsCount == 3) { - if (a == 0 || a == 2) { - layoutParams.leftMargin = width / 2 + AndroidUtilities.dp(10); - } else { - layoutParams.rightMargin = width / 2 + AndroidUtilities.dp(10); - } - } else { - if (a == 1) { - layoutParams.leftMargin = width / 2 + AndroidUtilities.dp(10); - } else { - layoutParams.rightMargin = width / 2 + AndroidUtilities.dp(10); - } - } - buttonsContainer.addView(checkBoxView[a], layoutParams); - WallpaperCheckBoxView view = checkBoxView[a]; - checkBoxView[a].setOnClickListener(v -> { - if (buttonsContainer.getAlpha() != 1.0f || patternViewAnimation != null) { - return; - } - if ((screenType == SCREEN_TYPE_ACCENT_COLOR || currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) && num == 2) { - view.setChecked(!view.isChecked(), true); - isMotion = view.isChecked(); - parallaxEffect.setEnabled(isMotion); - animateMotionChange(); - } else if (num == 1 && (screenType == SCREEN_TYPE_ACCENT_COLOR || currentWallpaper instanceof WallpapersListActivity.ColorWallpaper)) { - if (checkBoxView[1].isChecked()) { - lastSelectedPattern = selectedPattern; - backgroundImage.setImageDrawable(null); - selectedPattern = null; - isMotion = false; - updateButtonState(false, true); - animateMotionChange(); - if (patternLayout[1].getVisibility() == View.VISIBLE) { - if (screenType == SCREEN_TYPE_ACCENT_COLOR) { - showPatternsView(0, true, true); - } else { - showPatternsView(num, patternLayout[num].getVisibility() != View.VISIBLE, true); - } + messagesPlayAnimationImageView.animate().rotationBy(-45).setDuration(300).setInterpolator(CubicBezierInterpolator.EASE_OUT).start(); + if (accent.myMessagesAnimated) { + if (Theme.chat_msgOutDrawable.getMotionBackgroundDrawable() != null) { + Theme.chat_msgOutDrawable.getMotionBackgroundDrawable().switchToNextPosition(); } } else { - selectPattern(lastSelectedPattern != null ? -1 : 0); - if (screenType == SCREEN_TYPE_ACCENT_COLOR) { - showPatternsView(1, true, true); + int temp; + if (accent.myMessagesGradientAccentColor3 != 0) { + temp = accent.myMessagesAccentColor != 0 ? accent.myMessagesAccentColor : accent.accentColor; + accent.myMessagesAccentColor = accent.myMessagesGradientAccentColor1; + accent.myMessagesGradientAccentColor1 = accent.myMessagesGradientAccentColor2; + accent.myMessagesGradientAccentColor2 = accent.myMessagesGradientAccentColor3; + accent.myMessagesGradientAccentColor3 = temp; } else { - showPatternsView(num, patternLayout[num].getVisibility() != View.VISIBLE, true); + temp = accent.myMessagesAccentColor != 0 ? accent.myMessagesAccentColor : accent.accentColor; + accent.myMessagesAccentColor = accent.myMessagesGradientAccentColor1; + accent.myMessagesGradientAccentColor1 = accent.myMessagesGradientAccentColor2; + accent.myMessagesGradientAccentColor2 = temp; } - } - checkBoxView[1].setChecked(selectedPattern != null, true); - updateSelectedPattern(true); - patternsListView.invalidateViews(); - updateMotionButton(); - } else if (currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) { - showPatternsView(num, patternLayout[num].getVisibility() != View.VISIBLE, true); - } else if (screenType != SCREEN_TYPE_ACCENT_COLOR) { - view.setChecked(!view.isChecked(), true); - if (num == 0) { - isBlurred = view.isChecked(); - if (isBlurred) { - backgroundImage.getImageReceiver().setForceCrossfade(true); - } - updateBlurred(); - } else { - isMotion = view.isChecked(); - parallaxEffect.setEnabled(isMotion); - animateMotionChange(); + colorPicker.setColor(accent.myMessagesGradientAccentColor3, 3); + colorPicker.setColor(accent.myMessagesGradientAccentColor2, 2); + colorPicker.setColor(accent.myMessagesGradientAccentColor1, 1); + colorPicker.setColor(accent.myMessagesAccentColor != 0 ? accent.myMessagesAccentColor : accent.accentColor, 0); + messagesCheckBoxView[1].setColor(0, accent.myMessagesAccentColor); + messagesCheckBoxView[1].setColor(1, accent.myMessagesGradientAccentColor1); + messagesCheckBoxView[1].setColor(2, accent.myMessagesGradientAccentColor2); + messagesCheckBoxView[1].setColor(3, accent.myMessagesGradientAccentColor3); + Theme.refreshThemeColors(true, true); + listView2.invalidateViews(); } } }); - if (a == 2) { - checkBoxView[a].setAlpha(0.0f); - checkBoxView[a].setVisibility(View.INVISIBLE); + + messagesPlayAnimationImageView = new ImageView(context); + messagesPlayAnimationImageView.setScaleType(ImageView.ScaleType.CENTER); + messagesPlayAnimationImageView.setImageResource(R.drawable.bg_rotate_large); + messagesPlayAnimationView.addView(messagesPlayAnimationImageView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + + for (int a = 0; a < 2; a++) { + final int num = a; + messagesCheckBoxView[a] = new WallpaperCheckBoxView(context, a == 0, backgroundImage); + messagesCheckBoxView[a].setText(texts[a], textSizes[a], maxTextSize); + + if (a == 0) { + messagesCheckBoxView[a].setChecked(accent.myMessagesAnimated, false); + } + int width = maxTextSize + AndroidUtilities.dp(14 * 2 + 28); + FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT); + layoutParams.gravity = Gravity.CENTER; + if (a == 1) { + layoutParams.leftMargin = width / 2 + AndroidUtilities.dp(10); + } else { + layoutParams.rightMargin = width / 2 + AndroidUtilities.dp(10); + } + messagesButtonsContainer.addView(messagesCheckBoxView[a], layoutParams); + WallpaperCheckBoxView view = messagesCheckBoxView[a]; + messagesCheckBoxView[a].setOnClickListener(v -> { + if (messagesButtonsContainer.getAlpha() != 1.0f) { + return; + } + if (num == 0) { + view.setChecked(!view.isChecked(), true); + accent.myMessagesAnimated = view.isChecked(); + Theme.refreshThemeColors(true, true); + listView2.invalidateViews(); + } + }); } } @@ -1465,7 +1615,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } else { backgroundImage.setImage(ImageLocation.getForDocument(selectedPattern.document), imageFilter, null, null, "jpg", selectedPattern.document.size, 1, selectedPattern); } - checkBoxView[1].setChecked(selectedPattern != null, false); + backgroundCheckBoxView[1].setChecked(selectedPattern != null, false); currentIntensity = previousIntensity; intensitySeekBar.setProgress(currentIntensity); @@ -1479,7 +1629,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro if (selectedPattern == null) { if (isMotion) { isMotion = false; - checkBoxView[0].setChecked(false, true); + backgroundCheckBoxView[0].setChecked(false, true); animateMotionChange(); } updateMotionButton(); @@ -1555,7 +1705,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro updateMotionButton(); } updateSelectedPattern(true); - checkBoxView[1].setChecked(selectedPattern != null, true); + backgroundCheckBoxView[1].setChecked(selectedPattern != null, true); patternsListView.invalidateViews(); int left = view.getLeft(); @@ -1919,10 +2069,14 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } private void selectColorType(int id) { + selectColorType(id, true); + } + + private void selectColorType(int id, boolean ask) { if (getParentActivity() == null || colorType == id || patternViewAnimation != null) { return; } - if (id == 2 && (Theme.hasCustomWallpaper() || accent.backgroundOverrideColor == 0x100000000L)) { + if (ask && id == 2 && (Theme.hasCustomWallpaper() || accent.backgroundOverrideColor == 0x100000000L)) { AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("ChangeChatBackground", R.string.ChangeChatBackground)); if (!Theme.hasCustomWallpaper() || Theme.isCustomWallpaperColor()) { @@ -1938,7 +2092,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } removeBackgroundOverride = true; Theme.resetCustomWallpaper(true); - selectColorType(2); + selectColorType(2, false); }); builder.setNegativeButton(LocaleController.getString("Continue", R.string.Continue), (dialog, which) -> { if (Theme.isCustomWallpaperColor()) { @@ -1963,7 +2117,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro selectedPattern = null; } removeBackgroundOverride = true; - checkBoxView[1].setChecked(selectedPattern != null, true); + backgroundCheckBoxView[1].setChecked(selectedPattern != null, true); updatePlayAnimationView(false); Theme.refreshThemeColors(); } @@ -1986,7 +2140,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro intensitySeekBar.setProgress(currentIntensity); } Theme.resetCustomWallpaper(true); - selectColorType(2); + selectColorType(2, false); }); } else { builder.setMessage(LocaleController.getString("ChangeWallpaperToColor", R.string.ChangeWallpaperToColor)); @@ -2001,7 +2155,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } removeBackgroundOverride = true; Theme.resetCustomWallpaper(true); - selectColorType(2); + selectColorType(2, false); }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); } @@ -2015,8 +2169,11 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro dropDown.setText(LocaleController.getString("ColorPickerMainColor", R.string.ColorPickerMainColor)); colorPicker.setType(1, hasChanges(1), false, 1, false, 0, false); colorPicker.setColor(accent.accentColor, 0); + if (prevType == 2 || prevType == 3 && accent.myMessagesGradientAccentColor2 != 0) { + messagesAdapter.notifyItemRemoved(0); + } break; - case 2: + case 2: { dropDown.setText(LocaleController.getString("ColorPickerBackground", R.string.ColorPickerBackground)); int defaultBackground = Theme.getColor(Theme.key_chat_wallpaper); @@ -2054,28 +2211,67 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro colorPicker.setColor(backgroundGradientOverrideColor2 != 0 ? backgroundGradientOverrideColor2 : defaultGradient2, 2); colorPicker.setColor(backgroundGradientOverrideColor1 != 0 ? backgroundGradientOverrideColor1 : defaultGradient1, 1); colorPicker.setColor(backgroundOverrideColor != 0 ? backgroundOverrideColor : defaultBackground, 0); - messagesAdapter.notifyItemInserted(0); + if (prevType == 1 || accent.myMessagesGradientAccentColor2 == 0) { + messagesAdapter.notifyItemInserted(0); + } else { + messagesAdapter.notifyItemChanged(0); + } listView2.smoothScrollBy(0, AndroidUtilities.dp(60)); break; - case 3: + } + case 3: { dropDown.setText(LocaleController.getString("ColorPickerMyMessages", R.string.ColorPickerMyMessages)); - colorPicker.setType(2, hasChanges(3), true, accent.myMessagesGradientAccentColor != 0 ? 2 : 1, true, 0, false); - colorPicker.setColor(accent.myMessagesGradientAccentColor, 1); + int count; + if (accent.myMessagesGradientAccentColor1 != 0) { + if (accent.myMessagesGradientAccentColor3 != 0) { + count = 4; + } else if (accent.myMessagesGradientAccentColor2 != 0) { + count = 3; + } else { + count = 2; + } + } else { + count = 1; + } + colorPicker.setType(2, hasChanges(3), true, count, true, 0, false); + colorPicker.setColor(accent.myMessagesGradientAccentColor3, 3); + colorPicker.setColor(accent.myMessagesGradientAccentColor2, 2); + colorPicker.setColor(accent.myMessagesGradientAccentColor1, 1); colorPicker.setColor(accent.myMessagesAccentColor != 0 ? accent.myMessagesAccentColor : accent.accentColor, 0); + messagesCheckBoxView[1].setColor(0, accent.myMessagesAccentColor); + messagesCheckBoxView[1].setColor(1, accent.myMessagesGradientAccentColor1); + messagesCheckBoxView[1].setColor(2, accent.myMessagesGradientAccentColor2); + messagesCheckBoxView[1].setColor(3, accent.myMessagesGradientAccentColor3); + if (accent.myMessagesGradientAccentColor2 != 0) { + if (prevType == 1) { + messagesAdapter.notifyItemInserted(0); + } else { + messagesAdapter.notifyItemChanged(0); + } + } else if (prevType == 2) { + messagesAdapter.notifyItemRemoved(0); + } + listView2.smoothScrollBy(0, AndroidUtilities.dp(60)); + showAnimationHint(); break; + } } if (id == 1 || id == 3) { if (prevType == 2) { - messagesAdapter.notifyItemRemoved(0); if (patternLayout[1].getVisibility() == View.VISIBLE) { showPatternsView(0, true, true); } } - if (applyingTheme.isDark()) { - colorPicker.setMinBrightness(0.2f); + if (id == 1) { + if (applyingTheme.isDark()) { + colorPicker.setMinBrightness(0.2f); + } else { + colorPicker.setMinBrightness(0.05f); + colorPicker.setMaxBrightness(0.8f); + } } else { - colorPicker.setMinBrightness(0.05f); - colorPicker.setMaxBrightness(0.8f); + colorPicker.setMinBrightness(0f); + colorPicker.setMaxBrightness(1f); } } else { colorPicker.setMinBrightness(0f); @@ -2095,7 +2291,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } backgroundImage.setImage(ImageLocation.getForDocument(wallPaper.document), imageFilter, null, null, "jpg", wallPaper.document.size, 1, wallPaper); selectedPattern = wallPaper; - isMotion = checkBoxView[2].isChecked(); + isMotion = backgroundCheckBoxView[2].isChecked(); updateButtonState(false, true); } @@ -2190,15 +2386,36 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro return true; } } - if (backupMyMessagesGradientAccentColor != 0) { - if (backupMyMessagesGradientAccentColor != accent.myMessagesGradientAccentColor) { + if (backupMyMessagesGradientAccentColor1 != 0) { + if (backupMyMessagesGradientAccentColor1 != accent.myMessagesGradientAccentColor1) { return true; } } else { - if (accent.myMessagesGradientAccentColor != 0) { + if (accent.myMessagesGradientAccentColor1 != 0) { return true; } } + if (backupMyMessagesGradientAccentColor2 != 0) { + if (backupMyMessagesGradientAccentColor2 != accent.myMessagesGradientAccentColor2) { + return true; + } + } else { + if (accent.myMessagesGradientAccentColor2 != 0) { + return true; + } + } + if (backupMyMessagesGradientAccentColor3 != 0) { + if (backupMyMessagesGradientAccentColor3 != accent.myMessagesGradientAccentColor3) { + return true; + } + } else { + if (accent.myMessagesGradientAccentColor3 != 0) { + return true; + } + } + if (backupMyMessagesAnimated != accent.myMessagesAnimated) { + return true; + } } return false; } @@ -2207,7 +2424,10 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro if (screenType == SCREEN_TYPE_ACCENT_COLOR && ( accent.accentColor != backupAccentColor || accent.myMessagesAccentColor != backupMyMessagesAccentColor || - accent.myMessagesGradientAccentColor != backupMyMessagesGradientAccentColor || + accent.myMessagesGradientAccentColor1 != backupMyMessagesGradientAccentColor1 || + accent.myMessagesGradientAccentColor2 != backupMyMessagesGradientAccentColor2 || + accent.myMessagesGradientAccentColor3 != backupMyMessagesGradientAccentColor3 || + accent.myMessagesAnimated != backupMyMessagesAnimated || accent.backgroundOverrideColor != backupBackgroundOverrideColor || accent.backgroundGradientOverrideColor1 != backupBackgroundGradientOverrideColor1 || accent.backgroundGradientOverrideColor2 != backupBackgroundGradientOverrideColor2 || @@ -2232,6 +2452,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro @Override public boolean onFragmentCreate() { NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.invalidateMotionBackground); if (screenType == SCREEN_TYPE_ACCENT_COLOR || screenType == SCREEN_TYPE_PREVIEW) { NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.didSetNewWallpapper); } @@ -2266,6 +2487,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro @Override public void onFragmentDestroy() { NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.invalidateMotionBackground); if (frameLayout != null && onGlobalLayoutListener != null) { frameLayout.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener); } @@ -2410,6 +2632,10 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro cell.update(0); } } + } else if (id == NotificationCenter.invalidateMotionBackground) { + if (listView2 != null) { + listView2.invalidateViews(); + } } else if (id == NotificationCenter.didSetNewWallpapper) { if (page2 != null) { setCurrentImage(true); @@ -2535,7 +2761,10 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro if (editingTheme) { accent.accentColor = backupAccentColor; accent.myMessagesAccentColor = backupMyMessagesAccentColor; - accent.myMessagesGradientAccentColor = backupMyMessagesGradientAccentColor; + accent.myMessagesGradientAccentColor1 = backupMyMessagesGradientAccentColor1; + accent.myMessagesGradientAccentColor2 = backupMyMessagesGradientAccentColor2; + accent.myMessagesGradientAccentColor3 = backupMyMessagesGradientAccentColor3; + accent.myMessagesAnimated = backupMyMessagesAnimated; accent.backgroundOverrideColor = backupBackgroundOverrideColor; accent.backgroundGradientOverrideColor1 = backupBackgroundGradientOverrideColor1; accent.backgroundGradientOverrideColor2 = backupBackgroundGradientOverrideColor2; @@ -2608,13 +2837,25 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } else { accent.myMessagesAccentColor = 0; } - if (backupMyMessagesGradientAccentColor != 0) { - accent.myMessagesGradientAccentColor = backupMyMessagesGradientAccentColor; + if (backupMyMessagesGradientAccentColor1 != 0) { + accent.myMessagesGradientAccentColor1 = backupMyMessagesGradientAccentColor1; } else { - accent.myMessagesGradientAccentColor = 0; + accent.myMessagesGradientAccentColor1 = 0; + } + if (backupMyMessagesGradientAccentColor2 != 0) { + accent.myMessagesGradientAccentColor2 = backupMyMessagesGradientAccentColor2; + } else { + accent.myMessagesGradientAccentColor2 = 0; + } + if (backupMyMessagesGradientAccentColor3 != 0) { + accent.myMessagesGradientAccentColor3 = backupMyMessagesGradientAccentColor3; + } else { + accent.myMessagesGradientAccentColor3 = 0; } if (colorType == 3) { - colorPicker.setColor(accent.myMessagesGradientAccentColor, 1); + colorPicker.setColor(accent.myMessagesGradientAccentColor3, 3); + colorPicker.setColor(accent.myMessagesGradientAccentColor2, 2); + colorPicker.setColor(accent.myMessagesGradientAccentColor1, 1); colorPicker.setColor(accent.myMessagesAccentColor != 0 ? accent.myMessagesAccentColor : accent.accentColor, 0); } } @@ -2668,18 +2909,33 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } } } - Theme.refreshThemeColors(true); + Theme.refreshThemeColors(true, false); colorPicker.setHasChanges(hasChanges(colorType)); updatePlayAnimationView(true); } else if (colorType == 3) { if (lastPickedColorNum == 0) { accent.myMessagesAccentColor = color; + } else if (lastPickedColorNum == 1) { + accent.myMessagesGradientAccentColor1 = color; + } else if (lastPickedColorNum == 2) { + int prevColor = accent.myMessagesGradientAccentColor2; + accent.myMessagesGradientAccentColor2 = color; + if (prevColor != 0 && color == 0) { + messagesAdapter.notifyItemRemoved(0); + } else if (prevColor == 0 && color != 0) { + messagesAdapter.notifyItemInserted(0); + showAnimationHint(); + } } else { - accent.myMessagesGradientAccentColor = color; + accent.myMessagesGradientAccentColor3 = color; } - Theme.refreshThemeColors(); + if (lastPickedColorNum >= 0) { + messagesCheckBoxView[1].setColor(lastPickedColorNum, color); + } + Theme.refreshThemeColors(true, true); listView2.invalidateViews(); colorPicker.setHasChanges(hasChanges(colorType)); + updatePlayAnimationView(true); } for (int i = 0, size = themeDescriptions.size(); i < size; i++) { @@ -2764,8 +3020,8 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } backgroundImage.invalidate(); } - if (selectedPattern == null && buttonsContainer != null) { - buttonsContainer.setAlpha(fileExists ? 1.0f : 0.5f); + if (selectedPattern == null && backgroundButtonsContainer != null) { + backgroundButtonsContainer.setAlpha(fileExists ? 1.0f : 0.5f); } if (screenType == SCREEN_TYPE_PREVIEW) { doneButton.setEnabled(fileExists); @@ -2805,6 +3061,32 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } } + private void showAnimationHint() { + if (page2 == null || messagesCheckBoxView == null || accent.myMessagesGradientAccentColor2 == 0) { + return; + } + SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + if (preferences.getBoolean("bganimationhint", false)) { + return; + } + if (animationHint == null) { + animationHint = new HintView(getParentActivity(), 8); + animationHint.setShowingDuration(5000); + animationHint.setAlpha(0); + animationHint.setVisibility(View.INVISIBLE); + animationHint.setText(LocaleController.getString("BackgroundAnimateInfo", R.string.BackgroundAnimateInfo)); + animationHint.setExtraTranslationY(AndroidUtilities.dp(6)); + frameLayout.addView(animationHint, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 10, 0, 10, 0)); + } + AndroidUtilities.runOnUIThread(() -> { + if (colorType != 3) { + return; + } + preferences.edit().putBoolean("bganimationhint", true).commit(); + animationHint.showForView(messagesCheckBoxView[0], true); + }, 500); + } + private void updateSelectedPattern(boolean animated) { int count = patternsListView.getChildCount(); for (int a = 0; a < count; a++) { @@ -2818,47 +3100,47 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro private void updateMotionButton() { if (screenType == SCREEN_TYPE_ACCENT_COLOR || screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { if (selectedPattern == null && currentWallpaper instanceof WallpapersListActivity.ColorWallpaper) { - checkBoxView[2].setChecked(false, true); + backgroundCheckBoxView[2].setChecked(false, true); } - checkBoxView[selectedPattern != null ? 2 : 0].setVisibility(View.VISIBLE); + backgroundCheckBoxView[selectedPattern != null ? 2 : 0].setVisibility(View.VISIBLE); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether( - ObjectAnimator.ofFloat(checkBoxView[2], View.ALPHA, selectedPattern != null ? 1.0f : 0.0f), - ObjectAnimator.ofFloat(checkBoxView[0], View.ALPHA, selectedPattern != null ? 0.0f : 1.0f)); + ObjectAnimator.ofFloat(backgroundCheckBoxView[2], View.ALPHA, selectedPattern != null ? 1.0f : 0.0f), + ObjectAnimator.ofFloat(backgroundCheckBoxView[0], View.ALPHA, selectedPattern != null ? 0.0f : 1.0f)); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - checkBoxView[selectedPattern != null ? 0 : 2].setVisibility(View.INVISIBLE); + backgroundCheckBoxView[selectedPattern != null ? 0 : 2].setVisibility(View.INVISIBLE); } }); animatorSet.setInterpolator(CubicBezierInterpolator.EASE_OUT); animatorSet.setDuration(200); animatorSet.start(); } else { - if (checkBoxView[0].isEnabled() == (selectedPattern != null)) { + if (backgroundCheckBoxView[0].isEnabled() == (selectedPattern != null)) { return; } if (selectedPattern == null) { - checkBoxView[0].setChecked(false, true); + backgroundCheckBoxView[0].setChecked(false, true); } - checkBoxView[0].setEnabled(selectedPattern != null); + backgroundCheckBoxView[0].setEnabled(selectedPattern != null); if (selectedPattern != null) { - checkBoxView[0].setVisibility(View.VISIBLE); + backgroundCheckBoxView[0].setVisibility(View.VISIBLE); } - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) checkBoxView[1].getLayoutParams(); + FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) backgroundCheckBoxView[1].getLayoutParams(); AnimatorSet animatorSet = new AnimatorSet(); int offset = (layoutParams.width + AndroidUtilities.dp(9)) / 2; - animatorSet.playTogether(ObjectAnimator.ofFloat(checkBoxView[0], View.ALPHA, selectedPattern != null ? 1.0f : 0.0f)); - animatorSet.playTogether(ObjectAnimator.ofFloat(checkBoxView[0], View.TRANSLATION_X, selectedPattern != null ? 0.0f : offset)); - animatorSet.playTogether(ObjectAnimator.ofFloat(checkBoxView[1], View.TRANSLATION_X, selectedPattern != null ? 0.0f : -offset)); + animatorSet.playTogether(ObjectAnimator.ofFloat(backgroundCheckBoxView[0], View.ALPHA, selectedPattern != null ? 1.0f : 0.0f)); + animatorSet.playTogether(ObjectAnimator.ofFloat(backgroundCheckBoxView[0], View.TRANSLATION_X, selectedPattern != null ? 0.0f : offset)); + animatorSet.playTogether(ObjectAnimator.ofFloat(backgroundCheckBoxView[1], View.TRANSLATION_X, selectedPattern != null ? 0.0f : -offset)); animatorSet.setInterpolator(CubicBezierInterpolator.EASE_OUT); animatorSet.setDuration(200); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (selectedPattern == null) { - checkBoxView[0].setVisibility(View.INVISIBLE); + backgroundCheckBoxView[0].setVisibility(View.INVISIBLE); } } }); @@ -2908,7 +3190,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } } if (screenType == SCREEN_TYPE_ACCENT_COLOR || screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { - checkBoxView[showMotion ? 2 : 0].setVisibility(View.VISIBLE); + backgroundCheckBoxView[showMotion ? 2 : 0].setVisibility(View.VISIBLE); } if (num == 1 && !intensitySeekBar.isTwoSided() && currentIntensity < 0) { currentIntensity = -currentIntensity; @@ -2922,8 +3204,8 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro patternLayout[num].setVisibility(View.VISIBLE); if (screenType == SCREEN_TYPE_ACCENT_COLOR) { animators.add(ObjectAnimator.ofFloat(listView2, View.TRANSLATION_Y, num == 1 ? -AndroidUtilities.dp(21) : 0)); - animators.add(ObjectAnimator.ofFloat(checkBoxView[2], View.ALPHA, showMotion ? 1.0f : 0.0f)); - animators.add(ObjectAnimator.ofFloat(checkBoxView[0], View.ALPHA, showMotion ? 0.0f : 1.0f)); + animators.add(ObjectAnimator.ofFloat(backgroundCheckBoxView[2], View.ALPHA, showMotion ? 1.0f : 0.0f)); + animators.add(ObjectAnimator.ofFloat(backgroundCheckBoxView[0], View.ALPHA, showMotion ? 0.0f : 1.0f)); if (num == 1) { animators.add(ObjectAnimator.ofFloat(patternLayout[num], View.ALPHA, 0.0f, 1.0f)); } else { @@ -2933,8 +3215,8 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro colorPicker.hideKeyboard(); } else if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { animators.add(ObjectAnimator.ofFloat(listView2, View.TRANSLATION_Y, -patternLayout[num].getMeasuredHeight() + AndroidUtilities.dp(48))); - animators.add(ObjectAnimator.ofFloat(checkBoxView[2], View.ALPHA, showMotion ? 1.0f : 0.0f)); - animators.add(ObjectAnimator.ofFloat(checkBoxView[0], View.ALPHA, showMotion ? 0.0f : 1.0f)); + animators.add(ObjectAnimator.ofFloat(backgroundCheckBoxView[2], View.ALPHA, showMotion ? 1.0f : 0.0f)); + animators.add(ObjectAnimator.ofFloat(backgroundCheckBoxView[0], View.ALPHA, showMotion ? 0.0f : 1.0f)); animators.add(ObjectAnimator.ofFloat(backgroundImage, View.ALPHA, 0.0f)); if (patternLayout[otherNum].getVisibility() == View.VISIBLE) { animators.add(ObjectAnimator.ofFloat(patternLayout[otherNum], View.ALPHA, 0.0f)); @@ -2955,8 +3237,8 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } else { animators.add(ObjectAnimator.ofFloat(listView2, View.TRANSLATION_Y, 0)); animators.add(ObjectAnimator.ofFloat(patternLayout[num], View.TRANSLATION_Y, patternLayout[num].getMeasuredHeight())); - animators.add(ObjectAnimator.ofFloat(checkBoxView[0], View.ALPHA, 1.0f)); - animators.add(ObjectAnimator.ofFloat(checkBoxView[2], View.ALPHA, 0.0f)); + animators.add(ObjectAnimator.ofFloat(backgroundCheckBoxView[0], View.ALPHA, 1.0f)); + animators.add(ObjectAnimator.ofFloat(backgroundCheckBoxView[2], View.ALPHA, 0.0f)); animators.add(ObjectAnimator.ofFloat(backgroundImage, View.ALPHA, 1.0f)); } patternViewAnimation.playTogether(animators); @@ -2971,7 +3253,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro patternLayout[num].setVisibility(View.INVISIBLE); } if (screenType == SCREEN_TYPE_ACCENT_COLOR || screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { - checkBoxView[showMotion ? 0 : 2].setVisibility(View.INVISIBLE); + backgroundCheckBoxView[showMotion ? 0 : 2].setVisibility(View.INVISIBLE); } else { if (num == 1) { patternLayout[otherNum].setAlpha(0.0f); @@ -2988,8 +3270,8 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro patternLayout[num].setVisibility(View.VISIBLE); if (screenType == SCREEN_TYPE_ACCENT_COLOR) { listView2.setTranslationY(num == 1 ? -AndroidUtilities.dp(21) : 0); - checkBoxView[2].setAlpha(showMotion ? 1.0f : 0.0f); - checkBoxView[0].setAlpha(showMotion ? 0.0f : 1.0f); + backgroundCheckBoxView[2].setAlpha(showMotion ? 1.0f : 0.0f); + backgroundCheckBoxView[0].setAlpha(showMotion ? 0.0f : 1.0f); if (num == 1) { patternLayout[num].setAlpha(1.0f); } else { @@ -2999,8 +3281,8 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro colorPicker.hideKeyboard(); } else if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { listView2.setTranslationY(-AndroidUtilities.dp(num == 0 ? 343 : 316) + AndroidUtilities.dp(48)); - checkBoxView[2].setAlpha(showMotion ? 1.0f : 0.0f); - checkBoxView[0].setAlpha(showMotion ? 0.0f : 1.0f); + backgroundCheckBoxView[2].setAlpha(showMotion ? 1.0f : 0.0f); + backgroundCheckBoxView[0].setAlpha(showMotion ? 0.0f : 1.0f); backgroundImage.setAlpha(0.0f); if (patternLayout[otherNum].getVisibility() == View.VISIBLE) { patternLayout[otherNum].setAlpha(0.0f); @@ -3021,8 +3303,8 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } else { listView2.setTranslationY(0); patternLayout[num].setTranslationY(patternLayout[num].getMeasuredHeight()); - checkBoxView[0].setAlpha(1.0f); - checkBoxView[2].setAlpha(1.0f); + backgroundCheckBoxView[0].setAlpha(1.0f); + backgroundCheckBoxView[2].setAlpha(1.0f); backgroundImage.setAlpha(1.0f); } @@ -3033,7 +3315,7 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro patternLayout[num].setVisibility(View.INVISIBLE); } if (screenType == SCREEN_TYPE_ACCENT_COLOR || screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { - checkBoxView[showMotion ? 0 : 2].setVisibility(View.INVISIBLE); + backgroundCheckBoxView[showMotion ? 0 : 2].setVisibility(View.INVISIBLE); } else { if (num == 1) { patternLayout[otherNum].setAlpha(0.0f); @@ -3097,62 +3379,101 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } } - if (playAnimationView == null) { - return; - } - boolean visible; - if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { - visible = backgroundGradientColor1 != 0; - } else if (screenType == SCREEN_TYPE_ACCENT_COLOR) { - int defaultBackgroundGradient1 = Theme.getDefaultAccentColor(Theme.key_chat_wallpaper_gradient_to1); - int backgroundGradientOverrideColor1 = (int) accent.backgroundGradientOverrideColor1; - int color1; - if (backgroundGradientOverrideColor1 == 0 && accent.backgroundGradientOverrideColor1 != 0) { - color1 = 0; + if (backgroundPlayAnimationView != null) { + boolean visible; + if (screenType == SCREEN_TYPE_CHANGE_BACKGROUND) { + visible = backgroundGradientColor1 != 0; + } else if (screenType == SCREEN_TYPE_ACCENT_COLOR) { + int defaultBackgroundGradient1 = Theme.getDefaultAccentColor(Theme.key_chat_wallpaper_gradient_to1); + int backgroundGradientOverrideColor1 = (int) accent.backgroundGradientOverrideColor1; + int color1; + if (backgroundGradientOverrideColor1 == 0 && accent.backgroundGradientOverrideColor1 != 0) { + color1 = 0; + } else { + color1 = backgroundGradientOverrideColor1 != 0 ? backgroundGradientOverrideColor1 : defaultBackgroundGradient1; + } + visible = color1 != 0; } else { - color1 = backgroundGradientOverrideColor1 != 0 ? backgroundGradientOverrideColor1 : defaultBackgroundGradient1; + visible = false; } - visible = color1 != 0; - } else { - visible = false; - } - boolean wasVisible = playAnimationView.getTag() != null; - playAnimationView.setTag(visible ? 1 : null); - if (wasVisible != visible) { - if (visible) { - playAnimationView.setVisibility(View.VISIBLE); - } - if (playViewAnimator != null) { - playViewAnimator.cancel(); - } - if (animated) { - playViewAnimator = new AnimatorSet(); - playViewAnimator.playTogether( - ObjectAnimator.ofFloat(playAnimationView, View.ALPHA, visible ? 1.0f : 0.0f), - ObjectAnimator.ofFloat(playAnimationView, View.SCALE_X, visible ? 1.0f : 0.0f), - ObjectAnimator.ofFloat(playAnimationView, View.SCALE_Y, visible ? 1.0f : 0.0f), - ObjectAnimator.ofFloat(checkBoxView[0], View.TRANSLATION_X, visible ? AndroidUtilities.dp(34) : 0.0f), - ObjectAnimator.ofFloat(checkBoxView[1], View.TRANSLATION_X, visible ? -AndroidUtilities.dp(34) : 0.0f), - ObjectAnimator.ofFloat(checkBoxView[2], View.TRANSLATION_X, visible ? AndroidUtilities.dp(34) : 0.0f)); - playViewAnimator.setDuration(180); - playViewAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (playAnimationView.getTag() == null) { - playAnimationView.setVisibility(View.INVISIBLE); + boolean wasVisible = backgroundPlayAnimationView.getTag() != null; + backgroundPlayAnimationView.setTag(visible ? 1 : null); + if (wasVisible != visible) { + if (visible) { + backgroundPlayAnimationView.setVisibility(View.VISIBLE); + } + if (backgroundPlayViewAnimator != null) { + backgroundPlayViewAnimator.cancel(); + } + if (animated) { + backgroundPlayViewAnimator = new AnimatorSet(); + backgroundPlayViewAnimator.playTogether( + ObjectAnimator.ofFloat(backgroundPlayAnimationView, View.ALPHA, visible ? 1.0f : 0.0f), + ObjectAnimator.ofFloat(backgroundPlayAnimationView, View.SCALE_X, visible ? 1.0f : 0.0f), + ObjectAnimator.ofFloat(backgroundPlayAnimationView, View.SCALE_Y, visible ? 1.0f : 0.0f), + ObjectAnimator.ofFloat(backgroundCheckBoxView[0], View.TRANSLATION_X, visible ? AndroidUtilities.dp(34) : 0.0f), + ObjectAnimator.ofFloat(backgroundCheckBoxView[1], View.TRANSLATION_X, visible ? -AndroidUtilities.dp(34) : 0.0f), + ObjectAnimator.ofFloat(backgroundCheckBoxView[2], View.TRANSLATION_X, visible ? AndroidUtilities.dp(34) : 0.0f)); + backgroundPlayViewAnimator.setDuration(180); + backgroundPlayViewAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (backgroundPlayAnimationView.getTag() == null) { + backgroundPlayAnimationView.setVisibility(View.INVISIBLE); + } + backgroundPlayViewAnimator = null; } - playViewAnimator = null; - } - }); - playViewAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT); - playViewAnimator.start(); - } else { - playAnimationView.setAlpha(visible ? 1.0f : 0.0f); - playAnimationView.setScaleX(visible ? 1.0f : 0.0f); - playAnimationView.setScaleY(visible ? 1.0f : 0.0f); - checkBoxView[0].setTranslationX(visible ? AndroidUtilities.dp(34) : 0.0f); - checkBoxView[1].setTranslationX(visible ? -AndroidUtilities.dp(34) : 0.0f); - checkBoxView[2].setTranslationX(visible ? AndroidUtilities.dp(34) : 0.0f); + }); + backgroundPlayViewAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT); + backgroundPlayViewAnimator.start(); + } else { + backgroundPlayAnimationView.setAlpha(visible ? 1.0f : 0.0f); + backgroundPlayAnimationView.setScaleX(visible ? 1.0f : 0.0f); + backgroundPlayAnimationView.setScaleY(visible ? 1.0f : 0.0f); + backgroundCheckBoxView[0].setTranslationX(visible ? AndroidUtilities.dp(34) : 0.0f); + backgroundCheckBoxView[1].setTranslationX(visible ? -AndroidUtilities.dp(34) : 0.0f); + backgroundCheckBoxView[2].setTranslationX(visible ? AndroidUtilities.dp(34) : 0.0f); + } + } + } + if (messagesPlayAnimationView != null) { + boolean visible = true;//accent.myMessagesGradientAccentColor1 != 0; + boolean wasVisible = messagesPlayAnimationView.getTag() != null; + messagesPlayAnimationView.setTag(visible ? 1 : null); + if (wasVisible != visible) { + if (visible) { + messagesPlayAnimationView.setVisibility(View.VISIBLE); + } + if (messagesPlayViewAnimator != null) { + messagesPlayViewAnimator.cancel(); + } + if (animated) { + messagesPlayViewAnimator = new AnimatorSet(); + messagesPlayViewAnimator.playTogether( + ObjectAnimator.ofFloat(messagesPlayAnimationView, View.ALPHA, visible ? 1.0f : 0.0f), + ObjectAnimator.ofFloat(messagesPlayAnimationView, View.SCALE_X, visible ? 1.0f : 0.0f), + ObjectAnimator.ofFloat(messagesPlayAnimationView, View.SCALE_Y, visible ? 1.0f : 0.0f), + ObjectAnimator.ofFloat(messagesCheckBoxView[0], View.TRANSLATION_X, visible ? -AndroidUtilities.dp(34) : 0.0f), + ObjectAnimator.ofFloat(messagesCheckBoxView[1], View.TRANSLATION_X, visible ? AndroidUtilities.dp(34) : 0.0f)); + messagesPlayViewAnimator.setDuration(180); + messagesPlayViewAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (messagesPlayAnimationView.getTag() == null) { + messagesPlayAnimationView.setVisibility(View.INVISIBLE); + } + messagesPlayViewAnimator = null; + } + }); + messagesPlayViewAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT); + messagesPlayViewAnimator.start(); + } else { + messagesPlayAnimationView.setAlpha(visible ? 1.0f : 0.0f); + messagesPlayAnimationView.setScaleX(visible ? 1.0f : 0.0f); + messagesPlayAnimationView.setScaleY(visible ? 1.0f : 0.0f); + messagesCheckBoxView[0].setTranslationX(visible ? -AndroidUtilities.dp(34) : 0.0f); + messagesCheckBoxView[1].setTranslationX(visible ? AndroidUtilities.dp(34) : 0.0f); + } } } } @@ -3168,10 +3489,10 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro backgroundGradientColor3 = color; } updatePlayAnimationView(animated); - if (checkBoxView != null) { - for (int a = 0; a < checkBoxView.length; a++) { - if (checkBoxView[a] != null) { - checkBoxView[a].setColor(num, color); + if (backgroundCheckBoxView != null) { + for (int a = 0; a < backgroundCheckBoxView.length; a++) { + if (backgroundCheckBoxView[a] != null) { + backgroundCheckBoxView[a].setColor(num, color); } } } @@ -3208,8 +3529,11 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro int c = Theme.getColor(Theme.key_chat_serviceBackground); Theme.applyChatServiceMessageColor(new int[]{c, c, c, c}, backgroundImage.getBackground()); } - if (playAnimationImageView != null) { - playAnimationImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); + if (backgroundPlayAnimationImageView != null) { + backgroundPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); + } + if (messagesPlayAnimationImageView != null) { + messagesPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); } if (backgroundImage != null) { backgroundImage.getImageReceiver().setColorFilter(new PorterDuffColorFilter(patternColor, blendMode)); @@ -3235,9 +3559,14 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro if (listView2 != null) { listView2.invalidateViews(); } - if (buttonsContainer != null) { - for (int a = 0, N = buttonsContainer.getChildCount(); a < N; a++) { - buttonsContainer.getChildAt(a).invalidate(); + if (backgroundButtonsContainer != null) { + for (int a = 0, N = backgroundButtonsContainer.getChildCount(); a < N; a++) { + backgroundButtonsContainer.getChildAt(a).invalidate(); + } + } + if (messagesButtonsContainer != null) { + for (int a = 0, N = messagesButtonsContainer.getChildCount(); a < N; a++) { + messagesButtonsContainer.getChildAt(a).invalidate(); } } if (radialProgress != null) { @@ -3415,20 +3744,28 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro intensitySeekBar.setProgress(currentIntensity); } } - if (checkBoxView != null) { - for (int a = 0; a < checkBoxView.length; a++) { - checkBoxView[a].setColor(0, backgroundColor); - checkBoxView[a].setColor(1, color1); - checkBoxView[a].setColor(2, color2); - checkBoxView[a].setColor(3, color3); + if (backgroundCheckBoxView != null) { + for (int a = 0; a < backgroundCheckBoxView.length; a++) { + backgroundCheckBoxView[a].setColor(0, backgroundColor); + backgroundCheckBoxView[a].setColor(1, color1); + backgroundCheckBoxView[a].setColor(2, color2); + backgroundCheckBoxView[a].setColor(3, color3); } } - if (playAnimationImageView != null) { - playAnimationImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); + if (backgroundPlayAnimationImageView != null) { + backgroundPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); } - if (buttonsContainer != null) { - for (int a = 0, N = buttonsContainer.getChildCount(); a < N; a++) { - buttonsContainer.getChildAt(a).invalidate(); + if (messagesPlayAnimationImageView != null) { + messagesPlayAnimationImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_chat_serviceText), PorterDuff.Mode.MULTIPLY)); + } + if (backgroundButtonsContainer != null) { + for (int a = 0, N = backgroundButtonsContainer.getChildCount(); a < N; a++) { + backgroundButtonsContainer.getChildAt(a).invalidate(); + } + } + if (messagesButtonsContainer != null) { + for (int a = 0, N = messagesButtonsContainer.getChildCount(); a < N; a++) { + messagesButtonsContainer.getChildAt(a).invalidate(); } } } @@ -3678,6 +4015,25 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro message.peer_id.user_id = 0; MessageObject replyMessageObject = new MessageObject(UserConfig.selectedAccount, message, true, false); + if (BuildVars.DEBUG_PRIVATE_VERSION) { + message = new TLRPC.TL_message(); + message.message = "this is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text\nthis is very very long text"; + message.date = date + 960; + message.dialog_id = 1; + message.flags = 259; + message.from_id = new TLRPC.TL_peerUser(); + message.from_id.user_id = UserConfig.getInstance(UserConfig.selectedAccount).getClientUserId(); + message.id = 1; + message.media = new TLRPC.TL_messageMediaEmpty(); + message.out = true; + message.peer_id = new TLRPC.TL_peerUser(); + message.peer_id.user_id = 0; + MessageObject message1 = new MessageObject(UserConfig.selectedAccount, message, true, false); + message1.resetLayout(); + message1.eventId = 1; + messages.add(message1); + } + message = new TLRPC.TL_message(); String text = LocaleController.getString("NewThemePreviewLine3", R.string.NewThemePreviewLine3); StringBuilder builder = new StringBuilder(text); @@ -3948,7 +4304,8 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro } private boolean hasButtons() { - return buttonsContainer != null && (screenType == SCREEN_TYPE_CHANGE_BACKGROUND || screenType == SCREEN_TYPE_ACCENT_COLOR && colorType == 2); + return messagesButtonsContainer != null && screenType == SCREEN_TYPE_ACCENT_COLOR && colorType == 3 && accent.myMessagesGradientAccentColor2 != 0 || + backgroundButtonsContainer != null && (screenType == SCREEN_TYPE_CHANGE_BACKGROUND || screenType == SCREEN_TYPE_ACCENT_COLOR && colorType == 2); } @Override @@ -3980,8 +4337,8 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro }); } else if (viewType == 2) { - if (buttonsContainer.getParent() != null) { - ((ViewGroup) buttonsContainer.getParent()).removeView(buttonsContainer); + if (backgroundButtonsContainer.getParent() != null) { + ((ViewGroup) backgroundButtonsContainer.getParent()).removeView(backgroundButtonsContainer); } FrameLayout frameLayout = new FrameLayout(mContext) { @Override @@ -3989,7 +4346,19 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(60), MeasureSpec.EXACTLY)); } }; - frameLayout.addView(buttonsContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 76, Gravity.CENTER)); + frameLayout.addView(backgroundButtonsContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 76, Gravity.CENTER)); + view = frameLayout; + } else if (viewType == 3) { + if (messagesButtonsContainer.getParent() != null) { + ((ViewGroup) messagesButtonsContainer.getParent()).removeView(messagesButtonsContainer); + } + FrameLayout frameLayout = new FrameLayout(mContext) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(60), MeasureSpec.EXACTLY)); + } + }; + frameLayout.addView(messagesButtonsContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 76, Gravity.CENTER)); view = frameLayout; } view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); @@ -3998,7 +4367,8 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { - if (holder.getItemViewType() != 2) { + int type = holder.getItemViewType(); + if (type != 2 && type != 3) { if (hasButtons()) { position--; } @@ -4039,7 +4409,11 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro public int getItemViewType(int position) { if (hasButtons()) { if (position == 0) { - return 2; + if (colorType == 3) { + return 3; + } else { + return 2; + } } position--; } @@ -4251,7 +4625,9 @@ public class ThemePreviewActivity extends BaseFragment implements DownloadContro items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgInDrawable.getShadowDrawables(), null, Theme.key_chat_inBubbleShadow)); items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgInMediaDrawable.getShadowDrawables(), null, Theme.key_chat_inBubbleShadow)); items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubble)); - items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient)); + items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient1)); + items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient2)); + items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient3)); items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutSelectedDrawable, Theme.chat_msgOutMediaSelectedDrawable}, null, Theme.key_chat_outBubbleSelected)); items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgOutDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow)); items.add(new ThemeDescription(listView2, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgOutMediaDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ThemeSetUrlActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ThemeSetUrlActivity.java index ec6fd5e3c..855e76552 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ThemeSetUrlActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ThemeSetUrlActivity.java @@ -689,7 +689,9 @@ public class ThemeSetUrlActivity extends BaseFragment implements NotificationCen themeDescriptions.add(new ThemeDescription(messagesCell, 0, null, null, Theme.chat_msgInDrawable.getShadowDrawables(), null, Theme.key_chat_inBubbleShadow)); themeDescriptions.add(new ThemeDescription(messagesCell, 0, null, null, Theme.chat_msgInMediaDrawable.getShadowDrawables(), null, Theme.key_chat_inBubbleShadow)); themeDescriptions.add(new ThemeDescription(messagesCell, 0, null, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubble)); - themeDescriptions.add(new ThemeDescription(messagesCell, 0, null, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient)); + themeDescriptions.add(new ThemeDescription(messagesCell, 0, null, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient1)); + themeDescriptions.add(new ThemeDescription(messagesCell, 0, null, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient2)); + themeDescriptions.add(new ThemeDescription(messagesCell, 0, null, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient3)); themeDescriptions.add(new ThemeDescription(messagesCell, 0, null, null, new Drawable[]{Theme.chat_msgOutSelectedDrawable, Theme.chat_msgOutMediaSelectedDrawable}, null, Theme.key_chat_outBubbleSelected)); themeDescriptions.add(new ThemeDescription(messagesCell, 0, null, null, Theme.chat_msgOutDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow)); themeDescriptions.add(new ThemeDescription(messagesCell, 0, null, null, Theme.chat_msgOutMediaDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/WallpapersListActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/WallpapersListActivity.java index 504e01f4d..491388012 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/WallpapersListActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/WallpapersListActivity.java @@ -416,7 +416,7 @@ public class WallpapersListActivity extends BaseFragment implements Notification NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.wallpapersNeedReload); getMessagesStorage().getWallpapers(); } else { - boolean darkTheme = Theme.isCurrentThemeNight(); + boolean darkTheme = Theme.isCurrentThemeDark(); int[][] defaultColors = darkTheme ? defaultColorsDark : defaultColorsLight; for (int a = 0; a < defaultColors.length; a++) { if (defaultColors[a].length == 1) { @@ -991,6 +991,9 @@ public class WallpapersListActivity extends BaseFragment implements Notification } allWallPapersDict.put(wallPaper.slug, wallPaper); if (currentType != TYPE_COLOR && (!wallPaper.pattern || wallPaper.settings != null && wallPaper.settings.background_color != 0)) { + if (!Theme.isCurrentThemeDark() && wallPaper.settings != null && wallPaper.settings.intensity < 0) { + continue; + } wallPapers.add(wallPaper); } } else if (wallPaper.settings.background_color != 0) { @@ -1016,6 +1019,9 @@ public class WallpapersListActivity extends BaseFragment implements Notification localWallPapers.add(colorWallpaper); localDict.put(hash, colorWallpaper); } + if (!Theme.isCurrentThemeDark() && wallPaper.settings != null && wallPaper.settings.intensity < 0) { + continue; + } wallPapers.add(colorWallpaper); } } @@ -1083,9 +1089,15 @@ public class WallpapersListActivity extends BaseFragment implements Notification patternsDict.put(wallPaper.document.id, wallPaper); } if (currentType != TYPE_COLOR && (!wallPaper.pattern || wallPaper.settings != null && wallPaper.settings.background_color != 0)) { + if (!Theme.isCurrentThemeDark() && wallPaper.settings != null && wallPaper.settings.intensity < 0) { + continue; + } wallPapers.add(wallPaper); } } else if (wallPaper.settings.background_color != 0) { + if (!Theme.isCurrentThemeDark() && wallPaper.settings != null && wallPaper.settings.intensity < 0) { + continue; + } ColorWallpaper colorWallpaper; if (wallPaper.settings.second_background_color != 0 && wallPaper.settings.third_background_color != 0) { colorWallpaper = new ColorWallpaper(null, wallPaper.settings.background_color, wallPaper.settings.second_background_color, wallPaper.settings.third_background_color, wallPaper.settings.fourth_background_color); diff --git a/TMessagesProj/src/main/res/drawable-hdpi/emoji_tabs_faves.png b/TMessagesProj/src/main/res/drawable-hdpi/emoji_tabs_faves.png new file mode 100644 index 0000000000000000000000000000000000000000..7ff44f700b15bddfe7680e6c372922dae7767180 GIT binary patch literal 1465 zcmV;q1xEUbP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91IiLdo1ONa40RR91IRF3v0Jo8fpa1{`BS}O-RA>e5n%|36RTRg)nIR=< zf+dA^g~|tm6h$*c2kI}#pr8h-mmtVjq359Iu(x2uhr)-b6is1jMx%!aB8%*Yg?Dg5 zr6M09u>3K7zIpbYbysK4`Q1I>{lb^M*WPQd?^c0YWb92k3R8sciIn6#D9e^2SCb>ovmOtZW+MQnG4 z$lR0^xhZ9CdwI1Xt)z%!u8_kv73L1R`D1pz^?j1x>I#s##U?I0#b>MaX?36EkGKMk zrX`HeQRjCg&98N1B)!=sE^}L2q8d-}-JIszsxgv2hwLn1FYtNJNL^5BEc@uZria(7^3V zWbK1TtV5))ebIyVwozZV&Mddhb=p!Ohg(><2Kp_ct)l0^5}1Xr$)R6|FY*GbSDOfP z^z~5q0osCQ2%`P*zk&;(=Rg;RXM@`HP!Bg-%LVegm$CnXAa6sDw;0gXZ5@;sRDK=@ zqsijA8tNsJ#{mXfRjBfT%F9Ffr~eg0zWvs9+a+!^0S`^#pMaG3V!7yV^6}{i$^TJLOO{a%>a~_Ju4Rv z9##-&CH!I_{0-v-%^kt7iB|EUbUc*L%33uExB}{I=O>|!qf@tl79?~y#E-suK0=j*sp~d;afpRQB;Jg)~KMfn*9{scs(t*L^Vt=y6 z4gwdg2>ouE@MVu`4>+;!oL!gcvePKlr8dM7u_f0rc0YX zuGh^K_@*c_*OjnNZ2k`X!#xlh`OHE?pUoE1wO@{HYW4LI=~`9#vw5TAtCQ21+5&54 z@we#aT^sz$+^&9K_0`tg;?Oru3o#NF@>wDf}O z1*mtqf?C;UG%r65MsV7V52Z+-gpPm9;cFy!7Vd$u@1Gji{8{;H9et0=e@P!O(VvnE4* zd`oXP)YDn5>=+bB@f26_r4u0iaNE~kC&AYTrKHtuHV)OsFvG<2a3C?+$++fY2=RN) zLGN0d0Ydr<Cw~4g&TtgFo2ihfNWK)6ou9Zw_f=TvE0O$@by}Z= z7n>|H?FZA4kbbCn9o|(X0j*EsC|(TB{sRT76HfVW>^EVxH)ih2C|(j*@h$c{*G#kU zIS@6ic@pk~`hn;mKd#KsHUjjz1RjU?z$y4Xdio)1gwYikU4hXR7+ry(UV(oAsPtmD TS*&lq00000NkvXXu0mjfmd(2h literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/emoji_tabs_new1.png b/TMessagesProj/src/main/res/drawable-hdpi/emoji_tabs_new1.png new file mode 100644 index 0000000000000000000000000000000000000000..5fece5fe504bf6fc38e00347d61f8cb383790b80 GIT binary patch literal 1495 zcmV;|1t|K7P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91IiLdo1ONa40RR91IRF3v0Jo8fpa1{`K}keGRA>e5n%|36RTRg)WsZtD zrf6yk(SykdDw025l73*KNohXiYfqJri9Yz?i$91U{R0F+2`U0J5G0bw@CQLsQvM*q zha6Od(viYZ8$aLcnZr73_L;fo*PSzP7ks&U?Y-C9-?h))`|Pv#9XGBcogV1)K&J=V z+yjj&+8Y=cn2p~xpa(1fv%u3}0_X#Gz%}q2xByNz8jXH_>n#NKBG?X2ga1Jm`IX9( zY8xTsR)aHHB1)csFehi+kD#-C!9YaPopMb64$rSth zClK8Q@>Y-C$Rg$hF&Fd(0z68d=GWL3f|v+?3Iw>Dyh=6_ZSOGx`799Nwzd4GRqeIiFTsrmS6q9tY`>~#oz z6OsB5eK`;1yeH|O!$wg2tpEu#QQu&tRTf6+iDezh8wa$HSE; zqWcNkyE@+Rt76E2*^*KhM9(*9UpRMe(-NYojtuyCL(6 zA*2`Yw?>bXKFFhS$>YR2oK0|fbx}31?V53xDQBl!%rg-rl{BU=#ift$6xSHNPRVy1 znbePoM39k@f0{d;vpnj5&dBSoczBi3r7MX|u9oNRij9f`-FZ9&R(!7+i;DJ3{L7!ex$5iz@QNU==M#(p8#1X|rm1)X89OJ6V9?_=;V z59k=u%C2CmZOnv>P7-y=WaU_H3V7LPJ(1Q9Xmf6d1r2Gt0d&b^MR%h4uM9En23M^| z(s#kqf_!tC)2=R=yzI~A%o^cwF;HMu{z7SMptQHm5{S~e@h-GZ#IfQ?i-m@&6$M`T z(4~zoaL6_srFG*yJtkR30*5;T4Jq(lpq$=xMDeS|PnS$ybmQIQs+XhUSb_Q{Dgt`F z6nc+ZhWOH2um||novh}Kw>_b=rsbq*L_^olQ8g~nAC}$(x%Rr)%?2L<-MxfFH{Sl! zs+_>Q7aCdu{Hkk7X*hijwt)E{IGoAXJ5LMS4xkPSjS{k(q8smto=c@V4Qfc~JsKKP zWG6)AZ-DdQ2cY5I2U-zb1XsSm4cl1|(Bhyipx^6~RY-I{sw>3cC>WMhRfUkzrhYHD z2=Yolh9`i6^T(rwsF(gX6$t7Kb|Y8|wA81a6Y2kVx`y}}X#aUUI%gtTu9P06r$^8_ x8=VRC_0a`%^`N7}UV;jqlTy}^P7efn;1Sz875S^F+~)uQ002ovPDHLkV1mmYvV#Bs literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/emoji_tabs_new2.png b/TMessagesProj/src/main/res/drawable-hdpi/emoji_tabs_new2.png new file mode 100644 index 0000000000000000000000000000000000000000..f9d795ca414b94d15d241afa0e6842e1bfae8aba GIT binary patch literal 474 zcmeAS@N?(olHy`uVBq!ia0vp^mLSZ*1|)kVnp=SsV{wqX6T`Z5GB1G~m(&Q)G+$o^ zEg+kNfw4W4fd!-lh^2s-fq{7eBLg##W(0{XV1mn9E?`EmK?=7|>{nxM9K$);^rQmi%n5*?1Ir=HwnDQ>b>cC9@O zzccRFnRz^HXL?iDvcmYv*UzSZ8J+*eEt$UHT+xZ@loKGT*5$ia|7u=U8o-5sFa#X53QQ}s?|90+~a-RP}*PRftNvqGv&CV3u^2X(EYhu#r z(iZ8~g7xo0Zh45mRNMJK5UAcI$CHyqfq{|3fq_Y&fq@Xy!HDG^*XElR(_OjaLE-A@ L>gTe~DWM4f6Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91IiLdo1ONa40RR91IRF3v0Jo8fpa1{`)=5M`RA>e5noEpSMHGg+<)Pt` ziQ)r6GQLPe0~(1MS+#CVSl|Q_5|+jXF%o2hu8@Sd!?+Z~PG!XcqYxj7$q*MVa182w$)Bz|b>fU zr3AJN>;;!V8FCPkHQ=U>H<%z6f!*L5Xovg&X{~5OqsAl^tB1FNH^7{fAVw+s9-KDN z*t@Hah5SAV9tLT$hr*4F+keuGN-Lv*ec*GDB;Qf+7FdzBQf!xim%wKrNe)xcpOrJ( zj!Kq-;e;p-p|3@g*B#+l2@WLGajuLy5@UpX6}S+n{15CG zS_7Xb$ff1Q%k#N7@!shej+39Ai^Lh@|Eb|TMY_%q& zy`DJ0?*E$8bxU@i3*g&adCd>4>1A|+@xbeC|dS1Tf zgstHzy^*R3-XBf8O0Mz|_}5hjPF8o4SM{QI>azSH7tcK|U-1*l{mwvN^qGZT)QOzu zZ1~&GlWqa_jWYWy<#{?cSZEFF>EE4Brw&cx1M;6uzcpDWWC-?7ioOJoC76WN!y#o| zcN)$C8v1RDO`}N1n^ttB(j>lj{rJoJBwvL!PXn8R2xJza+aAz|RKAhw@Fww%)WNTn z^qK00#iQU& zWip7Kx)tAsrb=aa!CI5{1g`jcma%h%)GNWlx{@(AO0wVBtiGNpHKsM#wkbK+)am?kLpCqr(I0j!EBXzxH`I7;8U z!l^3>D{EUvQn$d%J8#YGoL~s&h>eD{wt&%sf6ejbdl)!xbp-lwE*g}s_!@FJx^*~0 zqVr(gy;#|sixbZ|K<6-4cIF~+#t_{MUI*Jjp&1*0-Mv`R{pd1RW$P?XFIyJUmw|RQ z9Tj5e1O5zZcT`Xi%>X|ED>}97*x4&}D<)ksS^1lc-wRO@DI%=bZYc|*n zbjjpJH{MTM`%=@8>xqUgWfh2eSDT3FEg-%#!3Ll=BqZu&`%PME`zFb#L}N5G88w2^ z_G&+P5wu+Nu~EmZ10Mo)RFdqYAgTkSZk$x~X5mfXE1Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91IiLdo1ONa40RR91IRF3v0Jo8fpa1{`hDk(0RA>e5nq7!hRTRhP`c+|0 z*-)TU4`OOc&`VTe`Yj6D_jBEFap2G)zvqdrNNFG4R-foU%TQV+mU^zGrZh}Vg4+cMi zFTv;F0+3 z8j{mAD(R^E3BT3gD7XPaa+t>X?Wn8F3Hdy5BovvpufV2M!Qom3J^~>bqj4}*tSTOe zHKe?#$M;kjCRE_w3@-bi=qfbrFACvkAdrHOB&;b&MT|$mcY*kagAOr`kUs_fc0nf5 zSH|$g_z-IloN+lwZGAF=MC2F6X5iBE zJH8qbx4~PH92&a*9vzF}D*HgT4EzPG9Ec@^L@Ss~G-2Hx!-bzFBQJ3)BUs*bOcUq} zV-k}OzCSy@s9{^-@{bpFU~|uA9996oT-zC2U>l0mb5x_j%8tDJ^lldtIoRjWIrI-h zhTUl6b!>vT@4477JH5V2_l(;v;(S+qkH%NwNH$y*4L&Uat>`svQR7VJoFBqivuGw7 z7E22`*D-=4X^{#Ar(e{+5K195Twlb{wtvC!_%r0(18v$(u=0{svU6KZ*7j_6QaOI& z^j=a=xnz9q<;$TW*Eq*-o!(38H!c}17VLysz8osjuSusIQO~H{M%PG2$mA|ghBGBQ zhKjr|yI48)Y>_$n-S6@rSYR9YskNd}Z)9?v-nyD8iI4k`px_hnUSOVIqxAIKkjPTMZAIKr8jD}moV*N0%gtZSYfl40PVN1Bv6 zRw%L7f=OUS-$6DuUr~++WT7ux`bFD5bcwMQ=p&Pt*1u@^dWkUVqg3=09Fxzwa4rM~ zfI8F55uaZ&ok%LwFZq;!?v;I@QfqoB!d*DCco00bv$LVGO O0000|k1|%Oc%$NbB7>k44ofy`glX(f`xTHpSruq6Z zXaU(A42G$TlC0TWzSWdSpS4N|DZ_xKM`S&OHOV@L$& z+o|^6%#H#_l{*zS3N-a9++bB=W0|<%o0+Cgj!R2IZnCg_L#UR0gRjGzToG1nX>RX> zhr9%IoCG+94E|ZyGe=5H^X@rvmh0VtbO@`FXDIYz(bjWqLh z2ltiJH{XBPxN%KH_7mf~d>ieU^sY#HmhYPT^P*G=>w@*GZG<-$s$7};faArD?%TJ+ zdB6F7SQzqT*_oS5t~~j@n=OPs-zC%dzJFBu!;T$|g&v4(M{ zu9}oecfx6rTaQH_uMieC({x(xu)p-_-9`V;ML&D|&2o7$OSl8~jgJnAr&d;|7qxUh zaJ8J2u5~~2zr!nu2&U%`M2_s^VoOr#XyF4vi&qE4t#Xt$PxA7-1Bw?Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91B%lKT1ONa40RR91Bme*a04fgS`~Uz107*naR9Fe^m`P|=XB5ZZv&OM? zD8Y%w39Ac>w&F%mM6I-4Sc1@h3>l3cBw0eRs{uT8oCgLLP*gBf;6CR6ci~n zA-0K-Ds?svsCoYWdEdF5ci+9aQ3!(Mz&~gDpYy)=-TR$;Uvjwtd0%#*P$*P^{h$Rr z1^%A|2t6O;@#LSpaK3f7w?-%)9-=F zD~xks52ytlz@!c1L*Etuk|h)LU&KD~{7jpIso-H~){b82i|~g5EzaaXEWevXwcesd5K<-RzU9ez>}KpdHBP@NQcqbCxPU?=Vsaz*d;mJ8yVhwbq}(sH?DH< zeljvT@xHYlgUo;^ds6T(Sv*BwA^s}l-t4|eX=veSL7V`chNmGh-UunWlK2N9*X23d z0nh+0fEhk#Ht`|g2)GVrTD(k?YzC5lz_2nx2$0(#Qjl*vkJuRSEaW=Tp85OJNxU0)TKLd7kJvh(gCV&C4)$2ZKlRT34cm4)-lpf~v+cH; zGZ;&NzV)YIGq?{VI^IjmyA&hjR)Ob0Vmns!ihqAar7y-BL((JT?_y2rUoGZGt4Y&v zz5-ndT}}{-?%%ZfC=OY(WEaq+j!P?nMG-l}fV&Vpc1@0Ar!J<~nLyzLK5rS&(S8DS z+?zR(cI0uhg+8-kZ3CKb$HR%vE*xMa>_FBfQ@#f1m0~+veLkT_YKTjvQ=|v<954cO zfJXYR!&F0cELaAbfXV5!K0R?*+L>d< zMmK^7!9R+&*As<)h&Llv0os&A?ty6u9DRoW!mHz)$cOkPFgc5%0ci`;pqv6)fJ8f0 z^6G)VT!|p5?@8o80=EONK3V`qd}3ZBb{QN8-*Ou4bp$t;!~YkhDSZZ<1Gj*exz72+ z*(D}@9ua5`L0^*_?3oZd@#pzi+=lZXv9IIt;ExQtfX+JF_GPT5r!>Apq2F7po1rp~ z{yd}07a?yo8T63mI^PJ_S~vJaQ0Xw%cOgC*bOTA9&v~1izgKtqT&7L2ACCHPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91B%lKT1ONa40RR91Bme*a04fgS`~Uz05lKWrR9Fe^ms^O9Q547LjA<~; zWHc^GjmwBgQ^+)VFv61%Px8PhjR#CkG${{A9w-l#l$S#^UI>{NmxxeOE`=fu(c~I3 z{F(abYBxcNuQq56v6HUB(@YArFO`SOJA5H7ulf8mQHWR8u6kTpkyXRKWRr_$47)Q7(mr z@ZJf2!NsNkje)Qc-Z(vVrwKCuMv~mDmG~)8 z1tVcF1fK_L%3vFGfZBPpcYjF5BCW#P=Z#FuSe`pCK+ei-xB`cuzfYOP}Ci?QD zHL~lpBEZWNyfP9=5shIh&`+dhLpT0CFy@wKAyAS;KTfG%JBy zb_XuLg#YV@Fr8LU){{P{Xk3O^6V$*F*t#b-a8%3E_7JNJE<2;Ye z!$FoO@ky>%1Irb|^Vi~gcNlb?5u9CExX8c5|1Qf{o1Xd>Hm#pKDmh;#%{j%;=5wg& z;{U11e@+L4w=h2{iCw#DqxAwMy`86z#rQA0&rlks`h`^`VqHU2@Cv&|4ew{)IZF;2 zeOMIo#>z!F`kV5BBdcb~TyURyXObAxv%uA2a@*F49b3S23l`ul%RCzj%c7ndEZh(~h?%O*X%L f_i@^S)sjD$Y?rgoFAr*r00pb3tDnm{r-UW||6r>^ delta 205 zcmV;;05bp91Kt6U8Gi%-002t~P!j+E0HsMpK~#9!?ba^~!ay8{@rgs!fgmW%uy-p< zTeS_7n6%l3!4)lQ1AR4*!JXjKM)x0_d9-sTWaZ3kX^RxjUR(FSjgUM6BtsA+a)6*V00000NkvXX Hu0mjfnBZ4& diff --git a/TMessagesProj/src/main/res/drawable-mdpi/emoji_tabs_faves.png b/TMessagesProj/src/main/res/drawable-mdpi/emoji_tabs_faves.png new file mode 100644 index 0000000000000000000000000000000000000000..177fcc62cf3e05ad5dec17ab83b18fe48582d4a7 GIT binary patch literal 991 zcmV<510ei~P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91CZGcV1ONa40RR91CIA2c0EF@&TL1t9Nl8ROR9Fe^mpw>TVHn4~;s-h; z7DyBY5>`V&O||7%OH)fycm**?8*(U|99k2EzNUn>w#FKwt)LoeFsLMv#G=85zAUM3 zzyI-l&VA3h*Xw(aH;wQQf1l@lKK{>hj(Rw!uI_(Ep-||8w;Z?q6W7Wz&2SnTUg9*b z`*JKXXd8KUa9tN;ktX=oE%X`t#rcXgt!BCU)il%F2X{D)>)30~S4_FJ2X~|q&bT}D z72Dw`Ur?@b%{OCczot7}wgotkSrFTA@inmwY=HKk_zi!n4VDiT6I9b+xxa21me*8= z@$^9A){5V(>c@RI8qrY8J|+ixJSj`7YR%;%?5{q2F^g zm&=_aRswXc5FGLn1^hYq8cwD72AxG>rEpjE0Q(Du;2lNr-&R^tIjEWtXy#LR&~)Of zC-{uu0ZXLpJZJwZUva+2Renmzm~)JhoUS zi$D(AN9~^lXh@cU*DHt{4194 zV#@hYuQUvuRWD=>C>%DGl+}lZzJxlgHf@!2HTF@$F@f%MPw*KTJ#*|EQL1{=xCwWL zim2B0sMoN7v+VamkTwF{@D98I_3oYt>V;$QHawC^WTR>04%F}t`~f3Uukf9?u`vJu N002ovPDHLkV1luPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91CZGcV1ONa40RR91CIA2c0EF@&TL1t9ib+I4R9Fe^mrqDkQ542!%!G=J zlIX9A6|@ohr%;fzG$JB{zzEw!8=+OJz}7*Ki?9e<)S_J^1`$F_sR)y76A?s|Qbt)p zpi=4d`=)o~%s9=v)g2fn%I-Fwct-@Es|bI+tw|7C%!Kzgq4k;!B>;=3KJkFbPx z1Pp_B>2&%T?Y|Mo)PeKhDTtG+iL-)14d?=sactapUpd4oA&96ev>O3~F4B5N_y^(1Nf)0F=^LyS;IT)5j#PgG?= zn!v1M;1lJ6h`}e?hl#R@fZ8s<45rN>;Z{dL70zOyqckJg z6@#rZdQH^(er7aJ!EOs(CxA*EljWw+?#JeWdzL!Di}vL8+2co3XxBMCWm~BOY%*#} zf%X=CdkXYh*IpDMM8(@3kq+2mVwnkInLly#1+?X>fszzfPAxX42WJybhIP7_N+r?-q69EqZ{-;iATF5Qj_PF;Je173`{j z6%6`=4D zT<`~Bv*1$;ZUQgeHa@48NLo3+tqR@`PJ+KCVaD{QrWsrYD$`B$VReL%i9I_6swU7y za0+Cvuqf~p?EugPt?NWNOjU~RTGdv%Q@tp&pCOU<`_8g;2r88*&rN|+MER;6_+%c- zCw9g1BQSwhLRXglBkTZLr7B>eO63jc1HG)!pbeDLmP{-^I7Aggxk$CuC`7D$$~RpY a&%Xgl(c~)g9SmFm0000k44ofy`glX(f`xTHpSruq6Z zXaU(A42G$TlC0TWzSZ2>ca4N}f;HR*s(LJsH zM&-{Na~B1av^aeD(=YM3qbl3Pxwgb%w!op8`PPjqrD6l*!qvpq?pXAJVQ!Io+MF|? z*J94|{hOh0S^ekXAH{6#-S1L!lDf^F1Y5sPeUk@d=vVLQc`JS9#>@O~H~#K_9`i=% z?U~f`m%l^>Yk%bD3V$(o=a;D0cP5rM>lzxkpJ%I9xqm4kf2U!us{@M*8d@-!MXtMJ UMs-Y2Y!Aq@p00i_>zopr0BLD|J^%m! literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/emoji_tabs_new3.png b/TMessagesProj/src/main/res/drawable-mdpi/emoji_tabs_new3.png new file mode 100644 index 0000000000000000000000000000000000000000..8c8c958287ae455d2d23828e3f44bd307cddc2d0 GIT binary patch literal 1142 zcmV-+1d02JP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91CZGcV1ONa40RR91CIA2c0EF@&TL1t9;7LS5R9Fe^m^o-wQ4ogT6ZaKG zOtcB&0xAlk7AgV3wQ~s~2zH`^jYTS(ScrnCD8a_m+8R)Bg%mEJVkHE(xPU~hTyV>R z@%#16bMAe)H|M=1+W5ntGv}O{IrHYMH-*A~{G$x?_4Q4KV}@$@Vl|EkU;%bp;6_*n zXF?0=1M(Yq8(x7oipAnb%HaXQ6yXlo4lBV|luy7Jl|&YmF`TZ#>1nt;<@^)%hp-dA zh2vojoF7P0!WX1d@DLg7EwxrF5Y%4S4GmogEpP)Io#mIlI=CNpK#w*m)3fXbY{ytr z*9<+*Q<*)Wu~R^QB-{-wJGHFe08uhweb( zB8Gjx#bNz>$$8)8((mug$9GnZfj*9lb*8pkALkT=kCY41B;f_Z0a*{z?XZ0;u*F8jmZ9W&i*H07*qo IM6N<$f?H+-@Bjb+ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/emoji_tabs_recent.png b/TMessagesProj/src/main/res/drawable-mdpi/emoji_tabs_recent.png new file mode 100644 index 0000000000000000000000000000000000000000..4aff9f827bf9e12a3c79c7e57b5b9f19985501e4 GIT binary patch literal 1051 zcmV+$1mydPP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91CZGcV1ONa40RR91CIA2c0EF@&TL1t9g-Jv~R9Fe^n9WO6VHCz^L_^Sm zz|f4yawQmqQM({_7J;@4!nR4&uD#GAXdzTk;YR!qvW+1FfwZelE~-V4W};Cf^kJkG z)9=B1%Q%nARcO9`FcwndD(t zS|1@EhJ9eod3#8|HAKqrfb9_QOJOpD`6d+y9pF7Mxk;z$9c&5U<0$YOC);bqxByJX z&2d}{u?d_9hvPUl4oAt^%xTS~+Yk^3fXP!jP3APNg>yA9&}=ls@oXHhNpfcS=Utj$x(uk&DtmOR6$+33pFi1S-;2Z-JKxMj#zP{qmtRR(qbV}h-9E~t? z3-~H?o#!Xu8PIj2yG|Fes)Qpzh2J6IrNF}_Zi>*o=n0woB&Xe~qr}cRx~XLj=0N74 zSquoELs&P2)?cM0=h5E=s^@*(mnvMUR~hGtfX(Ri>ID0LOeN4Vuj>=-j{LJ3_zS8> VA|^owt?vK;002ovPDHLkV1m32)9U~L literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_forward_check.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_forward_check.png new file mode 100644 index 0000000000000000000000000000000000000000..a1ba9ab426446a0ad0e9919e74cc56f627eb10a5 GIT binary patch literal 404 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfU{quBj4DsN7 zJK2!ykb{70vd$5=3#X#?G<0k;n|I*Up+jy0jy6qZatGXiRImC5fgI5yF6)DCuD$t9 zo`RcFm~YSI@4tKI+@1-dy4t=~%)87V?8;MHb-hnZZLOldadDFEs<&Gi+gob1(mpk=+cxJJmxVzvZ=o5B zeR61g%G{O9>`(6EFb{lv;P-W7{`uLq#x_hpOHK;yu@}hZey4CV<79|02it@FDQEYw zg->{+uzJ?jb;>WB>t+4FO=5nsRkg79MAZkT&Obkn?wEPxxrektW6-0AB@gWL9&oFy V<}OqWT(<=jBA%{(F6*2UngHf1j7$Ik literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_forward_replace.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_forward_replace.png new file mode 100644 index 0000000000000000000000000000000000000000..6387de32173afa8c29dee3975322e7b8c1713fbb GIT binary patch literal 706 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDq2jfl0^H#WBQ# z_wJPQ-XVb^$Ddnf{bSP%DsNOsda=lU$^h)$IElXd@5xMKH5MUNNEHw9Y!d;AZxPx!i< zSE}=P!ljK3(|PY~b8K6p>+!o&>TvmwXRpN;vnxoOpZ%VCZ$YnxG|R!(Gw?w+})ia+c^-X8Wd z_U*qx+jTj_@I1Y=XLh#$Te-2f9L-C{l$FM+L}gl0qfR{ zRci9iVqXrXSgEd0_T;&+Wyjrwt-@QU$}7xf{QpDzl;CU!cR&9;TTfsAZ`ba(Y?OWJ z_~=9DN6`<0DYkWW63XA2#janJW}SI2gYSzQZ08oW^ZzjgB>@t#Fm5{=9jWp5r?PifTuE3$K|u=RG& h%xUzKZq6pHh?bt>zONj@n3c)I$ztaD0e0suyNEmr^l literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_forward_send.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_forward_send.png new file mode 100644 index 0000000000000000000000000000000000000000..9db40df5f1c09697818c61a6707dfd8fa6ed4e58 GIT binary patch literal 644 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgf?475JV~7Xu z)2X%^Zh;cVWA9JtSBXD)#N{TBkC6sj*FgtEFVC(V9v!ZY8#`P*bwnlyBpC#%bu~U& zT=T>@ZvXyw7x%8Oz8|-D@}Jk&i_ca5J74_nt?l!Dfr0h0(>}8@H99bTRy)z)dU})J zoSz-Xb2c!z>GP;5SU)aJV4iN5}_W`~fRuuY}`hZV~uy^zX?yWTUlJrFxoRaz5iz;|BZ z<%;98*RAVb<~k|4MejyidIiU$c|Ucgf0*$@_Z8QS9w+84ljaA0`n@G1>Gj3Fr)iBV zdap#>X*4U(bvPVs7MUC?ozk@3K=+{Z6Gy8G+m_W5{;WAoOD<1+Z8U?ElY3pl^);6 z7I0otG&$5?E&NSQi>=l${$NeIpTu@YpBueSl9?eqzaDN|DEvkI*ZHrUyM7yNn6~iF QN>B>$boFyt=akR{0QrLjqyPW_ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_panel_forward.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_panel_forward.png old mode 100755 new mode 100644 index 01dfdfb4d29ecc28f9ed52e1ac6ae856cfec18b7..2ee52f5c4dca084cf263423f6a4cdab269227311 GIT binary patch literal 390 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlfUeeiT~4DsN7 zJJpu&kO5CCCsWIholNZsE;0*v?ldVE7>k8DJay{fd0HH*a$GmF^$x4xOU=II{nu6e ztR()-{>~@jx{)n7|K242N1PQ++%tA}J6JKlFFP8sqsw6htILV-C5{d*c@G|Yxm9(t z^hwGe^!hzj_GtJ&-M=&M#Bw}bnDkoBQS!&Orz(27uP<0+F{as>-Z9YB?zC9r(6!+7 zoQ^pq3$`~r)630ov|dxaHi`98=mO){+a{!Mo7B|zxMA0nmyXXv%KYxfE#vK+*v-Ge zTr&LEB3G8&X12z-`CN+MYXcSEeBU`u*Ja6mdB59|f1mx~UVMkeHmu~(JWv>Ty85}S Ib4q9e0FF$C-2eap delta 116 zcmZo;ZeyGvnaje!z~EK)wHQeGdb&7zopr04&cc{Qv*} diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/emoji_tabs_faves.png b/TMessagesProj/src/main/res/drawable-xhdpi/emoji_tabs_faves.png new file mode 100644 index 0000000000000000000000000000000000000000..47fdcb12fb4e028411451abef902e84340138190 GIT binary patch literal 2038 zcmVPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91OrQe*1ONa40RR91OaK4?02aB&-~a#zU`a$lRCodHoLz`jRTRf(G$jnt zG!w%#)J!pnOzNS+zygVU8ITke$)UoaG)R09$I=i49~45Zhu*7Ksq95a2znBt2{S)X zn=uGev(hx3e*c+s?wr|o?R&mw=6)>rbIx9Sef-zDbN9Wo&z@;(Ye}mEWp<#w%-~sK z_x1HXhTk(_JUG_g-hL)aoQC(IQSIO(a0f)?AcK1vz5!_qq$yp|E;;wR)5dFPdzv!~ z+;W-}9cpe?LpLE!aWvylv^$>rgK1(mtS!x%3I6poDXNyu%#m)yn2{zx{%t^?h;bqt z**3566Eg$ci2fJ__b0dtuE=&q({_yF_q!bl#vNP*OZGQq!|23x@Q=}vAJFMLnV@@D zN)x7mzl~;eqpN%n=^jYuihQRvMYB{S#qM=G z5RBKjO+dfKW4qV2ZOAdjPX*VFCY(f9`RNLamkwyo6~&ku+BNKzBKNqJ2jg|VqM2Xk z@!8|^X$U<owFfNWe*tSLUfy4O?OF1O-fydkbQ>^69Ocg6YE;bSn+UowfX;bJ%x!V(g6C)#W{fY2BF%R)Dv` z72wJ_6yuT>C|e*OR}@=0luJ3i>tWJdKD%6d`Vf=UP@g{rrpJ10* z$oU4wAICV{X8tVrC4y7I{Y7&2ai2*NH4Ksu5j|{#u3>ZLa({28%x!HK@tX(vTWS2^ zG9LTWKrd7lxrvh=tf-k`6Z;zrm)=y7H_zRRF8#9r^2{IeYAVf^ndK|n3 z;@%wt^fA=ha}1OzhqE>-^n={tG6@p}5H3YSA zF=clu`h?hJh8a-yaM3B7x)gB-s=+7Oc0h=>-4_x4Z(vcqbxfQC?p#dSo~tH8ZZ*RW zD1YbjM6LwG=q#>4oPDyj5IRKP?8|`uGI*+xL?s3h$ABvrQ#Kcxo)nva@6%J8cseyW zDn;CYwRwrUzot#45GQyixT7qFXn*~=QWEC~Do((9j-V1$OJ|Po@vbCT5B>v|=(qhB z;-d_Yg}4CUZi{MIKP*g0v>Mz7mgwxmOT$8>MiOxV*4Yhl7WdUP+>4(gVF%DrN=t5_ ze7X3n$s#H@5C==dxg%Mmf~`or65O;{1^QwCa!X!)DXqn6Wx;Jo7Y=fI@ZftjkEDy1 z-x@AQfF~QU}j>lwmH2kb{^d}9wvQ<`2i(4ihMK+r8QS;O0|-|v@~%zMV=L#xfMf1lg)|HnzE`k2H^cM+d^vp_Q)MWs zVb&Va!xNXQsn`@?9OTrKFIOy%u1)N?%!XNSjaWhj8Cba+w_&U|I<7D_NApH+!zfHY zr3;PXEYgPApo&<_z!5ZhD6BJI`ZZ_Sphe<4EB8WT74?ovOr**X67zv2ORJ1M3Y@il zeAEgO=rosj8ywrEo>+R?J+cBZZS%OIt?UVL%&=Jiz5u?hZ>B0D8`&;M5O)H0R?Lyv z)`=2^SQl_b2j0Ab@oexZ@a^0(%|IFmvbfgVqnPaYIfiCgG64?UzBoQBnsy{I-y-{HTSbva&xnc7&x>rHf3CJ+E?<$|bS2424 z`@Yyo^m!Nk_*2G1S^UcIb+CwEl0O5r7WJW?!AL&xRSck5ikU`Izes6<*R8r>5HwkxmYMIqr%%o? zF|N?WX@fyP4^F#5RP+o`|1}`XQSY=AhS)1-kXsz0qA$90h8SlU6!LzCh`xR6(@tk! zd%;oGnPJ~NByUCFq`%bFQ|<@APe7dRa&1Yg1Fa6UI?(Dss{^eLv^vo0Kpl4Af2(yr Udfkcy7ytkO07*qoM6N<$f|B8|rT_o{ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/emoji_tabs_new1.png b/TMessagesProj/src/main/res/drawable-xhdpi/emoji_tabs_new1.png new file mode 100644 index 0000000000000000000000000000000000000000..b7503d7b84579bf2e3937349dcb11aacab3d2a30 GIT binary patch literal 2050 zcmV+d2>thoP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91OrQe*1ONa40RR91OaK4?02aB&-~a#zY)M2xRCodHoJ))qMHGe^gaJif zf(cPDYCuSUh^UFLs7PX15mr1FKG4LKupqb~m>3Nq5ffoUf;<)|K_Ux{!516U1<^o8 zgJKYQDPS-fAAo>7X8iu4r{~_fr}}o^TlaGBoj%Dw-F^DhIj8=quCA)CnST9p$r;EQ z$Qj5P$Qj5Puo-Bx)%DkQb#>i9)S+N7I15g5P6Pc>k1@24Ay~TpjtVJ1CicZLZya-W#BLfEt{?tTLVG|hMAl2 zxdgljhKKea`Mae?e#^QIMi@k?KYR|n4n~!YT%ExO)fKP2bP&QIYdk5u4IWIHN+)(5 z;2ZEG_zC<8&H#1vH-ce6tyM3EDbZ2+Zl=?Ji(_*NWX%QtfO2vUgRNi=7}1=yts-kG z-5;z5<>WUE76Fa1`le9%cCZa3$ysi`2Kp{hY90_U2z&;7a)lcjbB*bn&8eW)kf6tK zJMhUFZpmd4(Iavh^K*&-LS%0I~C_I-Q1|VcC(3{Vd%P3~`*yYEF0Z3WzG8J8~%t>+> zfEcx|{|v%@N2iH#P{F4L#7n>?@C>N?YH(E*BH=Z_lEqay_7o=2M^3O+9@EBCllWku zPTdu)p0~SJ4XBG!U%-Yq@F%cp`J1 zD6|5&@&}5Vz2@=#fEzHn4nBM-B!n1Gg3f~27dgMX(8fKtTG)NA5!btT9J>sCv#aJ) zfx?EMMFgw=j_YX=X3Y=x#3fqmo(%?~oTP zhXHk(5v&4*eUV*3M@yMUJbCzjfhDcU_eo4&q_fH^6j%TTWi6P|UbBIhKw@X@@u_`i zmUw3v?&D`Md!@qZ(4$r+HPlrS&$rLa;-NcrmoV*XaOFCht=reRHrBN^z|~W<=`O2W z?|6nqwsYsi7-U zl@J!)-BM(A^$mKo08bR$I9M>*bH znb-Q3BF`y6zhq0E51I`T^j0}xMMC!_2;QHCtN{6q2HFyD$!?Uv-}+j9nF*}stRyys ztYAoA*qn7AmL* z{@cM~pz&0aZ0BaJKPnew=A05-k7rV!)|31mPJ*v;;8T6-e4vfW<0vN4Ou%0HZ(}`3|E6_}ibxxb8MoQZoWgqpZ0n z>eQ0dX{T|bm(EfUd@Zdeq>`v6>{{ipmWEK*O!Q|y>%rKXRIJucNqjQUBIE^7OVOBX z4QNiH++PG083A4Znh8|>8H(x%4r+=D{)}IMUJlxvte3@ZI&*7l7Po3EbtTNI3MA@nsJE+*f?AAr&uE{AHliHAR^$*n gmz;r|fyQRwf8Fo=eUBw8i~s-t07*qoM6N<$f}AhIegFUf literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/emoji_tabs_new2.png b/TMessagesProj/src/main/res/drawable-xhdpi/emoji_tabs_new2.png new file mode 100644 index 0000000000000000000000000000000000000000..e2fd9790a0729e6ae98e60286e406639f14f869f GIT binary patch literal 594 zcmeAS@N?(olHy`uVBq!ia0vp^J|N7&1|*M957Y)yjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<^vw#`F1}PNVdHexT*&a_9$B+uf zw>NCP4+n@G`*^I0Lu_Gl^P_dES34CJeyA?_w0p-5e?^O(J1llsbkBbGHC3#uwCnuM zrY8>Xe5cL%!*649=iZ%fe==s>>vI!f;!tQ{VB)yKE%Nnnq1)+AcLL0ppPE^;AV+il zsm(?4B1gB&X*%{!-MJ}b&#$Y(+^OXkl9*;_@q1nVb?L}t$3-hYc7D1PvdSob?c;|F zSH7Ih<@9P{?WMNj%2hWbR|TB?b2LHE*R(#Id4g4nd6O*v*}R>KUcPy&Cw}Ieb@Pgp z_&uw{16LkQOxnM3*NWu)i5>~nag!wLYruE=H^H z%rLvSbk?4)Qoa)P*Y7NE`T2M5s`+BSPkChqfBwgnvZvTHcH4zbyuU3Tr~a<(nziqg z!)D&!Tkg-@UhzHao?WJ|zW?nF{!x2kdV*UM?q^;<)&6OJok>FV`ADzr5C1PR<5?In vzuoJr{;Tl2k>THLT7kg>4kJp5Sk6C;0ozunZhoCx4~kDuS3j3^P6Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91OrQe*1ONa40RR91OaK4?02aB&-~a#!CP_p=RCodHn|r8KMI6WP&G(~1 zucnm|nU-46!^)y0wGfhm#GtUG2NeF3kt9VykkSjq@bQNvBBFs=AsK;|SSC@38i7Kl zf`VCIQ}B^*yM4af8hg+FX3lfZ-QD&F-`zdGnfblWnVH|rnQdxnP$L432s9$lh`|3Q z0?pOpXQ4D35Vr12P|@GZTn$H*iZ=fIB1J+OyRA5o`q-%zp)k z!EtZ~w15F6xR&3VC~gK(={qVu$>gvSu4txwG zFN_XT;nCQND$xK#)CK2-Sxor>@B&B_B$w^T>kX!XJs^x`y5!`-$GWaSciBgJ0^R^U zD`^z+b>p4~VJXXE>g1n-pj5$H2V8W4g886dB?A?djlRplLg1s%D9kTtDAV0s<*`Up z`@=zJ^1POQaL8lh;@s@Phy@-)G?t9W%@~g&?9k-!v&YuTk7+2M;xS+YWl9CAhOkFn z^jnY36DwvcN{4|H4(03Ncd3~26oRplmdd83+`ZASrd25JOalSH)h< zo)&5CnazS7L9SVGPKQSzfdTmA%G2ISAg+BAgbx5GL6Ec?;oBi?FDT*_GBrTI4n6^| zfXpWnWNJLKRs_l))9t4r;C*1xjEFIaRSiX-2b3+3B_LvDbZA{`(e#KS%Bvx*3$O;s zeek=Nw@Al9rZydebRI_L>riBBwX!>?jBD-OdY4U?c-(x5GHWgPCi_dLJ?)Yf^*0*I z_{>ul?Ef3N^9_C4a~o^$!&yXH<4BPHgpv8UB5RO4BB-o*oqb~YuRd)JS+oBlUv$hT z3_-fHZT00M?AL(oj&#taXO*SHRPIQ zlj{wJAHxh@fmIeT20ZbG7!7tByn0~mGn^P~@CvNXMu7%BD=|$RFm8fQ=7cS!^bXP@ zE&eTjDenl~>trb-#_xkhT_w@IkQshzh>AmjMb{aEy)1s_GSGWI5tmyCr$# z?=I6=S;nXY;&nX1q8=903Ybp*S!4>ZDwgoJmHf`*=T8ZgbaS*Z<6s5M)%9Fm)uFGz zew)v)eHAh+TZ62*3FNu_GUH&)l4eB=X#soV7{n|iS>qW|H3O=>kFKXsP6ufm4A(ew zvF;&s=~!nHa@i%@%g72?E46eKscWG2P_DNIc#$q~Mh<(OCa5l1yJ)}PRhR)^$Fzc^ zG{sCQ%DN;KFVK2-Py*M^?ANGUODtJ4|0^gb{4u0WVlWa=$@khIHTE`r14`O1;0U6cbsWnAkY zE53)wy^FwOspNH;p;#IDRD++2g+3*=!C_!Ed}}OCrHjTdgDVXoOVFoxmdS`{>xj@9 z5G0*VaIL|TDWjESg7?5zU?yiwM>ex4WsEACrlaoP=Pn&WBmMzZ$FiXJ!V%UIiBE=)mC26je|VcIqgCjx<@6 z?U;-Po#feJX@a*MWImo{89VH20egT&n_+rq#zeLt^cHZ+(g^PhkiF7GmbPz%UAoyi z5^s?PdTV>9y^y3=2%lYi0GreVi$ zpd;}vnitvWG!@a_8GPxo0=_1KF==$9ZHxX1z{<5?7fAoY%WnulSx>O3pxXX83wsvG zwTq&!510e2)B$#Z^e;SXODFAp^0!B+g64bDWL+zG5oEshEVT~#I(XaN=S>xLJDWPM|ZW=~hjQR>JaT+9Wd zK%(t#+UX|U2NnX2mtmw;inekxA?I1nxY9A|R@!_7Mnr6TjCvcvR-l&-yTD1iNCSzxima8koD&St!KtLEr-rHhWPe73m)<`kRlo&}*M?q#Kw7R)eg%tGb=i zghxO{)0WaZklCPLD%AJ8=-ge@84L6_U4!`$;LDFrQ?BC+njADq=)K_<2J;Ji=SsN; z%<_&PYH-%sTFr`r-bm{JpJq&Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91OrQe*1ONa40RR91OaK4?02aB&-~a#zoJmAMRCodHn@?<1RTRc4R*(t= z6bjXXwJIvQ08tl85UMVWm>4&>P{RhIL=#9{XfUb9jk*wUBP4_-8Zo+o1QMdrDqu{g zQA|_>wWJj)L9BoHBi7p1-`7ls$>ZEN@6FtK)7QL{e3^OY-h0k>&fIt3efQoOH!hPb z16c;L3}hL|GLU7!&OnW=PDVR4G&BoOH|8*xQCr~`k4GvGAn21kUNnwnF5R!tI3 z7zwH;(g#3G4vi&5-bI^s@Cn#M0(4oizfe1M5H! zNJ6@BvIbn1s>DIK2CN3BKsDqSoIYNuqHd2SOa#N9Cs&;||4XlvrJowH9bhi6Y-n>2SprPW1^ zCs_ePQ^C#%?0c#68o1R5KkVEFHh{lCG1*Q9_1NkPBxZqwMcC?Beicls1Y7LR0GmJ` z2$Rn#yrL2uBH6@wGw2LqI6zq&m=nohnU(M(KJS7-;F9ksxVlVN@tfmxHV8{rH|2}s zTQ0u^e(we+fJ>CLS=m{&-YFSqvL^VOxQ=6m)j6KSAloHAVCXqu$(B454_|OF-*SSklto4-BkXB%YsngCqo@T~)xH0#Vf`$+hNyO; z)Wcp<3DX;a^%|w&rRXB$_#(lqpxhdxHn41RqDb3OSq$%^5YrM=P_9AoP*54`nvA!D zps2@boyx0VGm#!9()iK~yS2)(qgYVR9)>}^G%qy_yveVmMZVMu{C|V|HY0!siTg@J zpI12(Mh$gOYen|$eCu?7vEtAerd=(kge9!SXw|{5^OK=Zor*ZVg7jg7_%cJ6SD6W; zTdHR*&DXl{<--eK$HX@)9;>6|gEbRIX*00yWo@92CDCSZP@3p4*34kIo-1M6z7NU} zijJT%<<|!s{H^$htHw!K5BL2!hl5nAiAQruo!tmZ?1VK1EpV-U!?7qtB1wvreSPilnunF8@1}a5h_}@ z-&t3?I0>8KGU7?EWf$uaUyk`7BQ&-q=uRH4gmoK^<{3I~D&|D}7+VGY@np0IJ8L7@ zvf}#L)y_4B=>Tp}<}irX5t#G-4mOB;jqUgGp^cB*fmROwi)$lSUqi}4>{(-R-Xy;2 zxv(A#R{Z&P7k$Cd4ZxBm7gbKNf&g_s%@UpNHpO*Ic2fHfqeY9MOS8&~=Qus{q$?;h zVKf+|!Cs@$eTFX0s{0M{p(`?G!g5BehIi3_n+zwZQxS(wLRzOGt+?~?g(Yk^wX~#D zC|X#2tWc5S?@t)q^^nNU!5dW!g`n*zGbx5 z2JSjTpC;AwkezN2wl+qLY7P~@!epS+&q2|iUZrRL4p!x<;jQ_kjyVNI|5%w=o^@Jd zq%~+ccfF}><5e&K_clj7e(oBKPY0(BCpw>SQx#$^*(H1z0iEs+igxq;jmsEw9o0NS zne}Q>a!{3O2+w+;)7_T%J-Ug9TJWvq3Ec7>B9-W4qaCDg*{%^)mkeU$5Od5OFc;HLp>0sX)wN>+0T z;reNUbM>^a+f&5#8l{2P-^8$hK6a;om%%v@Ci+9!?1HA%`3eb*X<-k7ks!3n@d9Y_ z!4x~!gO`A2>BVFh6_OjXiUkN6Jw02&04OGdRL~^yMX&@!>Uq?=94rIsO`2>*5#648 zOYt@_^=3#(h1g;~HiKJq9Z|GMc?{?jjSlN-$49$8N~AVfb+}DiVT~Y;=t$<1EVq9V zr%~c{Nm_00SG8W)DXO?=VNm~#Dz?^_cy2-+LRhODt-xAOL+xEoVo`td89ifFXoehx z1fWI8RX`I-jbA=R_S(TpkjP;fuVa`aXL&V2UCYIC&;srO)-o~bQ0b-gQ=r|QU5rs0 z{}Pi#6IK`%$m(Nx|JL)R|-%$TE;+Aj?3O c0nZuuALcIY4w^*iX#fBK07*qoM6N<$g36gB0F2&*5QUU}E=laSX{| zeLMZEwo9PMfpiHqdCetLf=)R_YKVzl(b3TK5{kUx5qV?drcdk}$-Pv>Yf{a#}-Ml z*+~5s$9-}awVVG~?Obt{S0REoo$Y6AQQ7_P9McXpI{upwv-9obe#M>16YSN)lXusM zoD6OLW4e=V_0y>`lRmTkWG~W9oG{<;#QuriPkiQehc{L&oqI?ph5rnd^N13d!fr|E#xbTV<1_5&sI z+6$Hb9$KUQkR|2KM2>g2=RIQnz-MIUxnzIrk7%afpBS#W2P`y~I$Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NG;Ymb6RA>e5m|KWeRTRf(A_;Ox zO)M(WAVJ1rK(d=i>NKSflSM`T2aHO^?i45$!(jiH<}hxtr!oleE3Hwe>|~r&Mw_;C$5t zo+oFAu~l~-cDwZ%y%T-8vD?YtZ|w3a9eoah96^~-lntj191KacUliroIQ(0{6_fv* zjpymHOZB)(_==S{ZS1HD=L~^f6SOsqJFb#{!9~V7JJU=|fECa!b(dlPV)P=_a;H$M z^dO1~=fx8f$Rk!2V`2-zT9EZTB)WokA9x=uP6V z=jyyOv`+*cx_t(g)%kPqpRDsQ(cT^Sp^aUEz7VACCaKgz40nmivo(ynsI5c+Nq{=P4xhulC*wO27YUA=#?SC$%N84|^= z=?4wCHn(|P%qXV$9Z*En?!w#>!p$>n%s))A&*4h-4Fcs zK`*iG;5G0j&?l{KQ7f9n^pQ#>G!lA%9elX|L*es5q19{um7=n6elj^yHSpAS9p4>b z2e9&WQT^dKSu24C6-4*D&V#Jzp_bpuTf;@10%?gQqHl4OlnH02%XutC@(vQ(fZdC0 zkn?~(1+7fRcoc*$CdG8cqL&)KZjB&^QEu~ndmGN3K&N>@)_G3si);u|I4>GRU&N`w z9-k;1|Bo~aK=_1Ip1z!7Voxe`+A$C#SsxtL{m%GojhZ_(0g}!V=>qS8+1NkSm>ECB ze?`H3i%^-fJ5lRXCBM{e?B9D%wyCWf~E0Bm* zWH3Q>+;7V1ij|c)hIrKIHMNlm_!EI!eX#<&&&0dAtx7$_`0T*5xmD9d^3EmZ2Vnd zY&LVT0aW2*0%NzGu42GhHJHfQe9&YKpy_X}!2e(c{sUkBOq$qqYy1EJ002ovPDHLk FV1hz`e-r=! literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_forward_send.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_forward_send.png new file mode 100644 index 0000000000000000000000000000000000000000..0ee5081c11aa02c3a2bb99ff51972571b42420f1 GIT binary patch literal 1219 zcmV;!1U&nRP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NGElET{RA>e5m`P|A{g8RX^5IwpRL0m3w!ChQ~ z8jM@wS0NpWPG{aW69<`spRTU1`m0_~PrvRLi)AD;1DP54e`lbfp`ka}3*LcWpb9Jp zozniPDY6fE4qWmY<5G~Fwhuya6|TxH+Y|b;LA$hi5DMxJegh-tP!@t~#$dJg=ub_X zhoG1NR)IrJao9`1U2EKQucM4JzW=PHh&)qKQ#(T!gRLEqrSrIH+-@o{4A(Jju2X=l zZzE-{0vOB&@@IZ!9If^g{XwqSl-o#rNELG@Q9}GWfz{xniZSXRl*6DP5l7O;NH`A| zks`WzCBY{LYzAL}Ek7{q0X@BVk}^)B0;_@&zr@7Je`q(TvpA&t4QvIue)&TCk>nXL zvNI?VfqlV2ptoeh_X%Y!=p2;Sw?E|NCL3*)??B6u*g|j|)B{`IU|0q^SYgq-kS<99?RR0MEO z26uoQf=%e3ev=GcC5*EFWBQcJbp^ zi=SCa(rVPuF9*JbVyED|Nd??x3Dv89qw1%yLMvzjeO+8*(Jpc;&R3Vh$KtpRjI;ud zSbcw82sZ#DpHW);jQ*Gsd%-&l$VJE{CozW0OW-BNcs6kv!AId92&6!l$k&fW!C9Lk z2+jtzig{&dGAj_yd?1%5*GW|_Mr%N{?@thn0$L*9b0O;q%7ARGw#Y?j3+QS+Z@4Z* zYgL9X=ZteMDy#@I11+xFgv}nh&&$)`ScU24}Q`(qE!427b<2nl^bl z$)!LoV}u<;e;}V)*iDZiCZYUaDy` hl9_?b45Yyf`~?CodfZb_>>&UE002ovPDHLkV1lCk`xF2G literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/msg_panel_forward.png b/TMessagesProj/src/main/res/drawable-xhdpi/msg_panel_forward.png old mode 100755 new mode 100644 index cdfacb8e7812692cac1b3974cdb32c9808afe896..1d0905bfd1af37aafc04623a0a1fcb26e738cb58 GIT binary patch literal 593 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyy~ySip>6gB0F2&*1?o+wJM%7?Q#I zcBZZ0VF!WMIZ}C=gpDtTf zZp(vGt^9~M0fB$Q4LbTCWY>GnzbwkgAuxGoLVtV|1II_M@4O2cSUO_+PyWA_!oVR= zt*6+-&{EN??ieCv$rk6vBk$y_8}=gJ`S#okdZuSDR0M@yZN4(|3fsrzAIf|5XFqiM z-Q5uz)TDo5|H>mJH_vw6WqNeLY6aVdMA;RGEu8K|D4I0zmFk*=tT?i;Y+5+OE4hP_ zor}#l&NHZSyk}^MXpLVi^k~X_W(LJY)0hviUzz`yG2C0E=2VqgmwoGxOrIssxmG;l zd@A}t_STO$k+U@`I=%^4aW&e?HUIc+A1@qyUE#oWx@C}-L`E;eO=m-6Il3ffbd$-QG z`|O=S=!0Ib)7~{ZPAdG7sN+2Up1+SFuuqj41pk{e$8!qnzlnO1$p?x~Pgg&ebxsLQ E0LX&ZqyPW_ delta 245 zcmVb zDlPz5YAygb8ZH2LnlA7p+Ai=+*9M?(1281+0x*6f0vCRsDQ*cDfH_GQfF&sxfHi3s zfGrspfIV3k*tx*Q-J+Fy$P4!~ow+~Sv2;EwB_d*O84OTYEUwdF?L vt&UZ<{Fcq~dvfdh!F_;~$VCsyDBsBgv)Ofh88+5500000NkvXXu0mjf>u+mq diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_faves.png b/TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_faves.png new file mode 100644 index 0000000000000000000000000000000000000000..e0e5a1e8225f6556134a5320e0ef088db89cdeac GIT binary patch literal 3157 zcmZuz=Q|q=-%X4lxou)JF11*K}#zA3ft+SJ}Pqedt- zizr%DtY}g5X|L3_H%95ugSo)~0DvB;t7-b1z5jp~^!p|);X-~B z5NoQV0jMUxwtgE;CoAM5LqmY*Z%qpT#<~Ki{#1U0`!@gp$^syO@;3wjWCaxe|Mf1Q z{IC8~^bAdf0sxS1q~={Sf8b7@dnMeQ9UoNU*4S}9o>r=vFM063rm-F*vFuG`TuzoO zNbTmwG?zF!Zgw@7IER5b#u%f)Xl~kRp-*xmj8$9onS5OotAgyz<nlRD9W}Dv~O?O!F7V(0B}mYDwOdg5s=zU!b1$p zFR*a|a-Y{>zTQAVk*H7Fu?5Cso6UPhPnPn8VXU0y;S_n9BB^_QhuW$GuTks;-cC(} z4HI4!qz1QpJ!Ql34btoj?Duw8r>fZs{HNH#SrXa1qI;b3MSy9m*A~Uv?J8^1Y9tL{ zyu1^eu`tCE7q+x*NOjPFtw3Sq1%>Mg4P9l~NMS8p1mV=gJ^eAGg)gGf{K-z-;FX_M ze0~4T!kDMJ=98GDU;2~3>O7FZc<)$~tW2(1d0v@PJR(G*T`-ULGnB1>#ks9K<;9Xh zF8D$|uu>W8j}Bf=reIsMDB?TgQg>45I1L}PZF2e(hf|oX_*k;1*8P*9Aro4UKb=aL z!CxZ4#?fQi{FnHX-a=1q%AB=#V_MsoFaWjjrR;uu1;vYtIq*O(eGKD(!WfwqFD4q^ z(q`7@B>KKx{5XfQlA~XH#0XYX0O*w@GhIRLXHVLwTMTe^>)VoqLo&v3x8XxHjIthZE@4AF%-d{2 zl`Yu~yAmEcE;87b!WigEhjAd0R&NyjzRc^L`Jhys^s{^MA z*fjKsy{-F;KMeg~X^6$!MsK4=8$^1!ktJ=s_(QQ(^?@X#jG#Y1^SXcYM7%qFtwH*;Ou*Wt6~D@BM6ULvrq!F4F~?Q^P? zK;ckJ*)Q$UL0{f~1%&?Q4C!wgyO zkk(@jbVW_|OLT(&1G2zaY;d8LgI1dEYEkt|?>ZkD4z-)tjzRtfPul7W^OrQ@y$rG$ z#aw5*-|-x^vsj|+<2D6XgMpC`Tp^E8~wY!>x-u|W8xgaln7)n`A zvZEPmeM%@s7SbnXqL*lCH0-~!U#=9Nxx93}o+9NVDjqI-&{)A;f`r;1jYS89QF(-@ z&)?irB9z=Mq)yBXDMP93H5Pj-)=D%R$t)A1nuC<{omdCpx|F=HN;8{9fj~=2qNCc- z;+lGT|3gKiN+oSCoX5K|`i_1^LlD zUxKPeB)&I`$=a}>KV=6De>MCaPkyAYvH5pZSsV0zys_~s zT4ZIAEk(X!uM)Nv=A+Ap*j{y~I8|M(a<)6B9@2z+A4|Eu5u3-4>z+Se(Kl13!(f$+ zS}BO|Yp$M0C+4?bNm2Re)Gn)qklIEk?bM@3ebu6~1b5T--tMN;G94^&O=#I-9(V=< ztmMR=eFK3h*0d0@Fo~d02a}|H#A*Ns^I*TZC4NjTYEBUpu!NI^f|Bs`5*rQ3bzcvV zV1_TiDF+N=QG`yi=A5U#ukth6k?4=RU}u1sx_f?6LN2$ zeCPXW8oWJx6LM+CC5eK{pumqRkmX!;OzAP_Zsti1rbGVTegNcy%9gI~u?!C|-x7w6 zq;G81`VMijWcEwcXD$g%-Ima~(cjo8`SJ6u;-40nPIE=s?Z-pI^Q*912UbIYrh^*a zHjg)QySn_W74hQl_Sg-`+hfrAVLP!P|2CQX8zu=);-)42To|7|CN!2O@8=j5sU8Tx zkEP^z?ao}UH8!aU`bIr6Vb=28Xkj-&3M+0mL*or{2Rgm57cq#Tl^JK;PpG*PM^lEN zbpyCKte-5*^HlTPSx*@&X}l|}SQ>XW7p%ImB6SG$q3>@H-cJ7IeZbPD^wXXSJr4-c_YVBN_(IYU_@32>wdKhRFja4^r#-GlHcY(O}_ig zYx?1VGKVeb(zSo++6;G9)j4zD!a%@FnUGVHw+HgHuKHd}n?)nv(8?OE0=17QT-^OR zKi&D2TvOONz6ksDf%~E(UV$yUWp!78XS-*R`!|EPI z$xu3ds?LzDRJ{wxd$fafKu(F}%|Ud0*&I)WIKxcBykK;Y-Zfl{}k zuhc>cZG%DE|GjQ0wRa;nak#{Vp%*soe6?#k93LuI|7(EA>1$>AEI&b0gGSKVoFpQrbWP91PifQc`{- z3+VHI0+tIWZU66-*kp4{|j4eRH3kNwhba!4-p8h<@}XVJCQ;s_W3yE2wu;;XfAL>kjo z)OkZ_(ObtzSEgRQ{CJXK&x4w^YNgVsgLE{jDFdSQQys7GSdn+ImA!FFD@r5mo%c!% zR8+WNv3xEyX3`BW{z5*@;er;cPRWpW-9=dc>gKTc2b0#*h4FL2A3j0`KLttMcR2SR zohKChI8H@>w5UcBhL@Z^bNVcaeq_gyz4)a$f_YRx?@F=xN?9{bIE?EsJedA5jrHd~ zRy41ycX=&cVM#Z0%`2+nA07!^S`*fs7Zooq-y6&}5WrEy=S&|(dP5Ll#Y@9^R`nVk sx&r?n+a literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_new1.png b/TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_new1.png new file mode 100644 index 0000000000000000000000000000000000000000..89caa4eb798cf81e7bfcfa22b09958d8e78df07f GIT binary patch literal 3040 zcmZ8jc{me}A0BhH<_@C?ZK05oGuK>W%0f9tZS2UJj@i$}unwk^ud%nWeuXy*d3BzOYY{zi@6Mcn+ZOaC`MD3Mkb7R?QbqhP&;qe8tS8et((Fb9a0EQ&+P_v!$Z5H2tq z$G_zLECOvtITFj9ghI!#|xY#>#SPy6- z1D&vWhF_=Gnp(iJl~lN=oN-+O$WXUN^H{=x>T9waMjLV)P+9w_XZXIPR|XHmRVs}1 zzspEE3GtQHyoAS951Wb%DvemiVQ&NG&44!e)neZUsh5Xl1G?2{*v2@n_dO_zv%hjE z$M%a%_m2d(I#uJ@jw{ZKGW0`k*JToSO?O{mZ)K*sd3$`YQ(kWT$hl_2faCQ@uTrHO zmYC#}M%sECwT{p@taqCTe$1|UdIc-TxdJ?ZK|lSdhfMYT{=jv791Xm_#T#PH^Hk7^ zU}^_P7aC(i0h5{EAI8C>AU=s3GA1#aes1IBi=`2q4uZQ`W3cdl_TQUuI?J`bluW-$ z^pM*VFz*9dssQ<7*y`Dtna%Khok#X>CZD{60M(s1#@VWSli_>9kA(WpuZ*?<(CR!s zEEuL)_E>CQ@Hn)0#Tj7S&ygN0zr~r|9Z?NR2)>BF^HlGWTT#svBNA zr|Rxs0K1euHeUlrvy`gGyU9?p%=7&G?joBOBadx1( zxhwL@+b6}8UwZky0G*^tPOT&VagZ$(0Yfn_M)sq@1(01GzJut#|BeXYhng$d~UsVEx)&p9bfT z9M(^oJr=UX4+B8{3j7-HeSk~-(>QpSr&0a#>nz8phBB(D;cb%FxKroJo&xbz#z!lp zkSa}#bP1=A>jA5aqv|50=Sc437koPs7C;dH%fu~3WuHB@&0W#UQjM$}^2X;?e8x@c zE_*KA^N3@K&{4?60J^{qLm#N zDL0Huo?`MDu8qx2`*vNoB7p6$b%E+DthGCVL%(ca1dWd+b?|n6=y%A4l?RN);a(>6 z3&h4I6V?Utl%q>GH`E-CMKW%t)lwfQH0Q-L`oX0;l%Dya)5H%BXd1Ei|qCoRmDE0zJhz4=_40A|))4?J!jd^w8*Nf$oYLN{(YpcCy zp-ku2{m1DoYjbB3)8LdHN2Eup<95EOSV(fn;D>~LlVIq|2x9tOQqsrMtJ4@LC! znYXy717i?p0^utBC{LM(FH5Yt9Wed`il*gsaR6#<{VN%JIY(fsmR_=28&>;kU`D1~ z=pBt_GPUWu)zfpJU$+~te5u$&Q{j|?F3#38h#bsa3tl)uZ}{iZw&Tul`SsOqb`jrA zkDpIm(T>J;Y6eBDQ6cq$Q7p}hc=xDn?*ZDI6}j=B-LZXd!?r`P6C3S~T=tCq{+)QZ z!nx7986EIe3QX7N@|eS%+ic9S>ZGpBbZ1g0YBY)x6PxMbWZdF&!il>jmDxkf2+nxa z4TH;3?ga{vJ8-d~#6Qod7MDW4pLBcY(h*uR)M#xc-tb66aJHKI4hQ1a6z3QQHgx1C zuaeDhaQC$W53On1(lo6pJ!n79zyx{pkrAIgWiBfzb@ejiL{oW|E^;i;wqFIv3lBF% z;_>d0z@aqG0A@+;RfBpFAPL#m`DbW5CCgDmRP=D*A7GrW<=hc!)I@zy z?vQ`TIZxAu4oN`uwM47LVmo7OE|#T{G0W7QTMiSGoDr7J<+aOb{lLTzMw7dKH}y&7U|83-<0|^bRyj3wqzHP?}YkIw0Do z4S$<8y!9d!h)cb>x#m1x1EF`2lBq5R>$}LPTeU-(%Imo%0oQK5Si^fa^0sHTwdN*< zeoFf3KV@@oC^PfNo#?Vgpm0!*=hYi^P*rf%Y$`L(xP3@I5aY_+winBIDRTAd8slRV z%F}GB!mPh}0x@~JsUsD1?%vW&?_>@tPE8>=3NKbQIuCgr;+c-VbisSw6SoRx|Vb@<@2)s%s6zZD`g@xqoh zuK}g{$;ZHg$09q;6ImiVwj+~tGQSM074{f8U6^yN;KPgy`=bpy8MDg=dDWyuiouuq zoOQuej7pJ2g<@qXo}wNu~R z_wp5v+F5n?B!)+>pqy8up3-+?1R9h4M*~78)cI z*1>!)qN_=Kle4$DOaspST$-o|#rU!|y3Gq621Q`Rzu5$inrnPIyCkDH4XGm9N!N3G zocUouU#ExX@VeP8Jl8~aoK(3)xUY8oQD4iHKn^Tf;9=QZv2J%_meK8ry{O^KZFWuyNPWs? z$@B@*VAx=vG9PQLkD)iddTKC>96}@ADc@D=C0B-r8$7U{PmMb^fcug}c=N4a`E$@& zVZ98y(q}7<9A*Lv-2;CviBKM$ip_%Ht3(m&4pZz~DGU?q2c-wU#ALU+WXpQv?^#8R z<&U_S3`6Gvi=|>OaAu!rJct5#KmvF}iyH+`iFww-A~7AWk8|0xQdz{1T!Fr$5ewoS z;{1=X2a;hY=EYq!vxyJ!T~-3~OL8{@s~l|##1y?s>}n94t;6+}_HP;;)O{4cO8%>~ dwtr}iVR_M9T$#(zLH=#mC?m9Cg}zhle*mOyv<3hG literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_new2.png b/TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_new2.png new file mode 100644 index 0000000000000000000000000000000000000000..a50fe92f0da9a4cb1c5af1cd8f00a8c14e773262 GIT binary patch literal 915 zcmeAS@N?(olHy`uVBq!ia0vp^MIg+<1|<9Mw$=hE#^NA%Cx&(BWL^R}E~ycoX}-P; zT0k}j17mw80}DtA5K93u0|WB{Mh0de%?J`(zyy~qTEL88gA^9jO%h{ZVEXIn;uuoF z`1ZDSwn(4^+k-VtVr+_Y#dY4~9LY#bl-wYuxc{KqToxVXe;rqnau+6u9(0m+6kyTT zcI?m$(b*DcvX8xG?}1k{-<(M+oX0FbbF%I9xzEq$ZQ4C~scJ6+BNGdUfPzB9=zEZS@a0?U?|e`y zRo}X4#q&$2e@}G?3$|Xl>}|1H&a%GmLU|MYtQ2#W)GbNN)HnY5Mrm$%{yXmn>?_|! zUYb2)_w0X9?=k3|>SX)-&P5=%#CwL^%+FkwH(uv1F-z_JWb(Q^?Rp|xrR7$0)s}7h z_TKzj`K9i!%GRr4QT36_|CmPqz4XN6eQ+j+z@6loMY|tvY39*aytitu#)+tv4|i;p znI}_bZM!RS_b!F(rEQP%c=)aDcg;#~xNN$cW0PBE&6%Yi^Oo=Z6!+ukELoPw#U^&k zUI`l6r7nxRTGy157sGnH=H^OcKi|EnPuLv$5<=wa*2-MJwjtk?n-iql=ci5KytP^O zj%NK&YCmflpIvdwf5O?``6ly=9!SnN`W$*T$MKp*Z-1r5y|-JwTTE~Hv*xDb1XC~N zWTxMGcb_ZYfA#4@-SqD7b#M3FF4DbPdi$mA7aN92kDB==>rC%mbMCQC)qeK{;X(P< zT*ZsOANb7T(IFo6wD_>u?Qb^c560EAOiJL~yw*K!C))|v$L(J)i~jj!VtxAknWCE~ zekIZe;=h literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_new3.png b/TMessagesProj/src/main/res/drawable-xxhdpi/emoji_tabs_new3.png new file mode 100644 index 0000000000000000000000000000000000000000..8d8e056f36d4323a0cc6563f6cb39e14fd472511 GIT binary patch literal 3296 zcmZ`+S3DaG_s+5ju?4ZIDq3Q%B36v5&D+g3jn5g10a7amxNyu005T*0pLpo{^^y2{%;yw z4*viAk1;Sdn-c(FL>r;Bts;QirGBrht$9XvsgUeR>1h_cCD*f4^>aVon30Jw6rbZ0 zN!J->R!J}$XSdIf9W5#e5FB$v`Yd7~R><9c)9%sj z2OU6oGHOJB20qmoP}Ee4 zw>1nDD67h`5Y2vWV zee8wg9=D#5ScqH)+{+|Nnw!A^d*8~D_7M{f$r}21yb@bCb#We>)g7vD3CES&*Zi^o z0$}F&(sKLTp4-6k(yIHA6x9KZaxSFEr1|&o@|W$)km#*M4N%lg`6ev7U_)QoSXWt& zzTQii{!`tXfjs-42j0BU^?@E|EibDh^RI)N;qlT5;LU{A%qX*8ctPdOuc0y&@dz+d zI7RX_&6(|AVd|jmp-H7RY4`R7`-X7csevF+1UphL=-KRnsnFi>H^A2*XQ{os9QIS_ z)UuB^q<4#Z8_@fF|L1sBAj|WL0cxkoEA0#)uOx*GK2AL0e#J*g&-(c4Rx=A99pd;f zHMkFi%UVjN3pjtW=LV$6?!o?+PauJ8K2Jd` z7&Q3JX}(SYd}5dy`T1m&stX!5)DFp;|50nJR58prL2HrH9tz}g+m zMF;)Hh{{`*9xg1EA%d7jboC_mrM#6bG)Z-4eU`{?fZ^>m=$Wl4ehEK;J2@Q7xB4r-@fFJ?4cCSh zoN|n@cWt^|z|D8r+tg=GB9YMcHyAO(q3o-ehwl76PiL=Kt?r4?Oe#%tM15g9Q56i8 z4t?MCF+SCr#EJJGW7ni(OwsBRPBeS;mE&sQ6kO0)TuEn!giIa!XSSx5^N^-~QyrhW zpE2g3W=YG&t~Gdk!E-*TLd@vPk0m2=qC?eDC(x}J~L?mH~hfx7!A~$4_6}Vo;B8v zqbw|pVhmb`r(`c&XJ>VlMx+riO6%=!mdU-fM@Abze7=9}Qy>3)c(hmBfluuZU_5Ir zf%S;f%ouLSQy0}V2mm#Vj`Z(`rRXy_O6`=SnH(j5hw55``VObywj`KxB}X&3z;y>gO$zh@ zE+Ihn(#ui7uS_Q|ZoS(b^(w=8l*)A|f?1EIV# z*dVnj*ovN$RGaGyk1wo2GRuANwG zME}w|&B1?f@sX&KwS6&XR>ea~y{CemA6ijWuuc6Pv(>e})$^+bYg;rZ$s4>v%9E60 z=AVaom#X3{8{7NtgoKWqGJobsm!fc1KPYI5l*+O}yfwPZS~wI_D}|`7NeEF=TXC`! zUknz;SyqHGwa_9rWPvjx1c9m<+kp|xuG*OI_MHK(hW$+jVk@!=Htis4YS0kB?gW2H zw4YC}h(xJwv&?byiyf3Ov6m;-AKGAFQcVRHCmv|qKAyHuQ@n+XE@7PL{`%z#E53pD zv1n=79eFPU6^25CN<889Hw)Yw5gCE!J0_5NZU+@&fS7m_WkoU-Wwt)&oQX+iwq9gx zkR!InqJsVBqp}zHlS5w!T^LmJC!%YL{Q{5RTXrvO2$W`*QDIjrt!RA7_}u$nsW70g zq~U|GGL%Ez#^o89D9<%dg{hL4l%)`%#Ja0xsD{RT#QDQ{$F8o)!G-j{I53lNmshS% zIDheM4IQqfGdx_kj~F>93ti!B?-1^vXw*KJzFece*`>9sXtZ*p%O~?|-!*t_$nsWd zoX60e4;Dz3vM6C&Ra&DeC7TFbs^TY=Cv)=?7glmUd+BMruujbU5nNj42Kja=Dz5Y# z-5bsWw09xW)by~ve;QQmFn_y~Hhx!ND(c3C9mCrhM`e6Q!7)4!TH)lI%Ts=YzH2e~ zNf$YIfCJ*V4kkHM2={;FHe@}i@KLSZU=6LC{^S!;TnEZf+^Q4Wk6z728=hjdXr)KQ^F z_r}~X5kv!@Yu@1;F(H_^@n%jD@+0Iz)CpdgGis>?p%R!T1D*{zL?m=E6G*Ok#BZDE zYd78Axm;WP7gkr5G?lWbi`XEGzhmaqxz18SX>{Ui+6bd(Kd3`-_X4^-ZlD^K^s|^n z8#-tDcTY6dDukWJ@)n)lSPB`)u3^UHUjKt%NdwyjzaE_S%+jNuKe$S(CA1!r&f%F! z3jI*GO{KMoiQ3K3v*swBeyjYKHfmkui@=$c+|uvScYR6DdrMCUNYtEt3D2A()3dbx z%>tT@OtWT}A4!r1us(8aV3{$~(C-~1LX7ITc0B`;X-M-q)O?BTvF|P#u{g3{nSmGz z#Nmx3B*)u8os+;a--OfYsY>#?NlXWXICR|GlpkMFoEhYQDA>8UP*J%or)jKGN)upe z>!4MJ?I2t{>rM2HH1Lo7DF-3!&Nm%X8afGr;-BvGC6XX}g(*(0dSvk0za-w94hGe` z-fe4vN>21WW#7@F%ldI|;$kbSVbc|{>v#NUa0%TFMZNsxXy+(O6FVfwydwkjFizDw zGd;HSS#CiVP>sv1Mv0_`e94r(!>LYB|5s^>pQ-NF$0lB+NZ0uzK9=cpXeGuAJ_1T=%p8jHOj2o-f55s5#jwgrC|t?_J^b*duPx^3W;YrJ#{ zgspck<>y9!uc}UaC5%H{p%KtrJ*YNW)BN*mm;4hZOeezbVY#G5A@Nu~ETsY$FjSTA z22$Wqrwv_i^_q^bNr*a&^|;!zF$c1qHQ(u(hcA7z1-d=7&TQ|ab9Sq+7GlAHRz~%zym|ZO zni3#ssrn3_!!>6dn<~_2BtM065l}97zO1_jk9E^HtawNJ-%#QQqi8w81n+G8jQ0EU OT^Q+^p&E3Y_lbV|lCAp8emynx;ITz+CIVMbUXIYNi zW+sfZTwUVT=ktEv_x-$oe4p?4`#j(Op68DTYhh-@%_+hO006iV#`?%3e()E^Ku0xo zE-w6tfc{7$T|g~aZ0*SCyWT{&nVJIRkMuDBFu@bR_BV0_ks|;AtT-Tm^@xFgdpMT= zOdsG_|EvE7{UXvK0037fLLYu32>7MIi*U>OWM8X-4~A2pliho%hVPCw*x0)9{0K7r zu1qd2TH>6TXnoFg?nTmekAW;n>=6u*DsI3>xF=NjG0yTEZl|91BcpAOk5A##Y z<(lW3=dQpsZn1DTMd7=a(24Yem=Dtk1p&ksId;lCCXTv(L< z7>-N5mHTr}hpPz#-IB^OuS01mM*8g=GiRU_q|RilhbIZCUF)BrS`m&-^P5lBG!|zV%~P z>h^#9&ze;YeH+xiv4LgxptZ1V+y~VHEdq>QX$0n}@>#cuk5yd|4^(anlPxp{D7*;O zx5{7)tKK(RN&2MCZ?QszKtfzN8o~klqXXSFS)8KyRF?b#@*T_bEB=0<(;$po9ttQQ z-k@C!aE}0MJ#hZ`qEB5?&!_^Q|A%+Cao)a`8e980)Ww=oNXx8%<72jAi$a)&O8*LX zz(ZfkEOe?HQ8Vie%m!OJO4*)xwh_{S_PsU|@Ya&T^1~FJjt{p9sK-+XMuY24-!?-M z5|k|QBfH3rVV1f_%r6W=I(rp_}q&-W%I7`H+SD>7Y}kdXCzd6#`i~gXw>7YInLf{lkOV3 zEX(DDY4mq^cXO-ao@#BZd7*XP$y51X2s{am^3DT21()ge(azSU^oL=u6fvHI>4(55 zeuvA=@<_}OY$knI#G^)u^z>F#fPDD-Pf>rb!!iFiy= z!`D2u-+h}X&AlY|nF8ZXyTM)RYMr*>?^Yf$ahU@NVX<59F#egA_{zzwe#0W4Wc^Q2 z&y+j5U_v<)hKn|=4LLogP^(|^MGx%;tNwM6+Yf*{CuztBGUpiWkW?RMpHRWpev6%B zWFJ+=JDMAaAkonioY6!O2#(B{_|el%@_kXZ?J9RA%j%h7y`qXB_T_+w=FEr;~ zUN&j1>k(~If_nubvW#fRGrr3x&T~$O)QFx=K-u?bJE3FX-F;8C$|f_6+Ye-9GvN}(f?B-%C4LIWvv*=d>~3UqOk)0CdwIN$fn_Y%zf=%jXU1jte5cxv%guQa5b zrLLBDHAhxknx+oq(-3rQJY^<-(y(m}_j-7&0RBFu!nF-|5`Fhb|h zd*1f@cKfNZ>j?TbNM$q5LC@T#M+Z(}wL5>k*eI}f?rF(^hjmhCXQ~Z2Jmj`9?)8`~ zQ1-z);-Sol){Ioar!@1D-F2AylY;4b9OaG&b>c<#FFl9Wly{=_;+ovA&-H%r+;~2U zWKN`nd%O8_arbsexv^|@H*fUTK*6Ln4%RnS9vQYpc(2#B_rk`!E>3tkl4xYwHOo~5 z-3sw#Pt=_`6dd}yq~#MDoo0FOei2k%5Yc%k^;$0nRkrk8-+9)!J1W3`y!*DRo0RC| zcyL_OldYRtDZmhwxv{;J`)z#@b02by`ZkaKy~;CXv6r-~spGCM82Pjhr_!z&`u@-< zHA8O5-Z7&4eXI)5>m+QW0{gUqAVu!X%6U{Pj5Dp0!L;e+s=F=@TDcc zRP}cREf0a0LU?~Rv}i z@VfYVM<#Nqzgt6P{Y_A6Oi{tumGit3vzqs*Y|NM4Q$AyP%QmyDC$ZljaVS;2d?T<( zOLR|z@%XZYt8~hc*H6^zMHmz~acC6Z;UFv|P9*1Ps{*952hdq^Ov8d|t>@V` z9C9ld2XYkjS{X^b=Osj*5fgGjcWKsIQ` zh+b&W2tCfc2zeraTYfW57^YR9{nZStIUnk(eG-qFwXPMzXT66SRT$eHm)EZ9=}f{L ze3D6?HF8}qKgb$#r16of^LGnXjj)ucH4}@4u34Yb`*K#CYcH$nPd}-%xwzoB!c68?p z6Z8hoB_34#czOJa<#;cf8oNi7GC!pi(R@7&KzBm;>fCr#dX+71wVdnM`|f$09Ytx3tJYS^T6$b8 zgVwlW##&{K+)3~ic{=VJksGji2Hu<;c1nwbA@xkc%O^nD>)PEqUS#G-If>|-EnkjJ zfjKCXznd|q-7^4^2zB_n)2?Q{#0_}iTdZ$XUOVajq6%yljKh<5j2HAP*r4VLDDbBn z?za~)fsV?l{I`VeO`1yHke-V}TP}_6*81A}BlUc9Lxfk?ycd<8=qCIm#EqRzTbhz- zAa4I7_?7Y#cWnjmXX>A}%HG?1UM3}hZS8D^D+Q%R<{2$98+GMlYH5%e8B~MkqJxt5 ptU)@@O&_P=|9g%9ZF9l<66(Xx%)V`bCjY+l2m>?yT3vMXzW_hd@Rk4o literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_forward_check.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_forward_check.png new file mode 100644 index 0000000000000000000000000000000000000000..2ba7ff0e3bb6146768b9f928cf2476d6bf898b31 GIT binary patch literal 997 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(<^v49!D1}U6i==L6{+~3p1F{Fa= z?JVmIkwB3)<4!fZq#h3!og*Di4vGm2J5DGiO;qxg+PJ}EmPwh}ts|$7dCd+ni^^EI zV8((42?E@%tlWZ*TR1xSYwSO)eO;cgOmgS*dp(<9zq?!fAieDU&hKY_+M9j$iJsjm zXu#xlfJ1^&If1380iCnfc|p|LZ;MpdKDM}^84$K0^NX~n&%%8No)-EuxBSU^x3I?N z_*|`J+FXEIXJEB=XOR z{Ir_M>!>;F=zWo%_b&HEEbLG49(`_eK=7FP zLVhk8%cbvm!E)=J^+hZu-IAaGnZaEmzEh5yQMHd{N>@IYL)C>?)}AGPi&X5s%el{Z z=Va{B-L3Mx((CB-V|Ul}X@;|^pJe`%`FT$1cS@{$)r*z&TRh(GTD$Nk*Cjdq4*SZzmrSQG zn{mJQor3O@^E*D~D`pgN?Cf)$dC5<7 zY;C7GQp^@TXXWtSk>zI2H-}Lrec^=}J$y~)1yZglSKfNgz4N5**TT;A+uKei*XYg) z<%m@^U;cYrn_+mEGDAf36OsA+J6FEd%(maUDtP+tWQP-nx7d38JN?Ld*EE%rtlPwJ gCtPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS?h)G02RCodHoO!5KRTRd3&QrcJ zhn$)*(fXqag20Tdj39#ogHRfYAh3xd8jvDrK+Ci;vJ45qq)4nJi*TrrC<-DioaeI4 zA?NA!`(F3xx%;hs=J(#!&Vr9~_u6ZHYn^q^*>~@K&U?!x;C`T{6Y zro<&lu&EgX{)6BBAoKVdms?dofyPE8x{JQoz(~*^bOimt)!-Gd1Nh{=sz6Qyr)56J zg!+dZ%Ho?O(g)}pvE+S}y`!Si-xIuJIiu4cKgj1#-)xUxQuSU8FYxgfq9?%Xo@|~@ z69A4!JUs9Dc(5M`zV-MeRnPSC=hl-!Z*a)SWJVTpc&sty2zu{Tq&W2V0DWU{CjAqE zD;u-qn#hMpce&AMZv&?$B5;iG>khU6R~~RoGc_UY1dcl7hiT^;tlLIpqZzU*)0h?S zaFt2g`0d7Ss$o0Y(4|(ve*mWj$5GRXTm^FYjyI|N3g_EU^YY^s=|AB=%;4OVp2_xr zTeqw$Q5S(t6w-RDOO!p>bq(3-V|TtO=`#tO`bPClhbM|hXm^5F$6NxxINXA?gZpS! zVwbFuhd`bA;1*|pp5lnOQUv!FRk1RSYvOEfF$=_r>?iPECwONH*UizTYF4fww;1}? z-J&(&#rGEQ8(0f65A68r7x6=|70gw;Nw6de9BXC_A{YIP9{^#|OucG|pdC2|(XZLQ ziX4^?qUvE@K^2Ez49A-dT^g0;zbpa4Mc8z7>>i{&oimL(hhr|-4WO|Q3_hm(@57}| zJ6RLLnwbZYi>F;maG)IoOBSKD3be*DMyGXN_Tnf(2wH75ZxyJL z(`DrK7qG-#YFIYXeGN|W#Ym-xXsj4L(ro)dQAv`EX5^baImM|LW1t5P#R-HhzM~kI zJvp5VR)aLM5aV2PIWsRvTYOL2OvF)=*8s2#B$2N$a#sfqW6mZ4YVN59IrReCu#b{2 z>Fkp8^pm+0sOFxFkyAZb0xVg8vSo@BE+Ij+0d^~x3BCuLfR+OqJoSxg&w32V_fr}U z8u6JpU4e25%M2P?cxRTXwM=G~8gir{+Nc+nv*=z?L#dUiloco|P*xyy1qvCY85l1l z^2K01Sj^z)K3zzi*h%3)VcLUfAS_xX4{V7OB#!1kn!L^enp)_&o2D8Y!FS+YFdDSV zJg5VKKLVDlL3v(t6$x(5p7pAWyA6xV6unktyiePIS%-C z7o+k~-ycA)4K|+;K*gB{lE^lUay@Je&IF#e;N>JiV{8u7|8T%t=n-N)|JL?q8quE7 zh`fowp*Pct6m9w!CuFAQV)HX*xDUS@vu1Ml9q}$0-OPKg=-r`d)RLQ-#)gk zr0cTgROJVeit{4UP4+Op;K@04Bzi=V|UjR!s zqZC}3{9^SG>B`YgoF$rT3du)59;kW_a2$mBo zjlL=sHqWBauZ6qxP~0o3jO~W+C9a*ed9;>%)?zW+u(IMbn#C398;5Ki?W+j1%F%n9 z*iNQXn zTP(0^@gujU?&CcgWEf(#xO< zl#t}LFRYWyBR~0KLE4%4+hxG)}B=9K6Dv@vRLt5MWLE&QfK64Ty!McnU zn&aaQ^zatFqc}d`jymQZq{HN#gx=)PpITl6`hyN2N;F6>0Z%eWMlL|%)*$;LcogIj ztqC6^-lRMvHS0^E#(~;>s!Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS>lu1NERCodHnhl6lRTRf}a$PmE z%%Y^EPu#6BRc)!foP)+p?gXkkOaDC`4*!Zfmsf+Q*m3M-=}D-6O!V<~rKODxI0 z!|cPgjn&q&Y);lOa|zf6H2DZ+&7iFi$zKNk z(JEu;KA=BsP!)=V&jwvaA^f`QWZs}C6p60)t45CT{pjBSa=#jwLULnsB)7*=iAui>ofLJb&1cDuLHu1Msat~(X5OJ-3HU?tVXD4IaT7Qwi0_iIf$$TaAiLJi)stmDkY;f7gG2c2bO~%tMX0QUE~rc);W+I2DDGU z(v=AQD#7bO7L$I`fWf)0v0|AA!6qe(d4&eY=OnOJ@vW)eL}~qVD3p)$e26`$Fs^rY zXiUWS1)y1pVXFQ`u>zc6xF~KHLNiRJ^jpJbh0n{u7A2Ocl2!TBU~JeNocuv#1Q1fD zoaE%tGKm1PD!(3lV+K?Dvdi!Y7;SMG&k!WuR|wWc6`feJDxV8}RN|Vd-%#8Kinl5! z_)?P<{g1jt^}Q_uxp8n0I^4b#bB?(!&GfYQ48W* zl@n|v&=z>H?(@Lm2=F-g6Zpwj7+f9Khge21S>2ja8yf*nAV?ewo&(y4W^$B6g^NAY zqiGW4F*7p#7ov$4%q#K7gI7V2Rrz}CLjUK@GR3)^ZkY02)}V+DNSz7ZHpwY{*@vo* zges0@`=Un)dUPZK9h|~#3eX;nytL(Kd7hv9l%Ih%j^j_zPcp{tq`w=;No!{s((cIg zIeqP>9h<;&7QvQ+T*psMaI)?exE5#?LFYJR+QXbo@%*lPv27rM&mH1qr%)uxQMf(? zwBx5LVl-zbgwY`AG7yKz&tP&$oMo}8Jqawd`FCAJ?Xo!yaZHYuribA#{qtU}_!^_= zvMSdC#q|We52o*A*%r{?+au3t+HGNv&29GZ&t{~IxvZS`<0o^|?x5|3+>g9{Y5xuy z`Q%%;79)}9S0nML*eCpn0Gs$<6Z0`Z{=vx$KCQ+m? z=j4dC6@62VF*h|q`@=fue0VkjoBYjSuEMz$M`qzvcKl5F*4<-xGa+>bSZ$J1`W@)5 z3oo+A6OOfKvE?3<*qd-V8Po&Wul5sp7ORYmB+j*``I{pPXmJdeW5BaOelC8p83S2a zM<)b01ZZ0e$3+*luV*-n0rE}O-WfpsKlz&8HGREApdpa9qqseSP^wGf%LjBn2(qKt zf?XXLqB@I0NiaD*Fl9v)vAjMBD31wufFOI)yRcgTD!e{oX%kf5U^S&Ks^A0~0&W38 zPc;@|J2EOhR=)%u0lI-Hq3_NSK+eUk1@fw&pUAn`qu_9B0#Oe_pju(t8g^V_%mN=P zu}qcxb3Fs%dM>o53XvryL;CghS;0r~H5F`7Vwfsf>%0O^4DLM!O9*M?=N1|ozh{9p zAc(w$(dmW841GSNh6C;Wpb4(Q@HYXh1VK(B-ows!|JapF!Sf(D4VcpA;ut66!1F*` zaGEhbL{aS$C)PQTTx>F@zcL>Z*vmVLK~F=!#5TUu(0mfnY#}ROaZ~1Nwx*6rqrd|o z$Xl*IU{?!VcRJ1R(f1)+0kmhH(`?b1BloSl!EWFuJ2AKo47K?z$uUSzRI-zuLshUX z2lGIyGS5`)L2)lA-a9>N7I`QkKgG<7exvCEoveOu0Na3{bYdXy8WsPoFf);S`y??# z$#w_2?0z=wE5N6UovD&Xm8U?-98>38k3RM!BU=pGdpL6Ty$Easej;c2F9Da+kUpGB zPC;Tp-k88ll~ri1H}sNAIA21NAPBNa<*iTyo$yZ$@+BXaJ;~Mpc~JBd`N>U>i&sg- zA#pix(jHo+Xj{PSk|G=e8Q0feE+qC;Ab->CpqW$C&0G^nDv(qlsX$VJqyk9=N~Qw; Y0R+pg#5`ghBme*a07*qoM6N<$g8f=VK>z>% literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/msg_panel_forward.png b/TMessagesProj/src/main/res/drawable-xxhdpi/msg_panel_forward.png old mode 100755 new mode 100644 index 70ae5956e0a2c88e9000a6b30e27b6f6f0f82cde..943bf5ffaeebaec71eed14ea7c8e4fac58753268 GIT binary patch delta 754 zcmZo*{?9r=vYw5Bfg$wmW_KXPSRCZ;#IWw1%u680B{jk`&DWPf3&`eRU~JE1U;!xs zVksbIU|?Rr$iNJw89`zTnBcM=3z!jXkirRuZtocwm@++G978G?-`@82JLDk2USKFu zF4vf6Q{VKFLrw0W@Q*s7MI38F0-X2E(B8i50asq_kzfCPJa%O~*S#?FzlyN*v!8!8 zrt)20s;b5n(7+_JfPq!Rff2;~$o-^pU+;9%K^5sw3E;1@<9}wa1RB7WS%iM0Pj_6Z7ek1GcX zcB)TMhz%)`J${<8fn}v^(BjP7l4=_|&5H83^xfHzyME>VRhs6Sa>;$XOE!8wT~KQ9 zLF|)xk!+BFS3S4Heg4LrMmxhBUFRE$GpbpauwO6CZ7jOp z`0UO)?j_aJEp>LAtV*0MsK*~Wy>dB|ikY***PMw#OhU7x6~5+92x1nREv>-oGcjnV z$MniAg%9hrv^DlkGrGX8aIHZlK2|HZOMatQ&!(m+jq6I&`y1+)a+w(H{Kqlj>)kyj zt14eHKR;*EcBP@}faI)49($)WJeg&@>7XZPMwl0>o-nk z<+zc?wJG7*903QyEUq+${Y?yE@jI+2!WrfFqjw7CKB~e|2wy`iYta>pBi6qofH1vBvmX&-`Vj SJEpONQlO`+pUXO@geCwYFe{$` delta 368 zcmV-$0gwLw1%LyP8Gi%-0095wd&~d;0Y^zhK~#9!?b)#_gmE0l@kg!;b)qN(#X>Pz zSeTgH#Ky+P$i}E{xr|K8#Ky!T6AQ~Nx;l$1hAWCFB=Xi1e}T|@_zjXmWwzgk~2A&S-Uk zphUY11Z6s0Ah@8*1%fI)E)ZPP=K?{E0T&2v8FYc*p5dQA{5N@I)CGbk#$6zIA@2gg zD^o5IyffnhrKC9*2tHVFfl|_v3zTNX23mH#vPIWNJ?}d9v!-MJ#sX)#rL z$K*_<=b@_>-lxG-obS3;3)_-?&^Kq(wVqmcE%1u273XRai9{li-^nNQO8B4Pm>InQ O0000 + + record_audio + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/record_video_l.svg b/TMessagesProj/src/main/res/raw/record_video_l.svg new file mode 100644 index 000000000..72892bf57 --- /dev/null +++ b/TMessagesProj/src/main/res/raw/record_video_l.svg @@ -0,0 +1,42 @@ + + + record_video_l + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/record_video_p.svg b/TMessagesProj/src/main/res/raw/record_video_p.svg new file mode 100644 index 000000000..85ea85938 --- /dev/null +++ b/TMessagesProj/src/main/res/raw/record_video_p.svg @@ -0,0 +1,42 @@ + + + record_video_p + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values-ar/strings.xml b/TMessagesProj/src/main/res/values-ar/strings.xml index e355ffa07..1dc9d873a 100644 --- a/TMessagesProj/src/main/res/values-ar/strings.xml +++ b/TMessagesProj/src/main/res/values-ar/strings.xml @@ -3167,7 +3167,7 @@ المعذرة، لا يمكن استخدام اسم العائلة هذا. يتم التحميل... لا تملك أي مشغل للمقاطع المرئية على جهازك، يرجى تنزيل واحد للمتابعة - يرجى مراسلتنا عبر البريد الإلكتروني sms@stel.com لإخبارنا بمشكلتك. + يرجى مراسلتنا عبر البريد الإلكتروني reports@stel.com لإخبارنا بمشكلتك. لا تملك تطبيقًا يمكنه فتح صيغة الملفات \'%1$s\'، يرجى تنزيل تطبيق مناسب للاستمرار هذا المستخدم ليس لديه تيليجرام بعد، هل تريد دعوته؟ هل أنت متأكد؟ diff --git a/TMessagesProj/src/main/res/values-de/strings.xml b/TMessagesProj/src/main/res/values-de/strings.xml index 8923c72c9..95bf47245 100644 --- a/TMessagesProj/src/main/res/values-de/strings.xml +++ b/TMessagesProj/src/main/res/values-de/strings.xml @@ -3167,7 +3167,7 @@ Dieser Name kann leider nicht benutzt werden Lädt… Du hast keinen Videoplayer. Bitte installiere einen um fortzufahren. - Bitte sende eine E-Mail an sms@stel.com mit einer Beschreibung des Problems. + Bitte sende eine E-Mail an reports@stel.com mit einer Beschreibung des Problems. Du hast keine Applikationen, die den Dateityp \'%1$s\' öffnen könnten. Bitte installiere eine entsprechende Anwendung um fortzufahren. Dieser Nutzer hat noch kein Telegram. Möchtest du ihn einladen? Bist du sicher? diff --git a/TMessagesProj/src/main/res/values-es/strings.xml b/TMessagesProj/src/main/res/values-es/strings.xml index 63aab785e..bdd835350 100644 --- a/TMessagesProj/src/main/res/values-es/strings.xml +++ b/TMessagesProj/src/main/res/values-es/strings.xml @@ -3167,7 +3167,7 @@ Lo sentimos, este apellido no se puede usar Cargando... No tienes reproductor de video. Por favor, instala uno para continuar. - Por favor, envía un correo describiendo tu problema a sms@stel.com + Por favor, envía un correo describiendo tu problema a reports@stel.com No tienes aplicaciones que puedan manejar el tipo de archivo “%1$s”. Por favor, instala una para continuar. Este usuario aún no tiene Telegram. ¿Enviarle una invitación? ¿Quieres hacerlo? diff --git a/TMessagesProj/src/main/res/values-it/strings.xml b/TMessagesProj/src/main/res/values-it/strings.xml index 0c7b3d051..7c855268c 100644 --- a/TMessagesProj/src/main/res/values-it/strings.xml +++ b/TMessagesProj/src/main/res/values-it/strings.xml @@ -3167,7 +3167,7 @@ Spiacenti, questo cognome non può essere usato Carico... Non hai un lettore video, per favore installane uno per continuare - Per favore invia un’email descrivendo il problema a sms@stel.com + Per favore invia un’email descrivendo il problema a reports@stel.com Non hai applicazioni che possono gestire il tipo di file \'%1$s\': installane una per proseguire Questo utente non ha ancora Telegram, vuoi invitarlo? Sei sicuro? diff --git a/TMessagesProj/src/main/res/values-ko/strings.xml b/TMessagesProj/src/main/res/values-ko/strings.xml index 895512cb1..af66e5ec7 100644 --- a/TMessagesProj/src/main/res/values-ko/strings.xml +++ b/TMessagesProj/src/main/res/values-ko/strings.xml @@ -3167,7 +3167,7 @@ 죄송합니다. 본 성을 사용하실 수 없습니다 불러오는 중... 동영상 재생기가 없습니다. 계속하려면 앱을 설치하세요. - 발생한 문제를 자세히 적어 sms@stel.com으로 이메일을 보내 주세요. + 발생한 문제를 자세히 적어 reports@stel.com으로 이메일을 보내 주세요. \'%1$s\' 파일 유형을 처리할 수 있는 응용 프로그램이 없습니다. 계속하려면 프로그램을 설치해 주세요 지인분이 아직 텔레그램을 설치하지 않았습니다. 초대장을 보낼까요? 확실하십니까? diff --git a/TMessagesProj/src/main/res/values-nl/strings.xml b/TMessagesProj/src/main/res/values-nl/strings.xml index 7c008bc01..030d5a652 100644 --- a/TMessagesProj/src/main/res/values-nl/strings.xml +++ b/TMessagesProj/src/main/res/values-nl/strings.xml @@ -3167,7 +3167,7 @@ Ongeldige achternaam Bezig met laden Je hebt geen mediaspeler. Installeer een mediaspeler om door te gaan. - E-mail ons op sms@stel.com en vertel ons wat het probleem is. + E-mail ons op reports@stel.com en vertel ons wat het probleem is. Je hebt geen apps die bestandstype \'%1$s\' kunnen verwerken, gelieve een compatibele app te installeren Deze gebruiker heeft nog geen Telegram. Wil je een uitnodiging sturen? Weet je het zeker? diff --git a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml index ae37ab2a0..02c99d046 100644 --- a/TMessagesProj/src/main/res/values-pt-rBR/strings.xml +++ b/TMessagesProj/src/main/res/values-pt-rBR/strings.xml @@ -3167,7 +3167,7 @@ Desculpe, este sobrenome não pode ser usado Carregando... Você não possui um reprodutor de vídeo, instale um para continuar - Por favor, envie um email descrevendo o problema para sms@stel.com. + Por favor, envie um email descrevendo o problema para reports@stel.com. Você não possui um aplicativo que suporte o tipo de arquivo \'%1$s\', por favor instale um para continuar Esse usuário ainda não tem o Telegram, deseja enviar um convite? Tem certeza? diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index 1a8d87d5b..d5ff58a68 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -768,7 +768,10 @@ Previous link un1 edited the group description: un1 edited the channel description: + un1 changed the group theme: + un1 changed the channel theme: Previous description + Previous theme un1 made group history visible for new members un1 made group history hidden for new members un1 enabled group invites @@ -817,6 +820,8 @@ un1 disabled the slow mode un1 started a voice chat un1 ended the voice chat + un1 started a live stream + un1 ended the live stream un1 muted un2 un1 unmuted un2 un1 allowed new participants to speak @@ -892,6 +897,13 @@ VIEW THEME VIEW CHANNEL VIEW MESSAGE + OPEN BOT + sponsored + What are sponsored\nmessages? + Sponsored messages + See https://telegram.org + LEARN MORE + https://telegram.org The dark theme will turn automatically during night time Reference %1$s is playing a game... @@ -1212,6 +1224,11 @@ Background shared to **%1$s**. Background shared to **%1$s**. Background shared to **%1$s**. + You (sender’s name hidden) + You (senders’ names hidden) + Sender names removed + %s: %s + From %s %1$s set the self-destruct timer to %2$s You set the self-destruct timer to %1$s @@ -1855,6 +1872,8 @@ Brightness Hex color code Colors + Colors will move when you send messages + Animate Pattern Choose pattern Intensity @@ -2160,9 +2179,13 @@ **What\'s new?**\n\n• Bug fixes and improvements. Permanent Link is no longer active - Cancel Forward - Select another chat - Do you want to cancel forwarding or to forward messages to a different chat? + Forward to another chat + What would you like to do with %1$s from your chat with **%2$s**? + What would you like to do with %1$s from **%2$s**? + Show senders\’ names + Hide senders\’ names + Show sender\’s name + Hide sender\’s name Chat list swipe gesture Pin Read @@ -3145,9 +3168,10 @@ un1 invited you to this channel un1 started a voice chat You started a voice chat - Voice chat started Voice chat scheduled on %1$s - Voice chat finished (%s) + Live stream started + Live stream scheduled on %1$s + Live stream finished (%s) un1 ended the voice chat (%s) You ended the voice chat (%s) un1 invited un2 to the voice chat @@ -3217,7 +3241,7 @@ Sorry, this last name can\'t be used Loading... You don\'t have a video player, please install one to continue - Please send an email describing your problem to sms@stel.com + Please send an email describing your problem to reports@stel.com You don\'t have applications that can handle the file type \'%1$s\', please install one to continue This user does not have Telegram yet, send an invitation? Are you sure? @@ -3447,6 +3471,7 @@ Telegram Video Call Ongoing Telegram call Ongoing Voice Chat + Ongoing Live Stream End call Another call in progress You currently have an ongoing call with **%1$s**. Would you like to hang up and start a new one with **%2$s**? @@ -3574,8 +3599,10 @@ Reconnecting Start Voice chat Voice Chat + Start Live stream + Live Stream Do you want to start a voice chat in this group? - Do you want to start a voice chat in this channel? + Do you want to start a live stream in this channel? Manage Voice Chats Join %1$s members talking @@ -3592,9 +3619,9 @@ Leave End Mute microphone - Are you sure you want to mute **%1$s** in this voice chat? Remove participant Do you want to remove %1$s from the voice chat and ban them in %2$s? + Do you want to remove %1$s from the live stream and ban them in %2$s? **%1$s** removed from the group. **%1$s** can now speak. **%1$s** is now unmuted for you. @@ -3606,6 +3633,8 @@ Share invite link End voice chat Cancel voice chat + End live stream + Cancel live stream Connecting... Leave voice chat Do you want to leave this voice chat? @@ -3619,9 +3648,19 @@ The selected user is already in this voice chat. Sorry, you can\'t join voice chats as an anonymous administrator. Sorry, this voice chat is full. - un1 invited un2 to the voice chat - Join voice chat Hey! Join our voice chat: %1$s + Leave live stream + Do you want to leave this live stream? + End live stream + Do you want to end this live stream? + End live stream + VIEW LIVE STREAM + Live Stream + Scheduled Live Stream + Open live stream + Sorry, you can\'t join live streams as an anonymous administrator. + Sorry, this live stream is full. + Hey! Join our live stream: %1$s Invite Members Share Invite Link Remove @@ -3633,10 +3672,10 @@ Copy Invite Link Invite link copied to clipboard. Unmute - Voice chat ended. Start a new one? Add Member Do you want to add **%1$s** to **%2$s**? You invited **%1$s** to the voice chat. + You invited **%1$s** to the live stream. Add Volume Mute for me @@ -3645,16 +3684,19 @@ Nobody talking Start recording Stop recording - Start Voice Chat as... Join Voice Chat as... + Join Live Stream as... Display me as... Choose whether you want to be displayed as your personal account or as your channel. Choose whether you want to be displayed as your personal account, this group, or one of your channels. Continue as %1$s Edit voice chat title + Edit live stream title Edit permissions Members of this voice chat will now see you as **%1$s** Voice chat title + Members of this live stream will now see you as **%1$s** + Live stream title Can speak Listen only COPY SPEAKER LINK @@ -3663,12 +3705,18 @@ You can now speak in **%1$s**. Stop recording Do you want to stop recording this voice chat? + Do you want to stop recording this live stream? Start recording Do you want to start recording this chat and save the result into an audio file?\n\nOther members will see that the chat is being recorded. + Do you want to start recording this chat and save the result into an video file?\n\nOther members will see that the chat is being recorded. + Do you want to start recording this live stream and save the result into an video file?\n\nOther members will see that the stream is being recorded. Recording Title Audio saved to **Saved Messages**. + Video saved to **Saved Messages**. Audio recording started. + Video recording started. Voice chat is being recorded + Live stream is being recorded personal account Cancel request to speak JOIN AS SPEAKER @@ -3678,21 +3726,29 @@ Voice chat sound muted. Voice chat sound unmuted. Members of this group will be notified once you start the voice chat. - Subscribers of this channel will be notified once you start the voice chat. + Subscribers of this channel will be notified once you start the live stream. You will be displayed as: Start Voice Chat Schedule Voice Chat + Join Live Stream + Are sure you want to appear in this live stream as your personal account? + Live stream sound muted. + Live stream sound unmuted. + Start Live Stream + Schedule Live Stream Start Now Set Reminder Cancel Reminder Members of the group will be notified that the voice chat will start in %1$s. - Subscribers of the channel will be notified that the voice chat will start in %1$s. + Subscribers of the channel will be notified that the live stream will start in %1$s. Starts in Late by Share We will notify you when it starts. **%1$s** joined the voice chat. **%1$s** joined the voice chat. + **%1$s** joined the live stream. + **%1$s** joined the live stream. Active voice chats Recent calls Share screen @@ -3718,7 +3774,6 @@ Set New Photo Camera Video Preview - Video from your camera will be shared with other members of this voice chat Share Camera Video Preview Pin @@ -3738,7 +3793,15 @@ Enabled Disabled Video is only available\nfor the first %1$s - The voice chat is over %1$s.\nNew participants only have access to audio stream. + The voice chat is over %1$s.\nNew participants only have access to the audio stream. + The live stream is over %1$s.\nNew participants only have access to the audio stream. + AUDIO ONLY + PORTRAIT VIDEO + LANDSCAPE VIDEO + Record voice chat + Record live stream + Choose how to record this chat + Start Recording Manage Invite Links You can create additional invite links that have a limited time or number of uses @@ -3939,6 +4002,12 @@ %1$d messages %1$d messages %1$d messages + **%1$d** messages + **%1$d** message + **%1$d** messages + **%1$d** messages + **%1$d** messages + **%1$d** messages %1$d items %1$d item %1$d items @@ -4138,6 +4207,12 @@ %1$d forwarded messages %1$d forwarded messages %1$d forwarded messages + Forward %1$d messages + Forward message + Forward %1$d messages + Forward %1$d messages + Forward %1$d messages + Forward %1$d messages %1$d photos %1$d photo %1$d photos @@ -4210,6 +4285,66 @@ %1$d forwarded photos %1$d forwarded photos %1$d forwarded photos + Forward %1$d files + Forward file + Forward %1$d files + Forward %1$d files + Forward %1$d files + Forward %1$d files + Forward %1$d photos + Forward photo + Forward %1$d photos + Forward %1$d photos + Forward %1$d photos + Forward %1$d photos + Forward %1$d videos + Forward video + Forward %1$d videos + Forward %1$d videos + Forward %1$d videos + Forward %1$d videos + Forward %1$d audio files + Forward audio file + Forward %1$d audio files + Forward %1$d audio files + Forward %1$d audio files + Forward %1$d audio files + Forward %1$d voice messages + Forward voice message + Forward %1$d voice messages + Forward %1$d voice messages + Forward %1$d voice messages + Forward %1$d voice messages + Forward %1$d video messages + Forward video message + Forward %1$d video messages + Forward %1$d video messages + Forward %1$d video messages + Forward %1$d video messages + Forward %1$d locations + Forward location + Forward %1$d locations + Forward %1$d locations + Forward %1$d locations + Forward %1$d locations + Forward %1$d contacts + Forward contact + Forward %1$d contacts + Forward %1$d contacts + Forward %1$d contacts + Forward %1$d contacts + Forward %1$d stickers + Forward sticker + Forward %1$d stickers + Forward %1$d stickers + Forward %1$d stickers + Forward %1$d stickers + Forward %1$d polls + Forward poll + Forward %1$d polls + Forward %1$d polls + Forward %1$d polls + Forward %1$d polls %1$d forwarded videos Forwarded video %1$d forwarded videos @@ -4541,4 +4676,29 @@ \'Remind on\' MMM d \'at\' HH:mm \'Remind on\' MMM d yyyy \'at\' HH:mm Menu + Pull up to go to the next unread channel + Release to go to the next unread channel + Pull up to go to the next unread folder + Release to go to the next unread folder + Pull up to go to archived channels + Release to go to archived channels + + You have no unread channels + Favorites + Trending + Recent + %1$s is ch**oo**sing a sticker + ch**oo**sing a sticker + Change recipient + Send messages + %s will see that it was forwarded + Members will see that it was forwarded + Subscribers will see that it was forwarded + %s won\'t see they were forwarded + Members won\'t see they were forwarded + Subscribers won\'t see they were forwarded + Show forwarding options + Show caption + Hide caption + Tap here for forwarding options diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f3dc29415..ee5360fdd 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip