From 928146c3daec432a73b318d73598f4bde4e950f1 Mon Sep 17 00:00:00 2001 From: dkaraush Date: Thu, 18 Jan 2024 12:41:46 +0400 Subject: [PATCH] update to 10.6.1 (4275) --- TMessagesProj/jni/tgnet/ApiScheme.cpp | 60 +- TMessagesProj/jni/tgnet/ApiScheme.h | 45 + TMessagesProj/src/main/AndroidManifest.xml | 6 +- .../widget/ChatListItemAnimator.java | 31 +- .../widget/GridLayoutManagerFixed.java | 35 +- .../SQLite/SQLitePreparedStatement.java | 2 + .../telegram/messenger/AndroidUtilities.java | 5 + .../messenger/AnimatedFileDrawableStream.java | 4 +- .../messenger/ChannelBoostsController.java | 25 +- .../org/telegram/messenger/ChatObject.java | 8 +- .../messenger/ContactsController.java | 102 +- .../messenger/DatabaseMigrationHelper.java | 23 + .../java/org/telegram/messenger/Emoji.java | 6 +- .../org/telegram/messenger/FileLoader.java | 6 +- .../telegram/messenger/FileRefController.java | 6 +- .../org/telegram/messenger/ImageLoader.java | 50 +- .../telegram/messenger/LocaleController.java | 16 +- .../telegram/messenger/MediaController.java | 95 +- .../messenger/MediaDataController.java | 243 ++- .../org/telegram/messenger/MessageObject.java | 355 +-- .../messenger/MessagesController.java | 686 +++++- .../telegram/messenger/MessagesStorage.java | 474 +++- .../messenger/MusicBrowserService.java | 33 +- .../messenger/NotificationCenter.java | 40 +- .../messenger/NotificationsController.java | 84 +- .../NotificationsSettingsFacade.java | 18 +- .../messenger/SavedMessagesController.java | 1139 +++++++++- .../telegram/messenger/SecretChatHelper.java | 6 +- .../messenger/SendMessagesHelper.java | 66 +- .../telegram/messenger/TopicsController.java | 22 +- .../org/telegram/messenger/UserObject.java | 7 +- .../org/telegram/messenger/Utilities.java | 10 + .../telegram/messenger/VideoEditedInfo.java | 3 + .../messenger/VideoEncodingService.java | 8 +- .../telegram/messenger/WearReplyReceiver.java | 18 +- .../telegram/messenger/browser/Browser.java | 10 + .../telegram/messenger/video/MP4Builder.java | 49 + .../org/telegram/messenger/video/Track.java | 3 + .../telegram/messenger/voip/VoIPService.java | 17 +- .../main/java/org/telegram/tgnet/TLRPC.java | 382 +++- .../org/telegram/ui/ActionBar/ActionBar.java | 10 + .../telegram/ui/ActionBar/ActionBarMenu.java | 12 +- .../ui/ActionBar/ActionBarMenuItem.java | 90 +- .../telegram/ui/ActionBar/AlertDialog.java | 6 +- .../telegram/ui/ActionBar/BaseFragment.java | 2 +- .../telegram/ui/ActionBar/SimpleTextView.java | 119 +- .../java/org/telegram/ui/ActionBar/Theme.java | 20 +- .../telegram/ui/ActionBar/ThemeColors.java | 4 + .../ui/Adapters/DialogsSearchAdapter.java | 18 +- .../org/telegram/ui/Adapters/FiltersView.java | 13 +- .../telegram/ui/Adapters/MentionsAdapter.java | 8 +- .../java/org/telegram/ui/ArticleViewer.java | 4 +- .../org/telegram/ui/CalendarActivity.java | 4 +- .../org/telegram/ui/Cells/ChatActionCell.java | 5 +- .../telegram/ui/Cells/ChatMessageCell.java | 897 ++++++-- .../org/telegram/ui/Cells/DialogCell.java | 110 +- .../ui/Cells/GroupCreateUserCell.java | 80 +- .../org/telegram/ui/Cells/HintDialogCell.java | 73 +- .../telegram/ui/Cells/ProfileSearchCell.java | 67 + .../java/org/telegram/ui/Cells/RadioCell.java | 5 + .../telegram/ui/Cells/ShareDialogCell.java | 120 +- .../java/org/telegram/ui/ChatActivity.java | 1246 ++++++++--- .../telegram/ui/ChatActivityContainer.java | 85 + .../java/org/telegram/ui/CodeNumberField.java | 9 +- .../telegram/ui/Components/AlertsCreator.java | 81 +- .../ui/Components/AnimatedEmojiDrawable.java | 3 +- .../ui/Components/AnimatedFileDrawable.java | 6 +- .../telegram/ui/Components/AnimatedFloat.java | 3 + .../ui/Components/AvatarDrawable.java | 51 +- .../ui/Components/AvatarsDrawable.java | 3 + .../ui/Components/BackButtonMenu.java | 4 +- .../ui/Components/BlurBehindDrawable.java | 1 + .../BottomSheetWithRecyclerListView.java | 7 +- .../ui/Components/BulletinFactory.java | 17 +- .../ui/Components/ChatActivityEnterView.java | 1906 ++++++++++------- .../ui/Components/ChatActivityInterface.java | 2 +- .../ui/Components/ChatAttachAlert.java | 4 +- .../ui/Components/ChatAvatarContainer.java | 259 ++- .../ui/Components/ChatGreetingsView.java | 212 +- .../ChatNotificationsPopupWrapper.java | 2 +- .../telegram/ui/Components/EarListener.java | 330 +++ .../ui/Components/EmojiPacksAlert.java | 2 +- .../org/telegram/ui/Components/EmojiView.java | 4 +- .../ui/Components/Forum/ForumUtilities.java | 9 +- .../ui/Components/InstantCameraView.java | 702 ++++-- .../telegram/ui/Components/MediaActivity.java | 18 +- .../ui/Components/MentionsContainerView.java | 2 +- .../ui/Components/MessagePrivateSeenView.java | 323 +++ .../Paint/Views/LPhotoPaintView.java | 34 +- .../Paint/Views/MessageEntityView.java | 2 +- .../ui/Components/PhonebookShareAlert.java | 4 +- .../ui/Components/PipRoundVideoView.java | 8 +- .../ui/Components/PipVideoOverlay.java | 13 +- .../ui/Components/PollVotesAlert.java | 15 +- .../Premium/LimitReachedBottomSheet.java | 15 +- .../boosts/UserSelectorBottomSheet.java | 2 +- .../ui/Components/RLottieDrawable.java | 19 +- .../telegram/ui/Components/RadioButton.java | 27 + .../Reactions/ReactionsLayoutInBubble.java | 227 +- .../Components/ReactionsContainerLayout.java | 89 +- .../ui/Components/RecyclerListView.java | 21 +- .../ui/Components/ReplyMessageLine.java | 21 +- .../ui/Components/SearchTagsList.java | 426 ++++ .../telegram/ui/Components/ShareAlert.java | 60 +- .../ui/Components/SharedMediaLayout.java | 1019 ++++++++- .../ui/Components/StaticLayoutEx.java | 20 + .../ui/Components/StatusBadgeComponent.java | 10 +- .../telegram/ui/Components/ThanosEffect.java | 138 +- .../ui/Components/TimerParticles.java | 47 +- .../org/telegram/ui/Components/UndoView.java | 11 +- .../ui/Components/VideoTimelineView.java | 32 +- .../Components/spoilers/SpoilerEffect2.java | 19 +- .../ui/Components/voip/AcceptDeclineView.java | 2 +- .../ui/Components/voip/HideEmojiTextView.java | 1 + .../voip/PrivateVideoPreviewDialogNew.java | 2 + .../voip/VoIPBackgroundProvider.java | 2 - .../voip/VoIPNotificationsLayout.java | 17 +- .../Components/voip/VoIPStatusTextView.java | 2 - .../ui/Components/voip/VoIPTimerView.java | 1 - .../ui/Components/voip/VoIpCoverView.java | 216 ++ .../ui/Components/voip/VoIpSwitchLayout.java | 31 +- .../ui/Components/voip/VoipCoverEmoji.java | 171 ++ .../org/telegram/ui/ContactsActivity.java | 2 +- .../java/org/telegram/ui/DialogsActivity.java | 68 +- .../telegram/ui/EmojiAnimationsOverlay.java | 6 +- .../org/telegram/ui/FilteredSearchView.java | 42 +- .../org/telegram/ui/GroupCreateActivity.java | 27 +- .../java/org/telegram/ui/LaunchActivity.java | 59 +- .../telegram/ui/MessageStatisticActivity.java | 13 +- .../telegram/ui/NewContactBottomSheet.java | 34 +- .../ui/NotificationsSoundActivity.java | 4 +- .../java/org/telegram/ui/PhotoViewer.java | 50 +- .../ui/PopupNotificationActivity.java | 5 + .../telegram/ui/PrivacyControlActivity.java | 219 +- .../telegram/ui/PrivacySettingsActivity.java | 20 +- .../java/org/telegram/ui/ProfileActivity.java | 175 +- .../ui/ProfileNotificationsActivity.java | 6 +- .../org/telegram/ui/SecretVoicePlayer.java | 422 +++- .../org/telegram/ui/StatisticActivity.java | 4 +- .../telegram/ui/Stories/PeerStoriesView.java | 196 +- .../telegram/ui/Stories/StoryCaptionView.java | 2 +- .../org/telegram/ui/Stories/StoryViewer.java | 12 +- .../ui/Stories/recorder/PaintView.java | 2 +- .../ui/Stories/recorder/StoryRecorder.java | 2 +- .../org/telegram/ui/TopicCreateFragment.java | 4 +- .../java/org/telegram/ui/TopicsFragment.java | 8 +- .../ui/TopicsNotifySettingsFragments.java | 4 +- .../java/org/telegram/ui/VoIPFragment.java | 9 +- .../res/drawable-hdpi/filled_open_message.png | Bin 0 -> 813 bytes .../main/res/drawable-hdpi/large_hidden.png | Bin 0 -> 1619 bytes .../res/drawable-hdpi/large_message_lock.png | Bin 0 -> 1845 bytes .../main/res/drawable-hdpi/large_notes.png | Bin 0 -> 1169 bytes .../res/drawable-hdpi/mini_external_link.png | Bin 0 -> 544 bytes .../res/drawable-mdpi/filled_open_message.png | Bin 0 -> 573 bytes .../main/res/drawable-mdpi/large_hidden.png | Bin 0 -> 1074 bytes .../res/drawable-mdpi/large_message_lock.png | Bin 0 -> 1201 bytes .../main/res/drawable-mdpi/large_notes.png | Bin 0 -> 830 bytes .../res/drawable-mdpi/mini_external_link.png | Bin 0 -> 402 bytes .../drawable-xhdpi/filled_open_message.png | Bin 0 -> 1090 bytes .../main/res/drawable-xhdpi/large_hidden.png | Bin 0 -> 2165 bytes .../res/drawable-xhdpi/large_message_lock.png | Bin 0 -> 2572 bytes .../main/res/drawable-xhdpi/large_notes.png | Bin 0 -> 1477 bytes .../res/drawable-xhdpi/mini_external_link.png | Bin 0 -> 654 bytes .../drawable-xxhdpi/filled_open_message.png | Bin 0 -> 1700 bytes .../main/res/drawable-xxhdpi/large_hidden.png | Bin 0 -> 3431 bytes .../drawable-xxhdpi/large_message_lock.png | Bin 0 -> 3950 bytes .../main/res/drawable-xxhdpi/large_notes.png | Bin 0 -> 2170 bytes .../drawable-xxhdpi/mini_external_link.png | Bin 0 -> 835 bytes .../src/main/res/raw/large_lastseen.json | 1 + .../src/main/res/raw/large_message_lock.json | 1 + .../src/main/res/raw/large_readtime.json | 1 + .../src/main/res/raw/spoiler_vertex.glsl | 17 +- .../src/main/res/raw/thanos_vertex.glsl | 50 +- TMessagesProj/src/main/res/values/strings.xml | 92 +- TMessagesProj/src/main/res/xml/actions.xml | 31 - .../src/main/res/xml/automotive_app_desc.xml | 5 + TMessagesProj/src/main/res/xml/shortcuts.xml | 42 + gradle.properties | 4 +- 178 files changed, 12558 insertions(+), 2961 deletions(-) create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/ChatActivityContainer.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/EarListener.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/MessagePrivateSeenView.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/SearchTagsList.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIpCoverView.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoipCoverEmoji.java create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/filled_open_message.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/large_hidden.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/large_message_lock.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/large_notes.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/mini_external_link.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/filled_open_message.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/large_hidden.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/large_message_lock.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/large_notes.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/mini_external_link.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/filled_open_message.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/large_hidden.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/large_message_lock.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/large_notes.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/mini_external_link.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/filled_open_message.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/large_hidden.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/large_message_lock.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/large_notes.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/mini_external_link.png create mode 100644 TMessagesProj/src/main/res/raw/large_lastseen.json create mode 100644 TMessagesProj/src/main/res/raw/large_message_lock.json create mode 100644 TMessagesProj/src/main/res/raw/large_readtime.json delete mode 100644 TMessagesProj/src/main/res/xml/actions.xml create mode 100644 TMessagesProj/src/main/res/xml/automotive_app_desc.xml diff --git a/TMessagesProj/jni/tgnet/ApiScheme.cpp b/TMessagesProj/jni/tgnet/ApiScheme.cpp index 9093b19c8b..923bfbaa7c 100644 --- a/TMessagesProj/jni/tgnet/ApiScheme.cpp +++ b/TMessagesProj/jni/tgnet/ApiScheme.cpp @@ -1210,21 +1210,30 @@ UserStatus *UserStatus::TLdeserialize(NativeByteBuffer *stream, uint32_t constru case 0x8c703f: result = new TL_userStatusOffline(); break; - case 0x7bf09fc: - result = new TL_userStatusLastWeek(); - break; case 0x9d05049: result = new TL_userStatusEmpty(); break; - case 0x77ebc742: - result = new TL_userStatusLastMonth(); - break; case 0xedb93949: result = new TL_userStatusOnline(); break; - case 0xe26f42f1: + case 0x7b197dc8: result = new TL_userStatusRecently(); break; + case 0x541a1d1a: + result = new TL_userStatusLastWeek(); + break; + case 0x65899777: + result = new TL_userStatusLastMonth(); + break; + case 0xe26f42f1: + result = new TL_userStatusRecently_layer171(); + break; + case 0x7bf09fc: + result = new TL_userStatusLastWeek_layer171(); + break; + case 0x77ebc742: + result = new TL_userStatusLastMonth_layer171(); + break; default: error = true; if (LOGS_ENABLED) DEBUG_FATAL("can't parse magic %x in UserStatus", constructor); @@ -1243,8 +1252,19 @@ void TL_userStatusOffline::serializeToStream(NativeByteBuffer *stream) { stream->writeInt32(expires); } +void TL_userStatusLastWeek_layer171::serializeToStream(NativeByteBuffer *stream) { + stream->writeInt32(constructor); +} + void TL_userStatusLastWeek::serializeToStream(NativeByteBuffer *stream) { stream->writeInt32(constructor); + flags = by_me ? flags | 1 : flags &~ 1; + stream->writeInt32(flags); +} + +void TL_userStatusLastWeek::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) { + flags = stream->readInt32(&error); + by_me = (flags & 1) != 0; } void TL_userStatusEmpty::serializeToStream(NativeByteBuffer *stream) { @@ -1253,6 +1273,17 @@ void TL_userStatusEmpty::serializeToStream(NativeByteBuffer *stream) { void TL_userStatusLastMonth::serializeToStream(NativeByteBuffer *stream) { stream->writeInt32(constructor); + flags = by_me ? flags | 1 : flags &~ 1; + stream->writeInt32(flags); +} + +void TL_userStatusLastMonth::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) { + flags = stream->readInt32(&error); + by_me = (flags & 1) != 0; +} + +void TL_userStatusLastMonth_layer171::serializeToStream(NativeByteBuffer *stream) { + stream->writeInt32(constructor); } void TL_userStatusOnline::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) { @@ -1266,6 +1297,21 @@ void TL_userStatusOnline::serializeToStream(NativeByteBuffer *stream) { void TL_userStatusRecently::serializeToStream(NativeByteBuffer *stream) { stream->writeInt32(constructor); + flags = by_me ? flags | 1 : flags &~ 1; + stream->writeInt32(flags); +} + +void TL_userStatusRecently::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error) { + flags = stream->readInt32(&error); + by_me = (flags & 1) != 0; +} + +void TL_userStatusRecently_layer171::serializeToStream(NativeByteBuffer *stream) { + stream->writeInt32(constructor); +} + +void TL_userStatusHidden::serializeToStream(NativeByteBuffer *stream) { + stream->writeInt32(constructor); } FileLocation *FileLocation::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) { diff --git a/TMessagesProj/jni/tgnet/ApiScheme.h b/TMessagesProj/jni/tgnet/ApiScheme.h index edee2074f7..e2d6820765 100644 --- a/TMessagesProj/jni/tgnet/ApiScheme.h +++ b/TMessagesProj/jni/tgnet/ApiScheme.h @@ -203,6 +203,18 @@ class TL_userStatusOffline : public UserStatus { class TL_userStatusLastWeek : public UserStatus { +public: + static const uint32_t constructor = 0x7bf09fc; + + uint32_t flags; + bool by_me; + + void serializeToStream(NativeByteBuffer *stream); + void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error); +}; + +class TL_userStatusLastWeek_layer171 : public UserStatus { + public: static const uint32_t constructor = 0x7bf09fc; @@ -219,6 +231,19 @@ class TL_userStatusEmpty : public UserStatus { class TL_userStatusLastMonth : public UserStatus { +public: + static const uint32_t constructor = 0x65899777; + + uint32_t flags; + bool by_me; + + void serializeToStream(NativeByteBuffer *stream); + + void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error); +}; + +class TL_userStatusLastMonth_layer171 : public UserStatus { + public: static const uint32_t constructor = 0x77ebc742; @@ -236,12 +261,32 @@ class TL_userStatusOnline : public UserStatus { class TL_userStatusRecently : public UserStatus { +public: + static const uint32_t constructor = 0x7b197dc8; + + uint32_t flags; + bool by_me; + + void serializeToStream(NativeByteBuffer *stream); + void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error); +}; + +class TL_userStatusRecently_layer171 : public UserStatus { + public: static const uint32_t constructor = 0xe26f42f1; void serializeToStream(NativeByteBuffer *stream); }; +class TL_userStatusHidden : public UserStatus { + +public: + static const uint32_t constructor = 0xcf7d64b1; + + void serializeToStream(NativeByteBuffer *stream); +}; + class FileLocation : public TLObject { public: diff --git a/TMessagesProj/src/main/AndroidManifest.xml b/TMessagesProj/src/main/AndroidManifest.xml index fded91118b..63066e626a 100644 --- a/TMessagesProj/src/main/AndroidManifest.xml +++ b/TMessagesProj/src/main/AndroidManifest.xml @@ -569,15 +569,13 @@ - - + + - - diff --git a/TMessagesProj/src/main/java/androidx/recyclerview/widget/ChatListItemAnimator.java b/TMessagesProj/src/main/java/androidx/recyclerview/widget/ChatListItemAnimator.java index 66516c7b06..765c7c3cf4 100644 --- a/TMessagesProj/src/main/java/androidx/recyclerview/widget/ChatListItemAnimator.java +++ b/TMessagesProj/src/main/java/androidx/recyclerview/widget/ChatListItemAnimator.java @@ -5,6 +5,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.util.Log; import android.util.LongSparseArray; import android.view.View; import android.view.ViewPropertyAnimator; @@ -51,6 +52,7 @@ public class ChatListItemAnimator extends DefaultItemAnimator { private ArrayList willChangedGroups = new ArrayList<>(); HashMap animators = new HashMap<>(); + ArrayList thanosViews = new ArrayList<>(); ArrayList runOnAnimationsEnd = new ArrayList<>(); HashMap groupIdToEnterDelay = new HashMap<>(); @@ -925,7 +927,12 @@ public void onAnimationEnd(Animator animator) { animator.removeAllListeners(); restoreTransitionParams(holder.itemView); if (holder.itemView instanceof ChatMessageCell) { - MessageObject.GroupedMessages group = ((ChatMessageCell) view).getCurrentMessagesGroup(); + ChatMessageCell cell = (ChatMessageCell) holder.itemView; + if (cell.makeVisibleAfterChange) { + cell.makeVisibleAfterChange = false; + cell.setVisibility(View.VISIBLE); + } + MessageObject.GroupedMessages group = cell.getCurrentMessagesGroup(); if (group != null) { group.transitionParams.reset(); } @@ -1092,6 +1099,12 @@ private void cancelAnimators() { animator.cancel(); } } + if (!thanosViews.isEmpty()) { + ThanosEffect thanosEffect = getThanosEffectContainer.run(); + if (thanosEffect != null) { + thanosEffect.kill(); + } + } } @Override @@ -1100,6 +1113,12 @@ public void endAnimation(RecyclerView.ViewHolder item) { if (animator != null) { animator.cancel(); } + if (thanosViews.contains(item.itemView)) { + ThanosEffect thanosEffect = getThanosEffectContainer.run(); + if (thanosEffect != null) { + thanosEffect.cancel(item.itemView); + } + } super.endAnimation(item); restoreTransitionParams(item.itemView); } @@ -1230,6 +1249,12 @@ protected boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, RecyclerV if (a != null) { a.cancel(); } + if (thanosViews.contains(item.itemView)) { + ThanosEffect thanosEffect = getThanosEffectContainer.run(); + if (thanosEffect != null) { + thanosEffect.cancel(item.itemView); + } + } boolean oldItem = false; if (changeInfo.newHolder == item) { @@ -1447,7 +1472,9 @@ protected void animateRemoveImpl(final RecyclerView.ViewHolder holder, boolean t dispatchRemoveFinished(holder); dispatchFinishedWhenDone(); } + thanosViews.remove(view); }); + thanosViews.add(view); } else { ObjectAnimator animator = ObjectAnimator.ofFloat(view, View.ALPHA, view.getAlpha(), 0f); dispatchRemoveStarting(holder); @@ -1497,7 +1524,9 @@ private void animateRemoveGroupImpl(final ArrayList hol } dispatchFinishedWhenDone(); } + thanosViews.removeAll(views); }); + thanosViews.add(views.get(0)); recyclerListView.stopScroll(); } diff --git a/TMessagesProj/src/main/java/androidx/recyclerview/widget/GridLayoutManagerFixed.java b/TMessagesProj/src/main/java/androidx/recyclerview/widget/GridLayoutManagerFixed.java index 265c55aad0..06b9b0c105 100644 --- a/TMessagesProj/src/main/java/androidx/recyclerview/widget/GridLayoutManagerFixed.java +++ b/TMessagesProj/src/main/java/androidx/recyclerview/widget/GridLayoutManagerFixed.java @@ -11,6 +11,10 @@ import android.graphics.Rect; import android.view.View; +import com.google.android.exoplayer2.util.Log; + +import org.telegram.ui.Cells.ChatMessageCell; + import java.util.ArrayList; import java.util.Arrays; @@ -68,8 +72,9 @@ protected void recycleViewsFromStart(RecyclerView.Recycler recycler, int scrolli } else { for (int i = 0; i < childCount; i++) { View child = getChildAt(i); - if (mOrientationHelper.getDecoratedEnd(child) > scrollingOffset - || mOrientationHelper.getTransformedEndWithDecoration(child) > scrollingOffset) { + final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + if (child.getBottom() + params.bottomMargin > scrollingOffset + || child.getTop() + child.getHeight() > scrollingOffset) { // stop here recycleChildren(recycler, 0, i); return; @@ -115,12 +120,15 @@ void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, Layou final int otherDirSpecMode = mOrientationHelper.getModeInOther(); final boolean layingOutInPrimaryDirection = layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL; - boolean working = true; result.mConsumed = 0; - int yOffset = 0; int startPosition = layoutState.mCurrentPosition; - if (layoutState.mLayoutDirection != LayoutState.LAYOUT_START && hasSiblingChild(layoutState.mCurrentPosition) && findViewByPosition(layoutState.mCurrentPosition + 1) == null) { + if ( + mShouldReverseLayout && + layoutState.mLayoutDirection != LayoutState.LAYOUT_START && + hasSiblingChild(layoutState.mCurrentPosition) && + findViewByPosition(layoutState.mCurrentPosition + 1) == null + ) { if (hasSiblingChild(layoutState.mCurrentPosition + 1)) { layoutState.mCurrentPosition += 3; } else { @@ -144,18 +152,14 @@ void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, Layou layoutState.mCurrentPosition = backupPosition; } + boolean working = true; while (working) { int count = 0; - int consumedSpanCount = 0; int remainingSpan = mSpanCount; - working = !additionalViews.isEmpty(); - int firstPositionStart = layoutState.mCurrentPosition; - while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) { int pos = layoutState.mCurrentPosition; final int spanSize = getSpanSize(recycler, state, pos); - remainingSpan -= spanSize; if (remainingSpan < 0) { break; @@ -171,7 +175,6 @@ void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, Layou if (view == null) { break; } - consumedSpanCount += spanSize; mSet[count] = view; count++; if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START && remainingSpan <= 0 && hasSiblingChild(pos)) { @@ -236,9 +239,11 @@ void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, Layou } int left, right, top, bottom; - - boolean fromOpositeSide = shouldLayoutChildFromOpositeSide(mSet[0]); - if (fromOpositeSide && layoutState.mLayoutDirection == LayoutState.LAYOUT_START || !fromOpositeSide && layoutState.mLayoutDirection == LayoutState.LAYOUT_END) { + boolean fromOppositeSide = shouldLayoutChildFromOpositeSide(mSet[0]); + if ( + fromOppositeSide && layoutState.mLayoutDirection == LayoutState.LAYOUT_START || + !fromOppositeSide && layoutState.mLayoutDirection == LayoutState.LAYOUT_END + ) { if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { bottom = layoutState.mOffset - result.mConsumed; top = bottom - maxSize; @@ -284,7 +289,7 @@ void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, Layou left -= right; } layoutDecoratedWithMargins(view, left, top, left + right, bottom); - if (layoutState.mLayoutDirection != LayoutState.LAYOUT_START) { + if (layoutState.mLayoutDirection == LayoutState.LAYOUT_END) { left += right; } if (params.isItemRemoved() || params.isItemChanged()) { diff --git a/TMessagesProj/src/main/java/org/telegram/SQLite/SQLitePreparedStatement.java b/TMessagesProj/src/main/java/org/telegram/SQLite/SQLitePreparedStatement.java index 3ada488855..40b1145a8c 100755 --- a/TMessagesProj/src/main/java/org/telegram/SQLite/SQLitePreparedStatement.java +++ b/TMessagesProj/src/main/java/org/telegram/SQLite/SQLitePreparedStatement.java @@ -10,6 +10,8 @@ import android.os.SystemClock; +import com.google.android.exoplayer2.util.Log; + import org.telegram.messenger.BuildVars; import org.telegram.messenger.FileLog; import org.telegram.tgnet.NativeByteBuffer; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java index 1b0801312d..a09a3d127d 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java @@ -153,6 +153,8 @@ import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.MotionBackgroundDrawable; import org.telegram.ui.Components.PickerBottomLayout; +import org.telegram.ui.Components.PipRoundVideoView; +import org.telegram.ui.Components.PipVideoOverlay; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.ShareAlert; import org.telegram.ui.Components.TypefaceSpan; @@ -5225,6 +5227,9 @@ public static void makeGlobalBlurBitmap(Utilities.Callback onBitmapDone, int[] location = new int[2]; for (int i = 0; i < finalViews.size(); ++i) { View view = finalViews.get(i); + if (view instanceof PipRoundVideoView.PipFrameLayout || view instanceof PipRoundVideoView.PipFrameLayout) { + continue; + } ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); if (layoutParams instanceof WindowManager.LayoutParams) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AnimatedFileDrawableStream.java b/TMessagesProj/src/main/java/org/telegram/messenger/AnimatedFileDrawableStream.java index 8e2b380079..92e45c3232 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AnimatedFileDrawableStream.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AnimatedFileDrawableStream.java @@ -27,14 +27,14 @@ public class AnimatedFileDrawableStream implements FileLoadOperationStream { private int debugCanceledCount; private boolean debugReportSend; - public AnimatedFileDrawableStream(TLRPC.Document d, ImageLocation l, Object p, int a, boolean prev, int loadingPriority) { + public AnimatedFileDrawableStream(TLRPC.Document d, ImageLocation l, Object p, int a, boolean prev, int loadingPriority, int cacheType) { document = d; location = l; parentObject = p; currentAccount = a; preview = prev; this.loadingPriority = loadingPriority; - loadOperation = FileLoader.getInstance(currentAccount).loadStreamFile(this, document, location, parentObject, 0, preview, loadingPriority); + loadOperation = FileLoader.getInstance(currentAccount).loadStreamFile(this, document, location, parentObject, 0, preview, loadingPriority, cacheType); } public boolean isFinishedLoadingFile() { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ChannelBoostsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ChannelBoostsController.java index 3a2d60869b..8a4ae41922 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ChannelBoostsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ChannelBoostsController.java @@ -5,12 +5,19 @@ import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.tl.TL_stories; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.Premium.boosts.BoostRepository; +import org.telegram.ui.LaunchActivity; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class ChannelBoostsController { @@ -27,7 +34,6 @@ public ChannelBoostsController(int currentAccount) { connectionsManager = ConnectionsManager.getInstance(currentAccount); } - public void getBoostsStats(long dialogId, Consumer consumer) { TL_stories.TL_premium_getBoostsStatus req = new TL_stories.TL_premium_getBoostsStatus(); req.peer = messagesController.getInputPeer(dialogId); @@ -35,7 +41,22 @@ public void getBoostsStats(long dialogId, Consumer colorsReplacement = new HashMap<>(); + colorsReplacement.put("info1.**", Theme.getColor(Theme.key_dialogTopBackground)); + colorsReplacement.put("info2.**", Theme.getColor(Theme.key_dialogTopBackground)); + builder.setTopAnimation(R.raw.not_available, AlertsCreator.NEW_DENY_DIALOG_TOP_ICON_SIZE, false, Theme.getColor(Theme.key_dialogTopBackground), colorsReplacement); + builder.setTopAnimationIsNew(true); + builder.setTitle(LocaleController.getString(R.string.ChannelPrivate)); + builder.setMessage(LocaleController.getString("ChannelCantOpenPrivate2", R.string.ChannelCantOpenPrivate2)); + builder.setPositiveButton(LocaleController.getString(R.string.Close), null); + builder.show(); + } else { + BulletinFactory.global().showForError(error); + } consumer.accept(null); } })); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java index d3891b2702..1113b1c703 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ChatObject.java @@ -1826,11 +1826,11 @@ public static boolean canManageTopics(TLRPC.Chat chat) { public static boolean canManageTopic(int currentAccount, TLRPC.Chat chat, TLRPC.TL_forumTopic topic) { return canManageTopics(chat) || isMyTopic(currentAccount, topic); } - public static boolean canManageTopic(int currentAccount, TLRPC.Chat chat, int topicId) { + public static boolean canManageTopic(int currentAccount, TLRPC.Chat chat, long topicId) { return canManageTopics(chat) || isMyTopic(currentAccount, chat, topicId); } - public static boolean canDeleteTopic(int currentAccount, TLRPC.Chat chat, int topicId) { + public static boolean canDeleteTopic(int currentAccount, TLRPC.Chat chat, long topicId) { if (topicId == 1) { // general topic can't be deleted return false; @@ -1849,11 +1849,11 @@ public static boolean isMyTopic(int currentAccount, TLRPC.TL_forumTopic topic) { return topic != null && (topic.my || topic.from_id instanceof TLRPC.TL_peerUser && topic.from_id.user_id == UserConfig.getInstance(currentAccount).clientUserId); } - public static boolean isMyTopic(int currentAccount, TLRPC.Chat chat, int topicId) { + public static boolean isMyTopic(int currentAccount, TLRPC.Chat chat, long topicId) { return chat != null && chat.forum && isMyTopic(currentAccount, chat.id, topicId); } - public static boolean isMyTopic(int currentAccount, long chatId, int topicId) { + public static boolean isMyTopic(int currentAccount, long chatId, long topicId) { return isMyTopic(currentAccount, MessagesController.getInstance(currentAccount).getTopicsController().findTopic(chatId, topicId)); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java index e9be416590..2c6bfd3eb7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java @@ -93,6 +93,7 @@ public class ContactsController extends BaseController { public final static int PRIVACY_RULES_TYPE_ADDED_BY_PHONE = 7; public final static int PRIVACY_RULES_TYPE_VOICE_MESSAGES = 8; public final static int PRIVACY_RULES_TYPE_BIO = 9; + public final static int PRIVACY_RULES_TYPE_MESSAGES = 10; public final static int PRIVACY_RULES_TYPE_COUNT = 10; @@ -221,6 +222,7 @@ public static String getLetter(String first_name, String last_name) { public ArrayList phoneBookContacts = new ArrayList<>(); public HashMap> phoneBookSectionsDict = new HashMap<>(); public ArrayList phoneBookSectionsArray = new ArrayList<>(); + public HashMap phoneBookByShortPhones = new HashMap<>(); public ArrayList contacts = new ArrayList<>(); public ConcurrentHashMap contactsDict = new ConcurrentHashMap<>(20, 1.0f, 2); @@ -306,6 +308,7 @@ public void cleanup() { contactsByShortPhone.clear(); phoneBookSectionsDict.clear(); phoneBookSectionsArray.clear(); + phoneBookByShortPhones.clear(); loadingContacts = false; contactsSyncInProgress = false; @@ -528,6 +531,7 @@ public void deleteAllContacts(final Runnable runnable) { sortedUsersSectionsArray.clear(); phoneBookSectionsDict.clear(); phoneBookSectionsArray.clear(); + phoneBookByShortPhones.clear(); delayedContactsUpdate.clear(); sortedUsersMutualSectionsArray.clear(); contactsByPhone.clear(); @@ -1717,7 +1721,7 @@ public void processLoadedContacts(final ArrayList contactsArr, if (from != 1 && !isEmpty) { saveContactsLoadTime(); } else { - reloadContactsStatusesMaybe(); + reloadContactsStatusesMaybe(false); } if (finalReloadContacts) { loadContacts(false, 0); @@ -1752,11 +1756,11 @@ public boolean isContact(long userId) { return contactsDict.get(userId) != null; } - public void reloadContactsStatusesMaybe() { + public void reloadContactsStatusesMaybe(boolean force) { try { SharedPreferences preferences = MessagesController.getMainSettings(currentAccount); long lastReloadStatusTime = preferences.getLong("lastReloadStatusTime", 0); - if (lastReloadStatusTime < System.currentTimeMillis() - 1000 * 60 * 60 * 3) { + if (lastReloadStatusTime < System.currentTimeMillis() - 1000 * 60 * 60 * 3 || force) { reloadContactsStatuses(); } } catch (Exception e) { @@ -1774,29 +1778,35 @@ private void saveContactsLoadTime() { } private void mergePhonebookAndTelegramContacts(final HashMap> phoneBookSectionsDictFinal, final ArrayList phoneBookSectionsArrayFinal, final HashMap phoneBookByShortPhonesFinal) { + mergePhonebookAndTelegramContacts(phoneBookSectionsDictFinal, phoneBookSectionsArrayFinal,phoneBookByShortPhonesFinal, true); + } + + private void mergePhonebookAndTelegramContacts(final HashMap> phoneBookSectionsDictFinal, final ArrayList phoneBookSectionsArrayFinal, final HashMap phoneBookByShortPhonesFinal, boolean needUpdateLists) { final ArrayList contactsCopy = new ArrayList<>(contacts); Utilities.globalQueue.postRunnable(() -> { - for (int a = 0, size = contactsCopy.size(); a < size; a++) { - TLRPC.TL_contact value = contactsCopy.get(a); - TLRPC.User user = getMessagesController().getUser(value.user_id); - if (user == null || TextUtils.isEmpty(user.phone)) { - continue; - } - String phone = user.phone.substring(Math.max(0, user.phone.length() - 7)); - Contact contact = phoneBookByShortPhonesFinal.get(phone); - if (contact != null) { - if (contact.user == null) { - contact.user = user; + if(needUpdateLists) { + for (int a = 0, size = contactsCopy.size(); a < size; a++) { + TLRPC.TL_contact value = contactsCopy.get(a); + TLRPC.User user = getMessagesController().getUser(value.user_id); + if (user == null || TextUtils.isEmpty(user.phone)) { + continue; } - } else { - String key = Contact.getLetter(user.first_name, user.last_name); - ArrayList arrayList = phoneBookSectionsDictFinal.get(key); - if (arrayList == null) { - arrayList = new ArrayList<>(); - phoneBookSectionsDictFinal.put(key, arrayList); - phoneBookSectionsArrayFinal.add(key); + String phone = user.phone.substring(Math.max(0, user.phone.length() - 7)); + Contact contact = phoneBookByShortPhonesFinal.get(phone); + if (contact != null) { + if (contact.user == null) { + contact.user = user; + } + } else { + String key = Contact.getLetter(user.first_name, user.last_name); + ArrayList arrayList = phoneBookSectionsDictFinal.get(key); + if (arrayList == null) { + arrayList = new ArrayList<>(); + phoneBookSectionsDictFinal.put(key, arrayList); + phoneBookSectionsArrayFinal.add(key); + } + arrayList.add(user); } - arrayList.add(user); } } final Collator collator = getLocaleCollator(); @@ -1847,6 +1857,7 @@ private void mergePhonebookAndTelegramContacts(final HashMap { phoneBookSectionsArray = phoneBookSectionsArrayFinal; + phoneBookByShortPhones = phoneBookByShortPhonesFinal; phoneBookSectionsDict = phoneBookSectionsDictFinal; }); }); @@ -2386,8 +2397,46 @@ public void addContact(TLRPC.User user, boolean exception) { } AndroidUtilities.runOnUIThread(() -> { + boolean needResort = false; for (int a = 0; a < res.users.size(); a++) { TLRPC.User u = res.users.get(a); + if (u.contact) { + Contact phoneBookContact = contactsBookSPhones.get(u.phone); + if (phoneBookContact != null) { + String oldKey = phoneBookContact.getLetter(); + String newKey = Contact.getLetter(user.first_name, user.last_name); + if (phoneBookContact.user == null) { + phoneBookContact.user = user; + if (!oldKey.equals(newKey)) { + ArrayList arrayList = phoneBookSectionsDict.get(newKey); + if (arrayList == null) { + arrayList = new ArrayList<>(); + phoneBookSectionsDict.put(newKey, arrayList); + phoneBookSectionsArray.add(newKey); + } + arrayList.add(phoneBookContact); + + arrayList = phoneBookSectionsDict.get(oldKey); + if (arrayList != null) { + for (Object obj : arrayList) { + if (obj instanceof Contact) { + Contact oldContact = (Contact) obj; + if (oldContact.contact_id == phoneBookContact.contact_id) { + boolean removed = arrayList.remove(oldContact); + if (removed && arrayList.isEmpty()) { + phoneBookSectionsDict.remove(oldKey); + phoneBookSectionsArray.remove(oldKey); + } + break; + } + } + } + } + } + needResort = true; + } + } + } if (!u.contact || contactsDict.get(u.id) != null) { continue; } @@ -2397,6 +2446,9 @@ public void addContact(TLRPC.User user, boolean exception) { contactsDict.put(newContact.user_id, newContact); } buildContactsSectionsArrays(true); + if (needResort) { + mergePhonebookAndTelegramContacts(phoneBookSectionsDict, phoneBookSectionsArray, phoneBookByShortPhones, false); + } getNotificationCenter().postNotificationName(NotificationCenter.contactsDidLoad); }); }, ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagCanCompress); @@ -2491,11 +2543,11 @@ private void reloadContactsStatuses() { continue; } if (status.status instanceof TLRPC.TL_userStatusRecently) { - status.status.expires = -100; + status.status.expires = status.status.by_me ? -1000 : -100; } else if (status.status instanceof TLRPC.TL_userStatusLastWeek) { - status.status.expires = -101; + status.status.expires = status.status.by_me ? -1001 : -101; } else if (status.status instanceof TLRPC.TL_userStatusLastMonth) { - status.status.expires = -102; + status.status.expires = status.status.by_me ? -1002 : -102; } TLRPC.User user = getMessagesController().getUser(status.user_id); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java index 2945860ea5..497231fe73 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DatabaseMigrationHelper.java @@ -1392,6 +1392,29 @@ public static int migrate(MessagesStorage messagesStorage, int version) throws E version = 136; } + if (version == 136) { + database.executeFast("CREATE TABLE saved_dialogs(did INTEGER PRIMARY KEY, date INTEGER, last_mid INTEGER, pinned INTEGER, flags INTEGER, folder_id INTEGER, last_mid_group INTEGER, count INTEGER)").stepThis().dispose(); + database.executeFast("CREATE INDEX IF NOT EXISTS date_idx_dialogs ON saved_dialogs(date);").stepThis().dispose(); + database.executeFast("CREATE INDEX IF NOT EXISTS last_mid_idx_dialogs ON saved_dialogs(last_mid);").stepThis().dispose(); + database.executeFast("CREATE INDEX IF NOT EXISTS folder_id_idx_dialogs ON saved_dialogs(folder_id);").stepThis().dispose(); + database.executeFast("CREATE INDEX IF NOT EXISTS flags_idx_dialogs ON saved_dialogs(flags);").stepThis().dispose(); + + database.executeFast("PRAGMA user_version = 137").stepThis().dispose(); + version = 137; + } + + if (version == 137) { + database.executeFast("ALTER TABLE unread_push_messages ADD COLUMN is_reaction INTEGER default 0").stepThis().dispose(); + database.executeFast("PRAGMA user_version = 138").stepThis().dispose(); + version = 138; + } + + if (version == 138) { + database.executeFast("CREATE TABLE IF NOT EXISTS saved_reaction_tags (data BLOB);").stepThis().dispose(); + database.executeFast("PRAGMA user_version = 139").stepThis().dispose(); + version = 139; + } + return version; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java b/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java index f19eecc69e..5b09b9333a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Emoji.java @@ -452,8 +452,10 @@ public static ArrayList parseEmojis(CharSequence cs, int[] emoji } else if (emojiCode.length() >= 2 && emojiCode.charAt(0) == 0xD83C && emojiCode.charAt(1) == 0xDFF4 && next == 0xDB40) { i++; while (true) { - emojiCode.append(cs.charAt(i)); - if (i + 1 >= cs.length()) { + if (i < cs.length()) { + emojiCode.append(cs.charAt(i)); + } + if (i + 1 < cs.length()) { emojiCode.append(cs.charAt(i + 1)); } startLength += 2; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java index 0ba4d9047d..0fe68a6fa1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java @@ -1077,10 +1077,14 @@ private void loadFile(final TLRPC.Document document, final SecureDocument secure } protected FileLoadOperation loadStreamFile(final FileLoadOperationStream stream, final TLRPC.Document document, final ImageLocation location, final Object parentObject, final long offset, final boolean priority, int loadingPriority) { + return loadStreamFile(stream, document, location, parentObject, offset, priority, loadingPriority, document == null ? 1 : 0); + } + + protected FileLoadOperation loadStreamFile(final FileLoadOperationStream stream, final TLRPC.Document document, final ImageLocation location, final Object parentObject, final long offset, final boolean priority, int loadingPriority, int cacheType) { final CountDownLatch semaphore = new CountDownLatch(1); final FileLoadOperation[] result = new FileLoadOperation[1]; fileLoaderQueue.postRunnable(() -> { - result[0] = loadFileInternal(document, null, null, document == null && location != null ? location.location : null, location, parentObject, document == null && location != null ? "mp4" : null, document == null && location != null ? location.currentSize : 0, loadingPriority, stream, offset, priority, document == null ? 1 : 0); + result[0] = loadFileInternal(document, null, null, document == null && location != null ? location.location : null, location, parentObject, document == null && location != null ? "mp4" : null, document == null && location != null ? location.currentSize : 0, loadingPriority, stream, offset, priority, cacheType); semaphore.countDown(); }); awaitFileLoadOperation(semaphore, true); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java index 32682a310e..63cd043c9b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java @@ -1081,13 +1081,13 @@ private boolean onRequestComplete(String locationKey, String parentKey, TLObject TL_stories.StoryItem storyItem = stories.stories.get(0); if (storyItem.media != null) { newStoryItem = storyItem; - if (storyItem.media.photo != null) { + if (result == null && storyItem.media.photo != null) { result = getFileReference(storyItem.media.photo, requester.location, needReplacement, locationReplacement); } - if (storyItem.media.document != null) { + if (result == null && storyItem.media.document != null) { result = getFileReference(storyItem.media.document, requester.location, needReplacement, locationReplacement); } - if (storyItem.media.alt_document != null) { + if (result == null && storyItem.media.alt_document != null) { result = getFileReference(storyItem.media.alt_document, requester.location, needReplacement, locationReplacement); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java index 653409b0b3..04c2b17c9a 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java @@ -1115,7 +1115,12 @@ public void run() { } } boolean createDecoder = fistFrame || (cacheImage.filter != null && ("d".equals(cacheImage.filter) || cacheImage.filter.contains("_d"))); - fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, createDecoder, 0, cacheImage.priority, notCreateStream ? null : cacheImage.imageLocation.document, null, null, seekTo, cacheImage.currentAccount, false, w, h, cacheOptions); + TLRPC.Document document = notCreateStream ? null : cacheImage.imageLocation.document; + int cacheType = document != null ? 1 : 0; + if (cacheImage.cacheType > 1) { + cacheType = cacheImage.cacheType; + } + fileDrawable = new AnimatedFileDrawable(cacheImage.finalFilePath, createDecoder, 0, cacheImage.priority, notCreateStream ? null : cacheImage.imageLocation.document, null, null, seekTo, cacheImage.currentAccount, false, w, h, cacheOptions, cacheType); fileDrawable.setIsWebmSticker(MessageObject.isWebM(cacheImage.imageLocation.document) || MessageObject.isVideoSticker(cacheImage.imageLocation.document) || isAnimatedAvatar(cacheImage.filter)); } if (fistFrame) { @@ -1229,7 +1234,9 @@ public void run() { w_filter = Float.parseFloat(args[0]) * AndroidUtilities.density; h_filter = Float.parseFloat(args[1]) * AndroidUtilities.density; } - if (cacheImage.filter.contains("b2")) { + if (cacheImage.filter.contains("b2r")) { + blurType = 4; + } else if (cacheImage.filter.contains("b2")) { blurType = 3; } else if (cacheImage.filter.contains("b1")) { blurType = 2; @@ -1430,8 +1437,23 @@ public void run() { if (image.getConfig() == Bitmap.Config.ARGB_8888) { Utilities.blurBitmap(image, 1, opts.inPurgeable ? 0 : 1, image.getWidth(), image.getHeight(), image.getRowBytes()); } - } else if (blurType == 3) { + } else if (blurType == 3 || blurType == 4) { if (image.getConfig() == Bitmap.Config.ARGB_8888) { + if (blurType == 4) { + Bitmap nbitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), image.getConfig()); + Canvas canvas = new Canvas(nbitmap); + canvas.save(); + final float s = 1.2f; + canvas.scale(s, s, image.getWidth() / 2f, image.getHeight() / 2f); + canvas.drawBitmap(image, 0, 0, null); + canvas.restore(); + android.graphics.Path path = new android.graphics.Path(); + path.addCircle(image.getWidth() / 2f, image.getHeight() / 2f, Math.min(image.getWidth(), image.getHeight()) / 2f, android.graphics.Path.Direction.CW); + canvas.clipPath(path); + canvas.drawBitmap(image, 0, 0, null); + image.recycle(); + image = nbitmap; + } Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1, image.getWidth(), image.getHeight(), image.getRowBytes()); Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1, image.getWidth(), image.getHeight(), image.getRowBytes()); Utilities.blurBitmap(image, 7, opts.inPurgeable ? 0 : 1, image.getWidth(), image.getHeight(), image.getRowBytes()); @@ -1829,8 +1851,24 @@ public static Bitmap getStrippedPhotoBitmap(byte[] photoBytes, String filter) { data[166] = photoBytes[2]; BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = SharedConfig.deviceIsHigh() ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + final boolean isRound = !TextUtils.isEmpty(filter) && filter.contains("r"); + options.inPreferredConfig = SharedConfig.deviceIsHigh() || isRound ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, len, options); + if (isRound) { + Bitmap nbitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig()); + Canvas canvas = new Canvas(nbitmap); + canvas.save(); + final float s = 1.2f; + canvas.scale(s, s, bitmap.getWidth() / 2f, bitmap.getHeight() / 2f); + canvas.drawBitmap(bitmap, 0, 0, null); + canvas.restore(); + android.graphics.Path path = new android.graphics.Path(); + path.addCircle(bitmap.getWidth() / 2f, bitmap.getHeight() / 2f, Math.min(bitmap.getWidth(), bitmap.getHeight()) / 2f, android.graphics.Path.Direction.CW); + canvas.clipPath(path); + canvas.drawBitmap(bitmap, 0, 0, null); + bitmap.recycle(); + bitmap = nbitmap; + } if (bitmap != null && !TextUtils.isEmpty(filter) && filter.contains("b")) { Utilities.blurBitmap(bitmap, 3, 1, bitmap.getWidth(), bitmap.getHeight(), bitmap.getRowBytes()); } @@ -1852,6 +1890,7 @@ private class CacheImage { protected long size; protected int imageType; protected int type; + protected int cacheType; protected int currentAccount; @@ -2274,6 +2313,7 @@ public void fileLoadProgressChanged(FileLoadOperation operation, final String lo cacheImage.cacheTask = new CacheOutTask(cacheImage); cacheImage.filter = filter; cacheImage.imageType = img.imageType; + cacheImage.cacheType = img.cacheType; imageLoadingByKeys.put(key, cacheImage); tasks.add(cacheImage.cacheTask); } @@ -3212,6 +3252,7 @@ private void createLoadOperationForImageReceiver(final ImageReceiver imageReceiv img.type = type; img.key = key; + img.cacheType = cacheType; img.filter = filter; img.imageLocation = imageLocation; img.ext = ext; @@ -3727,6 +3768,7 @@ private void fileDidLoaded(final String location, final File finalFile, final in cacheImage.parentObject = img.parentObject; cacheImage.isPFrame = img.isPFrame; cacheImage.key = key; + cacheImage.cacheType = img.cacheType; cacheImage.imageLocation = img.imageLocation; cacheImage.type = type; cacheImage.ext = img.ext; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java b/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java index 50c45f8c01..e5529442f5 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/LocaleController.java @@ -2212,11 +2212,11 @@ public static String formatUserStatus(int currentAccount, TLRPC.User user, boole public static String formatUserStatus(int currentAccount, TLRPC.User user, boolean[] isOnline, boolean[] madeShorter) { if (user != null && user.status != null && user.status.expires == 0) { if (user.status instanceof TLRPC.TL_userStatusRecently) { - user.status.expires = -100; + user.status.expires = user.status.by_me ? -1000 : -100; } else if (user.status instanceof TLRPC.TL_userStatusLastWeek) { - user.status.expires = -101; + user.status.expires = user.status.by_me ? -1001 : -101; } else if (user.status instanceof TLRPC.TL_userStatusLastMonth) { - user.status.expires = -102; + user.status.expires = user.status.by_me ? -1002 : -102; } } if (user != null && user.status != null && user.status.expires <= 0) { @@ -2239,11 +2239,11 @@ public static String formatUserStatus(int currentAccount, TLRPC.User user, boole } else { if (user.status.expires == -1) { return getString("Invisible", R.string.Invisible); - } else if (user.status.expires == -100) { + } else if (user.status.expires == -100 || user.status.expires == -1000) { return getString("Lately", R.string.Lately); - } else if (user.status.expires == -101) { + } else if (user.status.expires == -101 || user.status.expires == -1001) { return getString("WithinAWeek", R.string.WithinAWeek); - } else if (user.status.expires == -102) { + } else if (user.status.expires == -102 || user.status.expires == -1002) { return getString("WithinAMonth", R.string.WithinAMonth); } else { return formatDateOnline(user.status.expires, madeShorter); @@ -2616,7 +2616,7 @@ public String getTranslitString(String src, boolean ru, boolean onlyEnglish) { } if (translitChars == null) { - translitChars = new HashMap<>(487); + translitChars = new HashMap<>(488); translitChars.put("ȼ", "c"); translitChars.put("ᶇ", "n"); translitChars.put("ɖ", "d"); @@ -3103,6 +3103,8 @@ public String getTranslitString(String src, boolean ru, boolean onlyEnglish) { translitChars.put("ő", "o"); translitChars.put("ꜩ", "tz"); translitChars.put("ẻ", "e"); + translitChars.put("і", "i"); + translitChars.put("ї", "i"); } StringBuilder dst = new StringBuilder(src.length()); int len = src.length(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java index 5911e37046..6bf995be45 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java @@ -54,6 +54,7 @@ import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.text.TextUtils; +import android.util.Log; import android.util.Pair; import android.util.SparseArray; import android.view.HapticFeedbackConstants; @@ -121,8 +122,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener, public native byte[] getWaveform2(short[] array, int length); - public static native byte[] getMp3Waveform(String path, int samplesCount); - public boolean isBuffering() { if (audioPlayer != null) { return audioPlayer.isBuffering(); @@ -754,6 +753,7 @@ public void run() { } }; + private boolean audioRecorderPaused; private AudioRecord audioRecorder; private TLRPC.TL_document recordingAudio; private int recordingGuid = -1; @@ -867,7 +867,7 @@ public void run() { AndroidUtilities.runOnUIThread(() -> NotificationCenter.getInstance(recordingCurrentAccount).postNotificationName(NotificationCenter.recordProgressChanged, recordingGuid, amplitude)); } else { recordBuffers.add(buffer); - if (sendAfterDone != 3) { + if (sendAfterDone != 3 && sendAfterDone != 4) { stopRecordingInternal(sendAfterDone, sendAfterDoneNotify, sendAfterDoneScheduleDate, sendAfterDoneOnce); } } @@ -1860,16 +1860,16 @@ public void onSensorChanged(SensorEvent event) { } } if (proximityTouched && wakelockAllowed) { - if (allowRecording && recordStartRunnable == null && recordingAudio == null) { + if (allowRecording && recordStartRunnable == null) { if (!raiseToEarRecord) { if (BuildVars.LOGS_ENABLED) { FileLog.d("start record"); } useFrontSpeaker = true; - if (!raiseChat.playFirstUnreadVoiceMessage()) { + if (recordingAudio != null || !raiseChat.playFirstUnreadVoiceMessage()) { raiseToEarRecord = true; useFrontSpeaker = false; - startRecording(raiseChat.getCurrentAccount(), raiseChat.getDialogId(), null, raiseChat.getThreadMessage(), null, raiseChat.getClassGuid(), false); + raiseToSpeakUpdated(true); } if (useFrontSpeaker) { setUseFrontSpeaker(true); @@ -1915,7 +1915,7 @@ public void onSensorChanged(SensorEvent event) { if (BuildVars.LOGS_ENABLED) { FileLog.d("stop record"); } - stopRecording(2, false, 0, false); + raiseToSpeakUpdated(false); raiseToEarRecord = false; ignoreOnPause = false; // if (!ignoreAccelerometerGestures() && proximityHasDifferentValues && proximityWakeLock != null && proximityWakeLock.isHeld()) { @@ -1942,6 +1942,16 @@ public void onSensorChanged(SensorEvent event) { } } + private void raiseToSpeakUpdated(boolean raised) { + if (recordingAudio != null) { + toggleRecordingPause(); + } else if (raised) { + startRecording(raiseChat.getCurrentAccount(), raiseChat.getDialogId(), null, raiseChat.getThreadMessage(), null, raiseChat.getClassGuid(), false); + } else { + stopRecording(2, false, 0, false); + } + } + private void setUseFrontSpeaker(boolean value) { useFrontSpeaker = value; AudioManager audioManager = NotificationsController.audioManager; @@ -2633,7 +2643,7 @@ private void checkIsNextVoiceFileDownloaded(int currentAccount) { final File cacheFile = file != null ? file : FileLoader.getInstance(currentAccount).getPathToMessage(nextAudio.messageOwner); boolean exist = cacheFile.exists(); if (cacheFile != file && !cacheFile.exists()) { - FileLoader.getInstance(currentAccount).loadFile(nextAudio.getDocument(), nextAudio, FileLoader.PRIORITY_LOW, 0); + FileLoader.getInstance(currentAccount).loadFile(nextAudio.getDocument(), nextAudio, FileLoader.PRIORITY_LOW, nextAudio.shouldEncryptPhotoOrVideo() ? 2 : 0); } } @@ -2672,7 +2682,7 @@ private void checkIsNextMusicFileDownloaded(int currentAccount) { final File cacheFile = file != null ? file : FileLoader.getInstance(currentAccount).getPathToMessage(nextAudio.messageOwner); boolean exist = cacheFile.exists(); if (cacheFile != file && !cacheFile.exists() && nextAudio.isMusic()) { - FileLoader.getInstance(currentAccount).loadFile(nextAudio.getDocument(), nextAudio, FileLoader.PRIORITY_LOW, 0); + FileLoader.getInstance(currentAccount).loadFile(nextAudio.getDocument(), nextAudio, FileLoader.PRIORITY_LOW, nextAudio.shouldEncryptPhotoOrVideo() ? 2 : 0); } } @@ -3210,9 +3220,9 @@ public boolean playMessage(final MessageObject messageObject, boolean silent) { } } final File cacheFile = file != null ? file : FileLoader.getInstance(messageObject.currentAccount).getPathToMessage(messageObject.messageOwner); - boolean canStream = SharedConfig.streamMedia && (messageObject.isMusic() || messageObject.isRoundVideo() || messageObject.isVideo() && messageObject.canStreamVideo()) && !DialogObject.isEncryptedDialog(messageObject.getDialogId()); + boolean canStream = SharedConfig.streamMedia && (messageObject.isMusic() || messageObject.isRoundVideo() || messageObject.isVideo() && messageObject.canStreamVideo()) && !messageObject.shouldEncryptPhotoOrVideo() && !DialogObject.isEncryptedDialog(messageObject.getDialogId()); if (cacheFile != file && !(exists = cacheFile.exists()) && !canStream) { - FileLoader.getInstance(messageObject.currentAccount).loadFile(messageObject.getDocument(), messageObject, FileLoader.PRIORITY_LOW, 0); + FileLoader.getInstance(messageObject.currentAccount).loadFile(messageObject.getDocument(), messageObject, FileLoader.PRIORITY_LOW, messageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); downloadingCurrentMessage = true; isPaused = false; lastProgress = 0; @@ -3814,6 +3824,55 @@ public void requestAudioFocus(boolean request) { } } + public void toggleRecordingPause() { + recordQueue.postRunnable(() -> { + if (recordingAudio == null || recordingAudioFile == null) { + return; + } + audioRecorderPaused = !audioRecorderPaused; + final boolean isPaused = audioRecorderPaused; + if (isPaused) { + sendAfterDone = 4; + audioRecorder.stop(); + audioRecorder.release(); + audioRecorder = null; + + final TLRPC.TL_document audioToSend = recordingAudio; + final File recordingAudioFileToSend = recordingAudioFile; + AndroidUtilities.runOnUIThread(() -> { + boolean fileExist = recordingAudioFileToSend.exists(); + if (!fileExist && BuildVars.DEBUG_VERSION) { + FileLog.e(new RuntimeException("file not found :( recordTimeCount " + recordTimeCount + " writedFrames" + writedFrame)); + } + audioToSend.date = ConnectionsManager.getInstance(recordingCurrentAccount).getCurrentTime(); + audioToSend.size = (int) recordingAudioFileToSend.length(); + TLRPC.TL_documentAttributeAudio attributeAudio = new TLRPC.TL_documentAttributeAudio(); + attributeAudio.voice = true; + attributeAudio.waveform = getWaveform2(recordSamples, recordSamples.length); + if (attributeAudio.waveform != null) { + attributeAudio.flags |= 4; + } + attributeAudio.duration = recordTimeCount / 1000.0; + audioToSend.attributes.clear(); + audioToSend.attributes.add(attributeAudio); + NotificationCenter.getInstance(recordingCurrentAccount).postNotificationName(NotificationCenter.recordPaused); + NotificationCenter.getInstance(recordingCurrentAccount).postNotificationName(NotificationCenter.audioDidSent, recordingGuid, audioToSend, recordingAudioFileToSend.getAbsolutePath()); + }); + } else { + recordQueue.cancelRunnable(recordRunnable); + audioRecorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, recordBufferSize); + recordStartTime = System.currentTimeMillis(); + fileBuffer.rewind(); + audioRecorder.startRecording(); + recordQueue.postRunnable(recordRunnable); + + AndroidUtilities.runOnUIThread(() -> { + NotificationCenter.getInstance(recordingCurrentAccount).postNotificationName(NotificationCenter.recordResumed); + }); + } + }); + } + public void startRecording(int currentAccount, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem replyStory, int guid, boolean manual) { boolean paused = false; if (playingMessageObject != null && isPlayingMessage(playingMessageObject) && !isMessagePaused()) { @@ -3877,6 +3936,7 @@ public boolean delete() { return; } + audioRecorderPaused = false; audioRecorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, recordBufferSize); recordStartTime = System.currentTimeMillis(); recordTimeCount = 0; @@ -3951,7 +4011,7 @@ public void generateWaveform(MessageObject messageObject) { } TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages(); messagesRes.messages.add(messageObject1.messageOwner); - MessagesStorage.getInstance(messageObject1.currentAccount).putMessages(messagesRes, messageObject1.getDialogId(), -1, 0, false, messageObject.scheduled, 0); + MessagesStorage.getInstance(messageObject1.currentAccount).putMessages(messagesRes, messageObject1.getDialogId(), -1, 0, false, messageObject.scheduled ? 1 : 0, 0); ArrayList arrayList = new ArrayList<>(); arrayList.add(messageObject1); NotificationCenter.getInstance(messageObject1.currentAccount).postNotificationName(NotificationCenter.replaceMessagesObjects, messageObject1.getDialogId(), arrayList); @@ -3989,7 +4049,8 @@ private void stopRecordingInternal(final int send, boolean notify, int scheduleD attributeAudio.flags |= 4; } long duration = recordTimeCount; - attributeAudio.duration = (int) (recordTimeCount / 1000); + attributeAudio.duration = recordTimeCount / 1000.0; + audioToSend.attributes.clear(); audioToSend.attributes.add(attributeAudio); if (duration > 700) { if (send == 1) { @@ -4024,6 +4085,8 @@ private void stopRecordingInternal(final int send, boolean notify, int scheduleD recordingAudio = null; recordingAudioFile = null; manualRecording = false; + raiseToEarRecord = false; + ignoreOnPause = false; } public void stopRecording(final int send, boolean notify, int scheduleDate, boolean once) { @@ -4038,6 +4101,10 @@ public void stopRecording(final int send, boolean notify, int scheduleDate, bool return; } if (audioRecorder == null) { + recordingAudio = null; + manualRecording = false; + raiseToEarRecord = false; + ignoreOnPause = false; return; } try { @@ -4236,7 +4303,7 @@ private void addMessageToLoad(MessageObject messageObject) { } String fileName = FileLoader.getAttachFileName(document); loadingMessageObjects.put(fileName, messageObject); - currentAccount.getFileLoader().loadFile(document, messageObject, FileLoader.PRIORITY_LOW, 0); + currentAccount.getFileLoader().loadFile(document, messageObject, FileLoader.PRIORITY_LOW, messageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); }); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java index 7b597bcab0..0db5b6b0b6 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java @@ -34,7 +34,6 @@ import android.text.style.CharacterStyle; import android.text.style.URLSpan; import android.text.util.Linkify; -import android.util.Log; import android.util.Pair; import android.util.SparseArray; @@ -68,6 +67,7 @@ import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.ChatThemeBottomSheet; import org.telegram.ui.Components.QuoteSpan; +import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.StickerSetBulletinLayout; import org.telegram.ui.Components.StickersArchiveAlert; import org.telegram.ui.Components.TextStyleSpan; @@ -157,9 +157,9 @@ public MediaDataController(int num) { TLRPC.Message message = TLRPC.Message.TLdeserialize(serializedData, serializedData.readInt32(true), true); if (message != null) { message.readAttachPath(serializedData, getUserConfig().clientUserId); - SparseArray threads = draftMessages.get(did); + LongSparseArray threads = draftMessages.get(did); if (threads == null) { - threads = new SparseArray<>(); + threads = new LongSparseArray<>(); draftMessages.put(did, threads); } int threadId = isThread ? Utilities.parseInt(key.substring(key.lastIndexOf('_') + 1)) : 0; @@ -171,12 +171,12 @@ public MediaDataController(int num) { } else { TLRPC.DraftMessage draftMessage = TLRPC.DraftMessage.TLdeserialize(serializedData, serializedData.readInt32(true), true); if (draftMessage != null) { - SparseArray threads = drafts.get(did); + LongSparseArray threads = drafts.get(did); if (threads == null) { - threads = new SparseArray<>(); + threads = new LongSparseArray<>(); drafts.put(did, threads); } - int threadId = key.startsWith("t_") ? Utilities.parseInt(key.substring(key.lastIndexOf('_') + 1)) : 0; + long threadId = key.startsWith("t_") ? Utilities.parseInt(key.substring(key.lastIndexOf('_') + 1)) : 0; threads.put(threadId, draftMessage); } } @@ -2189,9 +2189,9 @@ private long calcFeaturedStickersHash(boolean emoji, ArrayList> 21; + hash ^= hash >>> 21; hash ^= hash << 35; - hash ^= hash >> 4; + hash ^= hash >>> 4; return hash + id; } @@ -3337,8 +3337,9 @@ public void processStickerSetInstallResultArchive(BaseFragment baseFragment, boo private int reqId; private int mergeReqId; private long lastMergeDialogId; - private int lastReplyMessageId; + private long lastReplyMessageId; private long lastDialogId; + private ReactionsLayoutInBubble.VisibleReaction lastReaction; private int lastReqId; private int lastGuid; private TLRPC.User lastSearchUser; @@ -3374,8 +3375,8 @@ public boolean isMessageFound(int messageId, boolean mergeDialog) { return searchResultMessagesMap[mergeDialog ? 1 : 0].indexOfKey(messageId) >= 0; } - public void searchMessagesInChat(String query, long dialogId, long mergeDialogId, int guid, int direction, int replyMessageId, TLRPC.User user, TLRPC.Chat chat) { - searchMessagesInChat(query, dialogId, mergeDialogId, guid, direction, replyMessageId, false, user, chat, true); + public void searchMessagesInChat(String query, long dialogId, long mergeDialogId, int guid, int direction, long replyMessageId, TLRPC.User user, TLRPC.Chat chat, ReactionsLayoutInBubble.VisibleReaction reaction) { + searchMessagesInChat(query, dialogId, mergeDialogId, guid, direction, replyMessageId, false, user, chat, true, reaction); } public void jumpToSearchedMessage(int guid, int index) { @@ -3387,18 +3388,22 @@ public void jumpToSearchedMessage(int guid, int index) { getNotificationCenter().postNotificationName(NotificationCenter.chatSearchResultsAvailable, guid, messageObject.getId(), getMask(), messageObject.getDialogId(), lastReturnedNum, messagesSearchCount[0] + messagesSearchCount[1], true); } + public boolean searchEndReached() { + return messagesSearchEndReached[0] && lastMergeDialogId == 0 && messagesSearchEndReached[1]; + } + public void loadMoreSearchMessages() { if (loadingMoreSearchMessages || messagesSearchEndReached[0] && lastMergeDialogId == 0 && messagesSearchEndReached[1]) { return; } int temp = searchResultMessages.size(); lastReturnedNum = searchResultMessages.size(); - searchMessagesInChat(null, lastDialogId, lastMergeDialogId, lastGuid, 1, lastReplyMessageId, false, lastSearchUser, lastSearchChat, false); + searchMessagesInChat(null, lastDialogId, lastMergeDialogId, lastGuid, 1, lastReplyMessageId, false, lastSearchUser, lastSearchChat, false, lastReaction); lastReturnedNum = temp; loadingMoreSearchMessages = true; } - private void searchMessagesInChat(String query, long dialogId, long mergeDialogId, int guid, int direction, int replyMessageId, boolean internal, TLRPC.User user, TLRPC.Chat chat, boolean jumpToMessage) { + private void searchMessagesInChat(String query, long dialogId, long mergeDialogId, int guid, int direction, long replyMessageId, boolean internal, TLRPC.User user, TLRPC.Chat chat, boolean jumpToMessage, ReactionsLayoutInBubble.VisibleReaction reaction) { int max_id = 0; long queryWithDialog = dialogId; boolean firstQuery = !internal; @@ -3484,8 +3489,17 @@ private void searchMessagesInChat(String query, long dialogId, long mergeDialogI req.flags |= 1; } if (replyMessageId != 0) { - req.top_msg_id = replyMessageId; - req.flags |= 2; + if (dialogId == getUserConfig().getClientUserId()) { + req.saved_peer_id = getMessagesController().getInputPeer(replyMessageId); + req.flags |= 4; + } else { + req.top_msg_id = (int) replyMessageId; + req.flags |= 2; + } + } + if (reaction != null) { + req.saved_reaction.add(reaction.toTLReaction()); + req.flags |= 8; } req.filter = new TLRPC.TL_inputMessagesFilterEmpty(); mergeReqId = getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { @@ -3495,11 +3509,11 @@ private void searchMessagesInChat(String query, long dialogId, long mergeDialogI TLRPC.messages_Messages res = (TLRPC.messages_Messages) response; messagesSearchEndReached[1] = res.messages.isEmpty(); messagesSearchCount[1] = res instanceof TLRPC.TL_messages_messagesSlice ? res.count : res.messages.size(); - searchMessagesInChat(req.q, dialogId, mergeDialogId, guid, direction, replyMessageId, true, user, chat, jumpToMessage); + searchMessagesInChat(req.q, dialogId, mergeDialogId, guid, direction, replyMessageId, true, user, chat, jumpToMessage, reaction); } else { messagesSearchEndReached[1] = true; messagesSearchCount[1] = 0; - searchMessagesInChat(req.q, dialogId, mergeDialogId, guid, direction, replyMessageId, true, user, chat, jumpToMessage); + searchMessagesInChat(req.q, dialogId, mergeDialogId, guid, direction, replyMessageId, true, user, chat, jumpToMessage, reaction); } } }), ConnectionsManager.RequestFlagFailOnServerErrors); @@ -3520,6 +3534,7 @@ private void searchMessagesInChat(String query, long dialogId, long mergeDialogI lastSearchUser = user; lastSearchChat = chat; lastReplyMessageId = replyMessageId; + lastReaction = reaction; req.limit = 21; req.q = query != null ? query : ""; req.offset_id = max_id; @@ -3531,8 +3546,17 @@ private void searchMessagesInChat(String query, long dialogId, long mergeDialogI req.flags |= 1; } if (lastReplyMessageId != 0) { - req.top_msg_id = lastReplyMessageId; - req.flags |= 2; + if (queryWithDialog == getUserConfig().getClientUserId()) { + req.saved_peer_id = getMessagesController().getInputPeer(lastReplyMessageId); + req.flags |= 4; + } else { + req.top_msg_id = (int) lastReplyMessageId; + req.flags |= 2; + } + } + if (reaction != null) { + req.saved_reaction.add(reaction.toTLReaction()); + req.flags |= 8; } req.filter = new TLRPC.TL_inputMessagesFilterEmpty(); int currentReqId = ++lastReqId; @@ -3587,7 +3611,7 @@ private void searchMessagesInChat(String query, long dialogId, long mergeDialogI searchResultMessages.add(messageObject); searchResultMessagesMap[queryWithDialogFinal == dialogId ? 0 : 1].put(messageObject.getId(), messageObject); } - messagesSearchEndReached[queryWithDialogFinal == dialogId ? 0 : 1] = res.messages.size() < 21; + messagesSearchEndReached[queryWithDialogFinal == dialogId ? 0 : 1] = res.messages.size() < req.limit; messagesSearchCount[queryWithDialogFinal == dialogId ? 0 : 1] = res instanceof TLRPC.TL_messages_messagesSlice || res instanceof TLRPC.TL_messages_channelMessages ? res.count : res.messages.size(); if (searchResultMessages.isEmpty()) { getNotificationCenter().postNotificationName(NotificationCenter.chatSearchResultsAvailable, guid, 0, getMask(), (long) 0, 0, 0, jumpToMessage); @@ -3601,7 +3625,7 @@ private void searchMessagesInChat(String query, long dialogId, long mergeDialogI } } if (queryWithDialogFinal == dialogId && messagesSearchEndReached[0] && mergeDialogId != 0 && !messagesSearchEndReached[1]) { - searchMessagesInChat(lastSearchQuery, dialogId, mergeDialogId, guid, 0, replyMessageId, true, user, chat, jumpToMessage); + searchMessagesInChat(lastSearchQuery, dialogId, mergeDialogId, guid, 0, replyMessageId, true, user, chat, jumpToMessage, lastReaction); } } } @@ -3627,7 +3651,7 @@ public String getLastSearchQuery() { public final static int MEDIA_STORIES = 8; - public void loadMedia(long dialogId, int count, int max_id, int min_id, int type, int topicId, int fromCache, int classGuid, int requestIndex) { + public void loadMedia(long dialogId, int count, int max_id, int min_id, int type, long topicId, int fromCache, int classGuid, int requestIndex) { boolean isChannel = DialogObject.isChatDialog(dialogId) && ChatObject.isChannel(-dialogId, currentAccount); if (BuildVars.LOGS_ENABLED) { @@ -3665,8 +3689,13 @@ public void loadMedia(long dialogId, int count, int max_id, int min_id, int type req.q = ""; req.peer = getMessagesController().getInputPeer(dialogId); if (topicId != 0) { - req.top_msg_id = topicId; - req.flags |= 2; + if (dialogId == getUserConfig().getClientUserId()) { + req.saved_peer_id = getMessagesController().getInputPeer(topicId); + req.flags |= 4; + } else { + req.top_msg_id = (int) topicId; + req.flags |= 2; + } } if (req.peer == null) { return; @@ -3689,7 +3718,7 @@ public void loadMedia(long dialogId, int count, int max_id, int min_id, int type } } - public void getMediaCounts(long dialogId, int topicId, int classGuid) { + public void getMediaCounts(long dialogId, long topicId, int classGuid) { getMessagesStorage().getStorageQueue().postRunnable(() -> { try { int[] counts = new int[]{-1, -1, -1, -1, -1, -1, -1, -1, -1}; @@ -3728,8 +3757,13 @@ public void getMediaCounts(long dialogId, int topicId, int classGuid) { TLRPC.TL_messages_getSearchCounters req = new TLRPC.TL_messages_getSearchCounters(); req.peer = getMessagesController().getInputPeer(dialogId); if (topicId != 0) { - req.top_msg_id = topicId; - req.flags |= 1; + if (dialogId == getUserConfig().getClientUserId()) { + req.saved_peer_id = getMessagesController().getInputPeer(topicId); + req.flags |= 4; + } else { + req.top_msg_id = (int) topicId; + req.flags |= 1; + } } for (int a = 0; a < counts.length; a++) { if (req.peer == null) { @@ -3810,7 +3844,7 @@ public void getMediaCounts(long dialogId, int topicId, int classGuid) { }); } - public void getMediaCount(long dialogId, int topicId, int type, int classGuid, boolean fromCache) { + public void getMediaCount(long dialogId, long topicId, int type, int classGuid, boolean fromCache) { if (fromCache || DialogObject.isEncryptedDialog(dialogId)) { getMediaCountDatabase(dialogId, topicId, type, classGuid); } else { @@ -3829,8 +3863,13 @@ public void getMediaCount(long dialogId, int topicId, int type, int classGuid, b req.filters.add(new TLRPC.TL_inputMessagesFilterGif()); } if (topicId != 0) { - req.top_msg_id = topicId; - req.flags |= 1; + if (dialogId == getUserConfig().getClientUserId()) { + req.saved_peer_id = getMessagesController().getInputPeer(dialogId); + req.flags |= 4; + } else { + req.top_msg_id = (int) topicId; + req.flags |= 1; + } } req.peer = getMessagesController().getInputPeer(dialogId); if (req.peer == null) { @@ -3916,7 +3955,7 @@ public static boolean canAddMessageToMedia(TLRPC.Message message) { } } - private void processLoadedMedia(TLRPC.messages_Messages res, long dialogId, int count, int max_id, int min_id, int type, int topicId, int fromCache, int classGuid, boolean isChannel, boolean topReached, int requestIndex) { + private void processLoadedMedia(TLRPC.messages_Messages res, long dialogId, int count, int max_id, int min_id, int type, long topicId, int fromCache, int classGuid, boolean isChannel, boolean topReached, int requestIndex) { if (BuildVars.LOGS_ENABLED) { int messagesCount = 0; if (res != null && res.messages != null) { @@ -3978,7 +4017,7 @@ private void processLoadedMedia(TLRPC.messages_Messages res, long dialogId, int } } - private void processLoadedMediaCount(int count, long dialogId, int topicId, int type, int classGuid, boolean fromCache, int old) { + private void processLoadedMediaCount(int count, long dialogId, long topicId, int type, int classGuid, boolean fromCache, int old) { AndroidUtilities.runOnUIThread(() -> { boolean isEncryptedDialog = DialogObject.isEncryptedDialog(dialogId); boolean reload = fromCache && (count == -1 || count == 0 && type == 2) && !isEncryptedDialog; @@ -3994,7 +4033,7 @@ private void processLoadedMediaCount(int count, long dialogId, int topicId, int }); } - private void putMediaCountDatabase(long uid, int topicId, int type, int count) { + private void putMediaCountDatabase(long uid, long topicId, int type, int count) { getMessagesStorage().getStorageQueue().postRunnable(() -> { try { SQLitePreparedStatement state2; @@ -4007,7 +4046,7 @@ private void putMediaCountDatabase(long uid, int topicId, int type, int count) { state2.requery(); state2.bindLong(pointer++, uid); if (topicId != 0) { - state2.bindInteger(pointer++, topicId); + state2.bindLong(pointer++, topicId); } state2.bindInteger(pointer++, type); state2.bindInteger(pointer++, count); @@ -4020,7 +4059,7 @@ private void putMediaCountDatabase(long uid, int topicId, int type, int count) { }); } - private void getMediaCountDatabase(long dialogId, int topicId, int type, int classGuid) { + private void getMediaCountDatabase(long dialogId, long topicId, int type, int classGuid) { getMessagesStorage().getStorageQueue().postRunnable(() -> { try { int count = -1; @@ -4054,7 +4093,7 @@ private void getMediaCountDatabase(long dialogId, int topicId, int type, int cla }); } - private void loadMediaDatabase(long uid, int count, int max_id, int min_id, int type, int topicId, int classGuid, boolean isChannel, int fromCache, int requestIndex) { + private void loadMediaDatabase(long uid, int count, int max_id, int min_id, int type, long topicId, int classGuid, boolean isChannel, int fromCache, int requestIndex) { Runnable runnable = new Runnable() { @Override public void run() { @@ -4099,7 +4138,7 @@ public void run() { state.requery(); state.bindLong(pointer++, uid); if (topicId != 0) { - state.bindInteger(pointer++, topicId); + state.bindLong(pointer++, topicId); } state.bindInteger(pointer++, type); state.bindInteger(pointer++, 0); @@ -4269,7 +4308,7 @@ public void run() { messagesStorage.bindTaskToGuid(runnable, classGuid); } - private void putMediaDatabase(long uid, int topicId, int type, ArrayList messages, int max_id, int min_id, boolean topReached) { + private void putMediaDatabase(long uid, long topicId, int type, ArrayList messages, int max_id, int min_id, boolean topReached) { getMessagesStorage().getStorageQueue().postRunnable(() -> { try { if (min_id == 0 && (messages.isEmpty() || topReached)) { @@ -4296,7 +4335,7 @@ private void putMediaDatabase(long uid, int topicId, int type, ArrayList messages) { } } - public void loadReplyMessagesForMessages(ArrayList messages, long dialogId, boolean scheduled, int threadMessageId, Runnable callback, int classGuid) { + public void loadReplyMessagesForMessages(ArrayList messages, long dialogId, boolean scheduled, long threadMessageId, Runnable callback, int classGuid) { if (DialogObject.isEncryptedDialog(dialogId)) { ArrayList replyMessages = new ArrayList<>(); LongSparseArray> replyMessageRandomOwners = new LongSparseArray<>(); @@ -5573,7 +5612,7 @@ public void loadReplyMessagesForMessages(ArrayList messages, long } } - if (channelId != 0 && messageObject.getDialogId() != -channelId) { + if (dialogId != UserObject.REPLY_BOT && channelId != 0 && messageObject.getDialogId() != -channelId) { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(channelId); if (chat != null && !ChatObject.isPublic(chat)) { continue; @@ -6591,8 +6630,8 @@ private static void removeOffset4After(int start, int end, ArrayList draftsFolderIds = new LongSparseArray<>(); - private LongSparseArray> drafts = new LongSparseArray<>(); - private LongSparseArray> draftMessages = new LongSparseArray<>(); + private LongSparseArray> drafts = new LongSparseArray<>(); + private LongSparseArray> draftMessages = new LongSparseArray<>(); private boolean inTransaction; private SharedPreferences draftPreferences; private boolean loadingDrafts; @@ -6629,28 +6668,28 @@ public void clearDraftsFolderIds() { draftsFolderIds.clear(); } - public LongSparseArray> getDrafts() { + public LongSparseArray> getDrafts() { return drafts; } - public TLRPC.DraftMessage getDraft(long dialogId, int threadId) { - SparseArray threads = drafts.get(dialogId); + public TLRPC.DraftMessage getDraft(long dialogId, long threadId) { + LongSparseArray threads = drafts.get(dialogId); if (threads == null) { return null; } return threads.get(threadId); } - public Pair getOneThreadDraft(long dialogId) { - SparseArray threads = drafts.get(dialogId); + public Pair getOneThreadDraft(long dialogId) { + LongSparseArray threads = drafts.get(dialogId); if (threads == null || threads.size() <= 0) { return null; } return new Pair(threads.keyAt(0), threads.valueAt(0)); } - public TLRPC.Message getDraftMessage(long dialogId, int threadId) { - SparseArray threads = draftMessages.get(dialogId); + public TLRPC.Message getDraftMessage(long dialogId, long threadId) { + LongSparseArray threads = draftMessages.get(dialogId); if (threads == null) { return null; } @@ -6661,7 +6700,7 @@ public void saveDraft(long dialogId, int threadId, CharSequence message, ArrayLi saveDraft(dialogId, threadId, message, entities, replyToMessage, null, noWebpage, false); } - public void saveDraft(long dialogId, int threadId, CharSequence message, ArrayList entities, TLRPC.Message replyToMessage, ChatActivity.ReplyQuote quote, boolean noWebpage, boolean clean) { + public void saveDraft(long dialogId, long threadId, CharSequence message, ArrayList entities, TLRPC.Message replyToMessage, ChatActivity.ReplyQuote quote, boolean noWebpage, boolean clean) { TLRPC.DraftMessage draftMessage; if (getMessagesController().isForum(dialogId) && threadId == 0) { replyToMessage = null; @@ -6708,7 +6747,7 @@ public void saveDraft(long dialogId, int threadId, CharSequence message, ArrayLi draftMessage.flags |= 8; } - SparseArray threads = drafts.get(dialogId); + LongSparseArray threads = drafts.get(dialogId); TLRPC.DraftMessage currentDraft = threads == null ? null : threads.get(threadId); if (!clean) { boolean sameDraft; @@ -6811,7 +6850,7 @@ private static TLRPC.InputReplyTo toInputReplyTo(int currentAccount, TLRPC.Messa return null; } - public void saveDraft(long dialogId, int threadId, TLRPC.DraftMessage draft, TLRPC.Message replyToMessage, boolean fromServer) { + public void saveDraft(long dialogId, long threadId, TLRPC.DraftMessage draft, TLRPC.Message replyToMessage, boolean fromServer) { if (getMessagesController().isForum(dialogId) && threadId == 0 && TextUtils.isEmpty(draft.message)) { if (draft.reply_to instanceof TLRPC.TL_inputReplyToMessage) { ((TLRPC.TL_inputReplyToMessage) draft.reply_to).reply_to_msg_id = 0; @@ -6821,7 +6860,7 @@ public void saveDraft(long dialogId, int threadId, TLRPC.DraftMessage draft, TLR MessagesController messagesController = getMessagesController(); if (draft == null || draft instanceof TLRPC.TL_draftMessageEmpty) { { - SparseArray threads = drafts.get(dialogId); + LongSparseArray threads = drafts.get(dialogId); if (threads != null) { threads.remove(threadId); if (threads.size() == 0) { @@ -6830,7 +6869,7 @@ public void saveDraft(long dialogId, int threadId, TLRPC.DraftMessage draft, TLR } } { - SparseArray threads = draftMessages.get(dialogId); + LongSparseArray threads = draftMessages.get(dialogId); if (threads != null) { threads.remove(threadId); if (threads.size() == 0) { @@ -6845,9 +6884,9 @@ public void saveDraft(long dialogId, int threadId, TLRPC.DraftMessage draft, TLR } messagesController.removeDraftDialogIfNeed(dialogId); } else { - SparseArray threads = drafts.get(dialogId); + LongSparseArray threads = drafts.get(dialogId); if (threads == null) { - threads = new SparseArray<>(); + threads = new LongSparseArray<>(); drafts.put(dialogId, threads); } threads.put(threadId, draft); @@ -6863,7 +6902,7 @@ public void saveDraft(long dialogId, int threadId, TLRPC.DraftMessage draft, TLR FileLog.e(e); } } - SparseArray threads = draftMessages.get(dialogId); + LongSparseArray threads = draftMessages.get(dialogId); if (replyToMessage == null && draft != null && draft.reply_to != null) { if (threads != null) { replyToMessage = threads.get(threadId); @@ -6888,7 +6927,7 @@ public void saveDraft(long dialogId, int threadId, TLRPC.DraftMessage draft, TLR } } else { if (threads == null) { - threads = new SparseArray<>(); + threads = new LongSparseArray<>(); draftMessages.put(dialogId, threads); } threads.put(threadId, replyToMessage); @@ -6991,17 +7030,17 @@ public void saveDraft(long dialogId, int threadId, TLRPC.DraftMessage draft, TLR } } - private void saveDraftReplyMessage(long dialogId, int threadId, TLRPC.Message message) { + private void saveDraftReplyMessage(long dialogId, long threadId, TLRPC.Message message) { if (message == null) { return; } AndroidUtilities.runOnUIThread(() -> { - SparseArray threads = drafts.get(dialogId); + LongSparseArray threads = drafts.get(dialogId); TLRPC.DraftMessage draftMessage = threads != null ? threads.get(threadId) : null; if (draftMessage != null && draftMessage.reply_to != null && draftMessage.reply_to.reply_to_msg_id == message.id) { - SparseArray threads2 = draftMessages.get(dialogId); + LongSparseArray threads2 = draftMessages.get(dialogId); if (threads2 == null) { - threads2 = new SparseArray<>(); + threads2 = new LongSparseArray<>(); draftMessages.put(dialogId, threads2); } threads2.put(threadId, message); @@ -7025,15 +7064,15 @@ public void clearAllDrafts(boolean notify) { } } - public void cleanDraft(long dialogId, int threadId, boolean replyOnly) { - SparseArray threads2 = drafts.get(dialogId); + public void cleanDraft(long dialogId, long threadId, boolean replyOnly) { + LongSparseArray threads2 = drafts.get(dialogId); TLRPC.DraftMessage draftMessage = threads2 != null ? threads2.get(threadId) : null; if (draftMessage == null) { return; } if (!replyOnly) { { - SparseArray threads = drafts.get(dialogId); + LongSparseArray threads = drafts.get(dialogId); if (threads != null) { threads.remove(threadId); if (threads.size() == 0) { @@ -7042,7 +7081,7 @@ public void cleanDraft(long dialogId, int threadId, boolean replyOnly) { } } { - SparseArray threads = draftMessages.get(dialogId); + LongSparseArray threads = draftMessages.get(dialogId); if (threads != null) { threads.remove(threadId); if (threads.size() == 0) { @@ -7120,7 +7159,7 @@ public void clearBotKeyboard(long dialogId) { if (dialogMessages != null) { for (int i = 0; i < dialogMessages.size(); ++i) { TLRPC.Message msg = dialogMessages.get(i); - int topicId = MessageObject.getTopicId(msg, ChatObject.isForum(currentAccount, dialogId)); + long topicId = MessageObject.getTopicId(currentAccount, msg, ChatObject.isForum(currentAccount, dialogId)); MessagesStorage.TopicKey topicKey = MessagesStorage.TopicKey.of(dialogId, topicId); botKeyboards.remove(topicKey); getNotificationCenter().postNotificationName(NotificationCenter.botKeyboardDidLoad, null, topicKey); @@ -7238,7 +7277,7 @@ public void putBotKeyboard(MessagesStorage.TopicKey topicKey, TLRPC.Message mess message.serializeToStream(data); if (topicKey.topicId != 0) { state.bindLong(1, topicKey.dialogId); - state.bindInteger(2, topicKey.topicId); + state.bindLong(2, topicKey.topicId); state.bindInteger(3, message.id); state.bindByteBuffer(4, data); } else { @@ -8343,7 +8382,9 @@ private void updateEmojiStatuses(int type, TLRPC.TL_account_emojiStatuses respon ArrayList recentReactions = new ArrayList<>(); ArrayList topReactions = new ArrayList<>(); - boolean loadingRecentReactions; + ArrayList savedReactions = new ArrayList<>(); + boolean loadingRecentReactions, loadedRecentReactions; + boolean loadingSavedReactions, loadedSavedReactions; public ArrayList getRecentReactions() { return recentReactions; @@ -8367,7 +8408,7 @@ public ArrayList getTopReactions() { } public void loadRecentAndTopReactions(boolean force) { - if (loadingRecentReactions || !recentReactions.isEmpty() || force) { + if (loadingRecentReactions || loadedRecentReactions && !force) { return; } SharedPreferences recentReactionsPref = ApplicationLoader.applicationContext.getSharedPreferences("recent_reactions_" + currentAccount, Context.MODE_PRIVATE); @@ -8378,14 +8419,17 @@ public void loadRecentAndTopReactions(boolean force) { recentReactions.addAll(loadReactionsFromPref(recentReactionsPref)); topReactions.addAll(loadReactionsFromPref(topReactionsPref)); loadingRecentReactions = true; + loadedRecentReactions = true; boolean loadFromServer = true; if (loadFromServer) { + boolean[] loaded = new boolean[2]; + TLRPC.TL_messages_getRecentReactions recentReactionsRequest = new TLRPC.TL_messages_getRecentReactions(); recentReactionsRequest.hash = recentReactionsPref.getLong("hash", 0); recentReactionsRequest.limit = 50; - ConnectionsManager.getInstance(currentAccount).sendRequest(recentReactionsRequest, (response, error) -> { + ConnectionsManager.getInstance(currentAccount).sendRequest(recentReactionsRequest, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (error == null) { if (response instanceof TLRPC.TL_messages_reactions) { TLRPC.TL_messages_reactions reactions = (TLRPC.TL_messages_reactions) response; @@ -8398,12 +8442,17 @@ public void loadRecentAndTopReactions(boolean force) { } } - }); + + loaded[0] = true; + if (loaded[1]) { + loadingRecentReactions = false; + } + })); TLRPC.TL_messages_getTopReactions topReactionsRequest = new TLRPC.TL_messages_getTopReactions(); topReactionsRequest.hash = topReactionsPref.getLong("hash", 0); topReactionsRequest.limit = 100; - ConnectionsManager.getInstance(currentAccount).sendRequest(topReactionsRequest, (response, error) -> { + ConnectionsManager.getInstance(currentAccount).sendRequest(topReactionsRequest, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (error == null) { if (response instanceof TLRPC.TL_messages_reactions) { TLRPC.TL_messages_reactions reactions = (TLRPC.TL_messages_reactions) response; @@ -8417,7 +8466,51 @@ public void loadRecentAndTopReactions(boolean force) { } } - }); + + loaded[1] = true; + if (loaded[0]) { + loadingRecentReactions = false; + } + })); + } + } + + public ArrayList getSavedReactions() { + return savedReactions; + } + public void loadSavedReactions(boolean force) { + if (loadingSavedReactions || loadedSavedReactions && !force) { + return; + } + SharedPreferences savedReactionsPref = ApplicationLoader.applicationContext.getSharedPreferences("saved_reactions_" + currentAccount, Context.MODE_PRIVATE); + + savedReactions.clear(); + savedReactions.addAll(loadReactionsFromPref(savedReactionsPref)); + loadingSavedReactions = true; + loadedSavedReactions = true; + + boolean loadFromServer = true; + if (loadFromServer) { + TLRPC.TL_messages_getDefaultTagReactions recentReactionsRequest = new TLRPC.TL_messages_getDefaultTagReactions(); + recentReactionsRequest.hash = savedReactionsPref.getLong("hash", 0); + ConnectionsManager.getInstance(currentAccount).sendRequest(recentReactionsRequest, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error == null) { + if (response instanceof TLRPC.TL_messages_reactions) { + TLRPC.TL_messages_reactions reactions = (TLRPC.TL_messages_reactions) response; + savedReactions.clear(); + savedReactions.addAll(reactions.reactions); + + saveReactionsToPref(savedReactionsPref, reactions.hash, reactions.reactions); + + getNotificationCenter().postNotificationName(NotificationCenter.savedReactionTagsUpdate); + } + if (response instanceof TLRPC.TL_messages_reactionsNotModified) { + + } + } + + loadingSavedReactions = false; + })); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java index 5f4165cc89..b84d23fc15 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java @@ -33,6 +33,7 @@ import android.text.style.URLSpan; import android.text.util.Linkify; import android.util.Base64; +import android.util.Log; import androidx.annotation.NonNull; import androidx.collection.LongSparseArray; @@ -57,6 +58,7 @@ import org.telegram.ui.Components.QuoteSpan; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.Reactions.ReactionsUtils; +import org.telegram.ui.Components.StaticLayoutEx; import org.telegram.ui.Components.Text; import org.telegram.ui.Components.TextStyleSpan; import org.telegram.ui.Components.TranscribeButton; @@ -236,6 +238,7 @@ public class MessageObject { public boolean cancelEditing; public boolean scheduled; + public boolean scheduledSent; public boolean preview; public boolean previewForward; @@ -311,6 +314,7 @@ public class MessageObject { public boolean isRepostVideoPreview; public boolean forceAvatar; public Drawable customAvatarDrawable; + public boolean isSaved; private byte[] randomWaveform; public boolean drawServiceWithDefaultTypeface; @@ -346,11 +350,23 @@ public static boolean isPremiumSticker(TLRPC.Document document) { return false; } - private static int getTopicId(TLRPC.Message message) { - return getTopicId(message, false); + private static long getTopicId(MessageObject message) { + if (message == null) { + return 0; + } + return getTopicId(message.currentAccount, message.messageOwner, false); } - public static int getTopicId(TLRPC.Message message, boolean sureIsForum) { + private static long getTopicId(int currentAccount, TLRPC.Message message) { + return getTopicId(currentAccount, message, false); + } + + public static long getTopicId(int currentAccount, TLRPC.Message message, boolean sureIsForum) { + final long selfId = UserConfig.getInstance(currentAccount).getClientUserId(); + if (!sureIsForum && message != null && currentAccount >= 0 && DialogObject.getPeerDialogId(message.peer_id) == selfId) { + return getSavedDialogId(selfId, message); + } + if (message != null && message.action instanceof TLRPC.TL_messageActionTopicCreate) { return message.id; } @@ -478,7 +494,7 @@ public void markReactionsAsRead() { } } if (changed) { - MessagesStorage.getInstance(currentAccount).markMessageReactionsAsRead(messageOwner.dialog_id, getTopicId(messageOwner), messageOwner.id, true); + MessagesStorage.getInstance(currentAccount).markMessageReactionsAsRead(messageOwner.dialog_id, getTopicId(currentAccount, messageOwner), messageOwner.id, true); } } @@ -946,6 +962,8 @@ private float multiHeight(float[] array, int start, int end) { return maxSizeWidth / sum; } + public boolean reversed; + public void calculate() { posArray.clear(); positions.clear(); @@ -975,9 +993,9 @@ public void calculate() { hasCaption = false; boolean checkCaption = true; - for (int a = 0; a < count; a++) { + for (int a = (reversed ? count - 1 : 0); (reversed ? a >= 0 : a < count);) { MessageObject messageObject = messages.get(a); - if (a == 0) { + if (a == (reversed ? count - 1 : 0)) { isOut = messageObject.isOutOwner(); needShare = !isOut && ( messageObject.messageOwner.fwd_from != null && messageObject.messageOwner.fwd_from.saved_from_peer != null || @@ -990,7 +1008,7 @@ public void calculate() { } TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(messageObject.photoThumbs, AndroidUtilities.getPhotoSize()); GroupedMessagePosition position = new GroupedMessagePosition(); - position.last = a == count - 1; + position.last = (reversed ? a == 0 : a == count - 1); position.aspectRatio = photoSize == null ? 1.0f : photoSize.w / (float) photoSize.h; if (position.aspectRatio > 1.2f) { @@ -1019,14 +1037,21 @@ public void calculate() { } hasCaption = true; } + + if (reversed) { + a--; + } else { + a++; + } } if (isDocuments) { for (int a = 0; a < count; a++) { GroupedMessagePosition pos = posArray.get(a); - pos.flags |= POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT; - if (a == 0) { + pos.flags = POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT; + if (!reversed && a == 0 || reversed && a == count - 1) { pos.flags |= POSITION_FLAG_TOP; - } else if (a == count - 1) { + pos.last = false; + } else if (reversed && a == 0 || !reversed && a == count - 1) { pos.flags |= POSITION_FLAG_BOTTOM; pos.last = true; } @@ -1034,8 +1059,8 @@ public void calculate() { pos.aspectRatio = 1.0f; pos.minX = 0; pos.maxX = 0; - pos.minY = (byte) a; - pos.maxY = (byte) a; + pos.minY = (byte) (reversed ? count - 1 - a : a); + pos.maxY = (byte) (reversed ? count - 1 - a : a); pos.spanSize = 1000; pos.pw = maxSizeWidth; pos.ph = 100; @@ -1490,7 +1515,11 @@ public MessageObject(int accountNum, TLRPC.Message message, AbstractMap users, LongSparseArray chats, boolean generateLayout, boolean checkMediaExists) { - this(accountNum, message, null, null, null, users, chats, generateLayout, checkMediaExists, 0); + this(accountNum, message, null, null, null, users, chats, generateLayout, checkMediaExists, 0, false, false, false); + } + + public MessageObject(int accountNum, TLRPC.Message message, LongSparseArray users, LongSparseArray chats, boolean generateLayout, boolean checkMediaExists, boolean isSavedMessages) { + this(accountNum, message, null, null, null, users, chats, generateLayout, checkMediaExists, 0, false, false, isSavedMessages); } public MessageObject(int accountNum, TLRPC.Message message, AbstractMap users, AbstractMap chats, boolean generateLayout, boolean checkMediaExists, long eid) { @@ -1498,14 +1527,15 @@ public MessageObject(int accountNum, TLRPC.Message message, AbstractMap users, AbstractMap chats, LongSparseArray sUsers, LongSparseArray sChats, boolean generateLayout, boolean checkMediaExists, long eid) { - this(accountNum, message, replyToMessage, users, chats, sUsers, sChats, generateLayout, checkMediaExists, eid, false, false); + this(accountNum, message, replyToMessage, users, chats, sUsers, sChats, generateLayout, checkMediaExists, eid, false, false, false); } - public MessageObject(int accountNum, TLRPC.Message message, MessageObject replyToMessage, AbstractMap users, AbstractMap chats, LongSparseArray sUsers, LongSparseArray sChats, boolean generateLayout, boolean checkMediaExists, long eid, boolean isRepostPreview, boolean isRepostVideoPreview) { + public MessageObject(int accountNum, TLRPC.Message message, MessageObject replyToMessage, AbstractMap users, AbstractMap chats, LongSparseArray sUsers, LongSparseArray sChats, boolean generateLayout, boolean checkMediaExists, long eid, boolean isRepostPreview, boolean isRepostVideoPreview, boolean isSavedMessages) { Theme.createCommonMessageResources(); this.isRepostPreview = isRepostPreview; this.isRepostVideoPreview = isRepostVideoPreview; + this.isSaved = isSavedMessages; currentAccount = accountNum; messageOwner = message; @@ -1633,10 +1663,14 @@ public void createStrippedThumb() { return; } try { + String filter = "b"; + if (isRoundVideo()) { + filter += "r"; + } for (int a = 0, N = photoThumbs.size(); a < N; a++) { TLRPC.PhotoSize photoSize = photoThumbs.get(a); if (photoSize instanceof TLRPC.TL_photoStrippedSize) { - strippedThumb = new BitmapDrawable(ApplicationLoader.applicationContext.getResources(), ImageLoader.getStrippedPhotoBitmap(photoSize.bytes, "b")); + strippedThumb = new BitmapDrawable(ApplicationLoader.applicationContext.getResources(), ImageLoader.getStrippedPhotoBitmap(photoSize.bytes, filter)); break; } } @@ -3557,6 +3591,7 @@ public ArrayList getWebPagePhotos(ArrayList array, } public void createMessageSendInfo() { + boolean notReadyYet = videoEditedInfo != null && videoEditedInfo.notReadyYet; if (messageOwner.message != null && (messageOwner.id < 0 || isEditing()) && messageOwner.params != null) { String param; if ((param = messageOwner.params.get("ve")) != null && (isVideo() || isNewGif() || isRoundVideo())) { @@ -3565,6 +3600,7 @@ public void createMessageSendInfo() { videoEditedInfo = null; } else { videoEditedInfo.roundVideo = isRoundVideo(); + videoEditedInfo.notReadyYet = notReadyYet; } } if (messageOwner.send_state == MESSAGE_SEND_STATE_EDITING && (param = messageOwner.params.get("prevMedia")) != null) { @@ -4339,6 +4375,9 @@ private void updateMessageText(AbstractMap users, AbstractMap< messageText = LocaleController.getString(R.string.ActionBotAllowedWebapp); } else if (botApp != null) { String botAppTitle = botApp.title; + if (botAppTitle == null) { + botAppTitle = ""; + } String text = LocaleController.getString("ActionBotAllowedApp", R.string.ActionBotAllowedApp); int start = text.indexOf("%1$s"); SpannableString str = new SpannableString(String.format(text, botAppTitle)); @@ -4473,6 +4512,10 @@ private void updateMessageText(AbstractMap users, AbstractMap< } else { messageText = LocaleController.getString("Poll", R.string.Poll); } + } else if (isVoiceOnce()) { + messageText = LocaleController.getString(R.string.AttachOnceAudio); + } else if (isRoundOnce()) { + messageText = LocaleController.getString(R.string.AttachOnceRound); } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaPhoto) { if (getMedia(messageOwner).ttl_seconds != 0 && !(messageOwner instanceof TLRPC.TL_message_secret)) { messageText = LocaleController.getString("AttachDestructingPhoto", R.string.AttachDestructingPhoto); @@ -4483,7 +4526,13 @@ private void updateMessageText(AbstractMap users, AbstractMap< } } else if (isVideo() || getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDocument && (getDocument() instanceof TLRPC.TL_documentEmpty || getDocument() == null) && getMedia(messageOwner).ttl_seconds != 0) { if (getMedia(messageOwner).ttl_seconds != 0 && !(messageOwner instanceof TLRPC.TL_message_secret)) { - messageText = LocaleController.getString("AttachDestructingVideo", R.string.AttachDestructingVideo); + if (getMedia(messageOwner).voice) { + messageText = LocaleController.getString(R.string.AttachVoiceExpired); + } else if (getMedia(messageOwner).round) { + messageText = LocaleController.getString(R.string.AttachRoundExpired); + } else { + messageText = LocaleController.getString(R.string.AttachDestructingVideo); + } } else { messageText = LocaleController.getString("AttachVideo", R.string.AttachVideo); } @@ -4563,7 +4612,7 @@ public CharSequence getMediaTitle(TLRPC.MessageMedia media) { } if (link != null) { SpannableString str = new SpannableString(link); - ((SpannableString) str).setSpan(new URLSpanReplacement("https://" + link, new TextStyleSpan.TextStyleRun()), 0, messageText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ((SpannableString) str).setSpan(new URLSpanReplacement("https://" + link, new TextStyleSpan.TextStyleRun()), 0, str.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); return str; } else { return ""; @@ -4589,7 +4638,13 @@ public CharSequence getMediaTitle(TLRPC.MessageMedia media) { } } else if (media != null && (isVideoDocument(media.document) || media instanceof TLRPC.TL_messageMediaDocument && (media.document instanceof TLRPC.TL_documentEmpty || media.document == null) && media.ttl_seconds != 0)) { if (media.ttl_seconds != 0 && !(messageOwner instanceof TLRPC.TL_message_secret)) { - return LocaleController.getString("AttachDestructingVideo", R.string.AttachDestructingVideo); + if (media.voice) { + return LocaleController.getString(R.string.AttachVoiceExpired); + } else if (media.round) { + return LocaleController.getString(R.string.AttachRoundExpired); + } else { + return LocaleController.getString(R.string.AttachDestructingVideo); + } } else { return LocaleController.getString("AttachVideo", R.string.AttachVideo); } @@ -6134,6 +6189,11 @@ public boolean needDrawShareButton() { if (isRepostPreview) { return false; } + if (isSaved) { + long selfId = UserConfig.getInstance(currentAccount).clientUserId; + long dialogId = MessageObject.getSavedDialogId(selfId, messageOwner); + return dialogId != selfId && dialogId != UserObject.ANONYMOUS; + } if (type == TYPE_JOINED_CHANNEL) { return false; } else if (isSponsored()) { @@ -6197,7 +6257,7 @@ public int getMaxMessageTextWidth() { generatedWithMinSize = AndroidUtilities.isTablet() ? AndroidUtilities.getMinTabletSide() : getParentWidth(); } generatedWithDensity = AndroidUtilities.density; - if (hasCode) { + if (hasCode && !isSaved) { maxWidth = generatedWithMinSize - dp(45 + 15); if (needDrawAvatarInternal() && !isOutOwner() && !messageOwner.isThreadMessage) { maxWidth -= dp(52); @@ -6303,6 +6363,50 @@ private boolean applyEntities() { return addEntitiesToText(messageText, useManualParse); } + private static StaticLayout makeStaticLayout(CharSequence text, TextPaint paint, int width, float lineSpacingMult, float lineSpacingAdd, boolean dontIncludePad) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + StaticLayout.Builder builder = + StaticLayout.Builder.obtain(text, 0, text.length(), paint, width) + .setLineSpacing(lineSpacingAdd, lineSpacingMult) + .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) + .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) + .setAlignment(Layout.Alignment.ALIGN_NORMAL); + if (dontIncludePad) { + builder.setIncludePad(false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + builder.setUseLineSpacingFromFallbacks(false); + } + } + StaticLayout layout = builder.build(); + + boolean realWidthLarger = false; + for (int l = 0; l < layout.getLineCount(); ++l) { + if (layout.getLineRight(l) > width) { + realWidthLarger = true; + break; + } + } + if (realWidthLarger) { + builder = StaticLayout.Builder.obtain(text, 0, text.length(), paint, width) + .setLineSpacing(lineSpacingAdd, lineSpacingMult) + .setBreakStrategy(StaticLayout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) + .setAlignment(Layout.Alignment.ALIGN_NORMAL); + if (dontIncludePad) { + builder.setIncludePad(false); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + builder.setUseLineSpacingFromFallbacks(false); + } + } + layout = builder.build(); + } + + return layout; + } else { + return new StaticLayout(text, paint, width, Layout.Alignment.ALIGN_NORMAL, lineSpacingMult, lineSpacingAdd, false); + } + } + public void generateLayout(TLRPC.User fromUser) { if (type != TYPE_TEXT && type != TYPE_EMOJIS && type != TYPE_STORY_MENTION || messageOwner.peer_id == null || TextUtils.isEmpty(messageText)) { return; @@ -6352,28 +6456,9 @@ public void generateLayout(TLRPC.User fromUser) { paint = Theme.chat_msgTextPaint; } - final float lineSpacing = 1f; - final float lineAdd = totalAnimatedEmojiCount >= 4 ? -1 : 0; - Layout.Alignment align = Layout.Alignment.ALIGN_NORMAL; //type == TYPE_EMOJIS && isOut() ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_NORMAL; CharSequence text = messageText; try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - StaticLayout.Builder builder = - StaticLayout.Builder.obtain(text, 0, text.length(), paint, maxWidth) - .setLineSpacing(lineAdd, lineSpacing) - .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) - .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) - .setAlignment(align); - if (emojiOnlyCount > 0) { - builder.setIncludePad(false); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - builder.setUseLineSpacingFromFallbacks(false); - } - } - textLayout = builder.build(); - } else { - textLayout = new StaticLayout(text, paint, maxWidth, align, lineSpacing, lineAdd, false); - } + textLayout = makeStaticLayout(text, paint, maxWidth, 1f, totalAnimatedEmojiCount >= 4 ? -1 : 0, emojiOnlyCount > 0); } catch (Exception e) { FileLog.e(e); return; @@ -6418,23 +6503,7 @@ public void updateDrawState(TextPaint tp) { }, text.length() - readMore.length(), text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - StaticLayout.Builder builder = - StaticLayout.Builder.obtain(text, 0, text.length(), paint, maxWidth) - .setLineSpacing(lineAdd, lineSpacing) - .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) - .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) - .setAlignment(align); - if (emojiOnlyCount > 0) { - builder.setIncludePad(false); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - builder.setUseLineSpacingFromFallbacks(false); - } - } - textLayout = builder.build(); - } else { - textLayout = new StaticLayout(text, paint, maxWidth, align, lineSpacing, lineAdd, false); - } + textLayout = makeStaticLayout(text, paint, maxWidth, 1f, totalAnimatedEmojiCount >= 4 ? -1 : 0, emojiOnlyCount > 0); } catch (Exception e) { FileLog.e(e); return; @@ -6557,23 +6626,7 @@ public void updateDrawState(TextPaint tp) { } else { sb = new SpannableString(blockText.toString()); } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - StaticLayout.Builder builder = - StaticLayout.Builder.obtain(sb, 0, sb.length(), layoutPaint, blockMaxWidth) - .setLineSpacing(lineAdd, lineSpacing) - .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) - .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) - .setAlignment(align); - if (emojiOnlyCount > 0) { - builder.setIncludePad(false); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - builder.setUseLineSpacingFromFallbacks(false); - } - } - textLayout = builder.build(); - } else { - textLayout = new StaticLayout(sb, 0, sb.length(), layoutPaint, blockMaxWidth, align, lineSpacing, lineAdd, false); - } + textLayout = makeStaticLayout(sb, layoutPaint, blockMaxWidth, 1f, totalAnimatedEmojiCount >= 4 ? -1 : 0, emojiOnlyCount > 0); } block.textLayout = textLayout; @@ -6614,17 +6667,7 @@ public void updateDrawState(TextPaint tp) { } else { sb = SpannableString.valueOf(blockText); } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - StaticLayout.Builder builder = - StaticLayout.Builder.obtain(sb, 0, sb.length(), layoutPaint, blockMaxWidth) - .setLineSpacing(lineAdd, lineSpacing) - .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) - .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) - .setAlignment(align); - block.textLayout = builder.build(); - } else { - block.textLayout = new StaticLayout(sb, 0, sb.length(), layoutPaint, blockMaxWidth, align, lineSpacing, lineAdd, false); - } + block.textLayout = makeStaticLayout(sb, layoutPaint, blockMaxWidth, 1f, totalAnimatedEmojiCount >= 4 ? -1 : 0, false); block.textYOffset = offset; if (a != 0 && emojiOnlyCount <= 0) { @@ -6838,17 +6881,7 @@ public TextLayoutBlocks(MessageObject messageObject, @NonNull CharSequence text, final float lineAdd = 0; Layout.Alignment align = Layout.Alignment.ALIGN_NORMAL; //type == TYPE_EMOJIS && isOut() ? Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_NORMAL; try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - StaticLayout.Builder builder = - StaticLayout.Builder.obtain(text, 0, text.length(), textPaint, width) - .setLineSpacing(lineAdd, lineSpacing) - .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) - .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) - .setAlignment(align); - textLayout = builder.build(); - } else { - textLayout = new StaticLayout(text, textPaint, width, align, lineSpacing, lineAdd, false); - } + textLayout = makeStaticLayout(text, textPaint, width, 1f, 0f, false); } catch (Exception e) { FileLog.e(e); return; @@ -6892,17 +6925,7 @@ public void updateDrawState(TextPaint tp) { }, text.length() - readMore.length(), text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - StaticLayout.Builder builder = - StaticLayout.Builder.obtain(text, 0, text.length(), textPaint, width) - .setLineSpacing(lineAdd, lineSpacing) - .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) - .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) - .setAlignment(align); - textLayout = builder.build(); - } else { - textLayout = new StaticLayout(text, textPaint, width, align, lineSpacing, lineAdd, false); - } + textLayout = makeStaticLayout(text, textPaint, width, 1f, 0f, false); } catch (Exception e) { FileLog.e(e); return; @@ -7017,17 +7040,7 @@ public void updateDrawState(TextPaint tp) { } else { sb = new SpannableString(text.subSequence(range.start, range.end)); } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - StaticLayout.Builder builder = - StaticLayout.Builder.obtain(sb, 0, text.length(), layoutPaint, blockMaxWidth) - .setLineSpacing(lineAdd, lineSpacing) - .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) - .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) - .setAlignment(align); - textLayout = builder.build(); - } else { - textLayout = new StaticLayout(sb, layoutPaint, blockMaxWidth, align, lineSpacing, lineAdd, false); - } + textLayout = makeStaticLayout(sb, layoutPaint, blockMaxWidth, 1f, 0f, false); } block.textLayout = textLayout; @@ -7052,7 +7065,7 @@ public void updateDrawState(TextPaint tp) { } else { sb = SpannableString.valueOf(text.subSequence(startCharacter, endCharacter)); } - block.textLayout = new StaticLayout(sb, 0, sb.length(), layoutPaint, blockMaxWidth, align, lineSpacing, lineAdd, false); + block.textLayout = makeStaticLayout(sb, layoutPaint, blockMaxWidth, 1f, 0f, false); block.textYOffset = offset; if (a != 0) { @@ -7225,6 +7238,10 @@ public boolean isOutOwner() { if (isOutOwnerCached != null) { return isOutOwnerCached; } + long selfUserId = UserConfig.getInstance(currentAccount).getClientUserId(); + if (isSaved && messageOwner.fwd_from != null) { + return isOutOwnerCached = messageOwner.fwd_from.from_id != null && messageOwner.fwd_from.from_id.user_id == selfUserId || messageOwner.fwd_from.saved_out; + } 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 isOutOwnerCached = false; @@ -7232,7 +7249,6 @@ public boolean isOutOwner() { if (messageOwner.fwd_from == null) { return isOutOwnerCached = true; } - long selfUserId = UserConfig.getInstance(currentAccount).getClientUserId(); if (getDialogId() == selfUserId) { return isOutOwnerCached = messageOwner.fwd_from.from_id instanceof TLRPC.TL_peerUser && messageOwner.fwd_from.from_id.user_id == selfUserId && (messageOwner.fwd_from.saved_from_peer == null || messageOwner.fwd_from.saved_from_peer.user_id == selfUserId) || messageOwner.fwd_from.saved_from_peer != null && messageOwner.fwd_from.saved_from_peer.user_id == selfUserId && (messageOwner.fwd_from.from_id == null || messageOwner.fwd_from.from_id.user_id == selfUserId); @@ -7244,6 +7260,9 @@ public boolean needDrawAvatar() { if (isRepostPreview) { return true; } + if (isSaved) { + return getSavedDialogId() < 0 || getSavedDialogId() == UserObject.ANONYMOUS; + } if (forceAvatar || customAvatarDrawable != null) { return true; } @@ -7254,6 +7273,9 @@ private boolean needDrawAvatarInternal() { if (isRepostPreview) { return true; } + if (isSaved) { + return getSavedDialogId() < 0 || getSavedDialogId() == UserObject.ANONYMOUS; + } if (forceAvatar || customAvatarDrawable != null) { return true; } @@ -7541,9 +7563,12 @@ public static long getChatId(TLRPC.Message message) { } public static boolean shouldEncryptPhotoOrVideo(int currentAccount, TLRPC.Message message) { - if (MessagesController.getInstance(currentAccount).isChatNoForwards(getChatId(message)) || message != null && message.noforwards) { + if (message != null && message.media != null && (isVoiceDocument(getDocument(message)) || isRoundVideoMessage(message)) && message.media.ttl_seconds == 0x7FFFFFFF) { return true; } +// if (MessagesController.getInstance(currentAccount).isChatNoForwards(getChatId(message)) || message != null && message.noforwards) { +// return true; +// } if (message instanceof TLRPC.TL_message_secret) { return (getMedia(message) instanceof TLRPC.TL_messageMediaPhoto || isVideoMessage(message)) && message.ttl > 0 && message.ttl <= 60; } else { @@ -7661,6 +7686,58 @@ public static long getDialogId(TLRPC.Message message) { return message.dialog_id; } + public long getSavedDialogId() { + return getSavedDialogId(UserConfig.getInstance(currentAccount).getClientUserId(), messageOwner); + } + + public static long getSavedDialogId(long self, TLRPC.Message message) { + if (message.saved_peer_id != null) { + if (message.saved_peer_id.chat_id != 0) { + return -message.saved_peer_id.chat_id; + } else if (message.saved_peer_id.channel_id != 0) { + return -message.saved_peer_id.channel_id; + } else { + return message.saved_peer_id.user_id; + } + } + if (message.from_id.user_id == self) { + if (message.fwd_from != null && message.fwd_from.saved_from_peer != null) { + return DialogObject.getPeerDialogId(message.fwd_from.saved_from_peer); + } else if (message.fwd_from != null && message.fwd_from.from_id != null) { + return self; + } else if (message.fwd_from != null) { + return UserObject.ANONYMOUS; + } else { + return self; + } + } + return 0; + } + + public static TLRPC.Peer getSavedDialogPeer(long self, TLRPC.Message message) { + if (message.saved_peer_id != null) { + return message.saved_peer_id; + } + if (message.peer_id != null && message.peer_id.user_id == self && message.from_id != null && message.from_id.user_id == self) { + if (message.fwd_from != null && message.fwd_from.saved_from_peer != null) { + return message.fwd_from.saved_from_peer; + } else if (message.fwd_from != null && message.fwd_from.from_id != null) { + TLRPC.Peer peer = new TLRPC.TL_peerUser(); + peer.user_id = self; + return peer; + } else if (message.fwd_from != null) { + TLRPC.Peer peer = new TLRPC.TL_peerUser(); + peer.user_id = 2666000L; + return peer; + } else { + TLRPC.Peer peer = new TLRPC.TL_peerUser(); + peer.user_id = self; + return peer; + } + } + return null; + } + public boolean isSending() { return messageOwner.send_state == MESSAGE_SEND_STATE_SENDING && messageOwner.id < 0; } @@ -8460,7 +8537,7 @@ public boolean isInvoice() { public boolean isRoundVideo() { if (isRoundVideoCached == 0) { - isRoundVideoCached = type == TYPE_ROUND_VIDEO || isRoundVideoMessage(messageOwner) ? TYPE_PHOTO : TYPE_VOICE; + isRoundVideoCached = type == TYPE_ROUND_VIDEO || isRoundVideoMessage(messageOwner) ? 1 : 2; } return isRoundVideoCached == 1; } @@ -8700,6 +8777,17 @@ public boolean isForwarded() { } public boolean needDrawForwarded() { + if (isSaved) { + if (messageOwner == null || messageOwner.fwd_from == null) return false; + final long selfId = UserConfig.getInstance(currentAccount).getClientUserId(); + final long savedId = getSavedDialogId(selfId, messageOwner); + long fromId = DialogObject.getPeerDialogId(messageOwner.fwd_from.saved_from_peer); + if (fromId >= 0) { + fromId = DialogObject.getPeerDialogId(messageOwner.fwd_from.from_id); + } + if (fromId == 0) return savedId != UserObject.ANONYMOUS; + return savedId != fromId && fromId != selfId; + } if (type == MessageObject.TYPE_STORY && !isExpiredStory()) { return true; } @@ -9078,7 +9166,7 @@ public void checkMediaExistance(boolean useFileDatabaseQueue) { } if (!attachPathExists) { File file = FileLoader.getInstance(currentAccount).getPathToMessage(messageOwner, useFileDatabaseQueue); - if (type == TYPE_VIDEO && needDrawBluredPreview()) { + if (type == TYPE_VIDEO && needDrawBluredPreview() || isVoiceOnce() || isRoundOnce()) { mediaExists = new File(file.getAbsolutePath() + ".enc").exists(); } if (!mediaExists) { @@ -9328,6 +9416,7 @@ public boolean isReactionsAvailable() { public boolean selectReaction(ReactionsLayoutInBubble.VisibleReaction visibleReaction, boolean big, boolean fromDoubleTap) { if (messageOwner.reactions == null) { messageOwner.reactions = new TLRPC.TL_messageReactions(); + messageOwner.reactions.reactions_as_tags = MessageObject.getDialogId(messageOwner) == UserConfig.getInstance(currentAccount).getClientUserId(); messageOwner.reactions.can_see_list = isFromGroup() || isFromUser(); } @@ -9373,6 +9462,9 @@ public boolean selectReaction(ReactionsLayoutInBubble.VisibleReaction visibleRea if (newReaction.count <= 0) { messageOwner.reactions.results.remove(newReaction); } + if (messageOwner.reactions.reactions_as_tags) { + MessagesController.getInstance(currentAccount).updateSavedReactionTags(visibleReaction, false); + } } if (messageOwner.reactions.can_see_list) { for (int i = 0; i < messageOwner.reactions.recent_reactions.size(); i++) { @@ -9399,6 +9491,9 @@ public boolean selectReaction(ReactionsLayoutInBubble.VisibleReaction visibleRea if (choosenReaction.count <= 0) { messageOwner.reactions.results.remove(choosenReaction); } + if (messageOwner.reactions.reactions_as_tags) { + MessagesController.getInstance(currentAccount).updateSavedReactionTags(ReactionsLayoutInBubble.VisibleReaction.fromTLReaction(choosenReaction.reaction), false); + } choosenReactions.remove(choosenReaction); if (messageOwner.reactions.can_see_list) { @@ -9412,20 +9507,16 @@ public boolean selectReaction(ReactionsLayoutInBubble.VisibleReaction visibleRea } if (newReaction == null) { newReaction = new TLRPC.TL_reactionCount(); - if (visibleReaction.emojicon != null) { - newReaction.reaction = new TLRPC.TL_reactionEmoji(); - ((TLRPC.TL_reactionEmoji) newReaction.reaction).emoticon = visibleReaction.emojicon; - messageOwner.reactions.results.add(newReaction); - } else { - newReaction.reaction = new TLRPC.TL_reactionCustomEmoji(); - ((TLRPC.TL_reactionCustomEmoji) newReaction.reaction).document_id = visibleReaction.documentId; - messageOwner.reactions.results.add(newReaction); - } + newReaction.reaction = visibleReaction.toTLReaction(); + messageOwner.reactions.results.add(newReaction); } newReaction.chosen = true; newReaction.count++; newReaction.chosen_order = maxChoosenOrder + 1; + if (messageOwner.reactions.reactions_as_tags) { + MessagesController.getInstance(currentAccount).updateSavedReactionTags(visibleReaction, true); + } if (messageOwner.reactions.can_see_list || (messageOwner.dialog_id > 0 && maxReactionsCount > 1)) { TLRPC.TL_messagePeerReaction action = new TLRPC.TL_messagePeerReaction(); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index 2ecc719240..03faf00fe1 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -40,6 +40,8 @@ import androidx.core.graphics.ColorUtils; import androidx.core.util.Consumer; +import com.google.android.exoplayer2.util.Log; + import org.telegram.SQLite.SQLiteCursor; import org.telegram.SQLite.SQLiteDatabase; import org.telegram.SQLite.SQLiteException; @@ -69,6 +71,7 @@ import org.telegram.ui.Components.JoinCallAlert; import org.telegram.ui.Components.MotionBackgroundDrawable; import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; +import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.SwipeGestureSettingsView; import org.telegram.ui.Components.TranscribeButton; import org.telegram.ui.DialogsActivity; @@ -81,6 +84,8 @@ import org.telegram.ui.TopicsFragment; import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -133,9 +138,9 @@ public class MessagesController extends BaseController implements NotificationCe public LongSparseIntArray deletedHistory = new LongSparseIntArray(); public SparseArray dialogMessagesByIds = new SparseArray<>(); public ConcurrentHashMap>> printingUsers = new ConcurrentHashMap<>(20, 1.0f, 2); - public LongSparseArray> printingStrings = new LongSparseArray<>(); - public LongSparseArray> printingStringsTypes = new LongSparseArray<>(); - public LongSparseArray>[] sendingTypings = new LongSparseArray[12]; + public LongSparseArray> printingStrings = new LongSparseArray<>(); + public LongSparseArray> printingStringsTypes = new LongSparseArray<>(); + public LongSparseArray>[] sendingTypings = new LongSparseArray[12]; public ConcurrentHashMap onlinePrivacy = new ConcurrentHashMap<>(20, 1.0f, 2); private LongSparseIntArray pendingUnreadCounter = new LongSparseIntArray(); private int lastPrintingStringCount; @@ -146,6 +151,7 @@ public class MessagesController extends BaseController implements NotificationCe public int stealthModePast; public int stealthModeCooldown; public StoriesController storiesController; + public SavedMessagesController savedMessagesController; public UnconfirmedAuthController unconfirmedAuthController; private boolean hasArchivedChats; private boolean hasStories; @@ -276,8 +282,11 @@ public ChatlistUpdatesStat(TL_chatlists.TL_chatlists_chatlistUpdates value) { private LongSparseArray> reloadingWebpagesPending = new LongSparseArray<>(); private HashMap> reloadingScheduledWebpages = new HashMap<>(); private LongSparseArray> reloadingScheduledWebpagesPending = new LongSparseArray<>(); + private HashMap> reloadingSavedWebpages = new HashMap<>(); + private LongSparseArray> reloadingSavedWebpagesPending = new LongSparseArray<>(); private LongSparseArray lastScheduledServerQueryTime = new LongSparseArray<>(); + private LongSparseArray lastSavedServerQueryTime = new LongSparseArray<>(); private LongSparseArray lastServerQueryTime = new LongSparseArray<>(); private LongSparseArray> reloadingMessages = new LongSparseArray<>(); @@ -469,6 +478,7 @@ protected boolean useCache(Integer arguments) { public int updateCheckDelay; public int chatReadMarkSizeThreshold; public int chatReadMarkExpirePeriod; + public int pmReadDateExpirePeriod; public String mapKey; public int maxMessageLength; public int maxCaptionLength; @@ -571,6 +581,8 @@ protected boolean useCache(Integer arguments) { public int channelEmojiStatusLevelMin; public int channelWallpaperLevelMin; public int channelCustomWallpaperLevelMin; + public int savedDialogsPinnedLimitDefault; + public int savedDialogsPinnedLimitPremium; public int uploadMaxFileParts; public int uploadMaxFilePartsPremium; @@ -616,7 +628,7 @@ public boolean premiumPurchaseBlocked() { public int checkResetLangpack; - public void getNextReactionMention(long dialogId, int topicId, int count, Consumer callback) { + public void getNextReactionMention(long dialogId, long topicId, int count, Consumer callback) { final MessagesStorage messagesStorage = getMessagesStorage(); messagesStorage.getStorageQueue().postRunnable(() -> { boolean needRequest = true; @@ -949,6 +961,7 @@ public boolean equals(Object obj) { public void clearQueryTime() { lastServerQueryTime.clear(); lastScheduledServerQueryTime.clear(); + lastSavedServerQueryTime.clear(); } public static class DiceFrameSuccess { @@ -1364,7 +1377,7 @@ public MessagesController(int num) { chatReadMarkExpirePeriod = mainPreferences.getInt("chatReadMarkExpirePeriod", 7 * 86400); ringtoneDurationMax = mainPreferences.getInt("ringtoneDurationMax", 5); ringtoneSizeMax = mainPreferences.getInt("ringtoneSizeMax", 1024_00); - chatReadMarkExpirePeriod = mainPreferences.getInt("chatReadMarkExpirePeriod", 7 * 86400); + pmReadDateExpirePeriod = mainPreferences.getInt("pmReadDateExpirePeriod", 7 * 86400); suggestStickersApiOnly = mainPreferences.getBoolean("suggestStickersApiOnly", false); roundVideoSize = mainPreferences.getInt("roundVideoSize", 384); roundVideoBitrate = mainPreferences.getInt("roundVideoBitrate", 1000); @@ -1457,6 +1470,8 @@ public MessagesController(int num) { recommendedChannelsLimitDefault = mainPreferences.getInt("recommendedChannelsLimitDefault", 10); recommendedChannelsLimitPremium = mainPreferences.getInt("recommendedChannelsLimitPremium", 100); boostsChannelLevelMax = mainPreferences.getInt("boostsChannelLevelMax", 100); + savedDialogsPinnedLimitDefault = mainPreferences.getInt("savedDialogsPinnedLimitDefault", 4); + savedDialogsPinnedLimitPremium = mainPreferences.getInt("savedDialogsPinnedLimitPremium", 6); scheduleTranscriptionUpdate(); BuildVars.GOOGLE_AUTH_CLIENT_ID = mainPreferences.getString("googleAuthClientId", BuildVars.GOOGLE_AUTH_CLIENT_ID); if (mainPreferences.contains("dcDomainName2")) { @@ -2721,6 +2736,17 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { } break; } + case "pm_read_date_expire_period": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber number = (TLRPC.TL_jsonNumber) value.value; + if (number.value != pmReadDateExpirePeriod) { + pmReadDateExpirePeriod = (int) number.value; + editor.putInt("pmReadDateExpirePeriod", pmReadDateExpirePeriod); + changed = true; + } + } + break; + } case "inapp_update_check_delay": { if (value.value instanceof TLRPC.TL_jsonNumber) { TLRPC.TL_jsonNumber number = (TLRPC.TL_jsonNumber) value.value; @@ -3655,6 +3681,28 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { } break; } + case "saved_dialogs_pinned_limit_default": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value; + if (num.value != savedDialogsPinnedLimitDefault) { + savedDialogsPinnedLimitDefault = (int) num.value; + editor.putInt("savedDialogsPinnedLimitDefault", savedDialogsPinnedLimitDefault); + changed = true; + } + } + break; + } + case "saved_dialogs_pinned_limit_premium": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value; + if (num.value != savedDialogsPinnedLimitPremium) { + savedDialogsPinnedLimitPremium = (int) num.value; + editor.putInt("savedDialogsPinnedLimitPremium", savedDialogsPinnedLimitPremium); + changed = true; + } + } + break; + } } } @@ -4870,6 +4918,7 @@ public void didReceivedNotification(int id, int account, Object... args) { } } else if (id == NotificationCenter.currentUserPremiumStatusChanged) { loadAppConfig(false); + getContactsController().reloadContactsStatusesMaybe(true); } } @@ -4883,6 +4932,7 @@ public void cleanup() { getMediaDataController().cleanup(); getColorPalette().cleanup(); getTranslateController().cleanup(); + getSavedMessagesController().cleanup(); if (storiesController != null) { storiesController.cleanup(); } @@ -4947,11 +4997,14 @@ public void cleanup() { } lastScheduledServerQueryTime.clear(); + lastSavedServerQueryTime.clear(); lastServerQueryTime.clear(); reloadingWebpages.clear(); reloadingWebpagesPending.clear(); reloadingScheduledWebpages.clear(); reloadingScheduledWebpagesPending.clear(); + reloadingSavedWebpages.clear(); + reloadingSavedWebpagesPending.clear(); sponsoredMessages.clear(); sendAsPeers.clear(); dialogs_dict.clear(); @@ -5334,6 +5387,24 @@ public boolean putUser(TLRPC.User user, boolean fromCache, boolean force) { return false; } + public void reloadUser(long userId) { + TLRPC.TL_users_getUsers req = new TLRPC.TL_users_getUsers(); + TLRPC.InputUser inputPeer = getInputUser(userId); + if (inputPeer == null) return; + req.id.add(inputPeer); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> { + if (res instanceof TLRPC.Vector) { + ArrayList objects = ((TLRPC.Vector) res).objects; + ArrayList users = new ArrayList<>(); + for (int i = 0; i < objects.size(); ++i) { + if (objects.get(i) instanceof TLRPC.User) { + users.add((TLRPC.User) objects.get(i)); + } + } + getMessagesController().putUsers(users, false); + } + }); + } public void putUsers(ArrayList users, boolean fromCache) { if (users == null || users.isEmpty()) { return; @@ -6034,10 +6105,12 @@ public void loadFullUser(final TLRPC.User user, int classGuid, boolean force) { getConnectionsManager().bindRequestToGuid(reqId, classGuid); } - private void reloadMessages(ArrayList mids, long dialogId, boolean scheduled) { + private void reloadMessages(ArrayList mids, long dialogId, int mode) { if (mids.isEmpty()) { return; } + final boolean scheduled = mode == ChatActivity.MODE_SCHEDULED; + final boolean saved = mode == ChatActivity.MODE_SAVED; TLObject request; ArrayList result = new ArrayList<>(); TLRPC.Chat chat; @@ -6110,7 +6183,7 @@ private void reloadMessages(ArrayList mids, long dialogId, boolean sche } ImageLoader.saveMessagesThumbs(messagesRes.messages); - getMessagesStorage().putMessages(messagesRes, dialogId, -1, 0, false, scheduled, 0); + getMessagesStorage().putMessages(messagesRes, dialogId, -1, 0, false, mode, 0); AndroidUtilities.runOnUIThread(() -> { ArrayList arrayList1 = reloadingMessages.get(dialogId); @@ -7679,9 +7752,9 @@ public int getTotalDialogsCount() { } public void putAllNeededDraftDialogs() { - LongSparseArray> drafts = getMediaDataController().getDrafts(); + LongSparseArray> drafts = getMediaDataController().getDrafts(); for (int i = 0, size = drafts.size(); i < size; i++) { - SparseArray threads = drafts.valueAt(i); + LongSparseArray threads = drafts.valueAt(i); TLRPC.DraftMessage draftMessage = threads.get(0); if (draftMessage == null) { continue; @@ -7954,7 +8027,7 @@ protected void deleteDialog(long did, int first, int onlyHistory, int max_id, bo ArrayList arr = new ArrayList<>(); arr.add(message); updateInterfaceWithMessages(did, objArr, false); - getMessagesStorage().putMessages(arr, false, true, false, 0, false, 0); + getMessagesStorage().putMessages(arr, false, true, false, 0, false, 0, 0); } else { dialog.top_message = 0; } @@ -8076,6 +8149,51 @@ protected void deleteDialog(long did, int first, int onlyHistory, int max_id, bo } } + public void deleteSavedDialog(long did) { + deleteSavedDialog(did, 0); + } + + protected void deleteSavedDialog(long did, int input_max_id) { + int[] max_id = new int[] { input_max_id }; + Runnable perform = () -> { + getMessagesStorage().deleteSavedDialog(did); + TLRPC.TL_messages_deleteSavedHistory req = new TLRPC.TL_messages_deleteSavedHistory(); + req.peer = getInputPeer(did); + if (input_max_id == 0) { + SavedMessagesController.SavedDialog dialog = null; + for (int i = 0; i < getSavedMessagesController().allDialogs.size(); ++i) { + if (getSavedMessagesController().allDialogs.get(i).dialogId == did) { + dialog = getSavedMessagesController().allDialogs.get(i); + break; + } + } + if (dialog != null) { + max_id[0] = Math.max(max_id[0], dialog.top_message_id); + getSavedMessagesController().deleteDialog(did); + } + req.max_id = max_id[0] <= 0 ? Integer.MAX_VALUE : max_id[0]; + } + getConnectionsManager().sendRequest(req, (response, error) -> { + if (error == null) { + TLRPC.TL_messages_affectedHistory res = (TLRPC.TL_messages_affectedHistory) response; + if (res.offset > 0) { + deleteSavedDialog(did, max_id[0]); + } + processNewDifferenceParams(-1, res.pts, -1, res.pts_count); + getMessagesStorage().onDeleteQueryComplete(did); + } + }, ConnectionsManager.RequestFlagInvokeAfter); + }; + if (max_id[0] <= 0) { + getMessagesStorage().getSavedDialogMaxMessageId(did, (param) -> { + max_id[0] = param; + perform.run(); + }); + } else { + perform.run(); + } + } + public void saveGif(Object parentObject, TLRPC.Document document) { if (parentObject == null || !MessageObject.isGifDocument(document)) { return; @@ -8863,8 +8981,8 @@ private String getUserNameForTyping(TLRPC.User user) { } private void updatePrintingStrings() { - LongSparseArray> newStrings = new LongSparseArray<>(); - LongSparseArray> newTypes = new LongSparseArray<>(); + LongSparseArray> newStrings = new LongSparseArray<>(); + LongSparseArray> newTypes = new LongSparseArray<>(); for (HashMap.Entry>> dialogEntry : printingUsers.entrySet()) { Long key = dialogEntry.getKey(); @@ -8875,8 +8993,8 @@ private void updatePrintingStrings() { Integer threadId = threadEntry.getKey(); ArrayList arr = threadEntry.getValue(); - SparseArray newPrintingStrings = new SparseArray<>(); - SparseArray newPrintingStringsTypes = new SparseArray<>(); + LongSparseArray newPrintingStrings = new LongSparseArray<>(); + LongSparseArray newPrintingStringsTypes = new LongSparseArray<>(); newStrings.put(key, newPrintingStrings); newTypes.put(key, newPrintingStringsTypes); @@ -9045,12 +9163,12 @@ private void updatePrintingStrings() { }); } - public void cancelTyping(int action, long dialogId, int threadMsgId) { + public void cancelTyping(int action, long dialogId, long threadMsgId) { if (action < 0 || action >= sendingTypings.length || sendingTypings[action] == null) { return; } - LongSparseArray> dialogs = sendingTypings[action]; - SparseArray threads = dialogs.get(dialogId); + LongSparseArray> dialogs = sendingTypings[action]; + LongSparseArray threads = dialogs.get(dialogId); if (threads == null) { return; } @@ -9060,16 +9178,20 @@ public void cancelTyping(int action, long dialogId, int threadMsgId) { } } - public boolean sendTyping(long dialogId, int threadMsgId, int action, int classGuid) { + public boolean sendTyping(long dialogId, long threadMsgId, int action, int classGuid) { return sendTyping(dialogId, threadMsgId, action, null, classGuid); } - public boolean sendTyping(long dialogId, int threadMsgId, int action, String emojicon, int classGuid) { + public boolean sendTyping(long dialogId, long threadMsgId, int action, String emojicon, int classGuid) { if (action < 0 || action >= sendingTypings.length || dialogId == 0) { return false; } + final long selfId = UserConfig.getInstance(UserConfig.selectedAccount).getClientUserId(); + if (dialogId == selfId) { + return false; + } if (dialogId < 0) { - if (ChatObject.getSendAsPeerId(getChat(-dialogId), getChatFull(-dialogId)) != UserConfig.getInstance(UserConfig.selectedAccount).getClientUserId()) { + if (ChatObject.getSendAsPeerId(getChat(-dialogId), getChatFull(-dialogId)) != selfId) { return false; } } else { @@ -9086,13 +9208,13 @@ public boolean sendTyping(long dialogId, int threadMsgId, int action, String emo } } } - LongSparseArray> dialogs = sendingTypings[action]; + LongSparseArray> dialogs = sendingTypings[action]; if (dialogs == null) { dialogs = sendingTypings[action] = new LongSparseArray<>(); } - SparseArray threads = dialogs.get(dialogId); + LongSparseArray threads = dialogs.get(dialogId); if (threads == null) { - dialogs.put(dialogId, threads = new SparseArray<>()); + dialogs.put(dialogId, threads = new LongSparseArray<>()); } if (threads.get(threadMsgId) != null) { return false; @@ -9100,7 +9222,7 @@ public boolean sendTyping(long dialogId, int threadMsgId, int action, String emo if (!DialogObject.isEncryptedDialog(dialogId)) { TLRPC.TL_messages_setTyping req = new TLRPC.TL_messages_setTyping(); if (threadMsgId != 0) { - req.top_msg_id = threadMsgId; + req.top_msg_id = (int) threadMsgId; req.flags |= 1; } req.peer = getInputPeer(dialogId); @@ -9181,29 +9303,84 @@ protected void removeDeletedMessagesFromArray(final long dialogId, ArrayList { + if (response != null) { + TLRPC.messages_Messages res = (TLRPC.messages_Messages) response; + removeDeletedMessagesFromArray(dialogId, res.messages); + if (res.messages.size() > count) { + res.messages.remove(0); + } + int mid = max_id; + if (offset_date != 0 && !res.messages.isEmpty()) { + mid = res.messages.get(res.messages.size() - 1).id; + for (int a = res.messages.size() - 1; a >= 0; a--) { + TLRPC.Message message = res.messages.get(a); + if (message.date > offset_date) { + mid = message.id; + break; + } + } + } + processLoadedMessages(res, res.messages.size(), dialogId, mergeDialogId, count, mid, offset_date, false, classGuid, first_unread, last_message_id, unread_count, last_date, load_type, false, mode, threadMessageId, loadIndex, queryFromServer, mentionsCount, processMessages, isTopic, null); + } else { + AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.loadingMessagesFailed, classGuid, req, error)); + } + }); + getConnectionsManager().bindRequestToGuid(reqId, classGuid); + return; + } if (loadDialog && isTopic && load_type == 2 && last_message_id == 0) { TLRPC.TL_forumTopic topic = topicsController.findTopic(-dialogId, threadMessageId); if (topic != null) { - loadMessagesInternal(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, false, minDate, classGuid, load_type, topic.top_message, 0, threadMessageId, loadIndex, first_unread, topic.unread_count, last_date, queryFromServer, topic.unread_mentions_count, false, processMessages, isTopic, loaderLogger); + loadMessagesInternal(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, false, minDate, classGuid, load_type, topic.top_message, 0, threadMessageId, loadIndex, first_unread, topic.unread_count, last_date, queryFromServer, topic.unread_mentions_count, false, processMessages, isTopic, loaderLogger, 0L); return; } } @@ -9212,7 +9389,7 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa } TLRPC.TL_messages_getReplies req = new TLRPC.TL_messages_getReplies(); req.peer = getInputPeer(dialogId); - req.msg_id = threadMessageId; + req.msg_id = (int) threadMessageId; req.offset_date = offset_date; if (load_type == 4) { req.add_offset = -count + 5; @@ -9233,6 +9410,7 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa } req.limit = count; req.offset_id = max_id; + req.hash = hash; long time = System.currentTimeMillis(); int reqId = getConnectionsManager().sendRequest(req, (response, error) -> { if (response != null) { @@ -9262,17 +9440,17 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa } } } - processLoadedMessages(res, res.messages.size(), dialogId, mergeDialogId, count, mid, offset_date, false, classGuid, fnid, last_message_id, unread_count, last_date, load_type, false, 0, threadMessageId, loadIndex, queryFromServer, mentionsCount, processMessages, isTopic, null); + processLoadedMessages(res, res.messages.size(), dialogId, mergeDialogId, count, mid, offset_date, false, classGuid, fnid, last_message_id, unread_count, last_date, load_type, false, mode, threadMessageId, loadIndex, queryFromServer, mentionsCount, processMessages, isTopic, null); } else { AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.loadingMessagesFailed, classGuid, req, error)); } }); getConnectionsManager().bindRequestToGuid(reqId, classGuid); - } else if (mode == 2) { - } else if (mode == 1) { + } else if (mode == ChatActivity.MODE_PINNED) { + } else if (mode == ChatActivity.MODE_SCHEDULED) { TLRPC.TL_messages_getScheduledHistory req = new TLRPC.TL_messages_getScheduledHistory(); req.peer = getInputPeer(dialogId); - req.hash = minDate; + req.hash = hash; int reqId = getConnectionsManager().sendRequest(req, (response, error) -> { if (response != null) { TLRPC.messages_Messages res = (TLRPC.messages_Messages) response; @@ -9295,7 +9473,7 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa }); getConnectionsManager().bindRequestToGuid(reqId, classGuid); } else { - if (loadDialog && (load_type == 3 || load_type == 2) && last_message_id == 0) { + if (loadDialog && (load_type == LOAD_AROUND_MESSAGE || load_type == LOAD_FROM_UNREAD) && last_message_id == 0) { TLRPC.TL_messages_getPeerDialogs req = new TLRPC.TL_messages_getPeerDialogs(); TLRPC.InputPeer inputPeer = getInputPeer(dialogId); TLRPC.TL_inputDialogPeer inputDialogPeer = new TLRPC.TL_inputDialogPeer(); @@ -9317,7 +9495,7 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa getMessagesStorage().putDialogs(dialogs, 2); } - loadMessagesInternal(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, false, minDate, classGuid, load_type, dialog.top_message, 0, threadMessageId, loadIndex, first_unread, dialog.unread_count, last_date, queryFromServer, dialog.unread_mentions_count, false, processMessages, isTopic, null); + loadMessagesInternal(dialogId, mergeDialogId, loadInfo, count, max_id, offset_date, false, minDate, classGuid, load_type, dialog.top_message, 0, threadMessageId, loadIndex, first_unread, dialog.unread_count, last_date, queryFromServer, dialog.unread_mentions_count, false, processMessages, isTopic, null, 0L); } } else { AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.loadingMessagesFailed, classGuid, req, error)); @@ -9347,6 +9525,7 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa req.limit = count; req.offset_id = max_id; req.offset_date = offset_date; +// req.hash = hash; int reqId = getConnectionsManager().sendRequest(req, (response, error) -> { if (response != null) { TLRPC.messages_Messages res = (TLRPC.messages_Messages) response; @@ -9365,7 +9544,7 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa } } } - processLoadedMessages(res, res.messages.size(), dialogId, mergeDialogId, count, mid, offset_date, false, classGuid, first_unread, last_message_id, unread_count, last_date, load_type, false, 0, threadMessageId, loadIndex, queryFromServer, mentionsCount, processMessages, isTopic, null); + processLoadedMessages(res, res.messages.size(), dialogId, mergeDialogId, count, mid, offset_date, false, classGuid, first_unread, last_message_id, unread_count, last_date, load_type, false, mode, threadMessageId, loadIndex, queryFromServer, mentionsCount, processMessages, isTopic, null); } else { AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.loadingMessagesFailed, classGuid, req, error)); } @@ -9375,9 +9554,21 @@ private void loadMessagesInternal(long dialogId, long mergeDialogId, boolean loa } } - public void reloadWebPages(final long dialogId, HashMap> webpagesToReload, boolean scheduled) { - HashMap> map = scheduled ? reloadingScheduledWebpages : reloadingWebpages; - LongSparseArray> array = scheduled ? reloadingScheduledWebpagesPending : reloadingWebpagesPending; + public void reloadWebPages(final long dialogId, HashMap> webpagesToReload, int mode) { + final boolean scheduled = mode == ChatActivity.MODE_SCHEDULED; + final boolean saved = mode == ChatActivity.MODE_SAVED; + HashMap> map; + LongSparseArray> array; + if (scheduled) { + map = reloadingScheduledWebpages; + array = reloadingScheduledWebpagesPending; + } else if (saved) { + map = reloadingSavedWebpages; + array = reloadingSavedWebpagesPending; + } else { + map = reloadingWebpages; + array = reloadingWebpagesPending; + } for (HashMap.Entry> entry : webpagesToReload.entrySet()) { String url = entry.getKey(); @@ -9416,7 +9607,7 @@ public void reloadWebPages(final long dialogId, HashMap 60 * 1000); + } else if (mode == ChatActivity.MODE_SAVED) { + reload = resCount == 0 && (!isInitialLoading || (SystemClock.elapsedRealtime() - lastSavedServerQueryTime.get(threadMessageId, 0L)) > 60 * 1000 || isCache); } else { reload = resCount == 0 && (!isInitialLoading || (SystemClock.elapsedRealtime() - lastServerQueryTime.get(dialogId, 0L)) > 60 * 1000 || (isCache && isTopic)); } if (!DialogObject.isEncryptedDialog(dialogId) && isCache && reload) { - int hash; - if (mode == 2) { - hash = 0; - } else if (mode == 1) { + if (mode == ChatActivity.MODE_SCHEDULED) { lastScheduledServerQueryTime.put(dialogId, SystemClock.elapsedRealtime()); - long h = 0; + } else if (mode == ChatActivity.MODE_SAVED) { + lastSavedServerQueryTime.put(threadMessageId, SystemClock.elapsedRealtime()); + } else if (mode == ChatActivity.MODE_DEFAULT) { + lastServerQueryTime.put(dialogId, SystemClock.elapsedRealtime()); + } + long hash = 0; + if (mode == ChatActivity.MODE_SCHEDULED) { for (int a = 0, N = messagesRes.messages.size(); a < N; a++) { TLRPC.Message message = messagesRes.messages.get(a); if (message.id < 0) { continue; } - h = MediaDataController.calcHash(h, message.id); - h = MediaDataController.calcHash(h, message.edit_date); - h = MediaDataController.calcHash(h, message.date); + hash = MediaDataController.calcHash(hash, message.id); + hash = MediaDataController.calcHash(hash, message.edit_date); + hash = MediaDataController.calcHash(hash, message.date); + } + } else if (mode == ChatActivity.MODE_DEFAULT || mode == ChatActivity.MODE_SAVED) { + final boolean includePeers = true; + for (int a = 0, N = messagesRes.messages.size(); a < N; a++) { + TLRPC.Message message = messagesRes.messages.get(a); + if (message.id < 0) { + continue; + } + if (includePeers) { + hash = MediaDataController.calcHash(hash, Math.abs(DialogObject.getPeerDialogId(message.peer_id))); + } + hash = MediaDataController.calcHash(hash, message.id); + if (message.pinned) { + hash = MediaDataController.calcHash(hash, 1); + } + int date = message.date; + if ((message.flags & TLRPC.MESSAGE_FLAG_EDITED) != 0) { + date = message.edit_date; + } + hash = MediaDataController.calcHash(hash, date); } - hash = (int) h - 1; - } else { - lastServerQueryTime.put(dialogId, SystemClock.elapsedRealtime()); - hash = 0; } if (loaderLogger != null) { loaderLogger.reload(); } - AndroidUtilities.runOnUIThread(() -> loadMessagesInternal(dialogId, mergeDialogId, false, count, load_type == 2 && queryFromServer ? first_unread : max_id, offset_date, false, hash, classGuid, load_type, last_message_id, mode, threadMessageId, loadIndex, first_unread, unread_count, last_date, queryFromServer, mentionsCount, true, needProcess, isTopic, loaderLogger)); + final long finalHash = hash; + AndroidUtilities.runOnUIThread(() -> loadMessagesInternal(dialogId, mergeDialogId, false, count, load_type == LOAD_FROM_UNREAD && queryFromServer ? first_unread : max_id, offset_date, false, 0, classGuid, load_type, last_message_id, mode, threadMessageId, loadIndex, first_unread, unread_count, last_date, queryFromServer, mentionsCount, true, needProcess, isTopic, loaderLogger, finalHash)); if (messagesRes.messages.isEmpty()) { return; } @@ -9534,8 +9747,11 @@ public void processLoadedMessages(TLRPC.messages_Messages messagesRes, int resCo } } } - if (threadMessageId == 0 || isTopic) { - getMessagesStorage().putMessages(messagesRes, dialogId, load_type, max_id, createDialog, mode == 1, threadMessageId); + if (threadMessageId == 0 || isTopic || mode == ChatActivity.MODE_SAVED) { + getMessagesStorage().putMessages(messagesRes, dialogId, load_type, max_id, createDialog, mode, threadMessageId); + } + if (mode == ChatActivity.MODE_SAVED) { + getSavedMessagesController().update(threadMessageId, messagesRes); } } @@ -9552,7 +9768,7 @@ public void processLoadedMessages(TLRPC.messages_Messages messagesRes, int resCo TLRPC.Message message = messagesRes.messages.get(a); message.dialog_id = dialogId; long checkFileTime = SystemClock.elapsedRealtime(); - MessageObject messageObject = new MessageObject(currentAccount, message, usersDict, chatsDict, true, false); + MessageObject messageObject = new MessageObject(currentAccount, message, usersDict, chatsDict, true, false, mode == ChatActivity.MODE_SAVED); messageObject.scheduled = mode == 1; objects.add(messageObject); if (isCache) { @@ -9612,7 +9828,7 @@ public void processLoadedMessages(TLRPC.messages_Messages messagesRes, int resCo first_unread_final = 0; } else { first_unread_final = Integer.MAX_VALUE; - if (queryFromServer && load_type == 2) { + if (queryFromServer && load_type == LOAD_FROM_UNREAD) { for (int a = 0; a < messagesRes.messages.size(); a++) { TLRPC.Message message = messagesRes.messages.get(a); if ((!message.out || message.from_scheduled) && message.id > first_unread && message.id < first_unread_final) { @@ -9642,10 +9858,10 @@ public void processLoadedMessages(TLRPC.messages_Messages messagesRes, int resCo } if (!messagesToReload.isEmpty()) { - reloadMessages(messagesToReload, dialogId, mode == 1); + reloadMessages(messagesToReload, dialogId, mode); } if (!webpagesToReload.isEmpty()) { - reloadWebPages(dialogId, webpagesToReload, mode == 1); + reloadWebPages(dialogId, webpagesToReload, mode); } }); } @@ -11151,7 +11367,7 @@ public void processLoadedDialogs(final TLRPC.messages_Dialogs dialogsRes, ArrayL }); } - private void applyDialogNotificationsSettings(long dialogId, int topicId, TLRPC.PeerNotifySettings notify_settings) { + private void applyDialogNotificationsSettings(long dialogId, long topicId, TLRPC.PeerNotifySettings notify_settings) { getNotificationsController().getNotificationsSettingsFacade().applyDialogNotificationsSettings(dialogId, topicId, notify_settings); } @@ -11272,7 +11488,7 @@ public void processDialogsUpdateRead(final LongSparseIntArray dialogsToUpdate, L if (currentDialog != null) { currentDialog.unread_mentions_count = dialogsMentionsToUpdate.valueAt(a); if (createdDialogMainThreadIds.contains(currentDialog.id)) { - getNotificationCenter().postNotificationName(NotificationCenter.updateMentionsCount, currentDialog.id, 0, currentDialog.unread_mentions_count); + getNotificationCenter().postNotificationName(NotificationCenter.updateMentionsCount, currentDialog.id, 0L, currentDialog.unread_mentions_count); } if (!filterDialogsChanged) { for (int b = 0; b < selectedDialogFilter.length; b++) { @@ -11371,7 +11587,7 @@ protected void checkLastDialogMessage(TLRPC.Dialog dialog, TLRPC.InputPeer peer, dialogs.messages.addAll(res.messages); dialogs.count = 1; processDialogsUpdate(dialogs, null, false); - getMessagesStorage().putMessages(res.messages, true, true, false, getDownloadController().getAutodownloadMask(), true, false, 0); + getMessagesStorage().putMessages(res.messages, true, true, false, getDownloadController().getAutodownloadMask(), true, 0, 0); } else { AndroidUtilities.runOnUIThread(() -> { if (BuildVars.LOGS_ENABLED) { @@ -11556,12 +11772,12 @@ public void processDialogsUpdate(final TLRPC.messages_Dialogs dialogsRes, ArrayL if (currentDialog.unread_mentions_count != value.unread_mentions_count) { currentDialog.unread_mentions_count = value.unread_mentions_count; if (createdDialogMainThreadIds.contains(currentDialog.id)) { - getNotificationCenter().postNotificationName(NotificationCenter.updateMentionsCount, currentDialog.id, 0, currentDialog.unread_mentions_count); + getNotificationCenter().postNotificationName(NotificationCenter.updateMentionsCount, currentDialog.id, 0L, currentDialog.unread_mentions_count); } } if (currentDialog.unread_reactions_count != value.unread_reactions_count) { currentDialog.unread_reactions_count = value.unread_reactions_count; - getNotificationCenter().postNotificationName(NotificationCenter.dialogsUnreadReactionsCounterChanged, currentDialog.id, 0, currentDialog.unread_reactions_count, null); + getNotificationCenter().postNotificationName(NotificationCenter.dialogsUnreadReactionsCounterChanged, currentDialog.id, 0L, currentDialog.unread_reactions_count, null); } ArrayList oldMsgs = dialogMessage.get(key); boolean oldMsgsDeleted = false; @@ -11691,6 +11907,7 @@ private int messagesMaxDate(ArrayList messages) { } public void addToViewsQueue(MessageObject messageObject) { + if (messageObject == null) return; Utilities.stageQueue.postRunnable(() -> { long peer = messageObject.getDialogId(); int id = messageObject.getId(); @@ -11982,7 +12199,7 @@ private void checkReadTasks() { } } - public void markDialogAsReadNow(long dialogId, int replyId) { + public void markDialogAsReadNow(long dialogId, long replyId) { Utilities.stageQueue.postRunnable(() -> { if (replyId != 0) { String key = dialogId + "_" + replyId; @@ -12005,15 +12222,15 @@ public void markDialogAsReadNow(long dialogId, int replyId) { }); } - public void markMentionsAsRead(long dialogId, int topicId) { - if (DialogObject.isEncryptedDialog(dialogId)) { + public void markMentionsAsRead(long dialogId, long topicId) { + if (DialogObject.isEncryptedDialog(dialogId) || dialogId == getUserConfig().getClientUserId()) { return; } getMessagesStorage().resetMentionsCount(dialogId, topicId, 0); TLRPC.TL_messages_readMentions req = new TLRPC.TL_messages_readMentions(); req.peer = getInputPeer(dialogId); if (topicId != 0) { - req.top_msg_id = topicId; + req.top_msg_id = (int) topicId; req.flags |= 1; } getConnectionsManager().sendRequest(req, (response, error) -> { @@ -12021,7 +12238,7 @@ public void markMentionsAsRead(long dialogId, int topicId) { }); } - public void markDialogAsRead(long dialogId, int maxPositiveId, int maxNegativeId, int maxDate, boolean popup, int threadId, int countDiff, boolean readNow, int scheduledCount) { + public void markDialogAsRead(long dialogId, int maxPositiveId, int maxNegativeId, int maxDate, boolean popup, long threadId, int countDiff, boolean readNow, int scheduledCount) { boolean createReadTask; if (threadId != 0) { @@ -13715,7 +13932,7 @@ protected void getChannelDifference(long channelId, int newDialogType, long task if (!pushMessages.isEmpty()) { AndroidUtilities.runOnUIThread(() -> getNotificationsController().processNewMessages(pushMessages, true, false, null)); } - getMessagesStorage().putMessages(res.new_messages, true, false, false, getDownloadController().getAutodownloadMask(), false, 0); + getMessagesStorage().putMessages(res.new_messages, true, false, false, getDownloadController().getAutodownloadMask(), 0, 0); }); } @@ -13984,7 +14201,7 @@ public void getDifference(int pts, int date, int qts, boolean slice) { if (!pushMessages.isEmpty()) { AndroidUtilities.runOnUIThread(() -> getNotificationsController().processNewMessages(pushMessages, !(res instanceof TLRPC.TL_updates_differenceSlice), false, null)); } - getMessagesStorage().putMessages(res.new_messages, true, false, false, getDownloadController().getAutodownloadMask(), false, 0); + getMessagesStorage().putMessages(res.new_messages, true, false, false, getDownloadController().getAutodownloadMask(), 0, 0); for (int a = 0; a < messages.size(); a++) { long dialogId = messages.keyAt(a); @@ -14505,8 +14722,7 @@ public void generateJoinMessage(long chatId, boolean ignoreLeft) { MessageObject obj = new MessageObject(currentAccount, message, true, false); pushMessages.add(obj); - getMessagesStorage().getStorageQueue().postRunnable(() -> AndroidUtilities.runOnUIThread(() -> getNotificationsController().processNewMessages(pushMessages, true, false, null))); - getMessagesStorage().putMessages(messagesArr, true, true, false, 0, false, 0); + getMessagesStorage().putMessages(messagesArr, true, true, false, 0, 0, 0); AndroidUtilities.runOnUIThread(() -> { updateInterfaceWithMessages(-chatId, pushMessages, false); @@ -14604,7 +14820,7 @@ public void checkChatInviter(long chatId, boolean createMessage) { MessageObject obj = new MessageObject(currentAccount, message, usersDict, true, false); pushMessages.add(obj); getMessagesStorage().getStorageQueue().postRunnable(() -> AndroidUtilities.runOnUIThread(() -> getNotificationsController().processNewMessages(pushMessages, true, false, null))); - getMessagesStorage().putMessages(messagesArr, true, true, false, 0, false, 0); + getMessagesStorage().putMessages(messagesArr, true, true, false, 0, 0, 0); } else { pushMessages = null; } @@ -14949,7 +15165,7 @@ public void processUpdates(final TLRPC.Updates updates, boolean fromQueue) { if (!obj.isOut()) { getMessagesStorage().getStorageQueue().postRunnable(() -> AndroidUtilities.runOnUIThread(() -> getNotificationsController().processNewMessages(objArr, true, false, null))); } - getMessagesStorage().putMessages(arr, false, true, false, 0, false, 0); + getMessagesStorage().putMessages(arr, false, true, false, 0, 0, 0); } else if (getMessagesStorage().getLastPtsValue() != updates.pts) { if (BuildVars.LOGS_ENABLED) { FileLog.d("need get diff short message, pts: " + getMessagesStorage().getLastPtsValue() + " " + updates.pts + " count = " + updates.pts_count); @@ -16402,12 +16618,12 @@ public boolean processUpdateArray(ArrayList updates, ArrayList(); } updatesOnMainThread.add(baseUpdate); - }if (baseUpdate instanceof TLRPC.TL_updateFavedStickers) { + } else if (baseUpdate instanceof TLRPC.TL_updateFavedStickers) { if (updatesOnMainThread == null) { updatesOnMainThread = new ArrayList<>(); } @@ -16579,6 +16795,16 @@ public boolean processUpdateArray(ArrayList updates, ArrayList(); } updatesOnMainThread.add(baseUpdate); + } else if (baseUpdate instanceof TLRPC.TL_updatePinnedSavedDialogs) { + if (updatesOnMainThread == null) { + updatesOnMainThread = new ArrayList<>(); + } + updatesOnMainThread.add(baseUpdate); + } else if (baseUpdate instanceof TLRPC.TL_updateSavedDialogPinned) { + if (updatesOnMainThread == null) { + updatesOnMainThread = new ArrayList<>(); + } + updatesOnMainThread.add(baseUpdate); } } if (messages != null) { @@ -16608,12 +16834,12 @@ public boolean processUpdateArray(ArrayList updates, ArrayList updates, ArrayList> editingMessagesFinal = editingMessages; getMessagesStorage().getStorageQueue().postRunnable(() -> AndroidUtilities.runOnUIThread(() -> { @@ -16870,7 +17096,7 @@ public boolean processUpdateArray(ArrayList updates, ArrayList updates, ArrayList updates, ArrayList updates, ArrayList> map = i == 1 ? reloadingScheduledWebpages : reloadingWebpages; - LongSparseArray> array = i == 1 ? reloadingScheduledWebpagesPending : reloadingWebpagesPending; + for (int i = 0; i < 3; i++) { + HashMap> map; + LongSparseArray> array; + int mode = 0; + if (i == 1) { + mode = ChatActivity.MODE_SCHEDULED; + map = reloadingScheduledWebpages; + array = reloadingScheduledWebpagesPending; + } else if (i == 2) { + mode = ChatActivity.MODE_SAVED; + map = reloadingSavedWebpages; + array = reloadingSavedWebpagesPending; + } else { + map = reloadingWebpages; + array = reloadingWebpagesPending; + } for (int b = 0, size = webPagesFinal.size(); b < size; b++) { long key = webPagesFinal.keyAt(b); @@ -17467,7 +17710,7 @@ public boolean processUpdateArray(ArrayList updates, ArrayList updates, ArrayList updates, ArrayList { boolean needReload = false; boolean changed = false; @@ -17873,7 +18116,7 @@ public void checkUnreadReactions(long dialogId, int topicId, SparseBooleanArray state.bindInteger(1, messageId); state.bindInteger(2, hasUnreadReaction ? 1 : 0); state.bindLong(3, dialogId); - state.bindInteger(4, topicId); + state.bindLong(4, topicId); state.step(); state.dispose(); } @@ -17905,7 +18148,7 @@ public void checkUnreadReactions(long dialogId, int topicId, SparseBooleanArray }); } else { TLRPC.TL_channels_getForumTopicsByID req = new TLRPC.TL_channels_getForumTopicsByID(); - req.topics.add(topicId); + req.topics.add((int) topicId); req.channel = getMessagesController().getInputChannel(-dialogId); ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { if (response != null) { @@ -17944,15 +18187,15 @@ public void checkUnreadReactions(long dialogId, int topicId, SparseBooleanArray }); } - public boolean isDialogMuted(long dialogId, int topicId) { + public boolean isDialogMuted(long dialogId, long topicId) { return isDialogMuted(dialogId, topicId, null); } - public boolean isDialogNotificationsSoundEnabled(long dialogId, int topicId) { + public boolean isDialogNotificationsSoundEnabled(long dialogId, long topicId) { return notificationsPreferences.getBoolean("sound_enabled_" + NotificationsController.getSharedPrefKey(dialogId, topicId), true); } - public boolean isDialogMuted(long dialogId, int topicId, TLRPC.Chat chat) { + public boolean isDialogMuted(long dialogId, long topicId, TLRPC.Chat chat) { int mute_type = notificationsPreferences.getInt("notify2_" + NotificationsController.getSharedPrefKey(dialogId, topicId), -1); if (mute_type == -1) { Boolean forceChannel; @@ -17978,7 +18221,7 @@ public boolean isDialogMuted(long dialogId, int topicId, TLRPC.Chat chat) { return false; } - public void markReactionsAsRead(long dialogId, int topicId) { + public void markReactionsAsRead(long dialogId, long topicId) { if (topicId == 0) { TLRPC.Dialog dialog = dialogs_dict.get(dialogId); if (dialog != null) { @@ -17991,7 +18234,7 @@ public void markReactionsAsRead(long dialogId, int topicId) { TLRPC.TL_messages_readReactions req = new TLRPC.TL_messages_readReactions(); req.peer = getInputPeer(dialogId); if (topicId != 0) { - req.top_msg_id = topicId; + req.top_msg_id = (int) topicId; } getConnectionsManager().sendRequest(req, (response, error) -> { @@ -18162,22 +18405,22 @@ public TLRPC.Peer getSendAsSelectedPeer(long dialogId) { return peerUser; } - public CharSequence getPrintingString(long dialogId, int threadId, boolean isDialog) { + public CharSequence getPrintingString(long dialogId, long threadId, boolean isDialog) { if (isDialog && DialogObject.isUserDialog(dialogId)) { TLRPC.User user = getUser(dialogId); if (user != null && user.status != null && user.status.expires < 0) { return null; } } - SparseArray threads = printingStrings.get(dialogId); + LongSparseArray threads = printingStrings.get(dialogId); if (threads == null) { return null; } return threads.get(threadId); } - public Integer getPrintingStringType(long dialogId, int threadId) { - SparseArray threads = printingStringsTypes.get(dialogId); + public Integer getPrintingStringType(long dialogId, long threadId) { + LongSparseArray threads = printingStringsTypes.get(dialogId); if (threads == null) { return null; } @@ -19037,9 +19280,9 @@ public void didReceivedNotification(int id, int account, Object... args) { int lastMessageId = (int) args[4]; if ((size < count / 2 && !isEnd) && isCache) { if (finalMessageId != 0) { - loadMessagesInternal(dialogId, 0, false, count, finalMessageId, 0, false, 0, classGuid, 3, lastMessageId, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null); + loadMessagesInternal(dialogId, 0, false, count, finalMessageId, 0, false, 0, classGuid, 3, lastMessageId, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null, 0L); } else { - loadMessagesInternal(dialogId, 0, false, count, finalMessageId, 0, false, 0, classGuid, 2, lastMessageId, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null); + loadMessagesInternal(dialogId, 0, false, count, finalMessageId, 0, false, 0, classGuid, 2, lastMessageId, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null, 0L); } } else { getNotificationCenter().removeObserver(this, NotificationCenter.messagesDidLoadWithoutProcess); @@ -19063,9 +19306,9 @@ public void didReceivedNotification(int id, int account, Object... args) { getNotificationCenter().addObserver(delegate, NotificationCenter.loadingMessagesFailed); if (messageId != 0) { - loadMessagesInternal(dialogId, 0, true, count, finalMessageId, 0, true, 0, classGuid, 3, 0, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null); + loadMessagesInternal(dialogId, 0, true, count, finalMessageId, 0, true, 0, classGuid, 3, 0, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null, 0L); } else { - loadMessagesInternal(dialogId, 0, true, count, finalMessageId, 0, true, 0, classGuid, 2, 0, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null); + loadMessagesInternal(dialogId, 0, true, count, finalMessageId, 0, true, 0, classGuid, 2, 0, 0, 0, -1, 0, 0, 0, false, 0, true, false, false, null, 0L); } } @@ -19142,7 +19385,7 @@ public void setCustomChatReactions(long chatId, int type, List r if (onSuccess != null) { onSuccess.run(); } - getNotificationCenter().postNotificationName(NotificationCenter.chatAvailableReactionsUpdated, chatId, 0); + getNotificationCenter().postNotificationName(NotificationCenter.chatAvailableReactionsUpdated, chatId, 0L); }); } else { AndroidUtilities.runOnUIThread(() -> { @@ -19184,7 +19427,7 @@ public void setChatReactions(long chatId, int type, List reactions) { full.available_reactions = req.available_reactions; getMessagesStorage().updateChatInfo(full, false); } - AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.chatAvailableReactionsUpdated, chatId, 0)); + AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.chatAvailableReactionsUpdated, chatId, 0L)); } }); } @@ -19386,7 +19629,7 @@ public void updateEmojiStatusUntil() { } } - public String getMutedString(long dialogId, int topicId) { + public String getMutedString(long dialogId, long topicId) { if (getMessagesController().isDialogMuted(dialogId, topicId)) { int mute_until = notificationsPreferences.getInt("notifyuntil_" + NotificationsController.getSharedPrefKey(dialogId, topicId), 0); if (mute_until >= getConnectionsManager().getCurrentTime()) { @@ -19598,6 +19841,19 @@ public StoriesController getStoriesController() { return storiesController; } + public SavedMessagesController getSavedMessagesController() { + if (savedMessagesController != null) { + return savedMessagesController; + } + synchronized (lockObjects[currentAccount]) { + if (savedMessagesController != null) { + return savedMessagesController; + } + savedMessagesController = new SavedMessagesController(currentAccount); + } + return savedMessagesController; + } + public UnconfirmedAuthController getUnconfirmedAuthController() { if (unconfirmedAuthController != null) { return unconfirmedAuthController; @@ -19704,6 +19960,129 @@ public ChannelRecommendations getChannelRecommendations(long chatId) { return rec; } + private boolean loadedReactionTags; + private TLRPC.TL_messages_savedReactionsTags reactionTags; + public void updateSavedReactionTags(ReactionsLayoutInBubble.VisibleReaction reaction, boolean add) { + if (reactionTags != null) { + boolean changed = false; + boolean found = false; + for (int i = 0; i < reactionTags.tags.size(); ++i) { + TLRPC.TL_savedReactionTag tag = reactionTags.tags.get(i); + if (reaction.isSame(tag.reaction)) { + found = true; + int wasCount = tag.count; + tag.count = Math.max(0, tag.count + (add ? +1 : -1)); + if (tag.count <= 0) { + reactionTags.tags.remove(i); + i--; + changed = true; + } else if (tag.count != wasCount) { + changed = true; + } + } + } + if (!found && add) { + TLRPC.TL_savedReactionTag tag = new TLRPC.TL_savedReactionTag(); + tag.reaction = reaction.toTLReaction(); + tag.count = 1; + reactionTags.tags.add(tag); + changed = true; + } + if (changed) { + Collections.sort(reactionTags.tags, (a, b) -> b.count - a.count); + long hash = 0; + for (int i = 0; i < reactionTags.tags.size(); ++i) { + TLRPC.TL_savedReactionTag tag = reactionTags.tags.get(i); + if (tag.count <= 0) continue; + if (tag.reaction instanceof TLRPC.TL_reactionEmoji) { + String md5 = Utilities.MD5(((TLRPC.TL_reactionEmoji) tag.reaction).emoticon); + hash = MediaDataController.calcHash(hash, Long.parseLong(md5.substring(0, 16), 16)); + } else if (tag.reaction instanceof TLRPC.TL_reactionCustomEmoji) { + hash = MediaDataController.calcHash(hash, ((TLRPC.TL_reactionCustomEmoji) tag.reaction).document_id); + } + if ((tag.flags & 1) != 0 && tag.title != null) { + hash = MediaDataController.calcHash(hash, Long.parseLong(Utilities.MD5(tag.title).substring(0, 16), 16)); + } + hash = MediaDataController.calcHash(hash, tag.count); + } + reactionTags.hash = hash; + saveSavedReactionsTags(reactionTags); + getNotificationCenter().postNotificationName(NotificationCenter.savedReactionTagsUpdate); + } + } + } + public TLRPC.TL_messages_savedReactionsTags getSavedReactionTags() { + if (loadedReactionTags) { + return reactionTags; + } + loadedReactionTags = true; + getMessagesStorage().getStorageQueue().postRunnable(() -> { + TLRPC.messages_SavedReactionTags result = null; + SQLiteDatabase database = getMessagesStorage().getDatabase(); + SQLiteCursor cursor = null; + try { + cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM saved_reaction_tags")); + if (cursor.next()) { + NativeByteBuffer data = cursor.byteBufferValue(0); + if (data != null) { + result = TLRPC.messages_SavedReactionTags.TLdeserialize(data, data.readInt32(true), true); + } + } + } catch (Exception e) { + FileLog.e(e); + } finally { + if (cursor != null) { + cursor.dispose(); + cursor = null; + } + } + + final TLRPC.messages_SavedReactionTags finalResult = result; + AndroidUtilities.runOnUIThread(() -> { + if (finalResult instanceof TLRPC.TL_messages_savedReactionsTags) { + reactionTags = (TLRPC.TL_messages_savedReactionsTags) finalResult; + getNotificationCenter().postNotificationName(NotificationCenter.savedReactionTagsUpdate); + } + + TLRPC.TL_messages_getSavedReactionTags req = new TLRPC.TL_messages_getSavedReactionTags(); + if (finalResult instanceof TLRPC.TL_messages_savedReactionsTags) { + req.hash = finalResult.hash; + } + getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TLRPC.TL_messages_savedReactionsTags) { + reactionTags = (TLRPC.TL_messages_savedReactionsTags) res; + getNotificationCenter().postNotificationName(NotificationCenter.savedReactionTagsUpdate); + saveSavedReactionsTags(reactionTags); + } + })); + }); + }); + return null; + } + + private void saveSavedReactionsTags(TLRPC.TL_messages_savedReactionsTags res) { + getMessagesStorage().getStorageQueue().postRunnable(() -> { + SQLiteDatabase database2 = getMessagesStorage().getDatabase(); + SQLitePreparedStatement state = null; + try { + database2.executeFast("DELETE FROM saved_reaction_tags WHERE 1").stepThis().dispose(); + state = database2.executeFast("REPLACE INTO saved_reaction_tags VALUES(?)"); + state.requery(); + NativeByteBuffer buffer = new NativeByteBuffer(res.getObjectSize()); + res.serializeToStream(buffer); + state.bindByteBuffer(1, buffer); + state.step(); + } catch (Exception e) { + FileLog.e(e); + } finally { + if (state != null) { + state.dispose(); + state = null; + } + } + }); + } + public void checkPeerColors(boolean force) { if (peerColors == null || peerColors.needUpdate() || force) { TLRPC.TL_help_getPeerColors req = new TLRPC.TL_help_getPeerColors(); @@ -19736,4 +20115,89 @@ public void checkPeerColors(boolean force) { }); } } + + private LongSparseArray cachedIsUserPremiumBlocked = new LongSparseArray<>(); + private HashSet loadingIsUserPremiumBlocked = new HashSet<>(); + public boolean isUserPremiumBlocked(long userId) { + return isUserPremiumBlocked(userId, false); + } + public boolean isUserPremiumBlocked(long userId, boolean cache) { + if (getUserConfig().isPremium()) { + return false; + } + Boolean cached = cachedIsUserPremiumBlocked.get(userId); + if (cached != null) + return cached; + TLRPC.User user = getUser(userId); + if (user != null && !user.contact_require_premium) + return false; + TLRPC.UserFull userFull = getUserFull(userId); + if (userFull != null) + return userFull.contact_require_premium; + if (getInputUser(userId) == null) + return false; + if (cache) + return false; + loadingIsUserPremiumBlocked.add(userId); + AndroidUtilities.cancelRunOnUIThread(requestIsUserPremiumBlockedRunnable); + AndroidUtilities.runOnUIThread(requestIsUserPremiumBlockedRunnable, 60); + return false; + } + + public void invalidateUserPremiumBlocked(long userId, int classGuid) { + if (loadingFullUsers.contains(userId)) { + return; + } + int index = loadedFullUsers.indexOfKey(userId); + if (index >= 0) { + loadedFullUsers.removeAt(index); + } + loadFullUser(getUser(userId), classGuid, true); + } + + private Runnable requestIsUserPremiumBlockedRunnable = this::requestIsUserPremiumBlocked; + private void requestIsUserPremiumBlocked() { + if (loadingIsUserPremiumBlocked.isEmpty()) return; + TLRPC.TL_users_getIsPremiumRequiredToContact req = new TLRPC.TL_users_getIsPremiumRequiredToContact(); + final ArrayList ids = new ArrayList<>(); + for (long userId : loadingIsUserPremiumBlocked) { + TLRPC.InputUser inputUser = getInputUser(userId); + if (inputUser != null) { + req.id.add(inputUser); + ids.add(userId); + } + } + loadingIsUserPremiumBlocked.clear(); + if (req.id.isEmpty()) { + return; + } + getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + boolean changed = false; + if (res instanceof TLRPC.Vector) { + ArrayList objects = ((TLRPC.Vector) res).objects; + for (int i = 0; i < Math.min(ids.size(), objects.size()); ++i) { + long userId = ids.get(i); + boolean blocked = objects.get(i) instanceof TLRPC.TL_boolTrue; + Boolean pastBlocked = cachedIsUserPremiumBlocked.get(userId); + if (pastBlocked == null || pastBlocked != blocked) { + cachedIsUserPremiumBlocked.put(userId, blocked); + changed = true; + } + + TLRPC.UserFull userFull = getUserFull(userId); + if (userFull != null && userFull.contact_require_premium != blocked) { + userFull.contact_require_premium = blocked; + getMessagesStorage().updateUserInfo(userFull, true); + changed = true; + } else if (userFull == null) { + getMessagesStorage().updateUserInfoPremiumBlocked(userId, blocked); + changed = true; + } + } + } + if (changed) { + getNotificationCenter().postNotificationName(NotificationCenter.userIsPremiumBlockedUpadted); + } + })); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java index ace1a6c242..726e9b46d0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesStorage.java @@ -8,6 +8,12 @@ package org.telegram.messenger; +import static org.telegram.messenger.MessagesController.LOAD_AROUND_DATE; +import static org.telegram.messenger.MessagesController.LOAD_AROUND_MESSAGE; +import static org.telegram.messenger.MessagesController.LOAD_BACKWARD; +import static org.telegram.messenger.MessagesController.LOAD_FORWARD; +import static org.telegram.messenger.MessagesController.LOAD_FROM_UNREAD; + import android.appwidget.AppWidgetManager; import android.content.SharedPreferences; import android.os.SystemClock; @@ -36,6 +42,7 @@ import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Adapters.DialogsSearchAdapter; +import org.telegram.ui.ChatActivity; import org.telegram.ui.DialogsActivity; import org.telegram.ui.EditWidgetActivity; @@ -97,7 +104,7 @@ public class MessagesStorage extends BaseController { } } - public final static int LAST_DB_VERSION = 136; + public final static int LAST_DB_VERSION = 139; private boolean databaseMigrationInProgress; public boolean showClearDatabaseAlert; private LongSparseIntArray dialogIsForum = new LongSparseIntArray(); @@ -494,6 +501,7 @@ private boolean recoverDatabase() { "emoji_statuses", "messages_holes_topics", "messages_topics", + "saved_dialogs", "media_topics", "media_holes_topics", "topics", @@ -527,6 +535,12 @@ public static void createTables(SQLiteDatabase database) throws SQLiteException database.executeFast("CREATE INDEX IF NOT EXISTS idx_to_reply_messages_v2 ON messages_v2(reply_to_message_id, mid);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS uid_mid_groupid_messages_v2 ON messages_v2(uid, mid, group_id);").stepThis().dispose(); + database.executeFast("CREATE TABLE saved_dialogs(did INTEGER PRIMARY KEY, date INTEGER, last_mid INTEGER, pinned INTEGER, flags INTEGER, folder_id INTEGER, last_mid_group INTEGER, count INTEGER)").stepThis().dispose(); + database.executeFast("CREATE INDEX IF NOT EXISTS date_idx_dialogs ON saved_dialogs(date);").stepThis().dispose(); + database.executeFast("CREATE INDEX IF NOT EXISTS last_mid_idx_dialogs ON saved_dialogs(last_mid);").stepThis().dispose(); + database.executeFast("CREATE INDEX IF NOT EXISTS folder_id_idx_dialogs ON saved_dialogs(folder_id);").stepThis().dispose(); + database.executeFast("CREATE INDEX IF NOT EXISTS flags_idx_dialogs ON saved_dialogs(flags);").stepThis().dispose(); + database.executeFast("CREATE TABLE download_queue(uid INTEGER, type INTEGER, date INTEGER, data BLOB, parent TEXT, PRIMARY KEY (uid, type));").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS type_date_idx_download_queue ON download_queue(type, date);").stepThis().dispose(); @@ -623,7 +637,7 @@ public static void createTables(SQLiteDatabase database) throws SQLiteException database.executeFast("CREATE TABLE wallpapers2(uid INTEGER PRIMARY KEY, data BLOB, num INTEGER)").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS wallpapers_num ON wallpapers2(num);").stepThis().dispose(); - database.executeFast("CREATE TABLE unread_push_messages(uid INTEGER, mid INTEGER, random INTEGER, date INTEGER, data BLOB, fm TEXT, name TEXT, uname TEXT, flags INTEGER, topicId INTEGER, PRIMARY KEY(uid, mid))").stepThis().dispose(); + database.executeFast("CREATE TABLE unread_push_messages(uid INTEGER, mid INTEGER, random INTEGER, date INTEGER, data BLOB, fm TEXT, name TEXT, uname TEXT, flags INTEGER, topicId INTEGER, is_reaction INTEGER, PRIMARY KEY(uid, mid))").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS unread_push_messages_idx_date ON unread_push_messages(date);").stepThis().dispose(); database.executeFast("CREATE INDEX IF NOT EXISTS unread_push_messages_idx_random ON unread_push_messages(random);").stepThis().dispose(); @@ -690,6 +704,8 @@ public static void createTables(SQLiteDatabase database) throws SQLiteException database.executeFast("CREATE TABLE unconfirmed_auth (data BLOB);").stepThis().dispose(); + database.executeFast("CREATE TABLE saved_reaction_tags (data BLOB);").stepThis().dispose(); + database.executeFast("PRAGMA user_version = " + MessagesStorage.LAST_DB_VERSION).stepThis().dispose(); } @@ -1277,7 +1293,7 @@ public void putPushMessage(MessageObject message) { flags |= 2; } - SQLitePreparedStatement state = database.executeFast("REPLACE INTO unread_push_messages VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + SQLitePreparedStatement state = database.executeFast("REPLACE INTO unread_push_messages VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); state.requery(); state.bindLong(1, message.getDialogId()); state.bindInteger(2, message.getId()); @@ -1300,7 +1316,8 @@ public void putPushMessage(MessageObject message) { state.bindString(8, message.localUserName); } state.bindInteger(9, flags); - state.bindInteger(10, MessageObject.getTopicId(message.messageOwner, false)); + state.bindLong(10, MessageObject.getTopicId(currentAccount, message.messageOwner, false)); + state.bindInteger(11, message.isReactionPush ? 1 : 0); state.step(); data.reuse(); @@ -1328,6 +1345,7 @@ public void clearLocalDatabase() { database.executeFast("DELETE FROM stickersets2").stepThis().dispose(); database.executeFast("DELETE FROM messages_holes_topics").stepThis().dispose(); database.executeFast("DELETE FROM messages_topics").stepThis().dispose(); + database.executeFast("DELETE FROM saved_dialogs").stepThis().dispose(); database.executeFast("DELETE FROM topics").stepThis().dispose(); database.executeFast("DELETE FROM media_holes_topics").stepThis().dispose(); database.executeFast("DELETE FROM media_topics").stepThis().dispose(); @@ -1414,6 +1432,9 @@ public void clearLocalDatabase() { database.executeFast("PRAGMA journal_size_limit = -1").stepThis().dispose(); getMessagesController().getTopicsController().databaseCleared(); + AndroidUtilities.runOnUIThread(() -> { + getMessagesController().getSavedMessagesController().cleanup(); + }); } catch (Exception e) { checkSQLException(e); } finally { @@ -1878,6 +1899,58 @@ public void loadGroupedMessagesForTopics(long dialogId, ArrayList { + SQLiteCursor cursor = null; + int[] max = new int[1]; + try { + final long selfId = getUserConfig().getClientUserId(); + cursor = database.queryFinalized("SELECT MAX(mid) FROM messages_topics WHERE uid = ? AND topic_id = ?", selfId, dialog_id); + if (cursor.next()) { + max[0] = cursor.intValue(0); + } + } catch (Exception e) { + checkSQLException(e); + } finally { + if (cursor != null) { + cursor.dispose(); + } + } + AndroidUtilities.runOnUIThread(() -> callback.run(max[0])); + }); + } + + public void deleteSavedDialog(long did) { + storageQueue.postRunnable(() -> { + SQLiteCursor cursor = null; + try { + long selfId = getUserConfig().getClientUserId(); + cursor = database.queryFinalized("SELECT mid FROM messages_topics WHERE uid = ? AND topic_id = ?", selfId, did); + ArrayList mids = new ArrayList<>(); + while (cursor.next()) { + final int mid = cursor.intValue(0); + mids.add(mid); + } + cursor.dispose(); + cursor = null; + if (!mids.isEmpty()) { + database.executeFast("DELETE FROM messages_v2 WHERE uid = " + selfId + " AND mid IN ("+TextUtils.join(",", mids)+")").stepThis().dispose(); + database.executeFast("DELETE FROM messages_topics WHERE uid = " + selfId + " AND topic_id = " + did).stepThis().dispose(); + database.executeFast("DELETE FROM media_v4 WHERE uid = " + selfId + " AND mid IN ("+TextUtils.join(",", mids)+")").stepThis().dispose(); + } + if (!mids.isEmpty()) { + AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.messagesDeleted, mids, 0L, false)); + } + } catch (Exception e) { + checkSQLException(e); + } finally { + if (cursor != null) { + cursor.dispose(); + } + } + }); + } + public void removeTopic(long dialogId, int topicId) { storageQueue.postRunnable(() -> { try { @@ -1959,6 +2032,7 @@ public void fullReset() { AndroidUtilities.runOnUIThread(() -> { getNotificationCenter().postNotificationName(NotificationCenter.onDatabaseReset); getNotificationCenter().postNotificationName(NotificationCenter.didClearDatabase); + getMessagesController().getSavedMessagesController().cleanup(); }); }); @@ -3518,7 +3592,7 @@ public void loadUnreadMessages() { cursor = null; database.executeFast("DELETE FROM unread_push_messages WHERE date <= " + maxDate).stepThis().dispose(); - cursor = database.queryFinalized("SELECT data, mid, date, uid, random, fm, name, uname, flags, topicId FROM unread_push_messages WHERE 1 ORDER BY date DESC LIMIT 50"); + cursor = database.queryFinalized("SELECT data, mid, date, uid, random, fm, name, uname, flags, topicId, is_reaction FROM unread_push_messages WHERE 1 ORDER BY date DESC LIMIT 50"); while (cursor.next()) { NativeByteBuffer data = cursor.byteBufferValue(0); if (data != null) { @@ -3554,7 +3628,9 @@ public void loadUnreadMessages() { message.reply_to.reply_to_top_id = topicId; } - pushMessages.add(new MessageObject(currentAccount, message, messageText, name, userName, (flags & 1) != 0, (flags & 2) != 0, (message.flags & 0x80000000) != 0, false)); + MessageObject messageObject = new MessageObject(currentAccount, message, messageText, name, userName, (flags & 1) != 0, (flags & 2) != 0, (message.flags & 0x80000000) != 0, false); + messageObject.isReactionPush = cursor.intValue(10) != 0; + pushMessages.add(messageObject); addUsersAndChatsFromMessage(message, usersToLoad, chatsToLoad, null); } } @@ -4239,11 +4315,13 @@ public void emptyMessagesMedia(long dialogId, ArrayList mids) { storageQueue.postRunnable(() -> { SQLiteCursor cursor = null; SQLitePreparedStatement state = null; + SQLitePreparedStatement state_saved = null; try { ArrayList filesToDelete = new ArrayList<>(); ArrayList namesToDelete = new ArrayList<>(); ArrayList> idsToDelete = new ArrayList<>(); ArrayList messages = new ArrayList<>(); + ArrayList changedSavedMessages = null; cursor = database.queryFinalized(String.format(Locale.US, "SELECT data, mid, date, uid, custom_params FROM messages_v2 WHERE mid IN (%s) AND uid = %d", TextUtils.join(",", mids), dialogId)); while (cursor.next()) { NativeByteBuffer data = cursor.byteBufferValue(0); @@ -4359,8 +4437,10 @@ public void emptyMessagesMedia(long dialogId, ArrayList mids) { storyData.reuse(); } } - state.dispose(); - state = null; + if (state != null) { + state.dispose(); + state = null; + } AndroidUtilities.runOnUIThread(() -> { for (int a = 0; a < messages.size(); a++) { getNotificationCenter().postNotificationName(NotificationCenter.updateMessageMedia, messages.get(a)); @@ -4369,6 +4449,14 @@ public void emptyMessagesMedia(long dialogId, ArrayList mids) { } AndroidUtilities.runOnUIThread(() -> getFileLoader().cancelLoadFiles(namesToDelete)); getFileLoader().deleteFiles(filesToDelete, 0); + if (changedSavedMessages != null) { + final ArrayList finalChangedSavedMessages = changedSavedMessages; + AndroidUtilities.runOnUIThread(() -> { + if (getMessagesController().getSavedMessagesController().updateSavedDialogs(finalChangedSavedMessages)) { + getMessagesController().getSavedMessagesController().update(); + } + }); + } } catch (Exception e) { checkSQLException(e); } finally { @@ -4414,12 +4502,14 @@ public void updateMessagePollResults(long pollId, TLRPC.Poll poll, TLRPC.PollRes boolean foundMessage = false; for (int k = 0; k < 2; k++) { boolean isTopic = k == 1; + SQLitePreparedStatement currentState; if (isTopic) { cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM messages_topics WHERE mid = %d AND uid = %d", mid, dialogId)); + currentState = state_topics; } else { cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM messages_v2 WHERE mid = %d AND uid = %d", mid, dialogId)); + currentState = state; } - SQLitePreparedStatement currentState = isTopic ? state_topics : state; if (cursor.next()) { NativeByteBuffer data = cursor.byteBufferValue(0); if (data != null) { @@ -4456,6 +4546,7 @@ public void updateMessagePollResults(long pollId, TLRPC.Poll poll, TLRPC.PollRes } } state.dispose(); + state_topics.dispose(); database.commitTransaction(); } } catch (Exception e) { @@ -4820,13 +4911,13 @@ public void markMentionMessageAsRead(long dialogId, int messageId, long did) { database.executeFast(String.format(Locale.US, "UPDATE messages_topics SET read_state = read_state | 2 WHERE mid = %d AND uid = %d", messageId, dialogId)).stepThis().dispose(); cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM messages_topics WHERE mid = %d AND uid = %d", messageId, dialogId)); - int topicId = 0; + long topicId = 0; while (cursor.next()) { NativeByteBuffer data = cursor.byteBufferValue(0); if (data != null) { TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); data.reuse(); - topicId = MessageObject.getTopicId(message, isForum(dialogId)); + topicId = MessageObject.getTopicId(currentAccount, message, isForum(dialogId)); } } cursor.dispose(); @@ -4866,7 +4957,7 @@ public void markMessageAsMention(long dialogId, int mid) { }); } - public void resetMentionsCount(long did, int topicId, int count) { + public void resetMentionsCount(long did, long topicId, int count) { storageQueue.postRunnable(() -> { SQLiteCursor cursor = null; try { @@ -5587,7 +5678,7 @@ private void updateFiltersReadCounter(LongSparseIntArray dialogsToUpdate, LongSp } if (filter != null) { if (!filter.alwaysShow.isEmpty()) { - if ((flags & MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_MUTED) != 0) { + if ((flags & MessagesController.DIALOG_FILTER_FLAG_EXCLUDE_MUTED) != 0 && dialogsToUpdateMentions != null) { for (int b = 0, N2 = dialogsToUpdateMentions.size(); b < N2; b++) { long did = dialogsToUpdateMentions.keyAt(b); TLRPC.Chat chat = chatsDict.get(-did); @@ -6312,6 +6403,51 @@ public void updateUserInfo(TLRPC.UserFull info, boolean ifExist) { }); } + public void updateUserInfoPremiumBlocked(long userId, boolean contact_require_premium) { + storageQueue.postRunnable(() -> { + SQLiteCursor cursor = null; + SQLitePreparedStatement state = null; + try { + TLRPC.UserFull userFull = null; + cursor = database.queryFinalized("SELECT uid, info, pinned FROM user_settings WHERE uid = " + userId); + boolean exist = cursor.next(); + if (exist) { + NativeByteBuffer data = cursor.byteBufferValue(1); + userFull = TLRPC.UserFull.TLdeserialize(data, data.readInt32(true), true); + if (userFull != null) { + userFull.pinned_msg_id = cursor.intValue(2); + } + data.reuse(); + } + cursor.dispose(); + cursor = null; + if (!exist || userFull == null || userFull.contact_require_premium == contact_require_premium) { + return; + } + userFull.contact_require_premium = contact_require_premium; + state = database.executeFast("REPLACE INTO user_settings VALUES(?, ?, ?)"); + NativeByteBuffer data = new NativeByteBuffer(userFull.getObjectSize()); + userFull.serializeToStream(data); + state.bindLong(1, userId); + state.bindByteBuffer(2, data); + state.bindInteger(3, userFull.pinned_msg_id); + state.step(); + state.dispose(); + state = null; + data.reuse(); + } catch (Exception e) { + checkSQLException(e); + } finally { + if (state != null) { + state.dispose(); + } + if (cursor != null) { + cursor.dispose(); + } + } + }); + } + public void saveChatInviter(long chatId, long inviterId) { storageQueue.postRunnable(() -> { SQLitePreparedStatement state = null; @@ -7647,7 +7783,7 @@ public boolean checkMessageId(long dialogId, int mid) { return result[0]; } - public void getUnreadMention(long dialog_id, int topicId, IntCallback callback) { + public void getUnreadMention(long dialog_id, long topicId, IntCallback callback) { storageQueue.postRunnable(() -> { SQLiteCursor cursor = null; try { @@ -7690,12 +7826,15 @@ public void getMessagesCount(long dialog_id, IntCallback callback) { } catch (Exception e) { checkSQLException(e); } finally { - cursor.dispose(); + if (cursor != null) { + cursor.dispose(); + cursor = null; + } } }); } - public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count, int max_id, int offset_date, int minDate, int classGuid, int load_type, boolean scheduled, int threadMessageId, int loadIndex, boolean processMessages, boolean isTopic, MessageLoaderLogger loaderLogger) { + public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count, int max_id, int offset_date, int minDate, int classGuid, int load_type, int mode, long threadMessageId, int loadIndex, boolean processMessages, boolean isTopic, MessageLoaderLogger loaderLogger) { TLRPC.TL_messages_messages res = new TLRPC.TL_messages_messages(); long currentUserId = getUserConfig().clientUserId; int count_unread = 0; @@ -7713,9 +7852,11 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count boolean isEnd = false; int num = dialogId == 777000 ? 10 : 1; int messagesCount = 0; + int totalMessagesCount = 0; int serviceUnreadCount = 0; long startLoadTime = SystemClock.elapsedRealtime(); SQLiteCursor cursor = null; + final boolean scheduled = mode == ChatActivity.MODE_SCHEDULED; try { ArrayList usersToLoad = new ArrayList<>(); ArrayList chatsToLoad = new ArrayList<>(); @@ -7793,7 +7934,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count cursor = null; } else { if (!DialogObject.isEncryptedDialog(dialogId)) { - if (load_type == 3 && minDate == 0) { + if (load_type == LOAD_AROUND_MESSAGE && minDate == 0) { if (threadMessageId == 0) { cursor = database.queryFinalized("SELECT inbox_max, unread_count, date, unread_count_i FROM dialogs WHERE did = " + dialogId); if (cursor.next()) { @@ -7813,8 +7954,8 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count cursor.dispose(); cursor = null; } - } else if (load_type != 1 && load_type != 3 && load_type != 4 && minDate == 0) { - if (load_type == 2) { + } else if (load_type != LOAD_FORWARD && load_type != LOAD_AROUND_MESSAGE && load_type != LOAD_AROUND_DATE && minDate == 0) { + if (load_type == LOAD_FROM_UNREAD) { if (threadMessageId == 0) { cursor = database.queryFinalized("SELECT inbox_max, unread_count, date, unread_count_i FROM dialogs WHERE did = " + dialogId); if (cursor.next()) { @@ -7958,7 +8099,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count state.requery(); state.bindLong(pointer++, dialogId); if (threadMessageId != 0) { - state.bindInteger(pointer++, threadMessageId); + state.bindLong(pointer++, threadMessageId); } state.bindInteger(pointer++, 0); state.bindInteger(pointer++, mid); @@ -7970,7 +8111,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count cursor.dispose(); cursor = null; - if (load_type == 3 || load_type == 4 || queryFromServer && load_type == 2) { + if (load_type == LOAD_AROUND_MESSAGE || load_type == LOAD_AROUND_DATE || queryFromServer && load_type == LOAD_FROM_UNREAD) { if (threadMessageId != 0) { cursor = database.queryFinalized(String.format(Locale.US, "SELECT max(mid) FROM messages_topics WHERE uid = %d AND topic_id = %d AND mid > 0", dialogId, threadMessageId)); } else { @@ -7982,7 +8123,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count cursor.dispose(); cursor = null; - if (load_type == 4 && offset_date != 0) { + if (load_type == LOAD_AROUND_DATE && offset_date != 0) { int startMid; int endMid; @@ -8104,7 +8245,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count } } } else { - if (load_type == 2) { + if (load_type == LOAD_FROM_UNREAD) { int existingUnreadCount = 0; if (threadMessageId != 0) { cursor = database.queryFinalized(String.format(Locale.US, "SELECT COUNT(*) FROM messages_topics WHERE uid = %d AND topic_id = %d AND mid != 0 AND out = 0 AND read_state IN(0,2)", dialogId, threadMessageId)); @@ -8128,7 +8269,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count } } } - } else if (load_type == 1) { + } else if (load_type == LOAD_FORWARD) { int holeMessageId = 0; if (threadMessageId != 0) { cursor = database.queryFinalized(String.format(Locale.US, "SELECT start, end FROM messages_holes_topics WHERE uid = %d AND topic_id = %d AND (start >= %d AND start != 1 AND end != 1 OR start < %d AND end > %d) ORDER BY start ASC LIMIT 1", dialogId, threadMessageId, max_id, max_id, max_id)); @@ -8228,7 +8369,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count } else { isEnd = true; - if (load_type == 3 && minDate == 0) { + if (load_type == LOAD_AROUND_MESSAGE && minDate == 0) { cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid) FROM messages_v2 WHERE uid = %d AND mid < 0", dialogId)); if (cursor.next()) { min_unread_id = cursor.intValue(0); @@ -8255,7 +8396,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count } } - if (load_type == 3 || load_type == 4) { + if (load_type == LOAD_AROUND_MESSAGE || load_type == LOAD_AROUND_DATE) { cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid) FROM messages_v2 WHERE uid = %d AND mid < 0", dialogId)); if (cursor.next()) { last_message_id = cursor.intValue(0); @@ -8265,7 +8406,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count cursor = database.queryFinalized(String.format(Locale.US, "SELECT * FROM (" + messageSelect + " WHERE m.uid = %d AND m.mid <= %d ORDER BY m.mid DESC LIMIT %d) UNION " + "SELECT * FROM (" + messageSelect + " WHERE m.uid = %d AND m.mid > %d ORDER BY m.mid ASC LIMIT %d)", dialogId, messageMaxId, count_query / 2, dialogId, messageMaxId, count_query / 2)); - } else if (load_type == 1) { + } else if (load_type == LOAD_FORWARD) { cursor = database.queryFinalized(String.format(Locale.US, "" + messageSelect + " WHERE m.uid = %d AND m.mid < %d ORDER BY m.mid DESC LIMIT %d", dialogId, max_id, count_query)); } else if (minDate != 0) { if (max_id != 0) { @@ -8274,7 +8415,7 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count cursor = database.queryFinalized(String.format(Locale.US, "" + messageSelect + " WHERE m.uid = %d AND m.date <= %d ORDER BY m.mid ASC LIMIT %d,%d", dialogId, minDate, offset_query, count_query)); } } else { - if (load_type == 2) { + if (load_type == LOAD_FROM_UNREAD) { cursor = database.queryFinalized(String.format(Locale.US, "SELECT min(mid) FROM messages_v2 WHERE uid = %d AND mid < 0", dialogId)); if (cursor.next()) { last_message_id = cursor.intValue(0); @@ -8593,11 +8734,11 @@ public Runnable getMessagesInternal(long dialogId, long mergeDialogId, int count }; } else {*/ int finalMessagesCount = scheduled ? res.messages.size() : messagesCount; - return () -> getMessagesController().processLoadedMessages(res, finalMessagesCount, dialogId, mergeDialogId, countQueryFinal, maxIdOverrideFinal, offset_date, true, classGuid, minUnreadIdFinal, lastMessageIdFinal, countUnreadFinal, maxUnreadDateFinal, load_type, isEndFinal, scheduled ? 1 : 0, threadMessageId, loadIndex, queryFromServerFinal, mentionsUnreadFinal, processMessages, isTopic, loaderLogger); + return () -> getMessagesController().processLoadedMessages(res, finalMessagesCount, dialogId, mergeDialogId, countQueryFinal, maxIdOverrideFinal, offset_date, true, classGuid, minUnreadIdFinal, lastMessageIdFinal, countUnreadFinal, maxUnreadDateFinal, load_type, isEndFinal, mode, threadMessageId, loadIndex, queryFromServerFinal, mentionsUnreadFinal, processMessages, isTopic, loaderLogger); //} } - private void getAnimatedEmoji(String join, ArrayList documents) { + public void getAnimatedEmoji(String join, ArrayList documents) { SQLiteCursor cursor = null; try { cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM animated_emoji WHERE document_id IN (%s)", join)); @@ -8624,12 +8765,12 @@ private void getAnimatedEmoji(String join, ArrayList documents) } } - public void getMessages(long dialogId, long mergeDialogId, boolean loadInfo, int count, int max_id, int offset_date, int minDate, int classGuid, int load_type, boolean scheduled, int replyMessageId, int loadIndex, boolean processMessages, boolean isTopic, MessageLoaderLogger loaderLogger) { + public void getMessages(long dialogId, long mergeDialogId, boolean loadInfo, int count, int max_id, int offset_date, int minDate, int classGuid, int load_type, int mode, long replyMessageId, int loadIndex, boolean processMessages, boolean isTopic, MessageLoaderLogger loaderLogger) { storageQueue.postRunnable(() -> { if (loaderLogger != null) { loaderLogger.logStorageQueuePost(); } - Runnable processMessagesRunnable = getMessagesInternal(dialogId, mergeDialogId, count, max_id, offset_date, minDate, classGuid, load_type, scheduled, replyMessageId, loadIndex, processMessages, isTopic, loaderLogger); + Runnable processMessagesRunnable = getMessagesInternal(dialogId, mergeDialogId, count, max_id, offset_date, minDate, classGuid, load_type, mode, replyMessageId, loadIndex, processMessages, isTopic, loaderLogger); if (loaderLogger != null) { loaderLogger.logStorageProccessing(); } @@ -9364,7 +9505,8 @@ private void putUsersInternal(List users) throws Exception { SQLitePreparedStatement state = database.executeFast("REPLACE INTO users VALUES(?, ?, ?, ?)"); for (int a = 0; a < users.size(); a++) { TLRPC.User user = users.get(a); - if (user != null && user.min) { + if (user == null) continue; + if (user.min) { SQLiteCursor cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM users WHERE uid = %d", user.id)); if (cursor.next()) { try { @@ -9405,11 +9547,11 @@ private void putUsersInternal(List users) throws Exception { state.bindString(2, formatUserSearchName(user)); if (user.status != null) { if (user.status instanceof TLRPC.TL_userStatusRecently) { - user.status.expires = -100; + user.status.expires = user.status.by_me ? -1000 : -100; } else if (user.status instanceof TLRPC.TL_userStatusLastWeek) { - user.status.expires = -101; + user.status.expires = user.status.by_me ? -1001 : -101; } else if (user.status instanceof TLRPC.TL_userStatusLastMonth) { - user.status.expires = -102; + user.status.expires = user.status.by_me ? -1002 : -102; } state.bindInteger(3, user.status.expires); } else { @@ -9763,7 +9905,7 @@ public void getDownloadQueue(int type) { data.reuse(); if (messageMedia.document != null) { downloadObject.object = messageMedia.document; - downloadObject.secret = MessageObject.isVideoDocument(messageMedia.document) && (messageMedia.ttl_seconds > 0 && messageMedia.ttl_seconds <= 60 || messageMedia.ttl_seconds == 0x7FFFFFFF); + downloadObject.secret = (MessageObject.isVideoDocument(messageMedia.document) || MessageObject.isVoiceDocument(messageMedia.document) || MessageObject.isRoundVideoDocument(messageMedia.document)) && (messageMedia.ttl_seconds > 0 && messageMedia.ttl_seconds <= 60 || messageMedia.ttl_seconds == 0x7FFFFFFF); } else if (messageMedia.photo != null) { downloadObject.object = messageMedia.photo; downloadObject.secret = messageMedia.ttl_seconds > 0 && messageMedia.ttl_seconds <= 60 || messageMedia.ttl_seconds == 0x7FFFFFFF; @@ -9786,7 +9928,7 @@ public void getDownloadQueue(int type) { }); } - private int getMessageMediaType(TLRPC.Message message) { + public int getMessageMediaType(TLRPC.Message message) { if (message instanceof TLRPC.TL_message_secret) { if (message.media instanceof TLRPC.TL_messageMediaPhoto || MessageObject.isGifMessage(message) || MessageObject.isVoiceMessage(message) || @@ -10371,7 +10513,7 @@ public void updateMessageVerifyFlags(ArrayList messages) { } - private void putMessagesInternal(ArrayList messages, boolean withTransaction, boolean doNotUpdateDialogDate, int downloadMask, boolean ifNoLastMessage, boolean scheduled, int threadMessageId) { + private void putMessagesInternal(ArrayList messages, boolean withTransaction, boolean doNotUpdateDialogDate, int downloadMask, boolean ifNoLastMessage, int mode, long threadMessageId) { boolean databaseInTransaction = false; SQLitePreparedStatement state_messages = null; SQLitePreparedStatement state_messages_topic = null; @@ -10388,6 +10530,8 @@ private void putMessagesInternal(ArrayList messages, boolean with SQLitePreparedStatement state_media_topics = null; SQLiteCursor cursor = null; try { + final boolean scheduled = mode == ChatActivity.MODE_SCHEDULED; + final boolean saved = mode == ChatActivity.MODE_SAVED; if (scheduled) { if (withTransaction) { database.beginTransaction(); @@ -10497,6 +10641,7 @@ private void putMessagesInternal(ArrayList messages, boolean with HashMap mediaIdsMapTopics = new HashMap<>(); HashMap> messagesMediaIdsMapTopics = new HashMap<>(); ArrayList createNewTopics = null; + ArrayList changedSavedMessages = null; state_messages = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)"); state_messages_topic = database.executeFast("REPLACE INTO messages_topics VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)"); @@ -10513,7 +10658,7 @@ private void putMessagesInternal(ArrayList messages, boolean with int messageId = message.id; MessageObject.getDialogId(message); - int topicId = MessageObject.getTopicId(message, isForum(message.dialog_id)); + long topicId = MessageObject.getTopicId(currentAccount, message, isForum(message.dialog_id)); if (message.mentioned && message.media_unread) { ArrayList ids = dialogMentionsIdsMap.get(message.dialog_id); @@ -10904,7 +11049,7 @@ private void putMessagesInternal(ArrayList messages, boolean with continue; } fixUnsupportedMedia(message); - int topicId = MessageObject.getTopicId(message, isForum(message.dialog_id)); + long topicId = MessageObject.getTopicId(currentAccount, message, isForum(message.dialog_id)); state_messages.requery(); int messageId = message.id; @@ -10940,12 +11085,13 @@ private void putMessagesInternal(ArrayList messages, boolean with createNewTopics.add(message); } + final long selfId = getUserConfig().getClientUserId(); if (updateDialog) { TLRPC.Message lastMessage = messagesMap.get(message.dialog_id); if (lastMessage == null || message.date > lastMessage.date || lastMessage.id > 0 && message.id > lastMessage.id || lastMessage.id < 0 && message.id < lastMessage.id) { messagesMap.put(message.dialog_id, message); } - if (topicId != 0) { + if (topicId != 0 && message.dialog_id != selfId) { TopicKey topicKey = TopicKey.of(message.dialog_id, topicId); lastMessage = topicMessagesMap.get(topicKey); if (lastMessage == null || message.date > lastMessage.date || lastMessage.id > 0 && message.id > lastMessage.id || lastMessage.id < 0 && message.id < lastMessage.id) { @@ -10954,21 +11100,32 @@ private void putMessagesInternal(ArrayList messages, boolean with } } - for (int i = 0; i < 2; i++) { - boolean isTopic = i == 1; + final boolean isTopic = i == 1; if (threadMessageId != 0 && !isTopic) { continue; } - if (isTopic && topicId == 0) { - continue; + SQLitePreparedStatement statement; + long dialogId = message.dialog_id; + if (isTopic) { + if (topicId == 0) { + continue; + } + statement = state_messages_topic; + if (selfId == dialogId && MessageObject.getSavedDialogId(selfId, message) != 0) { + if (changedSavedMessages == null) { + changedSavedMessages = new ArrayList<>(); + } + changedSavedMessages.add(message); + } + } else { + statement = state_messages; } - int pointer = 1; - SQLitePreparedStatement statement = isTopic ? state_messages_topic : state_messages; + int pointer = 1; statement.requery(); statement.bindInteger(pointer++, messageId); - statement.bindLong(pointer++, message.dialog_id); + statement.bindLong(pointer++, dialogId); if (isTopic) { statement.bindLong(pointer++, topicId); } @@ -11073,7 +11230,7 @@ private void putMessagesInternal(ArrayList messages, boolean with state_media_topics.requery(); state_media_topics.bindInteger(1, messageId); state_media_topics.bindLong(2, message.dialog_id); - state_media_topics.bindInteger(3, topicId); + state_media_topics.bindLong(3, topicId); state_media_topics.bindInteger(4, message.date); state_media_topics.bindInteger(5, MediaDataController.getMediaType(message)); state_media_topics.bindByteBuffer(6, data); @@ -11268,7 +11425,7 @@ private void putMessagesInternal(ArrayList messages, boolean with dids.add(key); if (exists) { - if (message == null || message.date > dialog_date || DialogObject.isEncryptedDialog(key)) { + if (message == null || (DialogObject.isEncryptedDialog(key) ? message.date > dialog_date : message.id >= last_mid) || message.send_state != 0 && ((message.flags & TLRPC.MESSAGE_FLAG_EDITED) == 0 || message.id >= last_mid)) { state_dialogs_update.requery(); state_dialogs_update.bindInteger(1, message != null && (!doNotUpdateDialogDate || dialog_date == 0) ? message.date : dialog_date); state_dialogs_update.bindInteger(2, old_unread_count + unread_count); @@ -11396,7 +11553,7 @@ private void putMessagesInternal(ArrayList messages, boolean with state_topics_update.bindInteger(3, newUnreadMentions); state_topics_update.bindInteger(4, newTotalMessagesCount); state_topics_update.bindLong(5, topicKey.dialogId); - state_topics_update.bindInteger(6, topicKey.topicId); + state_topics_update.bindLong(6, topicKey.topicId); state_topics_update.step(); if (isForum(topicKey.dialogId)) { @@ -11480,7 +11637,7 @@ private void putMessagesInternal(ArrayList messages, boolean with state_randoms.requery(); count += topicCountsMap.get(topicKey); state_randoms.bindLong(1, topicKey.dialogId); - state_randoms.bindInteger(2, topicKey.topicId); + state_randoms.bindLong(2, topicKey.topicId); state_randoms.bindInteger(3, type); state_randoms.bindInteger(4, Math.max(0, count)); state_randoms.bindInteger(5, old); @@ -11511,6 +11668,14 @@ private void putMessagesInternal(ArrayList messages, boolean with AndroidUtilities.runOnUIThread(() -> getDownloadController().newDownloadObjectsAvailable(downloadMediaMaskFinal)); } updateWidgets(dids); + if (changedSavedMessages != null) { + final ArrayList finalChangedSavedMessages = changedSavedMessages; + AndroidUtilities.runOnUIThread(() -> { + if (getMessagesController().getSavedMessagesController().updateSavedDialogs(finalChangedSavedMessages)) { + getMessagesController().getSavedMessagesController().update(); + } + }); + } } } catch (Exception e) { checkSQLException(e); @@ -11523,6 +11688,9 @@ private void putMessagesInternal(ArrayList messages, boolean with if (state_messages != null) { state_messages.dispose(); } + if (state_messages_topic != null) { + state_messages_topic.dispose(); + } if (state_randoms != null) { state_randoms.dispose(); } @@ -11586,7 +11754,7 @@ private void createOrEditTopic(long dialogId, TLRPC.Message message) { }); } else if (message.action instanceof TLRPC.TL_messageActionTopicEdit) { TLRPC.TL_messageActionTopicEdit action = (TLRPC.TL_messageActionTopicEdit) message.action; - forumTopic.id = MessageObject.getTopicId(message, true); + forumTopic.id = (int) MessageObject.getTopicId(currentAccount, message, true); forumTopic.icon_emoji_id = action.icon_emoji_id; forumTopic.title = action.title; forumTopic.closed = action.closed; @@ -11612,18 +11780,18 @@ private void createOrEditTopic(long dialogId, TLRPC.Message message) { } } - public void putMessages(ArrayList messages, boolean withTransaction, boolean useQueue, boolean doNotUpdateDialogDate, int downloadMask, boolean scheduled, int threadMessageId) { - putMessages(messages, withTransaction, useQueue, doNotUpdateDialogDate, downloadMask, false, scheduled, threadMessageId); + public void putMessages(ArrayList messages, boolean withTransaction, boolean useQueue, boolean doNotUpdateDialogDate, int downloadMask, int mode, long threadMessageId) { + putMessages(messages, withTransaction, useQueue, doNotUpdateDialogDate, downloadMask, false, mode, threadMessageId); } - public void putMessages(ArrayList messages, boolean withTransaction, boolean useQueue, boolean doNotUpdateDialogDate, int downloadMask, boolean ifNoLastMessage, boolean scheduled, int threadMessageId) { + public void putMessages(ArrayList messages, boolean withTransaction, boolean useQueue, boolean doNotUpdateDialogDate, int downloadMask, boolean ifNoLastMessage, int mode, long threadMessageId) { if (messages.size() == 0) { return; } if (useQueue) { - storageQueue.postRunnable(() -> putMessagesInternal(messages, withTransaction, doNotUpdateDialogDate, downloadMask, ifNoLastMessage, scheduled, threadMessageId)); + storageQueue.postRunnable(() -> putMessagesInternal(messages, withTransaction, doNotUpdateDialogDate, downloadMask, ifNoLastMessage, mode, threadMessageId)); } else { - putMessagesInternal(messages, withTransaction, doNotUpdateDialogDate, downloadMask, ifNoLastMessage, scheduled, threadMessageId); + putMessagesInternal(messages, withTransaction, doNotUpdateDialogDate, downloadMask, ifNoLastMessage, mode, threadMessageId); } } @@ -11685,6 +11853,7 @@ private long[] updateMessageStateAndIdInternal(long randomId, long dialogId, Int return null; } } + final long selfId = getUserConfig().getClientUserId(); oldMessageId = _oldId; if (_oldId < 0 && scheduled == 1) { SQLitePreparedStatement state = null; @@ -11760,6 +11929,7 @@ private long[] updateMessageStateAndIdInternal(long randomId, long dialogId, Int } SQLitePreparedStatement state = null; SQLitePreparedStatement state2 = null; + SQLitePreparedStatement state3 = null; if (oldMessageId == newId && date != 0) { try { if (scheduled == 0) { @@ -11821,6 +11991,10 @@ private long[] updateMessageStateAndIdInternal(long randomId, long dialogId, Int state2.dispose(); state2 = null; } + if (state3 != null) { + state3.dispose(); + state3 = null; + } } try { @@ -12237,6 +12411,7 @@ private ArrayList markMessagesAsDeletedInternal(long dialogId, ArrayList unknownMessages = new ArrayList<>(messages); ArrayList unknownMessagesInTopics = new ArrayList<>(messages); LongSparseArray dialogsToUpdate = new LongSparseArray<>(); + LongSparseArray> savedMessagesByDialogs = new LongSparseArray<>(); HashMap topicsMessagesToUpdate = new HashMap<>(); LongSparseArray> messagesByDialogs = new LongSparseArray<>(); String ids = TextUtils.join(",", messages); @@ -12279,15 +12454,29 @@ private ArrayList markMessagesAsDeletedInternal(long dialogId, ArrayList mids2 = savedMessagesByDialogs.get(savedDialogId); + if (mids2 == null) { + mids2 = new ArrayList<>(); + savedMessagesByDialogs.put(savedDialogId, mids2); + } + mids2.add(mid); + } + } } } } catch (Exception e) { @@ -12298,13 +12487,13 @@ private ArrayList markMessagesAsDeletedInternal(long dialogId, ArrayList topicsToDelete = null; if (dialogId < 0) { - cursor = database.queryFinalized(String.format(Locale.US, "SELECT uid, data, read_state, out, mention, mid FROM messages_topics WHERE mid IN(%s) AND uid = %d", ids, dialogId)); + cursor = database.queryFinalized(String.format(Locale.US, "SELECT uid, data, read_state, out, mention, mid FROM messages_topics WHERE mid IN(%s) AND uid = %d", ids, dialogId)); try { while (cursor.next()) { long did = cursor.longValue(0); int mid = cursor.intValue(5); - int topicId = 0; + long topicId = 0; unknownMessagesInTopics.remove((Integer) mid); NativeByteBuffer data = cursor.byteBufferValue(1); @@ -12319,7 +12508,7 @@ private ArrayList markMessagesAsDeletedInternal(long dialogId, ArrayList markMessagesAsDeletedInternal(long dialogId, ArrayList getMessagesController().getSavedMessagesController().updateDeleted(savedMessagesByDialogs)); + } database.executeFast(String.format(Locale.US, "DELETE FROM messages_seq WHERE mid IN(%s)", ids)).stepThis().dispose(); if (!unknownMessages.isEmpty()) { if (dialogId == 0) { @@ -13064,7 +13256,7 @@ private void fixUnsupportedMedia(TLRPC.Message message) { } } - private void doneHolesInTable(String table, long did, int max_id, int thread_message_id) throws Exception { + private void doneHolesInTable(String table, long did, int max_id, long thread_message_id) throws Exception { if (thread_message_id != 0) { if (max_id == 0) { database.executeFast(String.format(Locale.US, "DELETE FROM " + table + " WHERE uid = %d AND topic_id = %d", did, thread_message_id)).stepThis().dispose(); @@ -13089,7 +13281,7 @@ private void doneHolesInTable(String table, long did, int max_id, int thread_mes int pointer = 1; state.bindLong(pointer++, did); if (thread_message_id != 0) { - state.bindInteger(pointer++, thread_message_id); + state.bindLong(pointer++, thread_message_id); } state.bindInteger(pointer++, 1); state.bindInteger(pointer++, 1); @@ -13103,7 +13295,7 @@ private void doneHolesInTable(String table, long did, int max_id, int thread_mes } } - public void doneHolesInMedia(long did, int max_id, int type, int thread_message_id) throws Exception { + public void doneHolesInMedia(long did, int max_id, int type, long thread_message_id) throws Exception { if (type == -1) { if (thread_message_id != 0) { if (max_id == 0) { @@ -13130,7 +13322,7 @@ public void doneHolesInMedia(long did, int max_id, int type, int thread_message_ int pointer = 1; state.bindLong(pointer++, did); if (thread_message_id != 0) { - state.bindInteger(pointer++, thread_message_id); + state.bindLong(pointer++, thread_message_id); } state.bindInteger(pointer++, a); state.bindInteger(pointer++, 1); @@ -13170,7 +13362,7 @@ public void doneHolesInMedia(long did, int max_id, int type, int thread_message_ int pointer = 1; state.bindLong(pointer++, did); if (thread_message_id != 0) { - state.bindInteger(pointer++, thread_message_id); + state.bindLong(pointer++, thread_message_id); } state.bindInteger(pointer++, type); state.bindInteger(pointer++, 1); @@ -13205,7 +13397,7 @@ public Hole(int t, int s, int e) { public int type; } - public void closeHolesInMedia(long did, int minId, int maxId, int type, int topicId) { + public void closeHolesInMedia(long did, int minId, int maxId, int type, long topicId) { SQLiteCursor cursor = null; SQLitePreparedStatement state = null; try { @@ -13283,7 +13475,7 @@ public void closeHolesInMedia(long did, int minId, int maxId, int type, int topi int pointer = 1; state.bindLong(pointer++, did); if (topicId != 0) { - state.bindInteger(pointer++, topicId); + state.bindLong(pointer++, topicId); } state.bindInteger(pointer++, hole.type); state.bindInteger(pointer++, hole.start); @@ -13312,7 +13504,7 @@ public void closeHolesInMedia(long did, int minId, int maxId, int type, int topi } } - private void closeHolesInTable(String table, long did, int minId, int maxId, int thread_message_id) { + private void closeHolesInTable(String table, long did, int minId, int maxId, long thread_message_id) { SQLiteCursor cursor = null; SQLitePreparedStatement state = null; try { @@ -13386,7 +13578,7 @@ private void closeHolesInTable(String table, long did, int minId, int maxId, int state.requery(); state.bindLong(pointer++, did); if (thread_message_id != 0) { - state.bindInteger(pointer++, thread_message_id); + state.bindLong(pointer++, thread_message_id); } state.bindInteger(pointer++, hole.start); state.bindInteger(pointer++, minId); @@ -13395,7 +13587,7 @@ private void closeHolesInTable(String table, long did, int minId, int maxId, int pointer = 1; state.bindLong(pointer++, did); if (thread_message_id != 0) { - state.bindInteger(pointer++, thread_message_id); + state.bindLong(pointer++, thread_message_id); } state.bindInteger(pointer++, maxId); state.bindInteger(pointer++, hole.end); @@ -13454,15 +13646,25 @@ public void replaceMessageIfExists(TLRPC.Message message, ArrayList MessageObject.normalizeFlags(message); NativeByteBuffer data = new NativeByteBuffer(message.getObjectSize()); message.serializeToStream(data); + ArrayList changedSavedMessages = null; + final long selfId = getUserConfig().getClientUserId(); for (int i = 0; i < 2; i++) { boolean isTopic = i == 1; - int topicId = MessageObject.getTopicId(message, isForum(message.dialog_id)); - if (isTopic && topicId == 0) { - continue; - } + long topicId = MessageObject.getTopicId(currentAccount, message, isForum(message.dialog_id)); + long dialogId = message.dialog_id; + boolean removeSavedPeerIdLater = false; if (isTopic) { + if (topicId == 0) { + continue; + } state = database.executeFast("REPLACE INTO messages_topics VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)"); + if (dialogId == selfId && MessageObject.getSavedDialogId(selfId, message) != 0) { + if (changedSavedMessages == null) { + changedSavedMessages = new ArrayList<>(); + } + changedSavedMessages.add(message); + } } else { state = database.executeFast("REPLACE INTO messages_v2 VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?)"); } @@ -13470,9 +13672,9 @@ public void replaceMessageIfExists(TLRPC.Message message, ArrayList int pointer = 1; state.bindInteger(pointer++, message.id); - state.bindLong(pointer++, message.dialog_id); + state.bindLong(pointer++, dialogId); if (isTopic) { - state.bindInteger(pointer++, topicId); + state.bindLong(pointer++, topicId); } state.bindInteger(pointer++, readState); state.bindInteger(pointer++, message.send_state); @@ -13542,12 +13744,17 @@ public void replaceMessageIfExists(TLRPC.Message message, ArrayList if (storyData != null) { storyData.reuse(); } + + if (removeSavedPeerIdLater) { + message.flags &=~ 268435456; + message.saved_peer_id = null; + } } if (MediaDataController.canAddMessageToMedia(message)) { for (int i = 0; i < 2; i++) { boolean isTopic = i == 1; - int topicId = MessageObject.getTopicId(message, isForum(message.dialog_id)); + long topicId = MessageObject.getTopicId(currentAccount, message, isForum(message.dialog_id)); if (isTopic && topicId == 0) { continue; } @@ -13594,6 +13801,14 @@ public void replaceMessageIfExists(TLRPC.Message message, ArrayList arrayList.add(messageObject); AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.replaceMessagesObjects, messageObject.getDialogId(), arrayList)); } + if (changedSavedMessages != null) { + final ArrayList finalChangedSavedMessages = changedSavedMessages; + AndroidUtilities.runOnUIThread(() -> { + if (getMessagesController().getSavedMessagesController().updateSavedDialogs(finalChangedSavedMessages)) { + getMessagesController().getSavedMessagesController().update(); + } + }); + } } catch (Exception e) { checkSQLException(e); } finally { @@ -13611,7 +13826,7 @@ public void replaceMessageIfExists(TLRPC.Message message, ArrayList } // put messages in data base while load history - public void putMessages(TLRPC.messages_Messages messages, long dialogId, int load_type, int max_id, boolean createDialog, boolean scheduled, int threadMessageId) { + public void putMessages(TLRPC.messages_Messages messages, long dialogId, int load_type, int max_id, boolean createDialog, int mode, long threadMessageId) { storageQueue.postRunnable(() -> { SQLitePreparedStatement state_messages = null; SQLitePreparedStatement state_messages_topics = null; @@ -13623,6 +13838,7 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa SQLitePreparedStatement state3 = null; SQLiteCursor cursor = null; try { + final boolean scheduled = mode == ChatActivity.MODE_SCHEDULED; if (scheduled) { database.executeFast(String.format(Locale.US, "DELETE FROM scheduled_messages_v2 WHERE uid = %d AND mid > 0", dialogId)).stepThis().dispose(); state_messages = database.executeFast("REPLACE INTO scheduled_messages_v2 VALUES(?, ?, ?, ?, ?, ?, NULL, 0)"); @@ -13661,7 +13877,7 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa boolean isTopic = threadMessageId != 0; String holesTableName = isTopic ? "messages_holes_topics" : "messages_holes"; if (messages.messages.isEmpty()) { - if (load_type == 0) { + if (load_type == LOAD_BACKWARD) { doneHolesInTable(holesTableName, dialogId, max_id, threadMessageId); doneHolesInMedia(dialogId, max_id, -1, threadMessageId); } @@ -13669,16 +13885,16 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa } database.beginTransaction(); - if (load_type == 0) { + if (load_type == LOAD_BACKWARD) { int minId = messages.messages.get(messages.messages.size() - 1).id; closeHolesInTable(holesTableName, dialogId, minId, max_id, threadMessageId); closeHolesInMedia(dialogId, minId, max_id, -1, threadMessageId); - } else if (load_type == 1) { + } else if (load_type == LOAD_FORWARD) { int maxId = messages.messages.get(0).id; closeHolesInTable(holesTableName, dialogId, max_id, maxId, threadMessageId); closeHolesInMedia(dialogId, max_id, maxId, -1, threadMessageId); - } else if (load_type == 3 || load_type == 2 || load_type == 4) { - int maxId = max_id == 0 && load_type != 4 ? Integer.MAX_VALUE : messages.messages.get(0).id; + } else if (load_type == LOAD_AROUND_MESSAGE || load_type == LOAD_FROM_UNREAD || load_type == LOAD_AROUND_DATE) { + int maxId = max_id == 0 && load_type != LOAD_AROUND_DATE ? Integer.MAX_VALUE : messages.messages.get(0).id; int minId = messages.messages.get(messages.messages.size() - 1).id; closeHolesInTable(holesTableName, dialogId, minId, maxId, threadMessageId); closeHolesInMedia(dialogId, minId, maxId, -1, threadMessageId); @@ -13693,6 +13909,7 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa ArrayList filesToDelete = new ArrayList<>(); ArrayList namesToDelete = new ArrayList<>(); ArrayList> idsToDelete = new ArrayList<>(); + ArrayList changedSavedMessages = null; Integer lastMessageId = null; Long lastMessageGroupId = null; @@ -13706,6 +13923,7 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa int minDeleteTime = Integer.MAX_VALUE; HashMap botKeyboards = null; long channelId = 0; + final long selfId = getUserConfig().getClientUserId(); for (int a = 0; a < count; a++) { TLRPC.Message message = messages.messages.get(a); if (lastMessageId == null && message != null || lastMessageId != null && lastMessageId < message.id) { @@ -13841,22 +14059,34 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa for (int i = 0; i < 2; i++) { boolean isTopicMessage = i == 1; - int topicId = threadMessageId; - if (isTopicMessage && topicId == 0) { - topicId = MessageObject.getTopicId(message, isForum(message.dialog_id)); - } - if (isTopicMessage && topicId == 0) { - continue; + boolean removeSavedPeerIdLater = false; + SQLitePreparedStatement currentState; + long topicId = threadMessageId; + if (isTopicMessage) { + if (topicId == 0) { + topicId = MessageObject.getTopicId(currentAccount, message, isForum(message.dialog_id)); + } + if (topicId == 0) { + continue; + } + currentState = state_messages_topics; + if (dialogId == selfId && MessageObject.getSavedDialogId(selfId, message) != 0) { + if (changedSavedMessages == null) { + changedSavedMessages = new ArrayList<>(); + } + changedSavedMessages.add(message); + } + } else { + currentState = state_messages; } - SQLitePreparedStatement currentState = isTopicMessage ? state_messages_topics : state_messages; currentState.requery(); int pointer = 1; currentState.bindInteger(pointer++, message.id); currentState.bindLong(pointer++, dialogId); if (isTopicMessage) { - currentState.bindInteger(pointer++, topicId); + currentState.bindLong(pointer++, topicId); } currentState.bindInteger(pointer++, MessageObject.getUnreadFlags(message)); currentState.bindInteger(pointer++, message.send_state); @@ -13929,6 +14159,11 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa if (storyData != null) { storyData.reuse(); } + + if (removeSavedPeerIdLater) { + message.flags &=~ 268435456; + message.saved_peer_id = null; + } } if (threadMessageId == 0 || load_type == -2) { @@ -13949,7 +14184,7 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa } } } - int topicId = MessageObject.getTopicId(message, isForum(message.dialog_id)); + long topicId = MessageObject.getTopicId(currentAccount, message, isForum(message.dialog_id)); if (threadMessageId != 0 || (load_type == -2 && topicId != 0)) { if (MediaDataController.canAddMessageToMedia(message)) { state_media_topics.requery(); @@ -13999,7 +14234,7 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa } if (load_type == 0 && isValidKeyboardToSave(message)) { - TopicKey topicKey = TopicKey.of(dialogId, MessageObject.getTopicId(message, isForum(dialogId))); + TopicKey topicKey = TopicKey.of(dialogId, MessageObject.getTopicId(currentAccount, message, isForum(dialogId))); TLRPC.Message currentBotKeyboard = botKeyboards == null ? null : botKeyboards.get(topicKey); if (currentBotKeyboard == null || currentBotKeyboard.id < message.id) { if (botKeyboards == null) { @@ -14059,6 +14294,14 @@ public void putMessages(TLRPC.messages_Messages messages, long dialogId, int loa if (createDialog || updateDialogs) { updateDialogsWithDeletedMessages(dialogId, channelId, new ArrayList<>(), null, false); } + if (changedSavedMessages != null) { + final ArrayList finalChangedSavedMessages = changedSavedMessages; + AndroidUtilities.runOnUIThread(() -> { + if (getMessagesController().getSavedMessagesController().updateSavedDialogs(finalChangedSavedMessages)) { + getMessagesController().getSavedMessagesController().update(); + } + }); + } } } catch (Exception e) { checkSQLException(e); @@ -14260,12 +14503,12 @@ public static void addLoadPeerInfo(TLRPC.Peer peer, ArrayList usersToLoad, public void getDialogs(int folderId, int offset, int count, boolean loadDraftsPeersAndFolders) { long[] draftsDialogIds; if (loadDraftsPeersAndFolders) { - LongSparseArray> drafts = getMediaDataController().getDrafts(); + LongSparseArray> drafts = getMediaDataController().getDrafts(); int draftsCount = drafts.size(); if (draftsCount > 0) { draftsDialogIds = new long[draftsCount]; for (int i = 0; i < draftsCount; i++) { - SparseArray threads = drafts.valueAt(i); + LongSparseArray threads = drafts.valueAt(i); if (threads.get(0) == null) { continue; } @@ -14599,12 +14842,13 @@ public void getDialogs(int folderId, int offset, int count, boolean loadDraftsPe }); } - public static void createFirstHoles(long did, SQLitePreparedStatement state5, SQLitePreparedStatement state6, int messageId, int topicId) throws Exception { + public static void createFirstHoles(long did, SQLitePreparedStatement state5, SQLitePreparedStatement state6, int messageId, long topicId) throws Exception { + FileLog.d("createFirstHoles " + did + " " + messageId + " " + topicId); state5.requery(); int pointer = 1; state5.bindLong(pointer++, did); if (topicId != 0) { - state5.bindInteger(pointer++, topicId); + state5.bindLong(pointer++, topicId); } state5.bindInteger(pointer++, messageId == 1 ? 1 : 0); state5.bindInteger(pointer++, messageId); @@ -14615,7 +14859,7 @@ public static void createFirstHoles(long did, SQLitePreparedStatement state5, SQ pointer = 1; state6.bindLong(pointer++, did); if (topicId != 0) { - state6.bindInteger(pointer++, topicId); + state6.bindLong(pointer++, topicId); } state6.bindInteger(pointer++, b); state6.bindInteger(pointer++, messageId == 1 ? 1 : 0); @@ -14734,7 +14978,7 @@ private void putDialogsInternal(TLRPC.messages_Dialogs dialogs, int check) { messageDate = Math.max(message.date, messageDate); if (isValidKeyboardToSave(message)) { - TopicKey topicKey = TopicKey.of(dialog.id, MessageObject.getTopicId(message, isForum(dialog.id))); + TopicKey topicKey = TopicKey.of(dialog.id, MessageObject.getTopicId(currentAccount, message, isForum(dialog.id))); getMediaDataController().putBotKeyboard(topicKey, message); } @@ -15836,11 +16080,11 @@ public ArrayList getCachedMessagesInRange(long dialogId, int minDate, i return messageIds; } - public void updateUnreadReactionsCount(long dialogId, int topicId, int count) { + public void updateUnreadReactionsCount(long dialogId, long topicId, int count) { updateUnreadReactionsCount(dialogId, topicId, count, false); } - public void updateUnreadReactionsCount(long dialogId, int topicId, int count, boolean increment) { + public void updateUnreadReactionsCount(long dialogId, long topicId, int count, boolean increment) { storageQueue.postRunnable(() -> { SQLitePreparedStatement state = null; if (topicId != 0) { @@ -15856,7 +16100,7 @@ public void updateUnreadReactionsCount(long dialogId, int topicId, int count, bo state = database.executeFast("UPDATE topics SET unread_reactions = ? WHERE did = ? AND topic_id = ?"); state.bindInteger(1, Math.max(currentReactions + count, 0)); state.bindLong(2, dialogId); - state.bindInteger(3, topicId); + state.bindLong(3, topicId); state.step(); state.dispose(); state = null; @@ -15864,7 +16108,7 @@ public void updateUnreadReactionsCount(long dialogId, int topicId, int count, bo if (count == 0) { state = database.executeFast("UPDATE reaction_mentions_topics SET state = 0 WHERE dialog_id = ? AND topic_id = ? "); state.bindLong(1, dialogId); - state.bindInteger(2, topicId); + state.bindLong(2, topicId); state.step(); state.dispose(); state = null; @@ -15903,7 +16147,7 @@ public void updateUnreadReactionsCount(long dialogId, int topicId, int count, bo }); } - public void markMessageReactionsAsRead(long dialogId, int topicId, int messageId, boolean usequeue) { + public void markMessageReactionsAsRead(long dialogId, long topicId, int messageId, boolean usequeue) { if (usequeue) { getStorageQueue().postRunnable(() -> { markMessageReactionsAsReadInternal(dialogId, topicId, messageId); @@ -15913,7 +16157,7 @@ public void markMessageReactionsAsRead(long dialogId, int topicId, int messageId } } - public void markMessageReactionsAsReadInternal(long dialogId, int topicId, int messageId) { + public void markMessageReactionsAsReadInternal(long dialogId, long topicId, int messageId) { SQLitePreparedStatement state = null; SQLiteCursor cursor = null; try { @@ -15934,7 +16178,7 @@ public void markMessageReactionsAsReadInternal(long dialogId, int topicId, int m state = getMessagesStorage().getDatabase().executeFast("UPDATE reaction_mentions_topics SET state = 0 WHERE message_id = ? AND dialog_id = ? AND topic_id = ? "); state.bindInteger(1, messageId); state.bindLong(2, dialogId); - state.bindInteger(3, topicId); + state.bindLong(3, topicId); state.step(); state.dispose(); state = null; @@ -15989,7 +16233,7 @@ public void markMessageReactionsAsReadInternal(long dialogId, int topicId, int m } - public void updateDialogUnreadReactions(long dialogId, int topicId, int newUnreadCount, boolean increment) { + public void updateDialogUnreadReactions(long dialogId, long topicId, int newUnreadCount, boolean increment) { storageQueue.postRunnable(() -> { SQLiteCursor cursor = null; SQLitePreparedStatement state = null; @@ -16026,7 +16270,7 @@ public void updateDialogUnreadReactions(long dialogId, int topicId, int newUnrea state = getMessagesStorage().getDatabase().executeFast("UPDATE topics SET unread_reactions = ? WHERE did = ? AND topic_id = ?"); state.bindInteger(1, oldUnreadRactions); state.bindLong(2, dialogId); - state.bindInteger(3, topicId); + state.bindLong(3, topicId); state.step(); state.dispose(); state = null; @@ -16072,9 +16316,9 @@ public interface BooleanCallback { public static class TopicKey { public long dialogId; - public int topicId; + public long topicId; - public static TopicKey of(long dialogId, int topicId) { + public static TopicKey of(long dialogId, long topicId) { TopicKey topicKey = new TopicKey(); topicKey.dialogId = dialogId; topicKey.topicId = topicId; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MusicBrowserService.java b/TMessagesProj/src/main/java/org/telegram/messenger/MusicBrowserService.java index 428ec3d077..732ee31b43 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MusicBrowserService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MusicBrowserService.java @@ -34,6 +34,7 @@ import android.os.SystemClock; import android.service.media.MediaBrowserService; import android.text.TextUtils; +import android.util.Log; import android.widget.Toast; import androidx.collection.LongSparseArray; @@ -50,6 +51,8 @@ import java.util.List; import java.util.Locale; +import javax.annotation.Nullable; + @TargetApi(Build.VERSION_CODES.LOLLIPOP) public class MusicBrowserService extends MediaBrowserService implements NotificationCenter.NotificationCenterDelegate { @@ -57,6 +60,7 @@ public class MusicBrowserService extends MediaBrowserService implements Notifica private static final String SLOT_RESERVATION_SKIP_TO_PREV = "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS"; private static final String SLOT_RESERVATION_QUEUE = "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_QUEUE"; + @Nullable private MediaSession mediaSession; private static final String MEDIA_ID_ROOT = "__ROOT__"; @@ -91,6 +95,15 @@ public void onCreate() { lastSelectedDialog = AndroidUtilities.getPrefIntOrLong(MessagesController.getNotificationsSettings(currentAccount), "auto_lastSelectedDialog", 0); + updatePlaybackState(null); + + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingPlayStateChanged); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidStart); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidReset); + } + + private void createMediaSession() { + if (mediaSession != null) return; mediaSession = new MediaSession(this, "MusicService"); setSessionToken(mediaSession.getSessionToken()); mediaSession.setCallback(new MediaSessionCallback()); @@ -106,12 +119,6 @@ public void onCreate() { extras.putBoolean(SLOT_RESERVATION_SKIP_TO_PREV, true); extras.putBoolean(SLOT_RESERVATION_SKIP_TO_NEXT, true); mediaSession.setExtras(extras); - - updatePlaybackState(null); - - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingPlayStateChanged); - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidStart); - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidReset); } @Override @@ -135,7 +142,9 @@ public void onDestroy() { super.onDestroy(); handleStopRequest(null); delayedStopHandler.removeCallbacksAndMessages(null); - mediaSession.release(); + if (mediaSession != null) { + mediaSession.release(); + } } @Override @@ -241,6 +250,7 @@ public void onLoadChildren(String parentMediaId, Result arrayList = musicObjects.get(lastSelectedDialog); ArrayList arrayList1 = musicQueues.get(lastSelectedDialog); if (arrayList != null && !arrayList.isEmpty()) { + createMediaSession(); mediaSession.setQueue(arrayList1); if (lastSelectedDialog > 0) { TLRPC.User user = users.get(lastSelectedDialog); @@ -413,6 +423,7 @@ public void onPlayFromMediaId(String mediaId, Bundle extras) { lastSelectedDialog = did; MessagesController.getNotificationsSettings(currentAccount).edit().putLong("auto_lastSelectedDialog", did).commit(); MediaController.getInstance().setPlaylist(arrayList, arrayList.get(id), 0, false, null); + createMediaSession(); mediaSession.setQueue(arrayList1); if (did > 0) { TLRPC.User user = users.get(did); @@ -515,8 +526,9 @@ private void updatePlaybackState(String error) { } else { stateBuilder.setActiveQueueItemId(0); } - - mediaSession.setPlaybackState(stateBuilder.build()); + if (mediaSession != null) { + mediaSession.setPlaybackState(stateBuilder.build()); + } } private long getAvailableActions() { @@ -554,7 +566,8 @@ private void handlePlayRequest() { serviceStarted = true; } - if (!mediaSession.isActive()) { + if (mediaSession == null || !mediaSession.isActive()) { + createMediaSession(); mediaSession.setActive(true); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java index fa191a3087..0294d98d20 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java @@ -176,6 +176,8 @@ public class NotificationCenter { public static final int recordStarted = totalEvents++; public static final int recordStartError = totalEvents++; public static final int recordStopped = totalEvents++; + public static final int recordPaused = totalEvents++; + public static final int recordResumed = totalEvents++; public static final int screenshotTook = totalEvents++; public static final int albumsDidLoad = totalEvents++; public static final int audioDidSent = totalEvents++; @@ -223,7 +225,9 @@ public class NotificationCenter { public static final int unconfirmedAuthUpdate = totalEvents++; public static final int dialogPhotosUpdate = totalEvents++; public static final int channelRecommendationsLoaded = totalEvents++; - public static final int savedMessagesUpdate = totalEvents++; + public static final int savedMessagesDialogsUpdate = totalEvents++; + public static final int savedReactionTagsUpdate = totalEvents++; + public static final int userIsPremiumBlockedUpadted = totalEvents++; //global public static final int pushMessagesUpdated = totalEvents++; @@ -737,29 +741,35 @@ private AllowedNotifications() { } } - public static void listenEmojiLoading(View view) { - if (view == null) { - return; + public Runnable listen(View view, final int id, final Utilities.Callback callback) { + if (view == null || callback == null) { + return () -> {}; } - - final NotificationCenterDelegate delegate = (id, account, args) -> { - if (id == NotificationCenter.emojiLoaded) { - if (view != null && view.isAttachedToWindow()) { - view.invalidate(); - } + final NotificationCenterDelegate delegate = (_id, account, args) -> { + if (_id == id) { + callback.run(args); } }; - view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { + final View.OnAttachStateChangeListener viewListener = new View.OnAttachStateChangeListener() { @Override public void onViewAttachedToWindow(View view) { - NotificationCenter.getGlobalInstance().addObserver(delegate, NotificationCenter.emojiLoaded); + NotificationCenter.getGlobalInstance().addObserver(delegate, id); } - @Override public void onViewDetachedFromWindow(View view) { - NotificationCenter.getGlobalInstance().removeObserver(delegate, NotificationCenter.emojiLoaded); + NotificationCenter.getGlobalInstance().removeObserver(delegate, id); } - }); + }; + view.addOnAttachStateChangeListener(viewListener); + + return () -> { + view.removeOnAttachStateChangeListener(viewListener); + NotificationCenter.getGlobalInstance().removeObserver(delegate, id); + }; + } + + public static void listenEmojiLoading(View view) { + getGlobalInstance().listen(view, NotificationCenter.emojiLoaded, args -> view.invalidate()); } public void listenOnce(int id, Runnable callback) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java index 535974bb9b..da559b1a5e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java @@ -37,14 +37,12 @@ import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.Shader; -import android.graphics.Xfermode; import android.graphics.drawable.BitmapDrawable; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.SoundPool; import android.net.Uri; import android.os.Build; -import android.os.Looper; import android.os.PowerManager; import android.os.SystemClock; import android.provider.Settings; @@ -54,8 +52,6 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.collection.LongSparseArray; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; @@ -92,7 +88,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.TreeSet; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; @@ -119,7 +114,7 @@ public class NotificationsController extends BaseController { private final ArrayList storyPushMessages = new ArrayList<>(); private final LongSparseArray storyPushMessagesDict = new LongSparseArray<>(); private long openedDialogId = 0; - private int openedTopicId = 0; + private long openedTopicId = 0; private int lastButtonId = 5000; private int total_unread_count = 0; private int personalCount = 0; @@ -284,15 +279,15 @@ public static void checkOtherNotificationsChannel() { private static final LongSparseArray sharedPrefCachedKeys = new LongSparseArray<>(); - public static String getSharedPrefKey(long dialog_id, int topicId) { + public static String getSharedPrefKey(long dialog_id, long topicId) { return getSharedPrefKey(dialog_id, topicId, false); } - public static String getSharedPrefKey(long dialog_id, int topicId, boolean backgroundThread) { + public static String getSharedPrefKey(long dialog_id, long topicId, boolean backgroundThread) { if (backgroundThread) { String key; if (topicId != 0) { - key = String.format(Locale.US, "%d_%d",dialog_id, topicId); + key = String.format(Locale.US, "%d_%d", dialog_id, topicId); } else { key = String.valueOf(dialog_id); } @@ -310,7 +305,7 @@ public static String getSharedPrefKey(long dialog_id, int topicId, boolean backg } String key; if (topicId != 0) { - key = String.format(Locale.US, "%d_%d",dialog_id, topicId); + key = String.format(Locale.US, "%d_%d", dialog_id, topicId); } else { key = String.valueOf(dialog_id); } @@ -318,7 +313,7 @@ public static String getSharedPrefKey(long dialog_id, int topicId, boolean backg return key; } - public void muteUntil(long did, int topicId, int selectedTimeInSeconds) { + public void muteUntil(long did, long topicId, int selectedTimeInSeconds) { if (did != 0) { SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); SharedPreferences.Editor editor = preferences.edit(); @@ -425,7 +420,7 @@ public void setInChatSoundEnabled(boolean value) { inChatSoundEnabled = value; } - public void setOpenedDialogId(long dialog_id, int topicId) { + public void setOpenedDialogId(long dialog_id, long topicId) { notificationsQueue.postRunnable(() -> { openedDialogId = dialog_id; openedTopicId = topicId; @@ -462,8 +457,10 @@ public boolean hasMessagesToReply() { for (int a = 0; a < pushMessages.size(); a++) { MessageObject messageObject = pushMessages.get(a); long dialog_id = messageObject.getDialogId(); - if (messageObject.messageOwner.mentioned && messageObject.messageOwner.action instanceof TLRPC.TL_messageActionPinMessage || - DialogObject.isEncryptedDialog(dialog_id) || messageObject.messageOwner.peer_id.channel_id != 0 && !messageObject.isSupergroup()) { + if (messageObject.isReactionPush || + messageObject.messageOwner.mentioned && messageObject.messageOwner.action instanceof TLRPC.TL_messageActionPinMessage || + DialogObject.isEncryptedDialog(dialog_id) || + messageObject.messageOwner.peer_id.channel_id != 0 && !messageObject.isSupergroup()) { continue; } return true; @@ -1009,7 +1006,7 @@ public void processNewMessages(ArrayList messageObjects, boolean } long originalDialogId = dialogId; - int topicId = MessageObject.getTopicId(messageObject.messageOwner, getMessagesController().isForum(messageObject)); + long topicId = MessageObject.getTopicId(currentAccount, messageObject.messageOwner, getMessagesController().isForum(messageObject)); if (dialogId == openedDialogId && ApplicationLoader.isScreenOn) { if (!isFcm) { playInChatSound(); @@ -1104,7 +1101,7 @@ public void processNewMessages(ArrayList messageObjects, boolean } else if (added) { MessageObject messageObject = messageObjects.get(0); long dialog_id = messageObject.getDialogId(); - int topicId = MessageObject.getTopicId(messageObject.messageOwner, getMessagesController().isForum(dialog_id)); + long topicId = MessageObject.getTopicId(currentAccount, messageObject.messageOwner, getMessagesController().isForum(dialog_id)); Boolean isChannel; if (messageObject.isFcmMessage()) { isChannel = messageObject.localChannel; @@ -1354,7 +1351,7 @@ public void processLoadedUnreadMessages(LongSparseArray dialogs, ArrayL } long dialog_id = messageObject.getDialogId(); long original_dialog_id = dialog_id; - int topicId = MessageObject.getTopicId(messageObject.messageOwner, getMessagesController().isForum(messageObject)); + long topicId = MessageObject.getTopicId(currentAccount, messageObject.messageOwner, getMessagesController().isForum(messageObject)); if (messageObject.messageOwner.mentioned) { dialog_id = messageObject.getFromChatId(); } @@ -1426,7 +1423,7 @@ public void processLoadedUnreadMessages(LongSparseArray dialogs, ArrayL } long dialogId = messageObject.getDialogId(); long originalDialogId = dialogId; - int topicId = MessageObject.getTopicId(messageObject.messageOwner, getMessagesController().isForum(messageObject)); + long topicId = MessageObject.getTopicId(currentAccount, messageObject.messageOwner, getMessagesController().isForum(messageObject)); long randomId = messageObject.messageOwner.random_id; if (messageObject.messageOwner.mentioned) { dialogId = messageObject.getFromChatId(); @@ -2086,6 +2083,10 @@ private String getShortStringForMessage(MessageObject messageObject, String[] us } else { return LocaleController.getString("Message", R.string.Message); } + } else if (messageObject.isVoiceOnce()) { + return LocaleController.getString(R.string.AttachOnceAudio); + } else if (messageObject.isRoundOnce()) { + return LocaleController.getString(R.string.AttachOnceRound); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto) { if (Build.VERSION.SDK_INT >= 19 && !TextUtils.isEmpty(messageObject.messageOwner.message)) { return "\uD83D\uDDBC " + replaceSpoilers(messageObject); @@ -2899,7 +2900,7 @@ private boolean isPersonalMessage(MessageObject messageObject) { && (messageObject.messageOwner.action == null || messageObject.messageOwner.action instanceof TLRPC.TL_messageActionEmpty); } - private int getNotifyOverride(SharedPreferences preferences, long dialog_id, int topicId) { + private int getNotifyOverride(SharedPreferences preferences, long dialog_id, long topicId) { int notifyOverride = dialogsNotificationsFacade.getProperty(NotificationsSettingsFacade.PROPERTY_NOTIFY, dialog_id, topicId, -1); if (notifyOverride == 3) { int muteUntil = dialogsNotificationsFacade.getProperty(NotificationsSettingsFacade.PROPERTY_NOTIFY_UNTIL, dialog_id, topicId, 0); @@ -3041,11 +3042,11 @@ private boolean isEmptyVibration(long[] pattern) { return true; } - public void deleteNotificationChannel(long dialogId, int topicId) { + public void deleteNotificationChannel(long dialogId, long topicId) { deleteNotificationChannel(dialogId, topicId, -1); } - private void deleteNotificationChannelInternal(long dialogId, int topicId, int what) { + private void deleteNotificationChannelInternal(long dialogId, long topicId, int what) { if (Build.VERSION.SDK_INT < 26) { return; } @@ -3091,7 +3092,7 @@ private void deleteNotificationChannelInternal(long dialogId, int topicId, int w } } - public void deleteNotificationChannel(long dialogId, int topicId, int what) { + public void deleteNotificationChannel(long dialogId, long topicId, int what) { if (Build.VERSION.SDK_INT < 26) { return; } @@ -3406,7 +3407,7 @@ protected void ensureGroupsCreated() { } @TargetApi(26) - private String validateChannelId(long dialogId, int topicId, String name, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int type) { + private String validateChannelId(long dialogId, long topicId, String name, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int type) { ensureGroupsCreated(); SharedPreferences preferences = getAccountInstance().getNotificationsSettings(); @@ -3778,7 +3779,7 @@ private void showOrUpdateNotification(boolean notifyAboutLast) { } long dialog_id = lastMessageObject.getDialogId(); - int topicId = MessageObject.getTopicId(lastMessageObject.messageOwner, getMessagesController().isForum(lastMessageObject)); + long topicId = MessageObject.getTopicId(currentAccount, lastMessageObject.messageOwner, getMessagesController().isForum(lastMessageObject)); boolean story = lastMessageObject.isStoryPush; boolean isChannel = false; @@ -4324,7 +4325,7 @@ private void setNotificationChannel(Notification mainNotification, NotificationC } } - private void resetNotificationSound(NotificationCompat.Builder notificationBuilder, long dialogId, int topicId, String chatName, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int chatType) { + private void resetNotificationSound(NotificationCompat.Builder notificationBuilder, long dialogId, long topicId, String chatName, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int chatType) { Uri defaultSound = Settings.System.DEFAULT_RINGTONE_URI; if (defaultSound != null && sound != null && !TextUtils.equals(defaultSound.toString(), sound.toString())) { SharedPreferences preferences = getAccountInstance().getNotificationsSettings(); @@ -4365,7 +4366,7 @@ private void resetNotificationSound(NotificationCompat.Builder notificationBuild } @SuppressLint("InlinedApi") - private void showExtraNotifications(NotificationCompat.Builder notificationBuilder, String summary, long lastDialogId, int lastTopicId, String chatName, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int chatType) { + private void showExtraNotifications(NotificationCompat.Builder notificationBuilder, String summary, long lastDialogId, long lastTopicId, String chatName, long[] vibrationPattern, int ledColor, Uri sound, int importance, boolean isDefault, boolean isInApp, boolean isSilent, int chatType) { if (Build.VERSION.SDK_INT >= 26) { notificationBuilder.setChannelId(validateChannelId(lastDialogId, lastTopicId, chatName, vibrationPattern, ledColor, sound, importance, isDefault, isInApp, isSilent, chatType)); } @@ -4388,7 +4389,7 @@ private void showExtraNotifications(NotificationCompat.Builder notificationBuild for (int a = 0; a < pushMessages.size(); a++) { MessageObject messageObject = pushMessages.get(a); long dialog_id = messageObject.getDialogId(); - int topicId = MessageObject.getTopicId(messageObject.messageOwner, getMessagesController().isForum(messageObject)); + long topicId = MessageObject.getTopicId(currentAccount, messageObject.messageOwner, getMessagesController().isForum(messageObject)); int dismissDate = preferences.getInt("dismissDate" + dialog_id, 0); if (!messageObject.isStoryPush && messageObject.messageOwner.date <= dismissDate) { continue; @@ -4412,14 +4413,14 @@ private void showExtraNotifications(NotificationCompat.Builder notificationBuild class NotificationHolder { int id; long dialogId; - int topicId; + long topicId; boolean story; String name; TLRPC.User user; TLRPC.Chat chat; NotificationCompat.Builder notification; - NotificationHolder(int i, long li, boolean story, int topicId, String n, TLRPC.User u, TLRPC.Chat c, NotificationCompat.Builder builder) { + NotificationHolder(int i, long li, boolean story, long topicId, String n, TLRPC.User u, TLRPC.Chat c, NotificationCompat.Builder builder) { id = i; name = n; user = u; @@ -4462,7 +4463,8 @@ void call() { } DialogKey dialogKey = sortedDialogs.get(b); long dialogId; - int topicId, maxId; + long topicId; + int maxId; MessageObject lastMessageObject = null; ArrayList messageObjects; if (dialogKey.story) { @@ -4525,7 +4527,7 @@ void call() { photoPath = user.photo.photo_small; } } else if (!DialogObject.isEncryptedDialog(dialogId)) { - canReply = dialogId != 777000; + canReply = (lastMessageObject != null && !lastMessageObject.isReactionPush) && dialogId != 777000; if (DialogObject.isUserDialog(dialogId)) { user = getMessagesController().getUser(dialogId); if (user == null) { @@ -4758,7 +4760,7 @@ void call() { } else { for (int a = messageObjects.size() - 1; a >= 0; a--) { MessageObject messageObject = messageObjects.get(a); - int messageTopicId = MessageObject.getTopicId(messageObject.messageOwner, getMessagesController().isForum(messageObject)); + long messageTopicId = MessageObject.getTopicId(currentAccount, messageObject.messageOwner, getMessagesController().isForum(messageObject)); if (topicId != messageTopicId) { continue; } @@ -5374,7 +5376,7 @@ public void playOutChatSound() { public static final int SETTING_MUTE_UNMUTE = 4; public static final int SETTING_MUTE_CUSTOM = 5; - public void clearDialogNotificationsSettings(long did, int topicId) { + public void clearDialogNotificationsSettings(long did, long topicId) { SharedPreferences preferences = getAccountInstance().getNotificationsSettings(); SharedPreferences.Editor editor = preferences.edit(); String prefKey = NotificationsController.getSharedPrefKey(did, topicId); @@ -5388,7 +5390,7 @@ public void clearDialogNotificationsSettings(long did, int topicId) { getNotificationsController().updateServerNotificationsSettings(did, topicId,true); } - public void setDialogNotificationsSettings(long dialog_id, int topicId, int setting) { + public void setDialogNotificationsSettings(long dialog_id, long topicId, int setting) { SharedPreferences preferences = getAccountInstance().getNotificationsSettings(); SharedPreferences.Editor editor = preferences.edit(); TLRPC.Dialog dialog = MessagesController.getInstance(UserConfig.selectedAccount).dialogs_dict.get(dialog_id); @@ -5435,11 +5437,11 @@ public void setDialogNotificationsSettings(long dialog_id, int topicId, int sett updateServerNotificationsSettings(dialog_id, topicId); } - public void updateServerNotificationsSettings(long dialog_id, int topicId) { + public void updateServerNotificationsSettings(long dialog_id, long topicId) { updateServerNotificationsSettings(dialog_id, topicId, true); } - public void updateServerNotificationsSettings(long dialogId, int topicId, boolean post) { + public void updateServerNotificationsSettings(long dialogId, long topicId, boolean post) { if (post) { getNotificationCenter().postNotificationName(NotificationCenter.notificationsSettingsUpdated); } @@ -5492,10 +5494,10 @@ public void updateServerNotificationsSettings(long dialogId, int topicId, boolea } else { req.settings.sound = new TLRPC.TL_notificationSoundDefault(); } - if (topicId != 0) { + if (topicId != 0 && dialogId != getUserConfig().getClientUserId()) { TLRPC.TL_inputNotifyForumTopic topicPeer = new TLRPC.TL_inputNotifyForumTopic(); topicPeer.peer = getMessagesController().getInputPeer(dialogId); - topicPeer.top_msg_id = topicId; + topicPeer.top_msg_id = (int) topicId; req.peer = topicPeer; } else { req.peer = new TLRPC.TL_inputNotifyPeer(); @@ -5625,7 +5627,7 @@ public static String getGlobalNotificationsKey(int type) { } } - public void muteDialog(long dialog_id, int topicId, boolean mute) { + public void muteDialog(long dialog_id, long topicId, boolean mute) { if (mute) { NotificationsController.getInstance(currentAccount).muteUntil(dialog_id, topicId, Integer.MAX_VALUE); } else { @@ -5683,10 +5685,10 @@ public void loadTopicsNotificationsExceptions(long dialogId, Consumer> messages = new LongSparseArray>(); + public boolean unsupported; public SavedMessagesController(int account) { this.currentAccount = account; + unsupported = MessagesController.getMainSettings(currentAccount).getBoolean("savedMessagesUnsupported", true); + } + + public void cleanup() { + cachedDialogs.clear(); + loadedDialogs.clear(); + dialogsLoaded = false; + dialogsCount = 0; + dialogsCountHidden = 0; + dialogsEndReached = false; + loadedCache = true; + deleteCache(); + unsupported = true; + MessagesController.getMainSettings(currentAccount).edit().remove("savedMessagesUnsupported").apply(); + } + + private ArrayList cachedDialogs = new ArrayList<>(); + + private boolean dialogsLoading, dialogsLoaded; + public boolean dialogsEndReached; + private int dialogsCount; + private int dialogsCountHidden; + private ArrayList loadedDialogs = new ArrayList<>(); + + public ArrayList allDialogs = new ArrayList<>(); + private void updateAllDialogs(boolean notify) { + allDialogs.clear(); + HashSet ids = new HashSet<>(); + for (int i = 0; i < cachedDialogs.size(); ++i) { + SavedDialog d = cachedDialogs.get(i); + if (d.pinned && !ids.contains(d.dialogId) && !d.isHidden()) { + allDialogs.add(d); + ids.add(d.dialogId); + } + } + for (int i = 0; i < loadedDialogs.size(); ++i) { + SavedDialog d = loadedDialogs.get(i); + if (d.pinned && !ids.contains(d.dialogId) && !d.isHidden()) { + allDialogs.add(d); + ids.add(d.dialogId); + } + } + ArrayList dialogs = new ArrayList<>(); + for (int i = 0; i < loadedDialogs.size(); ++i) { + SavedDialog d = loadedDialogs.get(i); + if (!ids.contains(d.dialogId) && !d.isHidden()) { + dialogs.add(d); + ids.add(d.dialogId); + } + } + if (!dialogsEndReached) { + for (int i = 0; i < cachedDialogs.size(); ++i) { + SavedDialog d = cachedDialogs.get(i); + if (!ids.contains(d.dialogId) && !d.isHidden()) { + dialogs.add(d); + ids.add(d.dialogId); + } + } + } + Collections.sort(dialogs, (d1, d2) -> d2.getDate() - d1.getDate()); + allDialogs.addAll(dialogs); + if (notify) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.savedMessagesDialogsUpdate); + } + } + + public int getAllCount() { + if (dialogsEndReached) { + return allDialogs.size(); + } + if (dialogsLoaded) { + return dialogsCount - dialogsCountHidden; + } + return cachedDialogs.size(); + } + + public int getLoadedCount() { + return loadedDialogs.size(); + } + + public int getPinnedCount() { + int count = 0; + for (int i = 0; i < allDialogs.size(); ++i) { + if (allDialogs.get(i).pinned) { + count++; + } + } + return count; + } + + public SavedDialog findSavedDialog(long did) { + return findSavedDialog(allDialogs, did); + } + + public SavedDialog findSavedDialog(ArrayList dialogs, long did) { + for (int i = 0; i < dialogs.size(); ++i) { + SavedDialog d = dialogs.get(i); + if (d.dialogId == did) { + return d; + } + } + return null; + } + + public ArrayList searchDialogs(String q) { + ArrayList result = new ArrayList<>(); + if (TextUtils.isEmpty(q)) return result; + String lq = AndroidUtilities.translitSafe(q.toLowerCase()); + for (int i = 0; i < allDialogs.size(); ++i) { + SavedDialog d = allDialogs.get(i); + final String name; + String name2 = null; + if (d.dialogId == UserObject.ANONYMOUS) { + name = LocaleController.getString(R.string.AnonymousForward); + } else if (d.dialogId == UserConfig.getInstance(currentAccount).getClientUserId()) { + name = LocaleController.getString(R.string.MyNotes); + name2 = LocaleController.getString(R.string.SavedMessages); + } else if (d.dialogId >= 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(d.dialogId); + name = UserObject.getUserName(user); + } else { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-d.dialogId); + name = chat != null ? chat.title : ""; + } + if (name == null) continue; + String lname = AndroidUtilities.translitSafe(name.toLowerCase()); + if (lname.startsWith(lq) || lname.contains(" " + lq)) { + result.add(d); + } else if (name2 != null) { + lname = AndroidUtilities.translitSafe(name2.toLowerCase()); + if (lname.startsWith(lq) || lname.contains(" " + lq)) { + result.add(d); + } + } + } + return result; + } + + public int getMessagesCount(long dialogId) { + for (int i = 0; i < allDialogs.size(); ++i) { + SavedDialog d = allDialogs.get(i); + if (d.dialogId == dialogId) + return d.messagesCount; + } + return 0; + } + + public boolean containsDialog(long dialogId) { + for (int i = 0; i < allDialogs.size(); ++i) { + SavedDialog d = allDialogs.get(i); + if (d.dialogId == dialogId) + return true; + } + return false; + } + + public void preloadDialogs() { + if (!dialogsLoaded) { + loadDialogs(); + } + } + + public void loadDialogs() { + if (dialogsLoading || dialogsEndReached || loadingCache) { + return; + } + if (!loadedCache) { + loadCache(this::loadDialogs); + return; + } + dialogsLoading = true; + + TLRPC.TL_messages_getSavedDialogs req = new TLRPC.TL_messages_getSavedDialogs(); + SavedDialog lastDialog = loadedDialogs.isEmpty() ? null : loadedDialogs.get(loadedDialogs.size() - 1); + + if (lastDialog != null) { + req.offset_id = lastDialog.top_message_id; + req.offset_date = lastDialog.getDate(); + req.offset_peer = MessagesController.getInstance(currentAccount).getInputPeer(lastDialog.dialogId); + } else { + req.offset_id = Integer.MAX_VALUE; + req.offset_date = 0; + req.offset_peer = new TLRPC.TL_inputPeerEmpty(); + } + req.limit = 20; + + final ArrayList expectedDialogs = new ArrayList<>(); + expectedDialogs.addAll(allDialogs.subList( + Math.min(loadedDialogs.size(), allDialogs.size()), + Math.min(loadedDialogs.size() + req.limit, allDialogs.size()) + )); + for (int i = 0; i < expectedDialogs.size(); ++i) { + SavedDialog d = expectedDialogs.get(i); + req.hash = MediaDataController.calcHash(req.hash, d.pinned ? 1 : 0); + req.hash = MediaDataController.calcHash(req.hash, Math.abs(d.dialogId)); + req.hash = MediaDataController.calcHash(req.hash, d.top_message_id); + req.hash = MediaDataController.calcHash(req.hash, d.getDate()); + } + + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + final boolean wasUnsupported = unsupported; + if (res instanceof TLRPC.TL_messages_savedDialogs) { + dialogsLoaded = true; + TLRPC.TL_messages_savedDialogs r = (TLRPC.TL_messages_savedDialogs) res; + MessagesController.getInstance(currentAccount).putUsers(r.users, false); + MessagesController.getInstance(currentAccount).putChats(r.chats, false); + MessagesStorage.getInstance(currentAccount).putUsersAndChats(r.users, r.chats, true, true); + MessagesStorage.getInstance(currentAccount).putMessages(r.messages, false, true, false, 0, false, ChatActivity.MODE_SAVED, 0); + for (int i = 0; i < r.dialogs.size(); ++i) { + SavedDialog d = SavedDialog.fromTL(currentAccount, r.dialogs.get(i), r.messages); + for (int j = 0; j < cachedDialogs.size(); ++j) { + if (cachedDialogs.get(j).dialogId == d.dialogId) { + d.messagesCount = cachedDialogs.get(j).messagesCount; + cachedDialogs.get(j).pinned = d.pinned; + break; + } + } + boolean found = false; + for (int j = 0; j < loadedDialogs.size(); ++j) { + if (loadedDialogs.get(j).dialogId == d.dialogId) { + found = true; + break; + } + } + if (!found) { + loadedDialogs.add(d); + if (d.isHidden()) + dialogsCountHidden++; + } + } + dialogsEndReached = true; + dialogsCount = r.dialogs.size(); + updateAllDialogs(true); + saveCacheSchedule(); + unsupported = false; + } else if (res instanceof TLRPC.TL_messages_savedDialogsSlice) { + dialogsLoaded = true; + TLRPC.TL_messages_savedDialogsSlice r = (TLRPC.TL_messages_savedDialogsSlice) res; + MessagesController.getInstance(currentAccount).putUsers(r.users, false); + MessagesController.getInstance(currentAccount).putChats(r.chats, false); + MessagesStorage.getInstance(currentAccount).putUsersAndChats(r.users, r.chats, true, true); + MessagesStorage.getInstance(currentAccount).putMessages(r.messages, false, true, false, 0, false, ChatActivity.MODE_SAVED, 0); + for (int i = 0; i < r.dialogs.size(); ++i) { + SavedDialog d = SavedDialog.fromTL(currentAccount, r.dialogs.get(i), r.messages); + for (int j = 0; j < cachedDialogs.size(); ++j) { + if (cachedDialogs.get(j).dialogId == d.dialogId) { + d.messagesCount = cachedDialogs.get(j).messagesCount; + cachedDialogs.get(j).pinned = d.pinned; + break; + } + } + boolean found = false; + for (int j = 0; j < loadedDialogs.size(); ++j) { + if (loadedDialogs.get(j).dialogId == d.dialogId) { + found = true; + break; + } + } + if (!found) { + loadedDialogs.add(d); + if (d.isHidden()) + dialogsCountHidden++; + } + } + dialogsCount = r.count; + dialogsEndReached = getPinnedCount() + loadedDialogs.size() >= dialogsCount || r.dialogs.size() == 0; + updateAllDialogs(true); + saveCacheSchedule(); + unsupported = false; + } else if (res instanceof TLRPC.TL_messages_savedDialogsNotModified) { + dialogsLoaded = true; + loadedDialogs.addAll(expectedDialogs); + dialogsCount = ((TLRPC.TL_messages_savedDialogsNotModified) res).count; + dialogsCountHidden = 0; + for (int i = 0; i < expectedDialogs.size(); ++i) { + if (expectedDialogs.get(i).isHidden()) { + dialogsCountHidden++; + } + } + dialogsEndReached = loadedDialogs.size() >= dialogsCount; + unsupported = false; + } else if (err != null) { + dialogsLoaded = true; + if ("SAVED_DIALOGS_UNSUPPORTED".equals(err.text)) { + unsupported = true; + } + } + if (unsupported != wasUnsupported) { + MessagesController.getMainSettings(currentAccount).edit().putBoolean("savedMessagesUnsupported", unsupported).apply(); + } + + dialogsLoading = false; + })); + } + + public boolean updateSavedDialogs(ArrayList inputMessages) { + if (inputMessages == null) { + return false; + } + LongSparseArray messages = new LongSparseArray<>(); + LongSparseArray messagesCount = new LongSparseArray<>(); + HashSet dialogsCountToCheck = new HashSet<>(); + long self = UserConfig.getInstance(currentAccount).getClientUserId(); + for (int i = 0; i < inputMessages.size(); ++i) { + TLRPC.Message message = inputMessages.get(i); + long dialogId = MessageObject.getSavedDialogId(self, message); + if (dialogId != self && (message.id < 0 || message.send_state != 0 && message.fwd_from != null)) { + // we might not know does user privacy hide fwd_from.from_id + continue; + } + TLRPC.Message existingMessage = messages.get(dialogId); + if (existingMessage == null || existingMessage.id < message.id) { + messages.put(dialogId, message); + } + Integer count = messagesCount.get(dialogId); + messagesCount.put(dialogId, (count == null ? 0 : count) + 1); + } + + boolean changed = false; + for (int i = 0; i < messages.size(); ++i) { + long dialogId = messages.keyAt(i); + TLRPC.Message message = messages.valueAt(i); + Integer newMessagesCount = messagesCount.get(dialogId); + boolean found = false; + for (int j = 0; j < cachedDialogs.size(); ++j) { + SavedDialog d = cachedDialogs.get(j); + if (d.dialogId == dialogId) { + found = true; + if (d.top_message_id < message.id || message.id < 0 && message.date > d.getDate()) { + changed = true; + d.message = new MessageObject(currentAccount, message, false, false); + d.top_message_id = d.message.getId(); + } + dialogsCountToCheck.add(d.dialogId); + break; + } + } + if (!found) { + SavedDialog d = SavedDialog.fromMessage(currentAccount, message); + if (newMessagesCount != null) { + d.messagesCount = newMessagesCount; + } + cachedDialogs.add(d); + changed = true; + } + found = false; + for (int j = 0; j < loadedDialogs.size(); ++j) { + SavedDialog d = loadedDialogs.get(j); + if (d.dialogId == dialogId) { + found = true; + if (d.top_message_id < message.id || message.id < 0 && message.date > d.getDate()) { + changed = true; + d.message = new MessageObject(currentAccount, message, false, false); + d.top_message_id = d.message.getId(); + } + dialogsCountToCheck.add(d.dialogId); + break; + } + } + if (!found) { + SavedDialog d = SavedDialog.fromMessage(currentAccount, message); + if (newMessagesCount != null) { + d.messagesCount = newMessagesCount; + } + loadedDialogs.add(d); + changed = true; + } + } + if (!dialogsCountToCheck.isEmpty()) { + updateDialogsCount(dialogsCountToCheck); + } + return changed; } - public void getSavedMessagesDialogs() { - if (loaded || loading) { + public boolean updateSavedDialog(TLRPC.Message message) { + if (message == null) { + return false; + } + long self = UserConfig.getInstance(currentAccount).getClientUserId(); + long dialogId = MessageObject.getSavedDialogId(self, message); + for (int i = 0; i < allDialogs.size(); ++i) { + SavedDialog d = allDialogs.get(i); + if (d.dialogId == dialogId) { + d.message = new MessageObject(currentAccount, message, false, false); + d.top_message_id = d.message.getId(); + return true; + } + } + return false; + } + + public boolean updatedDialogCount(long dialogId, int messagesCount) { + for (int i = 0; i < allDialogs.size(); ++i) { + SavedDialog d = allDialogs.get(i); + if (d.dialogId == dialogId) { + if (d.messagesCount != messagesCount) { + d.messagesCount = messagesCount; + return true; + } + break; + } + } + return false; + } + + public void update(long dialogId, TLRPC.messages_Messages messagesRes) { + boolean changed = false; + changed = updateSavedDialogs(messagesRes.messages) || changed; + if (messagesRes instanceof TLRPC.TL_messages_messagesSlice) { + changed = updatedDialogCount(dialogId, messagesRes.count) || changed; + } else if (messagesRes instanceof TLRPC.TL_messages_messages) { + changed = updatedDialogCount(dialogId, messagesRes.messages.size()) || changed; + } + if (changed) { + AndroidUtilities.runOnUIThread(this::update); + } + } + + public void updateDeleted(LongSparseArray> messageIds) { + boolean changed = false; + ArrayList updateDialogsLastMessageId = new ArrayList<>(); + for (int i = 0; i < messageIds.size(); ++i) { + long did = messageIds.keyAt(i); + ArrayList ids = messageIds.valueAt(i); + int maxId = 0; + for (int j = 0; j < ids.size(); ++j) { + maxId = Math.max(maxId, ids.get(j)); + } + SavedDialog d = null; + for (int j = 0; j < allDialogs.size(); ++j) { + if (allDialogs.get(j).dialogId == did) { + d = allDialogs.get(j); + break; + } + } + if (d != null) { + if (Math.max(0, d.messagesCount - ids.size()) != d.messagesCount) { + d.messagesCount = Math.max(0, d.messagesCount - ids.size()); + changed = true; + } + if (d.messagesCount <= 0) { + removeDialog(d.dialogId); + changed = true; + } else if (d.top_message_id <= maxId) { + updateDialogsLastMessageId.add(d); + changed = true; + } + } + } + if (changed) { + if (!updateDialogsLastMessageId.isEmpty()) { + updateDialogsLastMessage(updateDialogsLastMessageId); + } else { + update(); + } + } + } + + private void invalidate() { + if (dialogsLoaded && loadedDialogs.isEmpty()) { return; } - loading = true; - final long myself = UserConfig.getInstance(currentAccount).getClientUserId(); - MessagesStorage storage = MessagesStorage.getInstance(currentAccount); - storage.getStorageQueue().postRunnable(() -> { - SQLiteDatabase database = storage.getDatabase(); + + // put everything in cached + for (int i = 0; i < loadedDialogs.size(); ++i) { + SavedDialog ld = loadedDialogs.get(i); + SavedDialog cd = null; + for (int j = 0; j < cachedDialogs.size(); ++j) { + SavedDialog d = cachedDialogs.get(j); + if (d.dialogId == ld.dialogId) { + cd = d; + break; + } + } + if (cd == null && !ld.pinned) { + cachedDialogs.add(ld); + } + } + // reload + loadedDialogs.clear(); + dialogsLoaded = false; + dialogsCount = 0; + dialogsEndReached = false; + update(); + loadDialogs(); + } + + public void deleteDialog(long did) { + dialogsCount -= removeDialog(did); + update(); + } + + public void deleteDialogs(ArrayList dids) { + for (int i = 0; i < dids.size(); ++i) { + dialogsCount -= removeDialog(dids.get(i)); + } + update(); + } + + private int removeDialog(long did) { + int acount = 0; + for (int i = 0; i < allDialogs.size(); ++i) { + if (allDialogs.get(i).dialogId == did) { + allDialogs.remove(i); + acount++; + i--; + } + } + int lcount = 0; + for (int i = 0; i < loadedDialogs.size(); ++i) { + if (loadedDialogs.get(i).dialogId == did) { + loadedDialogs.remove(i); + lcount++; + i--; + } + } + for (int i = 0; i < cachedDialogs.size(); ++i) { + if (cachedDialogs.get(i).dialogId == did) { + cachedDialogs.remove(i); + i--; + } + } + return Math.max(acount, lcount); + } + + public void update() { + updateAllDialogs(true); + saveCacheSchedule(); + } + + public boolean updatePinned(ArrayList dialogIds, boolean pin) { + ArrayList currentOrder = getCurrentPinnedOrder(allDialogs); + ArrayList newOrder = new ArrayList<>(currentOrder); + for (int i = dialogIds.size() - 1; i >= 0; --i) { + final long did = dialogIds.get(i); + if (pin && !newOrder.contains(did)) { + newOrder.add(0, did); + } else if (!pin) { + newOrder.remove(did); + } + } + int limit = ( + UserConfig.getInstance(currentAccount).isPremium() ? + MessagesController.getInstance(currentAccount).savedDialogsPinnedLimitPremium : + MessagesController.getInstance(currentAccount).savedDialogsPinnedLimitDefault + ); + if (newOrder.size() > limit) { + return false; + } + if (!sameOrder(currentOrder, newOrder)) { + updatePinnedOrderToServer(newOrder); + } + return true; + } + + public boolean updatePinnedOrder(ArrayList newOrder) { + ArrayList currentOrder = getCurrentPinnedOrder(allDialogs); + int limit = ( + UserConfig.getInstance(currentAccount).isPremium() ? + MessagesController.getInstance(currentAccount).savedDialogsPinnedLimitPremium : + MessagesController.getInstance(currentAccount).savedDialogsPinnedLimitDefault + ); + if (newOrder.size() > limit) { + return false; + } + if (!sameOrder(currentOrder, newOrder)) { + updatePinnedOrderToServer(newOrder); + } + return true; + } + + private void updatePinnedOrderToServer(ArrayList newOrder) { + final boolean updateLoaded = updatePinnedOrder(loadedDialogs, newOrder); + final boolean updateCached = updatePinnedOrder(cachedDialogs, newOrder); + if (updateLoaded || updateCached) { + TLRPC.TL_messages_reorderPinnedSavedDialogs req = new TLRPC.TL_messages_reorderPinnedSavedDialogs(); + req.force = true; + for (int i = 0; i < newOrder.size(); ++i) { + final long did = newOrder.get(i); + TLRPC.TL_inputDialogPeer dialogPeer = new TLRPC.TL_inputDialogPeer(); + dialogPeer.peer = MessagesController.getInstance(currentAccount).getInputPeer(did); + if (dialogPeer.peer != null) { + req.order.add(dialogPeer); + } + } + ConnectionsManager.getInstance(currentAccount).sendRequest(req, null); + update(); + } + } + + public void processUpdate(TLRPC.Update update) { + if (processUpdateInternal(update)) { + update(); + } + } + + private boolean processUpdateInternal(TLRPC.Update update) { + if (update instanceof TLRPC.TL_updateSavedDialogPinned) { + TLRPC.TL_updateSavedDialogPinned upd = (TLRPC.TL_updateSavedDialogPinned) update; + if (!(upd.peer instanceof TLRPC.TL_dialogPeer)) return false; + long dialogId = DialogObject.getPeerDialogId(((TLRPC.TL_dialogPeer) upd.peer).peer); + boolean changed = false; + ArrayList[] arraysToCheck = new ArrayList[] { loadedDialogs, cachedDialogs }; + for (int a = 0; a < arraysToCheck.length; ++a) { + for (int i = 0; i < arraysToCheck[a].size(); ++i) { + SavedDialog d = arraysToCheck[a].get(i); + if (d.dialogId == dialogId && d.pinned != upd.pinned) { + d.pinned = upd.pinned; + changed = true; + } + } + } + return changed; + } else if (update instanceof TLRPC.TL_updatePinnedSavedDialogs) { + TLRPC.TL_updatePinnedSavedDialogs upd = (TLRPC.TL_updatePinnedSavedDialogs) update; + ArrayList newOrder = new ArrayList<>(upd.order.size()); + for (int i = 0; i < upd.order.size(); ++i) { + TLRPC.DialogPeer dialogPeer = upd.order.get(i); + if (!(dialogPeer instanceof TLRPC.TL_dialogPeer)) { + continue; + } + newOrder.add(DialogObject.getPeerDialogId(((TLRPC.TL_dialogPeer) dialogPeer).peer)); + } + final boolean updateLoaded = updatePinnedOrder(loadedDialogs, newOrder); + final boolean updateCached = updatePinnedOrder(cachedDialogs, newOrder); + return updateLoaded || updateCached; + } + return false; + } + + private ArrayList getCurrentPinnedOrder(ArrayList dialogs) { + ArrayList currentOrder = new ArrayList<>(); + for (int i = 0; i < dialogs.size(); ++i) { + SavedDialog d = dialogs.get(i); + if (d.pinned) currentOrder.add(d.dialogId); + } + return currentOrder; + } + + private boolean sameOrder(ArrayList a, ArrayList b) { + if (a.size() != b.size()) { + return false; + } + for (int i = 0; i < a.size(); ++i) { + if (!Objects.equals(a.get(i), b.get(i))) { + return false; + } + } + return true; + } + + private boolean updatePinnedOrder(ArrayList dialogs, ArrayList order) { + ArrayList currentOrder = getCurrentPinnedOrder(dialogs); + if (sameOrder(order, currentOrder)) { + return false; + } + + // remove all pinned + ArrayList oldPinned = new ArrayList<>(); + for (int i = 0; i < dialogs.size(); ++i) { + SavedDialog d = dialogs.get(i); + if (d.pinned) { + d.pinned = false; + oldPinned.add(d); + dialogs.remove(i); + i--; + } + } + dialogs.addAll(oldPinned); + + // gather new pinned + ArrayList newPinned = new ArrayList<>(); + for (int i = 0; i < dialogs.size(); ++i) { + SavedDialog d = dialogs.get(i); + int index; + if ((index = order.indexOf(d.dialogId)) >= 0) { + d.pinnedOrder = index; + d.pinned = true; + newPinned.add(d); + dialogs.remove(i); + i--; + } + } + + // sort other not pinned + Collections.sort(dialogs, (d1, d2) -> d2.getDate() - d1.getDate()); + + // sort pinned by new order + Collections.sort(newPinned, (d1, d2) -> d1.pinnedOrder - d2.pinnedOrder); + + // add pinned + dialogs.addAll(0, newPinned); + + return true; + } + + private void updateDialogsCount(HashSet dialogIds) { + final long selfId = UserConfig.getInstance(currentAccount).getClientUserId(); + MessagesStorage messagesStorage = MessagesStorage.getInstance(currentAccount); + messagesStorage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase db = messagesStorage.getDatabase(); + LongSparseArray countResult = new LongSparseArray<>(); SQLiteCursor cursor = null; - final LongSparseArray> messages = new LongSparseArray<>(); try { - cursor = database.queryFinalized("SELECT data, mid, date, send_state, read_state, custom_params FROM messages_v2 WHERE out = 0 AND uid = ?", myself); + for (long did : dialogIds) { + cursor = db.queryFinalized("SELECT COUNT(*) FROM messages_topics WHERE uid = ? AND topic_id = ?", selfId, did); + if (cursor.next()) { + countResult.put(did, cursor.intValue(0)); + } + cursor.dispose(); + } + } catch (Exception e) { + FileLog.e(e); + } finally { + if (cursor != null) { + cursor.dispose(); + cursor = null; + } + } + AndroidUtilities.runOnUIThread(() -> { + boolean changed = false; + for (int i = 0; i < countResult.size(); ++i) { + long did = countResult.keyAt(i); + int count = countResult.valueAt(i); + + SavedDialog d = findSavedDialog(did); + if (d != null && d.messagesCount != count) { + d.messagesCount = count; + changed = true; + } + } + if (changed) { + update(); + } + }); + }); + } + + private boolean loadingCache, loadedCache; + private void loadCache(Runnable whenDone) { + if (loadingCache) { + return; + } + loadingCache = true; + final long selfId = UserConfig.getInstance(currentAccount).getClientUserId(); + MessagesStorage messagesStorage = MessagesStorage.getInstance(currentAccount); + messagesStorage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase db = messagesStorage.getDatabase(); + SQLiteCursor cursor = null; + SQLiteCursor cursor2 = null; + final ArrayList dialogs = new ArrayList<>(); + final ArrayList usersToLoad = new ArrayList<>(); + final ArrayList chatsToLoad = new ArrayList<>(); + final ArrayList emojiToLoad = new ArrayList<>(); + + final ArrayList users = new ArrayList<>(); + final ArrayList chats = new ArrayList<>(); + final ArrayList emojis = new ArrayList<>(); + + try { + cursor = db.queryFinalized("SELECT did, date, last_mid, pinned, flags, folder_id, last_mid_group, count FROM saved_dialogs ORDER BY pinned ASC, date DESC"); while (cursor.next()) { - NativeByteBuffer data = cursor.byteBufferValue(0); - if (data != null) { - TLRPC.Message message = TLRPC.Message.TLdeserialize(data, data.readInt32(false), false); - if (message.fwd_from == null || message.fwd_from.saved_from_peer == null) { - continue; - } - long did = DialogObject.getPeerDialogId(message.fwd_from.saved_from_peer); + SavedDialog d = new SavedDialog(); + d.dialogId = cursor.longValue(0); + d.localDate = cursor.intValue(1); + d.top_message_id = cursor.intValue(2); + d.pinnedOrder = cursor.intValue(3); + d.pinned = d.pinnedOrder != 999; + d.messagesCount = cursor.intValue(7); + if (d.dialogId < 0) { + chatsToLoad.add(-d.dialogId); + } else { + usersToLoad.add(d.dialogId); + } - message.id = cursor.intValue(1); - message.date = cursor.intValue(2); - message.send_state = cursor.intValue(3); - MessageObject.setUnreadFlags(message, cursor.intValue(4)); + cursor2 = db.queryFinalized("SELECT data FROM messages_topics WHERE uid = ? AND mid = ? AND topic_id = ?", selfId, d.top_message_id, d.dialogId); + if (cursor2.next()) { + NativeByteBuffer buffer = cursor2.byteBufferValue(0); + TLRPC.Message message = TLRPC.Message.TLdeserialize(buffer, buffer.readInt32(true), true); + MessagesStorage.addUsersAndChatsFromMessage(message, usersToLoad, chatsToLoad, emojiToLoad); + d.message = new MessageObject(currentAccount, message, null, null, null, null, null, false, false, 0, false, false, true); + } + cursor2.dispose(); - MessageObject messageObject = new MessageObject(currentAccount, message, true, true); - ArrayList messageObjects = messages.get(did); - if (messageObjects == null) { - messages.put(did, messageObjects = new ArrayList<>()); - } - messageObjects.add(messageObject); + dialogs.add(d); + } + + if (!usersToLoad.isEmpty()) { + messagesStorage.getUsersInternal(TextUtils.join(",", usersToLoad), users); + } + if (!chatsToLoad.isEmpty()) { + messagesStorage.getChatsInternal(TextUtils.join(",", chatsToLoad), chats); + } + if (!emojiToLoad.isEmpty()) { + messagesStorage.getAnimatedEmoji(TextUtils.join(",", emojiToLoad), emojis); + } + } catch (Exception e) { + FileLog.e(e); + } finally { + if (cursor != null) { + cursor.dispose(); + cursor = null; + } + if (cursor2 != null) { + cursor2.dispose(); + cursor2 = null; + } + } + AndroidUtilities.runOnUIThread(() -> { + loadingCache = false; + loadedCache = true; + + MessagesController.getInstance(currentAccount).putUsers(users, true); + MessagesController.getInstance(currentAccount).putChats(chats, true); + AnimatedEmojiDrawable.getDocumentFetcher(currentAccount).processDocuments(emojis); + + cachedDialogs.clear(); + cachedDialogs.addAll(dialogs); + updateAllDialogs(true); + + if (whenDone != null) { + whenDone.run(); + } + }); + }); + } + private void updateDialogsLastMessage(ArrayList dialogs) { + final long selfId = UserConfig.getInstance(currentAccount).getClientUserId(); + MessagesStorage messagesStorage = MessagesStorage.getInstance(currentAccount); + messagesStorage.getStorageQueue().postRunnable(() -> { + + SQLiteDatabase db = messagesStorage.getDatabase(); + SQLiteCursor cursor = null; + + final ArrayList dialogsToDelete = new ArrayList<>(); + final LongSparseArray newMessages = new LongSparseArray<>(); + + final ArrayList usersToLoad = new ArrayList<>(); + final ArrayList chatsToLoad = new ArrayList<>(); + final ArrayList emojiToLoad = new ArrayList<>(); + + final ArrayList users = new ArrayList<>(); + final ArrayList chats = new ArrayList<>(); + final ArrayList emojis = new ArrayList<>(); + + try { + for (int i = 0; i < dialogs.size(); ++i) { + SavedDialog d = dialogs.get(i); + + cursor = db.queryFinalized("SELECT mid, data FROM messages_topics WHERE uid = ? AND topic_id = ? ORDER BY mid DESC LIMIT 1", selfId, d.dialogId); + if (cursor.next()) { + int topMessageId = cursor.intValue(0); + NativeByteBuffer buffer = cursor.byteBufferValue(1); + TLRPC.Message message = TLRPC.Message.TLdeserialize(buffer, buffer.readInt32(true), true); + MessagesStorage.addUsersAndChatsFromMessage(message, usersToLoad, chatsToLoad, emojiToLoad); + newMessages.put(d.dialogId, message); + } else { + dialogsToDelete.add(d.dialogId); } + cursor.dispose(); } - AndroidUtilities.runOnUIThread(() -> { - SavedMessagesController.this.messages.clear(); - SavedMessagesController.this.messages.putAll(messages); - loading = false; - }); - } catch (SQLiteException e) { - e.printStackTrace(); + if (!usersToLoad.isEmpty()) { + messagesStorage.getUsersInternal(TextUtils.join(",", usersToLoad), users); + } + if (!chatsToLoad.isEmpty()) { + messagesStorage.getChatsInternal(TextUtils.join(",", chatsToLoad), chats); + } + if (!emojiToLoad.isEmpty()) { + messagesStorage.getAnimatedEmoji(TextUtils.join(",", emojiToLoad), emojis); + } + } catch (Exception e) { + FileLog.e(e); } finally { if (cursor != null) { cursor.dispose(); cursor = null; } } + AndroidUtilities.runOnUIThread(() -> { + MessagesController.getInstance(currentAccount).putUsers(users, true); + MessagesController.getInstance(currentAccount).putChats(chats, true); + AnimatedEmojiDrawable.getDocumentFetcher(currentAccount).processDocuments(emojis); + + for (int i = 0; i < dialogsToDelete.size(); ++i) { + removeDialog(dialogsToDelete.get(i)); + } + for (int i = 0; i < newMessages.size(); ++i) { + long did = newMessages.keyAt(i); + TLRPC.Message message = newMessages.valueAt(i); + MessageObject messageObject = new MessageObject(currentAccount, message, null, null, null, null, null, false, false, 0, false, false, true); + for (int j = 0; j < loadedDialogs.size(); ++j) { + SavedDialog d = loadedDialogs.get(j); + if (d.dialogId == did) { + d.top_message_id = messageObject.getId(); + d.message = messageObject; + } + } + for (int j = 0; j < cachedDialogs.size(); ++j) { + SavedDialog d = cachedDialogs.get(j); + if (d.dialogId == did) { + d.top_message_id = messageObject.getId(); + d.message = messageObject; + } + } + } + + update(); + }); + }); + } + + private final Runnable saveCacheRunnable = this::saveCache; + private void saveCacheSchedule() { + AndroidUtilities.cancelRunOnUIThread(saveCacheRunnable); + AndroidUtilities.runOnUIThread(saveCacheRunnable, 450); + } + + private boolean saving; + private void saveCache() { + if (saving) { + return; + } + saving = true; + ArrayList dialogsToSave = new ArrayList(allDialogs); + MessagesStorage messagesStorage = MessagesStorage.getInstance(currentAccount); + messagesStorage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase db = messagesStorage.getDatabase(); + SQLitePreparedStatement state = null; + try { + db.executeFast("DELETE FROM saved_dialogs").stepThis().dispose(); + state = db.executeFast("REPLACE INTO saved_dialogs VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); + for (int i = 0; i < dialogsToSave.size(); ++i) { + SavedDialog d = dialogsToSave.get(i); + state.requery(); + state.bindLong(1, d.dialogId); + state.bindInteger(2, d.getDate()); + state.bindInteger(3, d.top_message_id); + state.bindInteger(4, d.pinned ? i : 999); + state.bindInteger(5, 0); + state.bindInteger(6, 0); + state.bindInteger(7, 0); + state.bindInteger(8, d.messagesCount); + state.step(); + } + state.dispose(); + + } catch (Exception e) { + FileLog.e(e); + } finally { + if (state != null) { + state.dispose(); + state = null; + } + } + AndroidUtilities.runOnUIThread(() -> { + saving = false; + }); + }); + } + private void deleteCache() { + if (saving) { + return; + } + saving = true; + MessagesStorage messagesStorage = MessagesStorage.getInstance(currentAccount); + messagesStorage.getStorageQueue().postRunnable(() -> { + SQLiteDatabase db = messagesStorage.getDatabase(); + try { + db.executeFast("DELETE FROM saved_dialogs").stepThis().dispose(); + } catch (Exception e) { + FileLog.e(e); + } + AndroidUtilities.runOnUIThread(() -> { + saving = false; + loadedCache = false; + }); }); } + + public static class SavedDialog { + public long dialogId; + public boolean pinned; + public int top_message_id; + public MessageObject message; + public int messagesCount; + + // used only in cache + private int localDate; + + // used only when sorting in update + private int pinnedOrder; + + public int getDate() { + if (message == null || message.messageOwner == null) { + return localDate; + } + if ((message.messageOwner.flags & TLRPC.MESSAGE_FLAG_EDITED) != 0) { + return message.messageOwner.edit_date; + } + return message.messageOwner.date; + } + + public boolean isHidden() { + return message != null && message.messageOwner != null && message.messageOwner.action instanceof TLRPC.TL_messageActionHistoryClear; + } + + public static SavedDialog fromMessage(int currentAccount, TLRPC.Message message) { + SavedDialog d = new SavedDialog(); + d.dialogId = MessageObject.getSavedDialogId(UserConfig.getInstance(currentAccount).getClientUserId(), message); + d.pinned = false; + d.top_message_id = message.id; + d.message = new MessageObject(currentAccount, message, null, null, null, null, null, false, false, 0, false, false, true); + return d; + } + + public static SavedDialog fromTL(int currentAccount, TLRPC.TL_savedDialog tl, ArrayList messages) { + SavedDialog d = new SavedDialog(); + d.dialogId = DialogObject.getPeerDialogId(tl.peer); + d.pinned = tl.pinned; + d.top_message_id = tl.top_message; + TLRPC.Message message = null; + for (int i = 0; i < messages.size(); ++i) { + TLRPC.Message msg = messages.get(i); + if (d.top_message_id == msg.id) { + message = msg; + break; + } + } + if (message != null) { + d.message = new MessageObject(currentAccount, message, null, null, null, null, null, false, false, 0, false, false, true); + } + return d; + } + } + public static void openSavedMessages() { + BaseFragment lastFragment = LaunchActivity.getLastFragment(); + if (lastFragment == null) { + return; + } + Bundle args = new Bundle(); + args.putLong("user_id", UserConfig.getInstance(lastFragment.getCurrentAccount()).getClientUserId()); + lastFragment.presentFragment(new ChatActivity(args)); + } + + private final LongSparseArray>> checkMessagesCallbacks = new LongSparseArray<>(); + public void hasSavedMessages(long did, Utilities.Callback whenDone) { + if (whenDone == null) return; + + final SavedDialog savedDialog = findSavedDialog(did); + if (savedDialog != null && savedDialog.messagesCount > 0) { + whenDone.run(true); + return; + } + + ArrayList> existingCallbacks = checkMessagesCallbacks.get(did); + if (existingCallbacks != null) { + existingCallbacks.add(whenDone); + return; + } + existingCallbacks = new ArrayList<>(); + existingCallbacks.add(whenDone); + checkMessagesCallbacks.put(did, existingCallbacks); + + TLRPC.TL_messages_getSavedHistory req = new TLRPC.TL_messages_getSavedHistory(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(did); + req.limit = 1; + req.hash = 0; + req.offset_id = Integer.MAX_VALUE; + req.offset_date = Integer.MAX_VALUE; + req.add_offset = -1; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TLRPC.messages_Messages) { + TLRPC.messages_Messages r = (TLRPC.messages_Messages) res; + int count = r.messages.size(); + if (r instanceof TLRPC.TL_messages_messagesSlice) { + count = ((TLRPC.TL_messages_messagesSlice) r).count; + } + + MessagesController.getInstance(currentAccount).putUsers(r.users, false); + MessagesController.getInstance(currentAccount).putChats(r.chats, false); + MessagesStorage.getInstance(currentAccount).putUsersAndChats(r.users, r.chats, true, true); + + boolean hasMessages = count > 0; + if (count > 0) { + if (!updatedDialogCount(did, count)) { + if (!r.messages.isEmpty()) { + SavedDialog dialog = SavedDialog.fromMessage(currentAccount, r.messages.get(0)); + dialog.messagesCount = count; + cachedDialogs.add(dialog); + update(); + } + } + } + + ArrayList> callbacks = checkMessagesCallbacks.get(did); + checkMessagesCallbacks.remove(did); + if (callbacks != null) { + for (int i = 0; i < callbacks.size(); ++i) { + callbacks.get(i).run(hasMessages); + } + } + } + })); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SecretChatHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SecretChatHelper.java index 3880ca670b..e003ac849c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SecretChatHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SecretChatHelper.java @@ -154,7 +154,7 @@ private TLRPC.TL_messageService createServiceSecretMessage(TLRPC.EncryptedChat e ArrayList arr = new ArrayList<>(); arr.add(newMsg); - getMessagesStorage().putMessages(arr, false, true, true, 0, false, 0); + getMessagesStorage().putMessages(arr, false, true, true, 0, false, 0, 0); return newMsg; } @@ -534,7 +534,7 @@ private void updateMediaPaths(MessageObject newMsgObj, TLRPC.EncryptedFile file, ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, ImageLocation.getForPhoto(size, newMsg.media.photo), true); ArrayList arr = new ArrayList<>(); arr.add(newMsg); - getMessagesStorage().putMessages(arr, false, true, false, 0, false, 0); + getMessagesStorage().putMessages(arr, false, true, false, 0, false, 0, 0); //getMessagesStorage().putSentFile(originalPath, newMsg.media.photo, 3); } else if (newMsg.media instanceof TLRPC.TL_messageMediaDocument && newMsg.media.document != null) { @@ -568,7 +568,7 @@ private void updateMediaPaths(MessageObject newMsgObj, TLRPC.EncryptedFile file, ArrayList arr = new ArrayList<>(); arr.add(newMsg); - getMessagesStorage().putMessages(arr, false, true, false, 0, false, 0); + getMessagesStorage().putMessages(arr, false, true, false, 0, 0, 0); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java index 274bd9fdc1..6443c9c134 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java @@ -1134,7 +1134,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { ArrayList messages = new ArrayList<>(); messages.add(obj.messageOwner); - getMessagesStorage().putMessages(messages, false, true, false, 0, obj.scheduled, 0); + getMessagesStorage().putMessages(messages, false, true, false, 0, obj.scheduled ? 1 : 0, 0); break; } } @@ -1145,7 +1145,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { ArrayList messages = new ArrayList<>(); messages.add(message.obj.messageOwner); - getMessagesStorage().putMessages(messages, false, true, false, 0, message.obj.scheduled, 0); + getMessagesStorage().putMessages(messages, false, true, false, 0, message.obj.scheduled ? 1 : 0, 0); break; } } @@ -1216,7 +1216,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { messageObject.messageOwner.attachPath = cacheFile.toString(); ArrayList messages = new ArrayList<>(); messages.add(messageObject.messageOwner); - getMessagesStorage().putMessages(messages, false, true, false, 0, messageObject.scheduled, 0); + getMessagesStorage().putMessages(messages, false, true, false, 0, messageObject.scheduled ? 1 : 0, 0); getNotificationCenter().postNotificationName(NotificationCenter.updateMessageMedia, messageObject.messageOwner); message.photoSize = photo.sizes.get(photo.sizes.size() - 1); message.locationParent = photo; @@ -1265,7 +1265,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } ArrayList messages = new ArrayList<>(); messages.add(messageObject.messageOwner); - getMessagesStorage().putMessages(messages, false, true, false, 0, messageObject.scheduled, 0); + getMessagesStorage().putMessages(messages, false, true, false, 0, messageObject.scheduled ? 1 : 0, 0); message.performMediaUpload = true; performSendDelayedMessage(message); getNotificationCenter().postNotificationName(NotificationCenter.updateMessageMedia, message.obj.messageOwner); @@ -1328,7 +1328,7 @@ private void revertEditingMessageObject(MessageObject object) { ArrayList arr = new ArrayList<>(); arr.add(object.messageOwner); - getMessagesStorage().putMessages(arr, false, true, false, 0, object.scheduled, 0); + getMessagesStorage().putMessages(arr, false, true, false, 0, object.scheduled ? 1 : 0, 0); ArrayList arrayList = new ArrayList<>(); arrayList.add(object); @@ -1406,7 +1406,7 @@ public void cancelSendingMessage(ArrayList objects) { TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages(); messagesRes.messages.add(prevMessage.messageOwner); - getMessagesStorage().putMessages(messagesRes, message.peer, -2, 0, false, scheduled, 0); + getMessagesStorage().putMessages(messagesRes, message.peer, -2, 0, false, scheduled ? 1 : 0, 0); } if (!checkReadyToSendGroups.contains(message)) { checkReadyToSendGroups.add(message); @@ -1620,7 +1620,7 @@ public void sendScreenshotMessage(TLRPC.User user, int messageId, TLRPC.Message getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload); ArrayList arr = new ArrayList<>(); arr.add(message); - getMessagesStorage().putMessages(arr, false, true, false, 0, false, 0); + getMessagesStorage().putMessages(arr, false, true, false, 0, false, 0, 0); performSendMessageRequest(req, newMsgObj, null, null, null, null, false); } @@ -2140,7 +2140,7 @@ public int sendMessage(ArrayList messages, final long peer, boole } if (arr.size() == 100 || a == messages.size() - 1 || a != messages.size() - 1 && messages.get(a + 1).getDialogId() != msgObj.getDialogId()) { - getMessagesStorage().putMessages(new ArrayList<>(arr), false, true, false, 0, scheduleDate != 0, 0); + getMessagesStorage().putMessages(new ArrayList<>(arr), false, true, false, 0, scheduleDate != 0 ? 1 : 0, 0); getMessagesController().updateInterfaceWithMessages(peer, objArr, scheduleDate != 0); getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload); getUserConfig().saveConfig(false); @@ -2261,7 +2261,7 @@ public int sendMessage(ArrayList messages, final long peer, boole messageIds.add(oldId); getMessagesController().deleteMessages(messageIds, null, null, newMsgObj1.dialog_id, false, true); getMessagesStorage().getStorageQueue().postRunnable(() -> { - getMessagesStorage().putMessages(sentMessages, true, false, false, 0, false, 0); + getMessagesStorage().putMessages(sentMessages, true, false, false, 0, 0, 0); AndroidUtilities.runOnUIThread(() -> { ArrayList messageObjects = new ArrayList<>(); messageObjects.add(new MessageObject(msgObj.currentAccount, msgObj.messageOwner, true, true)); @@ -2275,7 +2275,7 @@ public int sendMessage(ArrayList messages, final long peer, boole } else { getMessagesStorage().getStorageQueue().postRunnable(() -> { getMessagesStorage().updateMessageStateAndId(newMsgObj1.random_id, MessageObject.getPeerId(peer_id), oldId, newMsgObj1.id, 0, false, scheduleDate != 0 ? 1 : 0); - getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduleDate != 0, 0); + getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduleDate != 0 ? 1 : 0, 0); AndroidUtilities.runOnUIThread(() -> { newMsgObj1.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; getMediaDataController().increasePeerRaiting(peer); @@ -2542,7 +2542,7 @@ public void editMessage(MessageObject messageObject, TLRPC.TL_photo photo, Video ArrayList arr = new ArrayList<>(); arr.add(newMsg); - getMessagesStorage().putMessages(arr, false, true, false, 0, messageObject.scheduled, MessageObject.getTopicId(newMsg, getMessagesController().isForum(newMsg))); + getMessagesStorage().putMessages(arr, false, true, false, 0, messageObject.scheduled ? 1 : 0, 0); getMessagesController().getTopicsController().processEditedMessage(newMsg); messageObject.type = -1; @@ -3971,7 +3971,7 @@ public void sendMessage(SendMessageParams sendMessageParams) { } } if (isForum) { - anotherTopic = replyToTopMsg.getId() != replyToMsg.getId() && MessageObject.getTopicId(replyToMsg.messageOwner, true) != replyToTopMsg.getId(); + anotherTopic = replyToTopMsg.getId() != replyToMsg.getId() && MessageObject.getTopicId(currentAccount, replyToMsg.messageOwner, true) != replyToTopMsg.getId(); } } if (anotherChat || anotherTopic) { @@ -4036,6 +4036,8 @@ public void sendMessage(SendMessageParams sendMessageParams) { } if (newMsgObj.videoEditedInfo != null && videoEditedInfo == null) { videoEditedInfo = newMsgObj.videoEditedInfo; + } else if (videoEditedInfo != null && videoEditedInfo.notReadyYet) { + newMsgObj.videoEditedInfo.notReadyYet = videoEditedInfo.notReadyYet; } if (groupId == 0) { @@ -4043,7 +4045,7 @@ public void sendMessage(SendMessageParams sendMessageParams) { objArr.add(newMsgObj); ArrayList arr = new ArrayList<>(); arr.add(newMsg); - MessagesStorage.getInstance(currentAccount).putMessages(arr, false, true, false, 0, scheduleDate != 0, 0); + MessagesStorage.getInstance(currentAccount).putMessages(arr, false, true, false, 0, scheduleDate != 0 ? 1 : 0, 0); MessagesController.getInstance(currentAccount).updateInterfaceWithMessages(peer, objArr, scheduleDate != 0); if (scheduleDate == 0) { NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.dialogsNeedReload); @@ -5057,10 +5059,12 @@ private void performSendDelayedMessage(final DelayedMessage message, int index) location = FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE) + "/" + document.id + ".mp4"; } putToDelayedMessages(location, message); - if (message.obj.videoEditedInfo != null && message.obj.videoEditedInfo.needConvert()) { - getFileLoader().uploadFile(location, false, false, document.size, ConnectionsManager.FileTypeVideo, false); - } else { - getFileLoader().uploadFile(location, false, false, ConnectionsManager.FileTypeVideo); + if (message.obj.videoEditedInfo == null || !message.obj.videoEditedInfo.notReadyYet) { + if (message.obj.videoEditedInfo != null && message.obj.videoEditedInfo.needConvert()) { + getFileLoader().uploadFile(location, false, false, document.size, ConnectionsManager.FileTypeVideo, false); + } else { + getFileLoader().uploadFile(location, false, false, ConnectionsManager.FileTypeVideo); + } } putToUploadingMessages(message.obj); } else { @@ -5392,7 +5396,7 @@ private void sendReadyToSendGroup(DelayedMessage message, boolean add, boolean c return; } else if (add) { delayedMessages.remove(key); - getMessagesStorage().putMessages(message.messages, false, true, false, 0, message.scheduled, 0); + getMessagesStorage().putMessages(message.messages, false, true, false, 0, message.scheduled ? 1 : 0, 0); getMessagesController().updateInterfaceWithMessages(message.peer, message.messageObjects, message.scheduled); if (!message.scheduled) { getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload); @@ -5731,7 +5735,7 @@ protected void performSendMessageRequestMulti(final TLRPC.TL_messages_sendMultiM getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, scheduled); getMessagesStorage().getStorageQueue().postRunnable(() -> { getMessagesStorage().updateMessageStateAndId(newMsgObj.random_id, MessageObject.getPeerId(newMsgObj.peer_id), oldId, newMsgObj.id, 0, false, scheduled ? 1 : 0); - getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduled, 0); + getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduled ? 1 : 0, 0); AndroidUtilities.runOnUIThread(() -> { getMediaDataController().increasePeerRaiting(newMsgObj.dialog_id); getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, grouped_id, existFlags, scheduled); @@ -6035,7 +6039,7 @@ protected void performSendMessageRequest(final TLObject req, final MessageObject messageIds.add(oldId); getMessagesController().deleteMessages(messageIds, null, null, newMsgObj.dialog_id, false, true); getMessagesStorage().getStorageQueue().postRunnable(() -> { - getMessagesStorage().putMessages(sentMessages, true, false, false, 0, false, 0); + getMessagesStorage().putMessages(sentMessages, true, false, false, 0, false, 0, 0); AndroidUtilities.runOnUIThread(() -> { ArrayList messageObjects = new ArrayList<>(); messageObjects.add(new MessageObject(msgObj.currentAccount, msgObj.messageOwner, true, true)); @@ -6049,7 +6053,7 @@ protected void performSendMessageRequest(final TLObject req, final MessageObject getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, 0L, existFlags, scheduled); getMessagesStorage().getStorageQueue().postRunnable(() -> { getMessagesStorage().updateMessageStateAndId(newMsgObj.random_id, MessageObject.getPeerId(newMsgObj.peer_id), oldId, newMsgObj.id, 0, false, scheduled ? 1 : 0); - getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduled, 0); + getMessagesStorage().putMessages(sentMessages, true, false, false, 0, scheduled ? 1 : 0, 0); AndroidUtilities.runOnUIThread(() -> { getMediaDataController().increasePeerRaiting(newMsgObj.dialog_id); getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, 0L, existFlags, scheduled); @@ -6987,7 +6991,7 @@ private static void finishGroup(AccountInstance accountInstance, long groupId, i TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages(); messagesRes.messages.add(prevMessage.messageOwner); - accountInstance.getMessagesStorage().putMessages(messagesRes, message.peer, -2, 0, false, scheduleDate != 0, 0); + accountInstance.getMessagesStorage().putMessages(messagesRes, message.peer, -2, 0, false, scheduleDate != 0 ? 1 : 0, 0); instance.sendReadyToSendGroup(message, true, true); } }); @@ -7478,7 +7482,7 @@ public static void prepareSendingText(AccountInstance accountInstance, String te } @UiThread - public static void prepareSendingText(AccountInstance accountInstance, String text, long dialogId, int topicId, boolean notify, int scheduleDate) { + public static void prepareSendingText(AccountInstance accountInstance, String text, long dialogId, long topicId, boolean notify, int scheduleDate) { accountInstance.getMessagesStorage().getStorageQueue().postRunnable(() -> Utilities.stageQueue.postRunnable(() -> AndroidUtilities.runOnUIThread(() -> { String textFinal = getTrimmedString(text); if (textFinal.length() != 0) { @@ -8625,7 +8629,12 @@ public static void prepareSendingVideo(AccountInstance accountInstance, String v } } if (document == null) { - thumb = createVideoThumbnailAtTime(videoPath, startTime); + if (videoEditedInfo != null && videoEditedInfo.notReadyYet) { + thumb = videoEditedInfo.thumb; + } + if (thumb == null) { + thumb = createVideoThumbnailAtTime(videoPath, startTime); + } if (thumb == null) { thumb = createVideoThumbnail(videoPath, MediaStore.Video.Thumbnails.MINI_KIND); } @@ -8669,14 +8678,19 @@ public static void prepareSendingVideo(AccountInstance accountInstance, String v } attributeVideo.round_message = isRound; document.attributes.add(attributeVideo); - if (videoEditedInfo != null && videoEditedInfo.needConvert()) { + if (videoEditedInfo != null && videoEditedInfo.notReadyYet) { + attributeVideo.w = videoEditedInfo.resultWidth; + attributeVideo.h = videoEditedInfo.resultHeight; + attributeVideo.duration = videoEditedInfo.estimatedDuration / 1000.0; + document.size = videoEditedInfo.estimatedSize; + } else if (videoEditedInfo != null && videoEditedInfo.needConvert()) { if (videoEditedInfo.muted) { document.attributes.add(new TLRPC.TL_documentAttributeAnimated()); fillVideoAttribute(videoPath, attributeVideo, videoEditedInfo); videoEditedInfo.originalWidth = attributeVideo.w; videoEditedInfo.originalHeight = attributeVideo.h; } else { - attributeVideo.duration = (int) (videoEditedInfo.estimatedDuration / 1000); + attributeVideo.duration = videoEditedInfo.estimatedDuration / 1000.0; } int w, h; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TopicsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/TopicsController.java index 4b2bd26573..e07eae3b57 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TopicsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TopicsController.java @@ -118,7 +118,7 @@ public void loadTopics(long chatId, boolean fromCache, int loadType) { topicsIsLoading.put(chatId, 0); processTopics(chatId, topics.topics, messagesMap, false, loadType, ((TLRPC.TL_messages_forumTopics) response).count); - getMessagesStorage().putMessages(topics.messages, false, true, false, 0, false, 0); + getMessagesStorage().putMessages(topics.messages, false, true, false, 0, false, 0, 0); sortTopics(chatId); getMessagesStorage().saveTopics(-chatId, topicsByChatId.get(chatId), true, true); @@ -390,7 +390,7 @@ public void reloadTopics(long chatId, ArrayList topicsToRel getMessagesController().putChats(((TLRPC.TL_messages_forumTopics) response).chats, false); processTopics(chatId, topics.topics, messagesMap, false, LOAD_TYPE_LOAD_UNKNOWN, -1); - getMessagesStorage().putMessages(topics.messages, false, true, false, 0, false, 0); + getMessagesStorage().putMessages(topics.messages, false, true, false, 0, false, 0, 0); getMessagesStorage().saveTopics(-chatId, topicsByChatId.get(chatId), true, true); if (callback != null) { callback.run(); @@ -400,7 +400,7 @@ public void reloadTopics(long chatId, ArrayList topicsToRel })); } - public void updateMaxReadId(long chatId, int topicId, int readMaxId, int unreadCount, int mentionsUnread) { + public void updateMaxReadId(long chatId, long topicId, int readMaxId, int unreadCount, int mentionsUnread) { TLRPC.TL_forumTopic topic = findTopic(chatId, topicId); if (topic != null) { topic.read_inbox_max_id = readMaxId; @@ -412,7 +412,7 @@ public void updateMaxReadId(long chatId, int topicId, int readMaxId, int unreadC } } - public TLRPC.TL_forumTopic findTopic(long chatId, int topicId) { + public TLRPC.TL_forumTopic findTopic(long chatId, long topicId) { LongSparseArray topicsMap = topicsMapByChatId.get(chatId); if (topicsMap != null) { return topicsMap.get(topicId); @@ -717,7 +717,7 @@ public void reorderPinnedTopics(long chatId, ArrayList topics) { ConnectionsManager.getInstance(currentAccount).sendRequest(req, null); } - public void updateMentionsUnread(long dialogId, int topicId, int topicMentionsCount) { + public void updateMentionsUnread(long dialogId, long topicId, int topicMentionsCount) { AndroidUtilities.runOnUIThread(() -> { TLRPC.TL_forumTopic topic = findTopic(-dialogId, topicId); if (topic != null) { @@ -727,7 +727,7 @@ public void updateMentionsUnread(long dialogId, int topicId, int topicMentionsCo }); } - public int updateReactionsUnread(long dialogId, int topicId, int count, boolean increment) { + public int updateReactionsUnread(long dialogId, long topicId, int count, boolean increment) { TLRPC.TL_forumTopic topic = findTopic(-dialogId, topicId); int totalCount = -1; if (topic != null) { @@ -745,7 +745,7 @@ public int updateReactionsUnread(long dialogId, int topicId, int count, boolean return totalCount; } - public void markAllReactionsAsRead(long chatId, int topicId) { + public void markAllReactionsAsRead(long chatId, long topicId) { TLRPC.TL_forumTopic topic = findTopic(chatId, topicId); if (topic != null && topic.unread_reactions_count > 0) { topic.unread_reactions_count = 0; @@ -811,7 +811,7 @@ public void processUpdate(List topicUpdates) { topicsToReload.put(update.dialogId, arrayList); } TLRPC.TL_forumTopic forumTopic = new TLRPC.TL_forumTopic(); - forumTopic.id = update.topicId; + forumTopic.id = (int) update.topicId; arrayList.add(forumTopic); } else { TLRPC.TL_forumTopic topic = findTopic(-update.dialogId, update.topicId); @@ -1031,7 +1031,7 @@ private class TopicsLoadOffset { public static class TopicUpdate { public int totalMessagesCount = -1; long dialogId; - int topicId; + long topicId; int unreadMentions; int unreadCount; int topMessageId; @@ -1057,13 +1057,13 @@ public void onTopicFragmentPause(long chatId) { openedTopicsBuChatId.put(chatId, v); } - public void getTopicRepliesCount(long dialogId, int topicId) { + public void getTopicRepliesCount(long dialogId, long topicId) { TLRPC.TL_forumTopic topic = findTopic(-dialogId, topicId); if (topic != null) { if (topic.totalMessagesCount == 0) { TLRPC.TL_messages_getReplies req = new TLRPC.TL_messages_getReplies(); req.peer = getMessagesController().getInputPeer(dialogId); - req.msg_id = topicId; + req.msg_id = (int) topicId; req.limit = 1; getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (response != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java index 6af2c53cb9..7279b93266 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/UserObject.java @@ -18,6 +18,7 @@ public class UserObject { public static final long REPLY_BOT = 1271266957L; + public static final long ANONYMOUS = 2666000L; public static boolean isDeleted(TLRPC.User user) { return user == null || user instanceof TLRPC.TL_userDeleted_old2 || user instanceof TLRPC.TL_userEmpty || user.deleted; @@ -32,7 +33,11 @@ public static boolean isUserSelf(TLRPC.User user) { } public static boolean isReplyUser(TLRPC.User user) { - return user != null && (user.id == 708513 || user.id == REPLY_BOT); + return user != null && (user.id == 708513L || user.id == REPLY_BOT); + } + + public static boolean isAnonymous(TLRPC.User user) { + return user != null && user.id == ANONYMOUS; } public static boolean isReplyUser(long did) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java index 68c1cd56ef..402c69cc50 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/Utilities.java @@ -12,6 +12,7 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.Rect; import com.carrotsearch.randomizedtesting.Xoroshiro128PlusRandom; @@ -86,12 +87,21 @@ public class Utilities { public static native void setupNativeCrashesListener(String path); public static Bitmap stackBlurBitmapMax(Bitmap bitmap) { + return stackBlurBitmapMax(bitmap, false); + } + + public static Bitmap stackBlurBitmapMax(Bitmap bitmap, boolean round) { int w = AndroidUtilities.dp(20); int h = (int) (AndroidUtilities.dp(20) * (float) bitmap.getHeight() / bitmap.getWidth()); Bitmap scaledBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(scaledBitmap); canvas.save(); canvas.scale((float) scaledBitmap.getWidth() / bitmap.getWidth(), (float) scaledBitmap.getHeight() / bitmap.getHeight()); + if (round) { + Path path = new Path(); + path.addCircle(bitmap.getWidth() / 2f, bitmap.getHeight() / 2f, Math.min(bitmap.getWidth(), bitmap.getHeight()) / 2f - 1, Path.Direction.CW); + canvas.clipPath(path); + } canvas.drawBitmap(bitmap, 0, 0, null); canvas.restore(); Utilities.stackBlurBitmap(scaledBitmap, Math.max(10, Math.max(w, h) / 150)); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java b/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java index 5dc4868b3c..1701798152 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java @@ -62,6 +62,9 @@ public class VideoEditedInfo { public boolean isStory; public StoryEntry.HDRInfo hdrInfo; + public Bitmap thumb; + public boolean notReadyYet; + public Integer gradientTopColor, gradientBottomColor; public int account; public boolean isDark; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/VideoEncodingService.java b/TMessagesProj/src/main/java/org/telegram/messenger/VideoEncodingService.java index 115b0fc21b..0c9f6bf851 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/VideoEncodingService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/VideoEncodingService.java @@ -31,8 +31,12 @@ public VideoEncodingService() { public static void start(boolean cancelled) { if (instance == null) { - Intent intent = new Intent(ApplicationLoader.applicationContext, VideoEncodingService.class); - ApplicationLoader.applicationContext.startService(intent); + try { + Intent intent = new Intent(ApplicationLoader.applicationContext, VideoEncodingService.class); + ApplicationLoader.applicationContext.startService(intent); + } catch (Exception e) { + FileLog.e(e); + } } else if (cancelled) { MediaController.VideoConvertMessage messageInController = MediaController.getInstance().getCurrentForegroundConverMessage(); if (instance.currentMessage != messageInController) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/WearReplyReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/WearReplyReceiver.java index 05eeb38f54..be4380a0f7 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/WearReplyReceiver.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/WearReplyReceiver.java @@ -33,7 +33,7 @@ public void onReceive(Context context, Intent intent) { } long dialogId = intent.getLongExtra("dialog_id", 0); int maxId = intent.getIntExtra("max_id", 0); - int topicId = intent.getIntExtra("topic_id", 0); + long topicId = intent.getLongExtra("topic_id", 0); int currentAccount = intent.getIntExtra("currentAccount", 0); if (dialogId == 0 || maxId == 0 || !UserConfig.isValidAccount(currentAccount)) { return; @@ -67,19 +67,27 @@ public void onReceive(Context context, Intent intent) { sendMessage(accountInstance, text, dialogId, topicId, maxId); } - private void sendMessage(AccountInstance accountInstance, CharSequence text, long dialog_id, int topicId, int max_id) { + private void sendMessage(AccountInstance accountInstance, CharSequence text, long dialog_id, long topicId, int max_id) { MessageObject replyToMsgId = null; + MessageObject replyToTopMsgId = null; + if (max_id != 0) { + TLRPC.TL_message replyMessage = new TLRPC.TL_message(); + replyMessage.message = ""; + replyMessage.id = max_id; + replyMessage.peer_id = accountInstance.getMessagesController().getPeer(dialog_id); + replyToMsgId = new MessageObject(accountInstance.getCurrentAccount(), replyMessage, false, false); + } if (topicId != 0) { TLRPC.TL_message topicStartMessage = new TLRPC.TL_message(); topicStartMessage.message = ""; - topicStartMessage.id = topicId; + topicStartMessage.id = (int) topicId; topicStartMessage.peer_id = accountInstance.getMessagesController().getPeer(dialog_id); topicStartMessage.action = new TLRPC.TL_messageActionTopicCreate(); topicStartMessage.action.title = ""; - replyToMsgId = new MessageObject(accountInstance.getCurrentAccount(), topicStartMessage, false, false); + replyToTopMsgId = new MessageObject(accountInstance.getCurrentAccount(), topicStartMessage, false, false); } - accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(text.toString(), dialog_id, replyToMsgId, null, null, true, null, null, null, true, 0, null, false)); + accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(text.toString(), dialog_id, replyToMsgId, replyToTopMsgId, null, true, null, null, null, true, 0, null, false)); //TODO handle topics if (topicId == 0) { accountInstance.getMessagesController().markDialogAsRead(dialog_id, max_id, max_id, 0, false, topicId, 0, true, 0); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java b/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java index 4c91e75553..d9ce3ed6b3 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/browser/Browser.java @@ -449,6 +449,16 @@ public static boolean isPassportUrl(String url) { return false; } + public static boolean isTMe(String url) { + try { + final String linkPrefix = MessagesController.getInstance(UserConfig.selectedAccount).linkPrefix; + return TextUtils.equals(AndroidUtilities.getHostAuthority(url), linkPrefix); + } catch (Exception e) { + FileLog.e(e); + } + return false; + } + public static boolean isInternalUri(Uri uri, boolean[] forceBrowser) { return isInternalUri(uri, false, forceBrowser); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java index 32a327a52b..ca6217bc1e 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/MP4Builder.java @@ -10,6 +10,7 @@ import android.media.MediaCodec; import android.media.MediaFormat; +import android.util.Log; import com.coremedia.iso.BoxParser; import com.coremedia.iso.IsoFile; @@ -38,11 +39,18 @@ import com.googlecode.mp4parser.DataSource; import com.googlecode.mp4parser.util.Matrix; +import org.telegram.messenger.AndroidUtilities; + +import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.WritableByteChannel; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -207,6 +215,47 @@ public void finishMovie() throws Exception { fos.close(); } + public void finishMovie(File into) throws Exception { + if (into == null) { + finishMovie(); + return; + } + + fos.flush(); + final long wasPosition = fc.position(); + if (allowSyncFiles) { + fos.getFD().sync(); + } + + AndroidUtilities.copyFile(currentMp4Movie.getCacheFile(), into); + + try (RandomAccessFile raf = new RandomAccessFile(into, "rw"); + FileChannel copiedFc = raf.getChannel()) { + + // put mdat box + copiedFc.position(wasPosition); + if (mdat.getContentSize() != 0) { + copiedFc.position(mdat.getOffset()); + mdat.getBox(copiedFc); + copiedFc.position(wasPosition); + } + + // put moov box + track2SampleSizes.clear(); + for (Track track : currentMp4Movie.getTracks()) { + List samples = track.getSamples(); + long[] sizes = new long[samples.size()]; + for (int i = 0; i < sizes.length; i++) { + sizes[i] = samples.get(i).getSize(); + } + track2SampleSizes.put(track, sizes); + } + Box moov = createMovieBox(currentMp4Movie); + moov.getBox(copiedFc); + } + } + + protected FileTypeBox createFileTypeBox(boolean hevc) { LinkedList minorBrands = new LinkedList<>(); minorBrands.add("isom"); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/Track.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/Track.java index 6979f6ddbc..cf4098543b 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/Track.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/Track.java @@ -11,6 +11,7 @@ import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaFormat; +import android.util.Log; import com.coremedia.iso.boxes.AbstractMediaHeaderBox; import com.coremedia.iso.boxes.SampleDescriptionBox; @@ -330,6 +331,8 @@ public void addSample(long offset, MediaCodec.BufferInfo bufferInfo) { } public void prepare() { + duration = 0; + ArrayList original = new ArrayList<>(samplePresentationTimes); Collections.sort(samplePresentationTimes, (o1, o2) -> { if (o1.presentationTime > o2.presentationTime) { 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 ebbf0456b5..7d7f941dc0 100755 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java @@ -3029,7 +3029,8 @@ private void startRingtoneAndVibration(long chatID) { } else { ringtonePlayer.setAudioStreamType(AudioManager.STREAM_RING); if (!USE_CONNECTION_SERVICE) { - am.requestAudioFocus(this, AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN); + int focusResult = am.requestAudioFocus(this, AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + hasAudioFocus = focusResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED; } } try { @@ -3156,8 +3157,8 @@ public void onDestroy() { } } cpuWakelock.release(); + AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); if (!playingSound) { - AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); VoipAudioManager vam = VoipAudioManager.get(); if (!USE_CONNECTION_SERVICE) { if (isBtHeadsetConnected || bluetoothScoActive || bluetoothScoConnecting) { @@ -3194,15 +3195,16 @@ public void onDestroy() { if (audioDeviceCallback != null) { am.unregisterAudioDeviceCallback(audioDeviceCallback); } - if (hasAudioFocus) { - am.abandonAudioFocus(this); - } + Utilities.globalQueue.postRunnable(() -> { if (soundPool != null) { soundPool.release(); } }); } + if (hasAudioFocus) { + am.abandonAudioFocus(this); + } if (USE_CONNECTION_SERVICE) { if (!didDeleteConnectionServiceContact) { @@ -3574,7 +3576,7 @@ public void onCreate() { } try { AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER) != null) { + if (am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER) != null) { int outFramesPerBuffer = Integer.parseInt(am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER)); Instance.setBufferSize(outFramesPerBuffer); } else { @@ -3763,7 +3765,8 @@ private void configureDeviceForCall() { FileLog.e(e); } AndroidUtilities.runOnUIThread(() -> { - am.requestAudioFocus(VoIPService.this, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN); + int focusResult = am.requestAudioFocus(VoIPService.this, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + hasAudioFocus = focusResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED; final VoipAudioManager vam = VoipAudioManager.get(); if (isBluetoothHeadsetConnected() && hasEarpiece()) { switch (audioRouteToSet) { diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java index b6fdda8a0e..272156eaf4 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java @@ -76,7 +76,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 = 170; + public static final int LAYER = 172; public static class TL_stats_megagroupStats extends TLObject { public static final int constructor = 0xef7ff916; @@ -10217,6 +10217,8 @@ public static class TL_globalPrivacySettings extends TLObject { public boolean archive_and_mute_new_noncontact_peers; public boolean keep_archived_unmuted; public boolean keep_archived_folders; + public boolean hide_read_marks; + public boolean new_noncontact_peers_require_premium; public static TL_globalPrivacySettings TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { if (TL_globalPrivacySettings.constructor != constructor) { @@ -10236,6 +10238,8 @@ public void readParams(AbstractSerializedData stream, boolean exception) { archive_and_mute_new_noncontact_peers = (flags & 1) != 0; keep_archived_unmuted = (flags & 2) != 0; keep_archived_folders = (flags & 4) != 0; + hide_read_marks = (flags & 8) != 0; + new_noncontact_peers_require_premium = (flags & 16) != 0; } public void serializeToStream(AbstractSerializedData stream) { @@ -10243,6 +10247,8 @@ public void serializeToStream(AbstractSerializedData stream) { flags = archive_and_mute_new_noncontact_peers ? (flags | 1) : (flags &~ 1); flags = keep_archived_unmuted ? (flags | 2) : (flags &~ 2); flags = keep_archived_folders ? (flags | 4) : (flags &~ 4); + flags = hide_read_marks ? (flags | 8) : (flags &~ 8); + flags = new_noncontact_peers_require_premium ? (flags | 16) : (flags &~ 16); stream.writeInt32(flags); } } @@ -24088,6 +24094,7 @@ public static abstract class User extends TLObject { public boolean close_friend; public boolean stories_unavailable; public boolean stories_hidden; + public boolean contact_require_premium; public int bot_info_version; public String bot_inline_placeholder; public String lang_code; @@ -24282,6 +24289,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { close_friend = (flags2 & 4) != 0; stories_hidden = (flags2 & 8) != 0; stories_unavailable = (flags2 & 16) != 0; + contact_require_premium = (flags2 & 1024) != 0; id = stream.readInt64(exception); if ((flags & 1) != 0) { access_hash = stream.readInt64(exception); @@ -24393,6 +24401,7 @@ public void serializeToStream(AbstractSerializedData stream) { flags2 = close_friend ? (flags2 | 4) : (flags2 &~ 4); flags2 = stories_hidden ? (flags2 | 8) : (flags2 &~ 8); flags2 = stories_unavailable ? (flags2 | 16) : (flags2 &~ 16); + flags2 = contact_require_premium ? (flags2 | 1024) : (flags2 &~ 1024); stream.writeInt32(flags2); stream.writeInt64(id); if ((flags & 1) != 0) { @@ -28754,6 +28763,7 @@ public static abstract class MessageReactions extends TLObject { public int flags; public boolean min; public boolean can_see_list; + public boolean reactions_as_tags; public ArrayList results = new ArrayList<>(); public ArrayList recent_reactions = new ArrayList<>(); @@ -28787,6 +28797,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); min = (flags & 1) != 0; can_see_list = (flags & 4) != 0; + reactions_as_tags = (flags & 8) != 0; int magic = stream.readInt32(exception); if (magic != 0x1cb5c415) { if (exception) { @@ -28825,6 +28836,7 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = min ? (flags | 1) : (flags &~ 1); flags = can_see_list ? (flags | 4) : (flags &~ 4); + flags = reactions_as_tags ? (flags | 8) : (flags &~ 8); stream.writeInt32(flags); stream.writeInt32(0x1cb5c415); int count = results.size(); @@ -28843,6 +28855,144 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_savedReactionTag extends TLObject { + public static final int constructor = 0xcb6ff828; + + public int flags; + public Reaction reaction; + public String title; + public int count; + + public static TL_savedReactionTag TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_savedReactionTag.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_savedReactionTag", constructor)); + } else { + return null; + } + } + TL_savedReactionTag result = new TL_savedReactionTag(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + reaction = Reaction.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 1) != 0) { + title = stream.readString(exception); + } + count = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + reaction.serializeToStream(stream); + if ((flags & 1) != 0) { + stream.writeString(title); + } + stream.writeInt32(count); + } + } + + public static class messages_SavedReactionTags extends TLObject { + public ArrayList tags = new ArrayList<>(); + public long hash; + + public static messages_SavedReactionTags TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + messages_SavedReactionTags result = null; + switch (constructor) { + case TL_messages_savedReactionsTagsNotModified.constructor: + result = new TL_messages_savedReactionsTagsNotModified(); + break; + case TL_messages_savedReactionsTags.constructor: + result = new TL_messages_savedReactionsTags(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in messages_SavedReactionTags", constructor)); + } + if (result != null) { + result.readParams(stream, exception); + } + return result; + } + } + + public static class TL_messages_savedReactionsTagsNotModified extends messages_SavedReactionTags { + public static final int constructor = 0x889b59ef; + + public void readParams(AbstractSerializedData stream, boolean exception) { + + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class TL_messages_savedReactionsTags extends messages_SavedReactionTags { + public static final int constructor = 0x3259950a; + + 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_savedReactionTag object = TL_savedReactionTag.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + tags.add(object); + } + hash = stream.readInt64(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = tags.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + tags.get(a).serializeToStream(stream); + } + stream.writeInt64(hash); + } + } + + public static class TL_outboxReadDate extends TLObject { + public static final int constructor = 0x3bb842ac; + + public int date; + + public static TL_outboxReadDate TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_outboxReadDate.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_outboxReadDate", constructor)); + } else { + return null; + } + } + TL_outboxReadDate result = new TL_outboxReadDate(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + date = stream.readInt32(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(date); + } + } + public static class TL_messageReactionsOld extends TL_messageReactions { public static final int constructor = 0x87b6e36; @@ -33558,7 +33708,9 @@ public void serializeToStream(AbstractSerializedData stream) { } public static abstract class UserStatus extends TLObject { + public int flags; public int expires; + public boolean by_me; public static UserStatus TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { UserStatus result = null; @@ -33566,21 +33718,33 @@ public static UserStatus TLdeserialize(AbstractSerializedData stream, int constr case 0x8c703f: result = new TL_userStatusOffline(); break; - case 0x7bf09fc: + case TL_userStatusLastWeek.constructor: result = new TL_userStatusLastWeek(); break; + case TL_userStatusLastWeek_layer171.constructor: + result = new TL_userStatusLastWeek_layer171(); + break; case 0x9d05049: result = new TL_userStatusEmpty(); break; - case 0x77ebc742: + case TL_userStatusLastMonth.constructor: result = new TL_userStatusLastMonth(); break; + case TL_userStatusLastMonth_layer171.constructor: + result = new TL_userStatusLastMonth_layer171(); + break; case 0xedb93949: result = new TL_userStatusOnline(); break; - case 0xe26f42f1: + case TL_userStatusRecently.constructor: result = new TL_userStatusRecently(); break; + case TL_userStatusRecently_layer171.constructor: + result = new TL_userStatusRecently_layer171(); + break; + case TL_userStatusHidden.constructor: + result = new TL_userStatusHidden(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in UserStatus", constructor)); @@ -33592,6 +33756,19 @@ public static UserStatus TLdeserialize(AbstractSerializedData stream, int constr } } + public static class TL_userStatusHidden extends UserStatus { + public static final int constructor = 0xcf7d64b1; + + + public void readParams(AbstractSerializedData stream, boolean exception) { + + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_userStatusOffline extends UserStatus { public static final int constructor = 0x8c703f; @@ -33607,8 +33784,29 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_userStatusLastWeek extends UserStatus { + public static final int constructor = 0x541a1d1a; + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + by_me = (flags & 1) != 0; + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = by_me ? flags | 1 : flags &~ 1; + stream.writeInt32(flags); + } + } + + + public static class TL_userStatusLastWeek_layer171 extends TL_userStatusLastWeek { public static final int constructor = 0x7bf09fc; + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + + } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); @@ -33625,8 +33823,28 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_userStatusLastMonth extends UserStatus { + public static final int constructor = 0x65899777; + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + by_me = (flags & 1) != 0; + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = by_me ? flags | 1 : flags &~ 1; + stream.writeInt32(flags); + } + } + + public static class TL_userStatusLastMonth_layer171 extends TL_userStatusLastMonth { public static final int constructor = 0x77ebc742; + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + + } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); @@ -33648,8 +33866,28 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_userStatusRecently extends UserStatus { + public static final int constructor = 0x7b197dc8; + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + by_me = (flags & 1) != 0; + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = by_me ? flags | 1 : flags &~ 1; + stream.writeInt32(flags); + } + } + + public static class TL_userStatusRecently_layer171 extends TL_userStatusRecently { public static final int constructor = 0xe26f42f1; + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + + } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); @@ -34180,6 +34418,9 @@ public static Update TLdeserialize(AbstractSerializedData stream, int constructo case TL_updateRecentReactions.constructor: result = new TL_updateRecentReactions(); break; + case TL_updateSavedReactionTags.constructor: + result = new TL_updateSavedReactionTags(); + break; case TL_updateWebPage.constructor: result = new TL_updateWebPage(); break; @@ -35306,6 +35547,14 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_updateSavedReactionTags extends Update { + public static final int constructor = 0x39c67432; + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + public static class TL_updateWebPage extends Update { public static final int constructor = 0x7f891213; @@ -51431,6 +51680,8 @@ public static abstract class UserFull extends TLObject { public boolean stories_pinned_available; public boolean blocked_my_stories_from; public boolean wallpaper_overridden; + public boolean contact_require_premium; + public boolean read_dates_private; public User user; public String about; public TL_contacts_link_layer101 link; @@ -51456,7 +51707,7 @@ public static abstract class UserFull extends TLObject { public static UserFull TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { UserFull result = null; switch (constructor) { - case 0xb9b12c6c: + case TL_userFull.constructor: result = new TL_userFull(); break; case 0x4fe1cc86: @@ -51522,6 +51773,8 @@ public void readParams(AbstractSerializedData stream, boolean exception) { stories_pinned_available = (flags & 67108864) != 0; blocked_my_stories_from = (flags & 134217728) != 0; wallpaper_overridden = (flags & 268435456) != 0; + contact_require_premium = (flags & 536870912) != 0; + read_dates_private = (flags & 1073741824) != 0; id = stream.readInt64(exception); if ((flags & 2) != 0) { about = stream.readString(exception); @@ -51600,6 +51853,8 @@ public void serializeToStream(AbstractSerializedData stream) { flags = stories_pinned_available ? (flags | 67108864) : (flags &~ 67108864); flags = blocked_my_stories_from ? (flags | 134217728) : (flags &~ 134217728); flags = wallpaper_overridden ? (flags | 268435456) : (flags &~ 268435456); + flags = contact_require_premium ? (flags | 536870912) : (flags &~ 536870912); + flags = read_dates_private ? (flags | 1073741824) : (flags &~ 1073741824); stream.writeInt32(flags); stream.writeInt64(id); if ((flags & 2) != 0) { @@ -56182,6 +56437,35 @@ public void serializeToStream(AbstractSerializedData stream) { id.serializeToStream(stream); } } + + public static class TL_users_getIsPremiumRequiredToContact extends TLObject { + public static final int constructor = 0xa622aa10; + + public ArrayList id = new ArrayList<>(); + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + Vector vector = new Vector(); + int size = stream.readInt32(exception); + for (int a = 0; a < size; a++) { + Bool object = Bool.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return vector; + } + vector.objects.add(object); + } + return vector; + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(0x1cb5c415); + int count = id.size(); + stream.writeInt32(count); + for (int i = 0; i < count; ++i) { + id.get(i).serializeToStream(stream); + } + } + } public static class TL_contacts_getStatuses extends TLObject { public static final int constructor = 0xc4a353ee; @@ -56587,13 +56871,14 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_search extends TLObject { - public static final int constructor = 0xa7b4e929; + public static final int constructor = 0x29ee847a; public int flags; public InputPeer peer; public String q; public InputPeer from_id; public InputPeer saved_peer_id; + public ArrayList saved_reaction = new ArrayList<>(); public int top_msg_id; public MessagesFilter filter; public int min_date; @@ -56620,6 +56905,14 @@ public void serializeToStream(AbstractSerializedData stream) { if ((flags & 4) != 0) { saved_peer_id.serializeToStream(stream); } + if ((flags & 8) != 0) { + stream.writeInt32(0x1cb5c415); + int count = saved_reaction.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + saved_reaction.get(a).serializeToStream(stream); + } + } if ((flags & 2) != 0) { stream.writeInt32(top_msg_id); } @@ -71481,6 +71774,23 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_messages_getDefaultTagReactions extends TLObject { + public static final int constructor = 0xbdf93428; + + public long hash; + + @Override + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return messages_Reactions.TLdeserialize(stream, constructor, exception); + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(hash); + } + } + public static abstract class ForumTopic extends TLObject { public static ForumTopic TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { @@ -74750,6 +75060,66 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_messages_getSavedReactionTags extends TLObject { + public static final int constructor = 0x761ddacf; + + public long hash; + + @Override + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return messages_SavedReactionTags.TLdeserialize(stream, constructor, exception); + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(hash); + } + } + + public static class TL_messages_updateSavedReactionTag extends TLObject { + public static final int constructor = 0x60297dec; + + public int flags; + public Reaction reaction; + public String title; + + @Override + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return Bool.TLdeserialize(stream, constructor, exception); + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + reaction.serializeToStream(stream); + if ((flags & 1) != 0) { + stream.writeString(title); + } + } + } + + public static class TL_messages_getOutboxReadDate extends TLObject { + public static final int constructor = 0x8c4bfe5d; + + public InputPeer peer; + public int msg_id; + + + @Override + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_outboxReadDate.TLdeserialize(stream, constructor, exception); + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt32(msg_id); + } + } + public static class Vector extends TLObject { public static final int constructor = 0x1cb5c415; public ArrayList objects = new ArrayList<>(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java index bc5b09713d..0bddabec4b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java @@ -1003,6 +1003,10 @@ public void setBackgroundColor(int color) { } } + public int getBackgroundColor() { + return actionBarColor; + } + public boolean isActionModeShowed() { return actionMode != null && actionModeVisible; } @@ -1159,6 +1163,12 @@ public void setSearchFilter(FiltersView.MediaFilterData filter) { } } + public void clearSearchFilters() { + if (menu != null) { + menu.clearSearchFilters(); + } + } + public void setSearchFieldText(String text) { menu.setSearchFieldText(text); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java index 9256e03379..ddeb8f9343 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenu.java @@ -566,7 +566,17 @@ public void translateXItems(float offset) { } public void clearSearchFilters() { - + int count = getChildCount(); + for (int a = 0; a < count; a++) { + View view = getChildAt(a); + if (view instanceof ActionBarMenuItem) { + ActionBarMenuItem item = (ActionBarMenuItem) view; + if (item.isSearchField()) { + item.clearSearchFilters(); + break; + } + } + } } private Runnable onLayoutListener; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java index 8ea9ed5624..855e4a0a92 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarMenuItem.java @@ -8,6 +8,8 @@ package org.telegram.ui.ActionBar; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -65,6 +67,7 @@ import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLRPC; import org.telegram.ui.Adapters.FiltersView; +import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CloseProgressDrawable2; import org.telegram.ui.Components.CombinedDrawable; @@ -73,6 +76,7 @@ import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Components.RLottieImageView; +import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import java.util.ArrayList; import java.util.HashMap; @@ -1052,7 +1056,7 @@ public void onTransitionResume(Transition transition) { } for (int i = 0; i < searchFilterLayout.getChildCount(); i++) { - boolean removed = localFilters.remove(((SearchFilterView)searchFilterLayout.getChildAt(i)).getFilter()); + boolean removed = localFilters.remove(((SearchFilterView) searchFilterLayout.getChildAt(i)).getFilter()); if (!removed) { searchFilterLayout.removeViewAt(i); i--; @@ -1060,8 +1064,14 @@ public void onTransitionResume(Transition transition) { } for (int i = 0; i < localFilters.size(); i++) { - SearchFilterView searchFilterView = new SearchFilterView(getContext(), resourcesProvider); - searchFilterView.setData(localFilters.get(i)); + FiltersView.MediaFilterData filter = localFilters.get(i); + SearchFilterView searchFilterView; + if (filter.reaction != null) { + searchFilterView = new ReactionFilterView(getContext(), resourcesProvider); + } else { + searchFilterView = new SearchFilterView(getContext(), resourcesProvider); + } + searchFilterView.setData(filter); searchFilterView.setOnClickListener(view -> { int index = currentSearchFilters.indexOf(searchFilterView.getFilter()); if (selectedFilterIndex != index) { @@ -2014,6 +2024,78 @@ private int getThemedColor(int key) { return Theme.getColor(key, resourcesProvider); } + private static class ReactionFilterView extends SearchFilterView { + + private ReactionsLayoutInBubble.ReactionButton reactionButton; + + public ReactionFilterView(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context, resourcesProvider); + removeAllViews(); + setBackground(null); + + setWillNotDraw(false); + } + + public void setData(FiltersView.MediaFilterData data) { + TLRPC.TL_reactionCount reactionCount = new TLRPC.TL_reactionCount(); + reactionCount.count = 1; + reactionCount.reaction = data.reaction.toTLReaction(); + + reactionButton = new ReactionsLayoutInBubble.ReactionButton(null, UserConfig.selectedAccount, this, reactionCount, false, resourcesProvider) { + @Override + protected void updateColors(float progress) { + lastDrawnBackgroundColor = ColorUtils.blendARGB(fromBackgroundColor, Theme.getColor(Theme.key_chat_inReactionButtonBackground, resourcesProvider), progress); + } + + @Override + protected int getCacheType() { + return AnimatedEmojiDrawable.CACHE_TYPE_ALERT_EMOJI_STATUS; + } + }; + reactionButton.width = dp(44.33f); + reactionButton.height = dp(28); + reactionButton.choosen = true; + if (attached) { + reactionButton.attach(); + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(dp(45 + 4), dp(32)); + } + + @Override + protected void onDraw(Canvas canvas) { + if (reactionButton != null) { + reactionButton.draw(canvas, (getWidth() - dp(4) - reactionButton.width) / 2f, (getHeight() - reactionButton.height) / 2f, 1f, 1f, false); + } + } + + private boolean attached; + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (!attached) { + if (reactionButton != null) { + reactionButton.attach(); + } + attached = true; + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (attached) { + if (reactionButton != null) { + reactionButton.detach(); + } + attached = false; + } + } + } + private static class SearchFilterView extends FrameLayout { Drawable thumbDrawable; @@ -2036,7 +2118,7 @@ public void run() { } }; - private final Theme.ResourcesProvider resourcesProvider; + protected final Theme.ResourcesProvider resourcesProvider; public SearchFilterView(Context context, Theme.ResourcesProvider resourcesProvider) { super(context); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java index 5f727c5ef4..99b549bbc7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/AlertDialog.java @@ -12,6 +12,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; @@ -19,6 +20,7 @@ import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.CornerPathEffect; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; @@ -53,6 +55,8 @@ import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; +import com.google.android.exoplayer2.util.Log; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.Emoji; import org.telegram.messenger.FileLog; @@ -198,7 +202,7 @@ public void setBlurParams(float blurAlpha, boolean blurBehind, boolean blurBackg } protected boolean supportsNativeBlur() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && LaunchActivity.systemBlurEnabled; + return false; // Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && LaunchActivity.systemBlurEnabled; } public void redPositive() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java index 8dd2cd34b5..8888ee3b4f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java @@ -67,7 +67,7 @@ public abstract class BaseFragment { protected Dialog visibleDialog; protected int currentAccount = UserConfig.selectedAccount; - protected View fragmentView; + public View fragmentView; protected INavigationLayout parentLayout; protected ActionBar actionBar; protected boolean inPreviewMode; 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 c796ea69e8..4ad9103c09 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/SimpleTextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/SimpleTextView.java @@ -97,6 +97,7 @@ public class SimpleTextView extends View implements Drawable.Callback { private boolean wasLayout; private boolean rightDrawableOutside; + private boolean rightDrawableInside; private boolean ellipsizeByGradient, ellipsizeByGradientLeft; private Boolean forceEllipsizeByGradientLeft; private int ellipsizeByGradientWidthDp = 16; @@ -317,7 +318,16 @@ private void calcOffset(int width) { offsetX = -AndroidUtilities.dp(8); } offsetX += getPaddingLeft(); - textDoesNotFit = textWidth > (width - paddingRight); + int rightDrawableWidth = 0; + if (rightDrawableInside) { + if (rightDrawable != null && !rightDrawableOutside) { + rightDrawableWidth += (int) (rightDrawable.getIntrinsicWidth() * rightDrawableScale); + } + if (rightDrawable2 != null && !rightDrawableOutside) { + rightDrawableWidth += (int) (rightDrawable2.getIntrinsicWidth() * rightDrawableScale); + } + } + textDoesNotFit = textWidth + rightDrawableWidth > (width - paddingRight); if (fullLayout != null && fullLayoutAdditionalWidth > 0) { fullLayoutLeftCharactersOffset = fullLayout.getPrimaryHorizontal(0) - firstLineLayout.getPrimaryHorizontal(0); @@ -342,15 +352,17 @@ protected boolean createLayout(int width) { width -= drawablePadding; } int rightDrawableWidth = 0; - if (rightDrawable != null && !rightDrawableOutside) { - rightDrawableWidth = (int) (rightDrawable.getIntrinsicWidth() * rightDrawableScale); - width -= rightDrawableWidth; - width -= drawablePadding; - } - if (rightDrawable2 != null && !rightDrawableOutside) { - rightDrawableWidth = (int) (rightDrawable2.getIntrinsicWidth() * rightDrawableScale); - width -= rightDrawableWidth; - width -= drawablePadding; + if (!rightDrawableInside) { + if (rightDrawable != null && !rightDrawableOutside) { + rightDrawableWidth += (int) (rightDrawable.getIntrinsicWidth() * rightDrawableScale); + width -= rightDrawableWidth; + width -= drawablePadding; + } + if (rightDrawable2 != null && !rightDrawableOutside) { + rightDrawableWidth += (int) (rightDrawable2.getIntrinsicWidth() * rightDrawableScale); + width -= rightDrawableWidth; + width -= drawablePadding; + } } if (replacedText != null && replacedDrawable != null) { replacingDrawableTextIndex = text.toString().indexOf(replacedText); @@ -487,7 +499,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } public int getTextWidth() { - return textWidth; + return textWidth + (rightDrawableInside ? (rightDrawable != null ? (int) (rightDrawable.getIntrinsicWidth() * rightDrawableScale) : 0) + (rightDrawable2 != null ? (int) (rightDrawable2.getIntrinsicWidth() * rightDrawableScale) : 0) : 0); } public int getRightDrawableWidth() { @@ -742,15 +754,17 @@ public void setRightPadding(int padding) { width -= drawablePadding; } int rightDrawableWidth = 0; - if (rightDrawable != null && !rightDrawableOutside) { - rightDrawableWidth = (int) (rightDrawable.getIntrinsicWidth() * rightDrawableScale); - width -= rightDrawableWidth; - width -= drawablePadding; - } - if (rightDrawable2 != null && !rightDrawableOutside) { - rightDrawableWidth = (int) (rightDrawable2.getIntrinsicWidth() * rightDrawableScale); - width -= rightDrawableWidth; - width -= drawablePadding; + if (!rightDrawableInside) { + if (rightDrawable != null && !rightDrawableOutside) { + rightDrawableWidth = (int) (rightDrawable.getIntrinsicWidth() * rightDrawableScale); + width -= rightDrawableWidth; + width -= drawablePadding; + } + if (rightDrawable2 != null && !rightDrawableOutside) { + rightDrawableWidth = (int) (rightDrawable2.getIntrinsicWidth() * rightDrawableScale); + width -= rightDrawableWidth; + width -= drawablePadding; + } } if (replacedText != null && replacedDrawable != null) { if ((replacingDrawableTextIndex = text.toString().indexOf(replacedText)) < 0) { @@ -823,7 +837,7 @@ protected void onDraw(Canvas canvas) { } } - if (rightDrawable != null && !rightDrawableHidden && rightDrawableScale > 0 && !rightDrawableOutside) { + if (rightDrawable != null && !rightDrawableHidden && rightDrawableScale > 0 && !rightDrawableOutside && !rightDrawableInside) { int x = textOffsetX + textWidth + drawablePadding + (int) -scrollingOffset; if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL || (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.RIGHT) { @@ -843,7 +857,7 @@ protected void onDraw(Canvas canvas) { rightDrawable.draw(canvas); totalWidth += drawablePadding + dw; } - if (rightDrawable2 != null && !rightDrawableHidden && rightDrawableScale > 0 && !rightDrawableOutside) { + if (rightDrawable2 != null && !rightDrawableHidden && rightDrawableScale > 0 && !rightDrawableOutside && !rightDrawableInside) { int x = textOffsetX + textWidth + drawablePadding + (int) -scrollingOffset; if (rightDrawable != null) { x += (int) (rightDrawable.getIntrinsicWidth() * rightDrawableScale) + drawablePadding; @@ -963,6 +977,47 @@ protected void onDraw(Canvas canvas) { if (offsetX + textOffsetX != 0 || offsetY != 0 || scrollingOffset != 0) { canvas.restore(); } + if (rightDrawable != null && !rightDrawableHidden && rightDrawableScale > 0 && !rightDrawableOutside && rightDrawableInside) { + int x = textOffsetX + textWidth + drawablePadding + (int) -scrollingOffset; + if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL || + (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.RIGHT) { + x += offsetX; + } + int dw = (int) (rightDrawable.getIntrinsicWidth() * rightDrawableScale); + int dh = (int) (rightDrawable.getIntrinsicHeight() * rightDrawableScale); + int y; + if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.CENTER_VERTICAL) { + y = (getMeasuredHeight() - dh) / 2 + rightDrawableTopPadding; + } else { + y = getPaddingTop() + (textHeight - dh) / 2 + rightDrawableTopPadding; + } + rightDrawable.setBounds(x, y, x + dw, y + dh); + rightDrawableX = x + (dw >> 1); + rightDrawableY = y + (dh >> 1); + rightDrawable.draw(canvas); + totalWidth += drawablePadding + dw; + } + if (rightDrawable2 != null && !rightDrawableHidden && rightDrawableScale > 0 && !rightDrawableOutside && rightDrawableInside) { + int x = textOffsetX + textWidth + drawablePadding + (int) -scrollingOffset; + if (rightDrawable != null) { + x += (int) (rightDrawable.getIntrinsicWidth() * rightDrawableScale) + drawablePadding; + } + if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL || + (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.RIGHT) { + x += offsetX; + } + int dw = (int) (rightDrawable2.getIntrinsicWidth() * rightDrawableScale); + int dh = (int) (rightDrawable2.getIntrinsicHeight() * rightDrawableScale); + int y; + if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.CENTER_VERTICAL) { + y = (getMeasuredHeight() - dh) / 2 + rightDrawableTopPadding; + } else { + y = getPaddingTop() + (textHeight - dh) / 2 + rightDrawableTopPadding; + } + rightDrawable2.setBounds(x, y, x + dw, y + dh); + rightDrawable2.draw(canvas); + totalWidth += drawablePadding + dw; + } if (fade) { if (scrollingOffset < AndroidUtilities.dp(10)) { fadePaint.setAlpha((int) (255 * (scrollingOffset / AndroidUtilities.dp(10)))); @@ -1170,6 +1225,12 @@ public void setRightDrawableOutside(boolean outside) { rightDrawableOutside = outside; } + // right drawable is ellipsized with text + public void setRightDrawableInside(boolean inside) { + rightDrawableInside = inside; + } + + public boolean getRightDrawableOutside() { return rightDrawableOutside; } @@ -1187,14 +1248,23 @@ public boolean onTouchEvent(MotionEvent event) { touchDownX = event.getX(); touchDownY = event.getY(); getParent().requestDisallowInterceptTouchEvent(true); + if (rightDrawable instanceof PressableDrawable) { + ((PressableDrawable) rightDrawable).setPressed(true); + } } else if (event.getAction() == MotionEvent.ACTION_MOVE && maybeClick) { if (Math.abs(event.getX() - touchDownX) >= AndroidUtilities.touchSlop || Math.abs(event.getY() - touchDownY) >= AndroidUtilities.touchSlop) { maybeClick = false; getParent().requestDisallowInterceptTouchEvent(false); + if (rightDrawable instanceof PressableDrawable) { + ((PressableDrawable) rightDrawable).setPressed(false); + } } } else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { if (maybeClick && event.getAction() == MotionEvent.ACTION_UP) { rightDrawableOnClickListener.onClick(this); + if (rightDrawable instanceof PressableDrawable) { + ((PressableDrawable) rightDrawable).setPressed(false); + } } maybeClick = false; getParent().requestDisallowInterceptTouchEvent(false); @@ -1202,4 +1272,9 @@ public boolean onTouchEvent(MotionEvent event) { } return super.onTouchEvent(event) || maybeClick; } + + public static interface PressableDrawable { + public void setPressed(boolean value); + public boolean isPressed(); + } } 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 7c88f7c8bb..d5c5d570d5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java @@ -863,6 +863,7 @@ private void generatePath(Path path, Rect bounds, int padding, int rad, int smal rad = heightHalf; } if (isOut) { + // LEFT-BOTTOM <- RIGHT-BOTTOM if (drawFullBubble || currentType == TYPE_PREVIEW || customPaint || drawFullBottom) { int radToUse = botButtonsBottom ? nearRad : rad; if (currentType == TYPE_MEDIA) { @@ -878,10 +879,12 @@ private void generatePath(Path path, Rect bounds, int padding, int rad, int smal path.lineTo(bounds.left + padding, top - topY + currentBackgroundHeight); } if (drawFullBubble || currentType == TYPE_PREVIEW || customPaint || drawFullTop) { + // LEFT-BOTTOM -> LEFT-TOP 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); + // LEFT-TOP -> RIGHT-TOP int radToUse = isTopNear ? nearRad : rad; if (currentType == TYPE_MEDIA) { path.lineTo(bounds.right - padding - radToUse, bounds.top + padding); @@ -892,13 +895,17 @@ private void generatePath(Path path, Rect bounds, int padding, int rad, int smal } path.arcTo(rect, 270, 90, false); } else { + // LEFT-BOTTOM -> LEFT-TOP path.lineTo(bounds.left + padding, top - topY - dp(2)); + + // LEFT-TOP -> RIGHT-TOP if (currentType == TYPE_MEDIA) { path.lineTo(bounds.right - padding, top - topY - dp(2)); } else { path.lineTo(bounds.right - dp(8), top - topY - dp(2)); } } + // RIGHT-TOP -> RIGHT-BOTTOM if (currentType == TYPE_MEDIA) { if (customPaint || drawFullBottom) { int radToUse = isBottomNear ? nearRad : rad; @@ -3098,7 +3105,7 @@ public void run() { public static Paint avatar_backgroundPaint; public static Drawable listSelector; - public static Drawable[] avatarDrawables = new Drawable[18]; + public static Drawable[] avatarDrawables = new Drawable[20]; public static Drawable moveUpDrawable; @@ -3635,6 +3642,9 @@ public void run() { public static final int key_chat_inBubbleSelectedOverlay = colorsCount++; public static final int key_chat_inBubbleShadow = colorsCount++; + public static final int key_actionBarActionModeReaction = colorsCount++; + public static final int key_actionBarActionModeReactionDot = colorsCount++; + //my messages bubbles public static final int myMessagesBubblesStartIndex = colorsCount; public static final int key_chat_outBubble = colorsCount++; @@ -4384,6 +4394,8 @@ public void run() { fallbackKeys.put(key_statisticChartLine_indigo, key_color_purple); fallbackKeys.put(key_statisticChartLine_cyan, key_color_cyan); + fallbackKeys.put(key_actionBarActionModeReaction, key_windowBackgroundGray); + for (int i = 0; i < keys_avatar_background.length; i++) { themeAccentExclusionKeys.add(keys_avatar_background[i]); } @@ -8134,6 +8146,8 @@ public static void createCommonResources(Context context) { avatarDrawables[15] = resources.getDrawable(R.drawable.filled_unknown); avatarDrawables[16] = resources.getDrawable(R.drawable.filled_unclaimed); avatarDrawables[17] = resources.getDrawable(R.drawable.large_repost_story); + avatarDrawables[18] = resources.getDrawable(R.drawable.large_hidden); + avatarDrawables[19] = resources.getDrawable(R.drawable.large_notes); if (dialogs_archiveAvatarDrawable != null) { dialogs_archiveAvatarDrawable.setCallback(null); @@ -8683,7 +8697,7 @@ public static void createChatResources(Context context, boolean fontsOnly) { chat_shareIconDrawable = resources.getDrawable(R.drawable.filled_button_share).mutate(); chat_replyIconDrawable = resources.getDrawable(R.drawable.filled_button_reply); chat_closeIconDrawable = resources.getDrawable(R.drawable.msg_voiceclose).mutate(); - chat_goIconDrawable = resources.getDrawable(R.drawable.message_arrow); + chat_goIconDrawable = resources.getDrawable(R.drawable.filled_open_message); int rad = AndroidUtilities.dp(2); RectF rect = new RectF(); @@ -9867,7 +9881,7 @@ public static BackgroundDrawableSettings createBackgroundDrawable( MotionBackgroundDrawable motionBackgroundDrawable = new MotionBackgroundDrawable(backgroundColor, gradientToColor1, gradientToColor2, gradientToColor3, false); Bitmap patternBitmap = null; - if (wallpaperFile != null && !isCustomTheme()) { + if (wallpaperFile != null) { if (wallpaperDocument != null) { File f = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(wallpaperDocument, true); patternBitmap = SvgHelper.getBitmap(f, AndroidUtilities.dp(360), AndroidUtilities.dp(640), false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java index d3a508cc58..2aa4323e70 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ThemeColors.java @@ -188,6 +188,8 @@ public static int[] createDefaultColors() { defaultColors[key_actionBarDefaultSubmenuBackground] = 0xffffffff; defaultColors[key_actionBarDefaultSubmenuSeparator] = 0xfff5f5f5; defaultColors[key_actionBarActionModeDefaultSelector] = 0xffe2e2e2; + defaultColors[key_actionBarActionModeReaction] = 0xfff0f0f0; + defaultColors[key_actionBarActionModeReactionDot] = 0xffc0c0c0; defaultColors[key_actionBarTabActiveText] = 0xffffffff; defaultColors[key_actionBarTabUnactiveText] = 0xffd5e8f7; defaultColors[key_actionBarTabLine] = 0xffffffff; @@ -954,6 +956,8 @@ public static SparseArray createColorKeysMap() { colorKeysMap.put(key_actionBarActionModeDefaultTop, "actionBarActionModeDefaultTop"); colorKeysMap.put(key_actionBarActionModeDefaultIcon, "actionBarActionModeDefaultIcon"); colorKeysMap.put(key_actionBarActionModeDefaultSelector, "actionBarActionModeDefaultSelector"); + colorKeysMap.put(key_actionBarActionModeReaction, "actionBarActionModeReaction"); + colorKeysMap.put(key_actionBarActionModeReactionDot, "actionBarActionModeReactionDot"); colorKeysMap.put(key_actionBarDefaultTitle, "actionBarDefaultTitle"); colorKeysMap.put(key_actionBarDefaultSubtitle, "actionBarDefaultSubtitle"); colorKeysMap.put(key_actionBarDefaultSearch, "actionBarDefaultSearch"); 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 cc43dbee9d..628a34cabf 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/DialogsSearchAdapter.java @@ -151,6 +151,7 @@ public static class RecentSearchObject { public interface DialogsSearchAdapterDelegate { void searchStateChanged(boolean searching, boolean animated); void didPressedOnSubDialog(long did); + void didPressedBlockedDialog(View view, long did); void needRemoveHint(long did); void needClearList(); void runResultsEnterAnimation(); @@ -164,12 +165,14 @@ public static class CategoryAdapterRecycler extends RecyclerListView.SelectionAd private final int currentAccount; private boolean drawChecked; private boolean forceDarkTheme; + private boolean showPremiumBlock; private Theme.ResourcesProvider resourcesProvider; - public CategoryAdapterRecycler(Context context, int account, boolean drawChecked, Theme.ResourcesProvider resourcesProvider) { + public CategoryAdapterRecycler(Context context, int account, boolean drawChecked, boolean showPremiumBlock, Theme.ResourcesProvider resourcesProvider) { this.drawChecked = drawChecked; mContext = context; currentAccount = account; + this.showPremiumBlock = showPremiumBlock; this.resourcesProvider = resourcesProvider; } @@ -180,6 +183,9 @@ public void setIndex(int value) { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { HintDialogCell cell = new HintDialogCell(mContext, drawChecked, resourcesProvider); + if (showPremiumBlock) { + cell.showPremiumBlocked(); + } cell.setLayoutParams(new RecyclerView.LayoutParams(AndroidUtilities.dp(80), AndroidUtilities.dp(86))); return new RecyclerListView.Holder(cell); } @@ -1374,7 +1380,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType View view; switch (viewType) { case VIEW_TYPE_PROFILE_CELL: - view = new ProfileSearchCell(mContext); + view = new ProfileSearchCell(mContext).showPremiumBlock(dialogsType == DialogsActivity.DIALOGS_TYPE_FORWARD); break; case VIEW_TYPE_GRAY_SECTION: view = new GraySectionCell(mContext); @@ -1422,8 +1428,14 @@ public boolean supportsPredictiveItemAnimations() { layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); horizontalListView.setLayoutManager(layoutManager); //horizontalListView.setDisallowInterceptTouchEvents(true); - horizontalListView.setAdapter(new CategoryAdapterRecycler(mContext, currentAccount, false, resourcesProvider)); + horizontalListView.setAdapter(new CategoryAdapterRecycler(mContext, currentAccount, false, dialogsType == DialogsActivity.DIALOGS_TYPE_FORWARD, resourcesProvider)); horizontalListView.setOnItemClickListener((view1, position) -> { + if (view1 instanceof HintDialogCell && ((HintDialogCell) view1).isBlocked()) { + if (delegate != null) { + delegate.didPressedBlockedDialog(view1, ((HintDialogCell) view1).getDialogId()); + } + return; + } if (delegate != null) { delegate.didPressedOnSubDialog((Long) view1.getTag()); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/FiltersView.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/FiltersView.java index da7039ba48..6e139588f9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/FiltersView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/FiltersView.java @@ -36,6 +36,7 @@ import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.RecyclerListView; import java.text.SimpleDateFormat; @@ -786,15 +787,21 @@ public ViewHolder(@NonNull FilterView itemView) { public static class MediaFilterData { - public final int iconResFilled; + public ReactionsLayoutInBubble.VisibleReaction reaction; + + public int iconResFilled; public int titleResId; private String title; - public final int filterType; - public final TLRPC.MessagesFilter filter; + public int filterType; + public TLRPC.MessagesFilter filter; public TLObject chat; public DateData dateData; public boolean removable = true; + public MediaFilterData(ReactionsLayoutInBubble.VisibleReaction reaction) { + this.reaction = reaction; + } + public MediaFilterData(int iconResFilled, String title, TLRPC.MessagesFilter filter, int filterType) { this.iconResFilled = iconResFilled; this.title = title; 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 6f72d93412..d7919c427c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MentionsAdapter.java @@ -83,7 +83,7 @@ public interface MentionsAdapterDelegate { private int currentAccount = UserConfig.selectedAccount; private Context mContext; private long dialog_id; - private int threadMessageId; + private long threadMessageId; private TLRPC.ChatFull info; private SearchAdapterHelper searchAdapterHelper; private ArrayList searchResultUsernames; @@ -181,7 +181,7 @@ public void stop() { } }; - public MentionsAdapter(Context context, boolean darkTheme, long did, int threadMessageId, MentionsAdapterDelegate mentionsAdapterDelegate, Theme.ResourcesProvider resourcesProvider) { + public MentionsAdapter(Context context, boolean darkTheme, long did, long threadMessageId, MentionsAdapterDelegate mentionsAdapterDelegate, Theme.ResourcesProvider resourcesProvider) { this.resourcesProvider = resourcesProvider; mContext = context; delegate = mentionsAdapterDelegate; @@ -1145,7 +1145,7 @@ public int compare(StickerResult lhs, StickerResult rhs) { } } final TLRPC.Chat chat; - int threadId; + long threadId; if (parentFragment != null) { chat = parentFragment.getCurrentChat(); threadId = parentFragment.getThreadId(); @@ -1333,7 +1333,7 @@ public void run() { channelParticipantsMentions.q = usernameString; if (threadId != 0) { channelParticipantsMentions.flags |= 2; - channelParticipantsMentions.top_msg_id = threadId; + channelParticipantsMentions.top_msg_id = (int) threadId; } req.filter = channelParticipantsMentions; final int currentReqId = ++channelLastReqId; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java index a518f2a762..170b4e95a3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java @@ -4262,7 +4262,7 @@ private boolean open(final MessageObject messageObject, TLRPC.WebPage webpage, S messageObject.messageOwner.media.webpage = webPage; TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages(); messagesRes.messages.add(messageObject.messageOwner); - MessagesStorage.getInstance(currentAccount).putMessages(messagesRes, messageObject.getDialogId(), -2, 0, false, messageObject.scheduled, 0); + MessagesStorage.getInstance(currentAccount).putMessages(messagesRes, messageObject.getDialogId(), -2, 0, false, messageObject.scheduled ? 1 : 0, 0); } pagesStack.set(0, webPage); if (pagesStack.size() == 1) { @@ -4295,7 +4295,7 @@ private boolean open(final MessageObject messageObject, TLRPC.WebPage webpage, S if (messageObject != null) { TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages(); messagesRes.messages.add(messageObject.messageOwner); - MessagesStorage.getInstance(currentAccount).putMessages(messagesRes, messageObject.getDialogId(), -2, 0, false, messageObject.scheduled, 0); + MessagesStorage.getInstance(currentAccount).putMessages(messagesRes, messageObject.getDialogId(), -2, 0, false, messageObject.scheduled ? 1 : 0, 0); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java index 5181b1b878..e3040fc653 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CalendarActivity.java @@ -95,7 +95,7 @@ public class CalendarActivity extends BaseFragment implements NotificationCenter Paint blackoutPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private long dialogId; - private int topicId; + private long topicId; private boolean loading; private boolean checkEnterItems; private boolean inSelectionMode; @@ -157,7 +157,7 @@ public CalendarActivity(Bundle args, int photosVideosTypeFilter, int selectedDat @Override public boolean onFragmentCreate() { dialogId = getArguments().getLong("dialog_id"); - topicId = getArguments().getInt("topic_id"); + topicId = getArguments().getLong("topic_id"); calendarType = getArguments().getInt("type"); if (calendarType == TYPE_PROFILE_STORIES) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java index de8837a389..25ffd401f3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatActionCell.java @@ -68,7 +68,6 @@ import org.telegram.messenger.SharedConfig; import org.telegram.messenger.SvgHelper; import org.telegram.messenger.UserConfig; -import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.TLObject; @@ -205,7 +204,7 @@ default long getDialogId() { return 0; } - default int getTopicId() { + default long getTopicId() { return 0; } @@ -1198,7 +1197,7 @@ private void buildLayout() { text = StoriesUtilities.createExpiredStoryString(true, "ExpiredStoryMentioned", R.string.ExpiredStoryMentioned, MessagesController.getInstance(currentAccount).getUser(messageObject.getDialogId()).first_name); } } else if (delegate.getTopicId() == 0 && MessageObject.isTopicActionMessage(messageObject)) { - TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(-messageObject.getDialogId(), MessageObject.getTopicId(messageObject.messageOwner, true)); + TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(-messageObject.getDialogId(), MessageObject.getTopicId(currentAccount, messageObject.messageOwner, true)); text = ForumUtilities.createActionTextWithTopic(topic, messageObject); } if (text == null) { 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 594f0761e4..90374361fa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -88,8 +88,6 @@ import androidx.core.graphics.ColorUtils; import androidx.core.math.MathUtils; -import com.google.android.exoplayer2.util.Log; - import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; @@ -139,7 +137,6 @@ import org.telegram.ui.Components.AudioVisualizerDrawable; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackgroundGradientDrawable; -import org.telegram.ui.Components.BottomPagerTabs; import org.telegram.ui.Components.ButtonBounce; import org.telegram.ui.Components.CheckBoxBase; import org.telegram.ui.Components.ClipRoundedDrawable; @@ -754,6 +751,20 @@ public static class PollButton { private TLRPC.TL_pollAnswer answer; } + public static final int INSTANT_BUTTON_TYPE_CONTACT_VIEW = 5; + public static final int INSTANT_BUTTON_TYPE_CONTACT_SEND_MESSAGE = 30; + public static final int INSTANT_BUTTON_TYPE_CONTACT_ADD = 31; + + private static class InstantViewButton { + private int type; + private int buttonWidth; + private float textX; + private StaticLayout layout; + private final RectF rect = new RectF(); + private ButtonBounce buttonBounce; + private Drawable selectorDrawable; + } + public boolean pinnedTop; public boolean pinnedBottom; public boolean drawPinnedTop; @@ -815,6 +826,7 @@ public boolean isCellAttachedToWindow() { private Paint onceClearPaint; private RLottieDrawable onceFire; private Paint onceRadialPaint; + private Paint onceRadialCutPaint; private Paint onceRadialStrokePaint; private int onceRadialPaintColor; @@ -877,6 +889,11 @@ public boolean isCellAttachedToWindow() { private float instantViewLayoutWidth; private float instantViewLayoutLeft; private boolean drawInstantView; + private boolean drawContact, drawContactSendMessage, drawContactView, drawContactAdd; + private int drawnContactButtonsFlag = 0; + private ArrayList contactButtons; + private RectF contactRect; + private boolean contactPressed; private boolean pollInstantViewTouchesBottom; public int drawInstantViewType; public String instantViewButtonText; @@ -1114,6 +1131,8 @@ class LoadingDrawableLocation { private boolean invalidatesParent; public boolean isChat; + public boolean isSavedChat; + public boolean isSavedPreviewChat; public boolean isBot; public boolean isMegagroup; public boolean isForum; @@ -1219,7 +1238,7 @@ class LoadingDrawableLocation { private TLRPC.PhotoSize currentReplyPhoto; public int instantDrawableColor; public Drawable instantDrawable; - public ReplyMessageLine quoteLine, linkLine, replyLine; + public ReplyMessageLine quoteLine, linkLine, replyLine, contactLine; private AnimatedFloat translationLoadingFloat; private LinkPath translationLoadingPath; @@ -1261,7 +1280,7 @@ class LoadingDrawableLocation { private StaticLayout timeLayout; public int timeWidth; private int timeTextWidth; - private int timeX; + protected int timeX; private CharSequence currentTimeString; private boolean drawTime = true; private boolean forceNotDrawTime; @@ -1276,6 +1295,7 @@ class LoadingDrawableLocation { private float mediaSpoilerRevealY; private float mediaSpoilerRevealMaxRadius; private SpoilerEffect2 mediaSpoilerEffect2; + private Integer mediaSpoilerEffect2Index; private float unlockAlpha = 1f; private float unlockX; @@ -1313,7 +1333,7 @@ class LoadingDrawableLocation { public AnimatedEmojiSpan.EmojiGroupedSpans animatedEmojiStack; public AnimatedEmojiSpan.EmojiGroupedSpans animatedEmojiReplyStack; public AnimatedEmojiSpan.EmojiGroupedSpans animatedEmojiDescriptionStack; - public ButtonBounce replyBounce; + public ButtonBounce replyBounce, contactBounce; public float replyBounceX, replyBounceY; public Drawable replySelector; public LoadingDrawable replyLoadingDrawable; @@ -1361,6 +1381,8 @@ class LoadingDrawableLocation { private FlagSecureReason flagSecure; + public boolean makeVisibleAfterChange; + private Runnable diceFinishCallback = new Runnable() { @Override public void run() { @@ -1668,7 +1690,7 @@ private boolean checkNameMotionEvent(MotionEvent event) { int id; TLRPC.Chat chat = currentChat; if (currentMessageObject.messageOwner.fwd_from != null) { - if ((currentMessageObject.messageOwner.fwd_from.flags & 16) != 0) { + if (chat == null && (currentMessageObject.messageOwner.fwd_from.flags & 16) != 0) { id = currentMessageObject.messageOwner.fwd_from.saved_from_msg_id; } else { id = currentMessageObject.messageOwner.fwd_from.channel_post; @@ -2264,7 +2286,13 @@ private boolean checkLinkPreviewMotionEvent(MotionEvent event) { } } else if (event.getAction() == MotionEvent.ACTION_UP) { if (instantPressed) { - if (drawInstantView) { + if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND) { + if (!MediaController.getInstance().isPlayingMessage(currentMessageObject) || MediaController.getInstance().isMessagePaused()) { + delegate.needPlayMessage(this, currentMessageObject, false); + } else { + MediaController.getInstance().pauseMessage(currentMessageObject); + } + } else if (drawInstantView) { if (delegate != null) { delegate.didPressInstantButton(this, drawInstantViewType); } @@ -2331,13 +2359,7 @@ private boolean checkLinkPreviewMotionEvent(MotionEvent event) { pressedEmoji = null; resetPressedLink(2); } else { - if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND) { - if (!MediaController.getInstance().isPlayingMessage(currentMessageObject) || MediaController.getInstance().isMessagePaused()) { - delegate.needPlayMessage(this, currentMessageObject, false); - } else { - MediaController.getInstance().pauseMessage(currentMessageObject); - } - } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_GIF && drawImageButton) { + if (documentAttachType == DOCUMENT_ATTACH_TYPE_GIF && drawImageButton) { if (buttonState == -1) { if (SharedConfig.isAutoplayGifs() && !currentMessageObject.isRepostPreview) { delegate.didPressImage(this, lastTouchX, lastTouchY); @@ -2551,6 +2573,91 @@ private boolean checkInstantButtonMotionEvent(MotionEvent event) { return false; } + private boolean checkContactMotionEvent(MotionEvent event) { + if (currentMessageObject.type != MessageObject.TYPE_CONTACT) { + return false; + } + int x = (int) event.getX(); + int y = (int) event.getY(); + + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (contactButtons != null && contactButtons.size() > 1) { + for (int i = 0; i < contactButtons.size(); i++) { + InstantViewButton instantViewButton = contactButtons.get(i); + if (instantViewButton.rect.contains(x, y)) { + if (instantViewButton.buttonBounce == null) { + instantViewButton.buttonBounce = new ButtonBounce(this); + } + instantViewButton.buttonBounce.setPressed(true); + if (Build.VERSION.SDK_INT >= 21 && instantViewButton.selectorDrawable != null) { + instantViewButton.selectorDrawable.setHotspot(x, y); + instantViewButton.selectorDrawable.setState(pressedState); + } + invalidate(); + return true; + } + } + } + if (contactRect.contains(x, y)) { + contactPressed = true; + contactBounce.setPressed(true); + if (Build.VERSION.SDK_INT >= 21 && selectorDrawable[0] != null) { + selectorDrawable[0].setHotspot(x, y); + selectorDrawable[0].setState(pressedState); + } + invalidate(); + return true; + } + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (contactPressed) { + if (delegate != null) { + if (contactButtons != null && contactButtons.size() == 1) { + delegate.didPressInstantButton(this, contactButtons.get(0).type); + } else { + delegate.didPressInstantButton(this, INSTANT_BUTTON_TYPE_CONTACT_VIEW); + } + } + playSoundEffect(SoundEffectConstants.CLICK); + if (Build.VERSION.SDK_INT >= 21 && selectorDrawable[0] != null) { + selectorDrawable[0].setState(StateSet.NOTHING); + } + contactPressed = false; + contactBounce.setPressed(false); + invalidate(); + } else if (contactButtons != null && contactButtons.size() > 1) { + for (int i = 0; i < contactButtons.size(); i++) { + InstantViewButton instantViewButton = contactButtons.get(i); + if (instantViewButton.buttonBounce != null && instantViewButton.buttonBounce.isPressed()) { + if (delegate != null) { + delegate.didPressInstantButton(this, instantViewButton.type); + } + if (Build.VERSION.SDK_INT >= 21 && instantViewButton.selectorDrawable != null) { + instantViewButton.selectorDrawable.setState(StateSet.NOTHING); + } + instantViewButton.buttonBounce.setPressed(false); + playSoundEffect(SoundEffectConstants.CLICK); + invalidate(); + } + } + } + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (contactPressed && Build.VERSION.SDK_INT >= 21 && selectorDrawable[0] != null) { + selectorDrawable[0].setHotspot(x, y); + } else if (contactButtons != null && contactButtons.size() > 1) { + for (int i = 0; i < contactButtons.size(); i++) { + InstantViewButton instantViewButton = contactButtons.get(i); + if (instantViewButton.buttonBounce != null && instantViewButton.buttonBounce.isPressed()) { + if (Build.VERSION.SDK_INT >= 21 && instantViewButton.selectorDrawable != null) { + instantViewButton.selectorDrawable.setHotspot(x, y); + } + break; + } + } + } + } + return false; + } + private void invalidateWithParent() { if (currentMessagesGroup != null && getParent() != null) { ((ViewGroup) getParent()).invalidate(); @@ -2713,6 +2820,27 @@ private void setInstantButtonPressed(boolean pressed) { instantButtonBounce.setPressed(instantButtonPressed = pressed); } + private void resetContactButtonsPressedState() { + contactPressed = false; + if (contactBounce != null) { + contactBounce.setPressed(false); + } + if (Build.VERSION.SDK_INT >= 21 && selectorDrawable[0] != null) { + selectorDrawable[0].setState(StateSet.NOTHING); + } + if (contactButtons != null) { + for (int i = 0; i < contactButtons.size(); i++) { + InstantViewButton instantViewButton = contactButtons.get(i); + if (instantViewButton.buttonBounce != null) { + instantViewButton.buttonBounce.setPressed(false); + } + if (instantViewButton.selectorDrawable != null) { + instantViewButton.selectorDrawable.setState(StateSet.NOTHING); + } + } + } + } + private boolean checkDateMotionEvent(MotionEvent event) { if (!currentMessageObject.isImportedForward()) { return false; @@ -3321,6 +3449,9 @@ public boolean onTouchEvent(MotionEvent event) { if (!result) { result = checkAudioMotionEvent(event); } + if (!result) { + result = checkContactMotionEvent(event); + } if (!result) { result = checkLinkPreviewMotionEvent(event); } @@ -3365,6 +3496,7 @@ public boolean onTouchEvent(MotionEvent event) { gamePreviewPressed = false; instantPressed = commentButtonPressed = false; setInstantButtonPressed(false); + resetContactButtonsPressedState(); if (Build.VERSION.SDK_INT >= 21) { for (int a = 0; a < selectorDrawable.length; a++) { if (selectorDrawable[a] != null) { @@ -3967,9 +4099,34 @@ public void setParentViewSize(int parentW, int parentH) { } } - public void setVisiblePart(int position, int height, int parent, float parentOffset, float visibleTop, int parentW, int parentH, int blurredViewTopOffset, int blurredViewBottomOffset) { + public void copyVisiblePartTo(ChatMessageCell cell) { + if (cell == null) return; + cell.setVisiblePart(childPosition, visibleHeight, visibleParent, visibleParentOffset, visibleTop, parentWidth, parentHeight, blurredViewTopOffset, blurredViewBottomOffset); + } + + private int childPosition; + private int visibleHeight; + private int visibleParent; + private float visibleParentOffset; + private float visibleTop; + public void setVisiblePart( + int position, + int height, + int parent, + float parentOffset, + float visibleTop, + int parentW, + int parentH, + int blurredViewTopOffset, + int blurredViewBottomOffset + ) { + this.childPosition = position; + this.visibleHeight = height; + this.visibleParent = parent; this.parentWidth = parentW; this.parentHeight = parentH; + this.visibleTop = visibleTop; + this.visibleParentOffset = parentOffset; this.backgroundHeight = parentH; this.blurredViewTopOffset = blurredViewTopOffset; this.blurredViewBottomOffset = blurredViewBottomOffset; @@ -4534,6 +4691,9 @@ protected void onAttachedToWindow() { if (mediaSpoilerEffect2 != null) { if (mediaSpoilerEffect2.destroyed) { mediaSpoilerEffect2 = SpoilerEffect2.getInstance(this); + if (mediaSpoilerEffect2Index != null) { + mediaSpoilerEffect2.reassignAttach(this, mediaSpoilerEffect2Index); + } } else { mediaSpoilerEffect2.attach(this); } @@ -4543,6 +4703,15 @@ protected void onAttachedToWindow() { } } + public void copySpoilerEffect2AttachIndexFrom(ChatMessageCell cell) { + if (cell != null && cell.mediaSpoilerEffect2 != null) { + mediaSpoilerEffect2Index = cell.mediaSpoilerEffect2.getAttachIndex(cell); + if (mediaSpoilerEffect2 != null) { + mediaSpoilerEffect2.reassignAttach(this, mediaSpoilerEffect2Index); + } + } + } + boolean imageReceiversAttachState; boolean imageReceiversVisibleState; @@ -4634,7 +4803,7 @@ private void fileAttach(boolean checkUI, final MessageObject messageObject) { } } else if (canDownload != 0) { if (document != null) { - FileLoader.getInstance(currentAccount).loadFile(document, messageObject, FileLoader.PRIORITY_NORMAL, MessageObject.isVideoDocument(document) && messageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); + FileLoader.getInstance(currentAccount).loadFile(document, messageObject, FileLoader.PRIORITY_NORMAL, (MessageObject.isVideoDocument(document) || messageObject.isVoiceOnce() || messageObject.isRoundOnce()) && messageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); } else if (photo != null) { FileLoader.getInstance(currentAccount).loadFile(ImageLocation.getForObject(photo, messageObject.photoThumbsObject), messageObject, null, FileLoader.PRIORITY_NORMAL, messageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); } @@ -4802,6 +4971,8 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe photoImage.setCrossfadeByScale(0); photoImage.setGradientBitmap(null); photoImage.clearDecorators(); + photoImage.setInvalidateAll(false); + linkPreviewY = 0; lastTranslated = messageObject.translated; lastSendState = messageObject.messageOwner.send_state; lastDeleteDate = messageObject.messageOwner.destroyTime; @@ -4833,8 +5004,8 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } else if (MessagesController.getInstance(currentAccount).isChatNoForwards(messageObject.getChatId()) || (messageObject.messageOwner != null && messageObject.messageOwner.noforwards)) { drawSideButton = 0; } else { - drawSideButton = !isRepliesChat && checkNeedDrawShareButton(messageObject) && (currentPosition == null || currentPosition.last) ? 1 : 0; - if (isPinnedChat || drawSideButton == 1 && messageObject.messageOwner.fwd_from != null && !messageObject.isOutOwner() && messageObject.messageOwner.fwd_from.saved_from_peer != null && messageObject.getDialogId() == UserConfig.getInstance(currentAccount).getClientUserId()) { + drawSideButton = !isRepliesChat && checkNeedDrawShareButton(messageObject) ? 1 : 0; + if (isPinnedChat || drawSideButton == 1 && (messageObject.messageOwner.fwd_from != null && !messageObject.isOutOwner() && messageObject.messageOwner.fwd_from.saved_from_peer != null && messageObject.getDialogId() == UserConfig.getInstance(currentAccount).getClientUserId() || messageObject.isSaved)) { drawSideButton = 2; } } @@ -4890,6 +5061,12 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } } } + resetContactButtonsPressedState(); + drawnContactButtonsFlag = 0; + drawContact = false; + drawContactView = false; + drawContactSendMessage = false; + drawContactAdd = false; spoilerPressed = null; isSpoilerRevealing = false; linkPreviewPressed = false; @@ -5168,7 +5345,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } else { maxWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(80); } - drawName = isPinnedChat || (messageObject.messageOwner.peer_id != null && messageObject.messageOwner.peer_id.channel_id != 0 && (!messageObject.isOutOwner() || messageObject.isSupergroup())) || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null; + drawName = isPinnedChat || isSavedChat && !messageObject.isOutOwner() && (messageObject.getSavedDialogId() < 0 || messageObject.getSavedDialogId() == UserObject.ANONYMOUS) || (messageObject.messageOwner.peer_id != null && messageObject.messageOwner.peer_id.channel_id != 0 && (!messageObject.isOutOwner() || messageObject.isSupergroup())) || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null; } availableTimeWidth = maxWidth; @@ -5248,7 +5425,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } else if ("telegram_megagroup".equals(webpageType)) { drawInstantView = true; drawInstantViewType = 2; - } else if ("telegram_message".equals(webpageType) || "photo".equals(webpageType) && webpage != null && webpage.url != null && Browser.isInternalUri(Uri.parse(webpage.url), null)) { + } else if ("telegram_message".equals(webpageType) || "photo".equals(webpageType) && webpage != null && Browser.isTMe(webpage.url)) { drawInstantView = true; drawInstantViewType = 3; } else if ("telegram_community".equals(webpageType) || "telegram_chatlist".equals(webpageType)) { @@ -5420,7 +5597,15 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe giveawayMessageCell.setMessageContent(messageObject, getParentWidth(), forwardedNameWidth); giveawayResultsMessageCell.setMessageContent(messageObject, getParentWidth(), forwardedNameWidth); - backgroundWidth = messageObject.textWidth + getExtraTextX() * 2 + (hasGamePreview || hasInvoicePreview ? AndroidUtilities.dp(10) : 0); + if (messageObject.isSponsored()) { + if (AndroidUtilities.isTablet()) { + backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(50), AndroidUtilities.dp(270)); + } else { + backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(50), AndroidUtilities.dp(270)); + } + } else { + backgroundWidth = messageObject.textWidth + getExtraTextX() * 2 + (hasGamePreview || hasInvoicePreview ? AndroidUtilities.dp(10) : 0); // todo! here we set + } if (messageObject.isSponsored()) { totalHeight = AndroidUtilities.dp(22.5f); } else { @@ -5521,7 +5706,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } duration = 0; type = null; - isSmallImage = true; + isSmallImage = photo != null || peerPhoto != null; linkPreviewAbove = false; smallImage = true; } else if (drawInstantViewType == 19) { @@ -6164,6 +6349,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe int height; if (smallImage || documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND) { width = height = maxPhotoWidth; + photoImage.setInvalidateAll(true); } else { if (hasGamePreview || hasInvoicePreview) { if (hasInvoicePreview) { @@ -6347,7 +6533,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe blurredPhotoImage.setImageBitmap((Bitmap) null); } if (photoImage.getBitmap() != null && !photoImage.getBitmap().isRecycled() && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed) { - blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(photoImage.getBitmap())); + blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(photoImage.getBitmap(), currentMessageObject.isRoundVideo())); blurredPhotoImage.setColorFilter(getFancyBlurFilter()); } drawPhotoImage = true; @@ -6509,7 +6695,9 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe namesOffset -= AndroidUtilities.dp(1); } } else if (messageObject.type == MessageObject.TYPE_CONTACT) { - drawName = messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null; + drawContact = true; + + drawName = isSavedChat && !messageObject.isOutOwner() && (messageObject.getSavedDialogId() < 0 || messageObject.getSavedDialogId() == UserObject.ANONYMOUS) || messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null; drawForwardedName = !isRepliesChat; drawPhotoImage = true; photoImage.setRoundRadius(AndroidUtilities.dp(22)); @@ -6560,8 +6748,12 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } if (user != null || !TextUtils.isEmpty(messageObject.vCardData) || MessageObject.getMedia(messageObject.messageOwner) instanceof TLRPC.TL_messageMediaContact) { - drawInstantView = true; - drawInstantViewType = 5; + if (user != null) { + drawContactSendMessage = true; + drawContactAdd = !user.contact; + } else { + drawContactView = true; + } } CharSequence currentNameString = ContactsController.formatName(MessageObject.getMedia(messageObject.messageOwner).first_name, MessageObject.getMedia(messageObject.messageOwner).last_name).replace('\n', ' '); @@ -6581,13 +6773,12 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } else if (drawNameLayout && messageObject.getReplyMsgId() == 0) { namesOffset += AndroidUtilities.dp(7); } - - totalHeight = AndroidUtilities.dp(70 - 15) + namesOffset + docTitleLayout.getHeight(); + totalHeight = AndroidUtilities.dp(65) + namesOffset + docTitleLayout.getHeight(); if (drawPinnedTop) { namesOffset -= AndroidUtilities.dp(1); } - if (drawInstantView) { - createInstantViewButton(); + if (drawContactSendMessage || drawContactAdd || drawContactView) { + createContactButtons(); } else { if (docTitleLayout.getLineCount() > 0) { int timeLeft = backgroundWidth - AndroidUtilities.dp(40 + 18 + 44 + 8) - (int) Math.ceil(docTitleLayout.getLineWidth(docTitleLayout.getLineCount() - 1)); @@ -6599,8 +6790,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe if (!reactionsLayoutInBubble.isSmall) { if (!reactionsLayoutInBubble.isEmpty) { reactionsLayoutInBubble.measure(backgroundWidth - AndroidUtilities.dp(32), Gravity.LEFT); - reactionsLayoutInBubble.totalHeight = reactionsLayoutInBubble.height + AndroidUtilities.dp(12); - reactionsLayoutInBubble.positionOffsetY += -AndroidUtilities.dp(4); + reactionsLayoutInBubble.totalHeight = reactionsLayoutInBubble.height; if (backgroundWidth - AndroidUtilities.dp(32) - reactionsLayoutInBubble.lastLineX < timeWidth) { reactionsLayoutInBubble.totalHeight += AndroidUtilities.dp(12); reactionsLayoutInBubble.positionOffsetY += -AndroidUtilities.dp(12); @@ -6610,7 +6800,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } } else if (messageObject.type == MessageObject.TYPE_VOICE) { drawForwardedName = !isRepliesChat; - drawName = messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null; + drawName = isSavedChat && !messageObject.isOutOwner() && (messageObject.getSavedDialogId() < 0 || messageObject.getSavedDialogId() == UserObject.ANONYMOUS) || messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null; int maxWidth; if (AndroidUtilities.isTablet()) { backgroundWidth = maxWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); @@ -6650,7 +6840,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } } } else if (messageObject.type == MessageObject.TYPE_MUSIC) { - drawName = (messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null) && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_TOP) != 0); + drawName = isSavedChat && !messageObject.isOutOwner() && (messageObject.getSavedDialogId() < 0 || messageObject.getSavedDialogId() == UserObject.ANONYMOUS) || (messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null) && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_TOP) != 0); int maxWidth; if (AndroidUtilities.isTablet()) { backgroundWidth = maxWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270)); @@ -6872,7 +7062,8 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe button.count = answer.voters; button.correct = answer.correct; if ((pollVoted || pollClosed) && media.results.total_voters > 0) { - button.decimal = 100 * (answer.voters / (float) media.results.total_voters); + float percent = answer.voters / (float) media.results.total_voters; + button.decimal = 100 * percent; button.percent = (int) button.decimal; button.decimal -= button.percent; } else { @@ -6987,7 +7178,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } else { drawForwardedName = (messageObject.messageOwner.fwd_from != null && !(messageObject.isAnyKindOfSticker() && messageObject.isDice())) || messageObject.type == MessageObject.TYPE_STORY; if (!messageObject.isAnyKindOfSticker() && messageObject.type != MessageObject.TYPE_ROUND_VIDEO) { - drawName = (messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isRepostPreview || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null) && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_TOP) != 0); + drawName = (isSavedChat && !messageObject.isOutOwner() && (messageObject.getSavedDialogId() < 0 || messageObject.getSavedDialogId() == UserObject.ANONYMOUS) || messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isRepostPreview || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null) && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_TOP) != 0); } mediaBackground = isMedia = messageObject.type != MessageObject.TYPE_FILE; drawImageButton = true; @@ -7473,7 +7664,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe blurredPhotoImage.setImageBitmap((Bitmap) null); } if (photoImage.getBitmap() != null && !photoImage.getBitmap().isRecycled() && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed) { - blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(photoImage.getBitmap())); + blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(photoImage.getBitmap(), currentMessageObject.isRoundVideo())); blurredPhotoImage.setColorFilter(getFancyBlurFilter()); } } else { @@ -7932,8 +8123,14 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe ) { if (messageObject.needDrawBluredPreview()) { photoImage.setColorFilter(getFancyBlurFilter()); - currentPhotoFilter += "_b2"; - currentPhotoFilterThumb += "_b2"; + if (!messageObject.isRoundOnce()) { + currentPhotoFilter += "_b2"; + } + if (messageObject.isRoundOnce()) { + currentPhotoFilterThumb += "_b2r"; + } else { + currentPhotoFilterThumb += "_b2"; + } } else { currentPhotoFilterThumb += "_b"; } @@ -8056,7 +8253,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe photoImage.setImage(ImageLocation.getForDocument(document), ImageLoader.AUTOPLAY_FILTER, ImageLocation.getForDocument(videoSize, documentAttach), null, ImageLocation.getForDocument(currentPhotoObject != null ? currentPhotoObject : currentPhotoObjectThumb, documentAttach), currentPhotoObject != null ? currentPhotoFilter : currentPhotoFilterThumb, currentPhotoObjectThumbStripped, document.size, null, messageObject, cacheType); } else { if (isRoundVideo && !messageIdChanged && photoImage.hasStaticThumb()) { - photoImage.setImage(ImageLocation.getForDocument(document), ImageLoader.AUTOPLAY_FILTER, ImageLocation.getForObject(currentPhotoObject, photoParentObject), currentPhotoFilter, null, null, photoImage.getStaticThumb(), document.size, null, messageObject, 0); + photoImage.setImage(ImageLocation.getForDocument(document), ImageLoader.AUTOPLAY_FILTER, ImageLocation.getForObject(currentPhotoObject, photoParentObject), currentPhotoFilter, null, null, photoImage.getStaticThumb(), document.size, null, messageObject, messageObject.isRoundOnce() ? cacheType : 0); } else { photoImage.setImage(ImageLocation.getForDocument(document), ImageLoader.AUTOPLAY_FILTER, ImageLocation.getForObject(currentPhotoObject, photoParentObject), currentPhotoFilter, ImageLocation.getForObject(currentPhotoObjectThumb, photoParentObject), currentPhotoFilterThumb, currentPhotoObjectThumbStripped, document.size, null, messageObject, cacheType); } @@ -8067,7 +8264,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe if (videoSize != null && (currentPhotoObject == null || currentPhotoObjectThumb == null)) { photoImage.setImage(ImageLocation.getForDocument(document), null, ImageLocation.getForDocument(videoSize, documentAttach), null, ImageLocation.getForDocument(currentPhotoObject != null ? currentPhotoObject : currentPhotoObjectThumb, documentAttach), currentPhotoObject != null ? currentPhotoFilter : currentPhotoFilterThumb, currentPhotoObjectThumbStripped, document.size, null, messageObject, cacheType); } else { - photoImage.setImage(ImageLocation.getForDocument(document), null, ImageLocation.getForObject(currentPhotoObject, photoParentObject), currentPhotoFilter, ImageLocation.getForObject(currentPhotoObjectThumb, photoParentObject), currentPhotoFilterThumb, currentPhotoObjectThumbStripped, document.size, null, messageObject, 0); + photoImage.setImage(ImageLocation.getForDocument(document), null, ImageLocation.getForObject(currentPhotoObject, photoParentObject), currentPhotoFilter, ImageLocation.getForObject(currentPhotoObjectThumb, photoParentObject), currentPhotoFilterThumb, currentPhotoObjectThumbStripped, document.size, null, messageObject, messageObject.isRoundOnce() ? cacheType : 0); } } } else { @@ -8100,7 +8297,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe blurredPhotoImage.setImageBitmap((Bitmap) null); } if (photoImage.getBitmap() != null && !photoImage.getBitmap().isRecycled() && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed) { - blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(photoImage.getBitmap())); + blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(photoImage.getBitmap(), currentMessageObject.isRoundVideo())); blurredPhotoImage.setColorFilter(getFancyBlurFilter()); } setMessageObjectInternal(messageObject); @@ -8177,6 +8374,9 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe if (messageObject.hasMediaSpoilers() && SpoilerEffect2.supports()) { if (mediaSpoilerEffect2 == null) { mediaSpoilerEffect2 = SpoilerEffect2.getInstance(this); + if (mediaSpoilerEffect2Index != null) { + mediaSpoilerEffect2.reassignAttach(this, mediaSpoilerEffect2Index); + } } } else { if (mediaSpoilerEffect2 != null) { @@ -8634,7 +8834,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe updateButtonState(false, !messageIdChanged && !messageObject.cancelEditing, true); if (!currentMessageObject.loadingCancelled && buttonState == 2 && documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO && DownloadController.getInstance(currentAccount).canDownloadMedia(messageObject)) { - FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, FileLoader.PRIORITY_NORMAL, 0); + FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, FileLoader.PRIORITY_NORMAL, currentMessageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); buttonState = 4; radialProgress.setIcon(getIconForCurrentState(), false, false); } @@ -8666,7 +8866,7 @@ private void updateFlagSecure() { Activity activity = AndroidUtilities.findActivity(getContext()); Window window = activity == null ? null : activity.getWindow(); if (window != null) { - flagSecure = new FlagSecureReason(window, () -> currentMessageObject != null && currentMessageObject.messageOwner != null && (currentMessageObject.messageOwner.noforwards || currentMessageObject.hasRevealedExtendedMedia())); + flagSecure = new FlagSecureReason(window, () -> currentMessageObject != null && currentMessageObject.messageOwner != null && (currentMessageObject.messageOwner.noforwards || currentMessageObject.isVoiceOnce() || currentMessageObject.hasRevealedExtendedMedia())); if (attachedToWindow) { flagSecure.attach(); } @@ -8823,6 +9023,8 @@ public void invalidate() { giveawayMessageCell.setButtonPressed(false); giveawayResultsMessageCell.setButtonPressed(false); + resetContactButtonsPressedState(); + if (pressedVoteButton != -1 || pollHintPressed || psaHintPressed || instantPressed || otherPressed || commentButtonPressed) { instantPressed = commentButtonPressed = false; setInstantButtonPressed(false); @@ -9348,6 +9550,10 @@ private int createDocumentLayout(int maxWidth, MessageObject messageObject) { } private void calcBackgroundWidth(int maxWidth, int timeMore, int maxChildWidth) { + if (currentMessageObject.isSponsored()) { + backgroundWidth = maxChildWidth + AndroidUtilities.dp(31); + return; + } boolean newLineForTime; int lastLineWidth = (reactionsLayoutInBubble.isEmpty || reactionsLayoutInBubble.isSmall) ? currentMessageObject.lastLineWidth : reactionsLayoutInBubble.lastLineX; if (!reactionsLayoutInBubble.isEmpty && !reactionsLayoutInBubble.isSmall) { @@ -9739,6 +9945,8 @@ public void createSelectorDrawable(int num) { color = getThemedColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outViews : Theme.key_chat_inViews); } else if (linkLine != null) { color = linkLine.getColor(); + } else if (contactLine != null) { + color = contactLine.getColor(); } else { color = getThemedColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outPreviewInstantText : Theme.key_chat_inPreviewInstantText); } @@ -9909,6 +10117,79 @@ private void createInstantViewButton() { } } + private void createContactButtons() { + if (Build.VERSION.SDK_INT >= 21) { + createSelectorDrawable(0); + } + if (drawContact) { + int needDrawFlag = 0; + int buttonsCount = 0; + if (drawContactView) { + needDrawFlag |= 1; + buttonsCount++; + } + if (drawContactSendMessage) { + needDrawFlag |= 2; + buttonsCount++; + } + if (drawContactAdd) { + needDrawFlag |= 4; + buttonsCount++; + } + if (buttonsCount == 0) { + contactButtons = null; + drawnContactButtonsFlag = 0; + return; + } + totalHeight += AndroidUtilities.dp(60); + + boolean needRecreate = needDrawFlag != drawnContactButtonsFlag; + if (needRecreate) { + drawnContactButtonsFlag = 0; + int mWidth = (backgroundWidth - AndroidUtilities.dp(10 + 24 + 10 + 31)) / buttonsCount; + int parentWidth = (backgroundWidth - AndroidUtilities.dp(37)) / buttonsCount; + + if (contactButtons == null) { + contactButtons = new ArrayList<>(buttonsCount); + } else { + contactButtons.clear(); + } + + if (drawContactView) { + drawnContactButtonsFlag |= 1; + String str = LocaleController.getString("ViewContact", R.string.ViewContact); + InstantViewButton instantViewButton = createInstantViewButton(INSTANT_BUTTON_TYPE_CONTACT_VIEW, str, mWidth, parentWidth); + contactButtons.add(instantViewButton); + } + if (drawContactSendMessage) { + drawnContactButtonsFlag |= 2; + String str = LocaleController.getString("SharedContactMessage", R.string.SharedContactMessage); + InstantViewButton instantViewButton = createInstantViewButton(INSTANT_BUTTON_TYPE_CONTACT_SEND_MESSAGE, str, mWidth, parentWidth); + contactButtons.add(instantViewButton); + } + if (drawContactAdd) { + drawnContactButtonsFlag |= 4; + String str = LocaleController.getString("SharedContactAdd", R.string.SharedContactAdd); + InstantViewButton instantViewButton = createInstantViewButton(INSTANT_BUTTON_TYPE_CONTACT_ADD, str, mWidth, parentWidth); + contactButtons.add(instantViewButton); + } + } + } + } + + private InstantViewButton createInstantViewButton(int type, String str, int availableWidth, int parentWidth) { + InstantViewButton instantViewButton = new InstantViewButton(); + instantViewButton.type = type; + instantViewButton.layout = new StaticLayout(TextUtils.ellipsize(str, Theme.chat_instantViewPaint, availableWidth, TextUtils.TruncateAt.END), Theme.chat_instantViewPaint, availableWidth + AndroidUtilities.dp(2), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + instantViewButton.buttonWidth = parentWidth; + if (instantViewButton.layout.getLineCount() > 0) { + instantViewButton.textX = (float) (instantViewButton.buttonWidth - Math.ceil(instantViewButton.layout.getLineWidth(0))) / 2; + int instantTextLeftX = (int) instantViewButton.layout.getLineLeft(0); + instantViewButton.textX -= instantTextLeftX; + } + return instantViewButton; + } + @Override public void requestLayout() { if (inLayout) { @@ -10165,7 +10446,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } if (!transitionParams.imageChangeBoundsTransition || transitionParams.updatePhotoImageX) { transitionParams.updatePhotoImageX = false; - photoImage.setImageCoords((float) x, photoImage.getImageY(), photoImage.getImageWidth(), photoImage.getImageHeight()); + photoImage.setImageCoords(x, currentMessageObject.type != MessageObject.TYPE_ROUND_VIDEO ? linkPreviewY : photoImage.getImageY(), photoImage.getImageWidth(), photoImage.getImageHeight()); } } } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC) { @@ -10214,13 +10495,13 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } else if (currentMessageObject.type == MessageObject.TYPE_CONTACT) { int x; if (currentMessageObject.isOutOwner()) { - x = layoutWidth - backgroundWidth + AndroidUtilities.dp(14); + x = layoutWidth - backgroundWidth + AndroidUtilities.dp(26); } else if (needDrawAvatar()) { - x = AndroidUtilities.dp(72); + x = AndroidUtilities.dp(84); } else { - x = AndroidUtilities.dp(23); + x = AndroidUtilities.dp(35); } - photoImage.setImageCoords(x, AndroidUtilities.dp(13) + namesOffset, AndroidUtilities.dp(44), AndroidUtilities.dp(44)); + photoImage.setImageCoords(x, AndroidUtilities.dp(24) + namesOffset, AndroidUtilities.dp(46), AndroidUtilities.dp(46)); } else { int x; if (currentMessageObject.type == MessageObject.TYPE_TEXT && (hasLinkPreview || hasGamePreview || hasInvoicePreview)) { @@ -10619,7 +10900,19 @@ public void drawContent(Canvas canvas, boolean preview) { canvas.restore(); } else { if (allowDrawPhotoImage()) { + boolean needRestore = false; + if (contactBounce != null) { + float contactScale = contactBounce.getScale(0.0125f); + if (contactScale != 1f) { + needRestore = true; + canvas.save(); + canvas.scale(contactScale, contactScale, contactRect.centerX(), contactRect.centerY()); + } + } imageDrawn = drawPhotoImage(canvas); + if (needRestore) { + canvas.restore(); + } } else { imageDrawn = true; } @@ -11231,7 +11524,7 @@ public void onAnimationEnd(Animator animation) { public boolean drawingToBitmap; - private void drawBlurredPhoto(Canvas canvas) { + public void drawBlurredPhoto(Canvas canvas) { if (currentMessageObject.isMediaSpoilersRevealed || mediaSpoilerRevealProgress == 1f) { return; } @@ -11263,16 +11556,19 @@ private void drawBlurredPhoto(Canvas canvas) { blurredPhotoImage.draw(canvas); } + drawBlurredPhotoParticles(canvas); + canvas.restore(); + } + + public void drawBlurredPhotoParticles(Canvas canvas) { if (mediaSpoilerEffect2 != null) { canvas.translate(photoImage.getImageX(), photoImage.getImageY()); mediaSpoilerEffect2.draw(canvas, this, (int) photoImage.getImageWidth(), (int) photoImage.getImageHeight(), photoImage.getAlpha(), drawingToBitmap); - canvas.restore(); } else { int sColor = Color.WHITE; mediaSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f * photoImage.getAlpha()))); mediaSpoilerEffect.setBounds((int) photoImage.getImageX(), (int) photoImage.getImageY(), (int) photoImage.getImageX2(), (int) photoImage.getImageY2()); mediaSpoilerEffect.draw(canvas); - canvas.restore(); invalidate(); } } @@ -11362,7 +11658,7 @@ public void drawVoiceOnce(Canvas canvas, float progress, Runnable drawRadialProg onceClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); } if (scale < 1f) { - canvas.saveLayerAlpha(radialProgress.progressRect, 0xFF, Canvas.ALL_SAVE_FLAG); + canvas.save(); final float s = (1f - scale) * .7f; canvas.scale(s, s, radialProgress.progressRect.centerX(), AndroidUtilities.lerp(radialProgress.progressRect.top, radialProgress.progressRect.bottom, .5f)); if (onceFire == null) { @@ -11371,6 +11667,7 @@ public void drawVoiceOnce(Canvas canvas, float progress, Runnable drawRadialProg onceFire.setAllowDecodeSingleFrame(true); onceFire.setAutoRepeat(1); onceFire.start(); + onceFire.scaleByCanvas = true; } onceFire.setBounds( (int) radialProgress.progressRect.left, @@ -11381,19 +11678,26 @@ public void drawVoiceOnce(Canvas canvas, float progress, Runnable drawRadialProg if (onceRadialPaint == null) { onceRadialPaint = new Paint(Paint.ANTI_ALIAS_FLAG); } + if (onceRadialCutPaint == null) { + onceRadialCutPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + onceRadialCutPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); + } if (onceRadialStrokePaint == null) { onceRadialStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); onceRadialStrokePaint.setStyle(Paint.Style.STROKE); } int iconColor = radialProgress.iconColorKey >= 0 ? getThemedColor(radialProgress.iconColorKey) : radialProgress.iconColor; - if (onceRadialPaintColor != iconColor) { - onceRadialPaint.setColorFilter(new PorterDuffColorFilter(onceRadialPaintColor = iconColor, PorterDuff.Mode.SRC_IN)); - onceRadialStrokePaint.setColorFilter(new PorterDuffColorFilter(onceRadialPaintColor, PorterDuff.Mode.SRC_IN)); - } + onceRadialPaint.setColor(iconColor); + onceRadialStrokePaint.setColor(iconColor); radialProgress.mediaActionDrawable.applyShaderMatrix(false); onceRadialPaint.setShader(radialProgress.mediaActionDrawable.paint2.getShader()); onceRadialStrokePaint.setShader(radialProgress.mediaActionDrawable.paint2.getShader()); - onceFire.draw(canvas, onceRadialPaint); + AndroidUtilities.rectTmp.set(onceFire.getBounds()); + canvas.saveLayerAlpha(AndroidUtilities.rectTmp, 0xFF, Canvas.ALL_SAVE_FLAG); + AndroidUtilities.rectTmp.inset(1, 1); + canvas.drawRect(AndroidUtilities.rectTmp, onceRadialPaint); + onceFire.draw(canvas, onceRadialCutPaint); + canvas.restore(); canvas.restore(); onceRadialStrokePaint.setAlpha((int) (0xFF * (1f - scale))); @@ -11802,7 +12106,7 @@ public void drawLinkPreview(Canvas canvas, float alpha) { } photoImage.setImageX(AndroidUtilities.lerp(transitionParams.photoImageFromCenterX, photoImage.getCenterX(), transitionParams.animateChangeProgress) - photoImage.getImageWidth() / 2f); photoImage.setImageY(AndroidUtilities.lerp(transitionParams.photoImageFromCenterY, photoImage.getCenterY(), transitionParams.animateChangeProgress) - photoImage.getImageHeight() / 2f); - } else if (!isSmallImage && documentAttachType != DOCUMENT_ATTACH_TYPE_DOCUMENT) { + } else if (!isSmallImage && documentAttachType != DOCUMENT_ATTACH_TYPE_ROUND && documentAttachType != DOCUMENT_ATTACH_TYPE_DOCUMENT) { photoImage.setImageWidth(photoWidth); } if (!isSmallImage && drawImageButton) { @@ -11923,7 +12227,11 @@ public void drawLinkPreview(Canvas canvas, float alpha) { color = linkLine.getColor(); } if (this.instantDrawable == null) { - this.instantDrawable = getContext().getResources().getDrawable(R.drawable.msg_instant).mutate(); + if (drawInstantViewType == 16) { + this.instantDrawable = getContext().getResources().getDrawable(R.drawable.mini_external_link).mutate(); + } else { + this.instantDrawable = getContext().getResources().getDrawable(R.drawable.msg_instant).mutate(); + } } if (this.instantDrawableColor != color) { this.instantDrawable.setColorFilter(new PorterDuffColorFilter(this.instantDrawableColor = color, PorterDuff.Mode.SRC_IN)); @@ -11953,6 +12261,11 @@ public void drawLinkPreview(Canvas canvas, float alpha) { instantDrawable.setAlpha((int) (0xFF * alpha)); instantDrawable.draw(canvas); l += AndroidUtilities.dp(15); + } else if (drawInstantViewType == 16) { + l -= AndroidUtilities.dp(11f); + setDrawableBounds(instantDrawable, l + textWidth + AndroidUtilities.dp(4f), instantY - AndroidUtilities.dp(2f), AndroidUtilities.dp(18), AndroidUtilities.dp(18)); + instantDrawable.setAlpha((int) (0xFF * alpha)); + instantDrawable.draw(canvas); } if (instantViewLayout != null) { canvas.save(); @@ -12044,6 +12357,111 @@ private boolean shouldDrawMenuDrawable() { return (currentMessagesGroup == null || (currentPosition.flags & MessageObject.POSITION_FLAG_TOP) != 0) && !hasLinkPreview && (currentMessageObject == null || !currentMessageObject.isRepostPreview); } + private void drawContact(Canvas canvas) { + if (contactLine == null) { + contactLine = new ReplyMessageLine(this); + } + int nameColor = contactLine.check(currentMessageObject, currentUser, currentChat, resourcesProvider, ReplyMessageLine.TYPE_CONTACT); + if (contactBounce == null) { + contactBounce = new ButtonBounce(this, 2.0f, 2.0f); + } + int textX = (int) (photoImage.getImageX() - AndroidUtilities.dp(13)); + int instantY = layoutHeight - AndroidUtilities.dp(62); + if (!reactionsLayoutInBubble.isEmpty && !reactionsLayoutInBubble.isSmall) { + instantY -= reactionsLayoutInBubble.totalHeight; + } + if (drawCommentButton) { + instantY -= AndroidUtilities.dp(shouldDrawTimeOnMedia() ? 39.3f : 41); + } + if (contactRect == null) { + contactRect = new RectF(); + } + int width = getBackgroundDrawableRight() - (AndroidUtilities.dp(10) + (currentMessageObject.isOutOwner() && !mediaBackground && !drawPinnedBottom ? AndroidUtilities.dp(6) : 0)) - getExtraTextX(); + contactRect.set(textX, photoImage.getImageY() - AndroidUtilities.dp(9), width, instantY + AndroidUtilities.dp(38f)); + final float scale = contactBounce.getScale(0.0125f); + boolean needContactViewScale = scale != 1f; + if (needContactViewScale) { + canvas.save(); + canvas.scale(scale, scale, contactRect.centerX(), contactRect.centerY()); + } + if (Build.VERSION.SDK_INT >= 21 && selectorDrawable[0] != null) { + selectorDrawableMaskType[0] = 0; + selectorDrawable[0].setBounds(textX, (int) (photoImage.getImageY() - dp(9)), width, instantY + AndroidUtilities.dp(38f)); + selectorDrawable[0].draw(canvas); + } + float radF = (float) Math.floor(SharedConfig.bubbleRadius / 3f); + int rad = (int) radF; + contactLine.drawBackground(canvas, contactRect, radF, radF, radF, 1f); + contactLine.drawLine(canvas, contactRect, 1f); + Theme.chat_contactNamePaint.setColor(nameColor); + Theme.chat_contactPhonePaint.setColor(getThemedColor(Theme.key_chat_inContactPhoneSelectedText)); + if (currentMessageObject.isOutOwner()) { + Theme.chat_contactPhonePaint.setColor(getThemedColor(Theme.key_chat_messageTextOut)); + } else { + Theme.chat_contactPhonePaint.setColor(getThemedColor(Theme.key_chat_messageTextIn)); + } + if (titleLayout != null) { + canvas.save(); + canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(9), AndroidUtilities.dp(27) + namesOffset); + titleLayout.draw(canvas); + canvas.restore(); + } + if (docTitleLayout != null) { + canvas.save(); + canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(9), AndroidUtilities.dp(50) + namesOffset); + docTitleLayout.draw(canvas); + canvas.restore(); + } + if (contactButtons != null && contactButtons.size() > 0) { + Theme.chat_instantViewPaint.setColor(nameColor); + Theme.chat_instantViewButtonPaint.setColor(Theme.multAlpha(nameColor, .10f)); + int wasAlpha = Theme.chat_instantViewPaint.getAlpha(); + Theme.chat_instantViewPaint.setAlpha((int) (wasAlpha * .18f)); + canvas.drawRect(contactRect.left + AndroidUtilities.dp(3 + 7), instantY + AndroidUtilities.dp(2), contactRect.right - AndroidUtilities.dp(7), instantY + AndroidUtilities.dp(2) + Math.max(1, AndroidUtilities.dp(0.66f)), Theme.chat_instantViewPaint); + Theme.chat_instantViewPaint.setAlpha(wasAlpha); + instantY += AndroidUtilities.dp(2); + textX += AndroidUtilities.dp(3); + boolean needCreateSelectorDrawable = contactButtons != null && contactButtons.size() > 1; + int rippleColor = contactLine.getBackgroundColor(); + for (int i = 0; i < contactButtons.size(); i++) { + InstantViewButton instantViewButton = contactButtons.get(i); + instantViewButton.rect.set(textX, instantY, textX + instantViewButton.buttonWidth, instantY + AndroidUtilities.dp(36)); + if (needCreateSelectorDrawable && instantViewButton.selectorDrawable == null) { + instantViewButton.selectorDrawable = Theme.createRadSelectorDrawable(linkPreviewSelectorColor = rippleColor, 0, 0, i == (contactButtons.size() - 1) ? rad : 0, 0); + } + if (instantViewButton.selectorDrawable != null) { + instantViewButton.selectorDrawable.setBounds(textX, instantY, textX + instantViewButton.buttonWidth, instantY + AndroidUtilities.dp(36)); + instantViewButton.selectorDrawable.draw(canvas); + } + float buttonScale = 1f; + boolean needButtonScale = false; + if (!needContactViewScale) { + if (instantViewButton.buttonBounce != null) { + buttonScale = instantViewButton.buttonBounce.getScale(0.02f); + needButtonScale = buttonScale != 1; + } + } + if (needButtonScale) { + canvas.save(); + canvas.scale(buttonScale, buttonScale, instantViewButton.rect.centerX(), instantViewButton.rect.centerY()); + } + if (instantViewButton.layout != null) { + canvas.save(); + canvas.translate(textX + instantViewButton.textX, instantY + AndroidUtilities.dp(10.5f)); + instantViewButton.layout.draw(canvas); + canvas.restore(); + } + if (needButtonScale) { + canvas.restore(); + } + textX += instantViewButton.buttonWidth; + } + } + if (needContactViewScale) { + canvas.restore(); + } + } + private void drawBotButtons(Canvas canvas, ArrayList botButtons, int alpha) { int addX; if (currentMessageObject != null && currentMessageObject.isOutOwner()) { @@ -13227,7 +13645,7 @@ private void didPressMiniButton(boolean animated) { currentMessageObject.putInDownloadsStore = true; } if (documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO || documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC) { - FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, FileLoader.PRIORITY_NORMAL_UP, 0); + FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, FileLoader.PRIORITY_NORMAL_UP, currentMessageObject.shouldEncryptPhotoOrVideo() ? 2 : 0); currentMessageObject.loadingCancelled = false; } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO || documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND) { createLoadingProgressLayout(documentAttach); @@ -13297,7 +13715,7 @@ private void didPressButton(boolean animated, boolean video) { } } else if (isRoundVideo) { if (currentMessageObject.isSecretMedia()) { - FileLoader.getInstance(currentAccount).loadFile(currentMessageObject.getDocument(), currentMessageObject, FileLoader.PRIORITY_NORMAL_UP, 1); + FileLoader.getInstance(currentAccount).loadFile(currentMessageObject.getDocument(), currentMessageObject, FileLoader.PRIORITY_NORMAL_UP, currentMessageObject.shouldEncryptPhotoOrVideo() ? 2 : 1); } else { currentMessageObject.gifState = 2; TLRPC.Document document = currentMessageObject.getDocument(); @@ -13409,7 +13827,7 @@ private void didPressButton(boolean animated, boolean video) { wouldBeInPip = true; ChatMessageCell.this.invalidate(); } - } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO || documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC) { //asdf1 + } else if (documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO || documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC) { radialProgress.setProgress(0, false); FileLoader.getInstance(currentAccount).loadFile(documentAttach, currentMessageObject, FileLoader.PRIORITY_NORMAL_UP, 0); currentMessageObject.loadingCancelled = false; @@ -13441,7 +13859,7 @@ private void didPressButton(boolean animated, boolean video) { delegate.didPressImage(this, 0, 0); } } else if (buttonState == 4) { - if (documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO || documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC || documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND && currentMessageObject != null && currentMessageObject.isVoiceTranscriptionOpen()) { // asdf2 + if (documentAttachType == DOCUMENT_ATTACH_TYPE_AUDIO || documentAttachType == DOCUMENT_ATTACH_TYPE_MUSIC || documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND && currentMessageObject != null && currentMessageObject.isVoiceTranscriptionOpen()) { if (currentMessageObject.isOut() && (currentMessageObject.isSending() || currentMessageObject.isEditing()) || currentMessageObject.isSendError()) { if (delegate != null && radialProgress.getIcon() != MediaActionDrawable.ICON_CHECK) { delegate.didPressCancelSendButton(this); @@ -13551,7 +13969,7 @@ public void didSetImage(ImageReceiver imageReceiver, boolean set, boolean thumb, blurredPhotoImage.setImageBitmap((Bitmap) null); } if (currentMessageObject.hasMediaSpoilers() && imageReceiver.getBitmap() != null && !imageReceiver.getBitmap().isRecycled()) { - blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(imageReceiver.getBitmap())); + blurredPhotoImage.setImageBitmap(Utilities.stackBlurBitmapMax(imageReceiver.getBitmap(), currentMessageObject.isRoundVideo())); blurredPhotoImage.setColorFilter(getFancyBlurFilter()); } } @@ -13948,8 +14366,9 @@ protected boolean checkNeedDrawShareButton(MessageObject messageObject) { if (currentMessageObject.deleted && !currentMessageObject.deletedByThanos || currentMessageObject.isSponsored()) { return false; } - if (currentPosition != null) { - if (!currentMessagesGroup.isDocuments && !currentPosition.last) { + if (currentMessagesGroup != null && currentPosition != null) { + final boolean last = (currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) != 0 && (currentPosition.flags & (messageObject.isOutOwner() ? MessageObject.POSITION_FLAG_LEFT : MessageObject.POSITION_FLAG_RIGHT)) != 0; + if ((!currentMessagesGroup.reversed && !currentMessagesGroup.isDocuments && !last || currentMessagesGroup.reversed && !last)) { return false; } } @@ -13971,7 +14390,7 @@ private void updateCurrentUserAndChat() { currentChat = MessagesController.getInstance(currentAccount).getChat(fwd_from.from_id.channel_id); } else if (fwd_from != null && fwd_from.saved_from_peer != null) { if (fwd_from.saved_from_peer.user_id != 0) { - if (fwd_from.from_id instanceof TLRPC.TL_peerUser) { + if (!isSavedChat && fwd_from.from_id instanceof TLRPC.TL_peerUser) { currentUser = messagesController.getUser(fwd_from.from_id.user_id); } else { currentUser = messagesController.getUser(fwd_from.saved_from_peer.user_id); @@ -14281,9 +14700,9 @@ private void setMessageObjectInternal(MessageObject messageObject) { } drawTopic = false; - if (!isThreadChat && (delegate != null && delegate.shouldShowTopicButton()) && !pinnedTop && (MessageObject.getTopicId(messageObject.messageOwner, true) != 0 || messageObject.replyToForumTopic != null)) { + if (!isThreadChat && (delegate != null && delegate.shouldShowTopicButton()) && !pinnedTop && (MessageObject.getTopicId(currentAccount, messageObject.messageOwner, true) != 0 || messageObject.replyToForumTopic != null)) { if (currentPosition == null || currentPosition.minY == 0) { - int topicId = messageObject.replyToForumTopic == null ? MessageObject.getTopicId(messageObject.messageOwner, true) : messageObject.replyToForumTopic.id; + long topicId = messageObject.replyToForumTopic == null ? MessageObject.getTopicId(currentAccount, messageObject.messageOwner, true) : messageObject.replyToForumTopic.id; TLRPC.TL_forumTopic topic = messageObject.replyToForumTopic == null ? MessagesController.getInstance(currentAccount).getTopicsController().findTopic(-messageObject.getDialogId(), topicId) : messageObject.replyToForumTopic; if (topic != null) { drawTopic = true; @@ -14522,6 +14941,7 @@ protected void onClick() { stringFinalText = TextUtils.ellipsize(stringFinalText, textPaint, maxWidth, TextUtils.TruncateAt.END); if (stringFinalText instanceof Spannable && messageObject.replyMessageObject.messageOwner != null) { MediaDataController.addTextStyleRuns(messageObject.replyMessageObject.messageOwner.entities, messageObject.replyMessageObject.caption, (Spannable) stringFinalText); + stringFinalText = TextUtils.ellipsize(stringFinalText, textPaint, maxWidth, TextUtils.TruncateAt.END); } } else if (messageObject.replyMessageObject != null && messageObject.replyMessageObject.messageText != null && messageObject.replyMessageObject.messageText.length() > 0) { String mess = messageObject.replyMessageObject.messageText.toString(); @@ -14536,6 +14956,7 @@ protected void onClick() { stringFinalText = TextUtils.ellipsize(stringFinalText, textPaint, maxWidth, TextUtils.TruncateAt.END); if (stringFinalText instanceof Spannable) { MediaDataController.addTextStyleRuns(messageObject.replyMessageObject, (Spannable) stringFinalText); + stringFinalText = TextUtils.ellipsize(stringFinalText, textPaint, maxWidth, TextUtils.TruncateAt.END); } } } else { @@ -14629,7 +15050,7 @@ protected void onClick() { replyTextWidth -= sz; maxWidth += sz; } - if (!isReplyQuote && (currentMessageObject.shouldDrawWithoutBackground() || Build.VERSION.SDK_INT < Build.VERSION_CODES.M)) { + if (changed || !isReplyQuote && (currentMessageObject.shouldDrawWithoutBackground() || Build.VERSION.SDK_INT < Build.VERSION_CODES.M)) { stringFinalText = TextUtils.ellipsize(sb, textPaint, maxWidth, TextUtils.TruncateAt.END); } else { stringFinalText = sb; @@ -14683,7 +15104,7 @@ protected void onClick() { } } } else if (!isThreadChat && messageObject.getReplyMsgId() != 0 && !messageObject.isGiveawayResults()) { - if (!(messageObject.replyMessageObject != null && (messageObject.replyMessageObject.messageOwner instanceof TLRPC.TL_messageEmpty || messageObject.replyMessageObject.messageOwner != null && messageObject.replyMessageObject.messageOwner.action instanceof TLRPC.TL_messageActionTopicCreate))) { + if (!(messageObject.replyMessageObject != null && (messageObject.replyMessageObject.messageOwner instanceof TLRPC.TL_messageEmpty || messageObject.replyMessageObject.messageOwner != null && messageObject.replyMessageObject.messageOwner.action instanceof TLRPC.TL_messageActionTopicCreate)) && messageObject.getDialogId() != UserObject.REPLY_BOT) { if (!messageObject.isAnyKindOfSticker() && messageObject.type != MessageObject.TYPE_ROUND_VIDEO) { namesOffset += AndroidUtilities.dp(14 + 4) + (Theme.chat_replyTextPaint.getTextSize() + Theme.chat_replyNamePaint.getTextSize()); if (messageObject.type != MessageObject.TYPE_TEXT) { @@ -14926,12 +15347,12 @@ protected boolean isWidthAdaptive() { @Override public int getBoundsLeft() { - return Math.max(0, getBackgroundDrawableLeft() - (needDrawAvatar() ? dp(currentPosition != null ? 73 : (currentMessageObject.isRepostPreview ? 42 : 63)) : 0)); + return Math.max(0, getBackgroundDrawableLeft() - (needDrawAvatar() ? dp(currentPosition != null ? 73 : (currentMessageObject != null && currentMessageObject.isRepostPreview ? 42 : 63)) : 0) - (currentMessageObject != null && currentMessageObject.isOutOwner() && (checkNeedDrawShareButton(currentMessageObject) || useTranscribeButton) ? dp(48) : 0)); } @Override public int getBoundsRight() { - return getBackgroundDrawableRight() + (checkNeedDrawShareButton(currentMessageObject) ? dp(48) : 0); + return getBackgroundDrawableRight() + (currentMessageObject != null && !currentMessageObject.isOutOwner() && (checkNeedDrawShareButton(currentMessageObject) || useTranscribeButton) ? dp(48) : 0); } @SuppressLint("WrongCall") @@ -15746,7 +16167,7 @@ public void drawOutboundsContent(Canvas canvas) { drawAnimatedEmojis(canvas, 1f); } - if (currentNameStatusDrawable != null && drawNameLayout && nameLayout != null) { + if (currentNameStatusDrawable != null && drawNameLayout && nameLayout != null && (currentPosition == null || currentPosition.minX == 0 && currentPosition.minY == 0) && !(currentMessageObject.deleted && !drawingToBitmap && currentMessagesGroup != null && currentMessagesGroup.messages.size() >= 1)) { int color; float nameX, nameY; if (currentMessageObject.shouldDrawWithoutBackground()) { @@ -15976,6 +16397,9 @@ public void drawAnimatedEmojiCaption(Canvas canvas, float alpha) { private void drawSideButton(Canvas canvas) { if (drawSideButton != 0) { + if (currentPosition != null && currentMessagesGroup != null && currentMessagesGroup.isDocuments && !currentPosition.last) { + return; + } if (currentMessageObject.isOutOwner()) { sideStartX = transitionParams.lastBackgroundLeft - AndroidUtilities.dp(8 + 32); if (currentMessagesGroup != null) { @@ -16036,17 +16460,8 @@ private void drawSideButton(Canvas canvas) { if (drawSideButton == 2) { Drawable goIconDrawable = getThemedDrawable(Theme.key_drawable_goIcon); - if (currentMessageObject.isOutOwner()) { - setDrawableBounds(goIconDrawable, sideStartX + AndroidUtilities.dp(10), sideStartY + AndroidUtilities.dp(9)); - canvas.save(); - canvas.scale(-1, 1, goIconDrawable.getBounds().centerX(), goIconDrawable.getBounds().centerY()); - } else { - setDrawableBounds(goIconDrawable, sideStartX + AndroidUtilities.dp(12), sideStartY + AndroidUtilities.dp(9)); - } + setDrawableBounds(goIconDrawable, sideStartX + AndroidUtilities.dp(16) - goIconDrawable.getIntrinsicWidth() / 2f, sideStartY + AndroidUtilities.dp(16) - goIconDrawable.getIntrinsicHeight() / 2f); goIconDrawable.draw(canvas); - if (currentMessageObject.isOutOwner()) { - canvas.restore(); - } } else if (drawSideButton == 4) { final int scx = (int) (sideStartX + AndroidUtilities.dp(16)), scy = (int) (sideStartY + AndroidUtilities.dp(16)); Drawable drawable = getThemedDrawable(Theme.key_drawable_closeIcon); @@ -16112,10 +16527,10 @@ public int getBackgroundDrawableRight() { int right = backgroundWidth; if (isRoundVideo) { right -= (int) (getVideoTranscriptionProgress() * AndroidUtilities.dp(3)); - if (drawPinnedBottom && currentMessageObject.isOutOwner()) { + if (drawPinnedBottom && (currentMessageObject != null && currentMessageObject.isOutOwner())) { right -= AndroidUtilities.dp(6) * (1f - getVideoTranscriptionProgress()); } - if (drawPinnedBottom && !currentMessageObject.isOutOwner()) { + if (drawPinnedBottom && !(currentMessageObject != null && currentMessageObject.isOutOwner())) { right -= AndroidUtilities.dp(6) * (1f - getVideoTranscriptionProgress()); } return getBackgroundDrawableLeft() + right; @@ -16150,7 +16565,7 @@ public int getBackgroundDrawableBottom() { additionalBottom += AndroidUtilities.dp(3); } if ((currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) == 0) { - additionalBottom += AndroidUtilities.dp((currentMessageObject.isOutOwner() ? 3 : 4)); + additionalBottom += AndroidUtilities.dp((currentMessageObject != null && currentMessageObject.isOutOwner() ? 3 : 4)); } } @@ -17686,7 +18101,7 @@ public boolean shouldDrawTimeOnMedia() { if (overideShouldDrawTimeOnMedia != 0) { return overideShouldDrawTimeOnMedia == 1; } - return mediaBackground && captionLayout == null && (reactionsLayoutInBubble.isEmpty || reactionsLayoutInBubble.isSmall || currentMessageObject.isAnyKindOfSticker() || currentMessageObject.isRoundVideo())/* || isMedia && drawCommentButton && !isRepliesChat*/; + return mediaBackground && captionLayout == null && (reactionsLayoutInBubble.isEmpty || reactionsLayoutInBubble.isSmall || currentMessageObject != null && (currentMessageObject.isAnyKindOfSticker() || currentMessageObject.isRoundVideo()))/* || isMedia && drawCommentButton && !isRepliesChat*/; } public void drawTime(Canvas canvas, float alpha, boolean fromParent) { @@ -17844,7 +18259,7 @@ private void drawTimeInternal(Canvas canvas, float alpha, boolean fromParent, fl } float x1 = timeX - AndroidUtilities.dp(bigRadius ? 6 : 4); float timeY; - if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND) { + if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND && (currentMessageObject == null || !currentMessageObject.isRoundOnce())) { timeY = layoutHeight - (AndroidUtilities.dp(drawPinnedBottom ? 4 : 5) + reactionsLayoutInBubble.getCurrentTotalHeight(transitionParams.animateChangeProgress)) * (1f - getVideoTranscriptionProgress()); } else { timeY = photoImage.getImageY2() + additionalTimeOffsetY; @@ -17853,7 +18268,7 @@ private void drawTimeInternal(Canvas canvas, float alpha, boolean fromParent, fl float timeHeight = Math.max(AndroidUtilities.dp(17), Theme.chat_timePaint.getTextSize() + AndroidUtilities.dp(5)); rect.set(x1, y1, x1 + timeWidth + AndroidUtilities.dp((bigRadius ? 12 : 8) + (currentMessageObject.isOutOwner() ? 20 + (currentMessageObject.type == MessageObject.TYPE_EMOJIS ? 4 : 0) : 0)), y1 + timeHeight); - if (currentMessageObject.hasMediaSpoilers()) { + if (currentMessageObject.hasMediaSpoilers() && currentMessageObject.type != MessageObject.TYPE_ROUND_VIDEO) { rectPath.rewind(); rectPath.addRoundRect(rect, r, r, Path.Direction.CW); canvas.save(); @@ -18494,7 +18909,7 @@ private void drawStatusDrawable(Canvas canvas, boolean drawCheck1, boolean drawC alpha = alpha * progress; } float timeY; - if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND) { + if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND && (currentMessageObject == null || !currentMessageObject.isRoundOnce())) { timeY = layoutHeight - (AndroidUtilities.dp(drawPinnedBottom ? 4 : 5) + reactionsLayoutInBubble.getCurrentTotalHeight(transitionParams.animateChangeProgress)) * (1f - getVideoTranscriptionProgress()); } else { timeY = photoImage.getImageY2() + additionalTimeOffsetY; @@ -19370,77 +19785,7 @@ public void drawOverlays(Canvas canvas) { updatePollAnimations(dt); canvas.restore(); } else if (currentMessageObject.type == MessageObject.TYPE_CONTACT) { - if (currentMessageObject.isOutOwner()) { - Theme.chat_contactNamePaint.setColor(getThemedColor(Theme.key_chat_outContactNameText)); - Theme.chat_contactPhonePaint.setColor(getThemedColor(isDrawSelectionBackground() ? Theme.key_chat_outContactPhoneSelectedText : Theme.key_chat_outContactPhoneText)); - } else { - Theme.chat_contactNamePaint.setColor(getThemedColor(Theme.key_chat_inContactNameText)); - Theme.chat_contactPhonePaint.setColor(getThemedColor(isDrawSelectionBackground() ? Theme.key_chat_inContactPhoneSelectedText : Theme.key_chat_inContactPhoneText)); - } - if (titleLayout != null) { - canvas.save(); - canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(9), AndroidUtilities.dp(16) + namesOffset); - titleLayout.draw(canvas); - canvas.restore(); - } - if (docTitleLayout != null) { - canvas.save(); - canvas.translate(photoImage.getImageX() + photoImage.getImageWidth() + AndroidUtilities.dp(9), AndroidUtilities.dp(39) + namesOffset); - docTitleLayout.draw(canvas); - canvas.restore(); - } - - if (!currentMessageObject.isRepostPreview) { - Drawable menuDrawable; - if (currentMessageObject.isOutOwner()) { - menuDrawable = getThemedDrawable(isDrawSelectionBackground() ? Theme.key_drawable_msgOutMenuSelected : Theme.key_drawable_msgOutMenu); - } else { - menuDrawable = isDrawSelectionBackground() ? Theme.chat_msgInMenuSelectedDrawable : Theme.chat_msgInMenuDrawable; - } - setDrawableBounds(menuDrawable, otherX = (int) (photoImage.getImageX() + backgroundWidth - AndroidUtilities.dp(48)), otherY = (int) (photoImage.getImageY() - AndroidUtilities.dp(2))); - menuDrawable.draw(canvas); - } - - if (drawInstantView) { - int textX = (int) (photoImage.getImageX() - AndroidUtilities.dp(2)); - int instantY = layoutHeight - AndroidUtilities.dp(36 + 30); - if (!reactionsLayoutInBubble.isEmpty && !reactionsLayoutInBubble.isSmall) { - instantY -= reactionsLayoutInBubble.totalHeight; - } - if (drawCommentButton) { - instantY -= AndroidUtilities.dp(shouldDrawTimeOnMedia() ? 39.3f : 41); - } - if (currentMessageObject.isOutOwner()) { - Theme.chat_instantViewPaint.setColor(getThemedColor(Theme.key_chat_outPreviewInstantText)); - Theme.chat_instantViewButtonPaint.setColor(Theme.multAlpha(getThemedColor(Theme.key_chat_outPreviewInstantText), .10f)); - } else { - Theme.chat_instantViewPaint.setColor(getThemedColor(Theme.key_chat_inPreviewInstantText)); - Theme.chat_instantViewButtonPaint.setColor(Theme.multAlpha(getThemedColor(Theme.key_chat_inPreviewInstantText), .10f)); - } - - instantButtonRect.set(textX, instantY, textX + instantWidth, instantY + AndroidUtilities.dp(36)); - float scale = instantButtonBounce.getScale(.02f); - boolean scaleRestore = scale != 1; - if (scaleRestore) { - canvas.save(); - canvas.scale(scale, scale, instantButtonRect.centerX(), instantButtonRect.centerY()); - } - if (Build.VERSION.SDK_INT >= 21) { - selectorDrawableMaskType[0] = 0; - selectorDrawable[0].setBounds(textX, instantY, textX + instantWidth, instantY + AndroidUtilities.dp(36)); - selectorDrawable[0].draw(canvas); - } - canvas.drawRoundRect(instantButtonRect, AndroidUtilities.dp(6), AndroidUtilities.dp(6), Theme.chat_instantViewButtonPaint); - if (instantViewLayout != null) { - canvas.save(); - canvas.translate(textX + instantTextX, instantY + AndroidUtilities.dp(10.5f)); - instantViewLayout.draw(canvas); - canvas.restore(); - } - if (scaleRestore) { - canvas.restore(); - } - } + drawContact(canvas); } if (drawImageButton && photoImage.getVisible() && !isSmallImage && !currentMessageObject.isRepostVideoPreview) { @@ -19454,12 +19799,12 @@ public void drawOverlays(Canvas canvas) { } boolean restore = false; boolean on = false; - if (currentMessageObject != null && currentMessageObject.isRoundVideo() && !currentMessageObject.mediaExists) { + if (currentMessageObject != null && currentMessageObject.isRoundVideo() && (!currentMessageObject.mediaExists || currentMessageObject.isRoundOnce())) { radialProgress.setProgressRect( - photoImage.getImageX() + (photoImage.getImageWidth() - radialProgress.getRadius()) / 2f, - photoImage.getImageY() + (photoImage.getImageHeight() - radialProgress.getRadius()) / 2f, - photoImage.getImageX() + (photoImage.getImageWidth() + radialProgress.getRadius()) / 2f, - photoImage.getImageY() + (photoImage.getImageHeight() + radialProgress.getRadius()) / 2f + photoImage.getImageX() + (photoImage.getImageWidth() / 2f - radialProgress.getRadius()), + photoImage.getImageY() + (photoImage.getImageHeight() / 2f - radialProgress.getRadius()), + photoImage.getImageX() + (photoImage.getImageWidth() / 2f + radialProgress.getRadius()), + photoImage.getImageY() + (photoImage.getImageHeight() / 2f + radialProgress.getRadius()) ); } else if (currentMessageObject != null && currentMessageObject.isRoundVideo()) { radialProgress.setProgressRect( @@ -19473,34 +19818,21 @@ public void drawOverlays(Canvas canvas) { canvas.scale(scale, scale, radialProgress.getProgressRect().centerX(), radialProgress.getProgressRect().centerY()); restore = true; } - if ((!isRoundVideo || !hasLinkPreview) && (!currentMessageObject.needDrawBluredPreview() || !MediaController.getInstance().isPlayingMessage(currentMessageObject)) && !(currentMessageObject.hasMediaSpoilers() && (!currentMessageObject.isMediaSpoilersRevealed || !currentMessageObject.revealingMediaSpoilers) && SharedConfig.isAutoplayVideo() && !currentMessageObject.isRepostPreview && currentMessagesGroup == null && (radialProgress.getIcon() == MediaActionDrawable.ICON_PLAY || radialProgress.getIcon() == MediaActionDrawable.ICON_NONE))) { + if ( + (!isRoundVideo || !hasLinkPreview) && + (!currentMessageObject.needDrawBluredPreview() || !MediaController.getInstance().isPlayingMessage(currentMessageObject)) && + !(currentMessageObject.hasMediaSpoilers() && (!currentMessageObject.isMediaSpoilersRevealed || !currentMessageObject.revealingMediaSpoilers) && SharedConfig.isAutoplayVideo() && !currentMessageObject.isRepostPreview && currentMessagesGroup == null && (radialProgress.getIcon() == MediaActionDrawable.ICON_PLAY || radialProgress.getIcon() == MediaActionDrawable.ICON_NONE)) + ) { if (currentMessageObject.needDrawBluredPreview()) { radialProgress.overrideCircleAlpha = 0f; } else if (isRoundVideo && !on) { radialProgress.overrideCircleAlpha = .25f + .75f * (1f - getVideoTranscriptionProgress()); } - if ((!SharedConfig.isAutoplayVideo() || currentMessagesGroup != null) && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed && radialProgress.getIcon() == MediaActionDrawable.ICON_PLAY) { + if (!currentMessageObject.isRoundOnce() && (!SharedConfig.isAutoplayVideo() || currentMessagesGroup != null) && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed && radialProgress.getIcon() == MediaActionDrawable.ICON_PLAY) { canvas.saveLayerAlpha(radialProgress.getProgressRect(), (int) (mediaSpoilerRevealProgress * 0xFF), Canvas.ALL_SAVE_FLAG); } - if (currentMessageObject.needDrawBluredPreview()) { - rectPath.rewind(); - rectPath.addRoundRect(radialProgress.getProgressRect(), AndroidUtilities.dp(48), AndroidUtilities.dp(48), Path.Direction.CW); - canvas.save(); - canvas.clipPath(rectPath); - float wasAlpha = photoImage.getAlpha(); - photoImage.setAlpha(.5f * wasAlpha); - photoImage.draw(canvas); - photoImage.setAlpha(wasAlpha); - canvas.restore(); - Paint dimPaint = getThemedPaint(Theme.key_paint_chatTimeBackground); - int oldAlpha2 = dimPaint.getAlpha(); - dimPaint.setAlpha((int) (oldAlpha2 * controlsAlpha * .4f)); - canvas.drawRoundRect(radialProgress.getProgressRect(), AndroidUtilities.dp(48), AndroidUtilities.dp(48), dimPaint); - dimPaint.setAlpha(oldAlpha2); - } - radialProgress.iconScale = 1f; - radialProgress.draw(canvas); - if ((!SharedConfig.isAutoplayVideo() || currentMessagesGroup != null) && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed && radialProgress.getIcon() == MediaActionDrawable.ICON_PLAY) { + drawRadialProgress(canvas); + if (!currentMessageObject.isRoundOnce() && (!SharedConfig.isAutoplayVideo() || currentMessagesGroup != null) && currentMessageObject.hasMediaSpoilers() && !currentMessageObject.isMediaSpoilersRevealed && radialProgress.getIcon() == MediaActionDrawable.ICON_PLAY) { canvas.restore(); } if (currentMessageObject.needDrawBluredPreview() || isRoundVideo && !on) { @@ -19566,8 +19898,13 @@ public void drawOverlays(Canvas canvas) { } } - x1 = backgroundDrawableLeft + transitionParams.deltaLeft + (!currentMessageObject.isOutOwner() && !drawPinnedBottom && drawBackground ? AndroidUtilities.dp(6) : 0) + AndroidUtilities.dp(8) + roundPlayingDrawableProgress + offsetX; - y1 = layoutHeight - AndroidUtilities.dp(28 - (drawPinnedBottom ? 2 : 0)); + if (currentMessageObject != null && currentMessageObject.isRoundOnce()) { + x1 = photoImage.getImageX(); + y1 = photoImage.getImageY2() - (AndroidUtilities.dp(drawPinnedBottom ? 4 : 5) + reactionsLayoutInBubble.getCurrentTotalHeight(transitionParams.animateChangeProgress)) * (1f - getVideoTranscriptionProgress()) - AndroidUtilities.dp(17); + } else { + x1 = backgroundDrawableLeft + transitionParams.deltaLeft + (!currentMessageObject.isOutOwner() && !drawPinnedBottom && drawBackground ? AndroidUtilities.dp(6) : 0) + AndroidUtilities.dp(8) + roundPlayingDrawableProgress + offsetX; + y1 = layoutHeight - AndroidUtilities.dp(28 - (drawPinnedBottom ? 2 : 0)); + } if (!reactionsLayoutInBubble.isEmpty) { y1 -= reactionsLayoutInBubble.totalHeight; } @@ -19652,8 +19989,8 @@ public void drawOverlays(Canvas canvas) { x1 += AndroidUtilities.dp(4); y1 += AndroidUtilities.dp(1.7f); } else { - x1 = backgroundDrawableLeft + AndroidUtilities.dp(currentMessageObject.isOutOwner() || drawPinnedBottom ? 12 : 18); - y1 = layoutHeight - AndroidUtilities.dp(6.3f - (drawPinnedBottom ? 2 : 0)) - timeLayout.getHeight(); + x1 = photoImage.getImageX(); + y1 = photoImage.getImageY2() - (durationLayout != null ? durationLayout.getHeight() : 0); } if (durationLayout != null) { @@ -19667,6 +20004,80 @@ public void drawOverlays(Canvas canvas) { } } + private Paint clipPaint; + protected float radialProgressAlpha = 1f; + protected void drawRadialProgress(Canvas canvas) { + final boolean withPeriod = currentMessageObject.isRoundOnce(); + if (withPeriod) { + AndroidUtilities.rectTmp.set(radialProgress.getProgressRect()); + AndroidUtilities.rectTmp.inset(-dp(15), -dp(15)); + canvas.saveLayerAlpha(AndroidUtilities.rectTmp, (int) (0xFF * radialProgressAlpha), Canvas.ALL_SAVE_FLAG); + } + + if (currentMessageObject.needDrawBluredPreview()) { + drawPhotoBlurRect(canvas, radialProgress.getProgressRect()); + } + radialProgress.iconScale = 1f; + radialProgress.draw(canvas); + + if (withPeriod) { + canvas.save(); + drawPhotoBlurRect(canvas, getRadialProgress().getProgressRect()); + getRadialProgress().draw(canvas); + + RectF pr = getRadialProgress().getProgressRect(); + final float cx = pr.centerX() + dp(18); + final float cy = pr.centerY() + dp(18); + final float r = dp(10), cr = r + dp(1.33f); + + if (clipPaint == null) { + clipPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + clipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + } + canvas.drawCircle(cx, cy, cr, clipPaint); + + AndroidUtilities.rectTmp.set(cx - r, cy - r, cx + r, cy + r); + drawPhotoBlurRect(canvas, AndroidUtilities.rectTmp); + + if (oncePeriod == null) { + oncePeriod = new CaptionContainerView.PeriodDrawable(3); + oncePeriod.updateColors(0xffffffff, 0, 0); + oncePeriod.diameterDp = 14; + oncePeriod.setTextSize(10); + oncePeriod.strokePaint.setStrokeWidth(dpf2(1.5f)); + oncePeriod.setValue(1, false, false); + oncePeriod.textOffsetX = -dpf2(.33f); + oncePeriod.textOffsetY = dpf2(.33f); + } + oncePeriod.diameterDp = 14; + oncePeriod.setTextSize(10); + oncePeriod.setClear(false); + oncePeriod.setCenterXY(cx, cy); + oncePeriod.draw(canvas, 1f); + + canvas.restore(); + + canvas.restore(); + } + } + + protected void drawPhotoBlurRect(Canvas canvas, RectF rect) { + rectPath.rewind(); + rectPath.addRoundRect(rect, rect.width() / 2f, rect.height() / 2f, Path.Direction.CW); + canvas.save(); + canvas.clipPath(rectPath); + float wasAlpha = photoImage.getAlpha(); + photoImage.setAlpha((currentMessageObject.isRoundOnce() ? 1f : .5f) * wasAlpha); + photoImage.draw(canvas); + photoImage.setAlpha(wasAlpha); + canvas.restore(); + Paint dimPaint = getThemedPaint(Theme.key_paint_chatTimeBackground); + int oldAlpha2 = dimPaint.getAlpha(); + dimPaint.setAlpha((int) (oldAlpha2 * controlsAlpha * .4f)); + canvas.drawRoundRect(rect, rect.width() / 2f, rect.height() / 2f, dimPaint); + dimPaint.setAlpha(oldAlpha2); + } + @Override public int getObserverTag() { return TAG; @@ -20059,6 +20470,10 @@ private class MessageAccessibilityNodeProvider extends AccessibilityNodeProvider public static final int POLL_HINT = 495; public static final int FORWARD = 494; public static final int TRANSCRIBE = 493; + public static final int CONTACT = 492; + public static final int CONTACT_VIEW = 491; + public static final int CONTACT_ADD = 490; + public static final int CONTACT_MESSAGE = 489; private Path linkPath = new Path(); private RectF rectF = new RectF(); private Rect rect = new Rect(); @@ -20377,6 +20792,22 @@ public void onClick(View view) { if (drawInstantView && !instantButtonRect.isEmpty()) { info.addChild(ChatMessageCell.this, INSTANT_VIEW); } + if (drawContact && !contactRect.isEmpty()) { + info.addChild(ChatMessageCell.this, CONTACT); + if (contactButtons != null && contactButtons.size() > 1) { + for (InstantViewButton instantViewButton : contactButtons) { + if (drawContactView && instantViewButton.type == INSTANT_BUTTON_TYPE_CONTACT_VIEW && !instantViewButton.rect.isEmpty()) { + info.addChild(ChatMessageCell.this, CONTACT_VIEW); + } + if (drawContactAdd && instantViewButton.type == INSTANT_BUTTON_TYPE_CONTACT_ADD && !instantViewButton.rect.isEmpty()) { + info.addChild(ChatMessageCell.this, CONTACT_ADD); + } + if (drawContactSendMessage && instantViewButton.type == INSTANT_BUTTON_TYPE_CONTACT_SEND_MESSAGE && !instantViewButton.rect.isEmpty()) { + info.addChild(ChatMessageCell.this, CONTACT_MESSAGE); + } + } + } + } if (commentLayout != null) { info.addChild(ChatMessageCell.this, COMMENT); } @@ -20578,6 +21009,56 @@ public void onClick(View view) { rect.offset(pos[0], pos[1]); info.setBoundsInScreen(rect); info.setClickable(true); + } else if (virtualViewId == CONTACT) { + info.setClassName("android.widget.Button"); + info.setEnabled(true); + if (titleLayout != null) { + info.setText(titleLayout.getText()); + } + info.addAction(AccessibilityNodeInfo.ACTION_CLICK); + contactRect.round(rect); + if (contactButtons != null && contactButtons.size() > 1) { + InstantViewButton instantViewButton = contactButtons.get(0); + if (!instantViewButton.rect.isEmpty()) { + rect.set(rect.left, rect.top, rect.right, (int) (rect.bottom - instantViewButton.rect.height())); + } + } + info.setBoundsInParent(rect); + if (accessibilityVirtualViewBounds.get(virtualViewId) == null || !accessibilityVirtualViewBounds.get(virtualViewId).equals(rect)) { + accessibilityVirtualViewBounds.put(virtualViewId, new Rect(rect)); + } + rect.offset(pos[0], pos[1]); + info.setBoundsInScreen(rect); + info.setClickable(true); + } else if (virtualViewId == CONTACT_VIEW || virtualViewId == CONTACT_ADD || virtualViewId == CONTACT_MESSAGE) { + int requiredType; + if (virtualViewId == CONTACT_VIEW) { + requiredType = INSTANT_BUTTON_TYPE_CONTACT_VIEW; + } else if (virtualViewId == CONTACT_ADD) { + requiredType = INSTANT_BUTTON_TYPE_CONTACT_ADD; + } else { + requiredType = INSTANT_BUTTON_TYPE_CONTACT_SEND_MESSAGE; + } + for (int i = 0; i < contactButtons.size(); i++) { + InstantViewButton instantViewButton = contactButtons.get(i); + if (instantViewButton.type == requiredType) { + info.setClassName("android.widget.Button"); + info.setEnabled(true); + if (instantViewButton.layout != null) { + info.setText(instantViewButton.layout.getText()); + } + info.addAction(AccessibilityNodeInfo.ACTION_CLICK); + instantViewButton.rect.round(rect); + info.setBoundsInParent(rect); + if (accessibilityVirtualViewBounds.get(virtualViewId) == null || !accessibilityVirtualViewBounds.get(virtualViewId).equals(rect)) { + accessibilityVirtualViewBounds.put(virtualViewId, new Rect(rect)); + } + rect.offset(pos[0], pos[1]); + info.setBoundsInScreen(rect); + info.setClickable(true); + break; + } + } } else if (virtualViewId == SHARE) { info.setClassName("android.widget.ImageButton"); info.setEnabled(true); @@ -20740,6 +21221,22 @@ public boolean performAction(int virtualViewId, int action, Bundle arguments) { if (delegate != null) { delegate.didPressInstantButton(ChatMessageCell.this, drawInstantViewType); } + } else if (virtualViewId == CONTACT) { + if (delegate != null) { + delegate.didPressInstantButton(ChatMessageCell.this, INSTANT_BUTTON_TYPE_CONTACT_VIEW); + } + } else if (virtualViewId == CONTACT_VIEW) { + if (delegate != null) { + delegate.didPressInstantButton(ChatMessageCell.this, INSTANT_BUTTON_TYPE_CONTACT_VIEW); + } + } else if (virtualViewId == CONTACT_ADD) { + if (delegate != null) { + delegate.didPressInstantButton(ChatMessageCell.this, INSTANT_BUTTON_TYPE_CONTACT_ADD); + } + } else if (virtualViewId == CONTACT_MESSAGE) { + if (delegate != null) { + delegate.didPressInstantButton(ChatMessageCell.this, INSTANT_BUTTON_TYPE_CONTACT_SEND_MESSAGE); + } } else if (virtualViewId == SHARE) { if (delegate != null) { delegate.didPressSideButton(ChatMessageCell.this); @@ -20810,6 +21307,10 @@ private ClickableSpan getLinkById(int id, boolean caption) { } } + public void setImageCoords(RectF rect) { + setImageCoords(rect.left, rect.top, rect.width(), rect.height()); + } + public void setImageCoords(float x, float y, float w, float h) { photoImage.setImageCoords(x, y, w, h); if (documentAttachType == DOCUMENT_ATTACH_TYPE_VIDEO || documentAttachType == DOCUMENT_ATTACH_TYPE_GIF) { @@ -21118,7 +21619,7 @@ public void recordDrawingState() { if (currentBackgroundDrawable != null) { lastDrawingBackgroundRect.set(currentBackgroundDrawable.getBounds()); } - lastDrawingTextBlocks = currentMessageObject.textLayoutBlocks; + lastDrawingTextBlocks = currentMessageObject != null ? currentMessageObject.textLayoutBlocks : null; lastDrawingEdited = edited; lastDrawingCaptionX = captionX; @@ -21180,7 +21681,7 @@ public void recordDrawingState() { lastDrawnForwardedNameLayout[0] = forwardedNameLayout[0]; lastDrawnForwardedNameLayout[1] = forwardedNameLayout[1]; - lastDrawnForwardedName = currentMessageObject.needDrawForwarded(); + lastDrawnForwardedName = currentMessageObject != null && currentMessageObject.needDrawForwarded(); lastForwardNameX = forwardNameX; lastForwardedNamesOffset = namesOffset; lastForwardNameWidth = forwardedNameWidth; @@ -21188,7 +21689,7 @@ public void recordDrawingState() { if (currentBackgroundDrawable != null) { lastBackgroundRight = currentBackgroundDrawable.getBounds().right; } - lastTextXOffset = currentMessageObject.textXOffset; + lastTextXOffset = currentMessageObject != null ? currentMessageObject.textXOffset : 0; lastDrawingReplyTextHeight = replyTextHeight; lastDrawnReplyTextLayout = replyTextLayout; @@ -21702,7 +22203,7 @@ private boolean isDark() { } public boolean needDrawAvatar() { - return isChat && !isThreadPost && currentMessageObject != null && !currentMessageObject.isOutOwner() && currentMessageObject.needDrawAvatar() || currentMessageObject != null && currentMessageObject.forceAvatar; + return isChat && !isSavedPreviewChat && !isThreadPost && currentMessageObject != null && !currentMessageObject.isOutOwner() && currentMessageObject.needDrawAvatar() || currentMessageObject != null && currentMessageObject.forceAvatar; } protected boolean drawPhotoImage(Canvas canvas) { 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 d6d2bc29d5..c52f1c644b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -8,6 +8,8 @@ package org.telegram.ui.Cells; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; @@ -68,6 +70,7 @@ 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.SharedConfig; import org.telegram.messenger.UserConfig; @@ -81,6 +84,7 @@ import org.telegram.ui.Adapters.DialogsAdapter; import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BubbleCounterPath; import org.telegram.ui.Components.CanvasButton; @@ -166,6 +170,7 @@ public class DialogCell extends BaseCell implements StoriesListPlaceProvider.Ava private Paint timerPaint; private Paint timerPaint2; SharedResources sharedResources; + public boolean isSavedDialog; public final StoriesUtilities.AvatarStoryParams storyParams = new StoriesUtilities.AvatarStoryParams(false) { @Override @@ -399,6 +404,13 @@ public static class CustomDialog { private int printingStringType; private TLRPC.DraftMessage draftMessage; + private final AnimatedFloat premiumBlockedT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean premiumBlocked; + + public boolean isBlocked() { + return premiumBlocked; + } + protected CheckBox2 checkBox; public boolean useForceThreeLines; @@ -596,6 +608,7 @@ public void setDialog(TLRPC.Dialog dialog, int type, int folder) { currentDialogFolderId = 0; } dialogsType = type; + showPremiumBlocked(dialogsType == DialogsActivity.DIALOGS_TYPE_FORWARD); folderId = folder; messageId = 0; if (update(0, false)) { @@ -1469,7 +1482,7 @@ public void buildLayout() { messageString = message.messageText; } if (message.topicIconDrawable[0] instanceof ForumBubbleDrawable) { - TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(-message.getDialogId(), MessageObject.getTopicId(message.messageOwner, true)); + TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(-message.getDialogId(), MessageObject.getTopicId(currentAccount, message.messageOwner, true)); if (topic != null) { ((ForumBubbleDrawable) message.topicIconDrawable[0]).setColor(topic.icon_color); } @@ -1621,7 +1634,7 @@ public void buildLayout() { } } } - if (message.isForwarded()) { + if (message.isForwarded() && message.needDrawForwarded()) { drawForwardIcon = true; SpannableStringBuilder builder = new SpannableStringBuilder(messageString); builder.insert(0, "d "); @@ -1647,10 +1660,10 @@ public void buildLayout() { timeString = LocaleController.stringForMessageListDate(message.messageOwner.date); } - if (message == null) { + if (message == null || isSavedDialog) { drawCheck1 = false; drawCheck2 = false; - drawClock = false; + drawClock = message != null && message.isSending() && currentDialogId == UserConfig.getInstance(currentAccount).getClientUserId(); drawCount = false; drawMention = false; drawReactionMention = false; @@ -1775,8 +1788,12 @@ public void buildLayout() { } else if (user != null) { if (UserObject.isReplyUser(user)) { nameString = LocaleController.getString("RepliesTitle", R.string.RepliesTitle); + } else if (UserObject.isAnonymous(user)) { + nameString = LocaleController.getString(R.string.AnonymousForward); } else if (UserObject.isUserSelf(user)) { - if (useMeForMyMessages) { + if (isSavedDialog) { + nameString = LocaleController.getString(R.string.MyNotes); + } else if (useMeForMyMessages) { nameString = LocaleController.getString("FromYou", R.string.FromYou); } else { if (dialogsType == DialogsActivity.DIALOGS_TYPE_FORWARD) { @@ -2918,6 +2935,12 @@ public boolean update(int mask, boolean animated) { if (UserObject.isReplyUser(user)) { avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_REPLIES); avatarImage.setImage(null, null, avatarDrawable, null, user, 0); + } else if (UserObject.isAnonymous(user)) { + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_ANONYMOUS); + avatarImage.setImage(null, null, avatarDrawable, null, user, 0); + } else if (UserObject.isUserSelf(user) && isSavedDialog) { + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_MY_NOTES); + avatarImage.setImage(null, null, avatarDrawable, null, user, 0); } else if (UserObject.isUserSelf(user) && !useMeForMyMessages) { avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_SAVED); avatarImage.setImage(null, null, avatarDrawable, null, user, 0); @@ -3050,6 +3073,7 @@ public void onAnimationEnd(Animator animation) { updateLayout = true; } } + updatePremiumBlocked(animated); return requestLayout; } @@ -3568,7 +3592,7 @@ protected void onDraw(Canvas canvas) { }); } - if (lastTopicMessageUnread && topMessageTopicEndIndex != topMessageTopicStartIndex) { + if (lastTopicMessageUnread && topMessageTopicEndIndex != topMessageTopicStartIndex && (dialogsType == DialogsActivity.DIALOGS_TYPE_DEFAULT || dialogsType == DialogsActivity.DIALOGS_TYPE_7 || dialogsType == DialogsActivity.DIALOGS_TYPE_8)) { canvasButton.setColor(ColorUtils.setAlphaComponent(currentMessagePaint.getColor(), Theme.isCurrentThemeDark() ? 36 : 26)); if (!buttonCreated) { canvasButton.rewind(); @@ -4021,8 +4045,39 @@ protected void onDraw(Canvas canvas) { } } + private PremiumGradient.PremiumGradientTools premiumGradient; + private Drawable lockDrawable; + public boolean drawAvatarOverlays(Canvas canvas) { boolean needInvalidate = false; + float lockT = premiumBlockedT.set(premiumBlocked); + if (lockT > 0) { + float top = avatarImage.getCenterY() + dp(18); + float left = avatarImage.getCenterX() + dp(18); + + canvas.save(); + Theme.dialogs_onlineCirclePaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + canvas.drawCircle(left, top, dp(10 + 1.33f) * lockT, Theme.dialogs_onlineCirclePaint); + if (premiumGradient == null) { + premiumGradient = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradient1, Theme.key_premiumGradient2, -1, -1, -1, resourcesProvider); + } + premiumGradient.gradientMatrix((int) (left - dp(10)), (int) (top - dp(10)), (int) (left + dp(10)), (int) (top + dp(10)), 0, 0); + canvas.drawCircle(left, top, dp(10) * lockT, premiumGradient.paint); + if (lockDrawable == null) { + lockDrawable = getContext().getResources().getDrawable(R.drawable.msg_mini_lock2).mutate(); + lockDrawable.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + } + lockDrawable.setBounds( + (int) (left - lockDrawable.getIntrinsicWidth() / 2f * .875f * lockT), + (int) (top - lockDrawable.getIntrinsicHeight() / 2f * .875f * lockT), + (int) (left + lockDrawable.getIntrinsicWidth() / 2f * .875f * lockT), + (int) (top + lockDrawable.getIntrinsicHeight() / 2f * .875f * lockT) + ); + lockDrawable.setAlpha((int) (0xFF * lockT)); + lockDrawable.draw(canvas); + canvas.restore(); + return false; + } if (isDialogCell && currentDialogFolderId == 0) { showTtl = ttlPeriod > 0 && !isOnline() && !hasCall; if (rightFragmentOpenedProgress != 1f && (showTtl || ttlProgress > 0)) { @@ -4534,6 +4589,8 @@ public void onPopulateAccessibilityEvent(AccessibilityEvent event) { } else if (user != null) { if (UserObject.isReplyUser(user)) { sb.append(LocaleController.getString("RepliesTitle", R.string.RepliesTitle)); + } else if (UserObject.isAnonymous(user)) { + sb.append(LocaleController.getString(R.string.AnonymousForward)); } else { if (user.bot) { sb.append(LocaleController.getString("Bot", R.string.Bot)); @@ -4740,6 +4797,12 @@ public String getMessageNameString() { TLRPC.User fromUser = null; TLRPC.Chat fromChat = null; long fromId = message.getFromChatId(); + if (isSavedDialog && message.messageOwner != null && message.messageOwner.fwd_from != null) { + fromId = DialogObject.getPeerDialogId(message.messageOwner.fwd_from.saved_from_id); + if (fromId == 0) { + fromId = DialogObject.getPeerDialogId(message.messageOwner.fwd_from.from_id); + } + } if (DialogObject.isUserDialog(fromId)) { fromUser = MessagesController.getInstance(currentAccount).getUser(fromId); } else { @@ -4748,7 +4811,7 @@ public String getMessageNameString() { if (message.isOutOwner()) { return LocaleController.getString("FromYou", R.string.FromYou); - } else if (message != null && message.messageOwner != null && message.messageOwner.from_id instanceof TLRPC.TL_peerUser && (user = MessagesController.getInstance(currentAccount).getUser(message.messageOwner.from_id.user_id)) != null) { + } else if (!isSavedDialog && message != null && message.messageOwner != null && message.messageOwner.from_id instanceof TLRPC.TL_peerUser && (user = MessagesController.getInstance(currentAccount).getUser(message.messageOwner.from_id.user_id)) != null) { return UserObject.getFirstName(user).replace("\n", ""); } else if (message != null && message.messageOwner != null && message.messageOwner.fwd_from != null && message.messageOwner.fwd_from.from_name != null) { return message.messageOwner.fwd_from.from_name; @@ -4786,7 +4849,7 @@ public SpannableStringBuilder getMessageStringFormatted(int messageFormatType, S if (MessageObject.isTopicActionMessage(message)) { stringBuilder = formatInternal(messageFormatType, mess, messageNameString); if (message.topicIconDrawable[0] instanceof ForumBubbleDrawable) { - TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(-message.getDialogId(), MessageObject.getTopicId(message.messageOwner, true)); + TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(-message.getDialogId(), MessageObject.getTopicId(currentAccount, message.messageOwner, true)); if (topic != null) { ((ForumBubbleDrawable) message.topicIconDrawable[0]).setColor(topic.icon_color); } @@ -4957,7 +5020,7 @@ public boolean onTouchEvent(MotionEvent event) { return true; } if (delegate == null || delegate.canClickButtonInside()) { - if (lastTopicMessageUnread && canvasButton != null && buttonLayout != null && canvasButton.checkTouchEvent(event)) { + if (lastTopicMessageUnread && canvasButton != null && buttonLayout != null && dialogsType == DialogsActivity.DIALOGS_TYPE_DEFAULT && canvasButton.checkTouchEvent(event)) { return true; } } @@ -5193,10 +5256,10 @@ private void formatTopicsNames(int currentAccount, MessageObject message, TLRPC. topics = new ArrayList<>(topics); Collections.sort(topics, Comparator.comparingInt(o -> -o.top_message)); SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); - int topMessageTopicId = 0; + long topMessageTopicId = 0; int boldLen = 0; if (message != null) { - topMessageTopicId = MessageObject.getTopicId(message.messageOwner, true); + topMessageTopicId = MessageObject.getTopicId(currentAccount, message.messageOwner, true); TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(chat.id, topMessageTopicId); if (topic != null) { CharSequence topicString = ForumUtilities.getTopicSpannedName(topic, currentMessagePaint, false); @@ -5272,4 +5335,29 @@ private ColorFilter getAdaptiveEmojiColorFilter(int n, int color) { } return adaptiveEmojiColorFilter[n]; } + + private Runnable unsubscribePremiumBlocked; + public void showPremiumBlocked(boolean show) { + if (show != (unsubscribePremiumBlocked != null)) { + if (!show && unsubscribePremiumBlocked != null) { + unsubscribePremiumBlocked.run(); + unsubscribePremiumBlocked = null; + } else if (show) { + unsubscribePremiumBlocked = NotificationCenter.getInstance(currentAccount).listen(this, NotificationCenter.userIsPremiumBlockedUpadted, args -> { + updatePremiumBlocked(true); + }); + } + } + } + + private void updatePremiumBlocked(boolean animated) { + final boolean wasPremiumBlocked = premiumBlocked; + premiumBlocked = (unsubscribePremiumBlocked != null) && user != null && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(user.id); + if (wasPremiumBlocked != premiumBlocked) { + if (!animated) { + premiumBlockedT.set(premiumBlocked, true); + } + invalidate(); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java index 53b5ccfb31..d3736c6177 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupCreateUserCell.java @@ -8,12 +8,18 @@ package org.telegram.ui.Cells; +import static org.telegram.messenger.AndroidUtilities.dp; + 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.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.view.Gravity; import android.view.accessibility.AccessibilityNodeInfo; @@ -33,11 +39,13 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CheckBox2; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Premium.PremiumGradient; public class GroupCreateUserCell extends FrameLayout { @@ -71,6 +79,34 @@ public class GroupCreateUserCell extends FrameLayout { private boolean showSelfAsSaved; Theme.ResourcesProvider resourcesProvider; + private final AnimatedFloat premiumBlockedT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean premiumBlocked; + private boolean showPremiumBlocked; + + public boolean isBlocked() { + return premiumBlocked; + } + + public GroupCreateUserCell showPremiumBlocked() { + if (showPremiumBlocked) return this; + showPremiumBlocked = true; + NotificationCenter.getInstance(currentAccount).listen(this, NotificationCenter.userIsPremiumBlockedUpadted, args -> { + updatePremiumBlocked(true); + }); + return this; + } + + private void updatePremiumBlocked(boolean animated) { + final boolean wasPremiumBlocked = premiumBlocked; + premiumBlocked = showPremiumBlocked && currentObject instanceof TLRPC.User && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(((TLRPC.User) currentObject).id); + if (wasPremiumBlocked != premiumBlocked) { + if (!animated) { + premiumBlockedT.set(premiumBlocked, true); + } + invalidate(); + } + } + public GroupCreateUserCell(Context context, int checkBoxType, int pad, boolean selfAsSaved) { this(context, checkBoxType, pad, selfAsSaved, false, null); } @@ -414,19 +450,26 @@ public void update(int mask) { } } - avatarImageView.setRoundRadius(currentChat != null && currentChat.forum ? AndroidUtilities.dp(14) : AndroidUtilities.dp(24)); if (currentStatus != null) { statusTextView.setText(currentStatus, true); statusTextView.setTag(Theme.key_windowBackgroundWhiteGrayText); statusTextView.setTextColor(Theme.getColor(forceDarkTheme ? Theme.key_voipgroup_lastSeenText : Theme.key_windowBackgroundWhiteGrayText, resourcesProvider)); } + + updatePremiumBlocked(false); } + private PremiumGradient.PremiumGradientTools premiumGradient; + private Drawable lockDrawable; + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); - if (checkBoxType == 2 && (isChecked || checkProgress > 0.0f)) { + float lockT = premiumBlockedT.set(premiumBlocked); + if (lockT > 0) { + + } else if (checkBoxType == 2 && (isChecked || checkProgress > 0.0f)) { paint.setColor(Theme.getColor(Theme.key_checkboxSquareBackground, resourcesProvider)); float cx = avatarImageView.getLeft() + avatarImageView.getMeasuredWidth() / 2; float cy = avatarImageView.getTop() + avatarImageView.getMeasuredHeight() / 2; @@ -444,6 +487,39 @@ protected void onDraw(Canvas canvas) { } } + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + float lockT = premiumBlockedT.set(premiumBlocked); + if (lockT > 0) { + float top = avatarImageView.getY() + avatarImageView.getHeight() / 2f + dp(18); + float left = avatarImageView.getX() + avatarImageView.getWidth() / 2f + dp(18); + + canvas.save(); + Theme.dialogs_onlineCirclePaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + canvas.drawCircle(left, top, dp(10 + 1.33f) * lockT, Theme.dialogs_onlineCirclePaint); + if (premiumGradient == null) { + premiumGradient = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradient1, Theme.key_premiumGradient2, -1, -1, -1, resourcesProvider); + } + premiumGradient.gradientMatrix((int) (left - dp(10)), (int) (top - dp(10)), (int) (left + dp(10)), (int) (top + dp(10)), 0, 0); + canvas.drawCircle(left, top, dp(10) * lockT, premiumGradient.paint); + if (lockDrawable == null) { + lockDrawable = getContext().getResources().getDrawable(R.drawable.msg_mini_lock2).mutate(); + lockDrawable.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + } + lockDrawable.setBounds( + (int) (left - lockDrawable.getIntrinsicWidth() / 2f * .875f * lockT), + (int) (top - lockDrawable.getIntrinsicHeight() / 2f * .875f * lockT), + (int) (left + lockDrawable.getIntrinsicWidth() / 2f * .875f * lockT), + (int) (top + lockDrawable.getIntrinsicHeight() / 2f * .875f * lockT) + ); + lockDrawable.setAlpha((int) (0xFF * lockT)); + lockDrawable.draw(canvas); + canvas.restore(); + } + } + @Override public boolean hasOverlappingRendering() { return false; 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 280caf3af7..f0a54875c8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/HintDialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/HintDialogCell.java @@ -8,9 +8,15 @@ package org.telegram.ui.Cells; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.content.Context; import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.RectF; +import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; @@ -23,16 +29,20 @@ import org.telegram.messenger.Emoji; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; 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.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Premium.PremiumGradient; public class HintDialogCell extends FrameLayout { @@ -54,6 +64,14 @@ public class HintDialogCell extends FrameLayout { CheckBox2 checkBox; private final boolean drawCheckbox; + private final AnimatedFloat premiumBlockedT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean showPremiumBlocked; + private boolean premiumBlocked; + + public boolean isBlocked() { + return premiumBlocked; + } + public HintDialogCell(Context context, boolean drawCheckbox, Theme.ResourcesProvider resourcesProvider) { super(context); this.drawCheckbox = drawCheckbox; @@ -100,6 +118,25 @@ public void setText(CharSequence text, BufferType type) { } } + public void showPremiumBlocked() { + if (showPremiumBlocked) return; + showPremiumBlocked = true; + NotificationCenter.getInstance(currentAccount).listen(this, NotificationCenter.userIsPremiumBlockedUpadted, args -> { + updatePremiumBlocked(true); + }); + } + + private void updatePremiumBlocked(boolean animated) { + final boolean wasPremiumBlocked =premiumBlocked; + premiumBlocked = showPremiumBlocked && currentUser != null && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(currentUser.id); + if (wasPremiumBlocked != premiumBlocked) { + if (!animated) { + premiumBlockedT.set(premiumBlocked, true); + } + invalidate(); + } + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(86), MeasureSpec.EXACTLY)); @@ -138,6 +175,7 @@ public void update() { avatarDrawable.setInfo(currentAccount, chat); currentUser = null; } + updatePremiumBlocked(true); } public void setColors(int textColorKey, int backgroundColorKey) { @@ -176,6 +214,7 @@ public void setDialog(long uid, boolean counter, CharSequence name) { currentUser = null; imageView.setForUserOrChat(chat, avatarDrawable); } + updatePremiumBlocked(false); if (counter) { update(0); } @@ -183,11 +222,14 @@ public void setDialog(long uid, boolean counter, CharSequence name) { private int backgroundColorKey = Theme.key_windowBackgroundWhite; + private PremiumGradient.PremiumGradientTools premiumGradient; + private Drawable lockDrawable; + @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { boolean result = super.drawChild(canvas, child, drawingTime); if (child == imageView) { - boolean showOnline = currentUser != null && !currentUser.bot && (currentUser.status != null && currentUser.status.expires > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || MessagesController.getInstance(currentAccount).onlinePrivacy.containsKey(currentUser.id)); + boolean showOnline = !premiumBlocked && currentUser != null && !currentUser.bot && (currentUser.status != null && currentUser.status.expires > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || MessagesController.getInstance(currentAccount).onlinePrivacy.containsKey(currentUser.id)); if (!wasDraw) { showOnlineProgress = showOnline ? 1f : 0f; } @@ -204,7 +246,34 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { } invalidate(); } - if (showOnlineProgress != 0) { + + final float lockT = premiumBlockedT.set(premiumBlocked); + if (lockT > 0) { + float top = child.getY() + child.getHeight() / 2f + dp(18); + float left = child.getX() + child.getWidth() / 2f + dp(18); + + canvas.save(); + Theme.dialogs_onlineCirclePaint.setColor(Theme.getColor(backgroundColorKey, resourcesProvider)); + canvas.drawCircle(left, top, dp(10 + 1.33f) * lockT, Theme.dialogs_onlineCirclePaint); + if (premiumGradient == null) { + premiumGradient = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradient1, Theme.key_premiumGradient2, -1, -1, -1, resourcesProvider); + } + premiumGradient.gradientMatrix((int) (left - dp(10)), (int) (top - dp(10)), (int) (left + dp(10)), (int) (top + dp(10)), 0, 0); + canvas.drawCircle(left, top, dp(10) * lockT, premiumGradient.paint); + if (lockDrawable == null) { + lockDrawable = getContext().getResources().getDrawable(R.drawable.msg_mini_lock2).mutate(); + lockDrawable.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + } + lockDrawable.setBounds( + (int) (left - lockDrawable.getIntrinsicWidth() / 2f * .875f * lockT), + (int) (top - lockDrawable.getIntrinsicHeight() / 2f * .875f * lockT), + (int) (left + lockDrawable.getIntrinsicWidth() / 2f * .875f * lockT), + (int) (top + lockDrawable.getIntrinsicHeight() / 2f * .875f * lockT) + ); + lockDrawable.setAlpha((int) (0xFF * lockT)); + lockDrawable.draw(canvas); + canvas.restore(); + } else if (showOnlineProgress != 0) { int top = AndroidUtilities.dp(53); int left = AndroidUtilities.dp(59); canvas.save(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java index fe844a1e95..a7d995bc11 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java @@ -8,9 +8,14 @@ package org.telegram.ui.Cells; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.content.Context; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.text.Layout; @@ -40,10 +45,12 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AnimatedEmojiDrawable; +import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.CanvasButton; import org.telegram.ui.Components.CheckBox2; import org.telegram.ui.Components.CombinedDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.Premium.PremiumGradient; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.NotificationsSettingsActivity; @@ -101,6 +108,10 @@ public class ProfileSearchCell extends BaseCell implements NotificationCenter.No private boolean drawCheck; private boolean drawPremium; + private final AnimatedFloat premiumBlockedT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean showPremiumBlocked; + private boolean premiumBlocked; + private int statusLeft; private StaticLayout statusLayout; private AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable statusDrawable; @@ -132,6 +143,11 @@ public ProfileSearchCell(Context context, Theme.ResourcesProvider resourcesProvi statusDrawable.setCallback(this); } + public ProfileSearchCell showPremiumBlock(boolean show) { + showPremiumBlocked = show; + return this; + } + private boolean customPaints; private TextPaint namePaint, statusPaint; public ProfileSearchCell useCustomPaints() { @@ -150,14 +166,17 @@ public void setData(Object object, TLRPC.EncryptedChat ec, CharSequence n, CharS user = (TLRPC.User) object; chat = null; contact = null; + premiumBlocked = showPremiumBlocked && user != null && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(user.id); } else if (object instanceof TLRPC.Chat) { chat = (TLRPC.Chat) object; user = null; contact = null; + premiumBlocked = false; } else if (object instanceof ContactsController.Contact) { contact = (ContactsController.Contact) object; chat = null; user = null; + premiumBlocked = showPremiumBlocked && contact != null && contact.user != null && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(contact.user.id); } encryptedChat = ec; subLabel = s; @@ -235,6 +254,9 @@ protected void onDetachedFromWindow() { super.onDetachedFromWindow(); avatarImage.onDetachedFromWindow(); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); + if (showPremiumBlocked) { + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.userIsPremiumBlockedUpadted); + } statusDrawable.detach(); } @@ -243,6 +265,9 @@ protected void onAttachedToWindow() { super.onAttachedToWindow(); avatarImage.onAttachedToWindow(); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); + if (showPremiumBlocked) { + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.userIsPremiumBlockedUpadted); + } statusDrawable.attach(); } @@ -250,6 +275,12 @@ protected void onAttachedToWindow() { public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.emojiLoaded) { invalidate(); + } else if (id == NotificationCenter.userIsPremiumBlockedUpadted) { + final boolean wasPremiumBlocked = premiumBlocked; + premiumBlocked = showPremiumBlocked && (user != null && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(user.id) || contact != null && contact.user != null && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(contact.user.id)); + if (premiumBlocked != wasPremiumBlocked) { + invalidate(); + } } } @@ -709,6 +740,9 @@ public void update(int mask) { postInvalidate(); } + private PremiumGradient.PremiumGradientTools premiumGradient; + private Drawable lockDrawable; + @Override protected void onDraw(Canvas canvas) { if (user == null && chat == null && encryptedChat == null && contact == null) { @@ -786,6 +820,7 @@ protected void onDraw(Canvas canvas) { actionLayout.draw(canvas); canvas.restore(); } + if (user != null) { StoriesUtilities.drawAvatarWithStory(user.id, canvas, avatarImage, avatarStoryParams); } else if (chat != null) { @@ -794,6 +829,38 @@ protected void onDraw(Canvas canvas) { avatarImage.setImageCoords(avatarStoryParams.originalAvatarRect); avatarImage.draw(canvas); } + + final float lockT = premiumBlockedT.set(premiumBlocked); + if (lockT > 0) { + float top = avatarImage.getCenterY() + dp(14); + float left = avatarImage.getCenterX() + dp(16); + + canvas.save(); + Theme.dialogs_onlineCirclePaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + canvas.drawCircle(left, top, dp(10 + 1.33f) * lockT, Theme.dialogs_onlineCirclePaint); + if (premiumGradient == null) { + premiumGradient = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradient1, Theme.key_premiumGradient2, -1, -1, -1, resourcesProvider); + } + premiumGradient.gradientMatrix((int) (left - dp(10)), (int) (top - dp(10)), (int) (left + dp(10)), (int) (top + dp(10)), 0, 0); + canvas.drawCircle(left, top, dp(10) * lockT, premiumGradient.paint); + if (lockDrawable == null) { + lockDrawable = getContext().getResources().getDrawable(R.drawable.msg_mini_lock2).mutate(); + lockDrawable.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + } + lockDrawable.setBounds( + (int) (left - lockDrawable.getIntrinsicWidth() / 2f * .875f * lockT), + (int) (top - lockDrawable.getIntrinsicHeight() / 2f * .875f * lockT), + (int) (left + lockDrawable.getIntrinsicWidth() / 2f * .875f * lockT), + (int) (top + lockDrawable.getIntrinsicHeight() / 2f * .875f * lockT) + ); + lockDrawable.setAlpha((int) (0xFF * lockT)); + lockDrawable.draw(canvas); + canvas.restore(); + } + } + + public boolean isBlocked() { + return premiumBlocked; } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioCell.java index f99c37a3ff..70f5b0c5cd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/RadioCell.java @@ -12,6 +12,7 @@ import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; +import android.graphics.drawable.Drawable; import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; @@ -75,6 +76,10 @@ public RadioCell(Context context, boolean dialog, int padding, Theme.ResourcesPr addView(radioButton, LayoutHelper.createFrame(22, 22, (LocaleController.isRTL ? Gravity.LEFT : Gravity.RIGHT) | Gravity.TOP, (LocaleController.isRTL ? padding + 1 : 0), 14, (LocaleController.isRTL ? 0 : padding + 1), 0)); } + public void setRadioIcon(Drawable icon) { + radioButton.setIcon(icon); + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.dp(50) + (needDivider ? 1 : 0)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareDialogCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareDialogCell.java index f7dd2b0224..d0dbf4a52e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareDialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ShareDialogCell.java @@ -10,6 +10,7 @@ import static org.telegram.messenger.AndroidUtilities.dp; +import android.app.Notification; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; @@ -51,14 +52,17 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CheckBox2; +import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.Forum.ForumUtilities; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Premium.PremiumGradient; import org.telegram.ui.Components.RLottieDrawable; -public class ShareDialogCell extends FrameLayout { +public class ShareDialogCell extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { private final BackupImageView imageView; private final TextView nameTextView; @@ -88,6 +92,13 @@ public void invalidateSelf() { public static final int TYPE_CALL = 1; public static final int TYPE_CREATE = 2; + private final AnimatedFloat premiumBlockedT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean premiumBlocked; + + public boolean isBlocked() { + return premiumBlocked; + } + public BackupImageView getImageView() { return imageView; } @@ -115,7 +126,7 @@ public void setText(CharSequence text, BufferType type) { } }; NotificationCenter.listenEmojiLoading(nameTextView); - nameTextView.setTextColor(getThemedColor(type == TYPE_CALL ? Theme.key_voipgroup_nameText : Theme.key_dialogTextBlack)); + nameTextView.setTextColor(getThemedColor(premiumBlocked ? Theme.key_windowBackgroundWhiteGrayText5 : currentType == TYPE_CALL ? Theme.key_voipgroup_nameText : Theme.key_dialogTextBlack)); nameTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); nameTextView.setMaxLines(2); nameTextView.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL); @@ -146,6 +157,30 @@ public void setText(CharSequence text, BufferType type) { setBackground(Theme.createRadSelectorDrawable(Theme.getColor(Theme.key_listSelector, resourcesProvider), dp(2), dp(2))); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.userIsPremiumBlockedUpadted); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.userIsPremiumBlockedUpadted); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.userIsPremiumBlockedUpadted) { + final boolean wasPremiumBlocked = premiumBlocked; + premiumBlocked = user != null && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(user.id); + nameTextView.setTextColor(getThemedColor(premiumBlocked ? Theme.key_windowBackgroundWhiteGrayText5 : currentType == TYPE_CALL ? Theme.key_voipgroup_nameText : Theme.key_dialogTextBlack)); + if (premiumBlocked != wasPremiumBlocked) { + invalidate(); + } + } + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(dp(currentType == TYPE_CREATE ? 95 : 103), MeasureSpec.EXACTLY)); @@ -164,6 +199,10 @@ public void setDialog(long uid, boolean checked, CharSequence name) { imageView.setImage(null, null, repostStoryDrawable, null); } else if (DialogObject.isUserDialog(uid)) { user = MessagesController.getInstance(currentAccount).getUser(uid); + premiumBlocked = MessagesController.getInstance(currentAccount).isUserPremiumBlocked(uid); + nameTextView.setTextColor(getThemedColor(premiumBlocked ? Theme.key_windowBackgroundWhiteGrayText5 : currentType == TYPE_CALL ? Theme.key_voipgroup_nameText : Theme.key_dialogTextBlack)); + premiumBlockedT.set(premiumBlocked, true); + invalidate(); avatarDrawable.setInfo(currentAccount, user); if (currentType != TYPE_CREATE && UserObject.isReplyUser(user)) { nameTextView.setText(LocaleController.getString("RepliesTitle", R.string.RepliesTitle)); @@ -186,6 +225,8 @@ public void setDialog(long uid, boolean checked, CharSequence name) { imageView.setRoundRadius(dp(28)); } else { user = null; + premiumBlocked = false; + premiumBlockedT.set(0, true); TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-uid); if (name != null) { nameTextView.setText(name); @@ -263,6 +304,9 @@ public void setTopic(TLRPC.TL_forumTopic topic, boolean animate) { } } + private PremiumGradient.PremiumGradientTools premiumGradient; + private Drawable lockDrawable; + @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { boolean result = super.drawChild(canvas, child, drawingTime); @@ -275,31 +319,59 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { } lastUpdateTime = newTime; - boolean isOnline = !user.self && !user.bot && (user.status != null && user.status.expires > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || MessagesController.getInstance(currentAccount).onlinePrivacy.containsKey(user.id)); - if (isOnline || onlineProgress != 0) { - int top = imageView.getBottom() - dp(6); - int left = imageView.getRight() - dp(10); + final float lockT = premiumBlockedT.set(premiumBlocked); + if (lockT > 0) { + int top = imageView.getBottom() - dp(9); + int left = imageView.getRight() - dp(9.33f); + + canvas.save(); Theme.dialogs_onlineCirclePaint.setColor(getThemedColor(currentType == TYPE_CALL ? Theme.key_voipgroup_inviteMembersBackground : Theme.key_windowBackgroundWhite)); - canvas.drawCircle(left, top, dp(7) * onlineProgress, Theme.dialogs_onlineCirclePaint); - Theme.dialogs_onlineCirclePaint.setColor(getThemedColor(Theme.key_chats_onlineCircle)); - canvas.drawCircle(left, top, dp(5) * onlineProgress, Theme.dialogs_onlineCirclePaint); - if (isOnline) { - if (onlineProgress < 1.0f) { - onlineProgress += dt / 150.0f; - if (onlineProgress > 1.0f) { - onlineProgress = 1.0f; + canvas.drawCircle(left, top, dp(12) * lockT, Theme.dialogs_onlineCirclePaint); + if (premiumGradient == null) { + premiumGradient = new PremiumGradient.PremiumGradientTools(Theme.key_premiumGradient1, Theme.key_premiumGradient2, -1, -1, -1, resourcesProvider); + } + premiumGradient.gradientMatrix(left - dp(10), top - dp(10), left + dp(10), top + dp(10), 0, 0); + canvas.drawCircle(left, top, dp(10) * lockT, premiumGradient.paint); + if (lockDrawable == null) { + lockDrawable = getContext().getResources().getDrawable(R.drawable.msg_mini_lock2).mutate(); + lockDrawable.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + } + lockDrawable.setBounds( + (int) (left - lockDrawable.getIntrinsicWidth() / 2f * .875f * lockT), + (int) (top - lockDrawable.getIntrinsicHeight() / 2f * .875f * lockT), + (int) (left + lockDrawable.getIntrinsicWidth() / 2f * .875f * lockT), + (int) (top + lockDrawable.getIntrinsicHeight() / 2f * .875f * lockT) + ); + lockDrawable.setAlpha((int) (0xFF * lockT)); + lockDrawable.draw(canvas); + canvas.restore(); + } else { + boolean isOnline = !premiumBlocked && !user.self && !user.bot && (user.status != null && user.status.expires > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || MessagesController.getInstance(currentAccount).onlinePrivacy.containsKey(user.id)); + if (isOnline || onlineProgress != 0) { + int top = imageView.getBottom() - dp(6); + int left = imageView.getRight() - dp(10); + Theme.dialogs_onlineCirclePaint.setColor(getThemedColor(currentType == TYPE_CALL ? Theme.key_voipgroup_inviteMembersBackground : Theme.key_windowBackgroundWhite)); + canvas.drawCircle(left, top, dp(7) * onlineProgress, Theme.dialogs_onlineCirclePaint); + Theme.dialogs_onlineCirclePaint.setColor(getThemedColor(Theme.key_chats_onlineCircle)); + canvas.drawCircle(left, top, dp(5) * onlineProgress, Theme.dialogs_onlineCirclePaint); + if (isOnline) { + if (onlineProgress < 1.0f) { + onlineProgress += dt / 150.0f; + if (onlineProgress > 1.0f) { + onlineProgress = 1.0f; + } + imageView.invalidate(); + invalidate(); } - imageView.invalidate(); - invalidate(); - } - } else { - if (onlineProgress > 0.0f) { - onlineProgress -= dt / 150.0f; - if (onlineProgress < 0.0f) { - onlineProgress = 0.0f; + } else { + if (onlineProgress > 0.0f) { + onlineProgress -= dt / 150.0f; + if (onlineProgress < 0.0f) { + onlineProgress = 0.0f; + } + imageView.invalidate(); + invalidate(); } - imageView.invalidate(); - invalidate(); } } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 5f02c4186a..c33fffe3c1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -189,6 +189,7 @@ import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; +import org.telegram.ui.Adapters.FiltersView; import org.telegram.ui.Adapters.MessagesSearchAdapter; import org.telegram.ui.Cells.BotHelpCell; import org.telegram.ui.Cells.BotSwitchCell; @@ -208,7 +209,6 @@ import org.telegram.ui.Components.FloatingDebug.FloatingDebugProvider; import org.telegram.ui.Components.Forum.ForumUtilities; import org.telegram.ui.Components.Premium.GiftPremiumBottomSheet; -import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; import org.telegram.ui.Components.Premium.PremiumPreviewBottomSheet; import org.telegram.ui.Components.Premium.boosts.BoostDialogs; @@ -221,9 +221,9 @@ import org.telegram.ui.Components.voip.CellFlickerDrawable; import org.telegram.ui.Components.voip.VoIPHelper; import org.telegram.ui.Delegates.ChatActivityMemberRequestsDelegate; -import org.telegram.ui.Stories.DialogStoriesCell; import org.telegram.ui.Stories.StoriesListPlaceProvider; import org.telegram.ui.Stories.StoriesUtilities; +import org.telegram.ui.Stories.recorder.HintView2; import org.telegram.ui.Stories.recorder.PreviewView; import org.telegram.ui.Stories.recorder.StoryEntry; import org.telegram.ui.Stories.recorder.StoryRecorder; @@ -311,6 +311,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private GridLayoutManagerFixed chatLayoutManager; private ChatActivityAdapter chatAdapter; private UnreadCounterTextView bottomOverlayChatText; + private boolean bottomOverlayLinks; + private LinkSpanDrawable.LinksTextView bottomOverlayLinksText; + private TextView bottomOverlayText; private TextView bottomOverlayStartButton; private ImageView bottomOverlayImage; private RadialProgressView bottomOverlayProgress; @@ -329,7 +332,6 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private ChatBigEmptyView bigEmptyView; private ArrayList actionModeViews = new ArrayList<>(); private ChatAvatarContainer avatarContainer; - private TextView bottomOverlayText; private NumberTextView selectedMessagesCountTextView; private RecyclerListView.OnItemClickListener mentionsOnItemClickListener; private SuggestEmojiView suggestEmojiPanel; @@ -351,6 +353,9 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private ImageView mentiondownButtonImage; private Bulletin messageSeenPrivacyBulletin; TextView webBotTitle; + public SearchTagsList actionBarSearchTags; + + private HintView2 savedMessagesHint; private int reactionsMentionCount; private FrameLayout reactionsMentiondownButton; @@ -444,6 +449,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private ImageView searchUpButton; private ImageView searchDownButton; private SearchCounterView searchCountText; + private AnimatedTextView searchOtherButton; private ChatActionCell floatingDateView; private ChatActionCell infoTopView; private int hideDateDelay = 500; @@ -458,6 +464,7 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not private boolean searchingForUser; private TLRPC.User searchingUserMessages; private TLRPC.Chat searchingChatMessages; + private ReactionsLayoutInBubble.VisibleReaction searchingReaction; private UndoView undoView; private UndoView topUndoView; private Bulletin pinBulletin; @@ -477,19 +484,21 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not public static final int MODE_DEFAULT = 0; public static final int MODE_SCHEDULED = 1; public static final int MODE_PINNED = 2; + public static final int MODE_SAVED = 3; private int chatMode; private int scheduledMessagesCount = -1; private int reportType = -1; + @Nullable private MessageObject threadMessageObject; private MessageObject topicStarterMessageObject; private boolean threadMessageVisible = true; private ArrayList threadMessageObjects; private MessageObject replyMessageHeaderObject; private TLRPC.TL_forumTopic forumTopic; - private int threadMessageId; + private long threadMessageId; private int replyOriginalMessageId; private TLRPC.Chat replyOriginalChat; private boolean isComments; @@ -1064,8 +1073,12 @@ public void showHeaderItem(boolean show) { } - public int getTopicId() { - return isTopic ? threadMessageId : 0; + public long getTopicId() { + return isTopic ? threadMessageId : 0L; + } + + public long getSavedDialogId() { + return chatMode == MODE_SAVED ? threadMessageId : 0L; } public boolean isForumInViewAsMessagesMode() { @@ -1323,6 +1336,9 @@ public RecyclerListView getChatListView() { } private void startMultiselect(int position) { + if (chatMode == MODE_SAVED) { + return; + } int indexOfMessage = position - chatAdapter.messagesStartRow; if (indexOfMessage < 0 || indexOfMessage >= messages.size()) { return; @@ -1429,10 +1445,13 @@ public void onItemClick(View view, int position, float x, float y) { } wasManualScroll = true; if (view instanceof ChatActionCell && ((ChatActionCell) view).getMessageObject().isDateObject) { + if (isInsideContainer) { + return; + } Bundle bundle = new Bundle(); int date = ((ChatActionCell) view).getMessageObject().messageOwner.date; bundle.putLong("dialog_id", dialog_id); - bundle.putInt("topic_id", getTopicId()); + bundle.putLong("topic_id", getTopicId()); bundle.putInt("type", CalendarActivity.TYPE_CHAT_ACTIVITY); CalendarActivity calendarActivity = new CalendarActivity(bundle, SharedMediaLayout.FILTER_PHOTOS_AND_VIDEOS, date); presentFragment(calendarActivity); @@ -1598,7 +1617,7 @@ public void onMessageSend(CharSequence message, boolean notify, int scheduleDate } } if (ChatObject.isForum(currentChat) && !isTopic && replyingMessageObject != null) { - int topicId = replyingMessageObject.replyToForumTopic != null ? replyingMessageObject.replyToForumTopic.id : MessageObject.getTopicId(replyingMessageObject.messageOwner, true); + long topicId = replyingMessageObject.replyToForumTopic != null ? replyingMessageObject.replyToForumTopic.id : MessageObject.getTopicId(currentAccount, replyingMessageObject.messageOwner, true); if (topicId != 0) { getMediaDataController().cleanDraft(dialog_id, topicId, false); } @@ -1675,7 +1694,7 @@ public void onTextSelectionChanged(int start, int end) { editTextItem.setTag(1); if (editTextItem.getVisibility() != View.VISIBLE) { - if (chatMode == 0 && (threadMessageId == 0 || isTopic) && !UserObject.isReplyUser(currentUser) && reportType < 0) { + if (chatMode == MODE_SAVED && getSavedDialogId() == getUserConfig().getClientUserId() || chatMode == 0 && (threadMessageId == 0 || isTopic) && !UserObject.isReplyUser(currentUser) && reportType < 0) { editTextItem.setVisibility(View.VISIBLE); checkEditTextItemMenu(); headerItem.setVisibility(View.GONE); @@ -1709,7 +1728,7 @@ public void onAnimationEnd(Animator animation) { if (editTextItem.getTag() != null) { editTextItem.setTag(null); if (editTextItem.getVisibility() != View.GONE) { - if (chatMode == 0 && (threadMessageId == 0 || isTopic) && !UserObject.isReplyUser(currentUser) && reportType < 0) { + if (chatMode == MODE_SAVED && getSavedDialogId() == getUserConfig().getClientUserId() || chatMode == 0 && (threadMessageId == 0 || isTopic) && !UserObject.isReplyUser(currentUser) && reportType < 0) { editTextItem.setVisibility(View.GONE); if (chatActivityEnterView.hasText() && TextUtils.isEmpty(chatActivityEnterView.getSlowModeTimer())) { @@ -1904,12 +1923,19 @@ public void didPressAttachButton() { openAttachMenu(); } + @Override + public void toggleVideoRecordingPause() { + if (instantCameraView != null) { + instantCameraView.togglePause(); + } + } + @Override public void needStartRecordVideo(int state, boolean notify, int scheduleDate, int ttl) { checkInstantCameraView(); if (instantCameraView != null) { if (state == 0) { - instantCameraView.showCamera(); + instantCameraView.showCamera(false); chatListView.stopScroll(); chatAdapter.updateRowsSafe(); } else if (state == 1 || state == 3 || state == 4) { @@ -2102,6 +2128,9 @@ public ReplyQuote getReplyQuote() { scheduledHintShown = true; }; + public boolean isInsideContainer; + public boolean reversed; + public ChatActivity(Bundle args) { super(args); } @@ -2356,6 +2385,10 @@ public boolean onFragmentCreate() { getNotificationCenter().addObserver(this, NotificationCenter.storiesUpdated); getNotificationCenter().addObserver(this, NotificationCenter.channelRecommendationsLoaded); getNotificationCenter().addObserver(this, NotificationCenter.updateTranscriptionLock); + getNotificationCenter().addObserver(this, NotificationCenter.savedMessagesDialogsUpdate); + if (actionBarSearchTags != null) { + actionBarSearchTags.attach(); + } super.onFragmentCreate(); @@ -2514,7 +2547,7 @@ public boolean onFragmentCreate() { if (isTopic) { getMessagesController().getTopicsController().getTopicRepliesCount(dialog_id, getTopicId()); } - + getMessagesController().getSavedMessagesController().preloadDialogs(); return true; } @@ -2530,9 +2563,9 @@ private void firstLoadMessages() { startLoadFromMessageIdSaved = startLoadFromMessageId; if (migrated_to != 0) { mergeDialogId = migrated_to; - getMessagesController().loadMessages(mergeDialogId, 0, loadInfo, initialMessagesSize, startLoadFromMessageId, 0, true, 0, classGuid, 3, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); + getMessagesController().loadMessages(mergeDialogId, 0, loadInfo, initialMessagesSize, startLoadFromMessageId, 0, true, 0, classGuid, MessagesController.LOAD_AROUND_MESSAGE, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); } else { - getMessagesController().loadMessages(dialog_id, mergeDialogId, loadInfo, initialMessagesSize, startLoadFromMessageId, 0, true, 0, classGuid, 3, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); + getMessagesController().loadMessages(dialog_id, mergeDialogId, loadInfo, initialMessagesSize, startLoadFromMessageId, 0, true, 0, classGuid, MessagesController.LOAD_AROUND_MESSAGE, 0, chatMode, threadMessageId, replyMaxReadId, lastLoadIndex++, isTopic); } } else { if (historyPreloaded) { @@ -2725,6 +2758,10 @@ public void onFragmentDestroy() { getNotificationCenter().removeObserver(this, NotificationCenter.storiesUpdated); getNotificationCenter().removeObserver(this, NotificationCenter.channelRecommendationsLoaded); getNotificationCenter().removeObserver(this, NotificationCenter.updateTranscriptionLock); + getNotificationCenter().removeObserver(this, NotificationCenter.savedMessagesDialogsUpdate); + if (actionBarSearchTags != null) { + actionBarSearchTags.detach(); + } if (currentEncryptedChat != null) { getNotificationCenter().removeObserver(this, NotificationCenter.didVerifyMessagesStickers); } @@ -2888,6 +2925,9 @@ public View createView(Context context) { actionBar.setTitleColor(getThemedColor(Theme.key_actionBarActionModeDefaultIcon)); actionBar.setSubtitleColor(getThemedColor(Theme.key_actionBarActionModeDefaultIcon)); } + if (isInsideContainer) { + actionBar.setVisibility(View.GONE); + } actionBarBackgroundPaint.setColor(getThemedColor(Theme.key_actionBarDefault)); sharedResources = new ChatMessageSharedResources(context); @@ -3031,6 +3071,39 @@ public void onItemClick(final int id) { if (getParentActivity() == null) { return; } + if (chatMode == MODE_SAVED) { + String dialogName = ""; + long dialogId = getSavedDialogId(); + TLRPC.User user = null; + TLRPC.Chat chat = null; + if (dialogId >= 0) { + user = getMessagesController().getUser(dialogId); + } else { + chat = getMessagesController().getChat(-dialogId); + } + if (UserObject.isAnonymous(user)) { + dialogName = LocaleController.getString(R.string.AnonymousForward); + } else if (chat != null) { + dialogName = chat.title; + } else if (user != null) { + dialogName = UserObject.getUserName(user); + } + AlertDialog dialog = new AlertDialog.Builder(getContext(), getResourceProvider()) + .setTitle(LocaleController.formatString(R.string.ClearHistoryTitleSingle, dialogName)) + .setMessage(LocaleController.formatString(R.string.ClearHistoryMessageSingle, dialogName)) + .setPositiveButton(LocaleController.getString(R.string.Delete), (di, w) -> { + getMessagesController().deleteSavedDialog(getSavedDialogId()); + finishFragment(); + }) + .setNegativeButton(LocaleController.getString(R.string.Cancel), null) + .create(); + showDialog(dialog); + TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); + if (button != null) { + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + } + return; + } boolean canDeleteHistory = chatInfo != null && chatInfo.can_delete_channel; if (id == auto_delete_timer || id == clear_history && currentEncryptedChat == null && ((currentUser != null && !UserObject.isUserSelf(currentUser) && !UserObject.isDeleted(currentUser)) || (chatInfo != null && chatInfo.can_delete_channel))) { AlertsCreator.createClearDaysDialogAlert(ChatActivity.this, -1, currentUser, currentChat, canDeleteHistory, new MessagesStorage.BooleanCallback() { @@ -3248,14 +3321,19 @@ public void onLongPress() { if (avatarContainer != null) { avatarContainer.onDestroy(); } - avatarContainer = new ChatAvatarContainer(context, this, currentEncryptedChat != null, themeDelegate); + avatarContainer = new ChatAvatarContainer(context, this, currentEncryptedChat != null, themeDelegate) { + @Override + protected boolean useAnimatedSubtitle() { + return chatMode == MODE_SAVED; + } + }; avatarContainer.allowShorterStatus = true; avatarContainer.premiumIconHiddable = true; avatarContainer.allowDrawStories = dialog_id < 0; avatarContainer.setClipChildren(false); AndroidUtilities.updateViewVisibilityAnimated(avatarContainer, true, 1f, false); updateTopicTitleIcon(); - if (inPreviewMode || inBubbleMode) { + if (inPreviewMode || inBubbleMode || isInsideContainer) { avatarContainer.setOccupyStatusBar(false); } if (reportType >= 0) { @@ -3328,14 +3406,14 @@ public void onLongPress() { ActionBarMenu menu = actionBar.createMenu(); - if (currentEncryptedChat == null && chatMode == 0 && reportType < 0) { + if (currentEncryptedChat == null && (chatMode == 0 || chatMode == MODE_SAVED) && reportType < 0) { searchIconItem = menu.addItem(search, R.drawable.ic_ab_search); searchIconItem.setContentDescription(LocaleController.getString("Search", R.string.Search)); searchItem = menu.addItem(chat_menu_search, R.drawable.ic_ab_search, themeDelegate); searchItem.setIsSearchField(true); searchItem.setActionBarMenuItemSearchListener(getSearchItemListener()); searchItem.setSearchFieldHint(LocaleController.getString("Search", R.string.Search)); - if (threadMessageId == 0 && !UserObject.isReplyUser(currentUser) || threadMessageObject != null && threadMessageObject.getRepliesCount() < 10) { + if (chatMode == MODE_SAVED || threadMessageId == 0 && !UserObject.isReplyUser(currentUser) || threadMessageObject != null && threadMessageObject.getRepliesCount() < 10) { searchItem.setVisibility(View.GONE); } else { searchItem.setVisibility(View.VISIBLE); @@ -3367,7 +3445,7 @@ public void onLongPress() { editTextItem.setTag(null); editTextItem.setVisibility(View.GONE); - if (chatMode == 0 && (threadMessageId == 0 || isTopic) && !UserObject.isReplyUser(currentUser) && reportType < 0) { + if (chatMode == MODE_SAVED || (chatMode == 0 && (threadMessageId == 0 || isTopic)) && !UserObject.isReplyUser(currentUser) && reportType < 0) { TLRPC.UserFull userFull = null; if (currentUser != null) { userFull = getMessagesController().getUserFull(currentUser.id); @@ -3375,7 +3453,7 @@ public void onLongPress() { headerItem = menu.addItem(chat_menu_options, R.drawable.ic_ab_other, themeDelegate); headerItem.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); - if (currentUser == null || !currentUser.self) { + if (chatMode != MODE_SAVED && (currentUser == null || !currentUser.self)) { chatNotificationsPopupWrapper = new ChatNotificationsPopupWrapper(context, currentAccount, headerItem.getPopupLayout().getSwipeBack(), false, false, new ChatNotificationsPopupWrapper.Callback() { @Override public void dismiss() { @@ -3412,14 +3490,14 @@ public void muteFor(int timeInSeconds) { @Override public void showCustomize() { - if (dialog_id != 0) { + if (dialog_id != 0 && chatMode != MODE_SAVED) { if (currentUser != null) { getMessagesController().putUser(currentUser, true); } Bundle args = new Bundle(); args.putLong("dialog_id", dialog_id); if (getTopicId() != 0) { - args.putInt("topic_id", getTopicId()); + args.putLong("topic_id", getTopicId()); } presentFragment(new ProfileNotificationsActivity(args, themeDelegate)); } @@ -3449,7 +3527,7 @@ public void toggleMute() { }); muteItemGap = headerItem.lazilyAddColoredGap(); } - if (currentUser != null) { + if (currentUser != null && chatMode != MODE_SAVED) { headerItem.lazilyAddSubItem(call, R.drawable.msg_callback, LocaleController.getString("Call", R.string.Call)); if (Build.VERSION.SDK_INT >= 18) { headerItem.lazilyAddSubItem(video_call, R.drawable.msg_videocall, LocaleController.getString("VideoCall", R.string.VideoCall)); @@ -3468,30 +3546,36 @@ public void toggleMute() { } if (searchItem != null) { - headerItem.lazilyAddSubItem(search, R.drawable.msg_search, LocaleController.getString("Search", R.string.Search)); - } - translateItem = headerItem.lazilyAddSubItem(translate, R.drawable.msg_translate, LocaleController.getString("TranslateMessage", R.string.TranslateMessage)); - updateTranslateItemVisibility(); - if (currentChat != null && !currentChat.creator && !ChatObject.hasAdminRights(currentChat)) { - headerItem.lazilyAddSubItem(report, R.drawable.msg_report, LocaleController.getString("ReportChat", R.string.ReportChat)); - } - if (currentUser != null) { - addContactItem = headerItem.lazilyAddSubItem(share_contact, R.drawable.msg_addcontact, ""); - } - if (currentEncryptedChat != null) { - timeItem2 = headerItem.lazilyAddSubItem(chat_enc_timer, R.drawable.msg_autodelete, LocaleController.getString("SetTimer", R.string.SetTimer)); + headerItem.lazilyAddSubItem(search, R.drawable.msg_search, LocaleController.getString(R.string.Search)); } - if (currentChat != null && !isTopic) { - viewAsTopics = headerItem.lazilyAddSubItem(view_as_topics, R.drawable.msg_topics, LocaleController.getString("TopicViewAsTopics", R.string.TopicViewAsTopics)); - } - if (themeDelegate.isThemeChangeAvailable(true)) { - headerItem.lazilyAddSubItem(change_colors, R.drawable.msg_colors, LocaleController.getString("SetWallpapers", R.string.SetWallpapers)); - } - if (!isTopic) { - clearHistoryItem = headerItem.lazilyAddSubItem(clear_history, R.drawable.msg_clear, LocaleController.getString("ClearHistory", R.string.ClearHistory)); + if (chatMode != MODE_SAVED) { + translateItem = headerItem.lazilyAddSubItem(translate, R.drawable.msg_translate, LocaleController.getString("TranslateMessage", R.string.TranslateMessage)); + updateTranslateItemVisibility(); + if (currentChat != null && !currentChat.creator && !ChatObject.hasAdminRights(currentChat)) { + headerItem.lazilyAddSubItem(report, R.drawable.msg_report, LocaleController.getString("ReportChat", R.string.ReportChat)); + } + if (currentUser != null) { + addContactItem = headerItem.lazilyAddSubItem(share_contact, R.drawable.msg_addcontact, ""); + } + if (currentEncryptedChat != null) { + timeItem2 = headerItem.lazilyAddSubItem(chat_enc_timer, R.drawable.msg_autodelete, LocaleController.getString("SetTimer", R.string.SetTimer)); + } + if (currentChat != null && !isTopic) { + viewAsTopics = headerItem.lazilyAddSubItem(view_as_topics, R.drawable.msg_topics, LocaleController.getString("TopicViewAsTopics", R.string.TopicViewAsTopics)); + } + if (themeDelegate.isThemeChangeAvailable(true)) { + headerItem.lazilyAddSubItem(change_colors, R.drawable.msg_colors, LocaleController.getString("SetWallpapers", R.string.SetWallpapers)); + } + if (!isTopic) { + clearHistoryItem = headerItem.lazilyAddSubItem(clear_history, R.drawable.msg_clear, LocaleController.getString("ClearHistory", R.string.ClearHistory)); + } } boolean addedSettings = false; - if (!isTopic) { + if (chatMode == MODE_SAVED) { + if (threadMessageId != getUserConfig().getClientUserId()) { + headerItem.lazilyAddSubItem(delete_chat, R.drawable.msg_delete, LocaleController.getString("DeleteChatUser", R.string.DeleteChatUser)); + } + } else if (!isTopic) { if (ChatObject.isChannel(currentChat) && !currentChat.creator) { if (!ChatObject.isNotInChat(currentChat)) { if (currentChat.megagroup) { @@ -3512,15 +3596,17 @@ public void toggleMute() { } } } - if (currentUser != null && currentUser.self) { - headerItem.lazilyAddSubItem(add_shortcut, R.drawable.msg_home, LocaleController.getString("AddShortcut", R.string.AddShortcut)); - } - if (currentUser != null && currentEncryptedChat == null && currentUser.bot) { - if (!addedSettings) { - headerItem.lazilyAddSubItem(bot_settings, R.drawable.msg_settings_old, LocaleController.getString("BotSettings", R.string.BotSettings)); + if (chatMode != MODE_SAVED) { + if (currentUser != null && currentUser.self) { + headerItem.lazilyAddSubItem(add_shortcut, R.drawable.msg_home, LocaleController.getString("AddShortcut", R.string.AddShortcut)); + } + if (currentUser != null && currentEncryptedChat == null && currentUser.bot) { + if (!addedSettings) { + headerItem.lazilyAddSubItem(bot_settings, R.drawable.msg_settings_old, LocaleController.getString("BotSettings", R.string.BotSettings)); + } + headerItem.lazilyAddSubItem(bot_help, R.drawable.msg_help, LocaleController.getString("BotHelp", R.string.BotHelp)); + updateBotButtons(); } - headerItem.lazilyAddSubItem(bot_help, R.drawable.msg_help, LocaleController.getString("BotHelp", R.string.BotHelp)); - updateBotButtons(); } } if (ChatObject.isForum(currentChat) && isTopic && getParentLayout() != null && getParentLayout().getFragmentStack() != null && chatMode == MODE_DEFAULT) { @@ -3574,7 +3660,7 @@ public void toggleMute() { fragmentView = contentView = new ChatActivityFragmentView(context, parentLayout); contentView.needBlur = true; contentView.needBlurBottom = true; - if (inBubbleMode) { + if (inBubbleMode || isInsideContainer) { contentView.setOccupyStatusBar(false); } @@ -3598,7 +3684,6 @@ public void toggleMute() { } chatListView = new RecyclerListViewInternal(context, themeDelegate) { - private int lastWidth; private final ArrayList drawTimeAfter = new ArrayList<>(); @@ -3687,7 +3772,7 @@ public void setTranslationY(float translationY) { @Override protected boolean allowSelectChildAtPosition(View child) { - if (child != null && child.getVisibility() == View.INVISIBLE) return false; + if (child != null && (child.getVisibility() == View.INVISIBLE || child.getVisibility() == View.GONE)) return false; return super.allowSelectChildAtPosition(child); } @@ -4182,7 +4267,7 @@ public void onDraw(Canvas c) { drawReplyButton(c); } - if (pullingDownOffset != 0 && !isInPreviewMode()) { + if (pullingDownOffset != 0 && !isInPreviewMode() && !isInsideContainer && chatMode != MODE_SAVED) { c.save(); float transitionOffset = 0; if (pullingDownAnimateProgress != 0) { @@ -4450,7 +4535,7 @@ protected void dispatchDraw(Canvas canvas) { invalidated = false; canvas.save(); - if (fragmentTransition == null || (fromPullingDownTransition && !toPullingDownTransition)) { + if ((fragmentTransition == null || (fromPullingDownTransition && !toPullingDownTransition)) && !isInsideContainer) { canvas.clipRect(0, chatListViewPaddingTop - chatListViewPaddingVisibleOffset - AndroidUtilities.dp(4), getMeasuredWidth(), getMeasuredHeight() - blurredViewBottomOffset); } selectorRect.setEmpty(); @@ -4815,7 +4900,6 @@ public boolean drawChild(Canvas canvas, View child, long drawingTime) { ChatMessageCell cell; ChatActionCell actionCell = null; float cilpTop = chatListViewPaddingTop - chatListViewPaddingVisibleOffset - AndroidUtilities.dp(4); - if (child.getY() > getMeasuredHeight() || child.getY() + child.getMeasuredHeight() < cilpTop || child.getVisibility() == View.INVISIBLE || child.getVisibility() == View.GONE) { skipDraw = true; } @@ -5291,7 +5375,7 @@ public void endAnimations() { chatListItemAnimator.setOnSnapMessage(this::supportsThanosEffect, this::getChatThanosEffect); } - chatLayoutManager = new GridLayoutManagerFixed(context, 1000, LinearLayoutManager.VERTICAL, true) { + chatLayoutManager = new GridLayoutManagerFixed(context, 1000, LinearLayoutManager.VERTICAL, !reversed) { boolean computingScroll; @@ -5440,7 +5524,7 @@ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerVi for (int i = 0; i < n; i++) { View child = chatListView.getChildAt(i); float padding = chatListViewPaddingTop; - if (chatListView.getChildAdapterPosition(child) == chatAdapter.getItemCount() - 1) { + if (chatListView.getChildAdapterPosition(child) == (reversed ? 0 : chatAdapter.getItemCount() - 1)) { int dyLocal = dy; if (child.getTop() - dy > padding) { dyLocal = (int) (child.getTop() - padding); @@ -5453,7 +5537,7 @@ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerVi 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() && reportType < 0) { + if (dy > 0 && scrolled == 0 && ChatObject.isChannel(currentChat) && chatMode != MODE_SAVED && !currentChat.megagroup && chatListView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING && !chatListView.isFastScrollAnimationRunning() && !chatListView.isMultiselect() && reportType < 0) { if (pullingDownOffset == 0 && pullingDownDrawable != null) { pullingDownDrawable.updateDialog(); } @@ -5837,7 +5921,7 @@ private void loadLastUnreadMention() { req.peer = getMessagesController().getInputPeer(dialog_id); req.limit = 1; if (isTopic) { - req.top_msg_id = threadMessageId; + req.top_msg_id = (int) threadMessageId; req.flags |= 1; } req.add_offset = newMentionsCount - 1; @@ -6352,19 +6436,21 @@ public void getOutline(View view, Outline outline) { reactionsMentiondownButton.addView(reactionsMentiondownButtonCounter, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 28, Gravity.TOP | Gravity.LEFT)); reactionsMentiondownButton.setContentDescription(LocaleController.getString("AccDescrReactionMentionDown", R.string.AccDescrReactionMentionDown)); - fragmentLocationContextView = new FragmentContextView(context, this, true, themeDelegate); - fragmentContextView = new FragmentContextView(context, this, false, themeDelegate); - contentView.addView(fragmentLocationContextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 38, Gravity.TOP | Gravity.LEFT, 0, -36, 0, 0)); - contentView.addView(fragmentContextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 38, Gravity.TOP | Gravity.LEFT, 0, -36, 0, 0)); + if (!isInsideContainer) { + fragmentLocationContextView = new FragmentContextView(context, this, true, themeDelegate); + fragmentContextView = new FragmentContextView(context, this, false, themeDelegate); + contentView.addView(fragmentLocationContextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 38, Gravity.TOP | Gravity.LEFT, 0, -36, 0, 0)); + contentView.addView(fragmentContextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 38, Gravity.TOP | Gravity.LEFT, 0, -36, 0, 0)); - fragmentContextView.setAdditionalContextView(fragmentLocationContextView); - fragmentLocationContextView.setAdditionalContextView(fragmentContextView); + fragmentContextView.setAdditionalContextView(fragmentLocationContextView); + fragmentLocationContextView.setAdditionalContextView(fragmentContextView); - fragmentContextView.setEnabled(!inPreviewMode); - fragmentLocationContextView.setEnabled(!inPreviewMode); + fragmentContextView.setEnabled(!inPreviewMode); + fragmentLocationContextView.setEnabled(!inPreviewMode); - if (chatMode != 0) { - fragmentContextView.setSupportsCalls(false); + if (chatMode != 0) { + fragmentContextView.setSupportsCalls(false); + } } messagesSearchListView = new RecyclerListView(context, themeDelegate); @@ -6603,7 +6689,7 @@ protected void onLineCountChanged(int oldLineCount, int newLineCount) { chatActivityEnterView.setFieldText(inlineQueryForInput); inlineQueryForInput = null; } - if (inPreviewMode) { + if (inPreviewMode || isInsideContainer) { chatActivityEnterView.setVisibility(View.INVISIBLE); } if (!ChatObject.isChannel(currentChat) || currentChat.megagroup) { @@ -6674,7 +6760,7 @@ public void setVisibility(int visibility) { } else if (messagePreviewParams != null) { forbidForwardingWithDismiss = false; if (fieldPanelShown == 2) { - if (DialogObject.isEncryptedDialog(dialog_id)) { + if (DialogObject.isEncryptedDialog(dialog_id) || messagePreviewParams.hasSecretMessages) { if (replyingMessageObject != null) { scrollToMessageId(replyingMessageObject.getId(), 0, true, 0, true, 0); } @@ -6725,7 +6811,7 @@ public void setVisibility(int visibility) { fallbackFieldPanel(); } else { if (ChatObject.isForum(currentChat) && !isTopic && replyingMessageObject != null) { - int topicId = MessageObject.getTopicId(replyingMessageObject.messageOwner, true); + long topicId = MessageObject.getTopicId(currentAccount, replyingMessageObject.messageOwner, true); if (topicId != 0) { getMediaDataController().cleanDraft(dialog_id, topicId, false); } @@ -6874,7 +6960,7 @@ public void onDraw(Canvas canvas) { bottomOverlayText.setEllipsize(TextUtils.TruncateAt.END); bottomOverlayText.setLineSpacing(AndroidUtilities.dp(2), 1); bottomOverlayText.setTextColor(getThemedColor(Theme.key_chat_secretChatStatusText)); - bottomOverlay.addView(bottomOverlayText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 14, 0, 14, 0)); + bottomOverlay.addView(bottomOverlayText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 24, 0, 24, 0)); bottomOverlayChat = new BlurredFrameLayout(context, contentView) { @Override @@ -6946,11 +7032,19 @@ public void setVisibility(int visibility) { bottomOverlayStartButton.setOnClickListener(v -> bottomOverlayChatText.callOnClick()); bottomOverlayChat.addView(bottomOverlayStartButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER, 8, 8, 8, 8)); - if (currentUser != null && currentUser.bot && !UserObject.isReplyUser(currentUser) && !isInScheduleMode() && chatMode != MODE_PINNED) { + if (currentUser != null && currentUser.bot && !UserObject.isReplyUser(currentUser) && !isInScheduleMode() && chatMode != MODE_PINNED && chatMode != MODE_SAVED) { bottomOverlayStartButton.setVisibility(View.VISIBLE); bottomOverlayChat.setVisibility(View.VISIBLE); } + bottomOverlayLinksText = new LinkSpanDrawable.LinksTextView(context, themeDelegate); + bottomOverlayLinksText.setVisibility(View.GONE); + bottomOverlayLinksText.setTextColor(getThemedColor(Theme.key_graySectionText)); + bottomOverlayLinksText.setGravity(Gravity.CENTER); + bottomOverlayLinksText.setTextAlignment(View.TEXT_ALIGNMENT_CENTER); + bottomOverlayLinksText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + bottomOverlayChat.addView(bottomOverlayLinksText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 0, 0, 0, 0)); + bottomOverlayChatText = new UnreadCounterTextView(context) { @Override protected void updateCounter() { @@ -6984,7 +7078,16 @@ protected float getTopOffset() { if (getParentActivity() == null || pullingDownOffset != 0) { return; } - if (reportType >= 0) { + if (chatMode == MODE_SAVED) { + Bundle args = new Bundle(); + long dialogId = getSavedDialogId(); + if (dialogId >= 0) { + args.putLong("user_id", dialogId); + } else { + args.putLong("chat_id", -dialogId); + } + presentFragment(new ChatActivity(args)); + } else if (reportType >= 0) { showDialog(new ReportAlert(getParentActivity(), reportType, getResourceProvider()) { @Override protected void onSend(int type, String message) { @@ -7311,45 +7414,126 @@ public void onAllEffectsEnd() { } } + if (getDialogId() == getUserConfig().getClientUserId() && chatMode != MODE_SAVED) { + savedMessagesHint = new HintView2(context, HintView2.DIRECTION_TOP); + savedMessagesHint.setMultilineText(true); + savedMessagesHint.setTextAlign(Layout.Alignment.ALIGN_CENTER); + savedMessagesHint.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.SavedMessagesHint))); + savedMessagesHint.setMaxWidthPx(HintView2.cutInFancyHalf(savedMessagesHint.getText(), savedMessagesHint.getTextPaint())); + savedMessagesHint.setJoint(0.5f, 0); + savedMessagesHint.setCloseButton(true); + savedMessagesHint.setDuration(-1); + contentView.addView(savedMessagesHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 120, Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, -8, 0, 0)); + } + + if (getDialogId() == getUserConfig().getClientUserId()) { + actionBarSearchTags = new SearchTagsList(context, contentView, currentAccount, themeDelegate) { + @Override + protected void setFilter(ReactionsLayoutInBubble.VisibleReaction reaction) { + actionBar.clearSearchFilters(); +// if (reaction != null) { +// actionBar.setSearchFilter(new FiltersView.MediaFilterData(reaction)); +// } + searchingReaction = reaction; +// showSearchShowOther(searchingReaction != null); + getMediaDataController().searchMessagesInChat(searchItem.getSearchField().getText().toString(), dialog_id, mergeDialogId, classGuid, 0, threadMessageId, searchingUserMessages, searchingChatMessages, searchingReaction); + } + + @Override + public void updateTags() { + super.updateTags(); +// showActionBarSearchTags(searchItem != null && searchItem.isSearchFieldVisible() && hasFilters()); + } + }; + actionBarSearchTags.setVisibility(View.GONE); + actionBarSearchTags.attach(); + contentView.addView(actionBarSearchTags, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 40, Gravity.FILL_HORIZONTAL | Gravity.TOP)); + } + return fragmentView; } + private void setFilterMessages(boolean filter) { + if (chatAdapter.isFiltered == filter) return; + chatAdapter.isFiltered = filter; + if (filter) { + updateFilteredMessages(); + } else { + chatAdapter.updateRowsSafe(); + chatAdapter.notifyDataSetChanged(true); + } + } + + private void updateFilteredMessages() { + ArrayList results = new ArrayList<>(MediaDataController.getInstance(currentAccount).getFoundMessageObjects()); + chatAdapter.filteredMessages.clear(); + for (int i = 0; i < results.size(); ++i) { + MessageObject msg = results.get(i); + MessageObject from = null; + for (int j = 0; j < messages.size(); ++j) { + MessageObject m = messages.get(j); + if (m.getDialogId() == msg.getDialogId() && m.getId() == msg.getId()) { + from = m; + break; + } + } + if (msg.stableId == 0) { + msg.checkMediaExistance(); + if (from != null) { + msg.copyStableParams(from); + } else { + msg.stableId = lastStableId++; + } + } + msg.isOutOwnerCached = null; + if (msg.messageOwner != null) { + msg.messageOwner.out = true; + } + chatAdapter.filteredMessages.add(msg); + } + chatAdapter.filteredEndReached = MediaDataController.getInstance(currentAccount).searchEndReached(); + chatAdapter.updateRowsSafe(); + chatAdapter.notifyDataSetChanged(true); + } + private void createBottomMessagesActionButtons() { if (replyButton != null || getContext() == null) { return; } - replyButton = new TextView(getContext()); - replyButton.setText(LocaleController.getString("Reply", R.string.Reply)); - replyButton.setGravity(Gravity.CENTER_VERTICAL); - replyButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); - replyButton.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(21), 0); - replyButton.setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_actionBarActionModeDefaultSelector), 3)); - replyButton.setTextColor(getThemedColor(Theme.key_actionBarActionModeDefaultIcon)); - replyButton.setCompoundDrawablePadding(AndroidUtilities.dp(7)); - replyButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - Drawable image = getContext().getResources().getDrawable(R.drawable.input_reply).mutate(); - image.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_actionBarActionModeDefaultIcon), PorterDuff.Mode.MULTIPLY)); - replyButton.setCompoundDrawablesWithIntrinsicBounds(image, null, null, null); - replyButton.setOnClickListener(v -> { - MessageObject messageObject = null; - for (int a = 1; a >= 0; a--) { - if (messageObject == null && selectedMessagesIds[a].size() != 0) { - messageObject = messagesDict[a].get(selectedMessagesIds[a].keyAt(0)); + if (!isInsideContainer) { + replyButton = new TextView(getContext()); + replyButton.setText(LocaleController.getString("Reply", R.string.Reply)); + replyButton.setGravity(Gravity.CENTER_VERTICAL); + replyButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); + replyButton.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(21), 0); + replyButton.setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_actionBarActionModeDefaultSelector), 3)); + replyButton.setTextColor(getThemedColor(Theme.key_actionBarActionModeDefaultIcon)); + replyButton.setCompoundDrawablePadding(AndroidUtilities.dp(7)); + replyButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); + Drawable image = getContext().getResources().getDrawable(R.drawable.input_reply).mutate(); + image.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_actionBarActionModeDefaultIcon), PorterDuff.Mode.MULTIPLY)); + replyButton.setCompoundDrawablesWithIntrinsicBounds(image, null, null, null); + replyButton.setOnClickListener(v -> { + MessageObject messageObject = null; + for (int a = 1; a >= 0; a--) { + if (messageObject == null && selectedMessagesIds[a].size() != 0) { + messageObject = messagesDict[a].get(selectedMessagesIds[a].keyAt(0)); + } + selectedMessagesIds[a].clear(); + selectedMessagesCanCopyIds[a].clear(); + selectedMessagesCanStarIds[a].clear(); } - selectedMessagesIds[a].clear(); - selectedMessagesCanCopyIds[a].clear(); - selectedMessagesCanStarIds[a].clear(); - } - hideActionMode(); - if (messageObject != null && (messageObject.messageOwner.id > 0 || messageObject.messageOwner.id < 0 && currentEncryptedChat != null)) { - showFieldPanelForReply(messageObject); - } - updatePinnedMessageView(true); - updateVisibleRows(); - updateSelectedMessageReactions(); - }); - bottomMessagesActionContainer.addView(replyButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + hideActionMode(); + if (messageObject != null && (messageObject.messageOwner.id > 0 || messageObject.messageOwner.id < 0 && currentEncryptedChat != null)) { + showFieldPanelForReply(messageObject); + } + updatePinnedMessageView(true); + updateVisibleRows(); + updateSelectedMessageReactions(); + }); + bottomMessagesActionContainer.addView(replyButton, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); + } forwardButton = new TextView(getContext()); forwardButton.setText(LocaleController.getString("Forward", R.string.Forward)); @@ -7360,7 +7544,7 @@ private void createBottomMessagesActionButtons() { forwardButton.setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_actionBarActionModeDefaultSelector), 3)); forwardButton.setTextColor(getThemedColor(Theme.key_actionBarActionModeDefaultIcon)); forwardButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - image = getContext().getResources().getDrawable(R.drawable.input_forward).mutate(); + Drawable image = getContext().getResources().getDrawable(R.drawable.input_forward).mutate(); image.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_actionBarActionModeDefaultIcon), PorterDuff.Mode.MULTIPLY)); forwardButton.setCompoundDrawablesWithIntrinsicBounds(image, null, null, null); forwardButton.setOnClickListener(v -> openForward(false)); @@ -7769,7 +7953,7 @@ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, i searchUpButton.setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_actionBarActionModeDefaultSelector), 1)); searchContainer.addView(searchUpButton, LayoutHelper.createFrame(48, 48, Gravity.RIGHT | Gravity.TOP, 0, 0, 48, 0)); searchUpButton.setOnClickListener(view -> { - getMediaDataController().searchMessagesInChat(null, dialog_id, mergeDialogId, classGuid, 1, threadMessageId, searchingUserMessages, searchingChatMessages); + getMediaDataController().searchMessagesInChat(null, dialog_id, mergeDialogId, classGuid, 1, threadMessageId, searchingUserMessages, searchingChatMessages, searchingReaction); showMessagesSearchListView(false); if (!SharedConfig.searchMessagesAsListUsed && SharedConfig.searchMessagesAsListHintShows < 3 && !searchAsListHintShown && Math.random() <= 0.25) { showSearchAsListHint(); @@ -7786,7 +7970,7 @@ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, i searchDownButton.setBackgroundDrawable(Theme.createSelectorDrawable(getThemedColor(Theme.key_actionBarActionModeDefaultSelector), 1)); searchContainer.addView(searchDownButton, LayoutHelper.createFrame(48, 48, Gravity.RIGHT | Gravity.TOP, 0, 0, 0, 0)); searchDownButton.setOnClickListener(view -> { - getMediaDataController().searchMessagesInChat(null, dialog_id, mergeDialogId, classGuid, 2, threadMessageId, searchingUserMessages, searchingChatMessages); + getMediaDataController().searchMessagesInChat(null, dialog_id, mergeDialogId, classGuid, 2, threadMessageId, searchingUserMessages, searchingChatMessages, searchingReaction); showMessagesSearchListView(false); }); searchDownButton.setContentDescription(LocaleController.getString("AccDescrSearchPrev", R.string.AccDescrSearchPrev)); @@ -7840,6 +8024,23 @@ public void run(int param) { searchCountText.setGravity(Gravity.LEFT); searchContainer.addView(searchCountText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 0, 0, 108, 0)); contentView.addView(searchContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, searchContainerHeight, Gravity.BOTTOM)); + + searchOtherButton = new AnimatedTextView(getContext(), true, true, true); + searchOtherButton.setGravity(Gravity.CENTER); + searchOtherButton.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + searchOtherButton.setTextColor(getThemedColor(Theme.key_chat_fieldOverlayText)); + searchOtherButton.setTextSize(AndroidUtilities.dp(15)); + searchOtherButton.setBackground(Theme.createSelectorWithBackgroundDrawable(getThemedColor(Theme.key_windowBackgroundWhite), Theme.blendOver(getThemedColor(Theme.key_windowBackgroundWhite), getThemedColor(Theme.key_listSelector)))); + searchOtherButton.setVisibility(View.GONE); + searchOtherButton.setAlpha(0f); + searchContainer.addView(searchOtherButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + } + + private void showSearchShowOther(boolean show) { + searchOtherButton.setVisibility(View.VISIBLE); + searchOtherButton.animate().alpha(show ? 1f : 0f).withEndAction(() -> { + searchOtherButton.setVisibility(show ? View.VISIBLE : View.GONE); + }).start(); } public void onPageDownClicked() { @@ -7912,7 +8113,12 @@ private void checkInstantCameraView() { if (instantCameraView != null || getContext() == null) { return; } - instantCameraView = new InstantCameraView(getContext(), this, themeDelegate); + instantCameraView = new InstantCameraView(getContext(), this, themeDelegate) { + @Override + protected void clipBlur(Canvas canvas) { + canvas.clipRect(0, 0, getWidth(), getHeight() - dp(1.5f)); + } + }; contentView.addView(instantCameraView, 21, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); } @@ -8403,7 +8609,7 @@ private void searchUserMessages(TLRPC.User user, TLRPC.Chat chat) { mentionContainer.getAdapter().searchUsernameOrHashtag(null, 0, null, false, true); searchItem.setSearchFieldHint(null); searchItem.clearSearchText(); - getMediaDataController().searchMessagesInChat("", dialog_id, mergeDialogId, classGuid, 0, threadMessageId, searchingUserMessages, searchingChatMessages); + getMediaDataController().searchMessagesInChat("", dialog_id, mergeDialogId, classGuid, 0, threadMessageId, searchingUserMessages, searchingChatMessages, searchingReaction); } private void updateTranslateItemVisibility() { @@ -8592,7 +8798,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { pinnedMessageView.setOnClickListener(v -> { wasManualScroll = true; if (isThreadChat() && !isTopic) { - scrollToMessageId(threadMessageId, 0, true, 0, true, 0); + scrollToMessageId((int) threadMessageId, 0, true, 0, true, 0); } else if (currentPinnedMessageId != 0) { int currentPinned = currentPinnedMessageId; @@ -8880,7 +9086,11 @@ public void onUnpin(boolean all, boolean hide) { ArrayList objects = new ArrayList<>(pinnedMessageObjects.values()); if (hide) { SharedPreferences preferences = MessagesController.getNotificationsSettings(currentAccount); - preferences.edit().putInt("pin_" + dialog_id, pinnedMessageIds.get(0)).commit(); + if (pinnedMessageIds.isEmpty()) { + preferences.edit().remove("pin_" + dialog_id).commit(); + } else { + preferences.edit().putInt("pin_" + dialog_id, pinnedMessageIds.get(0)).commit(); + } updatePinnedMessageView(true); } else { getNotificationCenter().postNotificationName(NotificationCenter.didLoadPinnedMessages, dialog_id, ids, false, null, null, 0, 0, true); @@ -9110,6 +9320,9 @@ private void updateChatListViewTopPadding() { chatListViewPaddingVisibleOffset = 0; chatListViewPaddingTop += contentPanTranslation + bottomPanelTranslationY; float searchExpandOffset = 0; + if (actionBarSearchTags != null && actionBarSearchTags.shown()) { + chatListViewPaddingTop += actionBarSearchTags.getMeasuredHeight(); + } if (searchExpandProgress != 0 && chatActivityEnterView.getVisibility() == View.VISIBLE) { chatListViewPaddingTop -= (searchExpandOffset = searchExpandProgress * (chatActivityEnterView.getMeasuredHeight() - AndroidUtilities.dp(searchContainerHeight))); } @@ -9137,7 +9350,7 @@ private void updateChatListViewTopPadding() { floatingDateView.setTranslationY(chatListView.getTranslationY() - searchExpandOffset + chatListViewPaddingTop + floatingDateViewOffset - AndroidUtilities.dp(4)); } - int p = chatListView.getMeasuredHeight() * 2 / 3; + int p = isInsideContainer ? 0 : chatListView.getMeasuredHeight() * 2 / 3; if (chatListView != null && chatLayoutManager != null && chatAdapter != null) { if (chatListView.getPaddingTop() != p) { @@ -9541,14 +9754,14 @@ private void showBottomOverlayProgress(boolean show, boolean animated) { ObjectAnimator.ofFloat(bottomOverlayProgress, View.ALPHA, 1.0f)); bottomOverlayAnimation.setStartDelay(200); } else { - bottomOverlayChatText.setVisibility(View.VISIBLE); + View text = bottomOverlayLinks ? bottomOverlayLinksText : bottomOverlayChatText; bottomOverlayAnimation.playTogether( ObjectAnimator.ofFloat(bottomOverlayProgress, View.SCALE_X, 0.1f), ObjectAnimator.ofFloat(bottomOverlayProgress, View.SCALE_Y, 0.1f), ObjectAnimator.ofFloat(bottomOverlayProgress, View.ALPHA, 0.0f), - ObjectAnimator.ofFloat(bottomOverlayChatText, View.SCALE_X, 1.0f), - ObjectAnimator.ofFloat(bottomOverlayChatText, View.SCALE_Y, 1.0f), - ObjectAnimator.ofFloat(bottomOverlayChatText, View.ALPHA, 1.0f)); + ObjectAnimator.ofFloat(text, View.SCALE_X, 1.0f), + ObjectAnimator.ofFloat(text, View.SCALE_Y, 1.0f), + ObjectAnimator.ofFloat(text, View.ALPHA, 1.0f)); } bottomOverlayAnimation.addListener(new AnimatorListenerAdapter() { @Override @@ -9557,7 +9770,7 @@ public void onAnimationEnd(Animator animation) { if (!show) { bottomOverlayProgress.setVisibility(View.INVISIBLE); } else { - bottomOverlayChatText.setVisibility(View.INVISIBLE); + (bottomOverlayLinks ? bottomOverlayLinksText : bottomOverlayChatText).setVisibility(View.INVISIBLE); } } } @@ -9576,10 +9789,14 @@ public void onAnimationCancel(Animator animation) { bottomOverlayProgress.setScaleX(show ? 1.0f : 0.1f); bottomOverlayProgress.setScaleY(show ? 1.0f : 0.1f); bottomOverlayProgress.setAlpha(show ? 1.0f : 1.0f); - bottomOverlayChatText.setVisibility(show ? View.INVISIBLE : View.VISIBLE); - bottomOverlayChatText.setScaleX(show ? 0.1f : 1.0f); - bottomOverlayChatText.setScaleY(show ? 0.1f : 1.0f); - bottomOverlayChatText.setAlpha(show ? 0.0f : 1.0f); + bottomOverlayChatText.setVisibility(show || bottomOverlayLinks ? View.INVISIBLE : View.VISIBLE); + bottomOverlayLinksText.setVisibility(show || !bottomOverlayLinks ? View.INVISIBLE : View.VISIBLE); + bottomOverlayChatText.setScaleX(show || bottomOverlayLinks ? 0.1f : 1.0f); + bottomOverlayLinksText.setScaleX(show || !bottomOverlayLinks ? 0.1f : 1.0f); + bottomOverlayChatText.setScaleY(show || bottomOverlayLinks ? 0.1f : 1.0f); + bottomOverlayLinksText.setScaleY(show || !bottomOverlayLinks ? 0.1f : 1.0f); + bottomOverlayChatText.setAlpha(show || bottomOverlayLinks ? 0.0f : 1.0f); + bottomOverlayLinksText.setAlpha(show || !bottomOverlayLinks ? 0.0f : 1.0f); } } @@ -10701,8 +10918,10 @@ public void onAnimationEnd(Animator animation) { } } + private boolean removingFromParent; @Override public void onRemoveFromParent() { + removingFromParent = true; MessageObject messageObject = MediaController.getInstance().getPlayingMessageObject(); if (messageObject != null && messageObject.isVideo()) { MediaController.getInstance().cleanupPlayer(true, true); @@ -10731,6 +10950,10 @@ private void checkScrollForLoad(boolean scroll) { if (chatLayoutManager == null || paused || chatAdapter.isFrozen || waitingForGetDifference) { return; } + if (chatAdapter.isFiltered) { + getMediaDataController().loadMoreSearchMessages(); + return; + } int firstVisibleItem = RecyclerListView.NO_POSITION; int visibleItemCount = 0; for (int i = 0; i < chatListView.getChildCount(); i++) { @@ -11667,7 +11890,7 @@ public void showFieldPanel(boolean show, MessageObject messageObjectToReply, Mes // set it for a case when replying in in View as messages mode MessageObject topicTopMessageObject = null; if (isForumInViewAsMessagesMode()) { - int topicId = MessageObject.getTopicId(messageObjectToReply.messageOwner, true); + long topicId = MessageObject.getTopicId(currentAccount, messageObjectToReply.messageOwner, true); if (topicId != 0) { TLRPC.TL_forumTopic topic = getMessagesController().getTopicsController().findTopic(currentChat.id, topicId); if (topic != null && topic.topicStartMessage != null) { @@ -11742,7 +11965,11 @@ public void showFieldPanel(boolean show, MessageObject messageObjectToReply, Mes replyIconImageView.setImageResource(R.drawable.filled_reply_quote); nameText = AndroidUtilities.replaceCharSequence("%s", LocaleController.getString(R.string.ReplyToQuote), name == null ? "" : name); } else { - replyIconImageView.setImageResource(R.drawable.filled_reply_settings); + if (messagePreviewParams == null || messagePreviewParams.hasSecretMessages) { + replyIconImageView.setImageResource(R.drawable.ic_ab_reply); + } else { + replyIconImageView.setImageResource(R.drawable.filled_reply_settings); + } nameText = AndroidUtilities.replaceCharSequence("%s", LocaleController.getString(R.string.ReplyTo), name == null ? "" : name); } nameText = Emoji.replaceEmoji(nameText, replyNameTextView.getPaint().getFontMetricsInt(), false); @@ -12200,9 +12427,11 @@ private Runnable sendSecretMediaDelete(MessageObject messageObject) { } final long taskId = getMessagesController().createDeleteShowOnceTask(dialog_id, messageObject.getId()); messageObject.forceExpired = true; - ArrayList msgs = new ArrayList<>(); - msgs.add(messageObject); - updateMessages(msgs, true); + if (messageObject.isOutOwner() || !messageObject.isRoundOnce() && !messageObject.isVoiceOnce()) { + ArrayList msgs = new ArrayList<>(); + msgs.add(messageObject); + updateMessages(msgs, true); + } return () -> getMessagesController().doDeleteShowOnceTask(taskId, dialog_id, messageObject.getId()); } @@ -12493,7 +12722,7 @@ public void updateMessagesVisiblePart(boolean inLayout) { threadMessageVisible = firstLoading; Integer currentReadMaxId = null; - int threadId = threadMessageId; + long threadId = threadMessageId; if (threadId != 0 && currentChat != null) { currentReadMaxId = replyMaxReadId; } else { @@ -12505,7 +12734,7 @@ public void updateMessagesVisiblePart(boolean inLayout) { int maxPositiveUnreadId = Integer.MIN_VALUE; int maxNegativeUnreadId = Integer.MAX_VALUE; int maxUnreadDate = Integer.MIN_VALUE; - int recyclerChatViewHeight = (contentView.getHeightWithKeyboard() - (inPreviewMode ? 0 : AndroidUtilities.dp(48)) - chatListView.getTop()); + int recyclerChatViewHeight = (contentView.getHeightWithKeyboard() - (inPreviewMode || isInsideContainer ? 0 : AndroidUtilities.dp(48)) - chatListView.getTop()); pollsToCheck.clear(); float clipTop = chatListViewPaddingTop; long currentTime = System.currentTimeMillis(); @@ -12668,7 +12897,7 @@ public void updateMessagesVisiblePart(boolean inLayout) { } } if (bottom <= clipTop) { - if (view instanceof ChatActionCell && messageObject.isDateObject) { + if (view instanceof ChatActionCell && messageObject != null && messageObject.isDateObject) { view.setAlpha(0); } continue; @@ -12682,7 +12911,7 @@ public void updateMessagesVisiblePart(boolean inLayout) { minChild = view; } if (chatListItemAnimator == null || (!chatListItemAnimator.willRemoved(view) && !chatListItemAnimator.willAddedFromAlpha(view))) { - if (view instanceof ChatActionCell && messageObject.isDateObject) { + if (view instanceof ChatActionCell && messageObject != null && messageObject.isDateObject) { if (view.getAlpha() != 1.0f) { view.setAlpha(1.0f); } @@ -12765,7 +12994,7 @@ public void updateMessagesVisiblePart(boolean inLayout) { if (minDateChild.getAlpha() != 1.0f) { minDateChild.setAlpha(1.0f); } - if (chatListView.getChildAdapterPosition(minDateChild) == chatAdapter.messagesStartRow + messages.size() - 1) { + if (chatListView.getChildAdapterPosition(minDateChild) == chatAdapter.messagesStartRow + (reversed ? 0 : messages.size() - 1)) { if (minDateChild.getAlpha() != 1.0f) { minDateChild.setAlpha(1.0f); } @@ -12787,7 +13016,7 @@ public void updateMessagesVisiblePart(boolean inLayout) { } float offset = minDateChild.getY() + minDateChild.getMeasuredHeight() - clipTop; if (offset > floatingDateView.getMeasuredHeight() && offset < floatingDateView.getMeasuredHeight() * 2) { - if (chatListView.getChildAdapterPosition(minDateChild) == chatAdapter.messagesStartRow + messages.size() - 1) { + if (chatListView.getChildAdapterPosition(minDateChild) == chatAdapter.messagesStartRow + (reversed ? 0 : messages.size() - 1)) { showFloatingView = false; if (minDateChild.getAlpha() != 1.0f) { minDateChild.setAlpha(1.0f); @@ -13062,6 +13291,8 @@ private int getHeightForMessage(MessageObject object, boolean withGroupCaption) dummyMessageCell = new ChatMessageCell(getParentActivity(), true, sharedResources, themeDelegate); } dummyMessageCell.isChat = currentChat != null || UserObject.isUserSelf(currentUser); + dummyMessageCell.isSavedChat = chatMode == MODE_SAVED; + dummyMessageCell.isSavedPreviewChat = chatMode == MODE_SAVED && isInsideContainer; dummyMessageCell.isBot = currentUser != null && currentUser.bot; dummyMessageCell.isMegagroup = ChatObject.isChannel(currentChat) && currentChat.megagroup; return dummyMessageCell.computeHeight(object, groupedMessagesMap.get(object.getGroupId()), withGroupCaption); @@ -13342,7 +13573,7 @@ private void updatePagedownButtonVisibility(boolean animated) { if (pagedownButton == null) { return; } - boolean show = canShowPagedownButton && !textSelectionHelper.isInSelectionMode() && !chatActivityEnterView.isRecordingAudioVideo(); + boolean show = canShowPagedownButton && !textSelectionHelper.isInSelectionMode() && !chatActivityEnterView.isRecordingAudioVideo() && !isInsideContainer; if (show) { if (animated && (openAnimationStartTime == 0 || SystemClock.elapsedRealtime() < openAnimationStartTime + 150)) { animated = false; @@ -13526,6 +13757,9 @@ protected void onPanTranslationUpdate(float y, float progress, boolean keyboardV setNonNoveTranslation(y); } else { actionBar.setTranslationY(y); + if (actionBarSearchTags != null) { + actionBarSearchTags.setTranslationY(y); + } if (emptyViewContainer != null) { emptyViewContainer.setTranslationY(y / 2); } @@ -13896,7 +14130,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { } } if (child == actionBar && parentLayout != null) { - parentLayout.drawHeaderShadow(canvas, actionBar.getVisibility() == VISIBLE ? (int) actionBar.getTranslationY() + actionBar.getMeasuredHeight() + (inPreviewMode && Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0) : 0); + parentLayout.drawHeaderShadow(canvas, actionBar.getVisibility() == VISIBLE ? (int) actionBar.getTranslationY() + actionBar.getMeasuredHeight() + (actionBarSearchTags != null ? actionBarSearchTags.getCurrentHeight() : 0) + (inPreviewMode && Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0) : 0); } return result; } @@ -14287,9 +14521,11 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int heightSize = allHeight = View.MeasureSpec.getSize(heightMeasureSpec); if (lastWidth != widthSize) { - globalIgnoreLayout = true; + globalIgnoreLayout = false; lastWidth = widthMeasureSpec; - if (!inPreviewMode && currentUser != null && currentUser.self) { + if (chatMode == MODE_SAVED) { + showSearchAsIcon = false; + } else if (!inPreviewMode && currentUser != null && currentUser.self) { SimpleTextView textView = avatarContainer.getTitleTextView(); int textWidth = (int) textView.getPaint().measureText(textView.getText(), 0, textView.getText().length()); if (widthSize - AndroidUtilities.dp(96 + 56) > textWidth + AndroidUtilities.dp(10)) { @@ -14397,7 +14633,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildWithMargins(chatActivityEnterView, widthMeasureSpec, 0, heightMeasureSpec, 0); int listViewTopHeight; - if (inPreviewMode) { + if (inPreviewMode || isInsideContainer) { inputFieldHeight = 0; listViewTopHeight = 0; } else { @@ -14407,7 +14643,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { blurredViewTopOffset = 0; blurredViewBottomOffset = 0; - if (SharedConfig.chatBlurEnabled()) { + if (SharedConfig.chatBlurEnabled() && !isInsideContainer) { blurredViewTopOffset = actionBarHeight; blurredViewBottomOffset = AndroidUtilities.dp(203); } @@ -14650,7 +14886,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { childTop -= inputFieldHeight; } else if (child == chatListView || child == chatListThanosEffect || child == floatingDateView || child == infoTopView) { childTop -= blurredViewTopOffset; - if (!inPreviewMode) { + if (!inPreviewMode && !isInsideContainer) { childTop -= (inputFieldHeight - AndroidUtilities.dp(51)); } childTop -= paddingBottom; @@ -14698,6 +14934,9 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { private void setNonNoveTranslation(float y) { contentView.setTranslationY(y); actionBar.setTranslationY(0); + if (actionBarSearchTags != null) { + actionBarSearchTags.setTranslationY(0); + } emptyViewContainer.setTranslationY(0); progressView.setTranslationY(0); contentPanTranslation = 0; @@ -14747,7 +14986,21 @@ private void updateSecretStatus() { return; } boolean hideKeyboard = false; - if (currentChat != null && !ChatObject.canSendMessages(currentChat) && !ChatObject.canSendAnyMedia(currentChat) && !currentChat.gigagroup && (!ChatObject.isChannel(currentChat) || currentChat.megagroup)) { + if (chatMode == MODE_SAVED && getSavedDialogId() == UserObject.ANONYMOUS) { + bottomOverlayText.setText(LocaleController.getString(R.string.AuthorHiddenDescription)); + bottomOverlay.setVisibility(View.VISIBLE); + if (mentionListAnimation != null) { + mentionListAnimation.cancel(); + mentionListAnimation = null; + } + mentionContainer.setVisibility(View.GONE); + mentionContainer.setTag(null); + updateMessageListAccessibilityVisibility(); + hideKeyboard = true; + if (suggestEmojiPanel != null) { + suggestEmojiPanel.forceClose(); + } + } else if (currentChat != null && !ChatObject.canSendMessages(currentChat) && !ChatObject.canSendAnyMedia(currentChat) && !currentChat.gigagroup && (!ChatObject.isChannel(currentChat) || currentChat.megagroup)) { if (currentChat.default_banned_rights != null && currentChat.default_banned_rights.send_messages) { bottomOverlayText.setText(LocaleController.getString("GlobalSendMessageRestricted", R.string.GlobalSendMessageRestricted)); } else if (AndroidUtilities.isBannedForever(currentChat.banned_rights)) { @@ -14795,7 +15048,7 @@ private void updateSecretStatus() { hideKeyboard = true; } else if (currentEncryptedChat instanceof TLRPC.TL_encryptedChat) { bottomOverlay.setVisibility(View.INVISIBLE); - if (!inPreviewMode) { + if (!inPreviewMode && !isInsideContainer && chatMode != MODE_SAVED) { chatActivityEnterView.setVisibility(View.VISIBLE); } } @@ -15158,7 +15411,7 @@ private void addToSelectedMessages(MessageObject messageObject, boolean outside, } if (messageObject.isMusic() && !noforwards) { canSaveMusicCount++; - } else if (messageObject.isDocument() && !noforwards) { + } else if (messageObject.isDocument() && !messageObject.isRoundOnce() && !messageObject.isVoiceOnce() && !noforwards) { canSaveDocumentsCount++; } else { cantSaveMessagesCount++; @@ -15484,7 +15737,29 @@ public void updateTitle(boolean animated) { if (avatarContainer == null) { return; } - if (isThreadChat()) { + if (chatMode == MODE_SAVED) { + long dialogId = threadMessageId; + TLRPC.User user = null; + TLRPC.Chat chat = null; + if (dialogId > 0) { + user = getMessagesController().getUser(dialogId); + } else { + chat = getMessagesController().getChat(-dialogId); + } + if (UserObject.isReplyUser(user)) { + avatarContainer.setTitle(LocaleController.getString("RepliesTitle", R.string.RepliesTitle)); + } else if (UserObject.isAnonymous(user)) { + avatarContainer.setTitle(LocaleController.getString(R.string.AnonymousForward)); + } else if (UserObject.isUserSelf(user)) { + avatarContainer.setTitle(LocaleController.getString(R.string.MyNotes)); + } else if (user != null) { + avatarContainer.setTitle(UserObject.getUserName(user)); + } else if (chat != null) { + avatarContainer.setTitle(chat.title); + } else { + avatarContainer.setTitle(""); + } + } else if (isThreadChat()) { if (isTopic) { updateTopicHeader(); } else if (isComments) { @@ -15498,6 +15773,8 @@ public void updateTitle(boolean animated) { } } else if (UserObject.isReplyUser(currentUser)) { avatarContainer.setTitle(LocaleController.getString("RepliesTitle", R.string.RepliesTitle)); + } else if (UserObject.isAnonymous(currentUser)) { + avatarContainer.setTitle(LocaleController.getString(R.string.AnonymousForward)); } else if (chatMode == MODE_SCHEDULED) { if (UserObject.isUserSelf(currentUser)) { avatarContainer.setTitle(LocaleController.getString("Reminders", R.string.Reminders)); @@ -16224,7 +16501,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { fragmentTransitionRunnable.run(); return; } - if (chatMode != mode) { + if (chatMode != mode && chatMode != MODE_SAVED) { if (chatMode != MODE_SCHEDULED) { if (isTopic) { ForumUtilities.filterMessagesByTopic(threadMessageId, messArr); @@ -16467,7 +16744,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (isThreadChat() && !isTopic && (load_type == 2 || load_type == 3) && !isCache) { if (load_type == 3 && scrollToThreadMessage) { - startLoadFromMessageId = threadMessageId; + startLoadFromMessageId = (int) threadMessageId; } int beforMax = 0; int afterMax = 0; @@ -16739,24 +17016,38 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (groupedMessages != null) { if (messages.size() > 1) { MessageObject previous; - if (load_type == 1) { + if (load_type == MessagesController.LOAD_FORWARD) { previous = messages.get(0); } else { - previous = messages.get(messages.size() - 2); + previous = messages.get(messages.size() - (reversed ? 1 : 2)); } if (previous.getGroupIdForUse() == obj.getGroupIdForUse()) { if (previous.localGroupId != 0) { obj.localGroupId = previous.localGroupId; groupedMessages = groupedMessagesMap.get(previous.localGroupId); } - } else if (previous.getGroupIdForUse() != obj.getGroupIdForUse()) { - obj.localGroupId = Utilities.random.nextLong(); - groupedMessages = null; + } else { + if (reversed) { + previous = messages.get(messages.size() - 2); + if (previous.getGroupIdForUse() == obj.getGroupIdForUse()) { + if (previous.localGroupId != 0) { + obj.localGroupId = previous.localGroupId; + groupedMessages = groupedMessagesMap.get(previous.localGroupId); + } + } else { + obj.localGroupId = Utilities.random.nextLong(); + groupedMessages = null; + } + } else { + obj.localGroupId = Utilities.random.nextLong(); + groupedMessages = null; + } } } } if (groupedMessages == null) { groupedMessages = new MessageObject.GroupedMessages(); + groupedMessages.reversed = reversed; groupedMessages.groupId = obj.getGroupId(); groupedMessagesMap.put(groupedMessages.groupId, groupedMessages); } else if (newGroups == null || newGroups.indexOfKey(obj.getGroupId()) < 0) { @@ -16791,7 +17082,11 @@ public void didReceivedNotification(int id, int account, final Object... args) { messages.add(0, obj); } else { messages.get(messages.size() - 1).stableId = lastStableId++; - messages.add(messages.size() - 1, obj); + if (reversed) { + messages.add(obj); + } else { + messages.add(messages.size() - 1, obj); + } } MessageObject prevObj; if (currentEncryptedChat == null) { @@ -16813,7 +17108,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { prevObj = null; } } - if (load_type == 2 && messageId != 0 && messageId == first_unread_id) { + if (load_type == 2 && messageId != 0 && messageId == first_unread_id && chatMode != MODE_SAVED) { if ((approximateHeightSum > AndroidUtilities.displaySize.y / 2 || isThreadChat()) || !forwardEndReached[0]) { if (!isThreadChat() || threadMaxInboxReadId != 0) { TLRPC.Message dateMsg = new TLRPC.TL_message(); @@ -16852,7 +17147,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } if (load_type != 2 && unreadMessageObject == null && createUnreadMessageAfterId != 0 && (currentEncryptedChat == null && (!obj.isOut() || obj.messageOwner.from_scheduled) && messageId >= createUnreadMessageAfterId || currentEncryptedChat != null && (!obj.isOut() || obj.messageOwner.from_scheduled) && messageId <= createUnreadMessageAfterId) && - (load_type == 1 || prevObj != null || prevObj == null && createUnreadLoading && a == messArr.size() - 1)) { + (load_type == 1 || prevObj != null || prevObj == null && createUnreadLoading && a == messArr.size() - 1) && chatMode != MODE_SAVED) { TLRPC.Message dateMsg = new TLRPC.TL_message(); dateMsg.message = ""; dateMsg.id = 0; @@ -16874,6 +17169,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { newRowsCount++; } } + checkGroupMessagesOrder(); if (createUnreadLoading) { createUnreadMessageAfterId = 0; } @@ -17228,17 +17524,20 @@ public void didReceivedNotification(int id, int account, final Object... args) { } if (did == dialog_id && !messages.isEmpty() && currentUser != null && (bottomOverlayStartButton != null && bottomOverlayStartButton.getVisibility() == View.VISIBLE)) { - if (!TextUtils.isEmpty(botUser) && !sentBotStart) { + if (!TextUtils.isEmpty(botUser) && !sentBotStart && chatMode == 0) { sentBotStart = true; getMessagesController().sendBotStart(currentUser, botUser); bottomOverlayChat.setVisibility(View.GONE); - chatActivityEnterView.setVisibility(View.VISIBLE); + if (!isInsideContainer) { + chatActivityEnterView.setVisibility(View.VISIBLE); + } chatActivityEnterView.setBotInfo(botInfo); } } checkNewMessagesOnQuoteEdit(true); + invalidatePremiumBlocked(); } else if (id == NotificationCenter.invalidateMotionBackground) { if (chatListView != null) { chatListView.invalidateViews(); @@ -17357,6 +17656,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } else if (id == NotificationCenter.didReceiveNewMessages) { long did = (Long) args[0]; ArrayList arr = (ArrayList) args[1]; + if (isInsideContainer) return; if (did == dialog_id) { boolean scheduled = (Boolean) args[2]; if (scheduled != (chatMode == MODE_SCHEDULED)) { @@ -17482,7 +17782,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } } } else if (id == NotificationCenter.messagesRead) { - if (chatMode == MODE_SCHEDULED) { + if (chatMode == MODE_SCHEDULED || chatMode == MODE_SAVED) { return; } LongSparseIntArray inbox = (LongSparseIntArray) args[0]; @@ -17627,7 +17927,8 @@ public void didReceivedNotification(int id, int account, final Object... args) { } ArrayList markAsDeletedMessages = (ArrayList) args[0]; long channelId = (Long) args[1]; - processDeletedMessages(markAsDeletedMessages, channelId); + boolean sent = args.length > 3 && (boolean) args[3]; + processDeletedMessages(markAsDeletedMessages, channelId, sent); } else if (id == NotificationCenter.messageReceivedByServer) { Boolean scheduled = (Boolean) args[6]; if (scheduled != (chatMode == MODE_SCHEDULED)) { @@ -18107,7 +18408,12 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (MediaController.getInstance().isPlayingMessage(messageObject1)) { boolean keyboardIsVisible = contentView.getKeyboardHeight() >= AndroidUtilities.dp(20); float topPadding = chatListViewPaddingTop - (contentPanTranslation + bottomPanelTranslationY); - int offset = (int) ((chatListView.getMeasuredHeight() - topPadding - blurredViewBottomOffset) / 2 - (keyboardIsVisible ? AndroidUtilities.roundMessageSize : AndroidUtilities.roundPlayingMessageSize) / 2 - (cell.reactionsLayoutInBubble == null ? 0 : cell.reactionsLayoutInBubble.totalHeight)); + int offset = (int) ((chatListView.getMeasuredHeight() - topPadding - blurredViewBottomOffset) / 2 - (cell.reactionsLayoutInBubble == null ? 0 : cell.reactionsLayoutInBubble.totalHeight)); + if (messageObject1.type != MessageObject.TYPE_ROUND_VIDEO) { + offset -= cell.getPhotoImage().getImageY(); + } else { + offset -= (keyboardIsVisible ? AndroidUtilities.roundMessageSize : AndroidUtilities.roundPlayingMessageSize) / 2; + } chatLayoutManager.scrollToPositionWithOffset(position, offset, false); } chatAdapter.notifyItemChanged(position); @@ -18357,6 +18663,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { MessageObject messageObject = null; if (idx > 0 && idx < groupedMessages.messages.size() - 1) { MessageObject.GroupedMessages slicedGroup = new MessageObject.GroupedMessages(); + slicedGroup.reversed = reversed; slicedGroup.groupId = Utilities.random.nextLong(); slicedGroup.messages.addAll(groupedMessages.messages.subList(idx + 1, groupedMessages.messages.size())); for (int b = 0; b < slicedGroup.messages.size(); b++) { @@ -18403,7 +18710,14 @@ public void didReceivedNotification(int id, int account, final Object... args) { transcriptionId = (Long) args[1]; transcriptionText = (String) args[2]; } - ArrayList messages = chatAdapter.isFrozen ? chatAdapter.frozenMessages : ChatActivity.this.messages; + final ArrayList messages; + if (chatAdapter.isFrozen) { + messages = chatAdapter.frozenMessages; + } else if (chatAdapter.isFiltered) { + messages = chatAdapter.filteredMessages; + } else { + messages = ChatActivity.this.messages; + } if (messages != null && !messages.contains(messageObject) && args.length > 1 && args[1] != null) { for (int a = 0; a < messages.size(); ++a) { if (messages.get(a) != null && messages.get(a).messageOwner != null && (messages.get(a).messageOwner.voiceTranscriptionId == transcriptionId || messageObject != null && messageObject.getId() == messages.get(a).getId() && messageObject.getDialogId() == messages.get(a).getDialogId())) { @@ -18451,7 +18765,14 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (chatAdapter != null) { MessageObject messageObject = (MessageObject) args[0]; if (messageObject != null) { - ArrayList messages = chatAdapter.isFrozen ? chatAdapter.frozenMessages : ChatActivity.this.messages; + final ArrayList messages; + if (chatAdapter.isFrozen) { + messages = chatAdapter.frozenMessages; + } else if (chatAdapter.isFiltered) { + messages = chatAdapter.filteredMessages; + } else { + messages = ChatActivity.this.messages; + } int index = messages.indexOf(messageObject); if (index >= 0 && index < messages.size()) { int position = index + chatAdapter.messagesStartRow; @@ -18482,7 +18803,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (messagePreviewParams != null) { messagePreviewParams.checkEdits(messageObjects); } - if (did != dialog_id && did != mergeDialogId) { + if (did != dialog_id && did != mergeDialogId || chatMode == MODE_SAVED) { return; } int loadIndex = did == dialog_id ? 0 : 1; @@ -18571,7 +18892,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (object == null && replaceObjects != null) { object = replaceObjects.get(mid); } - if (object != null && (!isTopic || getTopicId() == MessageObject.getTopicId(object.messageOwner, ChatObject.isForum(currentChat)))) { + if (object != null && (!isTopic || getTopicId() == MessageObject.getTopicId(currentAccount, object.messageOwner, ChatObject.isForum(currentChat)))) { pinnedMessageIds.add(mid); pinnedMessageObjects.put(mid, object); if (replaceObjects == null) { @@ -18627,7 +18948,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { processNewMessages(arrayList); } } else { - processDeletedMessages(ids, ChatObject.isChannel(currentChat) ? dialog_id : 0); + processDeletedMessages(ids, ChatObject.isChannel(currentChat) ? dialog_id : 0, false); } } } else { @@ -18794,9 +19115,18 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (jumpToMessage) { int messageId = (Integer) args[1]; long did = (Long) args[3]; +// if (searchingReaction != null) { +// if (chatAdapter.isFiltered) { +// updateFilteredMessages(); +// } else { +// setFilterMessages(true); +// } +// } else if (messageId != 0) { + setFilterMessages(false); scrollToMessageId(messageId, 0, true, did == dialog_id ? 0 : 1, true, 0); } else { + setFilterMessages(false); updateVisibleRows(); } updateSearchButtons((Integer) args[2], (Integer) args[4], (Integer) args[5]); @@ -18804,6 +19134,13 @@ public void didReceivedNotification(int id, int account, final Object... args) { searchItem.setShowSearchProgress(false); } } +// else if (searchingReaction != null) { +// if (chatAdapter.isFiltered) { +// updateFilteredMessages(); +// } else { +// setFilterMessages(true); +// } +// } if (messagesSearchAdapter != null) { messagesSearchAdapter.notifyDataSetChanged(); } @@ -18995,7 +19332,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { continue; } - int messageTopicId = MessageObject.getTopicId(messageObject.messageOwner, true); + long messageTopicId = MessageObject.getTopicId(currentAccount, messageObject.messageOwner, true); if (getTopicId() != messageTopicId) { pinnedMessageObjects.remove(messageId); pinnedMessageIds.remove(i); @@ -19019,6 +19356,10 @@ public void didReceivedNotification(int id, int account, final Object... args) { Long uid = (Long) args[0]; if (currentUser != null && currentUser.id == uid) { userInfo = (TLRPC.UserFull) args[1]; + if (greetingsViewContainer != null) { + greetingsViewContainer.setPremiumLock(userInfo != null && userInfo.contact_require_premium && !getUserConfig().isPremium(), dialog_id); + } + updateBottomOverlay(); checkThemeEmoticonOrWallpaper(); if (chatActivityEnterView != null) { chatActivityEnterView.checkChannelRights(); @@ -19131,7 +19472,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } } else if (id == NotificationCenter.updateMentionsCount) { long dialogId = (Long) args[0]; - int topicId = (Integer) args[1]; + long topicId = (Long) args[1]; if (dialog_id == dialogId && this.getTopicId() == topicId) { int count = (int) args[2]; if (newMentionsCount > count) { @@ -19220,13 +19561,13 @@ public void didReceivedNotification(int id, int account, final Object... args) { } } else if (id == NotificationCenter.chatAvailableReactionsUpdated) { long chatId = (long) args[0]; - int topicId = (int) args[1]; + long topicId = (long) args[1]; if (chatId == -dialog_id && (!isTopic || getTopicId() == topicId)) { chatInfo = getMessagesController().getChatFull(chatId); } } else if (id == NotificationCenter.dialogsUnreadReactionsCounterChanged) { long dialogId = (long) args[0]; - int topicId = (int) args[1]; + long topicId = (long) args[1]; if (dialogId == dialog_id && (!isTopic || topicId == getTopicId())) { reactionsMentionCount = (int) args[2]; ArrayList messages = null; @@ -19422,6 +19763,20 @@ public void didReceivedNotification(int id, int account, final Object... args) { } } } + } else if (id == NotificationCenter.savedMessagesDialogsUpdate) { + if (checkedSavedMessagesHint && !savedMessagesHintShown && chatMode == 0 && savedMessagesHint != null && MessagesController.getGlobalMainSettings().getInt("savedhint", 0) < 1 && !getMessagesController().getSavedMessagesController().unsupported && getMessagesController().getSavedMessagesController().getAllCount() > 2) { + savedMessagesHint.show(); + savedMessagesHintShown = true; + MessagesController.getGlobalMainSettings().edit().putInt("savedhint", MessagesController.getGlobalMainSettings().getInt("savedhint", 0) + 1).apply(); + } + + if (avatarContainer != null) { + avatarContainer.updateSubtitle(true); + } + + if (chatMode == MODE_SAVED && !isInsideContainer && getUserConfig().getClientUserId() != getSavedDialogId() && !getMessagesController().getSavedMessagesController().containsDialog(getSavedDialogId())) { + finishFragment(); + } } } @@ -20049,7 +20404,7 @@ private void processNewMessages(ArrayList arr) { } } int messageId = messageObject.getId(); - if (threadMessageId != 0) { + if (chatMode != MODE_SAVED && threadMessageId != 0) { if (messageId > 0 && messageId <= (messageObject.isOut() ? threadMaxOutboxReadId : threadMaxInboxReadId)) { messageObject.setIsRead(); } @@ -20167,8 +20522,14 @@ private void processNewMessages(ArrayList arr) { if (obj.isOut()) { rotateMotionBackgroundDrawable(); } - if (threadMessageId != 0 && threadMessageId != obj.getReplyTopMsgId() && threadMessageId != obj.getReplyMsgId()) { - continue; + if (chatMode == MODE_SAVED) { + if (MessageObject.getSavedDialogId(getUserConfig().getClientUserId(), obj.messageOwner) != threadMessageId) { + continue; + } + } else { + if (threadMessageId != 0 && threadMessageId != obj.getReplyTopMsgId() && threadMessageId != obj.getReplyMsgId()) { + continue; + } } int messageId = obj.getId(); @@ -20264,9 +20625,18 @@ private void processNewMessages(ArrayList arr) { int lastAdIndex = -1; for (int a = 0; a < arr.size(); a++) { MessageObject obj = arr.get(a); - if (obj.scheduled != (chatMode == MODE_SCHEDULED) || threadMessageId != 0 && (!ChatObject.isForum(currentChat) || (!isTopic || getTopicId() != MessageObject.getTopicId(obj.messageOwner, ChatObject.isForum(currentChat)))) && threadMessageId != obj.getReplyTopMsgId() && threadMessageId != obj.getReplyMsgId()) { + if (obj.scheduled != (chatMode == MODE_SCHEDULED)) { continue; } + if (chatMode == MODE_SAVED) { + if (MessageObject.getSavedDialogId(getUserConfig().getClientUserId(), obj.messageOwner) != threadMessageId) { + continue; + } + } else { + if (threadMessageId != 0 && (!ChatObject.isForum(currentChat) || (!isTopic || getTopicId() != MessageObject.getTopicId(currentAccount, obj.messageOwner, ChatObject.isForum(currentChat)))) && threadMessageId != obj.getReplyTopMsgId() && threadMessageId != obj.getReplyMsgId()) { + continue; + } + } if (obj.isOut() && messagesDict[0].indexOfKey(obj.getId()) < 0) { rotateMotionBackgroundDrawable(); } @@ -20337,6 +20707,7 @@ private void processNewMessages(ArrayList arr) { groupedMessages = groupedMessagesMap.get(obj.getGroupId()); if (groupedMessages == null) { groupedMessages = new MessageObject.GroupedMessages(); + groupedMessages.reversed = reversed; groupedMessages.groupId = obj.getGroupId(); groupedMessagesMap.put(groupedMessages.groupId, groupedMessages); } @@ -20506,7 +20877,7 @@ private void processNewMessages(ArrayList arr) { chatAdapter.notifyItemInserted(placeToPaste); } } - if (!(obj.messageOwner.action instanceof TLRPC.TL_messageActionGeoProximityReached) && (!obj.isOut() || obj.messageOwner.from_scheduled)) { + if (!(obj.messageOwner.action instanceof TLRPC.TL_messageActionGeoProximityReached) && (!obj.isOut() || obj.messageOwner.from_scheduled) && chatMode != MODE_SAVED) { if (paused && placeToPaste == 0) { if (!scrollToTopUnReadOnResume && unreadMessageObject != null) { removeMessageObject(unreadMessageObject); @@ -20586,7 +20957,7 @@ private void processNewMessages(ArrayList arr) { setChatThemeEmoticon(action.emoticon); } if (webpagesToReload != null) { - getMessagesController().reloadWebPages(dialog_id, webpagesToReload, chatMode == MODE_SCHEDULED); + getMessagesController().reloadWebPages(dialog_id, webpagesToReload, chatMode); } if (newGroups != null) { for (int a = 0; a < newGroups.size(); a++) { @@ -20691,6 +21062,7 @@ private void processNewMessages(ArrayList arr) { if (!isAd && notPushedSponsoredMessages != null && !notPushedSponsoredMessages.isEmpty() && arr != notPushedSponsoredMessages) { processNewMessages(notPushedSponsoredMessages); } + invalidatePremiumBlocked(); } private int getStableIdForDateObject(int date) { @@ -20739,9 +21111,10 @@ private int getSponsoredMessagesCount() { return sponsoredMessagesCount; } - private void processDeletedMessages(ArrayList markAsDeletedMessages, long channelId) { + private void processDeletedMessages(ArrayList markAsDeletedMessages, long channelId, boolean sent) { ArrayList removedIndexes = new ArrayList<>(); - ArrayList messagesIndexes = new ArrayList<>(); + ArrayList thanosMessagesIndexes = new ArrayList<>(); + final int currentTime = getConnectionsManager().getCurrentTime(); int loadIndex = 0; if (ChatObject.isChannel(currentChat)) { if (channelId == 0 && mergeDialogId != 0) { @@ -20826,6 +21199,9 @@ private void processDeletedMessages(ArrayList markAsDeletedMessages, lo } } obj.deleted = true; + if (obj.scheduled && sent) { + obj.scheduledSent = true; + } if (editingMessageObject == obj) { hideFieldPanel(true); } @@ -20842,8 +21218,8 @@ private void processDeletedMessages(ArrayList markAsDeletedMessages, lo MessageObject removed = messages.remove(index); if (chatAdapter != null) { removedIndexes.add(chatAdapter.messagesStartRow + index); - if (removed != null && removed.messageOwner != null && removed.messageOwner.send_state == MessageObject.MESSAGE_SEND_STATE_SENT) { - messagesIndexes.add(chatAdapter.messagesStartRow + index); + if (!sent && !obj.scheduledSent && removed != null && removed.messageOwner != null && removed.messageOwner.send_state == MessageObject.MESSAGE_SEND_STATE_SENT && currentTime - removed.messageOwner.date >= (currentChat != null || currentUser != null && currentUser.bot ? 2 : 0)) { + thanosMessagesIndexes.add(chatAdapter.messagesStartRow + index); removed.deletedByThanos = LiteMode.isEnabled(LiteMode.FLAG_CHAT_THANOS); } } @@ -20962,7 +21338,7 @@ private void processDeletedMessages(ArrayList markAsDeletedMessages, lo int prevLoadingDownRow = chatAdapter.loadingDownRow; for (int a = 0, N = removedIndexes.size(); a < N; a++) { final int pos = removedIndexes.get(a); - chatAdapter.notifyItemRemoved(pos, messagesIndexes.contains(pos)); + chatAdapter.notifyItemRemoved(pos, thanosMessagesIndexes.contains(pos)); } if (!isThreadChat() || messages.size() <= 3) { removeUnreadPlane(false); @@ -20989,6 +21365,7 @@ private void processDeletedMessages(ArrayList markAsDeletedMessages, lo if (updateScheduled) { updateScheduledInterface(true); } + invalidatePremiumBlocked(); } private void replaceMessageObjects(ArrayList messageObjects, int loadIndex, boolean remove) { @@ -21088,6 +21465,7 @@ private void replaceMessageObjects(ArrayList messageObjects, int newGroups.put(groupedMessages.groupId, groupedMessages); if (idx > 0 && idx < groupedMessages.messages.size() - 1) { MessageObject.GroupedMessages slicedGroup = new MessageObject.GroupedMessages(); + slicedGroup.reversed = reversed; slicedGroup.groupId = Utilities.random.nextLong(); slicedGroup.messages.addAll(groupedMessages.messages.subList(idx + 1, groupedMessages.messages.size())); for (int b = 0; b < slicedGroup.messages.size(); b++) { @@ -21112,6 +21490,9 @@ private void replaceMessageObjects(ArrayList messageObjects, int } } } + if (chatMode == MODE_SAVED) { + messageObject.isSaved = true; + } if (messageObject.type >= 0) { messageObject.copyStableParams(old); messages.set(index, messageObject); @@ -21324,6 +21705,22 @@ public void onBecomeFullyVisible() { } keyboardWasVisible = false; } + if (savedMessagesHint != null) { + AndroidUtilities.runOnUIThread(this::checkSavedMessagesHint, 600); + } + } + + private boolean checkedSavedMessagesHint; + private boolean savedMessagesHintShown; + private void checkSavedMessagesHint() { + if (checkedSavedMessagesHint) + return; + checkedSavedMessagesHint = true; + if (!savedMessagesHintShown && savedMessagesHint != null && chatMode == 0 && MessagesController.getGlobalMainSettings().getInt("savedhint", 0) < 1 && !getMessagesController().getSavedMessagesController().unsupported && getMessagesController().getSavedMessagesController().getAllCount() > 2) { + savedMessagesHint.show(); + savedMessagesHintShown = true; + MessagesController.getGlobalMainSettings().edit().putInt("savedhint", MessagesController.getGlobalMainSettings().getInt("savedhint", 0) + 1).apply(); + } } @Override @@ -21733,7 +22130,26 @@ private void updateBottomOverlay() { return; } bottomOverlayChatWaitsReply = false; - if (reportType >= 0) { + bottomOverlayLinks = false; + if (chatMode == MODE_DEFAULT && userInfo != null && userInfo.contact_require_premium && !getUserConfig().isPremium()) { + bottomOverlayLinks = true; + bottomOverlayChatText.setVisibility(View.GONE); + bottomOverlayLinksText.setVisibility(View.VISIBLE); + bottomOverlayLinksText.setText(AndroidUtilities.replaceSingleTag(LocaleController.formatString(R.string.OnlyPremiumCanMessage, UserObject.getFirstName(currentUser)), Theme.key_chat_messageLinkIn, 0, () -> { + ChatGreetingsView.showPremiumSheet(getContext(), currentAccount, dialog_id, themeDelegate); + })); + showBottomOverlayProgress(false, false); + } else if (chatMode == MODE_SAVED && getSavedDialogId() != getUserConfig().getClientUserId()) { + if (getSavedDialogId() == UserObject.ANONYMOUS) { + bottomOverlayChat.setVisibility(View.GONE); + chatActivityEnterView.setVisibility(View.INVISIBLE); + return; + } else { + bottomOverlayChatText.setTag(null); + bottomOverlayChatText.setText(LocaleController.getString(currentUser != null ? R.string.SavedOpenChat : (ChatObject.isChannelAndNotMegaGroup(currentChat) ? R.string.SavedOpenChannel : R.string.SavedOpenGroup))); + showBottomOverlayProgress(false, false); + } + } else if (reportType >= 0) { updateActionModeTitle(); } else if (chatMode == MODE_PINNED) { boolean allowPin; @@ -21864,13 +22280,16 @@ private void updateBottomOverlay() { } else { bottomOverlayImage.setVisibility(View.INVISIBLE); } - if (inPreviewMode) { + if (inPreviewMode || isInsideContainer) { if (searchContainer != null) { searchContainer.setVisibility(View.INVISIBLE); } bottomOverlayChat.setVisibility(View.INVISIBLE); chatActivityEnterView.setFieldFocused(false); chatActivityEnterView.setVisibility(View.INVISIBLE); + } else if (bottomOverlayLinks) { + bottomOverlayChat.setVisibility(View.VISIBLE); + chatActivityEnterView.setVisibility(View.INVISIBLE); } else if (searchItem != null && searchItemVisible) { createSearchContainer(); if (searchContainer == null) { @@ -22010,7 +22429,7 @@ public void onAnimationEnd(Animator animation) { headerItem.setVisibility(View.VISIBLE); } } else { - if (botUser != null && currentUser.bot) { + if (botUser != null && currentUser.bot || chatMode == MODE_SAVED && getSavedDialogId() != getUserConfig().getClientUserId()) { bottomOverlayChat.setVisibility(View.VISIBLE); chatActivityEnterView.setVisibility(View.INVISIBLE); } else { @@ -22039,7 +22458,7 @@ private boolean shouldDisplaySwipeToLeftToReplyInForum() { private boolean canSendMessageToTopic(MessageObject message) { if (message != null && ChatObject.isForum(currentChat)) { - TLRPC.TL_forumTopic topic = getMessagesController().getTopicsController().findTopic(currentChat.id, MessageObject.getTopicId(message.messageOwner, true)); + TLRPC.TL_forumTopic topic = getMessagesController().getTopicsController().findTopic(currentChat.id, MessageObject.getTopicId(currentAccount, message.messageOwner, true)); return canSendMessageToTopic(topic); } return false; @@ -22058,16 +22477,19 @@ private boolean canSendMessageToTopic(TLRPC.TL_forumTopic topic) { } public void updateReplyMessageHeader(boolean notify) { + if (chatMode == MODE_SAVED) { + return; + } if (avatarContainer != null && threadMessageId != 0) { if (isTopic) { updateTopicHeader(); } else if (isComments) { - if (threadMessageObject.hasReplies()) { + if (threadMessageObject != null && threadMessageObject.hasReplies()) { avatarContainer.setTitle(LocaleController.formatPluralString("Comments", threadMessageObject.getRepliesCount())); } else { avatarContainer.setTitle(LocaleController.getString("CommentsTitle", R.string.CommentsTitle)); } - } else { + } else if (threadMessageObject != null) { avatarContainer.setTitle(LocaleController.formatPluralString("Replies", threadMessageObject.getRepliesCount())); } } @@ -22400,7 +22822,7 @@ private void updatePinnedMessageView(boolean animated, int animateToNext) { } else if (isThreadChat() && !isTopic) { if (!threadMessageVisible) { pinnedMessageObject = threadMessageObject; - pinned_msg_id = threadMessageId; + pinned_msg_id = (int) threadMessageId; } else { pinnedMessageObject = null; pinned_msg_id = 0; @@ -22985,23 +23407,24 @@ public void onAnimationEnd(Animator animation) { pinnedNextAnimation[1].start(); } } else { - if (!showCounter || currentPinnedMessageIndex[0] == 0) { - if (pinnedCounterTextView.getTag() == null) { - pinnedCounterTextView.setAlpha(0.0f); - pinnedCounterTextView.setVisibility(View.INVISIBLE); - pinnedCounterTextView.setTag(1); - } - } else { - if (pinnedCounterTextView.getTag() != null) { - pinnedCounterTextView.setVisibility(View.VISIBLE); - pinnedCounterTextView.setAlpha(1.0f); - pinnedCounterTextView.setTag(null); + if (pinnedCounterTextView != null) { + if (!showCounter || currentPinnedMessageIndex[0] == 0) { + if (pinnedCounterTextView.getTag() == null) { + pinnedCounterTextView.setAlpha(0.0f); + pinnedCounterTextView.setVisibility(View.INVISIBLE); + pinnedCounterTextView.setTag(1); + } + } else { + if (pinnedCounterTextView.getTag() != null) { + pinnedCounterTextView.setVisibility(View.VISIBLE); + pinnedCounterTextView.setAlpha(1.0f); + pinnedCounterTextView.setTag(null); + } } + pinnedCounterTextView.setTranslationY(0.0f); + pinnedCounterTextView.setTranslationX(pinnedCounterTextViewX); + pinnedCounterTextView.setAlpha(shouldAnimateName || currentPinnedMessageIndex[0] == 0 ? 0.0f : 1.0f); } - pinnedCounterTextView.setTranslationY(0.0f); - pinnedCounterTextView.setTranslationX(pinnedCounterTextViewX); - - pinnedCounterTextView.setAlpha(shouldAnimateName || currentPinnedMessageIndex[0] == 0 ? 0.0f : 1.0f); messageTextView.setVisibility(View.VISIBLE); messageTextView.setAlpha(1.0f); @@ -23024,14 +23447,18 @@ public void onAnimationEnd(Animator animation) { BackupImageView backupImageView = pinnedMessageImageView[1]; pinnedMessageImageView[1] = pinnedMessageImageView[0]; pinnedMessageImageView[0] = backupImageView; - pinnedMessageImageView[0].setAlpha(1.0f); - pinnedMessageImageView[0].setScaleX(1f); - pinnedMessageImageView[0].setScaleY(1f); - pinnedMessageImageView[0].setTranslationY(0); - pinnedMessageImageView[1].setAlpha(1.0f); - pinnedMessageImageView[1].setScaleX(1f); - pinnedMessageImageView[1].setScaleY(1f); - pinnedMessageImageView[1].setTranslationY(0); + if (pinnedMessageImageView[0] != null) { + pinnedMessageImageView[0].setAlpha(1.0f); + pinnedMessageImageView[0].setScaleX(1f); + pinnedMessageImageView[0].setScaleY(1f); + pinnedMessageImageView[0].setTranslationY(0); + } + if (pinnedMessageImageView[1] != null) { + pinnedMessageImageView[1].setAlpha(1.0f); + pinnedMessageImageView[1].setScaleX(1f); + pinnedMessageImageView[1].setScaleY(1f); + pinnedMessageImageView[1].setTranslationY(0); + } } if (isThreadChat() && !isTopic) { pinnedLineView.set(0, 1, false); @@ -23040,8 +23467,10 @@ public void onAnimationEnd(Animator animation) { pinnedLineView.set(pinnedMessageIds.size() - 1 - position, pinnedMessageIds.size(), animated); } } else { - pinnedCounterTextView.setVisibility(loadedPinnedMessagesCount == 2 || currentPinnedMessageIndex[0] == 0 ? View.INVISIBLE : View.VISIBLE); - pinnedCounterTextView.setAlpha(loadedPinnedMessagesCount == 2 || currentPinnedMessageIndex[0] == 0 ? 0.0f : 1.0f); + if (pinnedCounterTextView != null) { + pinnedCounterTextView.setVisibility(loadedPinnedMessagesCount == 2 || currentPinnedMessageIndex[0] == 0 ? View.INVISIBLE : View.VISIBLE); + pinnedCounterTextView.setAlpha(loadedPinnedMessagesCount == 2 || currentPinnedMessageIndex[0] == 0 ? 0.0f : 1.0f); + } pinnedImageLocation = null; pinnedImageLocationObject = null; changed = hidePinnedMessageView(animated); @@ -23615,10 +24044,10 @@ public void setInPreviewMode(boolean value) { updateSecretStatus(); if (fragmentContextView != null) { - fragmentContextView.setEnabled(!value); + fragmentContextView.setEnabled(!value && !isInsideContainer); } if (fragmentLocationContextView != null) { - fragmentLocationContextView.setEnabled(!value); + fragmentLocationContextView.setEnabled(!value && !isInsideContainer); } if (pinnedMessageView != null) { pinnedMessageView.setEnabled(!isInPreviewMode()); @@ -23667,8 +24096,10 @@ public int getBottomOffset(int tag) { } else { height = chatActivityEnterView.getHeight(); } - } else { + } else if (!isInsideContainer) { height = AndroidUtilities.dp(51); + } else { + height = 0; } if (chatActivityEnterView.panelAnimationInProgress()) { float translationY = bottomPanelTranslationY - chatActivityEnterView.getEmojiPadding(); @@ -23802,7 +24233,7 @@ public void onPause() { scrimPopupWindow.setPauseNotifications(false); closeMenu(); } - int replyId = threadMessageId; + long replyId = threadMessageId; getMessagesController().markDialogAsReadNow(dialog_id, replyId); MediaController.getInstance().stopRaiseToEarSensors(this, true, true); paused = true; @@ -23835,12 +24266,12 @@ public void onPause() { if (chatMode == 0) { CharSequence[] message = new CharSequence[]{ignoreDraft ? null : draftMessage}; ArrayList entities = getMediaDataController().getEntities(message, currentEncryptedChat == null || AndroidUtilities.getPeerLayerVersion(currentEncryptedChat.layer) >= 101); - int draftThreadId; + long draftThreadId; if (ChatObject.isForum(currentChat) && !isTopic && replyMessage != null) { if (replyMessage.replyToForumTopic != null) { draftThreadId = replyMessage.replyToForumTopic.id; } else { - draftThreadId = MessageObject.getTopicId(replyMessage.messageOwner, ChatObject.isForum(currentChat)); + draftThreadId = MessageObject.getTopicId(currentAccount, replyMessage.messageOwner, ChatObject.isForum(currentChat)); } } else { draftThreadId = threadMessageId; @@ -23945,9 +24376,9 @@ public void applyDraftMaybe(boolean canClear) { return; } TLRPC.DraftMessage draftMessage = null; - Integer topicId = null; + Long topicId = null; if (isForumInViewAsMessagesMode()) { - Pair pair = getMediaDataController().getOneThreadDraft(dialog_id); + Pair pair = getMediaDataController().getOneThreadDraft(dialog_id); if (pair != null) { topicId = pair.first; draftMessage = pair.second; @@ -24203,6 +24634,9 @@ public boolean maybePlayVisibleVideo() { ChatMessageCell messageCell = (ChatMessageCell) child; MessageObject messageObject = messageCell.getMessageObject(); boolean isRoundVideo = messageObject.isRoundVideo(); + if (messageObject.isRoundOnce() || messageObject.isVoiceOnce()) { + continue; + } if ((!messageObject.isVideo() && !isRoundVideo) || messageObject.videoEditedInfo != null) { continue; } @@ -24294,7 +24728,7 @@ private void createDeleteMessagesAlert(final MessageObject finalSelectedObject, if (finalSelectedObject == null && (selectedMessagesIds[0].size() + selectedMessagesIds[1].size()) == 0) { return; } - AlertsCreator.createDeleteMessagesAlert(this, currentUser, currentChat, currentEncryptedChat, chatInfo, mergeDialogId, finalSelectedObject, selectedMessagesIds, finalSelectedGroup, chatMode == MODE_SCHEDULED, loadParticipant, () -> { + AlertsCreator.createDeleteMessagesAlert(this, currentUser, currentChat, currentEncryptedChat, chatInfo, mergeDialogId, finalSelectedObject, selectedMessagesIds, finalSelectedGroup, chatMode == MODE_SCHEDULED, chatMode == MODE_SAVED, loadParticipant, () -> { hideActionMode(); updatePinnedMessageView(true); }, hideDimAfter ? () -> dimBehindView(false) : null, themeDelegate); @@ -24491,7 +24925,9 @@ private boolean createMenu(View v, boolean single, boolean listView, float x, fl boolean allowChatActions = true; boolean allowPin; - if (chatMode == MODE_SCHEDULED || (isThreadChat() && !isTopic)) { + if (chatMode == MODE_SAVED) { + allowPin = false; + } else if (chatMode == MODE_SCHEDULED || (isThreadChat() && !isTopic)) { allowPin = false; } else if (currentChat != null) { allowPin = message.getDialogId() != mergeDialogId && ChatObject.canPinMessages(currentChat); @@ -24527,7 +24963,7 @@ private boolean createMenu(View v, boolean single, boolean listView, float x, fl 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 && !(bottomOverlayChatWaitsReply && selectedObject != null && (MessageObject.getTopicId(selectedObject.messageOwner, ChatObject.isForum(currentChat)) != 0 || selectedObject.wasJustSent)) || + bottomOverlayChat != null && bottomOverlayChat.getVisibility() == View.VISIBLE && !(bottomOverlayChatWaitsReply && selectedObject != null && (MessageObject.getTopicId(currentAccount, selectedObject.messageOwner, ChatObject.isForum(currentChat)) != 0 || selectedObject.wasJustSent)) || currentChat != null && (ChatObject.isNotInChat(currentChat) && !isThreadChat() || ChatObject.isChannel(currentChat) && !ChatObject.canPost(currentChat) && !currentChat.megagroup || !ChatObject.canSendMessages(currentChat))) { allowChatActions = false; } @@ -24541,13 +24977,13 @@ private boolean createMenu(View v, boolean single, boolean listView, float x, fl final ArrayList options = new ArrayList<>(); View optionsView = null; - if (AndroidUtilities.isAccessibilityScreenReaderEnabled() && message.messageOwner != null && message.messageOwner.from_id != null && message.messageOwner.from_id.user_id != getUserConfig().clientUserId) { + if (AndroidUtilities.isAccessibilityScreenReaderEnabled() && message.messageOwner != null && message.messageOwner.from_id != null && message.messageOwner.from_id.user_id != getUserConfig().clientUserId && chatMode != MODE_SAVED) { items.add(LocaleController.getString(R.string.OpenProfile)); options.add(OPTION_OPEN_PROFILE); icons.add(R.drawable.msg_user_search); } - if (!getUserConfig().isPremium() && !getMessagesController().premiumFeaturesBlocked() && message.getDocument() != null && message.getDocument().size >= 150 * 1024 * 1024 && FileLoader.getInstance(currentAccount).isLoadingFile(FileLoader.getAttachFileName(message.getDocument()))) { + if (!getUserConfig().isPremium() && !getMessagesController().premiumFeaturesBlocked() && message.getDocument() != null && message.getDocument().size >= 150 * 1024 * 1024 && FileLoader.getInstance(currentAccount).isLoadingFile(FileLoader.getAttachFileName(message.getDocument())) && chatMode != MODE_SAVED) { items.add(LocaleController.getString(R.string.PremiumSpeedPromo)); options.add(OPTION_SPEED_PROMO); icons.add(R.drawable.msg_speed); @@ -24644,7 +25080,7 @@ public void setAutoDeleteHistory(int time, int action) { icons.add(selectedObject.messageOwner.ttl_period != 0 ? R.drawable.msg_delete_auto : R.drawable.msg_delete); } else if (type == 1) { if (currentChat != null) { - if (allowChatActions) { + if (allowChatActions && !isInsideContainer) { items.add(LocaleController.getString("Reply", R.string.Reply)); options.add(OPTION_REPLY); icons.add(R.drawable.msg_reply); @@ -24684,7 +25120,7 @@ public void setAutoDeleteHistory(int time, int action) { icons.add(R.drawable.msg_report); } } else { - if (selectedObject.getId() > 0 && allowChatActions) { + if (selectedObject.getId() > 0 && allowChatActions && !isInsideContainer) { items.add(LocaleController.getString("Reply", R.string.Reply)); options.add(OPTION_REPLY); icons.add(R.drawable.msg_reply); @@ -24725,7 +25161,7 @@ public void setAutoDeleteHistory(int time, int action) { icons.add(R.drawable.msg_fave); } } - if (allowChatActions || !noforwards && ChatObject.isChannelAndNotMegaGroup(currentChat) && !selectedObject.isSponsored() && selectedObject.contentType == 0 && chatMode == MODE_DEFAULT) { + if ((allowChatActions || !noforwards && ChatObject.isChannelAndNotMegaGroup(currentChat) && !selectedObject.isSponsored() && selectedObject.contentType == 0 && chatMode == MODE_DEFAULT) && !isInsideContainer) { items.add(LocaleController.getString("Reply", R.string.Reply)); options.add(OPTION_REPLY); icons.add(R.drawable.msg_reply); @@ -24969,7 +25405,7 @@ public void setAutoDeleteHistory(int time, int action) { icons.add(selectedObject.messageOwner.ttl_period != 0 ? R.drawable.msg_delete_auto : R.drawable.msg_delete); } } else { - if (allowChatActions) { + if (allowChatActions && !isInsideContainer) { items.add(LocaleController.getString("Reply", R.string.Reply)); options.add(OPTION_REPLY); icons.add(R.drawable.msg_reply); @@ -25090,8 +25526,13 @@ public void setAutoDeleteHistory(int time, int action) { } else { isReactionsAvailable = !message.isSecretMedia() && !isSecretChat() && !isInScheduleMode() && message.isReactionsAvailable() && (chatInfo != null && !(chatInfo.available_reactions instanceof TLRPC.TL_chatReactionsNone) || (chatInfo == null && !ChatObject.isChannel(currentChat)) || currentUser != null) && !availableReacts.isEmpty(); } - boolean showMessageSeen = !isReactionsViewAvailable && !isInScheduleMode() && currentChat != null && message.isOutOwner() && message.isSent() && !message.isEditing() && !message.isSending() && !message.isSendError() && !message.isContentUnread() && !message.isUnread() && (ConnectionsManager.getInstance(currentAccount).getCurrentTime() - message.messageOwner.date < getMessagesController().chatReadMarkExpirePeriod) && (ChatObject.isMegagroup(currentChat) || !ChatObject.isChannel(currentChat)) && chatInfo != null && chatInfo.participants_count <= getMessagesController().chatReadMarkSizeThreshold && !(message.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest) && (v instanceof ChatMessageCell); + boolean showMessageSeen = !isReactionsViewAvailable && !isInScheduleMode() && currentChat != null && message.isOutOwner() && message.isSent() && !message.isEditing() && !message.isSending() && !message.isSendError() && !message.isContentUnread() && !message.isUnread() && (ConnectionsManager.getInstance(currentAccount).getCurrentTime() - message.messageOwner.date < getMessagesController().chatReadMarkExpirePeriod) && (ChatObject.isMegagroup(currentChat) || !ChatObject.isChannel(currentChat)) && chatInfo != null && chatInfo.participants_count <= getMessagesController().chatReadMarkSizeThreshold && !(message.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest) && (v instanceof ChatMessageCell); + boolean showPrivateMessageSeen = !isReactionsViewAvailable && currentChat == null && currentEncryptedChat == null && (currentUser != null && !UserObject.isUserSelf(currentUser) && !UserObject.isReplyUser(currentUser) && !UserObject.isAnonymous(currentUser) && !currentUser.bot && !UserObject.isService(currentUser.id)) && (userInfo == null || !userInfo.read_dates_private) && !isInScheduleMode() && message.isOutOwner() && message.isSent() && !message.isEditing() && !message.isSending() && !message.isSendError() && !message.isContentUnread() && !message.isUnread() && (ConnectionsManager.getInstance(currentAccount).getCurrentTime() - message.messageOwner.date < getMessagesController().pmReadDateExpirePeriod) && !(message.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByRequest) && (v instanceof ChatMessageCell); boolean showSponsorInfo = selectedObject != null && selectedObject.isSponsored() && (selectedObject.sponsoredInfo != null || selectedObject.sponsoredAdditionalInfo != null || selectedObject.sponsoredWebPage != null || selectedObject.sponsoredBotApp != null); + if (chatMode == MODE_SAVED) { + showMessageSeen = false; + } + final boolean isReactionsAvailableFinal = isReactionsAvailable; int flags = 0; if (isReactionsViewAvailable || showMessageSeen || showSponsorInfo) { @@ -25435,6 +25876,15 @@ public void onClick(View view) { popupLayout.addView(messageSeenLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 44)); addGap = true; + } else if (showPrivateMessageSeen) { + MessagePrivateSeenView messagePrivateSeenView = new MessagePrivateSeenView(getContext(), message, () -> { + if (scrimPopupWindow != null) { + scrimPopupWindow.dismiss(false); + scrimPopupWindow = null; + } + }, themeDelegate); + popupLayout.addView(messagePrivateSeenView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 36)); + addGap = true; } boolean showRateTranscription = selectedObject != null && selectedObject.isVoice() && selectedObject.messageOwner != null && getUserConfig().isPremium() && !TextUtils.isEmpty(selectedObject.messageOwner.voiceTranscription) && selectedObject.messageOwner != null && !selectedObject.messageOwner.voiceTranscriptionRated && selectedObject.messageOwner.voiceTranscriptionId != 0 && selectedObject.messageOwner.voiceTranscriptionOpen; @@ -25940,7 +26390,7 @@ public void onSwipeBackProgress(PopupSwipeBackLayout layout, float toProgress, f } } - boolean showNoForwards = noforwards && message.messageOwner.action == null && message.isSent() && !message.isEditing() && chatMode != MODE_SCHEDULED; + boolean showNoForwards = noforwards && message.messageOwner.action == null && message.isSent() && !message.isEditing() && chatMode != MODE_SCHEDULED && chatMode != MODE_SAVED; scrimPopupContainerLayout.addView(popupLayout, LayoutHelper.createLinearRelatively(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT, isReactionsAvailable ? 16 : 0, 0, isReactionsAvailable ? 36 : 0, 0)); scrimPopupContainerLayout.setPopupWindowLayout(popupLayout); if (showNoForwards) { @@ -26113,7 +26563,7 @@ public void dismiss(boolean animated) { return; } scrimPopupWindow.showAtLocation(chatListView, Gravity.LEFT | Gravity.TOP, finalPopupX, finalPopupY); - if (isReactionsAvailable && finalReactionsLayout != null) { + if (isReactionsAvailableFinal && finalReactionsLayout != null) { finalReactionsLayout.startEnterAnimation(true); } AndroidUtilities.runOnUIThread(() -> { @@ -26227,14 +26677,15 @@ private void createEmptyView() { contentView.addView(emptyViewContainer, 1, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); int distance = getArguments().getInt("nearby_distance", -1); - if ((distance >= 0 || preloadedGreetingsSticker != null) && currentUser != null && !userBlocked) { + if ((distance >= 0 || preloadedGreetingsSticker != null) && currentUser != null && !userBlocked || userInfo != null && userInfo.contact_require_premium && !getUserConfig().isPremium()) { greetingsViewContainer = new ChatGreetingsView(getContext(), currentUser, distance, currentAccount, preloadedGreetingsSticker, themeDelegate); + greetingsViewContainer.setPremiumLock(userInfo != null && userInfo.contact_require_premium && !getUserConfig().isPremium(), dialog_id); greetingsViewContainer.setListener((sticker) -> { animatingDocuments.put(sticker, 0); SendMessagesHelper.getInstance(currentAccount).sendSticker(sticker, null, dialog_id, null, null, null, replyingQuote, null, true, 0, false, null); }); greetingsViewContainer.setBackground(Theme.createServiceDrawable(AndroidUtilities.dp(10), greetingsViewContainer, contentView, getThemedPaint(Theme.key_paint_chatActionBackground))); - emptyViewContainer.addView(greetingsViewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 68, 0, 68, 0)); + emptyViewContainer.addView(greetingsViewContainer, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); } else if (currentEncryptedChat == null) { if (isTopic && chatMode == 0) { CreateTopicEmptyView createTopicEmptyView = new CreateTopicEmptyView(getContext(), contentView, themeDelegate); @@ -26262,12 +26713,13 @@ private void createEmptyView() { } if (emptyMessage == null) { greetingsViewContainer = new ChatGreetingsView(getContext(), currentUser, distance, currentAccount, preloadedGreetingsSticker, themeDelegate); + greetingsViewContainer.setPremiumLock(userInfo != null && userInfo.contact_require_premium && !getUserConfig().isPremium(), dialog_id); greetingsViewContainer.setListener((sticker) -> { animatingDocuments.put(sticker, 0); SendMessagesHelper.getInstance(currentAccount).sendSticker(sticker, null, dialog_id, null, null, null, replyingQuote, null, true, 0, false, null); }); - greetingsViewContainer.setBackground(Theme.createServiceDrawable(AndroidUtilities.dp(10), greetingsViewContainer, contentView, getThemedPaint(Theme.key_paint_chatActionBackground))); - emptyViewContainer.addView(greetingsViewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 68, 0, 68, 0)); + greetingsViewContainer.setBackground(Theme.createServiceDrawable(AndroidUtilities.dp(16), greetingsViewContainer, contentView, getThemedPaint(Theme.key_paint_chatActionBackground))); + emptyViewContainer.addView(greetingsViewContainer, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); } else { emptyView = new TextView(getContext()); emptyView.setText(emptyMessage); @@ -27283,7 +27735,7 @@ private void processSelectedOption(int option) { break; } case OPTION_VIEW_IN_TOPIC: { - int topicId = MessageObject.getTopicId(selectedObject.messageOwner, true); + long topicId = MessageObject.getTopicId(currentAccount, selectedObject.messageOwner, true); if (topicId != 0) { TLRPC.TL_forumTopic topic = getMessagesController().getTopicsController().findTopic(currentChat.id, topicId); if (topic != null) { @@ -27314,7 +27766,7 @@ private void processSelectedOption(int option) { if (error == null) { TLRPC.Updates updates = (TLRPC.Updates) response; getMessagesController().processUpdates(updates, false); - AndroidUtilities.runOnUIThread(() -> NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.messagesDeleted, req.id, -dialog_id, true, dialog_id)); + AndroidUtilities.runOnUIThread(() -> NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.messagesDeleted, req.id, getUserConfig().getClientUserId() == dialog_id ? 0 : -dialog_id, true, true)); } else if (error.text != null) { AndroidUtilities.runOnUIThread(() -> { if (error.text.startsWith("SLOWMODE_WAIT_")) { @@ -27599,6 +28051,9 @@ public boolean onBackPressed() { parentLayout.addFragmentToStack(backToPreviousFragment, parentLayout.getFragmentStack().size() - 1); backToPreviousFragment = null; } + if (instantCameraView != null) { + instantCameraView.cancel(false); + } return true; } @@ -27624,6 +28079,10 @@ public void onListItemAnimatorTick() { fragmentView.invalidate(); } } + + public void setSavedDialog(long savedDialogId) { + threadMessageId = savedDialogId; + } public void setThreadMessages(ArrayList messageObjects, TLRPC.Chat originalChat, int originalMessage, int maxInboxReadId, int maxOutboxReadId, TLRPC.TL_forumTopic forumTopic) { this.forumTopic = forumTopic; @@ -27835,7 +28294,7 @@ private void openSearchWithText(String text) { if (editTextItem != null) { editTextItem.setVisibility(View.GONE); } - if (threadMessageId == 0 && searchItem != null) { + if ((threadMessageId == 0 || chatMode == MODE_SAVED) && searchItem != null) { searchItem.setVisibility(View.VISIBLE); } if (searchIconItem != null && showSearchAsIcon) { @@ -27848,7 +28307,7 @@ private void openSearchWithText(String text) { updateSearchButtons(0, 0, -1); updateBottomOverlay(); } - if ((threadMessageId == 0 || isTopic) && !UserObject.isReplyUser(currentUser)) { + if ((threadMessageId == 0 || isTopic || chatMode == MODE_SAVED) && !UserObject.isReplyUser(currentUser)) { openSearchKeyboard = text == null; if (searchItem != null) { searchItem.openSearch(openSearchKeyboard); @@ -27858,7 +28317,7 @@ private void openSearchWithText(String text) { if (searchItem != null) { searchItem.setSearchFieldText(text, false); } - getMediaDataController().searchMessagesInChat(text, dialog_id, mergeDialogId, classGuid, 0, threadMessageId, searchingUserMessages, searchingChatMessages); + getMediaDataController().searchMessagesInChat(text, dialog_id, mergeDialogId, classGuid, 0, threadMessageId, searchingUserMessages, searchingChatMessages, searchingReaction); } updatePinnedMessageView(true); } @@ -27916,7 +28375,7 @@ public ChatActivity.ReplyQuote getReplyQuote() { return replyingQuote; } - public int getThreadId() { + public long getThreadId() { return threadMessageId; } @@ -29059,6 +29518,10 @@ public class ChatActivityAdapter extends RecyclerAnimationScrollHelper.Animatabl public boolean isFrozen; public ArrayList frozenMessages = new ArrayList<>(); + public boolean isFiltered; + public boolean filteredEndReached; + public ArrayList filteredMessages = new ArrayList<>(); + public ChatActivityAdapter(Context context) { mContext = context; isBot = currentUser != null && currentUser.bot; @@ -29082,9 +29545,16 @@ public void updateRowsSafe() { private void updateRowsInternal() { rowCount = 0; - ArrayList messages = isFrozen ? frozenMessages : ChatActivity.this.messages; + final ArrayList messages; + if (isFrozen) { + messages = frozenMessages; + } else if (isFiltered) { + messages = filteredMessages; + } else { + messages = ChatActivity.this.messages; + } if (!messages.isEmpty()) { - if ((!forwardEndReached[0] || mergeDialogId != 0 && !forwardEndReached[1]) && !hideForwardEndReached) { + if (!isFiltered && (!forwardEndReached[0] || mergeDialogId != 0 && !forwardEndReached[1]) && !hideForwardEndReached) { loadingDownRow = rowCount++; } else { loadingDownRow = -5; @@ -29099,7 +29569,7 @@ private void updateRowsInternal() { botInfoRow = -5; } - if ((!endReached[0] || mergeDialogId != 0 && !endReached[1]) && !(DISABLE_PROGRESS_VIEW && !AndroidUtilities.isTablet() && !isComments && currentUser == null)) { + if (isFiltered ? !filteredEndReached : (!endReached[0] || mergeDialogId != 0 && !endReached[1]) && !(DISABLE_PROGRESS_VIEW && !AndroidUtilities.isTablet() && !isComments && currentUser == null)) { loadingUpRow = rowCount++; } else { loadingUpRow = -5; @@ -29128,7 +29598,7 @@ public int getItemCount() { } return 0; } - return clearingHistory ? 0 : rowCount; + return rowCount; } @Override @@ -29138,7 +29608,14 @@ public long getItemId(int position) { return 1; } } - ArrayList messages = isFrozen ? frozenMessages : ChatActivity.this.messages; + final ArrayList messages; + if (isFrozen) { + messages = frozenMessages; + } else if (isFiltered) { + messages = filteredMessages; + } else { + messages = ChatActivity.this.messages; + } if (position >= messagesStartRow && position < messagesEndRow) { return messages.get(position - messagesStartRow).stableId; @@ -29385,7 +29862,7 @@ public BaseFragment getBaseFragment() { } @Override - public int getTopicId() { + public long getTopicId() { return ChatActivity.this.getTopicId(); } @@ -29467,7 +29944,14 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ChatLoadingCell loadingCell = (ChatLoadingCell) holder.itemView; loadingCell.setProgressVisible(loadsCount > 1); } else if (position >= messagesStartRow && position < messagesEndRow) { - ArrayList messages = isFrozen ? frozenMessages : ChatActivity.this.messages; + final ArrayList messages; + if (isFrozen) { + messages = frozenMessages; + } else if (isFiltered) { + messages = filteredMessages; + } else { + messages = ChatActivity.this.messages; + } MessageObject message = messages.get(position - messagesStartRow); View view = holder.itemView; @@ -29476,6 +29960,8 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { final ChatMessageCell messageCell = (ChatMessageCell) view; MessageObject.GroupedMessages groupedMessages = getValidGroupedMessage(message); messageCell.isChat = currentChat != null || UserObject.isUserSelf(currentUser) || UserObject.isReplyUser(currentUser); + messageCell.isSavedChat = chatMode == MODE_SAVED; + messageCell.isSavedPreviewChat = chatMode == MODE_SAVED && isInsideContainer; messageCell.isBot = currentUser != null && currentUser.bot; messageCell.isMegagroup = ChatObject.isChannel(currentChat) && currentChat.megagroup; messageCell.isForum = ChatObject.isForum(currentChat); @@ -29501,14 +29987,22 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { nextPosition = position - groupedMessages.posArray.size() + groupedMessages.posArray.indexOf(pos); } else { if ((pos.flags & MessageObject.POSITION_FLAG_TOP) != 0) { - prevPosition = position + groupedMessages.posArray.indexOf(pos) + 1; + if (groupedMessages.reversed) { + prevPosition = position - groupedMessages.posArray.size() + groupedMessages.posArray.indexOf(pos); + } else { + prevPosition = position + groupedMessages.posArray.indexOf(pos) + 1; + } } else { pinnedTop = true; pinnedTopByGroup = true; prevPosition = -100; } if ((pos.flags & MessageObject.POSITION_FLAG_BOTTOM) != 0) { - nextPosition = position - groupedMessages.posArray.size() + groupedMessages.posArray.indexOf(pos); + if (groupedMessages.reversed) { + nextPosition = position + groupedMessages.posArray.indexOf(pos) + 1; + } else { + nextPosition = position - groupedMessages.posArray.size() + groupedMessages.posArray.indexOf(pos); + } } else { pinnedBottom = true; pinnedBottomByGroup = true; @@ -29588,8 +30082,8 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { pinnedTop = false; } if (pinnedTop && isForumInViewAsMessagesMode()) { - int topicId = message.replyToForumTopic == null ? MessageObject.getTopicId(message.messageOwner, true) : message.replyToForumTopic.id; - int prevTopicId = prevMessage.replyToForumTopic == null ? MessageObject.getTopicId(prevMessage.messageOwner, true) : prevMessage.replyToForumTopic.id; + long topicId = message.replyToForumTopic == null ? MessageObject.getTopicId(currentAccount, message.messageOwner, true) : message.replyToForumTopic.id; + long prevTopicId = prevMessage.replyToForumTopic == null ? MessageObject.getTopicId(currentAccount, prevMessage.messageOwner, true) : prevMessage.replyToForumTopic.id; if (topicId != prevTopicId) { pinnedTop = false; } @@ -29618,6 +30112,16 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { groupedMessages.messages.get(i).updateTranslation(false); } } + if (reversed) { + if (groupedMessages != null) { + pinnedTop = false; + pinnedBottom = false; + } else { + boolean wasPinnedTop = pinnedTop; + pinnedTop = pinnedBottom; + pinnedBottom = wasPinnedTop; + } + } messageCell.setMessageObject(message, groupedMessages, pinnedBottom, pinnedTop); messageCell.setSpoilersSuppressed(chatListView.getScrollState() != RecyclerView.SCROLL_STATE_IDLE); messageCell.setHighlighted(highlightMessageId != Integer.MAX_VALUE && message.getId() == highlightMessageId); @@ -29680,8 +30184,8 @@ public boolean onPreDraw() { o.setInterpolator(CubicBezierInterpolator.DEFAULT); allAnimators.playTogether(o, animatorSet); - allAnimators.setStartDelay(120); - allAnimators.setDuration(180); +// allAnimators.setStartDelay(120); + allAnimators.setDuration(300); if (instantCameraView != null) { instantCameraView.setIsMessageTransition(true); @@ -29855,6 +30359,10 @@ public Float get(MessageObject.SendAnimationData object) { chatListItemAnimator.onGreetingStickerTransition(holder, greetingsViewContainer); } } + if (messageCell.makeVisibleAfterChange) { + messageCell.makeVisibleAfterChange = false; + messageCell.setVisibility(View.VISIBLE); + } } else if (view instanceof ChatActionCell) { ChatActionCell actionCell = (ChatActionCell) view; actionCell.setMessageObject(message); @@ -29878,7 +30386,14 @@ public int getItemViewType(int position) { } } if (position >= messagesStartRow && position < messagesEndRow) { - ArrayList messages = isFrozen ? frozenMessages : ChatActivity.this.messages; + final ArrayList messages; + if (isFrozen) { + messages = frozenMessages; + } else if (isFiltered) { + messages = filteredMessages; + } else { + messages = ChatActivity.this.messages; + } return messages.get(position - messagesStartRow).contentType; } else if (position == botInfoRow) { return 3; @@ -29961,7 +30476,14 @@ public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { int position = holder.getAdapterPosition(); if (position >= messagesStartRow && position < messagesEndRow) { - ArrayList messages = isFrozen ? frozenMessages : ChatActivity.this.messages; + final ArrayList messages; + if (isFrozen) { + messages = frozenMessages; + } else if (isFiltered) { + messages = filteredMessages; + } else { + messages = ChatActivity.this.messages; + } MessageObject message = messages.get(position - messagesStartRow); View view = holder.itemView; @@ -29993,7 +30515,7 @@ public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { } public void updateRowAtPosition(int index) { - if (chatLayoutManager == null || isFrozen) { + if (chatLayoutManager == null || isFrozen || isFiltered) { return; } int lastVisibleItem = RecyclerView.NO_POSITION; @@ -30047,7 +30569,14 @@ public View updateRowWithMessageObject(MessageObject messageObject, boolean allo } } } - ArrayList messages = isFrozen ? frozenMessages : ChatActivity.this.messages; + final ArrayList messages; + if (isFrozen) { + messages = frozenMessages; + } else if (isFiltered) { + messages = filteredMessages; + } else { + messages = ChatActivity.this.messages; + } int index = messages.indexOf(messageObject); if (index == -1) { @@ -30260,6 +30789,43 @@ public boolean isEnabled(RecyclerView.ViewHolder holder) { } } + private float actionBarTagsT; + private ValueAnimator actionBarTagsAnimator; + private void showActionBarSearchTags(boolean show) { + if (actionBarSearchTags == null) return; + if (actionBarTagsAnimator != null) { + actionBarTagsAnimator.cancel(); + } + actionBarSearchTags.setVisibility(View.VISIBLE); + actionBarTagsAnimator = ValueAnimator.ofFloat(actionBarTagsT, show ? 1f : 0f); + actionBarTagsAnimator.addUpdateListener(valueAnimator1 -> { + actionBarTagsT = (float) valueAnimator1.getAnimatedValue(); + if (actionBarSearchTags != null) { + actionBarSearchTags.setBackgroundColor(actionBar.getBackgroundColor()); + actionBarSearchTags.setShown(actionBarTagsT); + } + }); + actionBarTagsAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + actionBarTagsAnimator.setDuration(320); + actionBarTagsAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + actionBarTagsT = show ? 1f : 0f; + if (actionBarSearchTags != null) { + actionBarSearchTags.setBackgroundColor(actionBar.getBackgroundColor()); + actionBarSearchTags.setShown(actionBarTagsT); + if (!show) { + actionBarSearchTags.setVisibility(View.GONE); + } + } + + invalidateChatListViewTopPadding = true; + updateChatListViewTopPadding(); + } + }); + actionBarTagsAnimator.start(); + } + private class SearchItemListener extends ActionBarMenuItem.ActionBarMenuItemSearchListener { boolean searchWas; @@ -30273,6 +30839,17 @@ public boolean canCollapseSearch() { return true; } + @Override + public void onSearchFilterCleared(FiltersView.MediaFilterData filterData) { + super.onSearchFilterCleared(filterData); + if (actionBarSearchTags != null) { + actionBarSearchTags.clear(); + } + searchingReaction = null; + showSearchShowOther(false); + setFilterMessages(false); + } + @Override public void onSearchCollapse() { if (searchCalendarButton != null) { @@ -30342,7 +30919,7 @@ public void onSearchCollapse() { attachItem.setVisibility(View.GONE); } } - if (threadMessageId == 0 && !UserObject.isReplyUser(currentUser) || threadMessageObject != null && threadMessageObject.getRepliesCount() < 10) { + if (chatMode == MODE_SAVED || threadMessageId == 0 && !UserObject.isReplyUser(currentUser) || threadMessageObject != null && threadMessageObject.getRepliesCount() < 10) { searchItem.setVisibility(View.GONE); } searchItemVisible = false; @@ -30354,6 +30931,21 @@ public void onSearchCollapse() { updateBottomOverlay(); updatePinnedMessageView(true); updateVisibleRows(); + + whiteActionBar = getDialogId() == getUserConfig().getClientUserId(); + ValueAnimator valueAnimator = ValueAnimator.ofFloat(searchAnimationProgress, 0f); + valueAnimator.addUpdateListener(valueAnimator1 -> setSearchAnimationProgress((float) valueAnimator1.getAnimatedValue())); + valueAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + valueAnimator.setDuration(320); + valueAnimator.start(); + + searchingReaction = null; + showSearchShowOther(false); + if (actionBarSearchTags != null) { + actionBarSearchTags.clear(); + } + showActionBarSearchTags(false); + setFilterMessages(false); } @Override @@ -30373,13 +30965,46 @@ public void onSearchExpand() { removeKeyboardPositionBeforeTransition(); }, 500); hideSendButtonHints(); + + whiteActionBar = getDialogId() == getUserConfig().getClientUserId(); + ValueAnimator valueAnimator = ValueAnimator.ofFloat(searchAnimationProgress, 1f); + valueAnimator.addUpdateListener(valueAnimator1 -> setSearchAnimationProgress((float) valueAnimator1.getAnimatedValue())); + valueAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + valueAnimator.setDuration(320); + valueAnimator.start(); +// showActionBarSearchTags(actionBarSearchTags != null && actionBarSearchTags.hasFilters()); + } + + private float searchAnimationProgress; + private boolean whiteActionBar; + + public void setSearchAnimationProgress(float progress) { + searchAnimationProgress = progress; + if (whiteActionBar) { + int color1 = getThemedColor(Theme.key_actionBarDefaultIcon); + actionBar.setItemsColor(ColorUtils.blendARGB(color1, getThemedColor(Theme.key_actionBarActionModeDefaultIcon), searchAnimationProgress), false); + actionBar.setItemsColor(ColorUtils.blendARGB(getThemedColor(Theme.key_actionBarActionModeDefaultIcon), getThemedColor(Theme.key_actionBarActionModeDefaultIcon), searchAnimationProgress), true); + + color1 = getThemedColor(Theme.key_actionBarDefaultSelector); + int color2 = getThemedColor(Theme.key_actionBarActionModeDefaultSelector); + actionBar.setItemsBackgroundColor(ColorUtils.blendARGB(color1, color2, searchAnimationProgress), false); + + actionBar.setBackgroundColor(ColorUtils.blendARGB(getThemedColor(Theme.key_actionBarDefault), getThemedColor(Theme.key_actionBarActionModeDefault), searchAnimationProgress)); + actionBar.setSearchTextColor(ColorUtils.blendARGB(Theme.getColor(Theme.key_actionBarDefaultSearch), getThemedColor(Theme.key_windowBackgroundWhiteBlackText), searchAnimationProgress), false); + actionBar.setSearchTextColor(ColorUtils.blendARGB(Theme.getColor(Theme.key_actionBarDefaultSearchPlaceholder), getThemedColor(Theme.key_windowBackgroundWhiteGrayText), searchAnimationProgress), true); + actionBar.setSearchCursorColor(ColorUtils.blendARGB(Theme.getColor(Theme.key_actionBarDefaultSearch), getThemedColor(Theme.key_windowBackgroundWhiteBlueText), searchAnimationProgress)); + AndroidUtilities.setLightStatusBar(fragmentView, isLightStatusBar()); + } + if (fragmentView != null) { + fragmentView.invalidate(); + } } @Override public void onSearchPressed(EditText editText) { searchWas = true; updateSearchButtons(0, 0, -1); - getMediaDataController().searchMessagesInChat(editText.getText().toString(), dialog_id, mergeDialogId, classGuid, 0, threadMessageId, searchingUserMessages, searchingChatMessages); + getMediaDataController().searchMessagesInChat(editText.getText().toString(), dialog_id, mergeDialogId, classGuid, 0, threadMessageId, searchingUserMessages, searchingChatMessages, searchingReaction); } @Override @@ -30510,7 +31135,7 @@ public void didPressSideButton(ChatMessageCell cell) { if (chatMode == MODE_PINNED) { chatActivityDelegate.openReplyMessage(messageObject.getId()); finishFragment(); - } else if ((UserObject.isReplyUser(currentUser) || UserObject.isUserSelf(currentUser)) && messageObject.messageOwner.fwd_from.saved_from_peer != null) { + } else if (chatMode == MODE_SAVED || (UserObject.isReplyUser(currentUser) || UserObject.isUserSelf(currentUser)) && messageObject.messageOwner.fwd_from != null && messageObject.messageOwner.fwd_from.saved_from_peer != null) { if (UserObject.isReplyUser(currentUser) && messageObject.messageOwner.reply_to != null && messageObject.messageOwner.reply_to.reply_to_top_id != 0) { openDiscussionMessageChat(messageObject.messageOwner.reply_to.reply_to_peer_id.channel_id, null, messageObject.messageOwner.reply_to.reply_to_top_id, 0, -1, messageObject.messageOwner.fwd_from.saved_from_msg_id, messageObject); } else { @@ -30604,7 +31229,7 @@ protected void onSend(LongSparseArray dids, int count, TLRPC.TL_fo @Override public boolean needPlayMessage(ChatMessageCell cell, MessageObject messageObject, boolean muted) { - if (messageObject.isVoiceOnce()) { + if (messageObject.isVoiceOnce() || messageObject.isRoundOnce()) { if (secretVoicePlayer != null && secretVoicePlayer.isShown()) return false; try { AudioManager audioManager = (AudioManager) ApplicationLoader.applicationContext.getSystemService(Context.AUDIO_SERVICE); @@ -31353,7 +31978,7 @@ public boolean didPressAnimatedEmoji(ChatMessageCell cell, AnimatedEmojiSpan spa public void didPressTopicButton(ChatMessageCell cell) { MessageObject message = cell.getMessageObject(); if (message != null) { - int topicId = MessageObject.getTopicId(message.messageOwner, true); + long topicId = MessageObject.getTopicId(message.currentAccount, message.messageOwner, true); if (topicId != 0) { TLRPC.TL_forumTopic topic = getMessagesController().getTopicsController().findTopic(currentChat.id, topicId); if (topic != null) { @@ -31758,13 +32383,47 @@ public void end(boolean replaced) { ArticleViewer.getInstance().setParentActivity(getParentActivity(), ChatActivity.this); ArticleViewer.getInstance().open(messageObject); } - } else if (type == 5) { + } else if (type == ChatMessageCell.INSTANT_BUTTON_TYPE_CONTACT_VIEW) { long uid = messageObject.messageOwner.media.user_id; TLRPC.User user = null; if (uid != 0) { user = MessagesController.getInstance(currentAccount).getUser(uid); } openVCard(user, messageObject.messageOwner.media.phone_number, messageObject.messageOwner.media.vcard, messageObject.messageOwner.media.first_name, messageObject.messageOwner.media.last_name); + } else if (type == ChatMessageCell.INSTANT_BUTTON_TYPE_CONTACT_SEND_MESSAGE) { + long uid = messageObject.messageOwner.media.user_id; + Bundle args = new Bundle(); + args.putLong("user_id", uid); + presentFragment(new ChatActivity(args)); + } else if (type == ChatMessageCell.INSTANT_BUTTON_TYPE_CONTACT_ADD) { + long uid = messageObject.messageOwner.media.user_id; + TLRPC.User user = null; + if (uid != 0) { + user = MessagesController.getInstance(currentAccount).getUser(uid); + } + if (user != null) { + String phone; + if (!TextUtils.isEmpty(messageObject.vCardData)) { + phone = messageObject.vCardData.toString(); + } else { + if (!TextUtils.isEmpty(user.phone)) { + phone = PhoneFormat.getInstance().format("+" + user.phone); + } else { + phone = MessageObject.getMedia(messageObject.messageOwner).phone_number; + if (!TextUtils.isEmpty(phone)) { + phone = PhoneFormat.getInstance().format((String) phone); + } else { + phone = LocaleController.getString("NumberUnknown", R.string.NumberUnknown); + } + } + } + + Bundle args = new Bundle(); + args.putLong("user_id", user.id); + args.putString("phone", phone); + args.putBoolean("addContact", true); + presentFragment(new ContactAddActivity(args)); + } } else { if (messageObject.isSponsored()) { logSponsoredClicked(messageObject); @@ -33999,7 +34658,7 @@ public boolean isLightStatusBar() { } return ColorUtils.calculateLuminance(color) > 0.7f; } - return super.isLightStatusBar(); + return AndroidUtilities.computePerceivedBrightness(actionBar.getBackgroundColor()) > 0.721f; } public MessageObject.GroupedMessages getGroup(long id) { @@ -34056,7 +34715,7 @@ private void updateVisibleWallpaperActions() { } private void checkLeaveChannelButton() { - if (headerItem == null) return; + if (headerItem == null || chatMode == MODE_SAVED) return; if (!headerItem.hasSubItem(delete_chat)) { if (!isTopic) { if (ChatObject.isChannel(currentChat) && !currentChat.creator) { @@ -34081,6 +34740,9 @@ private void checkLeaveChannelButton() { } public boolean supportsThanosEffect() { + if (chatListThanosEffect != null && chatListThanosEffect.destroyed) { + return false; + } return ThanosEffect.supports() && LiteMode.isEnabled(LiteMode.FLAG_CHAT_THANOS); } @@ -34093,6 +34755,9 @@ public ThanosEffect getChatThanosEffect() { return null; } chatListThanosEffect = new ThanosEffect(getContext(), () -> { + if (removingFromParent) { + return; + } ThanosEffect thisThanosEffect = chatListThanosEffect; if (thisThanosEffect != null) { chatListThanosEffect = null; @@ -34103,4 +34768,47 @@ public ThanosEffect getChatThanosEffect() { } return chatListThanosEffect; } + + private void checkGroupMessagesOrder() { + if (!reversed) return; + int groupStart = -1; + long thisGroupId = 0; + for (int i = 0; i < messages.size(); ++i) { + MessageObject msg = messages.get(i); + long msgGroupId = msg.getGroupIdForUse(); + if (thisGroupId != msgGroupId) { + if (groupStart >= 0 && thisGroupId != 0 && i - groupStart > 1) { + // thisGroup from groupStart to (i - 1) + int count = i - groupStart; + ArrayList groupMessages = new ArrayList<>(); + for (int a = 0; a < count; ++a) { + groupMessages.add(messages.remove(groupStart)); + } + Collections.sort(groupMessages, (a, b) -> b.getId() - a.getId()); + messages.addAll(groupStart, groupMessages); + } + groupStart = i; + thisGroupId = msgGroupId; + } + } + if (groupStart >= 0 && thisGroupId != 0 && messages.size() - groupStart > 1) { + // thisGroup from groupStart to (messages.size() - 1) + int count = messages.size() - groupStart; + ArrayList groupMessages = new ArrayList<>(); + for (int a = 0; a < count; ++a) { + groupMessages.add(messages.remove(groupStart)); + } + messages.addAll(groupStart, groupMessages); + } + } + + private void invalidatePremiumBlocked() { + if (getUserConfig().isPremium()) + return; + if (currentUser == null || !currentUser.contact_require_premium) + return; + if (messages.isEmpty() == getMessagesController().isUserPremiumBlocked(getDialogId())) + return; + getMessagesController().invalidateUserPremiumBlocked(getDialogId(), classGuid); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivityContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivityContainer.java new file mode 100644 index 0000000000..531cecad6d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivityContainer.java @@ -0,0 +1,85 @@ +package org.telegram.ui; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.Menu; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.FrameLayout; + +import com.google.android.exoplayer2.util.Log; + +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.DrawerLayoutContainer; +import org.telegram.ui.ActionBar.INavigationLayout; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.BackButtonMenu; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.RecyclerListView; + +import java.util.List; + +public class ChatActivityContainer extends FrameLayout { + + public final ChatActivity chatActivity; + private final INavigationLayout parentLayout; + private View fragmentView; + + public ChatActivityContainer(Context context, INavigationLayout parentLayout, Bundle args) { + super(context); + this.parentLayout = parentLayout; + + chatActivity = new ChatActivity(args); + chatActivity.isInsideContainer = true; + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (!chatActivity.onFragmentCreate()) { + return; + } + + fragmentView = chatActivity.fragmentView; + chatActivity.setParentLayout(parentLayout); + if (fragmentView == null) { + fragmentView = chatActivity.createView(getContext()); + } else { + ViewGroup parent = (ViewGroup) fragmentView.getParent(); + if (parent != null) { + chatActivity.onRemoveFromParent(); + parent.removeView(fragmentView); + } + } + addView(fragmentView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + if (isActive) { + chatActivity.onResume(); + } + } + + private boolean isActive = true; + public void onPause() { + isActive = false; + if (fragmentView != null) { + chatActivity.onPause(); + } + } + + public void onResume() { + isActive = true; + if (fragmentView != null) { + chatActivity.onResume(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CodeNumberField.java b/TMessagesProj/src/main/java/org/telegram/ui/CodeNumberField.java index c2755a9df6..3bbd07ba0a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CodeNumberField.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CodeNumberField.java @@ -318,8 +318,13 @@ private void pasteFromClipboard() { if (clipboard == null) { return; } - clipboard.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN); - ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0); +// ClipDescription clipDescription = clipboard.getPrimaryClipDescription(); +// clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN); + ClipData clipData = clipboard.getPrimaryClip(); + if (clipData == null) { + return; + } + ClipData.Item item = clipData.getItemAt(0); int i = -1; String text = item.getText().toString(); try { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java index 5b7d35667d..ef3b2e35e4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AlertsCreator.java @@ -240,7 +240,24 @@ public static Dialog processError(int currentAccount, TLRPC.TL_error error, Base if (error == null || error.code == 406 || error.text == null) { return null; } - if (request instanceof TLRPC.TL_messages_initHistoryImport || request instanceof TLRPC.TL_messages_checkHistoryImportPeer || request instanceof TLRPC.TL_messages_checkHistoryImport || request instanceof TLRPC.TL_messages_startHistoryImport) { + if (request instanceof TLRPC.TL_messages_sendMessage && error.text.contains("PRIVACY_PREMIUM_REQUIRED")) { + TLRPC.TL_messages_sendMessage req = (TLRPC.TL_messages_sendMessage) request; + long dialogId = DialogObject.getPeerDialogId(req.peer); + String username = ""; + if (dialogId >= 0) { + username = UserObject.getFirstName(MessagesController.getInstance(currentAccount).getUser(dialogId)); + } else { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + if (chat != null) { + username = chat.title; + } + } + if (fragment == null) { + fragment = LaunchActivity.getLastFragment(); + } + showSimpleAlert(fragment, LocaleController.getString(R.string.MessagePremiumErrorTitle), LocaleController.formatString(R.string.MessagePremiumErrorMessage, username)); + MessagesController.getInstance(currentAccount).invalidateUserPremiumBlocked(dialogId, 0); + } else if (request instanceof TLRPC.TL_messages_initHistoryImport || request instanceof TLRPC.TL_messages_checkHistoryImportPeer || request instanceof TLRPC.TL_messages_checkHistoryImport || request instanceof TLRPC.TL_messages_startHistoryImport) { TLRPC.InputPeer peer; if (request instanceof TLRPC.TL_messages_initHistoryImport) { peer = ((TLRPC.TL_messages_initHistoryImport) request).peer; @@ -4094,7 +4111,7 @@ public CharSequence getAccessibilityClassName() { return builder; } - public static BottomSheet createMuteAlert(BaseFragment fragment, final long dialog_id, int topicId, Theme.ResourcesProvider resourcesProvider) { + public static BottomSheet createMuteAlert(BaseFragment fragment, final long dialog_id, long topicId, Theme.ResourcesProvider resourcesProvider) { if (fragment == null || fragment.getParentActivity() == null) { return null; } @@ -4615,7 +4632,7 @@ public static Dialog createColorSelectDialog(Activity parentActivity, final long return createColorSelectDialog(parentActivity, dialog_id, topicId, globalType, onSelect, null); } - public static Dialog createColorSelectDialog(Activity parentActivity, final long dialog_id, final int topicId, final int globalType, final Runnable onSelect, Theme.ResourcesProvider resourcesProvider) { + public static Dialog createColorSelectDialog(Activity parentActivity, final long dialog_id, final long topicId, final int globalType, final Runnable onSelect, Theme.ResourcesProvider resourcesProvider) { int currentColor; SharedPreferences preferences = MessagesController.getNotificationsSettings(UserConfig.selectedAccount); String key = NotificationsController.getSharedPrefKey(dialog_id, topicId); @@ -4725,11 +4742,11 @@ public static Dialog createColorSelectDialog(Activity parentActivity, final long return builder.create(); } - public static Dialog createVibrationSelectDialog(Activity parentActivity, final long dialogId, int topicId, final boolean globalGroup, final boolean globalAll, final Runnable onSelect) { + public static Dialog createVibrationSelectDialog(Activity parentActivity, final long dialogId, long topicId, final boolean globalGroup, final boolean globalAll, final Runnable onSelect) { return createVibrationSelectDialog(parentActivity, dialogId, topicId, globalGroup, globalAll, onSelect, null); } - public static Dialog createVibrationSelectDialog(Activity parentActivity, final long dialogId, int topicId, final boolean globalGroup, final boolean globalAll, final Runnable onSelect, Theme.ResourcesProvider resourcesProvider) { + public static Dialog createVibrationSelectDialog(Activity parentActivity, final long dialogId, long topicId, final boolean globalGroup, final boolean globalAll, final Runnable onSelect, Theme.ResourcesProvider resourcesProvider) { String prefix; if (dialogId != 0) { prefix = "vibrate_" + dialogId; @@ -4739,11 +4756,11 @@ public static Dialog createVibrationSelectDialog(Activity parentActivity, final return createVibrationSelectDialog(parentActivity, dialogId, topicId, prefix, onSelect, resourcesProvider); } - public static Dialog createVibrationSelectDialog(Activity parentActivity, final long dialogId, int topicId, final String prefKeyPrefix, final Runnable onSelect) { + public static Dialog createVibrationSelectDialog(Activity parentActivity, final long dialogId, long topicId, final String prefKeyPrefix, final Runnable onSelect) { return createVibrationSelectDialog(parentActivity, dialogId, topicId, prefKeyPrefix, onSelect, null); } - public static Dialog createVibrationSelectDialog(Activity parentActivity, final long dialogId, int topicId, final String prefKeyPrefix, final Runnable onSelect, Theme.ResourcesProvider resourcesProvider) { + public static Dialog createVibrationSelectDialog(Activity parentActivity, final long dialogId, long topicId, final String prefKeyPrefix, final Runnable onSelect, Theme.ResourcesProvider resourcesProvider) { SharedPreferences preferences = MessagesController.getNotificationsSettings(UserConfig.selectedAccount); final int[] selected = new int[1]; String[] descriptions; @@ -5092,7 +5109,7 @@ public static Dialog createPrioritySelectDialog(Activity parentActivity, final l return createPrioritySelectDialog(parentActivity, dialog_id, topicId, globalType, onSelect, null); } - public static Dialog createPrioritySelectDialog(Activity parentActivity, final long dialog_id, int topicId, final int globalType, final Runnable onSelect, Theme.ResourcesProvider resourcesProvider) { + public static Dialog createPrioritySelectDialog(Activity parentActivity, final long dialog_id, long topicId, final int globalType, final Runnable onSelect, Theme.ResourcesProvider resourcesProvider) { SharedPreferences preferences = MessagesController.getNotificationsSettings(UserConfig.selectedAccount); final int[] selected = new int[1]; String[] descriptions; @@ -5398,7 +5415,7 @@ public interface PaymentAlertDelegate { void didPressedNewCard(); } - public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User user, TLRPC.Chat chat, TLRPC.EncryptedChat encryptedChat, TLRPC.ChatFull chatInfo, long mergeDialogId, MessageObject selectedMessage, SparseArray[] selectedMessages, MessageObject.GroupedMessages selectedGroup, boolean scheduled, int loadParticipant, Runnable onDelete, Runnable hideDim, Theme.ResourcesProvider resourcesProvider) { + public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User user, TLRPC.Chat chat, TLRPC.EncryptedChat encryptedChat, TLRPC.ChatFull chatInfo, long mergeDialogId, MessageObject selectedMessage, SparseArray[] selectedMessages, MessageObject.GroupedMessages selectedGroup, boolean scheduled, boolean isSavedMessages, int loadParticipant, Runnable onDelete, Runnable hideDim, Theme.ResourcesProvider resourcesProvider) { if (fragment == null || user == null && chat == null && encryptedChat == null) { return; } @@ -5459,7 +5476,7 @@ public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User u boolean hasNotOut = false; int myMessagesCount = 0; boolean canDeleteInbox = encryptedChat == null && user != null && canRevokeInbox && revokeTimeLimit == 0x7fffffff; - if (chat != null && chat.megagroup && !scheduled) { + if (chat != null && chat.megagroup && !scheduled && !isSavedMessages) { boolean canBan = ChatObject.canBlockUsers(chat); if (selectedMessage != null) { if (selectedMessage.messageOwner.action == null || selectedMessage.messageOwner.action instanceof TLRPC.TL_messageActionEmpty || @@ -5535,7 +5552,7 @@ public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User u } else if (error != null && "USER_NOT_PARTICIPANT".equals(error.text)) { loadType = 0; } - createDeleteMessagesAlert(fragment, user, chat, encryptedChat, chatInfo, mergeDialogId, selectedMessage, selectedMessages, selectedGroup, scheduled, loadType, onDelete, hideDim, resourcesProvider); + createDeleteMessagesAlert(fragment, user, chat, encryptedChat, chatInfo, mergeDialogId, selectedMessage, selectedMessages, selectedGroup, scheduled, isSavedMessages, loadType, onDelete, hideDim, resourcesProvider); })); AndroidUtilities.runOnUIThread(() -> { if (progressDialog[0] == null) { @@ -5599,7 +5616,7 @@ public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User u } else { actionUser = null; } - } else if (!scheduled && !ChatObject.isChannel(chat) && encryptedChat == null) { + } else if (!scheduled && !isSavedMessages && !ChatObject.isChannel(chat) && encryptedChat == null) { if (user != null && user.id != UserConfig.getInstance(currentAccount).getClientUserId() && (!user.bot || user.support) || chat != null) { if (selectedMessage != null) { boolean hasOutgoing = !selectedMessage.isSendError() && ( @@ -5664,6 +5681,10 @@ public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User u DialogInterface.OnClickListener deleteAction = (dialogInterface, i) -> { ArrayList ids = null; + long thisDialogId = dialogId; + if (isSavedMessages) { + thisDialogId = UserConfig.getInstance(currentAccount).getClientUserId(); + } if (selectedMessage != null) { ids = new ArrayList<>(); ArrayList random_ids = null; @@ -5685,7 +5706,7 @@ public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User u random_ids.add(selectedMessage.messageOwner.random_id); } } - MessagesController.getInstance(currentAccount).deleteMessages(ids, random_ids, encryptedChat, dialogId, deleteForAll[0], scheduled); + MessagesController.getInstance(currentAccount).deleteMessages(ids, random_ids, encryptedChat, thisDialogId, deleteForAll[0], scheduled); } else { for (int a = 1; a >= 0; a--) { ids = new ArrayList<>(); @@ -5702,7 +5723,7 @@ public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User u } } } - MessagesController.getInstance(currentAccount).deleteMessages(ids, random_ids, encryptedChat, dialogId, deleteForAll[0], scheduled); + MessagesController.getInstance(currentAccount).deleteMessages(ids, random_ids, encryptedChat, thisDialogId, deleteForAll[0], scheduled); selectedMessages[a].clear(); } } @@ -5732,19 +5753,33 @@ public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User u } }; - if (count == 1) { - builder.setTitle(LocaleController.getString("DeleteSingleMessagesTitle", R.string.DeleteSingleMessagesTitle)); + if (isSavedMessages) { + if (count == 1) { + builder.setTitle(LocaleController.getString(R.string.UnsaveSingleMessagesTitle)); + } else { + builder.setTitle(LocaleController.formatString(R.string.UnsaveMessagesTitle, LocaleController.formatPluralString("messages", count))); + } } else { - builder.setTitle(LocaleController.formatString("DeleteMessagesTitle", R.string.DeleteMessagesTitle, LocaleController.formatPluralString("messages", count))); + if (count == 1) { + builder.setTitle(LocaleController.getString(R.string.DeleteSingleMessagesTitle)); + } else { + builder.setTitle(LocaleController.formatString(R.string.DeleteMessagesTitle, LocaleController.formatPluralString("messages", count))); + } } - if (chat != null && hasNotOut) { + if (isSavedMessages) { + if (count == 1) { + builder.setMessage(LocaleController.getString(R.string.AreYouSureUnsaveSingleMessage)); + } else { + builder.setMessage(LocaleController.getString(R.string.AreYouSureUnsaveFewMessages)); + } + } else if (chat != null && hasNotOut) { if (hasDeleteForAllCheck && myMessagesCount != count) { - builder.setMessage(LocaleController.formatString("DeleteMessagesTextGroupPart", R.string.DeleteMessagesTextGroupPart, LocaleController.formatPluralString("messages", myMessagesCount))); + builder.setMessage(LocaleController.formatString(R.string.DeleteMessagesTextGroupPart, LocaleController.formatPluralString("messages", myMessagesCount))); } else if (count == 1) { - builder.setMessage(LocaleController.getString("AreYouSureDeleteSingleMessage", R.string.AreYouSureDeleteSingleMessage)); + builder.setMessage(LocaleController.getString(R.string.AreYouSureDeleteSingleMessage)); } else { - builder.setMessage(LocaleController.getString("AreYouSureDeleteFewMessages", R.string.AreYouSureDeleteFewMessages)); + builder.setMessage(LocaleController.getString(R.string.AreYouSureDeleteFewMessages)); } } else if (hasDeleteForAllCheck && !canDeleteInbox && myMessagesCount != count) { if (chat != null) { @@ -5793,12 +5828,12 @@ public static void createDeleteMessagesAlert(BaseFragment fragment, TLRPC.User u } } - if (isActiveGiveawayAndOwner) { + if (isActiveGiveawayAndOwner && !isSavedMessages) { builder.setTitle(LocaleController.getString("BoostingGiveawayDeleteMsgTitle", R.string.BoostingGiveawayDeleteMsgTitle)); builder.setMessage(AndroidUtilities.replaceTags(LocaleController.formatString("BoostingGiveawayDeleteMsgText", R.string.BoostingGiveawayDeleteMsgText, giveawayEndDate))); builder.setNeutralButton(LocaleController.getString("Delete", R.string.Delete), deleteAction); } else { - builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), deleteAction); + builder.setPositiveButton(LocaleController.getString(isSavedMessages ? R.string.Remove : R.string.Delete), deleteAction); } builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); builder.setOnPreDismissListener(di -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiDrawable.java index bf959b9cfe..ac5d2f0997 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiDrawable.java @@ -72,6 +72,7 @@ public class AnimatedEmojiDrawable extends Drawable { public static final int CACHE_TYPE_AVATAR_CONSTRUCTOR_PREVIEW2 = 15; public static final int CACHE_TYPE_ALERT_PREVIEW_STATIC_WITH_THUMB = 16; public static final int CACHE_TYPE_EMOJI_CALL = 17; + public static final int CACHE_TYPE_SAVED_REACTION = 18; public int rawDrawIndex; @@ -625,7 +626,7 @@ protected boolean setImageBitmapByKey(Drawable drawable, String key, int type, b private void updateAutoRepeat(ImageReceiver imageReceiver) { if (cacheType == CACHE_TYPE_EMOJI_STATUS || cacheType == CACHE_TYPE_ALERT_EMOJI_STATUS || cacheType == CACHE_TYPE_FORUM_TOPIC) { imageReceiver.setAutoRepeatCount(2); - } else if (cacheType == CACHE_TYPE_FORUM_TOPIC_LARGE || cacheType == CACHE_TYPE_AVATAR_CONSTRUCTOR_PREVIEW || cacheType == CACHE_TYPE_TAB_STRIP || cacheType == CACHE_TYPE_ALERT_PREVIEW_TAB_STRIP) { + } else if (cacheType == CACHE_TYPE_FORUM_TOPIC_LARGE || cacheType == CACHE_TYPE_SAVED_REACTION || cacheType == CACHE_TYPE_AVATAR_CONSTRUCTOR_PREVIEW || cacheType == CACHE_TYPE_TAB_STRIP || cacheType == CACHE_TYPE_ALERT_PREVIEW_TAB_STRIP) { imageReceiver.setAutoRepeatCount(1); } else if (cacheType == CACHE_TYPE_EMOJI_CALL){ imageReceiver.setAutoRepeatCount(0); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java index d8acbd1ca5..15a350f4a0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFileDrawable.java @@ -483,6 +483,10 @@ public AnimatedFileDrawable(File file, boolean createDecoder, long streamSize, i } public AnimatedFileDrawable(File file, boolean createDecoder, long streamSize, int streamLoadingPriority, TLRPC.Document document, ImageLocation location, Object parentObject, long seekTo, int account, boolean preview, int w, int h, BitmapsCache.CacheOptions cacheOptions) { + this(file, createDecoder, streamSize, streamLoadingPriority, document, location, parentObject, seekTo, account, preview, w, h, cacheOptions, document != null ? 1 : 0); + } + + public AnimatedFileDrawable(File file, boolean createDecoder, long streamSize, int streamLoadingPriority, TLRPC.Document document, ImageLocation location, Object parentObject, long seekTo, int account, boolean preview, int w, int h, BitmapsCache.CacheOptions cacheOptions, int cacheType) { path = file; PRERENDER_FRAME = SharedConfig.deviceIsAboveAverage(); streamFileSize = streamSize; @@ -494,7 +498,7 @@ public AnimatedFileDrawable(File file, boolean createDecoder, long streamSize, i this.document = document; getPaint().setFlags(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); if (streamSize != 0 && (document != null || location != null)) { - stream = new AnimatedFileDrawableStream(document, location, parentObject, account, preview, streamLoadingPriority); + stream = new AnimatedFileDrawableStream(document, location, parentObject, account, preview, streamLoadingPriority, cacheType); } if (createDecoder && !this.precache) { nativePtr = createDecoder(file.getAbsolutePath(), metaData, currentAccount, streamFileSize, stream, preview); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFloat.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFloat.java index c32374e047..693f86173e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFloat.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedFloat.java @@ -124,6 +124,7 @@ public float get() { // set() must be called inside onDraw/dispatchDraw // the main purpose of AnimatedFloat is to interpolate between abrupt changing states + public float set(float mustBe) { return this.set(mustBe, false); } @@ -132,6 +133,8 @@ public float set(boolean mustBe) { return this.set(mustBe ? 1 : 0, false); } + // do set(value, true) when it's needed to skip animation + public float set(boolean mustBe, boolean force) { return this.set(mustBe ? 1 : 0, force); } 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 9481d99ec2..668988021f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarDrawable.java @@ -46,7 +46,9 @@ public class AvatarDrawable extends Drawable { private TextPaint namePaint; private boolean hasGradient; + private boolean hasAdvancedGradient; private int color, color2; + private GradientTools advancedGradient; private boolean needApplyColorAccent; private StaticLayout textLayout; private float textWidth; @@ -75,7 +77,6 @@ public class AvatarDrawable extends Drawable { public static final int AVATAR_TYPE_ARCHIVED = 2; public static final int AVATAR_TYPE_SHARES = 3; public static final int AVATAR_TYPE_REPLIES = 12; - public static final int AVATAR_TYPE_STORY = 20; public static final int AVATAR_TYPE_FILTER_CONTACTS = 4; public static final int AVATAR_TYPE_FILTER_NON_CONTACTS = 5; @@ -92,6 +93,9 @@ public class AvatarDrawable extends Drawable { public static final int AVATAR_TYPE_COUNTRY = 17; public static final int AVATAR_TYPE_UNCLAIMED = 18; public static final int AVATAR_TYPE_TO_BE_DISTRIBUTED = 19; + public static final int AVATAR_TYPE_STORY = 20; + public static final int AVATAR_TYPE_ANONYMOUS = 21; + public static final int AVATAR_TYPE_MY_NOTES = 22; private int alpha = 255; private Theme.ResourcesProvider resourcesProvider; @@ -231,11 +235,11 @@ public void setScaleSize(float value) { public void setAvatarType(int value) { avatarType = value; rotate45Background = false; + hasAdvancedGradient = false; + hasGradient = false; if (avatarType == AVATAR_TYPE_REGISTER) { - hasGradient = false; color = color2 = Theme.getColor(Theme.key_chats_actionBackground); } else if (avatarType == AVATAR_TYPE_ARCHIVED) { - hasGradient = false; color = color2 = getThemedColor(Theme.key_avatar_backgroundArchivedHidden); } else if (avatarType == AVATAR_TYPE_REPLIES || avatarType == AVATAR_TYPE_SAVED || avatarType == AVATAR_TYPE_OTHER_CHATS) { hasGradient = true; @@ -282,12 +286,24 @@ public void setAvatarType(int value) { hasGradient = true; color = getThemedColor(Theme.keys_avatar_background[getColorIndex(5)]); color2 = getThemedColor(Theme.keys_avatar_background2[getColorIndex(5)]); + } else if (avatarType == AVATAR_TYPE_ANONYMOUS) { + hasAdvancedGradient = true; + if (advancedGradient == null) { + advancedGradient = new GradientTools(); + } + advancedGradient.setColors(0xFF837CFF, 0xFFB063FF, 0xFFFF72A9, 0xFFE269FF); + } else if (avatarType == AVATAR_TYPE_MY_NOTES) { + hasAdvancedGradient = true; + if (advancedGradient == null) { + advancedGradient = new GradientTools(); + } + advancedGradient.setColors(0xFF4D8DFF, 0xFF2BBFFF, 0xFF20E2CD, 0xFF0EE1F1); } else { hasGradient = true; color = getThemedColor(Theme.keys_avatar_background[getColorIndex(4)]); color2 = getThemedColor(Theme.keys_avatar_background2[getColorIndex(4)]); } - needApplyColorAccent = avatarType != AVATAR_TYPE_ARCHIVED && avatarType != AVATAR_TYPE_SAVED && avatarType != AVATAR_TYPE_STORY && avatarType != AVATAR_TYPE_REPLIES && avatarType != AVATAR_TYPE_OTHER_CHATS; + needApplyColorAccent = avatarType != AVATAR_TYPE_ARCHIVED && avatarType != AVATAR_TYPE_SAVED && avatarType != AVATAR_TYPE_STORY && avatarType != AVATAR_TYPE_ANONYMOUS && avatarType != AVATAR_TYPE_REPLIES && avatarType != AVATAR_TYPE_OTHER_CHATS; } public void setArchivedAvatarHiddenProgress(float progress) { @@ -318,12 +334,14 @@ public void setInfo(int currentAccount, TLRPC.ChatInvite chat) { public void setColor(int value) { hasGradient = false; + hasAdvancedGradient = false; color = color2 = value; needApplyColorAccent = false; } public void setColor(int value, int value2) { hasGradient = true; + hasAdvancedGradient = false; color = value; color2 = value2; needApplyColorAccent = false; @@ -359,6 +377,7 @@ public void setInfo(long id, String firstName, String lastName, String custom) { public void setInfo(long id, String firstName, String lastName, String custom, Integer customColor, MessagesController.PeerColor profileColor) { hasGradient = true; + hasAdvancedGradient = false; invalidateTextLayout = true; if (profileColor != null) { color = profileColor.getAvatarColor1(); @@ -438,16 +457,20 @@ public void draw(Canvas canvas) { } int size = bounds.width(); namePaint.setColor(ColorUtils.setAlphaComponent(getThemedColor(Theme.key_avatar_text), alpha)); - if (hasGradient) { + Paint backgroundPaint = Theme.avatar_backgroundPaint; + if (hasAdvancedGradient && advancedGradient != null) { + advancedGradient.setBounds(bounds.left, bounds.top, bounds.left + size, bounds.top + size); + backgroundPaint = advancedGradient.paint; + } else if (hasGradient) { int color = ColorUtils.setAlphaComponent(getColor(), alpha); int color2 = ColorUtils.setAlphaComponent(getColor2(), alpha); if (gradient == null || gradientBottom != bounds.height() || gradientColor1 != color || gradientColor2 != color2) { gradient = new LinearGradient(0, 0, 0, gradientBottom = bounds.height(), gradientColor1 = color, gradientColor2 = color2, Shader.TileMode.CLAMP); } - Theme.avatar_backgroundPaint.setShader(gradient); + backgroundPaint.setShader(gradient); } else { - Theme.avatar_backgroundPaint.setShader(null); - Theme.avatar_backgroundPaint.setColor(ColorUtils.setAlphaComponent(getColor(), alpha)); + backgroundPaint.setShader(null); + backgroundPaint.setColor(ColorUtils.setAlphaComponent(getColor(), alpha)); } canvas.save(); canvas.translate(bounds.left, bounds.top); @@ -459,9 +482,9 @@ public void draw(Canvas canvas) { } if (roundRadius > 0) { AndroidUtilities.rectTmp.set(0, 0, size, size); - canvas.drawRoundRect(AndroidUtilities.rectTmp, roundRadius, roundRadius, Theme.avatar_backgroundPaint); + canvas.drawRoundRect(AndroidUtilities.rectTmp, roundRadius, roundRadius, backgroundPaint); } else { - canvas.drawCircle(size / 2.0f, size / 2.0f, size / 2.0f, Theme.avatar_backgroundPaint); + canvas.drawCircle(size / 2.0f, size / 2.0f, size / 2.0f, backgroundPaint); } if (rotate45Background) { canvas.restore(); @@ -470,8 +493,8 @@ public void draw(Canvas canvas) { if (avatarType == AVATAR_TYPE_ARCHIVED) { if (archivedAvatarProgress != 0) { - Theme.avatar_backgroundPaint.setColor(ColorUtils.setAlphaComponent(getThemedColor(Theme.key_avatar_backgroundArchived), alpha)); - canvas.drawCircle(size / 2.0f, size / 2.0f, size / 2.0f * archivedAvatarProgress, Theme.avatar_backgroundPaint); + backgroundPaint.setColor(ColorUtils.setAlphaComponent(getThemedColor(Theme.key_avatar_backgroundArchived), alpha)); + canvas.drawCircle(size / 2.0f, size / 2.0f, size / 2.0f * archivedAvatarProgress, backgroundPaint); if (Theme.dialogs_archiveAvatarDrawableRecolored) { Theme.dialogs_archiveAvatarDrawable.beginApplyLayerColors(); Theme.dialogs_archiveAvatarDrawable.setLayerColor("Arrow1.**", Theme.getNonAnimatedColor(Theme.key_avatar_backgroundArchived)); @@ -531,6 +554,10 @@ public void draw(Canvas canvas) { drawable = Theme.avatarDrawables[16]; } else if (avatarType == AVATAR_TYPE_STORY) { drawable = Theme.avatarDrawables[17]; + } else if (avatarType == AVATAR_TYPE_ANONYMOUS) { + drawable = Theme.avatarDrawables[18]; + } else if (avatarType == AVATAR_TYPE_MY_NOTES) { + drawable = Theme.avatarDrawables[19]; } else { drawable = Theme.avatarDrawables[9]; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java index fcef07bf4e..378a8d1105 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AvatarsDrawable.java @@ -209,6 +209,9 @@ public void setStepFactor(float factor) { } public void animateFromState(AvatarsDrawable avatarsDrawable, int currentAccount, boolean createAnimator) { + if (avatarsDrawable == null) { + return; + } if (avatarsDrawable.transitionProgressAnimator != null) { avatarsDrawable.transitionProgressAnimator.cancel(); if (transitionInProgress) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackButtonMenu.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackButtonMenu.java index d223db3f56..76dd519c91 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackButtonMenu.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackButtonMenu.java @@ -48,7 +48,7 @@ public static class PulledDialog { int filterId; } - public static ActionBarPopupWindow show(BaseFragment thisFragment, View backButton, long currentDialogId, int topicId, Theme.ResourcesProvider resourcesProvider) { + public static ActionBarPopupWindow show(BaseFragment thisFragment, View backButton, long currentDialogId, long topicId, Theme.ResourcesProvider resourcesProvider) { if (thisFragment == null) { return null; } @@ -219,7 +219,7 @@ public static ActionBarPopupWindow show(BaseFragment thisFragment, View backButt return scrimPopupWindow; } - private static ArrayList getStackedHistoryForTopic(BaseFragment thisFragment, long currentDialogId, int topicId) { + private static ArrayList getStackedHistoryForTopic(BaseFragment thisFragment, long currentDialogId, long topicId) { ArrayList dialogs = new ArrayList<>(); if (thisFragment.getParentLayout().getFragmentStack().size() > 1 && thisFragment.getParentLayout().getFragmentStack().get(thisFragment.getParentLayout().getFragmentStack().size() - 2) instanceof TopicsFragment) { PulledDialog pulledDialog = new PulledDialog(); 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 21f1c2b477..6f66869641 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurBehindDrawable.java @@ -123,6 +123,7 @@ public void draw(Canvas canvas) { canvas.drawBitmap(bitmap[0], 0, 0, emptyPaint); canvas.restore(); wasDraw = true; + canvas.drawColor(0x1a000000); } canvas.restore(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BottomSheetWithRecyclerListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BottomSheetWithRecyclerListView.java index 9737c16992..9dec200294 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BottomSheetWithRecyclerListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BottomSheetWithRecyclerListView.java @@ -43,10 +43,13 @@ public BottomSheetWithRecyclerListView(BaseFragment fragment, boolean needFocus, } public BottomSheetWithRecyclerListView(BaseFragment fragment, boolean needFocus, boolean hasFixedSize, boolean useNested, Theme.ResourcesProvider resourcesProvider) { - super(fragment.getParentActivity(), needFocus, resourcesProvider); + this(fragment.getParentActivity(), fragment, needFocus, hasFixedSize, useNested, resourcesProvider); + } + + public BottomSheetWithRecyclerListView(Context context, BaseFragment fragment, boolean needFocus, boolean hasFixedSize, boolean useNested, Theme.ResourcesProvider resourcesProvider) { + super(context, needFocus, resourcesProvider); this.baseFragment = fragment; this.hasFixedSize = hasFixedSize; - Context context = fragment.getParentActivity(); headerShadowDrawable = ContextCompat.getDrawable(context, R.drawable.header_shadow).mutate(); FrameLayout containerView; if (useNested) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java index 59673d5ded..d2fca34fd0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java @@ -32,6 +32,7 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationsController; import org.telegram.messenger.R; +import org.telegram.messenger.SavedMessagesController; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; @@ -66,20 +67,16 @@ public static boolean canShowBulletin(BaseFragment fragment) { public static BulletinFactory global() { BaseFragment baseFragment = LaunchActivity.getLastFragment(); if (baseFragment == null) { - return null; + return BulletinFactory.of(Bulletin.BulletinWindow.make(ApplicationLoader.applicationContext), null); } return BulletinFactory.of(baseFragment); } - public static void showForError(TLRPC.TL_error error) { - BulletinFactory bulletinFactory = BulletinFactory.global(); - if (bulletinFactory == null) { - return; - } + public void showForError(TLRPC.TL_error error) { if (BuildVars.DEBUG_VERSION) { - bulletinFactory.createErrorBulletin(error.code + " " + error.text).show(); + createErrorBulletin(error.code + " " + error.text).show(); } else { - bulletinFactory.createErrorBulletin(LocaleController.getString("UnknownError", R.string.UnknownError)).show(); + createErrorBulletin(LocaleController.getString("UnknownError", R.string.UnknownError)).show(); } } @@ -1039,9 +1036,9 @@ public static Bulletin createForwardedBulletin(Context context, FrameLayout cont if (dialogsCount <= 1) { if (did == UserConfig.getInstance(UserConfig.selectedAccount).clientUserId) { if (messagesCount <= 1) { - text = AndroidUtilities.replaceTags(LocaleController.getString("FwdMessageToSavedMessages", R.string.FwdMessageToSavedMessages)); + text = AndroidUtilities.replaceSingleTag(LocaleController.getString(R.string.FwdMessageToSavedMessages), SavedMessagesController::openSavedMessages); } else { - text = AndroidUtilities.replaceTags(LocaleController.getString("FwdMessagesToSavedMessages", R.string.FwdMessagesToSavedMessages)); + text = AndroidUtilities.replaceSingleTag(LocaleController.getString(R.string.FwdMessagesToSavedMessages), SavedMessagesController::openSavedMessages); } layout.setAnimation(R.raw.saved_messages, 30, 30); } else { 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 5c4a8bf6ed..0080eeb3bb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java @@ -8,6 +8,9 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; +import static org.telegram.messenger.AndroidUtilities.lerp; import static org.telegram.ui.LaunchActivity.getLastFragment; import android.Manifest; @@ -60,6 +63,7 @@ import android.text.TextUtils; import android.text.TextWatcher; import android.text.style.ImageSpan; +import android.util.Log; import android.util.Property; import android.util.TypedValue; import android.view.ActionMode; @@ -244,6 +248,8 @@ public interface ChatActivityEnterViewDelegate { void needStartRecordVideo(int state, boolean notify, int scheduleDate, int ttl); + void toggleVideoRecordingPause(); + void needChangeVideoPreviewState(int state, float seekProgress); void onSwitchRecordMode(boolean video); @@ -392,7 +398,7 @@ default boolean onceVoiceAvailable() { private HashMap animationParamsX = new HashMap<>(); - private class SeekBarWaveformView extends View { + protected class SeekBarWaveformView extends View { public SeekBarWaveformView(Context context) { super(context); @@ -486,15 +492,16 @@ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo i @Nullable protected FrameLayout recordedAudioPanel; @Nullable - private VideoTimelineView videoTimelineView; + protected VideoTimelineView videoTimelineView; @SuppressWarnings("FieldCanBeLocal") private RLottieImageView recordDeleteImageView; @Nullable - private SeekBarWaveformView recordedAudioSeekBar; + protected SeekBarWaveformView recordedAudioSeekBar; @Nullable private View recordedAudioBackground; @Nullable private ImageView recordedAudioPlayButton; + private long millisecondsRecorded; @Nullable private TextView recordedAudioTimeTextView; @Nullable @@ -531,7 +538,7 @@ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo i private AnimatorSet scheduledButtonAnimation; @Nullable private RecordCircle recordCircle; - private OnceButton onceButton; + public ControlsView controlsView; private CloseProgressDrawable2 progressDrawable; private Paint dotPaint; private MediaActionDrawable playPauseDrawable; @@ -588,7 +595,7 @@ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo i private boolean sendByEnter; private long lastTypingTimeSend; private float startedDraggingX = -1; - private float distCanMove = AndroidUtilities.dp(80); + private float distCanMove = dp(80); private boolean recordingAudioVideo; public int recordingGuid; private boolean forceShowSendButton; @@ -714,6 +721,18 @@ public void set(RecordCircle object, Float value) { } }; + private Property recordControlsCircleScale = new Property(Float.class, "controlsScale") { + @Override + public Float get(RecordCircle object) { + return object.getControlsScale(); + } + + @Override + public void set(RecordCircle object, Float value) { + object.setControlsScale(value); + } + }; + private Paint redDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private boolean stickersTabOpen; @@ -801,7 +820,7 @@ public void run() { recordingAudioVideo = true; updateRecordInterface(RECORD_STATE_ENTER); if (recordTimerView != null) { - recordTimerView.start(); + recordTimerView.start(0); } if (recordDot != null) { recordDot.enterAnimation = false; @@ -847,7 +866,7 @@ protected void onDetachedFromWindow() { public RecordDot(Context context) { super(context); int resId = R.raw.chat_audio_record_delete_2; - drawable = new RLottieDrawable(resId, "" + resId, AndroidUtilities.dp(28), AndroidUtilities.dp(28), false, null); + drawable = new RLottieDrawable(resId, "" + resId, dp(28), dp(28), false, null); drawable.setCurrentParentView(this); drawable.setInvalidateOnProgressSet(true); updateColors(); @@ -855,7 +874,7 @@ public RecordDot(Context context) { public void updateColors() { int dotColor = getThemedColor(Theme.key_chat_recordedVoiceDot); - int background = ColorUtils.blendARGB(dotColor, getThemedColor(Theme.key_chat_messagePanelBackground), 0.5f); + int background = getThemedColor(Theme.key_chat_messagePanelBackground); redDotPaint.setColor(dotColor); drawable.beginApplyLayerColors(); drawable.setLayerColor("Cup Red.**", dotColor); @@ -914,7 +933,7 @@ protected void onDraw(Canvas canvas) { drawable.draw(canvas); } if (!playing || !drawable.hasBitmap()) { - canvas.drawCircle(this.getMeasuredWidth() >> 1, this.getMeasuredHeight() >> 1, AndroidUtilities.dp(5), redDotPaint); + canvas.drawCircle(this.getMeasuredWidth() >> 1, this.getMeasuredHeight() >> 1, dp(5), redDotPaint); } invalidate(); } @@ -1017,30 +1036,93 @@ public void set(View object, Float value) { } }; - public class OnceButton extends FrameLayout { + private boolean showTooltip; + private long showTooltipStartTime; + private float tooltipAlpha; + + public class ControlsView extends FrameLayout { private HintView2 hintView; + private Drawable tooltipBackground; + private Drawable tooltipBackgroundArrow; + private String tooltipMessage; + private StaticLayout tooltipLayout; + private float tooltipWidth; + private TextPaint tooltipPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + Paint lockBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + Paint lockPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + Paint lockOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + Path path = new Path(); + private Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); + private CaptionContainerView.PeriodDrawable periodDrawable; - private Paint lockBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - public OnceButton(Context context) { + private Drawable micDrawable; + private Drawable vidDrawable; + + @Override + public void setAlpha(float alpha) { + super.setAlpha(alpha); + } + + @Override + protected boolean onSetAlpha(int alpha) { + return super.onSetAlpha(alpha); + } + + public ControlsView(Context context) { super(context); periodDrawable = new CaptionContainerView.PeriodDrawable(); periodDrawable.setCallback(this); periodDrawable.setValue(1, voiceOnce, false); + lockOutlinePaint.setStyle(Paint.Style.STROKE); + lockOutlinePaint.setStrokeCap(Paint.Cap.ROUND); + lockOutlinePaint.setStrokeWidth(dpf2(1.7f)); + + lockShadowDrawable = getResources().getDrawable(R.drawable.lock_round_shadow); + lockShadowDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelVoiceLockShadow), PorterDuff.Mode.MULTIPLY)); + tooltipBackground = Theme.createRoundRectDrawable(dp(5), getThemedColor(Theme.key_chat_gifSaveHintBackground)); + + tooltipPaint.setTextSize(dp(14)); + tooltipBackgroundArrow = ContextCompat.getDrawable(context, R.drawable.tooltip_arrow); + tooltipMessage = LocaleController.getString("SlideUpToLock", R.string.SlideUpToLock); + + radiiLeft[0] = radiiLeft[1] = radiiLeft[6] = radiiLeft[7] = dp(3); + radiiLeft[2] = radiiLeft[3] = radiiLeft[4] = radiiLeft[5] = 0; + + radiiRight[0] = radiiRight[1] = radiiRight[6] = radiiRight[7] = 0; + radiiRight[2] = radiiRight[3] = radiiRight[4] = radiiRight[5] = dp(3); + + micDrawable = getResources().getDrawable(R.drawable.input_mic).mutate(); + vidDrawable = getResources().getDrawable(R.drawable.input_video).mutate(); + setWillNotDraw(false); updateColors(); } + public void showTooltipIfNeed() { + if (SharedConfig.lockRecordAudioVideoHint < 3) { + showTooltip = true; + showTooltipStartTime = System.currentTimeMillis(); + } + } + public void showHintView() { hideHintView(); hintView = new HintView2(getContext(), HintView2.DIRECTION_RIGHT); hintView.setJoint(1, 0); hintView.setMultilineText(true); - hintView.setText(AndroidUtilities.replaceTags(LocaleController.getString(voiceOnce ? R.string.VoiceSetOnceHintEnabled : R.string.VoiceSetOnceHint))); + int text; + if (isInVideoMode) { + text = voiceOnce ? R.string.VideoSetOnceHintEnabled : R.string.VideoSetOnceHint; + } else { + text = voiceOnce ? R.string.VoiceSetOnceHintEnabled : R.string.VoiceSetOnceHint; + } + hintView.setText(AndroidUtilities.replaceTags(LocaleController.getString(text))); hintView.setMaxWidthPx(HintView2.cutInFancyHalf(hintView.getText(), hintView.getTextPaint())); if (voiceOnce) { hintView.setIcon(R.raw.fire_on); @@ -1067,31 +1149,334 @@ public void hideHintView() { } } + private int lastSize; + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int currentSize = MeasureSpec.getSize(widthMeasureSpec); + int h = dp(194 + 48 + 12); + if (lastSize != currentSize) { + lastSize = currentSize; + tooltipLayout = new StaticLayout(tooltipMessage, tooltipPaint, dp(220), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true); + int n = tooltipLayout.getLineCount(); + tooltipWidth = 0; + for (int i = 0; i < n; i++) { + float w = tooltipLayout.getLineWidth(i); + if (w > tooltipWidth) { + tooltipWidth = w; + } + } + } +// if (tooltipLayout != null && tooltipLayout.getLineCount() > 1) { +// h += tooltipLayout.getHeight() - tooltipLayout.getLineBottom(0); +// } super.onMeasure( widthMeasureSpec, - MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(194 + 48 + 12), MeasureSpec.EXACTLY) + MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY) ); } private final RectF rectF = new RectF(); public final RectF onceRect = new RectF(); + private long lastUpdateTime; + + private final Path path2 = new Path(); + private final float[] radiiLeft = new float[8], radiiRight = new float[8]; + + private AnimatedFloat hidePauseT = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + @Override protected void onDraw(Canvas canvas) { - if (recordCircle == null) return; + float sc; + if (scale <= 0.5f) { + sc = scale / 0.5f; + } else if (scale <= 0.75f) { + sc = 1.0f - (scale - 0.5f) / 0.25f * 0.1f; + } else { + sc = 0.9f + (scale - 0.75f) / 0.25f * 0.1f; + } + long dt = System.currentTimeMillis() - lastUpdateTime; + lastUpdateTime = System.currentTimeMillis(); - int cx = getMeasuredWidth() - AndroidUtilities.dp2(26); + float yAdd = 0; + if (lockAnimatedTranslation != 10000) { + yAdd = Math.max(0, (int) (startTranslation - lockAnimatedTranslation)); + if (yAdd > dp(57)) { + yAdd = dp(57); + } + } + + final int cx = getMeasuredWidth() - AndroidUtilities.dp2(26); + float moveProgress = 1.0f - yAdd / dp(57); + final float multilinTooltipOffset = getMeasuredHeight() - dp(194); + + float lockSize; + float lockY; + float lockTopY; + float lockMiddleY; + + float lockRotation; + float transformToPauseProgress = 0; + if (sendButtonVisible) { + lockSize = dp(36); + lockY = dp(60) + multilinTooltipOffset + dpf2(30) * (1.0f - sc) - yAdd + dpf2(14f) * moveProgress; + + lockMiddleY = lockY + lockSize / 2f - dpf2(8) + dpf2(2); + lockTopY = lockY + lockSize / 2f - dpf2(16) + dpf2(2); + float snapRotateBackProgress = moveProgress > 0.4f ? 1f : moveProgress / 0.4f; + + lockRotation = 9 * (1f - moveProgress) * (1f - snapAnimationProgress) - 15 * snapAnimationProgress * (1f - snapRotateBackProgress); + + transformToPauseProgress = moveProgress; + } else { + lockSize = dp(36) + (int) (dp(14) * moveProgress); + lockY = dp(60) + multilinTooltipOffset + (int) (dp(30) * (1.0f - sc)) - (int) yAdd + (moveProgress) * idleProgress * -dp(8); + lockMiddleY = lockY + lockSize / 2f - dpf2(8) + dpf2(2) + dpf2(2) * moveProgress; + lockTopY = lockY + lockSize / 2f - dpf2(16) + dpf2(2) + dpf2(2) * moveProgress; + lockRotation = 9 * (1f - moveProgress); + snapAnimationProgress = 0; + } + + if ((showTooltip && System.currentTimeMillis() - showTooltipStartTime > 200) || tooltipAlpha != 0f) { + if (moveProgress < 0.8f || sendButtonVisible || exitTransition != 0 || transformToSeekbar != 0) { + showTooltip = false; + } + if (showTooltip) { + if (tooltipAlpha != 1f) { + tooltipAlpha += dt / 150f; + if (tooltipAlpha >= 1f) { + tooltipAlpha = 1f; + SharedConfig.increaseLockRecordAudioVideoHintShowed(); + } + } + } else { + tooltipAlpha -= dt / 150f; + if (tooltipAlpha < 0) { + tooltipAlpha = 0f; + } + } + + + int alphaInt = (int) (tooltipAlpha * 255); - final float cy = AndroidUtilities.lerp(recordCircle.lockY + recordCircle.translationDy, getMeasuredHeight() - AndroidUtilities.dp(70 + 36), Math.max(recordCircle.exitTransition, Math.min(recordCircle.progressToSeekbarStep1, recordCircle.slideToCancelLockProgress))); - rectF.set(cx - AndroidUtilities.dpf2(18), cy, cx + AndroidUtilities.dpf2(18), cy + recordCircle.lockSize); - rectF.offset(0, getMeasuredHeight() - recordCircle.getMeasuredHeight()); - onceVisible = delegate != null && delegate.onceVoiceAvailable() && !isInVideoMode; + tooltipBackground.setAlpha(alphaInt); + tooltipBackgroundArrow.setAlpha(alphaInt); + tooltipPaint.setAlpha(alphaInt); + + if (tooltipLayout != null) { + canvas.save(); + rectF.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); + canvas.translate(getMeasuredWidth() - tooltipWidth - dp(44), multilinTooltipOffset + dpf2(16)); + tooltipBackground.setBounds( + -dp(8), -dp(2), + (int) (tooltipWidth + dp(36)), (int) (tooltipLayout.getHeight() + dpf2(4)) + ); + tooltipBackground.draw(canvas); + tooltipLayout.draw(canvas); + canvas.restore(); + + canvas.save(); + canvas.translate(getMeasuredWidth() - dp(26), multilinTooltipOffset + dpf2(16 + 1) + tooltipLayout.getHeight() / 2f - idleProgress * dpf2(3f)); + path.reset(); + path.setLastPoint(-dpf2(5), dpf2(4)); + path.lineTo(0, 0); + path.lineTo(dpf2(5), dpf2(4)); + + p.setColor(Color.WHITE); + p.setAlpha(alphaInt); + p.setStyle(Paint.Style.STROKE); + p.setStrokeCap(Paint.Cap.ROUND); + p.setStrokeJoin(Paint.Join.ROUND); + p.setStrokeWidth(dpf2(1.5f)); + canvas.drawPath(path, p); + canvas.restore(); + + canvas.save(); + tooltipBackgroundArrow.setBounds( + cx - tooltipBackgroundArrow.getIntrinsicWidth() / 2, (int) (tooltipLayout.getHeight() + multilinTooltipOffset + dpf2(20)), + cx + tooltipBackgroundArrow.getIntrinsicWidth() / 2, (int) (tooltipLayout.getHeight() + multilinTooltipOffset + dpf2(20)) + tooltipBackgroundArrow.getIntrinsicHeight() + ); + tooltipBackgroundArrow.draw(canvas); + canvas.restore(); + } + } + + float progressToSeekbarStep1 = 0f; + float progressToSeekbarStep2 = 0; + float exitProgress2 = 0f; + float hidePause = hidePauseT.set(isInVideoMode && millisecondsRecorded >= 59_000); + + if (transformToSeekbar != 0 && recordedAudioBackground != null) { + float step1Time = 0.38f; + float step2Time = 0.25f; +// float step3Time = 1f - step1Time - step2Time; + + progressToSeekbarStep1 = transformToSeekbar > step1Time ? 1f : transformToSeekbar / step1Time; + progressToSeekbarStep2 = transformToSeekbar > step1Time + step2Time ? 1f : Math.max(0, (transformToSeekbar - step1Time) / step2Time); +// progressToSeekbarStep3 = Math.max(0, (transformToSeekbar - step1Time - step2Time) / step3Time); + + progressToSeekbarStep1 = CubicBezierInterpolator.EASE_BOTH.getInterpolation(progressToSeekbarStep1); + progressToSeekbarStep2 = CubicBezierInterpolator.EASE_BOTH.getInterpolation(progressToSeekbarStep2); +// progressToSeekbarStep3 = CubicBezierInterpolator.EASE_BOTH.getInterpolation(progressToSeekbarStep3); +// +// radius = radius + AndroidUtilities.dp(16) * progressToSeekbarStep1; +// +// float toRadius = recordedAudioBackground.getMeasuredHeight() / 2f; +// radius = toRadius + (radius - toRadius) * (1f - progressToSeekbarStep2); + } else if (exitTransition != 0) { + float step1Time = 0.6f; + float step2Time = 0.4f; + + progressToSeekbarStep1 = exitTransition > step1Time ? 1f : exitTransition / step1Time; + exitProgress2 = messageTransitionIsRunning ? exitTransition : Math.max(0, (exitTransition - step1Time) / step2Time); + + progressToSeekbarStep1 = CubicBezierInterpolator.EASE_BOTH.getInterpolation(progressToSeekbarStep1); + exitProgress2 = CubicBezierInterpolator.EASE_BOTH.getInterpolation(exitProgress2); +// +// radius = radius + AndroidUtilities.dp(16) * progressToSeekbarStep1; +// radius *= (1f - exitProgress2); +// +// if (LiteMode.isEnabled(LiteMode.FLAGS_CHAT) && exitTransition > 0.6f) { +// circleAlpha = Math.max(0, 1f - (exitTransition - 0.6f) / 0.4f); +// } + } + + canvas.save(); + canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight() - textFieldContainer.getMeasuredHeight()); + float translation = 0; + if (1f - controlsScale != 0) { + translation = 1f - controlsScale; +// } else if (progressToSeekbarStep2 != 0) { +// translation = progressToSeekbarStep2; + } else if (exitProgress2 != 0) { + translation = exitProgress2; + } + if (slideToCancelProgress < 0.7f || canceledByGesture) { + showTooltip = false; + if (slideToCancelLockProgress != 0) { + slideToCancelLockProgress -= 0.12f; + if (slideToCancelLockProgress < 0) { + slideToCancelLockProgress = 0; + } + } + } else { + if (slideToCancelLockProgress != 1f) { + slideToCancelLockProgress += 0.12f; + if (slideToCancelLockProgress > 1f) { + slideToCancelLockProgress = 1f; + } + } + } + + float maxTranslationDy = dpf2(72); + float dy = ( + maxTranslationDy * translation + + dpf2(24) * (progressToSeekbarStep1) * (1f - translation) + + maxTranslationDy * (1f - slideToCancelLockProgress) + ); + if (dy > maxTranslationDy) { + dy = maxTranslationDy; + } +// canvas.translate(0, dy); + float s = (1f - hidePause) * controlsScale * (1f - exitProgress2) * slideToCancelLockProgress; + canvas.scale(s, s, cx, lockMiddleY + dy); + + rectF.set(cx - dpf2(18), lockY + dy, cx + dpf2(18), lockY + dy + lockSize); + lockShadowDrawable.setBounds( + (int) (rectF.left - dpf2(3)), (int) (rectF.top - dpf2(3)), + (int) (rectF.right + dpf2(3)), (int) (rectF.bottom + dpf2(3)) + ); + lockShadowDrawable.draw(canvas); + canvas.drawRoundRect(rectF, dpf2(18), dpf2(18), lockBackgroundPaint); + pauseRect.set(rectF); + scale(pauseRect, s); + + rectF.set( + cx - dpf2(6) - dpf2(2) * (1f - transformToPauseProgress), + lockMiddleY + dy - dpf2(2) * (1f - transformToPauseProgress), + cx + dp(6) + dpf2(2) * (1f - transformToPauseProgress), + lockMiddleY + dy + dp(12) + dpf2(2) * (1f - transformToPauseProgress) + ); + float lockBottom = rectF.bottom; + float locCx = rectF.centerX(); + float locCy = rectF.centerY(); + canvas.save(); + canvas.translate(0, dpf2(2) * (1f - moveProgress)); + canvas.rotate(lockRotation, locCx, locCy); + + if (transformToPauseProgress != 1f) { + AndroidUtilities.rectTmp.set(0, 0, dpf2(8), dpf2(8)); + canvas.save(); + canvas.clipRect(0, 0, getMeasuredWidth(), dy + lockBottom + dpf2(2) * (1f - moveProgress)); + canvas.translate(cx - dpf2(4), rectF.top - dp(6) - lerp(dpf2(2), dpf2(1.5f) * (1f - idleProgress), moveProgress) + dpf2(12) * transformToPauseProgress + dpf2(2) * snapAnimationProgress); + if (lockRotation > 0) { + canvas.rotate(lockRotation, dp(8), dp(8)); + } + canvas.drawLine(dpf2(8), dpf2(4), dpf2(8), dpf2(6) + dpf2(4) * (1f - transformToPauseProgress), lockOutlinePaint); + canvas.drawArc(AndroidUtilities.rectTmp, 0, -180, false, lockOutlinePaint); + canvas.drawLine( + 0, dpf2(4), + 0, dpf2(4) + dpf2(4) * idleProgress * (moveProgress) * (sendButtonVisible ? 0 : 1) + dpf2(4) * snapAnimationProgress * (1f - moveProgress), + lockOutlinePaint + ); + canvas.restore(); + } + + Drawable resumeDrawable = null; + float transformToResume = Utilities.clamp(transformToSeekbar * 2f, 1, 0); + if (transformToResume > 0) { + resumeDrawable = isInVideoMode ? vidDrawable : micDrawable; + } + + int wasAlpha = lockPaint.getAlpha(); + lockPaint.setAlpha((int) (wasAlpha * (1f - transformToResume))); + if (transformToPauseProgress > 0) { + canvas.drawRoundRect(rectF, dpf2(3), dpf2(3), lockBackgroundPaint); + + path2.rewind(); + AndroidUtilities.rectTmp.set(rectF); + AndroidUtilities.rectTmp.right = rectF.centerX() - dp(1.66f) * transformToPauseProgress; + radiiLeft[0] = radiiLeft[1] = radiiLeft[6] = radiiLeft[7] = lerp(dp(3), dp(1.5f), transformToPauseProgress); + radiiLeft[2] = radiiLeft[3] = radiiLeft[4] = radiiLeft[5] = dp(1.5f) * transformToPauseProgress; + path2.addRoundRect(AndroidUtilities.rectTmp, radiiLeft, Path.Direction.CW); + AndroidUtilities.rectTmp.set(rectF); + AndroidUtilities.rectTmp.left = rectF.centerX() + dp(1.66f) * transformToPauseProgress; + radiiRight[2] = radiiRight[3] = radiiRight[4] = radiiRight[5] = lerp(dp(3), dp(1.5f), transformToPauseProgress); + radiiRight[0] = radiiRight[1] = radiiRight[6] = radiiRight[7] = dp(1.5f) * transformToPauseProgress; + path2.addRoundRect(AndroidUtilities.rectTmp, radiiRight, Path.Direction.CW); + canvas.drawPath(path2, lockPaint); + } else { + canvas.drawRoundRect(rectF, dpf2(3), dpf2(3), lockPaint); + } + lockPaint.setAlpha(wasAlpha); + + if (resumeDrawable != null) { + final float _s = 0.9285f; + AndroidUtilities.rectTmp2.set( + (int) (rectF.centerX() - resumeDrawable.getIntrinsicWidth() / 2 * _s), + (int) (rectF.centerY() - resumeDrawable.getIntrinsicHeight() / 2 * _s), + (int) (rectF.centerX() + resumeDrawable.getIntrinsicWidth() / 2 * _s), + (int) (rectF.centerY() + resumeDrawable.getIntrinsicHeight() / 2 * _s) + ); + resumeDrawable.setBounds(AndroidUtilities.rectTmp2); + resumeDrawable.setAlpha((int) (0xFF * transformToResume)); + resumeDrawable.draw(canvas); + } + + if (transformToPauseProgress != 1) { + canvas.drawCircle(locCx, locCy, dpf2(2) * (1f - transformToPauseProgress), lockBackgroundPaint); + } + canvas.restore(); + canvas.restore(); + + final float cy = lerp(lockY, getMeasuredHeight() - dp(118), Math.max(exitTransition, Math.min(progressToSeekbarStep1, slideToCancelLockProgress))) + dy + dp(38) * hidePause; + rectF.set(cx - dpf2(18), cy, cx + dpf2(18), cy + lockSize); + onceVisible = delegate != null && delegate.onceVoiceAvailable(); if (onceVisible) { - final float onceOffset = AndroidUtilities.dpf2(AndroidUtilities.lerp(4, 12, recordCircle.moveProgress)); + final float onceOffset = dpf2(12); rectF.set( - rectF.left, rectF.top - AndroidUtilities.dpf2(36) - onceOffset, rectF.right, rectF.top - onceOffset + rectF.left, rectF.top - dpf2(36) - onceOffset, rectF.right, rectF.top - onceOffset ); if (hintView != null) { hintView.setJointPx(0, rectF.centerY()); @@ -1099,14 +1484,14 @@ protected void onDraw(Canvas canvas) { } onceRect.set(rectF); canvas.save(); - final float s = recordCircle.scale * (1f - recordCircle.exitTransition) * recordCircle.slideToCancelLockProgress * recordCircle.snapAnimationProgress; - canvas.scale(s, s, rectF.centerX(), rectF.centerY()); + final float s2 = controlsScale * (1f - exitTransition) * slideToCancelLockProgress * snapAnimationProgress; + canvas.scale(s2, s2, rectF.centerX(), rectF.centerY()); lockShadowDrawable.setBounds( - (int) (rectF.left - AndroidUtilities.dpf2(3)), (int) (rectF.top - AndroidUtilities.dpf2(3)), - (int) (rectF.right + AndroidUtilities.dpf2(3)), (int) (rectF.bottom + AndroidUtilities.dpf2(3)) + (int) (rectF.left - dpf2(3)), (int) (rectF.top - dpf2(3)), + (int) (rectF.right + dpf2(3)), (int) (rectF.bottom + dpf2(3)) ); lockShadowDrawable.draw(canvas); - canvas.drawRoundRect(rectF, AndroidUtilities.dpf2(18), AndroidUtilities.dpf2(18), lockBackgroundPaint); + canvas.drawRoundRect(rectF, dpf2(18), dpf2(18), lockBackgroundPaint); periodDrawable.setBounds((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom); periodDrawable.draw(canvas); canvas.restore(); @@ -1120,33 +1505,75 @@ public void updateColors() { 0xFFFFFFFF ); lockBackgroundPaint.setColor(getThemedColor(Theme.key_chat_messagePanelVoiceLockBackground)); - } - private boolean pressed; + tooltipPaint.setColor(getThemedColor(Theme.key_chat_gifSaveHintText)); + tooltipBackground = Theme.createRoundRectDrawable(dp(5), getThemedColor(Theme.key_chat_gifSaveHintBackground)); + tooltipBackgroundArrow.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_gifSaveHintBackground), PorterDuff.Mode.SRC_IN)); + + lockBackgroundPaint.setColor(getThemedColor(Theme.key_chat_messagePanelVoiceLockBackground)); + lockPaint.setColor(getThemedColor(Theme.key_chat_messagePanelVoiceLock)); + lockOutlinePaint.setColor(getThemedColor(Theme.key_chat_messagePanelVoiceLock)); + + micDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelVoiceLock), PorterDuff.Mode.SRC_IN)); + vidDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelVoiceLock), PorterDuff.Mode.SRC_IN)); + } + + private void scale(RectF rect, float s) { + final float cx = rect.centerX(), cy = rect.centerY(); + rect.left = lerp(cx, rect.left, s); + rect.right = lerp(cx, rect.right, s); + rect.top = lerp(cy, rect.top, s); + rect.bottom = lerp(cy, rect.bottom, s); + } + + private boolean oncePressed; + private boolean pausePressed; + @Override public boolean onTouchEvent(MotionEvent event) { - if (onceVisible && (recordCircle != null && recordCircle.snapAnimationProgress > .1f)) { - int x = (int) event.getX(); - int y = (int) event.getY(); - if (event.getAction() == MotionEvent.ACTION_DOWN) { - return pressed = onceRect.contains(x, y); - } else if (pressed) { - if (event.getAction() == MotionEvent.ACTION_UP) { - if (onceRect.contains(x, y)) { - voiceOnce = !voiceOnce; - periodDrawable.setValue(1, voiceOnce, true); - if (voiceOnce) { - showHintView(); - } else { - hideHintView(); - } - invalidate(); + final int x = (int) event.getX(); + final int y = (int) event.getY(); + + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (sendButtonVisible) { + pausePressed = pauseRect.contains(x, y); + } + if (onceVisible && (recordCircle != null && snapAnimationProgress > .1f)) { + oncePressed = onceRect.contains(x, y); + } + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (pausePressed && pauseRect.contains(x, y)) { + if (isInVideoMode()) { + if (slideText != null) { + slideText.setEnabled(false); + } + delegate.toggleVideoRecordingPause(); + } else { + MediaController.getInstance().toggleRecordingPause(); + delegate.needStartRecordAudio(0); + if (slideText != null) { + slideText.setEnabled(false); } } + pausePressed = oncePressed = false; + return true; + } else if (oncePressed && onceRect.contains(x, y)) { + voiceOnce = !voiceOnce; + periodDrawable.setValue(1, voiceOnce, true); + if (voiceOnce) { + showHintView(); + } else { + hideHintView(); + } + invalidate(); + pausePressed = oncePressed = false; return true; } + pausePressed = oncePressed = false; + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + pausePressed = oncePressed = false; } - return false; + return pausePressed || oncePressed; } @Override @@ -1155,20 +1582,81 @@ protected boolean verifyDrawable(@NonNull Drawable who) { } } + private float scale; + private float controlsScale; + private float slideToCancelProgress; + private float startTranslation; + private float lockAnimatedTranslation; + private float exitTransition; + private float snapAnimationProgress; + float idleProgress; + private float transformToSeekbar; + private float progressToSeekbarStep1, progressToSeekbarStep2; + private float slideToCancelLockProgress; + private boolean canceledByGesture; + private boolean sendButtonVisible; + private int slideDelta; + + @Keep + public float getExitTransition() { + return exitTransition; + } + + @Keep + public void setExitTransition(float value) { + exitTransition = value; + if (recordCircle != null) { + recordCircle.invalidate(); + } + } + + @Keep + public float getSlideToCancelProgress() { + return slideToCancelProgress; + } + + @Keep + public void setSlideToCancelProgress(float value) { + slideToCancelProgress = value; + float distance = getMeasuredWidth() * 0.35f; + if (distance > dp(140)) { + distance = dp(140); + } + slideDelta = (int) (-distance * (1f - slideToCancelProgress)); + if (recordCircle != null) { + recordCircle.invalidate(); + } + } + + @Keep + public float getLockAnimatedTranslation() { + return lockAnimatedTranslation; + } + @Keep + public void setLockAnimatedTranslation(float value) { + lockAnimatedTranslation = value; + if (recordCircle != null) { + recordCircle.invalidate(); + } + } + + @Keep + public void setSnapAnimationProgress(float value) { + snapAnimationProgress = value; + invalidate(); + } + + public boolean seekbarVisible() { + return !recordIsCanceled && transformToSeekbar > 0; + } + public class RecordCircle extends View { - public float scale; private float amplitude; private float animateToAmplitude; private float animateAmplitudeDiff; private long lastUpdateTime; - private float lockAnimatedTranslation; - private float snapAnimationProgress; - private float startTranslation; - private boolean sendButtonVisible; private boolean pressed; - private float transformToSeekbar; - private float exitTransition; public float progressToSeekbarStep3; private float progressToSendButton; @@ -1177,37 +1665,16 @@ public class RecordCircle extends View { BlobDrawable tinyWaveDrawable = new BlobDrawable(11, LiteMode.FLAGS_CHAT); BlobDrawable bigWaveDrawable = new BlobDrawable(12, LiteMode.FLAGS_CHAT); - private Drawable tooltipBackground; - private Drawable tooltipBackgroundArrow; - private String tooltipMessage; - private StaticLayout tooltipLayout; - private float tooltipWidth; - private TextPaint tooltipPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - private float tooltipAlpha; - private boolean showTooltip; - private long showTooltipStartTime; - - private float circleRadius = AndroidUtilities.dpf2(41); - private float circleRadiusAmplitude = AndroidUtilities.dp(30); - - Paint lockBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - Paint lockPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - Paint lockOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private float circleRadius = dpf2(41); + private float circleRadiusAmplitude = dp(30); RectF rectF = new RectF(); - Path path = new Path(); - float idleProgress; boolean incIdle; private VirtualViewHelper virtualViewHelper; private int paintAlpha; private float touchSlop; - public float slideToCancelProgress; - public float slideToCancelLockProgress; - private float progressToSeekbarStep1, progressToSeekbarStep2; - private int slideDelta; - private boolean canceledByGesture; private float lastMovingX; private float lastMovingY; @@ -1215,39 +1682,30 @@ public class RecordCircle extends View { private float wavesEnterAnimation = 0f; private boolean showWaves = true; - private Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); - public float drawingCx, drawingCy, drawingCircleRadius; public boolean voiceEnterTransitionInProgress; public boolean skipDraw; private int lastSize; + @Override + public void setTranslationY(float translationY) { + super.setTranslationY(translationY); + } + public RecordCircle(Context context) { super(context); virtualViewHelper = new VirtualViewHelper(this); ViewCompat.setAccessibilityDelegate(this, virtualViewHelper); - tinyWaveDrawable.minRadius = AndroidUtilities.dp(47); - tinyWaveDrawable.maxRadius = AndroidUtilities.dp(55); + tinyWaveDrawable.minRadius = dp(47); + tinyWaveDrawable.maxRadius = dp(55); tinyWaveDrawable.generateBlob(); - bigWaveDrawable.minRadius = AndroidUtilities.dp(47); - bigWaveDrawable.maxRadius = AndroidUtilities.dp(55); + bigWaveDrawable.minRadius = dp(47); + bigWaveDrawable.maxRadius = dp(55); bigWaveDrawable.generateBlob(); - - lockOutlinePaint.setStyle(Paint.Style.STROKE); - lockOutlinePaint.setStrokeCap(Paint.Cap.ROUND); - lockOutlinePaint.setStrokeWidth(AndroidUtilities.dpf2(1.7f)); - - lockShadowDrawable = getResources().getDrawable(R.drawable.lock_round_shadow); - lockShadowDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelVoiceLockShadow), PorterDuff.Mode.MULTIPLY)); - tooltipBackground = Theme.createRoundRectDrawable(AndroidUtilities.dp(5), getThemedColor(Theme.key_chat_gifSaveHintBackground)); - - tooltipPaint.setTextSize(AndroidUtilities.dp(14)); - tooltipBackgroundArrow = ContextCompat.getDrawable(context, R.drawable.tooltip_arrow); - tooltipMessage = LocaleController.getString("SlideUpToLock", R.string.SlideUpToLock); iconScale = 1f; final ViewConfiguration vc = ViewConfiguration.get(context); @@ -1297,21 +1755,16 @@ public void setScale(float value) { invalidate(); } - @Keep - public void setLockAnimatedTranslation(float value) { - lockAnimatedTranslation = value; - invalidate(); - } - - @Keep - public void setSnapAnimationProgress(float snapAnimationProgress) { - this.snapAnimationProgress = snapAnimationProgress; - invalidate(); + public float getControlsScale() { + return controlsScale; } @Keep - public float getLockAnimatedTranslation() { - return lockAnimatedTranslation; + public void setControlsScale(float value) { + controlsScale = value; + if (controlsView != null) { + controlsView.invalidate(); + } } public boolean isSendButtonVisible() { @@ -1321,129 +1774,91 @@ public boolean isSendButtonVisible() { public void setSendButtonInvisible() { sendButtonVisible = false; invalidate(); + if (controlsView != null) { + controlsView.invalidate(); + } } - public int setLockTranslation(float value) { - if (value == 10000) { + public void resetLockTranslation(boolean toLock) { + if (!toLock) { sendButtonVisible = false; lockAnimatedTranslation = -1; startTranslation = -1; - invalidate(); - snapAnimationProgress = 0; - transformToSeekbar = 0; - exitTransition = 0; - iconScale = 1f; - scale = 0f; - tooltipAlpha = 0f; - showTooltip = false; - progressToSendButton = 0f; slideToCancelProgress = 1f; slideToCancelLockProgress = 1f; - canceledByGesture = false; - return 0; - } else { - if (sendButtonVisible) { - return 2; - } - if (lockAnimatedTranslation == -1) { - startTranslation = value; - } - lockAnimatedTranslation = value; - invalidate(); - if (canceledByGesture || slideToCancelProgress < 0.7f) { - return 1; - } - if (startTranslation - lockAnimatedTranslation >= AndroidUtilities.dp(57)) { - sendButtonVisible = true; - return 2; - } + snapAnimationProgress = 0; + controlsScale = 0f; + } + invalidate(); + transformToSeekbar = 0; + isRecordingStateChanged(); + exitTransition = 0; + iconScale = 1f; + scale = 0f; + tooltipAlpha = 0f; + showTooltip = false; + progressToSendButton = 0f; + canceledByGesture = false; + if (controlsView != null) { + controlsView.invalidate(); } - return 1; } - @Override - public boolean onTouchEvent(MotionEvent event) { + public int setLockTranslation(float value) { if (sendButtonVisible) { - int x = (int) event.getX(); - int y = (int) event.getY(); - if (event.getAction() == MotionEvent.ACTION_DOWN) { - return pressed = pauseRect.contains(x, y); - } else if (pressed) { - if (event.getAction() == MotionEvent.ACTION_UP) { - if (pauseRect.contains(x, y)) { - if (isInVideoMode()) { - delegate.needStartRecordVideo(3, true, 0, voiceOnce ? 0x7FFFFFFF : 0); - } else { - MediaController.getInstance().stopRecording(2, true, 0, voiceOnce); - delegate.needStartRecordAudio(0); - } - if (slideText != null) { - slideText.setEnabled(false); - } - } - } - return true; - } + return 2; } - return false; + if (lockAnimatedTranslation == -1) { + startTranslation = value; + } + lockAnimatedTranslation = value; + invalidate(); + if (canceledByGesture || slideToCancelProgress < 0.7f) { + return 1; + } + if (startTranslation - lockAnimatedTranslation >= dp(57)) { + sendButtonVisible = true; + return 2; + } + return 1; } @SuppressLint("DrawAllocation") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int currentSize = MeasureSpec.getSize(widthMeasureSpec); - int h = AndroidUtilities.dp(194); - if (lastSize != currentSize) { - lastSize = currentSize; - tooltipLayout = new StaticLayout(tooltipMessage, tooltipPaint, AndroidUtilities.dp(220), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true); - int n = tooltipLayout.getLineCount(); - tooltipWidth = 0; - for (int i = 0; i < n; i++) { - float w = tooltipLayout.getLineWidth(i); - if (w > tooltipWidth) { - tooltipWidth = w; - } - } - } - if (tooltipLayout != null && tooltipLayout.getLineCount() > 1) { - h += tooltipLayout.getHeight() - tooltipLayout.getLineBottom(0); - } + int h = dp(194); super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(h, MeasureSpec.EXACTLY)); float distance = getMeasuredWidth() * 0.35f; - if (distance > AndroidUtilities.dp(140)) { - distance = AndroidUtilities.dp(140); + if (distance > dp(140)) { + distance = dp(140); } slideDelta = (int) (-distance * (1f - slideToCancelProgress)); } - public float moveProgress; - public float lockY, lockSize; - public float translationDy; - @Override protected void onDraw(Canvas canvas) { if (skipDraw) { return; } float multilinTooltipOffset = 0; - if (tooltipLayout != null && tooltipLayout.getLineCount() > 1) { - multilinTooltipOffset = tooltipLayout.getHeight() - tooltipLayout.getLineBottom(0); - } +// if (tooltipLayout != null && tooltipLayout.getLineCount() > 1) { +// multilinTooltipOffset = tooltipLayout.getHeight() - tooltipLayout.getLineBottom(0); +// } int cx = getMeasuredWidth() - AndroidUtilities.dp2(26); - int cy = (int) (AndroidUtilities.dp(170) + multilinTooltipOffset); - float yAdd = 0; + int cy = (int) (dp(170) + multilinTooltipOffset); +// float yAdd = 0; +// if (lockAnimatedTranslation != 10000) { +// yAdd = Math.max(0, (int) (startTranslation - lockAnimatedTranslation)); +// if (yAdd > AndroidUtilities.dp(57)) { +// yAdd = AndroidUtilities.dp(57); +// } +// } drawingCx = cx + slideDelta; drawingCy = cy; - if (lockAnimatedTranslation != 10000) { - yAdd = Math.max(0, (int) (startTranslation - lockAnimatedTranslation)); - if (yAdd > AndroidUtilities.dp(57)) { - yAdd = AndroidUtilities.dp(57); - } - } - float sc; float circleAlpha = 1f; if (scale <= 0.5f) { @@ -1494,7 +1909,7 @@ protected void onDraw(Canvas canvas) { progressToSeekbarStep2 = CubicBezierInterpolator.EASE_BOTH.getInterpolation(progressToSeekbarStep2); progressToSeekbarStep3 = CubicBezierInterpolator.EASE_BOTH.getInterpolation(progressToSeekbarStep3); - radius = radius + AndroidUtilities.dp(16) * progressToSeekbarStep1; + radius = radius + dp(16) * progressToSeekbarStep1; float toRadius = recordedAudioBackground.getMeasuredHeight() / 2f; radius = toRadius + (radius - toRadius) * (1f - progressToSeekbarStep2); @@ -1508,15 +1923,13 @@ protected void onDraw(Canvas canvas) { progressToSeekbarStep1 = CubicBezierInterpolator.EASE_BOTH.getInterpolation(progressToSeekbarStep1); exitProgress2 = CubicBezierInterpolator.EASE_BOTH.getInterpolation(exitProgress2); - radius = radius + AndroidUtilities.dp(16) * progressToSeekbarStep1; + radius = radius + dp(16) * progressToSeekbarStep1; radius *= (1f - exitProgress2); if (LiteMode.isEnabled(LiteMode.FLAGS_CHAT) && exitTransition > 0.6f) { circleAlpha = Math.max(0, 1f - (exitTransition - 0.6f) / 0.4f); } } - this.progressToSeekbarStep1 = progressToSeekbarStep1; - this.progressToSeekbarStep2 = progressToSeekbarStep2; if (canceledByGesture && slideToCancelProgress > 0.7f) { circleAlpha *= (1f - (slideToCancelProgress - 0.7f) / 0.3f); @@ -1549,291 +1962,108 @@ protected void onDraw(Canvas canvas) { replaceDrawable.setBounds(cx - replaceDrawable.getIntrinsicWidth() / 2, cy - replaceDrawable.getIntrinsicHeight() / 2, cx + replaceDrawable.getIntrinsicWidth() / 2, cy + replaceDrawable.getIntrinsicHeight() / 2); } - float moveProgress = this.moveProgress = 1.0f - yAdd / AndroidUtilities.dp(57); - - float lockSize; - float lockY; - float lockTopY; - float lockMiddleY; - - float lockRotation; - float transformToPauseProgress = 0; - if (incIdle) { idleProgress += 0.01f; if (idleProgress > 1f) { - incIdle = false; - idleProgress = 1f; - } - } else { - idleProgress -= 0.01f; - if (idleProgress < 0) { - incIdle = true; - idleProgress = 0; - } - } - - if (LiteMode.isEnabled(LiteMode.FLAGS_CHAT)) { - tinyWaveDrawable.minRadius = AndroidUtilities.dp(47); - tinyWaveDrawable.maxRadius = AndroidUtilities.dp(47) + AndroidUtilities.dp(15) * BlobDrawable.FORM_SMALL_MAX; - - bigWaveDrawable.minRadius = AndroidUtilities.dp(50); - bigWaveDrawable.maxRadius = AndroidUtilities.dp(50) + AndroidUtilities.dp(12) * BlobDrawable.FORM_BIG_MAX; - - bigWaveDrawable.updateAmplitude(dt); - bigWaveDrawable.update(bigWaveDrawable.amplitude, 1.01f); - tinyWaveDrawable.updateAmplitude(dt); - tinyWaveDrawable.update(tinyWaveDrawable.amplitude, 1.02f); - -// bigWaveDrawable.tick(radius); -// tinyWaveDrawable.tick(radius); - } - lastUpdateTime = System.currentTimeMillis(); - float slideToCancelProgress1 = slideToCancelProgress > 0.7f ? 1f : slideToCancelProgress / 0.7f; - - if (LiteMode.isEnabled(LiteMode.FLAGS_CHAT) && progressToSeekbarStep2 != 1 && exitProgress2 < 0.4f && slideToCancelProgress1 > 0 && !canceledByGesture) { - if (showWaves && wavesEnterAnimation != 1f) { - wavesEnterAnimation += 0.04f; - if (wavesEnterAnimation > 1f) { - wavesEnterAnimation = 1f; - } - } - if (!voiceEnterTransitionInProgress) { - float enter = CubicBezierInterpolator.EASE_OUT.getInterpolation(wavesEnterAnimation); - canvas.save(); - float s = scale * (1f - progressToSeekbarStep1) * slideToCancelProgress1 * enter * (BlobDrawable.SCALE_BIG_MIN + 1.4f * bigWaveDrawable.amplitude); - canvas.scale(s, s, cx + slideDelta, cy); - bigWaveDrawable.draw(cx + slideDelta, cy, canvas, bigWaveDrawable.paint); - canvas.restore(); - s = scale * (1f - progressToSeekbarStep1) * slideToCancelProgress1 * enter * (BlobDrawable.SCALE_SMALL_MIN + 1.4f * tinyWaveDrawable.amplitude); - canvas.save(); - canvas.scale(s, s, cx + slideDelta, cy); - tinyWaveDrawable.draw(cx + slideDelta, cy, canvas, tinyWaveDrawable.paint); - canvas.restore(); - } - } - - - if (!voiceEnterTransitionInProgress) { - paint.setAlpha((int) (paintAlpha * circleAlpha)); - if (this.scale == 1f) { - if (transformToSeekbar != 0) { - if (progressToSeekbarStep3 > 0 && recordedAudioBackground != null) { - float circleB = cy + radius; - float circleT = cy - radius; - float circleR = cx + slideDelta + radius; - float circleL = cx + slideDelta - radius; - - int topOffset = 0; - int leftOffset = 0; - - View transformToView = recordedAudioBackground; - View v = (View) transformToView.getParent(); - while (v != getParent()) { - topOffset += v.getY(); - leftOffset += v.getX(); - v = (View) v.getParent(); - } - - float seekbarT = transformToView.getY() + topOffset - getY(); - float seekbarB = transformToView.getY() + transformToView.getMeasuredHeight() + topOffset - getY(); - float seekbarR = transformToView.getX() + transformToView.getMeasuredWidth() + leftOffset - getX() - horizontalPadding; - float seekbarL = transformToView.getX() + leftOffset - getX() + horizontalPadding; - float toRadius = isInVideoMode() ? 0 : transformToView.getMeasuredHeight() / 2f; - - float top = seekbarT + (circleT - seekbarT) * (1f - progressToSeekbarStep3); - float bottom = seekbarB + (circleB - seekbarB) * (1f - progressToSeekbarStep3); - float left = seekbarL + (circleL - seekbarL) * (1f - progressToSeekbarStep3); - float right = seekbarR + (circleR - seekbarR) * (1f - progressToSeekbarStep3); - float transformRadius = toRadius + (radius - toRadius) * (1f - progressToSeekbarStep3); - - rectF.set(left, top, right, bottom); - canvas.drawRoundRect(rectF, transformRadius, transformRadius, paint); - } else { - canvas.drawCircle(cx + slideDelta, cy, radius, paint); - } - } else { - canvas.drawCircle(cx + slideDelta, cy, radius, paint); - } - canvas.save(); - float a = (1f - exitProgress2); - canvas.translate(slideDelta, 0); - drawIconInternal(canvas, drawable, replaceDrawable, progressToSendButton, (int) ((1f - progressToSeekbarStep2) * a * 255)); - canvas.restore(); - } - } - - if (isSendButtonVisible()) { - lockSize = this.lockSize = AndroidUtilities.dp(36); - lockY = this.lockY = AndroidUtilities.dp(60) + multilinTooltipOffset + AndroidUtilities.dpf2(30) * (1.0f - sc) - yAdd + AndroidUtilities.dpf2(14f) * moveProgress; - - lockMiddleY = lockY + lockSize / 2f - AndroidUtilities.dpf2(8) + AndroidUtilities.dpf2(2); - lockTopY = lockY + lockSize / 2f - AndroidUtilities.dpf2(16) + AndroidUtilities.dpf2(2); - float snapRotateBackProgress = moveProgress > 0.4f ? 1f : moveProgress / 0.4f; - - lockRotation = 9 * (1f - moveProgress) * (1f - snapAnimationProgress) - 15 * snapAnimationProgress * (1f - snapRotateBackProgress); - - transformToPauseProgress = moveProgress; - } else { - lockSize = this.lockSize = AndroidUtilities.dp(36) + (int) (AndroidUtilities.dp(14) * moveProgress); - lockY = this.lockY = AndroidUtilities.dp(60) + multilinTooltipOffset + (int) (AndroidUtilities.dp(30) * (1.0f - sc)) - (int) yAdd + (moveProgress) * idleProgress * -AndroidUtilities.dp(8); - lockMiddleY = lockY + lockSize / 2f - AndroidUtilities.dpf2(8) + AndroidUtilities.dpf2(2) + AndroidUtilities.dpf2(2) * moveProgress; - lockTopY = lockY + lockSize / 2f - AndroidUtilities.dpf2(16) + AndroidUtilities.dpf2(2) + AndroidUtilities.dpf2(2) * moveProgress; - lockRotation = 9 * (1f - moveProgress); - snapAnimationProgress = 0; - } - - if ((showTooltip && System.currentTimeMillis() - showTooltipStartTime > 200) || tooltipAlpha != 0f) { - if (moveProgress < 0.8f || isSendButtonVisible() || exitTransition != 0 || transformToSeekbar != 0) { - showTooltip = false; - } - if (showTooltip) { - if (tooltipAlpha != 1f) { - tooltipAlpha += dt / 150f; - if (tooltipAlpha >= 1f) { - tooltipAlpha = 1f; - SharedConfig.increaseLockRecordAudioVideoHintShowed(); - } - } - } else { - tooltipAlpha -= dt / 150f; - if (tooltipAlpha < 0) { - tooltipAlpha = 0f; - } - } - - - int alphaInt = (int) (tooltipAlpha * 255); - - tooltipBackground.setAlpha(alphaInt); - tooltipBackgroundArrow.setAlpha(alphaInt); - tooltipPaint.setAlpha(alphaInt); - - if (tooltipLayout != null) { - canvas.save(); - rectF.set(0, 0, getMeasuredWidth(), getMeasuredHeight()); - canvas.translate(getMeasuredWidth() - tooltipWidth - AndroidUtilities.dp(44), AndroidUtilities.dpf2(16)); - tooltipBackground.setBounds( - -AndroidUtilities.dp(8), -AndroidUtilities.dp(2), - (int) (tooltipWidth + AndroidUtilities.dp(36)), (int) (tooltipLayout.getHeight() + AndroidUtilities.dpf2(4)) - ); - tooltipBackground.draw(canvas); - tooltipLayout.draw(canvas); - canvas.restore(); - - canvas.save(); - canvas.translate(cx, AndroidUtilities.dpf2(17) + tooltipLayout.getHeight() / 2f - idleProgress * AndroidUtilities.dpf2(3f)); - path.reset(); - path.setLastPoint(-AndroidUtilities.dpf2(5), AndroidUtilities.dpf2(4)); - path.lineTo(0, 0); - path.lineTo(AndroidUtilities.dpf2(5), AndroidUtilities.dpf2(4)); - - p.setColor(Color.WHITE); - p.setAlpha(alphaInt); - p.setStyle(Paint.Style.STROKE); - p.setStrokeCap(Paint.Cap.ROUND); - p.setStrokeJoin(Paint.Join.ROUND); - p.setStrokeWidth(AndroidUtilities.dpf2(1.5f)); - canvas.drawPath(path, p); - canvas.restore(); - - canvas.save(); - tooltipBackgroundArrow.setBounds( - cx - tooltipBackgroundArrow.getIntrinsicWidth() / 2, (int) (tooltipLayout.getHeight() + AndroidUtilities.dpf2(20)), - cx + tooltipBackgroundArrow.getIntrinsicWidth() / 2, (int) (tooltipLayout.getHeight() + AndroidUtilities.dpf2(20)) + tooltipBackgroundArrow.getIntrinsicHeight() - ); - tooltipBackgroundArrow.draw(canvas); - canvas.restore(); + incIdle = false; + idleProgress = 1f; + } + } else { + idleProgress -= 0.01f; + if (idleProgress < 0) { + incIdle = true; + idleProgress = 0; } } - canvas.save(); - canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight() - textFieldContainer.getMeasuredHeight()); - float translation = 0; - if (1f - scale != 0) { - translation = 1f - scale; - } else if (progressToSeekbarStep2 != 0) { - translation = progressToSeekbarStep2; - } else if (exitProgress2 != 0) { - translation = exitProgress2; + if (LiteMode.isEnabled(LiteMode.FLAGS_CHAT)) { + tinyWaveDrawable.minRadius = dp(47); + tinyWaveDrawable.maxRadius = dp(47) + dp(15) * BlobDrawable.FORM_SMALL_MAX; + + bigWaveDrawable.minRadius = dp(50); + bigWaveDrawable.maxRadius = dp(50) + dp(12) * BlobDrawable.FORM_BIG_MAX; + + bigWaveDrawable.updateAmplitude(dt); + bigWaveDrawable.update(bigWaveDrawable.amplitude, 1.01f); + tinyWaveDrawable.updateAmplitude(dt); + tinyWaveDrawable.update(tinyWaveDrawable.amplitude, 1.02f); + +// bigWaveDrawable.tick(radius); +// tinyWaveDrawable.tick(radius); } - if (slideToCancelProgress < 0.7f || canceledByGesture) { - showTooltip = false; - if (slideToCancelLockProgress != 0) { - slideToCancelLockProgress -= 0.12f; - if (slideToCancelLockProgress < 0) { - slideToCancelLockProgress = 0; + lastUpdateTime = System.currentTimeMillis(); + float slideToCancelProgress1 = slideToCancelProgress > 0.7f ? 1f : slideToCancelProgress / 0.7f; + + if (LiteMode.isEnabled(LiteMode.FLAGS_CHAT) && progressToSeekbarStep2 != 1 && exitProgress2 < 0.4f && slideToCancelProgress1 > 0 && !canceledByGesture) { + if (showWaves && wavesEnterAnimation != 1f) { + wavesEnterAnimation += 0.04f; + if (wavesEnterAnimation > 1f) { + wavesEnterAnimation = 1f; } } - } else { - if (slideToCancelLockProgress != 1f) { - slideToCancelLockProgress += 0.12f; - if (slideToCancelLockProgress > 1f) { - slideToCancelLockProgress = 1f; - } + if (!voiceEnterTransitionInProgress) { + float enter = CubicBezierInterpolator.EASE_OUT.getInterpolation(wavesEnterAnimation); + canvas.save(); + float s = scale * (1f - progressToSeekbarStep1) * slideToCancelProgress1 * enter * (BlobDrawable.SCALE_BIG_MIN + 1.4f * bigWaveDrawable.amplitude); + canvas.scale(s, s, cx + slideDelta, cy); + bigWaveDrawable.draw(cx + slideDelta, cy, canvas, bigWaveDrawable.paint); + canvas.restore(); + s = scale * (1f - progressToSeekbarStep1) * slideToCancelProgress1 * enter * (BlobDrawable.SCALE_SMALL_MIN + 1.4f * tinyWaveDrawable.amplitude); + canvas.save(); + canvas.scale(s, s, cx + slideDelta, cy); + tinyWaveDrawable.draw(cx + slideDelta, cy, canvas, tinyWaveDrawable.paint); + canvas.restore(); } } - float maxTranslationDy = AndroidUtilities.dpf2(72); - float dy = ( - maxTranslationDy * translation - - AndroidUtilities.dpf2(20) * (progressToSeekbarStep1) * (1f - translation) - + maxTranslationDy * (1f - slideToCancelLockProgress) - ); - if (dy > maxTranslationDy) { - dy = maxTranslationDy; - } - this.translationDy = dy; - canvas.translate(0, dy); - float s = scale * (1f - progressToSeekbarStep2) * (1f - exitProgress2) * slideToCancelLockProgress; - canvas.scale(s, s, cx, lockMiddleY); + if (!voiceEnterTransitionInProgress) { + paint.setAlpha((int) (paintAlpha * circleAlpha)); + if (scale == 1f) { + if (transformToSeekbar != 0) { + if (!isInVideoMode && progressToSeekbarStep3 > 0 && recordedAudioBackground != null) { + float circleB = cy + radius; + float circleT = cy - radius; + float circleR = cx + slideDelta + radius; + float circleL = cx + slideDelta - radius; - rectF.set(cx - AndroidUtilities.dpf2(18), lockY, cx + AndroidUtilities.dpf2(18), lockY + lockSize); - lockShadowDrawable.setBounds( - (int) (rectF.left - AndroidUtilities.dpf2(3)), (int) (rectF.top - AndroidUtilities.dpf2(3)), - (int) (rectF.right + AndroidUtilities.dpf2(3)), (int) (rectF.bottom + AndroidUtilities.dpf2(3)) - ); - lockShadowDrawable.draw(canvas); - canvas.drawRoundRect(rectF, AndroidUtilities.dpf2(18), AndroidUtilities.dpf2(18), lockBackgroundPaint); - pauseRect.set(rectF); + int topOffset = 0; + int leftOffset = 0; - rectF.set( - cx - AndroidUtilities.dpf2(6) - AndroidUtilities.dpf2(2) * (1f - transformToPauseProgress), - lockMiddleY - AndroidUtilities.dpf2(2) * (1f - transformToPauseProgress), - cx + AndroidUtilities.dp(6) + AndroidUtilities.dpf2(2) * (1f - transformToPauseProgress), - lockMiddleY + AndroidUtilities.dp(12) + AndroidUtilities.dpf2(2) * (1f - transformToPauseProgress) - ); - float lockBottom = rectF.bottom; - float locCx = rectF.centerX(); - float locCy = rectF.centerY(); - canvas.save(); - canvas.translate(0, AndroidUtilities.dpf2(2) * (1f - moveProgress)); - canvas.rotate(lockRotation, locCx, locCy); - canvas.drawRoundRect(rectF, AndroidUtilities.dpf2(3), AndroidUtilities.dpf2(3), lockPaint); + View transformToView = recordedAudioBackground; + View v = (View) transformToView.getParent(); + while (v != getParent()) { + topOffset += v.getY(); + leftOffset += v.getX(); + v = (View) v.getParent(); + } - if (transformToPauseProgress != 1) { - canvas.drawCircle(locCx, locCy, AndroidUtilities.dpf2(2) * (1f - transformToPauseProgress), lockBackgroundPaint); - } + float seekbarT = transformToView.getY() + topOffset - getY(); + float seekbarB = transformToView.getY() + transformToView.getMeasuredHeight() + topOffset - getY(); + float seekbarR = transformToView.getX() + transformToView.getMeasuredWidth() + leftOffset - getX() - horizontalPadding; + float seekbarL = transformToView.getX() + leftOffset - getX() + horizontalPadding; + float toRadius = isInVideoMode() ? 0 : transformToView.getMeasuredHeight() / 2f; - if (transformToPauseProgress != 1f) { - rectF.set(0, 0, AndroidUtilities.dpf2(8), AndroidUtilities.dpf2(8)); - canvas.save(); - canvas.clipRect(0, 0, getMeasuredWidth(), dy + lockBottom + AndroidUtilities.dpf2(2) * (1f - moveProgress)); - canvas.translate(cx - AndroidUtilities.dpf2(4), lockTopY - AndroidUtilities.dpf2(1.5f) * (1f - idleProgress) * (moveProgress) - AndroidUtilities.dpf2(2) * (1f - moveProgress) + AndroidUtilities.dpf2(12) * transformToPauseProgress + AndroidUtilities.dpf2(2) * snapAnimationProgress); - if (lockRotation > 0) { - canvas.rotate(lockRotation, AndroidUtilities.dp(8), AndroidUtilities.dp(8)); + float top = seekbarT + (circleT - seekbarT) * (1f - progressToSeekbarStep3); + float bottom = seekbarB + (circleB - seekbarB) * (1f - progressToSeekbarStep3); + float left = seekbarL + (circleL - seekbarL) * (1f - progressToSeekbarStep3); + float right = seekbarR + (circleR - seekbarR) * (1f - progressToSeekbarStep3); + float transformRadius = toRadius + (radius - toRadius) * (1f - progressToSeekbarStep3); + + rectF.set(left, top, right, bottom); + canvas.drawRoundRect(rectF, transformRadius, transformRadius, paint); + } else { + canvas.drawCircle(cx + slideDelta, cy, radius * (1f - progressToSeekbarStep3), paint); + } + } else { + canvas.drawCircle(cx + slideDelta, cy, radius, paint); + } + canvas.save(); + float a = (1f - exitProgress2); + canvas.translate(slideDelta, 0); + drawIconInternal(canvas, drawable, replaceDrawable, progressToSendButton, (int) ((1f - progressToSeekbarStep2) * a * 255)); + canvas.restore(); } - canvas.drawLine(AndroidUtilities.dpf2(8), AndroidUtilities.dpf2(4), AndroidUtilities.dpf2(8), AndroidUtilities.dpf2(6) + AndroidUtilities.dpf2(4) * (1f - transformToPauseProgress), lockOutlinePaint); - canvas.drawArc(rectF, 0, -180, false, lockOutlinePaint); - canvas.drawLine( - 0, AndroidUtilities.dpf2(4), - 0, AndroidUtilities.dpf2(4) + AndroidUtilities.dpf2(4) * idleProgress * (moveProgress) * (isSendButtonVisible() ? 0 : 1) + AndroidUtilities.dpf2(4) * snapAnimationProgress * (1f - moveProgress), - lockOutlinePaint - ); - canvas.restore(); } - canvas.restore(); - canvas.restore(); if (scale != 1f) { canvas.drawCircle(cx + slideDelta, cy, radius, paint); @@ -1849,8 +2079,8 @@ protected void onDraw(Canvas canvas) { @Override public void invalidate() { super.invalidate(); - if (onceButton != null) { - onceButton.invalidate(); + if (controlsView != null) { + controlsView.invalidate(); } } @@ -1927,55 +2157,14 @@ public float getTransformToSeekbarProgressStep3() { return progressToSeekbarStep3; } - @Keep - public float getExitTransition() { - return exitTransition; - } - - @Keep - public void setExitTransition(float exitTransition) { - this.exitTransition = exitTransition; - invalidate(); - } - public void updateColors() { paint.setColor(getThemedColor(Theme.key_chat_messagePanelVoiceBackground)); tinyWaveDrawable.paint.setColor(ColorUtils.setAlphaComponent(getThemedColor(Theme.key_chat_messagePanelVoiceBackground), (int) (255 * WaveDrawable.CIRCLE_ALPHA_2))); bigWaveDrawable.paint.setColor(ColorUtils.setAlphaComponent(getThemedColor(Theme.key_chat_messagePanelVoiceBackground), (int) (255 * WaveDrawable.CIRCLE_ALPHA_1))); - tooltipPaint.setColor(getThemedColor(Theme.key_chat_gifSaveHintText)); - tooltipBackground = Theme.createRoundRectDrawable(AndroidUtilities.dp(5), getThemedColor(Theme.key_chat_gifSaveHintBackground)); - tooltipBackgroundArrow.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_gifSaveHintBackground), PorterDuff.Mode.SRC_IN)); - - lockBackgroundPaint.setColor(getThemedColor(Theme.key_chat_messagePanelVoiceLockBackground)); - lockPaint.setColor(getThemedColor(Theme.key_chat_messagePanelVoiceLock)); - lockOutlinePaint.setColor(getThemedColor(Theme.key_chat_messagePanelVoiceLock)); paintAlpha = paint.getAlpha(); } - public void showTooltipIfNeed() { - if (SharedConfig.lockRecordAudioVideoHint < 3) { - showTooltip = true; - showTooltipStartTime = System.currentTimeMillis(); - } - } - - @Keep - public float getSlideToCancelProgress() { - return slideToCancelProgress; - } - - @Keep - public void setSlideToCancelProgress(float slideToCancelProgress) { - this.slideToCancelProgress = slideToCancelProgress; - float distance = getMeasuredWidth() * 0.35f; - if (distance > AndroidUtilities.dp(140)) { - distance = AndroidUtilities.dp(140); - } - slideDelta = (int) (-distance * (1f - slideToCancelProgress)); - invalidate(); - } - public void canceledByGesture() { canceledByGesture = true; } @@ -2044,7 +2233,7 @@ protected int getVirtualViewAt(float x, float y) { protected void getVisibleVirtualViews(List list) { if (isSendButtonVisible()) { list.add(1); - list.add(2); +// list.add(2); list.add(3); } } @@ -2057,7 +2246,7 @@ protected void onPopulateNodeForVirtualView(int id, @NonNull AccessibilityNodeIn } else if (id == 2) { rect.set((int) pauseRect.left, (int) pauseRect.top, (int) pauseRect.right, (int) pauseRect.bottom); info.setBoundsInParent(rect); - info.setText(LocaleController.getString("Stop", R.string.Stop)); + info.setText(LocaleController.getString(R.string.Stop)); } else if (id == 3 && recordCircle != null) { if (slideText != null && slideText.cancelRect != null) { AndroidUtilities.rectTmp2.set(slideText.cancelRect); @@ -2099,6 +2288,8 @@ public ChatActivityEnterView(Activity context, SizeNotifierFrameLayout parent, C setClipChildren(false); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordStarted); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordPaused); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordResumed); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordStartError); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordStopped); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordProgressChanged); @@ -2136,7 +2327,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { }; textFieldContainer.setClipChildren(false); textFieldContainer.setClipToPadding(false); - textFieldContainer.setPadding(0, AndroidUtilities.dp(1), 0, 0); + textFieldContainer.setPadding(0, dp(1), 0, 0); addView(textFieldContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 0, 1, 0, 0)); FrameLayout frameLayout = messageEditTextContainer = new FrameLayout(context) { @@ -2144,7 +2335,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (scheduledButton != null) { - int x = getMeasuredWidth() - AndroidUtilities.dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - AndroidUtilities.dp(48); + int x = getMeasuredWidth() - dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - dp(48); scheduledButton.layout(x, scheduledButton.getTop(), x + scheduledButton.getMeasuredWidth(), scheduledButton.getBottom()); } if (!animationParamsX.isEmpty()) { @@ -2178,16 +2369,16 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (topAlpha <= 0 && bottomAlpha <= 0) { return super.drawChild(canvas, child, drawingTime); } - canvas.saveLayerAlpha(0, 0, messageEditText.getX() + messageEditText.getMeasuredWidth() + AndroidUtilities.dp(5), messageEditText.getY() + messageEditText.getMeasuredHeight() + AndroidUtilities.dp(2), 0xFF, Canvas.ALL_SAVE_FLAG); + canvas.saveLayerAlpha(0, 0, messageEditText.getX() + messageEditText.getMeasuredWidth() + dp(5), messageEditText.getY() + messageEditText.getMeasuredHeight() + dp(2), 0xFF, Canvas.ALL_SAVE_FLAG); final boolean result = super.drawChild(canvas, child, drawingTime); canvas.save(); if (topAlpha > 0) { AndroidUtilities.rectTmp.set( - messageEditText.getX() - AndroidUtilities.dp(5), + messageEditText.getX() - dp(5), messageEditText.getY() + animatedTop, - messageEditText.getX() + messageEditText.getMeasuredWidth() + AndroidUtilities.dp(5), - messageEditText.getY() + animatedTop + AndroidUtilities.dp(13) + messageEditText.getX() + messageEditText.getMeasuredWidth() + dp(5), + messageEditText.getY() + animatedTop + dp(13) ); clipMatrix.reset(); clipMatrix.postScale(1f, AndroidUtilities.rectTmp.height() / 16f); @@ -2199,10 +2390,10 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (bottomAlpha > 0) { AndroidUtilities.rectTmp.set( - messageEditText.getX() - AndroidUtilities.dp(5), - messageEditText.getY() + messageEditText.getMeasuredHeight() - AndroidUtilities.dp(13 + 2), - messageEditText.getX() + messageEditText.getMeasuredWidth() + AndroidUtilities.dp(5), - messageEditText.getY() + messageEditText.getMeasuredHeight() + AndroidUtilities.dp(2) + messageEditText.getX() - dp(5), + messageEditText.getY() + messageEditText.getMeasuredHeight() - dp(13 + 2), + messageEditText.getX() + messageEditText.getMeasuredWidth() + dp(5), + messageEditText.getY() + messageEditText.getMeasuredHeight() + dp(2) ); clipMatrix.reset(); clipMatrix.postScale(1f, AndroidUtilities.rectTmp.height() / 16f); @@ -2222,6 +2413,16 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { } return super.drawChild(canvas, child, drawingTime); } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + return super.dispatchTouchEvent(ev); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return super.onTouchEvent(event); + } }; frameLayout.setClipChildren(false); textFieldContainer.addView(frameLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM, 0, 0, 48, 0)); @@ -2231,15 +2432,15 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (getTag() != null && attachLayout != null && !emojiViewVisible && !MediaDataController.getInstance(currentAccount).getUnreadStickerSets().isEmpty() && dotPaint != null) { - int x = getWidth() / 2 + AndroidUtilities.dp(4 + 5); - int y = getHeight() / 2 - AndroidUtilities.dp(13 - 5); - canvas.drawCircle(x, y, AndroidUtilities.dp(5), dotPaint); + int x = getWidth() / 2 + dp(4 + 5); + int y = getHeight() / 2 - dp(13 - 5); + canvas.drawCircle(x, y, dp(5), dotPaint); } } }; emojiButton.setContentDescription(LocaleController.getString(R.string.AccDescrEmojiButton)); emojiButton.setFocusable(true); - int padding = AndroidUtilities.dp(9.5f); + int padding = dp(9.5f); emojiButton.setPadding(padding, padding, padding, padding); emojiButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelIcons), PorterDuff.Mode.SRC_IN)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -2292,7 +2493,7 @@ protected void onDraw(Canvas canvas) { attachLayout = new LinearLayout(context); attachLayout.setOrientation(LinearLayout.HORIZONTAL); attachLayout.setEnabled(false); - attachLayout.setPivotX(AndroidUtilities.dp(48)); + attachLayout.setPivotX(dp(48)); attachLayout.setClipChildren(false); messageEditTextContainer.addView(attachLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 48, Gravity.BOTTOM | Gravity.RIGHT)); @@ -2318,7 +2519,7 @@ public void onClick(View v) { notifySilentDrawable.setCrossOut(silent, true); notifyButton.setImageDrawable(notifySilentDrawable); MessagesController.getNotificationsSettings(currentAccount).edit().putBoolean("silent_" + dialog_id, silent).commit(); - NotificationsController.getInstance(currentAccount).updateServerNotificationsSettings(dialog_id, fragment == null ? 0 :fragment.getTopicId()); + NotificationsController.getInstance(currentAccount).updateServerNotificationsSettings(dialog_id, fragment == null ? 0 : fragment.getTopicId()); UndoView undoView = fragment.getUndoView(); if (undoView != null) { undoView.showWithAction(0, !silent ? UndoView.ACTION_NOTIFY_ON : UndoView.ACTION_NOTIFY_OFF, null); @@ -2380,6 +2581,11 @@ public boolean onTouchEvent(MotionEvent event) { audioVideoButtonContainer = new FrameLayout(context) { + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + } + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return true; @@ -2429,7 +2635,7 @@ public boolean onTouchEvent(MotionEvent motionEvent) { return true; } else if (motionEvent.getAction() == MotionEvent.ACTION_UP || motionEvent.getAction() == MotionEvent.ACTION_CANCEL) { if (motionEvent.getAction() == MotionEvent.ACTION_CANCEL && recordingAudioVideo) { - if (recordCircle.slideToCancelProgress < 0.7f) { + if (slideToCancelProgress < 0.7f) { if (hasRecordVideo && isInVideoMode()) { CameraController.getInstance().cancelOnInitRunnable(onFinishInitCameraRunnable); delegate.needStartRecordVideo(2, true, 0, voiceOnce ? 0x7FFFFFFF : 0); @@ -2437,10 +2643,11 @@ public boolean onTouchEvent(MotionEvent motionEvent) { delegate.needStartRecordAudio(0); MediaController.getInstance().stopRecording(0, false, 0, voiceOnce); } + millisecondsRecorded = 0; recordingAudioVideo = false; updateRecordInterface(RECORD_STATE_CANCEL_BY_GESTURE); } else { - recordCircle.sendButtonVisible = true; + sendButtonVisible = true; startLockTransition(); } return false; @@ -2463,6 +2670,7 @@ public boolean onTouchEvent(MotionEvent motionEvent) { delegate.needStartRecordAudio(0); MediaController.getInstance().stopRecording(0, false, 0, voiceOnce); } + millisecondsRecorded = 0; recordingAudioVideo = false; updateRecordInterface(RECORD_STATE_CANCEL_BY_GESTURE); } else { @@ -2515,8 +2723,8 @@ public boolean onTouchEvent(MotionEvent motionEvent) { if (startedDraggingX == -1) { startedDraggingX = x; distCanMove = (float) (sizeNotifierLayout.getMeasuredWidth() * 0.35); - if (distCanMove > AndroidUtilities.dp(140)) { - distCanMove = AndroidUtilities.dp(140); + if (distCanMove > dp(140)) { + distCanMove = dp(140); } } @@ -2532,9 +2740,7 @@ public boolean onTouchEvent(MotionEvent motionEvent) { if (slideText != null) { slideText.setSlideX(alpha); } - if (recordCircle != null) { - recordCircle.setSlideToCancelProgress(alpha); - } + setSlideToCancelProgress(alpha); } if (alpha == 0) { @@ -2729,7 +2935,7 @@ public boolean onTouchEvent(MotionEvent motionEvent) { audioVideoSendButton.setFocusable(true); audioVideoSendButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); audioVideoSendButton.setAccessibilityDelegate(mediaMessageButtonsDelegate); - padding = AndroidUtilities.dp(9.5f); + padding = dp(9.5f); audioVideoSendButton.setPadding(padding, padding, padding, padding); audioVideoSendButton.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelIcons), PorterDuff.Mode.SRC_IN)); audioVideoButtonContainer.addView(audioVideoSendButton, LayoutHelper.createFrame(48, 48)); @@ -2774,6 +2980,11 @@ protected int getCurrentColor() { sendButton = new View(context) { + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + } + private int drawableColor; private float animationProgress; private float animateBounce; @@ -2784,14 +2995,14 @@ protected int getCurrentColor() { @Override protected void onDraw(Canvas canvas) { - int rad = AndroidUtilities.dp(20); + int rad = dp(20); int center = getMeasuredWidth() >> 1; int x = (getMeasuredWidth() - sendButtonDrawable.getIntrinsicWidth()) / 2; int y = (getMeasuredHeight() - sendButtonDrawable.getIntrinsicHeight()) / 2; if (isInScheduleMode()) { - y -= AndroidUtilities.dp(1); + y -= dp(1); } else { - x += AndroidUtilities.dp(2); + x += dp(2); } int color; @@ -2853,16 +3064,16 @@ protected void onDraw(Canvas canvas) { float p = animationProgress; if (p <= 0.25f) { float progress = p / 0.25f; - rad += AndroidUtilities.dp(2) * CubicBezierInterpolator.EASE_IN.getInterpolation(progress); + rad += dp(2) * CubicBezierInterpolator.EASE_IN.getInterpolation(progress); } else { p -= 0.25f; if (p <= 0.5f) { float progress = p / 0.5f; - rad += AndroidUtilities.dp(2) - AndroidUtilities.dp(3) * CubicBezierInterpolator.EASE_IN.getInterpolation(progress); + rad += dp(2) - dp(3) * CubicBezierInterpolator.EASE_IN.getInterpolation(progress); } else { p -= 0.5f; float progress = p / 0.25f; - rad += -AndroidUtilities.dp(1) + AndroidUtilities.dp(1) * CubicBezierInterpolator.EASE_IN.getInterpolation(progress); + rad += -dp(1) + dp(1) * CubicBezierInterpolator.EASE_IN.getInterpolation(progress); } } } else { @@ -2910,7 +3121,7 @@ public boolean onTouchEvent(MotionEvent event) { slowModeButton.setScaleX(0.1f); slowModeButton.setScaleY(0.1f); slowModeButton.setAlpha(0.0f); - slowModeButton.setPadding(0, 0, AndroidUtilities.dp(13), 0); + slowModeButton.setPadding(0, 0, dp(13), 0); slowModeButton.setGravity(Gravity.RIGHT | Gravity.CENTER_VERTICAL); slowModeButton.setTextColor(getThemedColor(Theme.key_chat_messagePanelIcons)); sendButtonContainer.addView(slowModeButton, LayoutHelper.createFrame(64, 48, Gravity.RIGHT | Gravity.TOP)); @@ -2927,8 +3138,8 @@ public boolean onTouchEvent(MotionEvent event) { }); SharedPreferences sharedPreferences = MessagesController.getGlobalEmojiSettings(); - keyboardHeight = sharedPreferences.getInt("kbd_height", AndroidUtilities.dp(200)); - keyboardHeightLand = sharedPreferences.getInt("kbd_height_land3", AndroidUtilities.dp(200)); + keyboardHeight = sharedPreferences.getInt("kbd_height", dp(200)); + keyboardHeightLand = sharedPreferences.getInt("kbd_height_land3", dp(200)); setRecordVideoButtonVisible(false, false); checkSendButton(false); @@ -3056,11 +3267,11 @@ private void createDoneButton() { textFieldContainer.addView(doneButtonContainer, LayoutHelper.createFrame(48, 48, Gravity.BOTTOM | Gravity.RIGHT)); doneButtonContainer.setOnClickListener(view -> doneEditingMessage()); - Drawable doneCircleDrawable = Theme.createCircleDrawable(AndroidUtilities.dp(16), getThemedColor(Theme.key_chat_messagePanelSend)); + Drawable doneCircleDrawable = Theme.createCircleDrawable(dp(16), getThemedColor(Theme.key_chat_messagePanelSend)); doneCheckDrawable = getContext().getResources().getDrawable(R.drawable.input_done).mutate(); doneCheckDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_messagePanelVoicePressed), PorterDuff.Mode.MULTIPLY)); - CombinedDrawable combinedDrawable = new CombinedDrawable(doneCircleDrawable, doneCheckDrawable, 0, AndroidUtilities.dp(1)); - combinedDrawable.setCustomSize(AndroidUtilities.dp(32), AndroidUtilities.dp(32)); + CombinedDrawable combinedDrawable = new CombinedDrawable(doneCircleDrawable, doneCheckDrawable, 0, dp(1)); + combinedDrawable.setCustomSize(dp(32), dp(32)); doneButtonImage = new ImageView(getContext()); doneButtonImage.setScaleType(ImageView.ScaleType.CENTER); @@ -3150,6 +3361,11 @@ public void setVisibility(int visibility) { super.setVisibility(visibility); updateSendAsButton(); } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + return super.dispatchTouchEvent(ev); + } }; recordedAudioPanel.setVisibility(audioToSend == null ? GONE : VISIBLE); recordedAudioPanel.setFocusable(true); @@ -3175,6 +3391,7 @@ public void setVisibility(int visibility) { }); videoTimelineView = new VideoTimelineView(getContext()); + videoTimelineView.setVisibility(INVISIBLE); videoTimelineView.useClip = !shouldDrawBackground; videoTimelineView.setRoundFrames(true); videoTimelineView.setDelegate(new VideoTimelineView.VideoTimelineViewDelegate() { @@ -3219,7 +3436,7 @@ protected void dispatchDraw(Canvas canvas) { getBackground().draw(canvas); } }; - recordedAudioBackground.setBackgroundDrawable(Theme.createRoundRectDrawable(AndroidUtilities.dp(18), getThemedColor(Theme.key_chat_recordedVoiceBackground))); + recordedAudioBackground.setBackgroundDrawable(Theme.createRoundRectDrawable(dp(18), getThemedColor(Theme.key_chat_recordedVoiceBackground))); recordedAudioPanel.addView(recordedAudioBackground, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.CENTER_VERTICAL | Gravity.LEFT, 48, 0, 0, 0)); LinearLayout waveFormTimerLayout = new LinearLayout(getContext()); @@ -3228,7 +3445,7 @@ protected void dispatchDraw(Canvas canvas) { recordedAudioPlayButton = new ImageView(getContext()); Matrix matrix = new Matrix(); - matrix.postScale(0.8f, 0.8f, AndroidUtilities.dpf2(24), AndroidUtilities.dpf2(24)); + matrix.postScale(0.8f, 0.8f, dpf2(24), dpf2(24)); recordedAudioPlayButton.setImageMatrix(matrix); recordedAudioPlayButton.setImageDrawable(playPauseDrawable = new MediaActionDrawable()); recordedAudioPlayButton.setScaleType(ImageView.ScaleType.MATRIX); @@ -3250,6 +3467,7 @@ protected void dispatchDraw(Canvas canvas) { }); recordedAudioSeekBar = new SeekBarWaveformView(getContext()); + recordedAudioSeekBar.setVisibility(View.INVISIBLE); waveFormTimerLayout.addView(recordedAudioSeekBar, LayoutHelper.createLinear(0, 32, 1f, Gravity.CENTER_VERTICAL, 0, 0, 4, 0)); recordedAudioTimeTextView = new TextView(getContext()); @@ -3274,6 +3492,8 @@ private void resetRecordedState() { } new File(audioToSendPath).delete(); } + MediaController.getInstance().stopRecording(0, false, 0, false); + millisecondsRecorded = 0; hideRecordedAudioPanel(false); checkSendButton(true); } @@ -3289,17 +3509,17 @@ private void createSenderSelectView() { hidePopup(true, true); return; } - if (delegate.measureKeyboardHeight() > AndroidUtilities.dp(20)) { + if (delegate.measureKeyboardHeight() > dp(20)) { int totalHeight = delegate.getContentViewHeight(); int keyboard = delegate.measureKeyboardHeight(); - if (keyboard <= AndroidUtilities.dp(20)) { + if (keyboard <= dp(20)) { totalHeight += keyboard; } if (emojiViewVisible) { totalHeight -= getEmojiPadding(); } - if (totalHeight < AndroidUtilities.dp(200)) { + if (totalHeight < dp(200)) { onKeyboardClosed = () -> senderSelectView.callOnClick(); closeKeyboard(); return; @@ -3396,12 +3616,12 @@ private void createSenderSelectView() { senderSelectView.getLocationInWindow(location); float endX = location[0], endY = location[1]; - float off = AndroidUtilities.dp(5); - float startX = loc[0] + popupX + off + AndroidUtilities.dp(4) + offX, startY = loc[1] + popupY + off + offY; + float off = dp(5); + float startX = loc[0] + popupX + off + dp(4) + offX, startY = loc[1] + popupY + off + offY; avatar.setTranslationX(startX); avatar.setTranslationY(startY); - float startScale = (float) (SenderSelectPopup.AVATAR_SIZE_DP - 10) / SenderSelectPopup.AVATAR_SIZE_DP, endScale = senderSelectView.getLayoutParams().width / (float) AndroidUtilities.dp(SenderSelectPopup.AVATAR_SIZE_DP); + float startScale = (float) (SenderSelectPopup.AVATAR_SIZE_DP - 10) / SenderSelectPopup.AVATAR_SIZE_DP, endScale = senderSelectView.getLayoutParams().width / (float) dp(SenderSelectPopup.AVATAR_SIZE_DP); avatar.setPivotX(0); avatar.setPivotY(0); avatar.setScaleX(startScale); @@ -3457,17 +3677,17 @@ public boolean onPreDraw() { } }), new SpringAnimation(avatar, DynamicAnimation.TRANSLATION_X) - .setStartValue(MathUtils.clamp(startX, endX - AndroidUtilities.dp(6), startX)) + .setStartValue(MathUtils.clamp(startX, endX - dp(6), startX)) .setSpring(new SpringForce(endX) .setStiffness(translationStiffness) .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)) - .setMinValue(endX - AndroidUtilities.dp(6)), + .setMinValue(endX - dp(6)), new SpringAnimation(avatar, DynamicAnimation.TRANSLATION_Y) - .setStartValue(MathUtils.clamp(startY, startY, endY + AndroidUtilities.dp(6))) + .setStartValue(MathUtils.clamp(startY, startY, endY + dp(6))) .setSpring(new SpringForce(endY) .setStiffness(translationStiffness) .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)) - .setMaxValue(endY + AndroidUtilities.dp(6)) + .setMaxValue(endY + dp(6)) .addUpdateListener(new DynamicAnimation.OnAnimationUpdateListener() { boolean performedHapticFeedback = false; @@ -3539,13 +3759,13 @@ public void dismiss() { senderSelectPopupWindow.setOutsideTouchable(true); senderSelectPopupWindow.setClippingEnabled(true); senderSelectPopupWindow.setFocusable(true); - senderSelectPopupWindow.getContentView().measure(View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST)); + senderSelectPopupWindow.getContentView().measure(View.MeasureSpec.makeMeasureSpec(dp(1000), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(dp(1000), View.MeasureSpec.AT_MOST)); senderSelectPopupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED); senderSelectPopupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); senderSelectPopupWindow.getContentView().setFocusableInTouchMode(true); senderSelectPopupWindow.setAnimationEnabled(false); - int pad = -AndroidUtilities.dp(4); + int pad = -dp(4); int[] location = new int[2]; int popupX = pad; if (AndroidUtilities.isTablet()) { @@ -3555,22 +3775,22 @@ public void dismiss() { int totalHeight = delegate.getContentViewHeight(); int height = senderSelectPopupWindow.getContentView().getMeasuredHeight(); int keyboard = delegate.measureKeyboardHeight(); - if (keyboard <= AndroidUtilities.dp(20)) { + if (keyboard <= dp(20)) { totalHeight += keyboard; } if (emojiViewVisible) { totalHeight -= getEmojiPadding(); } - int shadowPad = AndroidUtilities.dp(1); + int shadowPad = dp(1); int popupY; if (height < totalHeight + pad * 2 - (parentFragment.isInBubbleMode() ? 0 : AndroidUtilities.statusBarHeight) - senderSelectPopupWindow.headerText.getMeasuredHeight()) { ChatActivityEnterView.this.getLocationInWindow(location); - popupY = location[1] - height - pad - AndroidUtilities.dp(2); - fl.addView(senderSelectPopupWindow.dimView, new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, popupY + pad + height + shadowPad + AndroidUtilities.dp(2))); + popupY = location[1] - height - pad - dp(2); + fl.addView(senderSelectPopupWindow.dimView, new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, popupY + pad + height + shadowPad + dp(2))); } else { popupY = parentFragment.isInBubbleMode() ? 0 : AndroidUtilities.statusBarHeight; - int off = AndroidUtilities.dp(14); + int off = dp(14); senderSelectPopupWindow.recyclerContainer.getLayoutParams().height = totalHeight - popupY - off - getHeightWithTopView(); fl.addView(senderSelectPopupWindow.dimView, new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, off + popupY + senderSelectPopupWindow.recyclerContainer.getLayoutParams().height + shadowPad)); } @@ -3636,7 +3856,7 @@ private void createBotWebViewButton() { } private void createRecordCircle() { - createOnceButton(); + createControlsView(); if (recordCircle != null) { return; } @@ -3645,16 +3865,13 @@ private void createRecordCircle() { sizeNotifierLayout.addView(recordCircle, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); } - private void createOnceButton() { - if (onceButton != null) { - return; - } - if (delegate == null || !delegate.onceVoiceAvailable()) { + private void createControlsView() { + if (controlsView != null) { return; } - onceButton = new OnceButton(getContext()); - onceButton.setVisibility(GONE); - sizeNotifierLayout.addView(onceButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); + controlsView = new ControlsView(getContext()); + controlsView.setVisibility(GONE); + sizeNotifierLayout.addView(controlsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); } private void showRestrictedHint() { @@ -3749,10 +3966,10 @@ private void startLockTransition() { AnimatorSet animatorSet = new AnimatorSet(); performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); - ObjectAnimator translate = ObjectAnimator.ofFloat(recordCircle, "lockAnimatedTranslation", recordCircle.startTranslation); + ObjectAnimator translate = ObjectAnimator.ofFloat(this, "lockAnimatedTranslation", startTranslation); translate.setStartDelay(100); translate.setDuration(350); - ObjectAnimator snap = ObjectAnimator.ofFloat(recordCircle, "snapAnimationProgress", 1f); + ObjectAnimator snap = ObjectAnimator.ofFloat(this, "snapAnimationProgress", 1f); snap.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); snap.setDuration(250); @@ -3761,7 +3978,7 @@ private void startLockTransition() { animatorSet.playTogether( snap, translate, - ObjectAnimator.ofFloat(recordCircle, "slideToCancelProgress", 1f).setDuration(200), + ObjectAnimator.ofFloat(this, "slideToCancelProgress", 1f).setDuration(200), ObjectAnimator.ofFloat(slideText, "cancelToProgress", 1f) ); @@ -3782,13 +3999,13 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (clip) { canvas.save(); if (child == textFieldContainer) { - int top = (int) (animatedTop + AndroidUtilities.dp(2) + chatSearchExpandOffset); + int top = (int) (animatedTop + dp(2) + chatSearchExpandOffset); if (topView != null && topView.getVisibility() == View.VISIBLE) { top += topView.getHeight(); } canvas.clipRect(0, top, getMeasuredWidth(), getMeasuredHeight()); } else { - canvas.clipRect(0, animatedTop, getMeasuredWidth(), animatedTop + child.getLayoutParams().height + AndroidUtilities.dp(2)); + canvas.clipRect(0, animatedTop, getMeasuredWidth(), animatedTop + child.getLayoutParams().height + dp(2)); } } boolean result = super.drawChild(canvas, child, drawingTime); @@ -3837,6 +4054,7 @@ public boolean hasOverlappingRendering() { return false; } + private ActionBarMenuSubItem actionScheduleButton; private boolean onSendLongClick(View view) { if (isInScheduleMode()) { return false; @@ -3874,14 +4092,14 @@ public boolean onTouch(View v, MotionEvent event) { boolean scheduleButtonValue = parentFragment != null && parentFragment.canScheduleMessage(); boolean sendWithoutSoundButtonValue = !(self || slowModeTimer > 0 && !isInScheduleMode()); if (scheduleButtonValue) { - ActionBarMenuSubItem scheduleButton = new ActionBarMenuSubItem(getContext(), true, !sendWithoutSoundButtonValue, resourcesProvider); + actionScheduleButton = new ActionBarMenuSubItem(getContext(), true, !sendWithoutSoundButtonValue, resourcesProvider); if (self) { - scheduleButton.setTextAndIcon(LocaleController.getString("SetReminder", R.string.SetReminder), R.drawable.msg_calendar2); + actionScheduleButton.setTextAndIcon(LocaleController.getString("SetReminder", R.string.SetReminder), R.drawable.msg_calendar2); } else { - scheduleButton.setTextAndIcon(LocaleController.getString("ScheduleMessage", R.string.ScheduleMessage), R.drawable.msg_calendar2); + actionScheduleButton.setTextAndIcon(LocaleController.getString("ScheduleMessage", R.string.ScheduleMessage), R.drawable.msg_calendar2); } - scheduleButton.setMinimumWidth(AndroidUtilities.dp(196)); - scheduleButton.setOnClickListener(v -> { + actionScheduleButton.setMinimumWidth(dp(196)); + actionScheduleButton.setOnClickListener(v -> { if (sendPopupWindow != null && sendPopupWindow.isShowing()) { sendPopupWindow.dismiss(); } @@ -3892,12 +4110,12 @@ public void didSelectDate(boolean notify, int scheduleDate) { } }, resourcesProvider); }); - sendPopupLayout.addView(scheduleButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + sendPopupLayout.addView(actionScheduleButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); SharedConfig.removeScheduledHint(); if (!self && dialog_id > 0) { sendWhenOnlineButton = new ActionBarMenuSubItem(getContext(), true, !sendWithoutSoundButtonValue, resourcesProvider); sendWhenOnlineButton.setTextAndIcon(LocaleController.getString("SendWhenOnline", R.string.SendWhenOnline), R.drawable.msg_online); - sendWhenOnlineButton.setMinimumWidth(AndroidUtilities.dp(196)); + sendWhenOnlineButton.setMinimumWidth(dp(196)); sendWhenOnlineButton.setOnClickListener(v -> { if (sendPopupWindow != null && sendPopupWindow.isShowing()) { sendPopupWindow.dismiss(); @@ -3910,7 +4128,7 @@ public void didSelectDate(boolean notify, int scheduleDate) { if (sendWithoutSoundButtonValue) { ActionBarMenuSubItem sendWithoutSoundButton = new ActionBarMenuSubItem(getContext(), !scheduleButtonValue, true, resourcesProvider); sendWithoutSoundButton.setTextAndIcon(LocaleController.getString("SendWithoutSound", R.string.SendWithoutSound), R.drawable.input_notify_off); - sendWithoutSoundButton.setMinimumWidth(AndroidUtilities.dp(196)); + sendWithoutSoundButton.setMinimumWidth(dp(196)); sendWithoutSoundButton.setOnClickListener(v -> { if (sendPopupWindow != null && sendPopupWindow.isShowing()) { sendPopupWindow.dismiss(); @@ -3941,6 +4159,9 @@ public void dismiss() { delegate.onSendLongClick(); } } + if (actionScheduleButton != null) { + actionScheduleButton.setVisibility(voiceOnce ? View.GONE : View.VISIBLE); + } if (sendWhenOnlineButton != null) { TLRPC.User user = parentFragment.getCurrentUser(); @@ -3950,16 +4171,16 @@ public void dismiss() { sendWhenOnlineButton.setVisibility(GONE); } } - sendPopupLayout.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), MeasureSpec.AT_MOST)); + sendPopupLayout.measure(MeasureSpec.makeMeasureSpec(dp(1000), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(dp(1000), MeasureSpec.AT_MOST)); sendPopupWindow.setFocusable(true); view.getLocationInWindow(location); int y; - if (keyboardVisible && ChatActivityEnterView.this.getMeasuredHeight() > AndroidUtilities.dp(topView != null && topView.getVisibility() == VISIBLE ? 48 + 58 : 58)) { + if (keyboardVisible && ChatActivityEnterView.this.getMeasuredHeight() > dp(topView != null && topView.getVisibility() == VISIBLE ? 48 + 58 : 58)) { y = location[1] + view.getMeasuredHeight(); } else { - y = location[1] - sendPopupLayout.getMeasuredHeight() - AndroidUtilities.dp(2); + y = location[1] - sendPopupLayout.getMeasuredHeight() - dp(2); } - sendPopupWindow.showAtLocation(view, Gravity.LEFT | Gravity.TOP, location[0] + view.getMeasuredWidth() - sendPopupLayout.getMeasuredWidth() + AndroidUtilities.dp(8), y); + sendPopupWindow.showAtLocation(view, Gravity.LEFT | Gravity.TOP, location[0] + view.getMeasuredWidth() - sendPopupLayout.getMeasuredWidth() + dp(8), y); sendPopupWindow.dimBehind(); sendButton.invalidate(); try { @@ -4058,12 +4279,12 @@ private void updateBotCommandsMenuContainerTopPadding() { } if (measuredFourViewsHeight > 0) { - padding = Math.max(0, sizeNotifierLayout.getMeasuredHeight() - measuredFourViewsHeight - AndroidUtilities.dp(8) - AndroidUtilities.dp(viewsCount > 4 ? 12 : 0)); + padding = Math.max(0, sizeNotifierLayout.getMeasuredHeight() - measuredFourViewsHeight - dp(8) - dp(viewsCount > 4 ? 12 : 0)); } else { if (botCommandsAdapter.getItemCount() > 4) { - padding = Math.max(0, sizeNotifierLayout.getMeasuredHeight() - AndroidUtilities.dp(8 + 36 * 4.3f)); + padding = Math.max(0, sizeNotifierLayout.getMeasuredHeight() - dp(8 + 36 * 4.3f)); } else { - padding = Math.max(0, sizeNotifierLayout.getMeasuredHeight() - AndroidUtilities.dp(8 + 36 * Math.max(1, Math.min(4, botCommandsAdapter.getItemCount())))); + padding = Math.max(0, sizeNotifierLayout.getMeasuredHeight() - dp(8 + 36 * Math.max(1, Math.min(4, botCommandsAdapter.getItemCount())))); } } @@ -4080,7 +4301,7 @@ private void updateBotCommandsMenuContainerTopPadding() { } } } - botCommandsMenuContainer.listView.setPadding(0, padding, 0, AndroidUtilities.dp(8)); + botCommandsMenuContainer.listView.setPadding(0, padding, 0, dp(8)); } } @@ -4257,7 +4478,7 @@ protected void extendActionMode(ActionMode actionMode, Menu menu) { @Override public boolean requestRectangleOnScreen(Rect rectangle) { - rectangle.bottom += AndroidUtilities.dp(1000); + rectangle.bottom += dp(1000); return super.requestRectangleOnScreen(rectangle); } @@ -4511,7 +4732,7 @@ protected void onAttachedToWindow() { messageEditText.setMaxLines(6); messageEditText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); messageEditText.setGravity(Gravity.BOTTOM); - messageEditText.setPadding(0, AndroidUtilities.dp(11), 0, AndroidUtilities.dp(12)); + messageEditText.setPadding(0, dp(11), 0, dp(12)); messageEditText.setBackgroundDrawable(null); messageEditText.setTextColor(getThemedColor(Theme.key_chat_messagePanelText)); messageEditText.setLinkTextColor(getThemedColor(Theme.key_chat_messageLinkOut)); @@ -4809,7 +5030,7 @@ private void setRecordVideoButtonVisible(boolean visible, boolean animated) { } public boolean isRecordingAudioVideo() { - return recordingAudioVideo || (runningAnimationAudio != null && runningAnimationAudio.isRunning()); + return recordingAudioVideo || (runningAnimationAudio != null && runningAnimationAudio.isRunning()) && !recordIsCanceled; } public boolean isRecordLocked() { @@ -4822,7 +5043,7 @@ public void cancelRecordingAudioVideo() { delegate.needStartRecordVideo(5, true, 0, voiceOnce ? 0x7FFFFFFF : 0); } else { delegate.needStartRecordAudio(0); - MediaController.getInstance().stopRecording(0, false, 0, voiceOnce); + MediaController.getInstance().stopRecording(0, false, 0, false); } recordingAudioVideo = false; updateRecordInterface(RECORD_STATE_CANCEL); @@ -5214,7 +5435,7 @@ private void onWindowSizeChanged() { delegate.onWindowSizeChanged(size); } if (topView != null) { - if (size < AndroidUtilities.dp(72) + ActionBar.getCurrentActionBarHeight()) { + if (size < dp(72) + ActionBar.getCurrentActionBarHeight()) { if (allowShowTopView) { allowShowTopView = false; if (needShowTopView) { @@ -5244,9 +5465,9 @@ private void onWindowSizeChanged() { private void resizeForTopView(boolean show) { LayoutParams layoutParams = (LayoutParams) textFieldContainer.getLayoutParams(); - layoutParams.topMargin = AndroidUtilities.dp(2) + (show ? topView.getLayoutParams().height : 0); + layoutParams.topMargin = dp(2) + (show ? topView.getLayoutParams().height : 0); textFieldContainer.setLayoutParams(layoutParams); - setMinimumHeight(AndroidUtilities.dp(51) + (show ? topView.getLayoutParams().height : 0)); + setMinimumHeight(dp(51) + (show ? topView.getLayoutParams().height : 0)); if (stickersExpanded) { if (searchingType == 0) { setStickersExpanded(false, true, false); @@ -5259,6 +5480,8 @@ private void resizeForTopView(boolean show) { public void onDestroy() { destroyed = true; NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordStarted); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordPaused); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordResumed); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordStartError); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordStopped); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordProgressChanged); @@ -5425,6 +5648,8 @@ public void setDialogId(long id, int account) { if (currentAccount != account) { notificationsLocker.unlock(); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordStarted); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordPaused); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordResumed); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordStartError); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordStopped); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.recordProgressChanged); @@ -5439,6 +5664,8 @@ public void setDialogId(long id, int account) { currentAccount = account; accountInstance = AccountInstance.getInstance(currentAccount); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordStarted); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordPaused); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordResumed); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordStartError); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordStopped); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.recordProgressChanged); @@ -5583,8 +5810,8 @@ public void updateFieldHint(boolean animated) { if (DialogObject.isChatDialog(dialog_id)) { TLRPC.Chat chat = accountInstance.getMessagesController().getChat(-dialog_id); TLRPC.ChatFull chatFull = accountInstance.getMessagesController().getChatFull(-dialog_id); - isChannel = ChatObject.isChannel(chat) && !chat.megagroup; - anonymously = ChatObject.getSendAsPeerId(chat, chatFull) == chat.id; + isChannel = ChatObject.isChannelAndNotMegaGroup(chat); + anonymously = ChatObject.getSendAsPeerId(chat, chatFull) == -dialog_id; } if (anonymously) { messageEditText.setHintText(LocaleController.getString("SendAnonymously", R.string.SendAnonymously)); @@ -5689,9 +5916,9 @@ private void hideRecordedAudioPanel(boolean wasSent) { animators.add(ObjectAnimator.ofFloat(attachButton, View.SCALE_Y, 1.0f)); animators.add(ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 1f)); animators.add(ObjectAnimator.ofFloat(messageEditText, MESSAGE_TEXT_TRANSLATION_X, 0)); - if (onceButton != null) { - animators.add(ObjectAnimator.ofFloat(onceButton, View.ALPHA, 0)); - onceButton.hideHintView(); + if (controlsView != null) { + animators.add(ObjectAnimator.ofFloat(controlsView, View.ALPHA, 0)); + controlsView.hideHintView(); } recordPannelAnimation.playTogether(animators); @@ -5729,11 +5956,11 @@ public void onAnimationEnd(Animator animation) { ArrayList animators = new ArrayList<>(); if (isInVideoMode()) { animators.add(ObjectAnimator.ofFloat(videoTimelineView, View.ALPHA, 0.0f)); - animators.add(ObjectAnimator.ofFloat(videoTimelineView, View.TRANSLATION_X, -AndroidUtilities.dp(20))); + animators.add(ObjectAnimator.ofFloat(videoTimelineView, View.TRANSLATION_X, -dp(20))); animators.add(ObjectAnimator.ofFloat(messageEditText, MESSAGE_TEXT_TRANSLATION_X, 0)); - if (onceButton != null) { - animators.add(ObjectAnimator.ofFloat(onceButton, View.ALPHA, 0.0f)); - onceButton.hideHintView(); + if (controlsView != null) { + animators.add(ObjectAnimator.ofFloat(controlsView, View.ALPHA, 0.0f)); + controlsView.hideHintView(); } exitAnimation.playTogether(animators); if (emojiButtonPaddingAlpha == 1f) { @@ -5761,13 +5988,13 @@ public void onAnimationEnd(Animator animation) { animators.add(ObjectAnimator.ofFloat(recordedAudioPlayButton, View.ALPHA, 0.0f)); animators.add(ObjectAnimator.ofFloat(recordedAudioBackground, View.ALPHA, 0.0f)); animators.add(ObjectAnimator.ofFloat(recordedAudioTimeTextView, View.ALPHA, 0.0f)); - animators.add(ObjectAnimator.ofFloat(recordedAudioSeekBar, View.TRANSLATION_X, -AndroidUtilities.dp(20))); - animators.add(ObjectAnimator.ofFloat(recordedAudioPlayButton, View.TRANSLATION_X, -AndroidUtilities.dp(20))); - animators.add(ObjectAnimator.ofFloat(recordedAudioBackground, View.TRANSLATION_X, -AndroidUtilities.dp(20))); - animators.add(ObjectAnimator.ofFloat(recordedAudioTimeTextView, View.TRANSLATION_X, -AndroidUtilities.dp(20))); - if (onceButton != null) { - animators.add(ObjectAnimator.ofFloat(onceButton, View.ALPHA, 0)); - onceButton.hideHintView(); + animators.add(ObjectAnimator.ofFloat(recordedAudioSeekBar, View.TRANSLATION_X, -dp(20))); + animators.add(ObjectAnimator.ofFloat(recordedAudioPlayButton, View.TRANSLATION_X, -dp(20))); + animators.add(ObjectAnimator.ofFloat(recordedAudioBackground, View.TRANSLATION_X, -dp(20))); + animators.add(ObjectAnimator.ofFloat(recordedAudioTimeTextView, View.TRANSLATION_X, -dp(20))); + if (controlsView != null) { + animators.add(ObjectAnimator.ofFloat(controlsView, View.ALPHA, 0)); + controlsView.hideHintView(); } exitAnimation.playTogether(animators); } @@ -5837,15 +6064,35 @@ public void onAnimationEnd(Animator animation) { recordPannelAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + if (videoTimelineView != null) { + videoTimelineView.setVisibility(GONE); + } + if (recordedAudioSeekBar != null) { + recordedAudioSeekBar.setVisibility(GONE); + } + if (recordedAudioPlayButton != null) { + recordedAudioPlayButton.setVisibility(GONE); + } + if (recordedAudioBackground != null) { + recordedAudioBackground.setVisibility(GONE); + } + if (recordedAudioTimeTextView != null) { + recordedAudioTimeTextView.setVisibility(GONE); + } + transformToSeekbar = 0; + isRecordingStateChanged(); hideRecordedAudioPanelInternal(); + if (recordCircle != null) { + recordCircle.setSendButtonInvisible(); + } } }); } if (recordPannelAnimation != null) { recordPannelAnimation.start(); } - if (onceButton != null) { - onceButton.invalidate(); + if (controlsView != null) { + controlsView.invalidate(); } } @@ -5933,6 +6180,11 @@ private void sendMessageInternal(boolean notify, int scheduleDate, boolean allow delegate.needStartRecordVideo(4, notify, scheduleDate, voiceOnce ? 0x7FFFFFFF : 0); hideRecordedAudioPanel(true); checkSendButton(true); + AndroidUtilities.runOnUIThread(() -> { + if (recordCircle != null) { + recordCircle.setSendButtonInvisible(); + } + }, 100); return; } else if (audioToSend != null) { MessageObject playing = MediaController.getInstance().getPlayingMessageObject(); @@ -5947,6 +6199,11 @@ private void sendMessageInternal(boolean notify, int scheduleDate, boolean allow } hideRecordedAudioPanel(true); checkSendButton(true); + AndroidUtilities.runOnUIThread(() -> { + if (recordCircle != null) { + recordCircle.setSendButtonInvisible(); + } + }, 100); return; } CharSequence message = messageEditText == null ? "" : messageEditText.getText(); @@ -6251,14 +6508,14 @@ public boolean processSendingText(CharSequence text, boolean notify, int schedul if (!delegate.hasForwardingMessages()) { sendAnimationData = new MessageObject.SendAnimationData(); - sendAnimationData.width = sendAnimationData.height = AndroidUtilities.dp(22); + sendAnimationData.width = sendAnimationData.height = dp(22); if (messageEditText != null) { messageEditText.getLocationInWindow(location); - sendAnimationData.x = location[0] + AndroidUtilities.dp(11); - sendAnimationData.y = location[1] + AndroidUtilities.dp(8 + 11); + sendAnimationData.x = location[0] + dp(11); + sendAnimationData.y = location[1] + dp(8 + 11); } else { - sendAnimationData.x = AndroidUtilities.dp(48 + 11); - sendAnimationData.y = AndroidUtilities.displaySize.y - AndroidUtilities.dp(8 + 11); + sendAnimationData.x = dp(48 + 11); + sendAnimationData.y = AndroidUtilities.displaySize.y - dp(8 + 11); } } @@ -6351,12 +6608,12 @@ private void checkSendButton(boolean animated) { if (hasScheduled) { scheduledButton.setVisibility(VISIBLE); scheduledButton.setTag(1); - scheduledButton.setPivotX(AndroidUtilities.dp(48)); - animators.add(ObjectAnimator.ofFloat(scheduledButton, View.TRANSLATION_X, AndroidUtilities.dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - AndroidUtilities.dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0))); + scheduledButton.setPivotX(dp(48)); + animators.add(ObjectAnimator.ofFloat(scheduledButton, View.TRANSLATION_X, dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0))); animators.add(ObjectAnimator.ofFloat(scheduledButton, View.ALPHA, 1.0f)); animators.add(ObjectAnimator.ofFloat(scheduledButton, View.SCALE_X, 1.0f)); } else { - scheduledButton.setTranslationX(AndroidUtilities.dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - AndroidUtilities.dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0)); + scheduledButton.setTranslationX(dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0)); scheduledButton.setAlpha(1.0f); scheduledButton.setScaleX(1.0f); } @@ -6483,7 +6740,7 @@ public void onAnimationCancel(Animator animation) { scheduledButton.setVisibility(VISIBLE); scheduledButton.setTag(1); } - scheduledButton.setTranslationX(AndroidUtilities.dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - AndroidUtilities.dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0)); + scheduledButton.setTranslationX(dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0)); scheduledButton.setAlpha(1.0f); scheduledButton.setScaleX(1.0f); scheduledButton.setScaleY(1.0f); @@ -6533,11 +6790,11 @@ public void onAnimationCancel(Animator animation) { scheduledButton.setTag(null); animators.add(ObjectAnimator.ofFloat(scheduledButton, View.ALPHA, 0.0f)); animators.add(ObjectAnimator.ofFloat(scheduledButton, View.SCALE_X, 0.0f)); - animators.add(ObjectAnimator.ofFloat(scheduledButton, View.TRANSLATION_X, AndroidUtilities.dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - AndroidUtilities.dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0))); + animators.add(ObjectAnimator.ofFloat(scheduledButton, View.TRANSLATION_X, dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0))); } else { scheduledButton.setAlpha(0.0f); scheduledButton.setScaleX(0.0f); - scheduledButton.setTranslationX(AndroidUtilities.dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - AndroidUtilities.dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0)); + scheduledButton.setTranslationX(dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0)); } } runningAnimation2.playTogether(animators); @@ -6692,7 +6949,7 @@ public void onAnimationCancel(Animator animation) { scheduledButton.setAlpha(0.0f); scheduledButton.setScaleX(0.0f); scheduledButton.setScaleY(1.0f); - scheduledButton.setTranslationX(AndroidUtilities.dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - AndroidUtilities.dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0)); + scheduledButton.setTranslationX(dp(botButton != null && botButton.getVisibility() == VISIBLE ? 96 : 48) - dp(giftButton != null && giftButton.getVisibility() == VISIBLE ? 48 : 0)); } } } @@ -6727,7 +6984,7 @@ public void onAnimationCancel(Animator animation) { if (hasScheduled) { scheduledButton.setVisibility(VISIBLE); scheduledButton.setTag(1); - scheduledButton.setPivotX(AndroidUtilities.dp(48)); + scheduledButton.setPivotX(dp(48)); animators.add(ObjectAnimator.ofFloat(scheduledButton, View.ALPHA, 1.0f)); animators.add(ObjectAnimator.ofFloat(scheduledButton, View.SCALE_X, 1.0f)); animators.add(ObjectAnimator.ofFloat(scheduledButton, View.TRANSLATION_X, 0)); @@ -6857,7 +7114,12 @@ public void onAnimationCancel(Animator animation) { scheduledButton.setTranslationX(0); } } - } else if (sendButton.getVisibility() == VISIBLE || cancelBotButton.getVisibility() == VISIBLE || expandStickersButton != null && expandStickersButton.getVisibility() == VISIBLE || slowModeButton.getVisibility() == VISIBLE) { + } else if ( + sendButton.getVisibility() == VISIBLE || + cancelBotButton.getVisibility() == VISIBLE || + expandStickersButton != null && expandStickersButton.getVisibility() == VISIBLE || + slowModeButton.getVisibility() == VISIBLE + ) { if (animated) { if (runningAnimationType == 2) { return; @@ -6892,10 +7154,10 @@ public void onAnimationCancel(Animator animation) { if (hasScheduled) { scheduledButton.setVisibility(VISIBLE); scheduledButton.setTag(1); - scheduledButton.setPivotX(AndroidUtilities.dp(48)); + scheduledButton.setPivotX(dp(48)); animators.add(ObjectAnimator.ofFloat(scheduledButton, View.ALPHA, 1.0f)); animators.add(ObjectAnimator.ofFloat(scheduledButton, View.SCALE_X, 1.0f)); - animators.add(ObjectAnimator.ofFloat(scheduledButton, View.TRANSLATION_X, giftButton != null && giftButton.getVisibility() == VISIBLE ? -AndroidUtilities.dp(48) : 0)); + animators.add(ObjectAnimator.ofFloat(scheduledButton, View.TRANSLATION_X, giftButton != null && giftButton.getVisibility() == VISIBLE ? -dp(48) : 0)); } else { scheduledButton.setAlpha(1.0f); scheduledButton.setScaleX(1.0f); @@ -7041,9 +7303,9 @@ public void onAnimationCancel(Animator animation) { private void setSlowModeButtonVisible(boolean visible) { slowModeButton.setVisibility(visible ? VISIBLE : GONE); - int padding = visible ? AndroidUtilities.dp(16) : 0; + int padding = visible ? dp(16) : 0; if (messageEditText != null && messageEditText.getPaddingRight() != padding) { - messageEditText.setPadding(0, AndroidUtilities.dp(11), padding, AndroidUtilities.dp(12)); + messageEditText.setPadding(0, dp(11), padding, dp(12)); } } @@ -7055,27 +7317,27 @@ private void updateFieldRight(int attachVisible) { int oldRightMargin = layoutParams.rightMargin; if (attachVisible == 1) { if (botButton != null && botButton.getVisibility() == VISIBLE && scheduledButton != null && scheduledButton.getVisibility() == VISIBLE && attachLayout != null && attachLayout.getVisibility() == VISIBLE) { - layoutParams.rightMargin = AndroidUtilities.dp(146); + layoutParams.rightMargin = dp(146); } else if (botButton != null && botButton.getVisibility() == VISIBLE || notifyButton != null && notifyButton.getVisibility() == VISIBLE || scheduledButton != null && scheduledButton.getTag() != null) { - layoutParams.rightMargin = AndroidUtilities.dp(98); + layoutParams.rightMargin = dp(98); } else { - layoutParams.rightMargin = AndroidUtilities.dp(50); + layoutParams.rightMargin = dp(50); } } else if (attachVisible == 2) { - if (layoutParams.rightMargin != AndroidUtilities.dp(2)) { + if (layoutParams.rightMargin != dp(2)) { if (botButton != null && botButton.getVisibility() == VISIBLE && scheduledButton != null && scheduledButton.getVisibility() == VISIBLE && attachLayout != null && attachLayout.getVisibility() == VISIBLE) { - layoutParams.rightMargin = AndroidUtilities.dp(146); + layoutParams.rightMargin = dp(146); } else if (botButton != null && botButton.getVisibility() == VISIBLE || notifyButton != null && notifyButton.getVisibility() == VISIBLE || scheduledButton != null && scheduledButton.getTag() != null) { - layoutParams.rightMargin = AndroidUtilities.dp(98); + layoutParams.rightMargin = dp(98); } else { - layoutParams.rightMargin = AndroidUtilities.dp(50); + layoutParams.rightMargin = dp(50); } } } else { if (scheduledButton != null && scheduledButton.getTag() != null) { - layoutParams.rightMargin = AndroidUtilities.dp(50); + layoutParams.rightMargin = dp(50); } else { - layoutParams.rightMargin = AndroidUtilities.dp(2); + layoutParams.rightMargin = dp(2); } } if (oldRightMargin != layoutParams.rightMargin) { @@ -7096,6 +7358,7 @@ public boolean canShowMessageTransition() { return moveToSendStateRunnable != null; } + private int lastRecordState; protected void updateRecordInterface(int recordState) { if (moveToSendStateRunnable != null) { AndroidUtilities.cancelRunOnUIThread(moveToSendStateRunnable); @@ -7106,12 +7369,19 @@ protected void updateRecordInterface(int recordState) { } if (recordingAudioVideo) { if (recordInterfaceState == 1) { + lastRecordState = recordState; return; } - voiceOnce = false; - if (onceButton != null) { - onceButton.periodDrawable.setValue(1, false, false); + final boolean fromPause = lastRecordState == RECORD_STATE_PREPARING; + + if (!fromPause) { + voiceOnce = false; + if (controlsView != null) { + controlsView.periodDrawable.setValue(1, false, false); + } + millisecondsRecorded = 0; } + createRecordAudioPanel(); recordInterfaceState = 1; if (emojiView != null) { @@ -7150,8 +7420,8 @@ protected void updateRecordInterface(int recordState) { recordCircle.setVisibility(VISIBLE); recordCircle.setAmplitude(0); } - if (onceButton != null) { - onceButton.setVisibility(delegate != null && delegate.onceVoiceAvailable() ? VISIBLE : GONE); + if (controlsView != null) { + controlsView.setVisibility(VISIBLE); } if (recordDot != null) { recordDot.resetAlpha(); @@ -7163,16 +7433,24 @@ protected void updateRecordInterface(int recordState) { runningAnimationAudio = new AnimatorSet(); - recordTimerView.setTranslationX(AndroidUtilities.dp(20)); + recordTimerView.setTranslationX(dp(20)); recordTimerView.setAlpha(0); - slideText.setTranslationX(AndroidUtilities.dp(20)); - slideText.setAlpha(0); - slideText.setCancelToProgress(0f); - slideText.setSlideX(1f); - recordCircle.setLockTranslation(10000); - slideText.setEnabled(true); + if (lastRecordState != RECORD_STATE_PREPARING) { + slideText.setTranslationX(dp(20)); + slideText.setAlpha(0); + slideText.setCancelToProgress(0f); + slideText.setSlideX(1f); + slideText.setEnabled(true); + } else { + slideText.setTranslationX(0); + slideText.setAlpha(0); + slideText.setCancelToProgress(1f); + slideText.setEnabled(true); + } + recordCircle.resetLockTranslation(lastRecordState == RECORD_STATE_PREPARING); recordIsCanceled = false; + isRecordingStateChanged(); //ENTER TRANSITION AnimatorSet iconChanges = new AnimatorSet(); @@ -7182,17 +7460,16 @@ protected void updateRecordInterface(int recordState) { ObjectAnimator.ofFloat(recordDot, View.SCALE_Y, 1), ObjectAnimator.ofFloat(recordDot, View.SCALE_X, 1), ObjectAnimator.ofFloat(recordTimerView, View.TRANSLATION_X, 0), - ObjectAnimator.ofFloat(recordTimerView, View.ALPHA, 1), - ObjectAnimator.ofFloat(slideText, View.TRANSLATION_X, 0), - ObjectAnimator.ofFloat(slideText, View.ALPHA, 1) + ObjectAnimator.ofFloat(recordTimerView, View.ALPHA, 1) ); - if (onceButton != null) { - iconChanges.playTogether(ObjectAnimator.ofFloat(onceButton, View.ALPHA, 1)); + iconChanges.playTogether(ObjectAnimator.ofFloat(slideText, View.TRANSLATION_X, 0)); + iconChanges.playTogether(ObjectAnimator.ofFloat(slideText, View.ALPHA, 1)); + if (controlsView != null) { + iconChanges.playTogether(ObjectAnimator.ofFloat(controlsView, View.ALPHA, 1)); } if (audioVideoSendButton != null) { iconChanges.playTogether(ObjectAnimator.ofFloat(audioVideoSendButton, View.ALPHA, 0)); } - if (botCommandsMenuButton != null) { iconChanges.playTogether( ObjectAnimator.ofFloat(botCommandsMenuButton, View.SCALE_Y, 0), @@ -7203,19 +7480,32 @@ protected void updateRecordInterface(int recordState) { AnimatorSet viewTransition = new AnimatorSet(); viewTransition.playTogether( - ObjectAnimator.ofFloat(messageEditText, MESSAGE_TEXT_TRANSLATION_X, AndroidUtilities.dp(20)), + ObjectAnimator.ofFloat(messageEditText, MESSAGE_TEXT_TRANSLATION_X, dp(20)), ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 0), ObjectAnimator.ofFloat(recordedAudioPanel, View.ALPHA, 1f) ); + if (fromPause) { + viewTransition.playTogether(ObjectAnimator.ofFloat(recordedAudioSeekBar, View.ALPHA, 0.0f)); + viewTransition.playTogether(ObjectAnimator.ofFloat(recordedAudioPlayButton, View.ALPHA, 0.0f)); + viewTransition.playTogether(ObjectAnimator.ofFloat(recordedAudioBackground, View.ALPHA, 0.0f)); + viewTransition.playTogether(ObjectAnimator.ofFloat(recordedAudioTimeTextView, View.ALPHA, 0.0f)); + + viewTransition.playTogether(ObjectAnimator.ofFloat(recordDeleteImageView, View.ALPHA, 0.0f)); + viewTransition.playTogether(ObjectAnimator.ofFloat(recordDeleteImageView, View.SCALE_X, 0.0f)); + viewTransition.playTogether(ObjectAnimator.ofFloat(recordDeleteImageView, View.SCALE_Y, 0.0f)); + + viewTransition.playTogether(ObjectAnimator.ofFloat(videoTimelineView, View.ALPHA, 0)); + } + if (scheduledButton != null) { viewTransition.playTogether( - ObjectAnimator.ofFloat(scheduledButton, View.TRANSLATION_X, AndroidUtilities.dp(30)), + ObjectAnimator.ofFloat(scheduledButton, View.TRANSLATION_X, dp(30)), ObjectAnimator.ofFloat(scheduledButton, View.ALPHA, 0f) ); } if (attachLayout != null) { viewTransition.playTogether( - ObjectAnimator.ofFloat(attachLayout, ATTACH_LAYOUT_TRANSLATION_X, AndroidUtilities.dp(30)), + ObjectAnimator.ofFloat(attachLayout, ATTACH_LAYOUT_TRANSLATION_X, dp(30)), ObjectAnimator.ofFloat(attachLayout, ATTACH_LAYOUT_ALPHA, 0f) ); } @@ -7225,6 +7515,9 @@ protected void updateRecordInterface(int recordState) { viewTransition.setDuration(150), ObjectAnimator.ofFloat(recordCircle, recordCircleScale, 1).setDuration(300) ); + if (!fromPause) { + runningAnimationAudio.playTogether(ObjectAnimator.ofFloat(recordCircle, recordControlsCircleScale, 1).setDuration(300)); + } runningAnimationAudio.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animator) { @@ -7235,15 +7528,26 @@ public void onAnimationEnd(Animator animator) { slideText.setAlpha(1f); slideText.setTranslationX(0); - recordCircle.showTooltipIfNeed(); + if (controlsView != null) { + controlsView.showTooltipIfNeed(); + } if (messageEditText != null) { messageEditText.setAlpha(0f); } + if (fromPause) { + if (recordedAudioSeekBar != null) { + recordedAudioSeekBar.setVisibility(View.GONE); + } + if (recordedAudioPanel != null) { + recordedAudioPanel.setVisibility(View.GONE); + } + isRecordingStateChanged(); + } } }); runningAnimationAudio.setInterpolator(new DecelerateInterpolator()); runningAnimationAudio.start(); - recordTimerView.start(); + recordTimerView.start(millisecondsRecorded); } else { if (recordIsCanceled && recordState == RECORD_STATE_PREPARING) { return; @@ -7259,6 +7563,7 @@ public void onAnimationEnd(Animator animator) { AndroidUtilities.unlockOrientation(parentActivity); wasSendTyping = false; if (recordInterfaceState == 0) { + lastRecordState = recordState; return; } accountInstance.getMessagesController().sendTyping(dialog_id, getThreadMessageId(), 2, 0); @@ -7297,17 +7602,17 @@ public void onAnimationEnd(Animator animator) { ObjectAnimator.ofFloat(recordDot, View.SCALE_Y, 0), ObjectAnimator.ofFloat(recordDot, View.SCALE_X, 0), ObjectAnimator.ofFloat(recordCircle, recordCircleScale, 0.0f), + ObjectAnimator.ofFloat(recordCircle, recordControlsCircleScale, 0.0f), ObjectAnimator.ofFloat(audioVideoButtonContainer, View.ALPHA, 1.0f), ObjectAnimator.ofFloat(recordTimerView, View.ALPHA, 0.0f), - ObjectAnimator.ofFloat(recordCircle, recordCircleScale, 0.0f), ObjectAnimator.ofFloat(audioVideoButtonContainer, View.ALPHA, 1.0f), ObjectAnimator.ofFloat(messageEditText, View.ALPHA, 1), ObjectAnimator.ofFloat(messageEditText, MESSAGE_TEXT_TRANSLATION_X, 0), - ObjectAnimator.ofFloat(recordCircle, "slideToCancelProgress", 1f) + ObjectAnimator.ofFloat(this, "slideToCancelProgress", 1f) ); - if (onceButton != null) { - runningAnimationAudio.playTogether(ObjectAnimator.ofFloat(onceButton, View.ALPHA, 0)); - onceButton.hideHintView(); + if (controlsView != null) { + runningAnimationAudio.playTogether(ObjectAnimator.ofFloat(controlsView, View.ALPHA, 0)); + controlsView.hideHintView(); } if (botCommandsMenuButton != null) { runningAnimationAudio.playTogether( @@ -7336,6 +7641,7 @@ public void onAnimationEnd(Animator animator) { } recordIsCanceled = true; + isRecordingStateChanged(); runningAnimationAudio.setDuration(150); } else if (recordState == RECORD_STATE_PREPARING) { if (slideText != null) { @@ -7354,6 +7660,7 @@ public void onAnimationEnd(Animator animator) { } if (recordedAudioSeekBar != null) { recordedAudioSeekBar.setVisibility(GONE); + isRecordingStateChanged(); } if (recordedAudioPanel != null) { recordedAudioPanel.setAlpha(1.0f); @@ -7366,6 +7673,7 @@ public void onAnimationEnd(Animator animator) { } else { if (videoTimelineView != null) { videoTimelineView.setVisibility(GONE); + isRecordingStateChanged(); } if (recordedAudioTimeTextView != null) { recordedAudioTimeTextView.setVisibility(VISIBLE); @@ -7386,9 +7694,21 @@ public void onAnimationEnd(Animator animator) { if (recordedAudioSeekBar != null) { recordedAudioSeekBar.setVisibility(VISIBLE); recordedAudioSeekBar.setAlpha(0f); + isRecordingStateChanged(); } } + sendButtonVisible = true; + snapAnimationProgress = 1f; + lockAnimatedTranslation = startTranslation; + slideToCancelProgress = 1f; + if (slideText != null) { + slideText.setCancelToProgress(1f); + } + if (controlsView != null) { + controlsView.invalidate(); + } + if (recordDeleteImageView != null) { recordDeleteImageView.setAlpha(0f); recordDeleteImageView.setScaleX(0f); @@ -7400,8 +7720,8 @@ public void onAnimationEnd(Animator animator) { ValueAnimator transformToSeekbar = ValueAnimator.ofFloat(0, 1f); transformToSeekbar.addUpdateListener(animation -> { float value = (float) animation.getAnimatedValue(); + recordCircle.setTransformToSeekbar(value); if (!isInVideoMode()) { - recordCircle.setTransformToSeekbar(value); seekBarWaveform.setWaveScaling(recordCircle.getTransformToSeekbarProgressStep3()); recordedAudioTimeTextView.setAlpha(recordCircle.getTransformToSeekbarProgressStep3()); recordedAudioPlayButton.setAlpha(recordCircle.getTransformToSeekbarProgressStep3()); @@ -7409,8 +7729,14 @@ public void onAnimationEnd(Animator animator) { recordedAudioPlayButton.setScaleY(recordCircle.getTransformToSeekbarProgressStep3()); recordedAudioSeekBar.setAlpha(recordCircle.getTransformToSeekbarProgressStep3()); recordedAudioSeekBar.invalidate(); - } else { - recordCircle.setExitTransition(value); + } + isRecordingStateChanged(); + }); + transformToSeekbar.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + recordCircle.setTransformToSeekbar(1); + isRecordingStateChanged(); } }); @@ -7421,13 +7747,14 @@ public void onAnimationEnd(Animator animator) { oldLayoutParams = recordedAudioPanel.getLayoutParams(); parent.removeView(recordedAudioPanel); - FrameLayout.LayoutParams newLayoutParams = new FrameLayout.LayoutParams(parent.getMeasuredWidth(), AndroidUtilities.dp(48)); + FrameLayout.LayoutParams newLayoutParams = new FrameLayout.LayoutParams(parent.getMeasuredWidth(), dp(48)); newLayoutParams.gravity = Gravity.BOTTOM; sizeNotifierLayout.addView(recordedAudioPanel, newLayoutParams); videoTimelineView.setVisibility(GONE); } else { videoTimelineView.setVisibility(VISIBLE); } + isRecordingStateChanged(); if (recordDeleteImageView != null) { recordDeleteImageView.setAlpha(0f); @@ -7441,7 +7768,7 @@ public void onAnimationEnd(Animator animator) { ObjectAnimator.ofFloat(recordDot, View.SCALE_Y, 0), ObjectAnimator.ofFloat(recordDot, View.SCALE_X, 0), ObjectAnimator.ofFloat(recordTimerView, View.ALPHA, 0.0f), - ObjectAnimator.ofFloat(recordTimerView, View.TRANSLATION_X, -AndroidUtilities.dp(20)), + ObjectAnimator.ofFloat(recordTimerView, View.TRANSLATION_X, -dp(20)), ObjectAnimator.ofFloat(slideText, View.ALPHA, 0), ObjectAnimator.ofFloat(recordDeleteImageView, View.ALPHA, 1), ObjectAnimator.ofFloat(recordDeleteImageView, View.SCALE_Y, 1f), @@ -7526,8 +7853,8 @@ public void onAnimationEnd(Animator animation) { botCommandsMenuButton.setScaleY(0f); } - if (onceButton != null && onceVisible && MessagesController.getGlobalMainSettings().getInt("voiceoncehint", 0) < 3) { - onceButton.showHintView(); + if (controlsView != null && onceVisible && !voiceOnce && (MessagesController.getGlobalMainSettings().getInt("voiceoncehint", 0) < 3)) { + controlsView.showHintView(); } } }); @@ -7537,6 +7864,7 @@ public void onAnimationEnd(Animator animation) { audioVideoSendButton.setVisibility(View.VISIBLE); } recordIsCanceled = true; + isRecordingStateChanged(); AnimatorSet iconsAnimator = new AnimatorSet(); iconsAnimator.playTogether( ObjectAnimator.ofFloat(emojiButton, EMOJI_BUTTON_SCALE, 1), @@ -7544,9 +7872,9 @@ public void onAnimationEnd(Animator animation) { ObjectAnimator.ofFloat(recordDot, View.SCALE_Y, 0), ObjectAnimator.ofFloat(recordDot, View.SCALE_X, 0) ); - if (onceButton != null) { - iconsAnimator.playTogether(ObjectAnimator.ofFloat(onceButton, View.ALPHA, 0)); - onceButton.hideHintView(); + if (controlsView != null) { + iconsAnimator.playTogether(ObjectAnimator.ofFloat(controlsView, View.ALPHA, 0)); + controlsView.hideHintView(); } if (botCommandsMenuButton != null) { @@ -7558,9 +7886,9 @@ public void onAnimationEnd(Animator animation) { AnimatorSet recordTimer = new AnimatorSet(); recordTimer.playTogether( ObjectAnimator.ofFloat(recordTimerView, View.ALPHA, 0.0f), - ObjectAnimator.ofFloat(recordTimerView, View.TRANSLATION_X, -AndroidUtilities.dp(20)), + ObjectAnimator.ofFloat(recordTimerView, View.TRANSLATION_X, -dp(20)), ObjectAnimator.ofFloat(slideText, View.ALPHA, 0.0f), - ObjectAnimator.ofFloat(slideText, View.TRANSLATION_X, -AndroidUtilities.dp(20)) + ObjectAnimator.ofFloat(slideText, View.TRANSLATION_X, -dp(20)) ); if (recordState != RECORD_STATE_CANCEL_BY_GESTURE) { @@ -7578,7 +7906,7 @@ public void onAnimationEnd(Animator animation) { } iconsAnimator.playTogether( - ObjectAnimator.ofFloat(recordCircle, "slideToCancelProgress", 1f), + ObjectAnimator.ofFloat(this, "slideToCancelProgress", 1f), ObjectAnimator.ofFloat(audioVideoButtonContainer, View.SCALE_X, 1f), ObjectAnimator.ofFloat(audioVideoButtonContainer, View.SCALE_Y, 1f), ObjectAnimator.ofFloat(audioVideoButtonContainer, View.ALPHA, 1f) @@ -7662,16 +7990,16 @@ public void onAnimationEnd(Animator animation) { iconsAnimator, recordTimer, messageEditTextAniamtor, - ObjectAnimator.ofFloat(recordCircle, "lockAnimatedTranslation", recordCircle.startTranslation).setDuration(200) + ObjectAnimator.ofFloat(this, "lockAnimatedTranslation", startTranslation).setDuration(200) ); if (recordState == RECORD_STATE_CANCEL_BY_GESTURE) { recordCircle.canceledByGesture(); - ObjectAnimator cancel = ObjectAnimator.ofFloat(recordCircle, "slideToCancelProgress", 1f).setDuration(200); + ObjectAnimator cancel = ObjectAnimator.ofFloat(this, "slideToCancelProgress", 1f).setDuration(200); cancel.setInterpolator(CubicBezierInterpolator.EASE_BOTH); runningAnimationAudio.playTogether(cancel); } else { - Animator recordCircleAnimator = ObjectAnimator.ofFloat(recordCircle, "exitTransition", 1.0f); + Animator recordCircleAnimator = ObjectAnimator.ofFloat(this, "exitTransition", 1.0f); recordCircleAnimator.setDuration(360); recordCircleAnimator.setStartDelay(490); runningAnimationAudio.playTogether( @@ -7695,9 +8023,9 @@ public void onAnimationEnd(Animator animation) { ObjectAnimator.ofFloat(recordDot, View.SCALE_X, 0), ObjectAnimator.ofFloat(audioVideoButtonContainer, View.ALPHA, 1.0f) ); - if (onceButton != null) { - iconsAnimator.playTogether(ObjectAnimator.ofFloat(onceButton, View.ALPHA, 0)); - onceButton.hideHintView(); + if (controlsView != null) { + iconsAnimator.playTogether(ObjectAnimator.ofFloat(controlsView, View.ALPHA, 0)); + controlsView.hideHintView(); } if (botCommandsMenuButton != null) { iconsAnimator.playTogether( @@ -7731,14 +8059,13 @@ public void onAnimationEnd(Animator animation) { AnimatorSet recordTimer = new AnimatorSet(); recordTimer.playTogether( ObjectAnimator.ofFloat(recordTimerView, View.ALPHA, 0.0f), - ObjectAnimator.ofFloat(recordTimerView, View.TRANSLATION_X, AndroidUtilities.dp(40)), + ObjectAnimator.ofFloat(recordTimerView, View.TRANSLATION_X, dp(40)), ObjectAnimator.ofFloat(slideText, View.ALPHA, 0.0f), - ObjectAnimator.ofFloat(slideText, View.TRANSLATION_X, AndroidUtilities.dp(40)) + ObjectAnimator.ofFloat(slideText, View.TRANSLATION_X, dp(40)) ); recordTimer.setDuration(150); - - Animator recordCircleAnimator = ObjectAnimator.ofFloat(recordCircle, "exitTransition", 1.0f); + Animator recordCircleAnimator = ObjectAnimator.ofFloat(this, "exitTransition", 1.0f); recordCircleAnimator.setDuration(messageTransitionIsRunning ? 220 : 360); messageTextTranslationX = 0; @@ -7762,7 +8089,10 @@ public void onAnimationEnd(Animator animator) { if (recordState != RECORD_STATE_PREPARING && messageEditText != null) { messageEditText.requestFocus(); } - cancelRecordIntefraceInternal(); + cancelRecordInterfaceInternal(); + if (recordState != RECORD_STATE_PREPARING && recordCircle != null) { + recordCircle.setSendButtonInvisible(); + } } } }); @@ -7773,15 +8103,15 @@ public void onAnimationEnd(Animator animator) { } delegate.onAudioVideoInterfaceUpdated(); updateSendAsButton(); + lastRecordState = recordState; } - private void cancelRecordIntefraceInternal() { + private void cancelRecordInterfaceInternal() { if (recordPanel != null) { recordPanel.setVisibility(GONE); } if (recordCircle != null) { recordCircle.setVisibility(GONE); - recordCircle.setSendButtonInvisible(); } runningAnimationAudio = null; isRecordingStateChanged(); @@ -7809,7 +8139,12 @@ private void createRecordPanel() { return; } - recordPanel = new FrameLayout(getContext()); + recordPanel = new FrameLayout(getContext()) { + @Override + public boolean onTouchEvent(MotionEvent event) { + return super.onTouchEvent(event); + } + }; recordPanel.setClipChildren(false); recordPanel.setVisibility(GONE); messageEditTextContainer.addView(recordPanel, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48)); @@ -7818,7 +8153,7 @@ private void createRecordPanel() { recordTimeContainer = new LinearLayout(getContext()); recordTimeContainer.setOrientation(LinearLayout.HORIZONTAL); - recordTimeContainer.setPadding(AndroidUtilities.dp(13), 0, 0, 0); + recordTimeContainer.setPadding(dp(13), 0, 0, 0); recordTimeContainer.setFocusable(false); recordTimeContainer.addView(recordDot = new RecordDot(getContext()), LayoutHelper.createLinear(28, 28, Gravity.CENTER_VERTICAL, 0, 0, 0, 0)); @@ -7917,7 +8252,7 @@ public void setEditingMessageObject(MessageObject messageObject, boolean caption } if (paint == null) { paint = new TextPaint(); - paint.setTextSize(AndroidUtilities.dp(18)); + paint.setTextSize(dp(18)); } fontMetricsInt = paint.getFontMetricsInt(); @@ -8035,7 +8370,7 @@ public void setEditingMessageObject(MessageObject messageObject, boolean caption openKeyboard(); if (messageEditText != null) { FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) messageEditText.getLayoutParams(); - layoutParams.rightMargin = AndroidUtilities.dp(4); + layoutParams.rightMargin = dp(4); messageEditText.setLayoutParams(layoutParams); } sendButton.setVisibility(GONE); @@ -8225,7 +8560,7 @@ public void updateColors() { private void updateRecordedDeleteIconColors() { int dotColor = getThemedColor(Theme.key_chat_recordedVoiceDot); - int background = ColorUtils.blendARGB(dotColor, getThemedColor(Theme.key_chat_messagePanelBackground), 0.5f); + int background = getThemedColor(Theme.key_chat_messagePanelBackground); int greyColor = getThemedColor(Theme.key_chat_messagePanelVoiceDelete); if (recordDeleteImageView != null) { @@ -8233,6 +8568,10 @@ private void updateRecordedDeleteIconColors() { recordDeleteImageView.setLayerColor("Box Red.**", dotColor); recordDeleteImageView.setLayerColor("Cup Grey.**", greyColor); recordDeleteImageView.setLayerColor("Box Grey.**", greyColor); + + recordDeleteImageView.setLayerColor("Line 1.**", background); + recordDeleteImageView.setLayerColor("Line 2.**", background); + recordDeleteImageView.setLayerColor("Line 3.**", background); } } @@ -8395,7 +8734,7 @@ public void updateGiftButton(boolean animated) { AndroidUtilities.updateViewVisibilityAnimated(giftButton, visible, 1f, animated); if (scheduledButton != null && scheduledButton.getVisibility() == View.VISIBLE) { - float tX = (visible ? -AndroidUtilities.dp(48) : 0) + AndroidUtilities.dp(botButton != null && botButton.getVisibility() == VISIBLE ? 48 : 0); + float tX = (visible ? -dp(48) : 0) + dp(botButton != null && botButton.getVisibility() == VISIBLE ? 48 : 0); if (animated) { scheduledButton.animate().translationX(tX).setDuration(150).start(); } else { @@ -8432,7 +8771,7 @@ public void updateScheduleButton(boolean animated) { if (newVisibility != notifyButton.getVisibility()) { notifyButton.setVisibility(newVisibility); if (attachLayout != null) { - attachLayout.setPivotX(AndroidUtilities.dp((botButton == null || botButton.getVisibility() == GONE) && (notifyButton == null || notifyButton.getVisibility() == GONE) ? 48 : 96)); + attachLayout.setPivotX(dp((botButton == null || botButton.getVisibility() == GONE) && (notifyButton == null || notifyButton.getVisibility() == GONE) ? 48 : 96)); } } } @@ -8454,14 +8793,14 @@ public void updateScheduleButton(boolean animated) { notifyButton.setVisibility(notifyVisible && scheduledButton.getVisibility() != VISIBLE ? VISIBLE : GONE); } if (giftButton != null && giftButton.getVisibility() == VISIBLE) { - scheduledButton.setTranslationX(-AndroidUtilities.dp(48)); + scheduledButton.setTranslationX(-dp(48)); } } } else if (scheduledButton != null) { if (visible) { scheduledButton.setVisibility(VISIBLE); } - scheduledButton.setPivotX(AndroidUtilities.dp(24)); + scheduledButton.setPivotX(dp(24)); scheduledButtonAnimation = new AnimatorSet(); scheduledButtonAnimation.playTogether(ObjectAnimator.ofFloat(scheduledButton, View.ALPHA, visible ? 1.0f : 0.0f), ObjectAnimator.ofFloat(scheduledButton, View.SCALE_X, visible ? 1.0f : 0.1f), @@ -8479,7 +8818,7 @@ public void onAnimationEnd(Animator animation) { scheduledButtonAnimation.start(); } if (attachLayout != null) { - attachLayout.setPivotX(AndroidUtilities.dp((botButton == null || botButton.getVisibility() == GONE) && (notifyButton == null || notifyButton.getVisibility() == GONE) ? 48 : 96)); + attachLayout.setPivotX(dp((botButton == null || botButton.getVisibility() == GONE) && (notifyButton == null || notifyButton.getVisibility() == GONE) ? 48 : 96)); } } @@ -8518,7 +8857,7 @@ public void updateSendAsButton(boolean animated) { } } boolean wasVisible = senderSelectView != null && senderSelectView.getVisibility() == View.VISIBLE; - int pad = AndroidUtilities.dp(2); + int pad = dp(2); float startAlpha = isVisible ? 0 : 1; float endAlpha = isVisible ? 1 : 0; final float startX, endX; @@ -8707,7 +9046,7 @@ private void updateBotButton(boolean animated) { } } updateFieldRight(2); - attachLayout.setPivotX(AndroidUtilities.dp((botButton == null || botButton.getVisibility() == GONE) && (notifyButton == null || notifyButton.getVisibility() == GONE) ? 48 : 96)); + attachLayout.setPivotX(dp((botButton == null || botButton.getVisibility() == GONE) && (notifyButton == null || notifyButton.getVisibility() == GONE) ? 48 : 96)); } public boolean isRtlText() { @@ -8843,7 +9182,7 @@ public boolean didPressedBotButton(final TLRPC.KeyboardButton button, final Mess Runnable onRequestWebView = new Runnable() { @Override public void run() { - if (sizeNotifierLayout.measureKeyboardHeight() > AndroidUtilities.dp(20)) { + if (sizeNotifierLayout.measureKeyboardHeight() > dp(20)) { AndroidUtilities.hideKeyboard(ChatActivityEnterView.this); AndroidUtilities.runOnUIThread(this, 150); return; @@ -9276,10 +9615,10 @@ public void onClearEmojiRecent() { return; } AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity, resourcesProvider); - builder.setTitle(LocaleController.getString("ClearRecentEmojiTitle", R.string.ClearRecentEmojiTitle)); - builder.setMessage(LocaleController.getString("ClearRecentEmojiText", R.string.ClearRecentEmojiText)); - builder.setPositiveButton(LocaleController.getString("ClearButton", R.string.ClearForAll), (dialogInterface, i) -> emojiView.clearRecentEmoji()); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.setTitle(LocaleController.getString(R.string.ClearRecentEmojiTitle)); + builder.setMessage(LocaleController.getString(R.string.ClearRecentEmojiText)); + builder.setPositiveButton(LocaleController.getString(R.string.ClearForAll), (dialogInterface, i) -> emojiView.clearRecentEmoji()); + builder.setNegativeButton(LocaleController.getString(R.string.Cancel), null); parentFragment.showDialog(builder.create()); } @@ -9422,7 +9761,7 @@ public void onDragStart() { NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.stopAllHeavyOperations, 1); stickersExpandedHeight = sizeNotifierLayout.getHeight() - (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? AndroidUtilities.statusBarHeight : 0) - ActionBar.getCurrentActionBarHeight() - getHeight() + Theme.chat_composeShadowDrawable.getIntrinsicHeight(); if (searchingType == 2) { - stickersExpandedHeight = Math.min(stickersExpandedHeight, AndroidUtilities.dp(120) + (AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight)); + stickersExpandedHeight = Math.min(stickersExpandedHeight, dp(120) + (AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight)); } emojiView.getLayoutParams().height = stickersExpandedHeight; emojiView.setLayerType(LAYER_TYPE_HARDWARE, null); @@ -9442,7 +9781,7 @@ public void onDragEnd(float velocity) { return; } stickersDragging = false; - if ((wasExpanded && velocity >= AndroidUtilities.dp(200)) || (!wasExpanded && velocity <= AndroidUtilities.dp(-200)) || (wasExpanded && stickersExpansionProgress <= 0.6f) || (!wasExpanded && stickersExpansionProgress >= 0.4f)) { + if ((wasExpanded && velocity >= dp(200)) || (!wasExpanded && velocity <= dp(-200)) || (wasExpanded && stickersExpansionProgress <= 0.6f) || (!wasExpanded && stickersExpansionProgress >= 0.4f)) { setStickersExpanded(!wasExpanded, true, true); } else { setStickersExpanded(wasExpanded, true, true); @@ -9598,10 +9937,10 @@ private void showPopup(int show, int contentType, boolean allowAnimation) { currentPopupContentType = contentType; if (keyboardHeight <= 0) { - keyboardHeight = MessagesController.getGlobalEmojiSettings().getInt("kbd_height", AndroidUtilities.dp(200)); + keyboardHeight = MessagesController.getGlobalEmojiSettings().getInt("kbd_height", dp(200)); } if (keyboardHeightLand <= 0) { - keyboardHeightLand = MessagesController.getGlobalEmojiSettings().getInt("kbd_height_land3", AndroidUtilities.dp(200)); + keyboardHeightLand = MessagesController.getGlobalEmojiSettings().getInt("kbd_height_land3", dp(200)); } int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; /*if (!samePannelWasVisible && !anotherPanelWasVisible) { @@ -10037,7 +10376,7 @@ public void onSizeChanged(int height, boolean isWidthGreater) { checkBotMenu(); return; } - if (height > AndroidUtilities.dp(50) && keyboardVisible && !AndroidUtilities.isInMultiwindow) { + if (height > dp(50) && keyboardVisible && !AndroidUtilities.isInMultiwindow) { if (isWidthGreater) { keyboardHeightLand = height; MessagesController.getGlobalEmojiSettings().edit().putInt("kbd_height_land3", keyboardHeightLand).commit(); @@ -10223,9 +10562,6 @@ public void didReceivedNotification(int id, int account, Object... args) { updateRecordInterface(RECORD_STATE_CANCEL); } } - if (id == NotificationCenter.recordStopped) { - Integer reason = (Integer) args[1]; - } } else if (id == NotificationCenter.recordStarted) { int guid = (Integer) args[0]; if (guid != recordingGuid) { @@ -10243,28 +10579,41 @@ public void didReceivedNotification(int id, int account, Object... args) { recordCircle.showWaves(true, true); } if (recordTimerView != null) { - recordTimerView.start(); + recordTimerView.start(millisecondsRecorded); } if (recordDot != null) { recordDot.enterAnimation = false; } + } else if (id == NotificationCenter.recordPaused) { + recordingAudioVideo = false; + audioToSend = null; + videoToSendMessageObject = null; + } else if (id == NotificationCenter.recordResumed) { + audioToSend = null; + videoToSendMessageObject = null; + checkSendButton(true); + recordingAudioVideo = true; + updateRecordInterface(RECORD_STATE_ENTER); } else if (id == NotificationCenter.audioDidSent) { int guid = (Integer) args[0]; if (guid != recordingGuid) { return; } + millisecondsRecorded = 0; Object audio = args[1]; if (audio instanceof VideoEditedInfo) { videoToSendMessageObject = (VideoEditedInfo) audio; audioToSendPath = (String) args[2]; ArrayList keyframes = (ArrayList) args[3]; + millisecondsRecorded = videoToSendMessageObject.estimatedDuration; if (videoTimelineView != null) { videoTimelineView.setVideoPath(audioToSendPath); videoTimelineView.setKeyframes(keyframes); videoTimelineView.setVisibility(VISIBLE); videoTimelineView.setMinProgressDiff(1000.0f / videoToSendMessageObject.estimatedDuration); + isRecordingStateChanged(); } updateRecordInterface(RECORD_STATE_PREPARING); checkSendButton(false); @@ -10317,7 +10666,8 @@ public void didReceivedNotification(int id, int account, Object... args) { break; } } - recordedAudioTimeTextView.setText(AndroidUtilities.formatShortDuration((int) duration)); + millisecondsRecorded = (long) (duration * 1000L); + recordedAudioTimeTextView.setText(AndroidUtilities.formatShortDuration((int) (duration))); checkSendButton(false); updateRecordInterface(RECORD_STATE_PREPARING); } else { @@ -10376,6 +10726,8 @@ public void didReceivedNotification(int id, int account, Object... args) { updateSlowModeText(); } } else if (id == NotificationCenter.audioRecordTooShort) { + audioToSend = null; + videoToSendMessageObject = null; updateRecordInterface(RECORD_STATE_CANCEL_BY_TIME); } else if (id == NotificationCenter.updateBotMenuButton) { long botId = (long) args[0]; @@ -10419,7 +10771,7 @@ public void checkStickresExpandHeight() { final int origHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; int newHeight = originalViewHeight - (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? AndroidUtilities.statusBarHeight : 0) - ActionBar.getCurrentActionBarHeight() - getHeight() + Theme.chat_composeShadowDrawable.getIntrinsicHeight(); if (searchingType == 2) { - newHeight = Math.min(newHeight, AndroidUtilities.dp(120) + origHeight); + newHeight = Math.min(newHeight, dp(120) + origHeight); } int currentHeight = emojiView.getLayoutParams().height; if (currentHeight == newHeight) { @@ -10516,7 +10868,7 @@ public void setStickersExpanded(boolean expanded, boolean animated, boolean byDr originalViewHeight = sizeNotifierLayout.getHeight(); stickersExpandedHeight = originalViewHeight - (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? AndroidUtilities.statusBarHeight : 0) - ActionBar.getCurrentActionBarHeight() - getHeight() + Theme.chat_composeShadowDrawable.getIntrinsicHeight(); if (searchingType == 2) { - stickersExpandedHeight = Math.min(stickersExpandedHeight, AndroidUtilities.dp(120) + origHeight); + stickersExpandedHeight = Math.min(stickersExpandedHeight, dp(120) + origHeight); } emojiView.getLayoutParams().height = stickersExpandedHeight; sizeNotifierLayout.requestLayout(); @@ -10788,8 +11140,13 @@ public void onCancelButtonPressed() { delegate.needStartRecordAudio(0); MediaController.getInstance().stopRecording(0, false, 0, voiceOnce); } + audioToSend = null; + audioToSendMessageObject = null; + videoToSendMessageObject = null; + millisecondsRecorded = 0; recordingAudioVideo = false; updateRecordInterface(RECORD_STATE_CANCEL); + checkSendButton(true); } @@ -10797,18 +11154,18 @@ public void onCancelButtonPressed() { public SlideTextView(@NonNull Context context) { super(context); - smallSize = AndroidUtilities.displaySize.x <= AndroidUtilities.dp(320); + smallSize = AndroidUtilities.displaySize.x <= dp(320); grayPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - grayPaint.setTextSize(AndroidUtilities.dp(smallSize ? 13 : 15)); + grayPaint.setTextSize(dp(smallSize ? 13 : 15)); bluePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - bluePaint.setTextSize(AndroidUtilities.dp(15)); + bluePaint.setTextSize(dp(15)); bluePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); arrowPaint.setColor(getThemedColor(Theme.key_chat_messagePanelIcons)); arrowPaint.setStyle(Paint.Style.STROKE); - arrowPaint.setStrokeWidth(AndroidUtilities.dpf2(smallSize ? 1f : 1.6f)); + arrowPaint.setStrokeWidth(dpf2(smallSize ? 1f : 1.6f)); arrowPaint.setStrokeCap(Paint.Cap.ROUND); arrowPaint.setStrokeJoin(Paint.Join.ROUND); @@ -10826,7 +11183,7 @@ public void updateColors() { bluePaint.setColor(getThemedColor(Theme.key_chat_recordVoiceCancel)); slideToAlpha = grayPaint.getAlpha(); cancelAlpha = bluePaint.getAlpha(); - selectableBackground = Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(60), 0, ColorUtils.setAlphaComponent(getThemedColor(Theme.key_chat_recordVoiceCancel), 26)); + selectableBackground = Theme.createSimpleSelectorCircleDrawable(dp(60), 0, ColorUtils.setAlphaComponent(getThemedColor(Theme.key_chat_recordVoiceCancel), 26)); selectableBackground.setCallback(this); } @@ -10863,13 +11220,13 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int heightHalf = getMeasuredHeight() >> 1; arrowPath.reset(); if (smallSize) { - arrowPath.setLastPoint(AndroidUtilities.dpf2(2.5f), heightHalf - AndroidUtilities.dpf2(3.12f)); + arrowPath.setLastPoint(dpf2(2.5f), heightHalf - dpf2(3.12f)); arrowPath.lineTo(0, heightHalf); - arrowPath.lineTo(AndroidUtilities.dpf2(2.5f), heightHalf + AndroidUtilities.dpf2(3.12f)); + arrowPath.lineTo(dpf2(2.5f), heightHalf + dpf2(3.12f)); } else { - arrowPath.setLastPoint(AndroidUtilities.dpf2(4f), heightHalf - AndroidUtilities.dpf2(5f)); + arrowPath.setLastPoint(dpf2(4f), heightHalf - dpf2(5f)); arrowPath.lineTo(0, heightHalf); - arrowPath.lineTo(AndroidUtilities.dpf2(4f), heightHalf + AndroidUtilities.dpf2(5f)); + arrowPath.lineTo(dpf2(4f), heightHalf + dpf2(5f)); } slideToLayout = new StaticLayout(slideToCancelString, grayPaint, (int) slideToCancelWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); @@ -10879,10 +11236,10 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @Override protected void onDraw(Canvas canvas) { - if (slideToLayout == null || cancelLayout == null) { + if (slideToLayout == null || cancelLayout == null || recordCircle == null) { return; } - int w = cancelLayout.getWidth() + AndroidUtilities.dp(16); + int w = cancelLayout.getWidth() + dp(16); grayPaint.setColor(getThemedColor(Theme.key_chat_recordTime)); grayPaint.setAlpha((int) (slideToAlpha * (1f - cancelToProgress) * slideProgress)); @@ -10890,21 +11247,21 @@ protected void onDraw(Canvas canvas) { arrowPaint.setColor(grayPaint.getColor()); if (smallSize) { - xOffset = AndroidUtilities.dp(16); + xOffset = dp(16); } else { long dt = (System.currentTimeMillis() - lastUpdateTime); lastUpdateTime = System.currentTimeMillis(); if (cancelToProgress == 0 && slideProgress > 0.8f) { if (moveForward) { - xOffset += (AndroidUtilities.dp(3) / 250f) * dt; - if (xOffset > AndroidUtilities.dp(6)) { - xOffset = AndroidUtilities.dp(6); + xOffset += (dp(3) / 250f) * dt; + if (xOffset > dp(6)) { + xOffset = dp(6); moveForward = false; } } else { - xOffset -= (AndroidUtilities.dp(3) / 250f) * dt; - if (xOffset < -AndroidUtilities.dp(6)) { - xOffset = -AndroidUtilities.dp(6); + xOffset -= (dp(3) / 250f) * dt; + if (xOffset < -dp(6)) { + xOffset = -dp(6); moveForward = true; } } @@ -10913,20 +11270,20 @@ protected void onDraw(Canvas canvas) { boolean enableTransition = cancelCharOffset >= 0; - int slideX = (int) ((getMeasuredWidth() - slideToCancelWidth) / 2) + AndroidUtilities.dp(5); + int slideX = (int) ((getMeasuredWidth() - slideToCancelWidth) / 2) + dp(5); int cancelX = (int) ((getMeasuredWidth() - cancelWidth) / 2); float offset = enableTransition ? slideToLayout.getPrimaryHorizontal(cancelCharOffset) : 0; float cancelDiff = enableTransition ? slideX + offset - cancelX : 0; - float x = slideX + xOffset * (1f - cancelToProgress) * slideProgress - cancelDiff * cancelToProgress + AndroidUtilities.dp(16); + float x = slideX + xOffset * (1f - cancelToProgress) * slideProgress - cancelDiff * cancelToProgress + dp(16); - float offsetY = enableTransition ? 0 : cancelToProgress * AndroidUtilities.dp(12); + float offsetY = enableTransition ? 0 : cancelToProgress * dp(12); if (cancelToProgress != 1) { int slideDelta = (int) (-getMeasuredWidth() / 4 * (1f - slideProgress) + recordCircle.getTranslationX() * 0.3f); canvas.save(); - canvas.clipRect((recordTimerView == null ? 0 : recordTimerView.getLeftProperty()) + AndroidUtilities.dp(4), 0, getMeasuredWidth(), getMeasuredHeight()); + canvas.clipRect((recordTimerView == null ? 0 : recordTimerView.getLeftProperty()) + dp(4), 0, getMeasuredWidth(), getMeasuredHeight()); canvas.save(); - canvas.translate((int) x - (smallSize ? AndroidUtilities.dp(7) : AndroidUtilities.dp(10)) + slideDelta, offsetY); + canvas.translate((int) x - (smallSize ? dp(7) : dp(10)) + slideDelta, offsetY); canvas.drawPath(arrowPath, arrowPaint); canvas.restore(); @@ -10940,7 +11297,7 @@ protected void onDraw(Canvas canvas) { float xi; float yi = (getMeasuredHeight() - cancelLayout.getHeight()) / 2f; if (!enableTransition) { - yi -= (AndroidUtilities.dp(12) - offsetY); + yi -= (dp(12) - offsetY); } if (enableTransition) { xi = x + offset; @@ -10948,7 +11305,7 @@ protected void onDraw(Canvas canvas) { xi = cancelX; } cancelRect.set((int) xi, (int) yi, (int) (xi + cancelLayout.getWidth()), (int) (yi + cancelLayout.getHeight())); - cancelRect.inset(-AndroidUtilities.dp(16), -AndroidUtilities.dp(16)); + cancelRect.inset(-dp(16), -dp(16)); if (cancelToProgress > 0) { selectableBackground.setBounds( getMeasuredWidth() / 2 - w, getMeasuredHeight() / 2 - w, @@ -11002,16 +11359,16 @@ public class TimerView extends View { float replaceTransition; TextPaint textPaint; - final float replaceDistance = AndroidUtilities.dp(15); + final float replaceDistance = dp(15); float left; public TimerView(Context context) { super(context); } - public void start() { + public void start(long milliseconds) { isRunning = true; - startTime = System.currentTimeMillis(); + startTime = System.currentTimeMillis() - milliseconds; lastSendTypingTime = startTime; invalidate(); } @@ -11032,7 +11389,7 @@ public void stop() { protected void onDraw(Canvas canvas) { if (textPaint == null) { textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); - textPaint.setTextSize(AndroidUtilities.dp(15)); + textPaint.setTextSize(dp(15)); textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); textPaint.setColor(getThemedColor(Theme.key_chat_recordTime)); } @@ -11208,21 +11565,21 @@ public RecordCircle getRecordCircle() { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (botCommandsMenuButton != null && botCommandsMenuButton.getTag() != null) { botCommandsMenuButton.measure(widthMeasureSpec, heightMeasureSpec); - ((MarginLayoutParams) emojiButton.getLayoutParams()).leftMargin = AndroidUtilities.dp(10) + (botCommandsMenuButton == null ? 0 : botCommandsMenuButton.getMeasuredWidth()); + ((MarginLayoutParams) emojiButton.getLayoutParams()).leftMargin = dp(10) + (botCommandsMenuButton == null ? 0 : botCommandsMenuButton.getMeasuredWidth()); if (messageEditText != null) { - ((MarginLayoutParams) messageEditText.getLayoutParams()).leftMargin = AndroidUtilities.dp(57) + (botCommandsMenuButton == null ? 0 : botCommandsMenuButton.getMeasuredWidth()); + ((MarginLayoutParams) messageEditText.getLayoutParams()).leftMargin = dp(57) + (botCommandsMenuButton == null ? 0 : botCommandsMenuButton.getMeasuredWidth()); } } else if (senderSelectView != null && senderSelectView.getVisibility() == View.VISIBLE) { int width = senderSelectView.getLayoutParams().width, height = senderSelectView.getLayoutParams().height; senderSelectView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - ((MarginLayoutParams) emojiButton.getLayoutParams()).leftMargin = AndroidUtilities.dp(16) + width; + ((MarginLayoutParams) emojiButton.getLayoutParams()).leftMargin = dp(16) + width; if (messageEditText != null) { - ((MarginLayoutParams) messageEditText.getLayoutParams()).leftMargin = AndroidUtilities.dp(63) + width; + ((MarginLayoutParams) messageEditText.getLayoutParams()).leftMargin = dp(63) + width; } } else { - ((MarginLayoutParams) emojiButton.getLayoutParams()).leftMargin = AndroidUtilities.dp(3); + ((MarginLayoutParams) emojiButton.getLayoutParams()).leftMargin = dp(3); if (messageEditText != null) { - ((MarginLayoutParams) messageEditText.getLayoutParams()).leftMargin = AndroidUtilities.dp(50); + ((MarginLayoutParams) messageEditText.getLayoutParams()).leftMargin = dp(50); } } updateBotCommandsMenuContainerTopPadding(); @@ -11232,7 +11589,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (botCommandsMenuButton != null) { botWebViewButton.setMeasuredButtonWidth(botCommandsMenuButton.getMeasuredWidth()); } - botWebViewButton.getLayoutParams().height = getMeasuredHeight() - AndroidUtilities.dp(2); + botWebViewButton.getLayoutParams().height = getMeasuredHeight() - dp(2); measureChild(botWebViewButton, widthMeasureSpec, heightMeasureSpec); } if (botWebViewMenuContainer != null) { @@ -11341,7 +11698,7 @@ protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); } else { canvas.save(); - canvas.clipRect(0, AndroidUtilities.dp(2), getMeasuredWidth(), getMeasuredHeight()); + canvas.clipRect(0, dp(2), getMeasuredWidth(), getMeasuredHeight()); canvas.translate(0, -emojiView.getStickersExpandOffset()); super.dispatchDraw(canvas); canvas.restore(); @@ -11367,19 +11724,22 @@ public void setChatSearchExpandOffset(float chatSearchExpandOffset) { public void setHorizontalPadding(float padding, float progress, boolean allowShare) { float leftPadding = -padding * (1f - progress); - float rightPadding = -(padding + AndroidUtilities.dp(40)) * (1f - progress); + float rightPadding = -(padding + dp(40)) * (1f - progress); float s = 0.5f + 0.5f * progress; emojiButtonPaddingScale = s; emojiButtonPaddingAlpha = progress; updateEmojiButtonParams(); emojiButton.setTranslationX(-leftPadding); - messageTextPaddingTranslationX = -leftPadding - AndroidUtilities.dp(31) * (1f - progress); + messageTextPaddingTranslationX = -leftPadding - dp(31) * (1f - progress); if (recordDeleteImageView != null) { recordDeleteImageView.setTranslationX(-leftPadding); } if (recordCircle != null) { recordCircle.setTranslationX(rightPadding); } + if (controlsView != null) { + controlsView.setTranslationX(rightPadding); + } if (recordTimeContainer != null) { recordTimeContainer.setTranslationX(-leftPadding); } @@ -11410,12 +11770,12 @@ public void setHorizontalPadding(float padding, float progress, boolean allowSha } } if (messageEditText != null) { - float scale = AndroidUtilities.lerp(0.88f, 1f, progress); + float scale = lerp(0.88f, 1f, progress); messageEditText.setPivotX(0); messageEditText.setPivotY(messageEditText.getMeasuredHeight() / 2f); messageEditText.setScaleX(scale); messageEditText.setScaleY(scale); - messageEditText.setHintRightOffset(AndroidUtilities.lerp(AndroidUtilities.dp(30), 0, progress)); + messageEditText.setHintRightOffset(lerp(dp(30), 0, progress)); } } @@ -11483,8 +11843,12 @@ public void reset() { audioVideoSendButton.setVisibility(View.VISIBLE); } recordIsCanceled = true; - cancelRecordIntefraceInternal(); + isRecordingStateChanged(); + cancelRecordInterfaceInternal(); hideRecordedAudioPanelInternal(); + if (recordCircle != null) { + recordCircle.setSendButtonInvisible(); + } } public enum BotMenuButtonType { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityInterface.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityInterface.java index 5d1ba021d5..a8ce41686b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityInterface.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityInterface.java @@ -37,7 +37,7 @@ default long getMergeDialogId() { return 0; } - default int getTopicId() { + default long getTopicId() { return 0; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java index 4513c0c633..1ecdc91ef2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java @@ -3884,7 +3884,7 @@ private void updateActionBarVisibility(boolean show, boolean animated) { actionBarAnimation = null; } - boolean needsSearchItem = searchItem != null && (avatarSearch || currentAttachLayout == photoLayout && !menuShowed && baseFragment instanceof ChatActivity && ((ChatActivity) baseFragment).allowSendGifs() && ((ChatActivity) baseFragment).allowSendPhotos()); + boolean needsSearchItem = searchItem != null && (avatarSearch || false && currentAttachLayout == photoLayout && !menuShowed && baseFragment instanceof ChatActivity && ((ChatActivity) baseFragment).allowSendGifs() && ((ChatActivity) baseFragment).allowSendPhotos()); boolean needMoreItem = !isPhotoPicker && (avatarPicker != 0 || !menuShowed) && currentAttachLayout == photoLayout && (photosEnabled || videosEnabled); if (currentAttachLayout == restrictedLayout) { needsSearchItem = false; @@ -4045,7 +4045,7 @@ public void updateCountButton(int animated) { menuAnimator.cancel(); menuAnimator = null; } - boolean needsSearchItem = searchItem != null && actionBar.getTag() != null && baseFragment instanceof ChatActivity && ((ChatActivity) baseFragment).allowSendGifs(); + boolean needsSearchItem = avatarPicker != 0 && searchItem != null && actionBar.getTag() != null && baseFragment instanceof ChatActivity && ((ChatActivity) baseFragment).allowSendGifs(); if (menuShowed) { if (avatarPicker == 0) { selectedMenuItem.setVisibility(View.VISIBLE); 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 2232ad2f17..483a612c32 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java @@ -20,6 +20,7 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.text.TextPaint; import android.text.TextUtils; import android.view.Gravity; import android.view.MotionEvent; @@ -67,6 +68,7 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent private SimpleTextView titleTextView; private AtomicReference titleTextLargerCopyView = new AtomicReference<>(); private SimpleTextView subtitleTextView; + private AnimatedTextView animatedSubtitleTextView; private AtomicReference subtitleTextLargerCopyView = new AtomicReference<>(); private ImageView timeItem; private TimerDrawable timerDrawable; @@ -104,8 +106,14 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent private AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable emojiStatusDrawable; + protected boolean useAnimatedSubtitle() { + return false; + } + public void hideSubtitle() { - subtitleTextView.setVisibility(View.GONE); + if (getSubtitleTextView() != null) { + getSubtitleTextView().setVisibility(View.GONE); + } } public void setStoriesForceState(Integer storiesForceState) { @@ -241,14 +249,27 @@ public boolean onTouchEvent(MotionEvent event) { titleTextView.setPadding(0, AndroidUtilities.dp(6), 0, AndroidUtilities.dp(12)); addView(titleTextView); - subtitleTextView = new SimpleTextConnectedView(context, subtitleTextLargerCopyView); - subtitleTextView.setEllipsizeByGradient(true); - subtitleTextView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubtitle)); - subtitleTextView.setTag(Theme.key_actionBarDefaultSubtitle); - subtitleTextView.setTextSize(14); - subtitleTextView.setGravity(Gravity.LEFT); - subtitleTextView.setPadding(0, 0, AndroidUtilities.dp(10), 0); - addView(subtitleTextView); + if (useAnimatedSubtitle()) { + animatedSubtitleTextView = new AnimatedTextView(context, true, true, true); + animatedSubtitleTextView.setAnimationProperties(.3f, 0, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + animatedSubtitleTextView.setEllipsizeByGradient(true); + animatedSubtitleTextView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubtitle)); + animatedSubtitleTextView.setTag(Theme.key_actionBarDefaultSubtitle); + animatedSubtitleTextView.setTextSize(AndroidUtilities.dp(14)); + animatedSubtitleTextView.setGravity(Gravity.LEFT); + animatedSubtitleTextView.setPadding(0, 0, AndroidUtilities.dp(10), 0); + animatedSubtitleTextView.setTranslationY(-AndroidUtilities.dp(1)); + addView(animatedSubtitleTextView); + } else { + subtitleTextView = new SimpleTextConnectedView(context, subtitleTextLargerCopyView); + subtitleTextView.setEllipsizeByGradient(true); + subtitleTextView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubtitle)); + subtitleTextView.setTag(Theme.key_actionBarDefaultSubtitle); + subtitleTextView.setTextSize(14); + subtitleTextView.setGravity(Gravity.LEFT); + subtitleTextView.setPadding(0, 0, AndroidUtilities.dp(10), 0); + addView(subtitleTextView); + } if (parentFragment != null) { timeItem = new ImageView(context); @@ -276,8 +297,8 @@ public boolean onTouchEvent(MotionEvent event) { } } - if (parentFragment != null && parentFragment.getChatMode() == 0) { - if ((!parentFragment.isThreadChat() || parentFragment.isTopic) && !UserObject.isReplyUser(parentFragment.getCurrentUser())) { + if (parentFragment != null && (parentFragment.getChatMode() == 0 || parentFragment.getChatMode() == ChatActivity.MODE_SAVED)) { + if ((!parentFragment.isThreadChat() || parentFragment.isTopic) && !UserObject.isReplyUser(parentFragment.getCurrentUser()) && parentFragment.getChatMode() != ChatActivity.MODE_SAVED) { setOnClickListener(v -> openProfile(false)); } @@ -406,7 +427,7 @@ public void openProfile(boolean byAvatar, boolean fromChatAnimation) { if (user != null) { Bundle args = new Bundle(); - if (UserObject.isUserSelf(user)) { + if (UserObject.isUserSelf(user) && parentFragment.getChatMode() != ChatActivity.MODE_SAVED) { args.putLong("dialog_id", parentFragment.getDialogId()); int[] media = new int[MediaDataController.MEDIA_TYPES_COUNT]; System.arraycopy(sharedMediaPreloader.getLastMediaCount(), 0, media, 0, media.length); @@ -414,11 +435,21 @@ public void openProfile(boolean byAvatar, boolean fromChatAnimation) { fragment.setChatInfo(parentFragment.getCurrentChatInfo()); parentFragment.presentFragment(fragment); } else { - args.putLong("user_id", user.id); - args.putBoolean("reportSpam", parentFragment.hasReportSpam()); - if (timeItem != null) { - args.putLong("dialog_id", parentFragment.getDialogId()); + if (parentFragment.getChatMode() == ChatActivity.MODE_SAVED) { + long dialogId = parentFragment.getSavedDialogId(); + args.putBoolean("saved", true); + if (dialogId >= 0) { + args.putLong("user_id", dialogId); + } else { + args.putLong("chat_id", -dialogId); + } + } else { + args.putLong("user_id", user.id); + if (timeItem != null) { + args.putLong("dialog_id", parentFragment.getDialogId()); + } } + args.putBoolean("reportSpam", parentFragment.hasReportSpam()); args.putInt("actionBarColor", getThemedColor(Theme.key_actionBarDefault)); ProfileActivity fragment = new ProfileActivity(args, sharedMediaPreloader); fragment.setUserInfo(parentFragment.getCurrentUserInfo()); @@ -430,8 +461,10 @@ public void openProfile(boolean byAvatar, boolean fromChatAnimation) { } else if (chat != null) { Bundle args = new Bundle(); args.putLong("chat_id", chat.id); - if (parentFragment.isTopic) { - args.putInt("topic_id", parentFragment.getThreadMessage().getId()); + if (parentFragment.getChatMode() == ChatActivity.MODE_SAVED) { + args.putLong("topic_id", parentFragment.getSavedDialogId()); + } else if (parentFragment.isTopic) { + args.putLong("topic_id", parentFragment.getThreadMessage().getId()); } ProfileActivity fragment = new ProfileActivity(args, sharedMediaPreloader); fragment.setChatInfo(parentFragment.getCurrentChatInfo()); @@ -458,7 +491,11 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int availableWidth = width - AndroidUtilities.dp((avatarImageView.getVisibility() == VISIBLE ? 54 : 0) + 16); avatarImageView.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(42), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(42), MeasureSpec.EXACTLY)); titleTextView.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(24 + 8) + titleTextView.getPaddingRight(), MeasureSpec.AT_MOST)); - subtitleTextView.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.AT_MOST)); + if (subtitleTextView != null) { + subtitleTextView.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.AT_MOST)); + } else if (animatedSubtitleTextView != null) { + animatedSubtitleTextView.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.AT_MOST)); + } if (timeItem != null) { timeItem.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(34), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(34), MeasureSpec.EXACTLY)); } @@ -511,7 +548,11 @@ private void fadeOutToLessWidth(int largerWidth) { subtitleTextLargerCopyView.setTag(Theme.key_actionBarDefaultSubtitle); subtitleTextLargerCopyView.setTextSize(14); subtitleTextLargerCopyView.setGravity(Gravity.LEFT); - subtitleTextLargerCopyView.setText(subtitleTextView.getText()); + if (subtitleTextView != null) { + subtitleTextLargerCopyView.setText(subtitleTextView.getText()); + } else if (animatedSubtitleTextView != null) { + subtitleTextLargerCopyView.setText(animatedSubtitleTextView.getText()); + } subtitleTextLargerCopyView.animate().alpha(0).setDuration(350).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).withEndAction(() -> { SimpleTextView subtitleTextLargerCopyView2 = this.subtitleTextLargerCopyView.get(); if (subtitleTextLargerCopyView2 != null) { @@ -534,7 +575,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto avatarImageView.layout(leftPadding, viewTop + 1, leftPadding + AndroidUtilities.dp(42), viewTop + 1 + AndroidUtilities.dp(42)); int l = leftPadding + (avatarImageView.getVisibility() == VISIBLE ? AndroidUtilities.dp( 54) : 0) + rightAvatarPadding; SimpleTextView titleTextLargerCopyView = this.titleTextLargerCopyView.get(); - if (subtitleTextView.getVisibility() != GONE) { + if (getSubtitleTextView().getVisibility() != GONE) { titleTextView.layout(l, viewTop + AndroidUtilities.dp(1.3f) - titleTextView.getPaddingTop(), l + titleTextView.getMeasuredWidth(), viewTop + titleTextView.getTextHeight() + AndroidUtilities.dp(1.3f) - titleTextView.getPaddingTop() + titleTextView.getPaddingBottom()); if (titleTextLargerCopyView != null) { titleTextLargerCopyView.layout(l, viewTop + AndroidUtilities.dp(1.3f), l + titleTextLargerCopyView.getMeasuredWidth(), viewTop + titleTextLargerCopyView.getTextHeight() + AndroidUtilities.dp(1.3f)); @@ -548,7 +589,11 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto if (timeItem != null) { timeItem.layout(leftPadding + AndroidUtilities.dp(16), viewTop + AndroidUtilities.dp(15), leftPadding + AndroidUtilities.dp(16 + 34), viewTop + AndroidUtilities.dp(15 + 34)); } - subtitleTextView.layout(l, viewTop + AndroidUtilities.dp(24), l + subtitleTextView.getMeasuredWidth(), viewTop + subtitleTextView.getTextHeight() + AndroidUtilities.dp(24)); + if (subtitleTextView != null) { + subtitleTextView.layout(l, viewTop + AndroidUtilities.dp(24), l + subtitleTextView.getMeasuredWidth(), viewTop + subtitleTextView.getTextHeight() + AndroidUtilities.dp(24)); + } else if (animatedSubtitleTextView != null) { + animatedSubtitleTextView.layout(l, viewTop + AndroidUtilities.dp(24), l + animatedSubtitleTextView.getMeasuredWidth(), viewTop + animatedSubtitleTextView.getTextHeight() + AndroidUtilities.dp(24)); + } SimpleTextView subtitleTextLargerCopyView = this.subtitleTextLargerCopyView.get(); if (subtitleTextLargerCopyView != null) { subtitleTextLargerCopyView.layout(l, viewTop + AndroidUtilities.dp(24), l + subtitleTextLargerCopyView.getMeasuredWidth(), viewTop + subtitleTextLargerCopyView.getTextHeight() + AndroidUtilities.dp(24)); @@ -692,7 +737,11 @@ public void setTitle(CharSequence value, boolean scam, boolean fake, boolean ver public void setSubtitle(CharSequence value) { if (lastSubtitle == null) { - subtitleTextView.setText(value); + if (subtitleTextView != null) { + subtitleTextView.setText(value); + } else if (animatedSubtitleTextView != null) { + animatedSubtitleTextView.setText(value); + } } else { lastSubtitle = value; } @@ -706,8 +755,18 @@ public SimpleTextView getTitleTextView() { return titleTextView; } - public SimpleTextView getSubtitleTextView() { - return subtitleTextView; + public View getSubtitleTextView() { + if (subtitleTextView != null) { + return subtitleTextView; + } + if (animatedSubtitleTextView != null) { + return animatedSubtitleTextView; + } + return null; + } + + public TextPaint getSubtitlePaint() { + return subtitleTextView != null ? subtitleTextView.getTextPaint() : animatedSubtitleTextView.getPaint(); } public void onDestroy() { @@ -717,6 +776,7 @@ public void onDestroy() { } private void setTypingAnimation(boolean start) { + if (subtitleTextView == null) return; if (start) { try { int type = MessagesController.getInstance(currentAccount).getPrintingStringType(parentFragment.getDialogId(), parentFragment.getThreadId()); @@ -759,9 +819,9 @@ public void updateSubtitle(boolean animated) { return; } TLRPC.User user = parentFragment.getCurrentUser(); - if (UserObject.isUserSelf(user) || UserObject.isReplyUser(user) || parentFragment.getChatMode() != 0) { - if (subtitleTextView.getVisibility() != GONE) { - subtitleTextView.setVisibility(GONE); + if ((UserObject.isUserSelf(user) || UserObject.isReplyUser(user) || parentFragment.getChatMode() != 0) && parentFragment.getChatMode() != ChatActivity.MODE_SAVED) { + if (getSubtitleTextView().getVisibility() != GONE) { + getSubtitleTextView().setVisibility(GONE); } return; } @@ -786,7 +846,7 @@ public void updateSubtitle(boolean animated) { titleAnimation = new AnimatorSet(); titleAnimation.playTogether( ObjectAnimator.ofFloat(titleTextView, View.TRANSLATION_Y, AndroidUtilities.dp(9.7f)), - ObjectAnimator.ofFloat(subtitleTextView, View.ALPHA, 0.0f)); + ObjectAnimator.ofFloat(getSubtitleTextView(), View.ALPHA, 0.0f)); titleAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationCancel(Animator animation) { @@ -796,7 +856,7 @@ public void onAnimationCancel(Animator animation) { @Override public void onAnimationEnd(Animator animation) { if (titleAnimation == animation) { - subtitleTextView.setVisibility(INVISIBLE); + getSubtitleTextView().setVisibility(INVISIBLE); titleAnimation = null; } } @@ -805,13 +865,16 @@ public void onAnimationEnd(Animator animation) { titleAnimation.start(); } else { titleTextView.setTranslationY(AndroidUtilities.dp(9.7f)); - subtitleTextView.setAlpha(0.0f); - subtitleTextView.setVisibility(INVISIBLE); + getSubtitleTextView().setAlpha(0.0f); + getSubtitleTextView().setVisibility(INVISIBLE); } return; } setTypingAnimation(false); - if (parentFragment.isTopic && chat != null) { + if (parentFragment.getChatMode() == ChatActivity.MODE_SAVED) { + int messagesCount = parentFragment.getMessagesController().getSavedMessagesController().getMessagesCount(parentFragment.getSavedDialogId()); + newSubtitle = LocaleController.formatPluralString("SavedMessagesCount", Math.max(1, messagesCount)); + } else if (parentFragment.isTopic && chat != null) { TLRPC.TL_forumTopic topic = MessagesController.getInstance(currentAccount).getTopicsController().findTopic(chat.id, parentFragment.getTopicId()); int count = 0; if (topic != null) { @@ -854,7 +917,7 @@ public void onAnimationEnd(Animator animation) { if (parentFragment.isThreadChat()) { if (titleTextView.getTag() != null) { titleTextView.setTag(null); - subtitleTextView.setVisibility(VISIBLE); + getSubtitleTextView().setVisibility(VISIBLE); if (titleAnimation != null) { titleAnimation.cancel(); titleAnimation = null; @@ -863,7 +926,7 @@ public void onAnimationEnd(Animator animation) { titleAnimation = new AnimatorSet(); titleAnimation.playTogether( ObjectAnimator.ofFloat(titleTextView, View.TRANSLATION_Y, 0), - ObjectAnimator.ofFloat(subtitleTextView, View.ALPHA, 1.0f)); + ObjectAnimator.ofFloat(getSubtitleTextView(), View.ALPHA, 1.0f)); titleAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -874,25 +937,35 @@ public void onAnimationEnd(Animator animation) { titleAnimation.start(); } else { titleTextView.setTranslationY(0.0f); - subtitleTextView.setAlpha(1.0f); + getSubtitleTextView().setAlpha(1.0f); } } } newSubtitle = printString; if (MessagesController.getInstance(currentAccount).getPrintingStringType(parentFragment.getDialogId(), parentFragment.getThreadId()) == 5) { - newSubtitle = Emoji.replaceEmoji(newSubtitle, subtitleTextView.getTextPaint().getFontMetricsInt(), AndroidUtilities.dp(15), false); + newSubtitle = Emoji.replaceEmoji(newSubtitle, getSubtitlePaint().getFontMetricsInt(), AndroidUtilities.dp(15), false); } useOnlineColor = true; setTypingAnimation(true); } lastSubtitleColorKey = useOnlineColor ? Theme.key_chat_status : Theme.key_actionBarDefaultSubtitle; if (lastSubtitle == null) { - subtitleTextView.setText(newSubtitle); - if (overrideSubtitleColor == null) { - subtitleTextView.setTextColor(getThemedColor(lastSubtitleColorKey)); - subtitleTextView.setTag(lastSubtitleColorKey); + if (subtitleTextView != null) { + subtitleTextView.setText(newSubtitle); + if (overrideSubtitleColor == null) { + subtitleTextView.setTextColor(getThemedColor(lastSubtitleColorKey)); + subtitleTextView.setTag(lastSubtitleColorKey); + } else { + subtitleTextView.setTextColor(overrideSubtitleColor); + } } else { - subtitleTextView.setTextColor(overrideSubtitleColor); + animatedSubtitleTextView.setText(newSubtitle, animated); + if (overrideSubtitleColor == null) { + animatedSubtitleTextView.setTextColor(getThemedColor(lastSubtitleColorKey)); + animatedSubtitleTextView.setTag(lastSubtitleColorKey); + } else { + animatedSubtitleTextView.setTextColor(overrideSubtitleColor); + } } } else { lastSubtitle = newSubtitle; @@ -984,6 +1057,12 @@ public void setUserAvatar(TLRPC.User user, boolean showSelf) { if (avatarImageView != null) { avatarImageView.setImage(null, null, avatarDrawable, user); } + } else if (UserObject.isAnonymous(user)) { + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_ANONYMOUS); + avatarDrawable.setScaleSize(.8f); + if (avatarImageView != null) { + avatarImageView.setImage(null, null, avatarDrawable, user); + } } else if (UserObject.isUserSelf(user) && !showSelf) { avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_SAVED); avatarDrawable.setScaleSize(.8f); @@ -1004,6 +1083,16 @@ public void checkAndUpdateAvatar() { } TLRPC.User user = parentFragment.getCurrentUser(); TLRPC.Chat chat = parentFragment.getCurrentChat(); + if (parentFragment.getChatMode() == ChatActivity.MODE_SAVED) { + long dialogId = parentFragment.getSavedDialogId(); + if (dialogId >= 0) { + user = parentFragment.getMessagesController().getUser(dialogId); + chat = null; + } else { + user = null; + chat = parentFragment.getMessagesController().getChat(-dialogId); + } + } if (user != null) { avatarDrawable.setInfo(currentAccount, user); if (UserObject.isReplyUser(user)) { @@ -1012,6 +1101,18 @@ public void checkAndUpdateAvatar() { if (avatarImageView != null) { avatarImageView.setImage(null, null, avatarDrawable, user); } + } else if (UserObject.isAnonymous(user)) { + avatarDrawable.setScaleSize(.8f); + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_ANONYMOUS); + if (avatarImageView != null) { + avatarImageView.setImage(null, null, avatarDrawable, user); + } + } else if (UserObject.isUserSelf(user) && parentFragment.getChatMode() == ChatActivity.MODE_SAVED) { + avatarDrawable.setScaleSize(.8f); + avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_MY_NOTES); + if (avatarImageView != null) { + avatarImageView.setImage(null, null, avatarDrawable, user); + } } else if (UserObject.isUserSelf(user)) { avatarDrawable.setScaleSize(.8f); avatarDrawable.setAvatarType(AvatarDrawable.AVATAR_TYPE_SAVED); @@ -1062,6 +1163,9 @@ protected void onAttachedToWindow() { if (parentFragment != null) { NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.didUpdateConnectionState); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); + if (parentFragment.getChatMode() == ChatActivity.MODE_SAVED) { + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.savedMessagesDialogsUpdate); + } currentConnectionState = ConnectionsManager.getInstance(currentAccount).getConnectionState(); updateCurrentConnectionState(); } @@ -1076,6 +1180,9 @@ protected void onDetachedFromWindow() { if (parentFragment != null) { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didUpdateConnectionState); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); + if (parentFragment.getChatMode() == ChatActivity.MODE_SAVED) { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.savedMessagesDialogsUpdate); + } } if (emojiStatusDrawable != null) { emojiStatusDrawable.detach(); @@ -1094,10 +1201,12 @@ public void didReceivedNotification(int id, int account, Object... args) { if (titleTextView != null) { titleTextView.invalidate(); } - if (subtitleTextView != null) { - subtitleTextView.invalidate(); + if (getSubtitleTextView() != null) { + getSubtitleTextView().invalidate(); } invalidate(); + } else if (id == NotificationCenter.savedMessagesDialogsUpdate) { + updateSubtitle(true); } } @@ -1114,25 +1223,49 @@ private void updateCurrentConnectionState() { } if (title == null) { if (lastSubtitle != null) { - subtitleTextView.setText(lastSubtitle); - lastSubtitle = null; - if (overrideSubtitleColor != null) { - subtitleTextView.setTextColor(overrideSubtitleColor); - } else if (lastSubtitleColorKey >= 0) { - subtitleTextView.setTextColor(getThemedColor(lastSubtitleColorKey)); - subtitleTextView.setTag(lastSubtitleColorKey); + if (subtitleTextView != null) { + subtitleTextView.setText(lastSubtitle); + lastSubtitle = null; + if (overrideSubtitleColor != null) { + subtitleTextView.setTextColor(overrideSubtitleColor); + } else if (lastSubtitleColorKey >= 0) { + subtitleTextView.setTextColor(getThemedColor(lastSubtitleColorKey)); + subtitleTextView.setTag(lastSubtitleColorKey); + } + } else if (animatedSubtitleTextView != null) { + animatedSubtitleTextView.setText(lastSubtitle, !LocaleController.isRTL); + lastSubtitle = null; + if (overrideSubtitleColor != null) { + animatedSubtitleTextView.setTextColor(overrideSubtitleColor); + } else if (lastSubtitleColorKey >= 0) { + animatedSubtitleTextView.setTextColor(getThemedColor(lastSubtitleColorKey)); + animatedSubtitleTextView.setTag(lastSubtitleColorKey); + } } } } else { - if (lastSubtitle == null) { - lastSubtitle = subtitleTextView.getText(); - } - subtitleTextView.setText(title); - if (overrideSubtitleColor != null) { - subtitleTextView.setTextColor(overrideSubtitleColor); - } else { - subtitleTextView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubtitle)); - subtitleTextView.setTag(Theme.key_actionBarDefaultSubtitle); + if (subtitleTextView != null) { + if (lastSubtitle == null) { + lastSubtitle = subtitleTextView.getText(); + } + subtitleTextView.setText(title); + if (overrideSubtitleColor != null) { + subtitleTextView.setTextColor(overrideSubtitleColor); + } else { + subtitleTextView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubtitle)); + subtitleTextView.setTag(Theme.key_actionBarDefaultSubtitle); + } + } else if (animatedSubtitleTextView != null) { + if (lastSubtitle == null) { + lastSubtitle = animatedSubtitleTextView.getText(); + } + animatedSubtitleTextView.setText(title, !LocaleController.isRTL); + if (overrideSubtitleColor != null) { + animatedSubtitleTextView.setTextColor(overrideSubtitleColor); + } else { + animatedSubtitleTextView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubtitle)); + animatedSubtitleTextView.setTag(Theme.key_actionBarDefaultSubtitle); + } } } } @@ -1151,7 +1284,11 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { sb.append(rightDrawable2ContentDescription); } sb.append("\n"); - sb.append(subtitleTextView.getText()); + if (subtitleTextView != null) { + sb.append(subtitleTextView.getText()); + } else if (animatedSubtitleTextView != null) { + sb.append(animatedSubtitleTextView.getText()); + } info.setContentDescription(sb); if (info.isClickable() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { info.addAction(new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, LocaleController.getString("OpenProfile", R.string.OpenProfile))); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatGreetingsView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatGreetingsView.java index 63d0a6d3fb..bbde88ed1c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatGreetingsView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatGreetingsView.java @@ -1,24 +1,46 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.content.Context; import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.text.Layout; import android.util.TypedValue; import android.view.Gravity; import android.view.View; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ContactsController; import org.telegram.messenger.DocumentObject; import org.telegram.messenger.FileLoader; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; import org.telegram.messenger.SvgHelper; +import org.telegram.messenger.UserObject; +import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.Premium.PremiumButtonView; +import org.telegram.ui.Components.Premium.StarParticlesView; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.PremiumPreviewFragment; +import org.telegram.ui.Stories.recorder.ButtonWithCounterView; +import org.telegram.ui.Stories.recorder.HintView2; import java.util.Locale; @@ -44,18 +66,16 @@ public ChatGreetingsView(Context context, TLRPC.User user, int distance, int cur titleView = new TextView(context); titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - titleView.setGravity(Gravity.CENTER_HORIZONTAL); + titleView.setTextAlignment(TEXT_ALIGNMENT_CENTER); + titleView.setGravity(Gravity.CENTER); descriptionView = new TextView(context); + descriptionView.setTextAlignment(TEXT_ALIGNMENT_CENTER); + descriptionView.setGravity(Gravity.CENTER); descriptionView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); descriptionView.setGravity(Gravity.CENTER_HORIZONTAL); - - stickerToSendView = new BackupImageView(context); - - addView(titleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 20, 14, 20, 14)); - addView(descriptionView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 20, 12, 20, 0)); - addView(stickerToSendView, LayoutHelper.createLinear(112, 112, Gravity.CENTER_HORIZONTAL, 0, 16, 0, 16)); + updateLayout(); updateColors(); @@ -66,6 +86,7 @@ public ChatGreetingsView(Context context, TLRPC.User user, int distance, int cur titleView.setText(LocaleController.formatString("NearbyPeopleGreetingsMessage", R.string.NearbyPeopleGreetingsMessage, user.first_name, LocaleController.formatDistance(distance, 1))); descriptionView.setText(LocaleController.getString("NearbyPeopleGreetingsDescription", R.string.NearbyPeopleGreetingsDescription)); } + descriptionView.setMaxWidth(HintView2.cutInFancyHalf(descriptionView.getText(), descriptionView.getPaint())); stickerToSendView.setContentDescription(descriptionView.getText()); preloadedGreetingsSticker = sticker; @@ -74,6 +95,126 @@ public ChatGreetingsView(Context context, TLRPC.User user, int distance, int cur } } + private ImageView premiumIconView; + private TextView premiumTextView; + private TextView premiumButtonView; + + private boolean premiumLock; + public void setPremiumLock(boolean lock, long dialogId) { + if (premiumLock == lock) return; + premiumLock = lock; + if (premiumLock) { + if (premiumIconView == null) { + premiumIconView = new ImageView(getContext()); + premiumIconView.setScaleType(ImageView.ScaleType.CENTER); + premiumIconView.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + premiumIconView.setBackground(Theme.createCircleDrawable(dp(78), 0x1c000000)); + premiumIconView.setImageResource(R.drawable.large_message_lock); + } + if (premiumTextView == null) { + premiumTextView = new TextView(getContext()); + premiumTextView.setTextAlignment(TEXT_ALIGNMENT_CENTER); + premiumTextView.setGravity(Gravity.CENTER); + premiumTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + } + String username = ""; + if (dialogId >= 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + if (user != null) { + username = UserObject.getUserName(user); + } + } + String text; + if (MessagesController.getInstance(currentAccount).premiumFeaturesBlocked()) { + text = LocaleController.formatString(R.string.MessageLockedPremiumLocked, username); + } else { + text = LocaleController.formatString(R.string.MessageLockedPremium, username); + } + premiumTextView.setText(AndroidUtilities.replaceTags(text)); + premiumTextView.setMaxWidth(HintView2.cutInFancyHalf(premiumTextView.getText(), premiumTextView.getPaint())); + premiumTextView.setTextColor(getThemedColor(Theme.key_chat_serviceText)); + premiumTextView.setLineSpacing(dp(2f), 1f); + if (premiumButtonView == null) { + premiumButtonView = new TextView(getContext()) { + StarParticlesView.Drawable starParticlesDrawable; + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + starParticlesDrawable = new StarParticlesView.Drawable(10); + starParticlesDrawable.type = 100; + starParticlesDrawable.isCircle = false; + starParticlesDrawable.roundEffect = true; + starParticlesDrawable.useRotate = false; + starParticlesDrawable.useBlur = true; + starParticlesDrawable.checkBounds = true; + starParticlesDrawable.size1 = 1; + starParticlesDrawable.k1 = starParticlesDrawable.k2 = starParticlesDrawable.k3 = 0.98f; + starParticlesDrawable.paused = false; + starParticlesDrawable.speedScale = 0f; + starParticlesDrawable.minLifeTime = 750; + starParticlesDrawable.randLifeTime = 750; + starParticlesDrawable.init(); + + AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); + starParticlesDrawable.rect.set(AndroidUtilities.rectTmp); + starParticlesDrawable.rect2.set(AndroidUtilities.rectTmp); + starParticlesDrawable.resetPositions(); + + clipPath.reset(); + clipPath.addRoundRect(AndroidUtilities.rectTmp, getHeight() / 2f, getHeight() / 2f, Path.Direction.CW); + } + + private final Path clipPath = new Path(); + @Override + protected void onDraw(Canvas canvas) { + if (starParticlesDrawable != null) { + canvas.save(); + canvas.clipPath(clipPath); + starParticlesDrawable.onDraw(canvas); + canvas.restore(); + invalidate(); + } + super.onDraw(canvas); + } + }; + premiumButtonView.setTextAlignment(TEXT_ALIGNMENT_CENTER); + premiumButtonView.setGravity(Gravity.CENTER); + premiumButtonView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + premiumButtonView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + premiumButtonView.setPadding(dp(13), dp(6.66f), dp(13), dp(7)); + premiumButtonView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(dp(15), 0x1e000000, 0x33000000)); + + ScaleStateListAnimator.apply(premiumButtonView); + } + premiumButtonView.setText(LocaleController.getString(R.string.MessagePremiumUnlock)); + premiumButtonView.setTextColor(getThemedColor(Theme.key_chat_serviceText)); + premiumButtonView.setOnClickListener(v -> { + BaseFragment fragment = LaunchActivity.getLastFragment(); + if (fragment != null) { + fragment.presentFragment(new PremiumPreviewFragment("contact")); + } + }); + } + updateLayout(); + } + + private void updateLayout() { + removeAllViews(); + if (premiumLock) { + addView(premiumIconView, LayoutHelper.createLinear(78, 78, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 20, 17, 20, 9)); + final boolean premiumLocked = MessagesController.getInstance(currentAccount).premiumFeaturesBlocked(); + addView(premiumTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 20, 0, 20, premiumLocked ? 13 : 9)); + if (!premiumLocked) { + addView(premiumButtonView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 30, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 20, 2, 20, 13)); + } + } else { + addView(titleView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 20, 14, 20, 6)); + addView(descriptionView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 20, 6, 20, 0)); + addView(stickerToSendView, LayoutHelper.createLinear(112, 112, Gravity.CENTER_HORIZONTAL, 0, 16, 0, 16)); + } + } + private void setSticker(TLRPC.Document sticker) { if (sticker == null) { return; @@ -116,7 +257,7 @@ public static String createFilter(TLRPC.Document document) { if (photoWidth == 0) { photoHeight = (int) maxHeight; - photoWidth = photoHeight + AndroidUtilities.dp(100); + photoWidth = photoHeight + dp(100); } photoHeight *= maxWidth / photoWidth; photoWidth = (int) maxWidth; @@ -202,4 +343,59 @@ private void fetchSticker() { private int getThemedColor(int key) { return Theme.getColor(key, resourcesProvider); } + + public static void showPremiumSheet(Context context, int currentAccount, long dialogId, Theme.ResourcesProvider resourcesProvider) { + BottomSheet sheet = new BottomSheet(context, false, resourcesProvider); + sheet.fixNavigationBar(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + + LinearLayout layout = new LinearLayout(context); + layout.setOrientation(LinearLayout.VERTICAL); + layout.setPadding(dp(16), 0, dp(16), 0); + + RLottieImageView imageView = new RLottieImageView(context); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setAnimation(R.raw.large_message_lock, 80, 80); + imageView.playAnimation(); + imageView.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + imageView.setBackground(Theme.createCircleDrawable(dp(80), Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider))); + layout.addView(imageView, LayoutHelper.createLinear(80, 80, Gravity.CENTER_HORIZONTAL, 0, 16, 0, 16)); + + final boolean premiumLocked = MessagesController.getInstance(currentAccount).premiumFeaturesBlocked(); + + TextView headerView = new TextView(context); + headerView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + headerView.setGravity(Gravity.CENTER); + headerView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + headerView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + headerView.setText(LocaleController.getString(premiumLocked ? R.string.PremiumMessageHeaderLocked : R.string.PremiumMessageHeader)); + layout.addView(headerView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 12, 0, 12, 0)); + + TextView descriptionView = new TextView(context); + descriptionView.setGravity(Gravity.CENTER); + descriptionView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + descriptionView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + String username = ""; + if (dialogId > 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + username = UserObject.getFirstName(user); + } + descriptionView.setText(AndroidUtilities.replaceTags(LocaleController.formatString(premiumLocked ? R.string.PremiumMessageTextLocked : R.string.PremiumMessageText, username, username))); + layout.addView(descriptionView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 12, 9, 12, 19)); + + if (!premiumLocked) { + PremiumButtonView button2 = new PremiumButtonView(context, true, resourcesProvider); + button2.setOnClickListener(v2 -> { + BaseFragment lastFragment = LaunchActivity.getLastFragment(); + if (lastFragment != null) { + lastFragment.presentFragment(new PremiumPreviewFragment("contact")); + sheet.dismiss(); + } + }); + button2.setOverlayText(LocaleController.getString(R.string.PremiumMessageButton), false, false); + layout.addView(button2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.CENTER_HORIZONTAL, 0, 0, 0, 4)); + } + + sheet.setCustomView(layout); + sheet.show(); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatNotificationsPopupWrapper.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatNotificationsPopupWrapper.java index 67f0190e75..a7f418216b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatNotificationsPopupWrapper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatNotificationsPopupWrapper.java @@ -158,7 +158,7 @@ private void dismiss() { lastDismissTime = System.currentTimeMillis(); } - public void update(long dialogId, int topicId, HashSet topicExceptions) { + public void update(long dialogId, long topicId, HashSet topicExceptions) { if (System.currentTimeMillis() - lastDismissTime < 200) { //do on popup close AndroidUtilities.runOnUIThread(() -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EarListener.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EarListener.java new file mode 100644 index 0000000000..9cccda9a6e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EarListener.java @@ -0,0 +1,330 @@ +package org.telegram.ui.Components; + +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.media.AudioDeviceInfo; +import android.media.AudioManager; +import android.os.Build; +import android.os.PowerManager; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.MediaController; +import org.telegram.messenger.NotificationsController; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.voip.VoIPService; +import org.telegram.ui.PhotoViewer; + +public class EarListener implements SensorEventListener { + + private final Context context; + private final SensorManager sensorManager; + private final PowerManager powerManager; + private final AudioManager audioManager; + + private Sensor proximitySensor; + private Sensor accelerometerSensor; + private Sensor linearSensor; + private Sensor gravitySensor; + private PowerManager.WakeLock proximityWakeLock; + + public EarListener(@NonNull Context context) { + this.context = context; + + sensorManager = (SensorManager) ApplicationLoader.applicationContext.getSystemService(Context.SENSOR_SERVICE); + proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + linearSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION); + gravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY); + if (linearSensor == null || gravitySensor == null) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("gravity or linear sensor not found"); + } + accelerometerSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + linearSensor = null; + gravitySensor = null; + } + + powerManager = (PowerManager) ApplicationLoader.applicationContext.getSystemService(Context.POWER_SERVICE); + proximityWakeLock = powerManager.newWakeLock(0x00000020, "telegram:proximity_lock2"); + + audioManager = (AudioManager) ApplicationLoader.applicationContext.getSystemService(Context.AUDIO_SERVICE); + } + + private boolean attached; + public void attach() { + if (!attached) { + if (gravitySensor != null) { + sensorManager.registerListener(this, gravitySensor, 30000); + } + if (linearSensor != null) { + sensorManager.registerListener(this, linearSensor, 30000); + } + if (accelerometerSensor != null) { + sensorManager.registerListener(this, accelerometerSensor, 30000); + } + sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL); + if (proximityWakeLock != null && !disableWakeLockWhenNotUsed()) { + proximityWakeLock.acquire(); + } + attached = true; + } + } + + public void detach() { + if (attached) { + if (gravitySensor != null) { + sensorManager.unregisterListener(this, gravitySensor); + } + if (linearSensor != null) { + sensorManager.unregisterListener(this, linearSensor); + } + if (accelerometerSensor != null) { + sensorManager.unregisterListener(this, accelerometerSensor); + } + sensorManager.unregisterListener(this, proximitySensor); + if (proximityWakeLock != null && proximityWakeLock.isHeld()) { + proximityWakeLock.release(); + } + attached = false; + } + } + + private boolean raised; + + public boolean isRaised() { + return raised; + } + + private VideoPlayer currentPlayer; + public void attachPlayer(VideoPlayer player) { + currentPlayer = player; + updateRaised(); + } + + protected void updateRaised() { + if (currentPlayer == null) { + return; + } + currentPlayer.setStreamType(raised ? AudioManager.STREAM_VOICE_CALL : AudioManager.STREAM_MUSIC); + } + + private boolean accelerometerVertical; + private long lastAccelerometerDetected; + private int raisedToTop; + private int raisedToTopSign; + private int raisedToBack; + private int countLess; + private long timeSinceRaise; + private long lastTimestamp = 0; + private boolean proximityTouched; + private boolean proximityHasDifferentValues; + private float lastProximityValue = -100; + private float previousAccValue; + private float[] gravity = new float[3]; + private float[] gravityFast = new float[3]; + private float[] linearAcceleration = new float[3]; + + @Override + public void onSensorChanged(SensorEvent event) { + if (!attached || VoIPService.getSharedInstance() != null) { + return; + } + if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("proximity changed to " + event.values[0] + " max value = " + event.sensor.getMaximumRange()); + } + if (lastProximityValue != event.values[0]) { + proximityHasDifferentValues = true; + } + lastProximityValue = event.values[0]; + if (proximityHasDifferentValues) { + proximityTouched = isNearToSensor(event.values[0]); + } + } else if (event.sensor == accelerometerSensor) { + final double alpha = lastTimestamp == 0 ? 0.98f : 1.0 / (1.0 + (event.timestamp - lastTimestamp) / 1000000000.0); + final float alphaFast = 0.8f; + lastTimestamp = event.timestamp; + gravity[0] = (float) (alpha * gravity[0] + (1.0 - alpha) * event.values[0]); + gravity[1] = (float) (alpha * gravity[1] + (1.0 - alpha) * event.values[1]); + gravity[2] = (float) (alpha * gravity[2] + (1.0 - alpha) * event.values[2]); + gravityFast[0] = (alphaFast * gravity[0] + (1.0f - alphaFast) * event.values[0]); + gravityFast[1] = (alphaFast * gravity[1] + (1.0f - alphaFast) * event.values[1]); + gravityFast[2] = (alphaFast * gravity[2] + (1.0f - alphaFast) * event.values[2]); + + linearAcceleration[0] = event.values[0] - gravity[0]; + linearAcceleration[1] = event.values[1] - gravity[1]; + linearAcceleration[2] = event.values[2] - gravity[2]; + } else if (event.sensor == linearSensor) { + linearAcceleration[0] = event.values[0]; + linearAcceleration[1] = event.values[1]; + linearAcceleration[2] = event.values[2]; + } else if (event.sensor == gravitySensor) { + gravityFast[0] = gravity[0] = event.values[0]; + gravityFast[1] = gravity[1] = event.values[1]; + gravityFast[2] = gravity[2] = event.values[2]; + } + final float minDist = 15.0f; + final int minCount = 6; + final int countLessMax = 10; + if (event.sensor == linearSensor || event.sensor == gravitySensor || event.sensor == accelerometerSensor) { + float val = gravity[0] * linearAcceleration[0] + gravity[1] * linearAcceleration[1] + gravity[2] * linearAcceleration[2]; + if (raisedToBack != minCount) { + if (val > 0 && previousAccValue > 0 || val < 0 && previousAccValue < 0) { + boolean goodValue; + int sign; + if (val > 0) { + goodValue = val > minDist; + sign = 1; + } else { + goodValue = val < -minDist; + sign = 2; + } + if (raisedToTopSign != 0 && raisedToTopSign != sign) { + if (raisedToTop == minCount && goodValue) { + if (raisedToBack < minCount) { + raisedToBack++; + if (raisedToBack == minCount) { + raisedToTop = 0; + raisedToTopSign = 0; + countLess = 0; + timeSinceRaise = System.currentTimeMillis(); + if (BuildVars.LOGS_ENABLED && BuildVars.DEBUG_PRIVATE_VERSION) { + FileLog.d("motion detected"); + } + } + } + } else { + if (!goodValue) { + countLess++; + } + if (countLess == countLessMax || raisedToTop != minCount || raisedToBack != 0) { + raisedToTop = 0; + raisedToTopSign = 0; + raisedToBack = 0; + countLess = 0; + } + } + } else { + if (goodValue && raisedToBack == 0 && (raisedToTopSign == 0 || raisedToTopSign == sign)) { + if (raisedToTop < minCount && !proximityTouched) { + raisedToTopSign = sign; + raisedToTop++; + if (raisedToTop == minCount) { + countLess = 0; + } + } + } else { + if (!goodValue) { + countLess++; + } + if (raisedToTopSign != sign || countLess == countLessMax || raisedToTop != minCount || raisedToBack != 0) { + raisedToBack = 0; + raisedToTop = 0; + raisedToTopSign = 0; + countLess = 0; + } + } + } + } + } + previousAccValue = val; + accelerometerVertical = gravityFast[1] > 2.5f && Math.abs(gravityFast[2]) < 4.0f && Math.abs(gravityFast[0]) > 1.5f; + } + if (raisedToBack == minCount || accelerometerVertical) { + lastAccelerometerDetected = System.currentTimeMillis(); + } + final boolean accelerometerDetected = raisedToBack == minCount || accelerometerVertical || System.currentTimeMillis() - lastAccelerometerDetected < 60; + final boolean wakelockAllowed = accelerometerDetected && !forbidRaiseToListen() && !VoIPService.isAnyKindOfCallActive() && !PhotoViewer.getInstance().isVisible(); + if (proximityWakeLock != null && disableWakeLockWhenNotUsed()) { + final boolean held = proximityWakeLock.isHeld(); + if (held && !wakelockAllowed) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("wake lock releasing"); + } + proximityWakeLock.release(); + } else if (!held && wakelockAllowed) { + if (BuildVars.LOGS_ENABLED) { + FileLog.d("wake lock acquiring"); + } + proximityWakeLock.acquire(); + } + } + if (proximityTouched && wakelockAllowed) { + if (!raised) { + raised = true; + updateRaised(); + } + raisedToBack = 0; + raisedToTop = 0; + raisedToTopSign = 0; + countLess = 0; + } else if (proximityTouched && ((accelerometerSensor == null || linearSensor == null) && gravitySensor == null) && !VoIPService.isAnyKindOfCallActive()) { + if (!raised) { + raised = true; + updateRaised(); + } + } else if (!proximityTouched) { + if (raised) { + raised = false; + updateRaised(); + } + } + if (timeSinceRaise != 0 && raisedToBack == minCount && Math.abs(System.currentTimeMillis() - timeSinceRaise) > 1000) { + raisedToBack = 0; + raisedToTop = 0; + raisedToTopSign = 0; + countLess = 0; + timeSinceRaise = 0; + } + } + + private boolean isNearToSensor(float value) { + return value < 5.0f && value != proximitySensor.getMaximumRange(); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + + } + + private boolean disableWakeLockWhenNotUsed() { + // Pixel devices cap phone fps to 60 when wake lock is held + // Samsung devices do much worse proximity detection when wake lock is not held + + // Solution: enable wake lock only when accelerometer detects raising, except for Samsung + return !Build.MANUFACTURER.equalsIgnoreCase("samsung"); + } + + protected boolean forbidRaiseToListen() { + try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); + for (AudioDeviceInfo device : devices) { + final int type = device.getType(); + if (( + type == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP || + type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO || + type == AudioDeviceInfo.TYPE_BLE_HEADSET || + type == AudioDeviceInfo.TYPE_BLE_SPEAKER || + type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES || + type == AudioDeviceInfo.TYPE_WIRED_HEADSET + ) && device.isSink()) { + return true; + } + } + return false; + } else { + return audioManager.isWiredHeadsetOn() || audioManager.isBluetoothA2dpOn() || audioManager.isBluetoothScoOn(); + } + } catch (Exception e) { + FileLog.e(e); + } + return false; + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java index d4005f942a..d55d682d16 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiPacksAlert.java @@ -1140,7 +1140,7 @@ private void updateButton() { removeButtonView.setOnClickListener(ev -> { dismiss(); if (fragment != null) { - MediaDataController.getInstance(fragment.getCurrentAccount()).removeMultipleStickerSets(fragment.getFragmentView().getContext(), fragment, installedPacks); + MediaDataController.getInstance(fragment.getCurrentAccount()).removeMultipleStickerSets(fragment.getContext(), fragment, installedPacks); } else { for (int i = 0; i < installedPacks.size(); ++i) { TLRPC.TL_messages_stickerSet stickerSet = installedPacks.get(i); 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 9b4ed05893..f87e28cb79 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmojiView.java @@ -8985,9 +8985,9 @@ public static abstract class ChooseStickerActionTracker { private final int currentAccount; private final long dialogId; - private final int threadId; + private final long threadId; - public ChooseStickerActionTracker(int currentAccount, long dialogId, int threadId) { + public ChooseStickerActionTracker(int currentAccount, long dialogId, long threadId) { this.currentAccount = currentAccount; this.dialogId = dialogId; this.threadId = threadId; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/ForumUtilities.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/ForumUtilities.java index 082d2dba30..2bec001782 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/ForumUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Forum/ForumUtilities.java @@ -16,13 +16,10 @@ import android.text.style.DynamicDrawableSpan; import android.text.style.ImageSpan; import android.util.SparseArray; -import android.util.SparseIntArray; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.google.android.exoplayer2.util.Log; - import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ContactsController; @@ -87,12 +84,12 @@ public static GeneralTopicDrawable createGeneralTopicDrawable(Context context, f return new GeneralTopicDrawable(context, scale, color, isDialog); } - public static void filterMessagesByTopic(int threadMessageId, ArrayList messageObjects) { + public static void filterMessagesByTopic(long threadMessageId, ArrayList messageObjects) { if (messageObjects == null) { return; } for (int i = 0; i < messageObjects.size(); i++) { - if (threadMessageId != MessageObject.getTopicId(messageObjects.get(i).messageOwner, true)) { + if (threadMessageId != MessageObject.getTopicId(messageObjects.get(i).currentAccount, messageObjects.get(i).messageOwner, true)) { messageObjects.remove(i); i--; } @@ -368,7 +365,7 @@ public static void applyTopicToMessage(MessageObject messageObject) { if (messageObject.getDialogId() > 0) { return; } - TLRPC.TL_forumTopic topic = MessagesController.getInstance(messageObject.currentAccount).getTopicsController().findTopic(-messageObject.getDialogId(), MessageObject.getTopicId(messageObject.messageOwner, true)); + TLRPC.TL_forumTopic topic = MessagesController.getInstance(messageObject.currentAccount).getTopicsController().findTopic(-messageObject.getDialogId(), MessageObject.getTopicId(messageObject.currentAccount, messageObject.messageOwner, true)); if (topic != null && messageObject.topicIconDrawable[0] instanceof ForumBubbleDrawable) { ((ForumBubbleDrawable) messageObject.topicIconDrawable[0]).setColor(topic.icon_color); } 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 6d6afa2ba2..5fadf30bc0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java @@ -16,7 +16,6 @@ import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; -import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothProfile; import android.content.Context; @@ -73,7 +72,6 @@ import com.google.android.exoplayer2.ExoPlayer; import org.telegram.messenger.AndroidUtilities; -import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.AutoDeleteMediaTask; import org.telegram.messenger.BuildVars; @@ -98,11 +96,10 @@ import org.telegram.messenger.video.MP4Builder; import org.telegram.messenger.video.Mp4Movie; import org.telegram.tgnet.ConnectionsManager; -import org.telegram.tgnet.NativeByteBuffer; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; -import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.voip.CellFlickerDrawable; +import org.telegram.ui.Stories.recorder.StoryEntry; import java.io.File; import java.io.FileOutputStream; @@ -163,7 +160,9 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter private boolean deviceHasGoodCamera; private boolean requestingPermissions; private File cameraFile; + private File previewFile; private long recordStartTime; + private long recordPlusTime; private boolean recording; private long recordedTime; private boolean cancelled; @@ -401,6 +400,7 @@ protected void onDraw(Canvas canvas) { }; addView(textureOverlayView, new LayoutParams(AndroidUtilities.roundPlayingMessageSize, AndroidUtilities.roundPlayingMessageSize, Gravity.CENTER)); + setVisibilityFromPause = false; setVisibility(INVISIBLE); blurBehindDrawable = new BlurBehindDrawable(parentView, this, 0, resourcesProvider); } @@ -494,17 +494,24 @@ public void destroy(boolean async, final Runnable beforeDestroyRunnable) { } } + protected void clipBlur(Canvas canvas) { + + } + @Override protected void onDraw(Canvas canvas) { if (drawBlur) { + canvas.save(); + clipBlur(canvas); blurBehindDrawable.draw(canvas); + canvas.restore(); } float x = cameraContainer.getX(); float y = cameraContainer.getY(); rect.set(x - AndroidUtilities.dp(8), y - AndroidUtilities.dp(8), x + cameraContainer.getMeasuredWidth() + AndroidUtilities.dp(8), y + cameraContainer.getMeasuredHeight() + AndroidUtilities.dp(8)); if (recording) { - recordedTime = System.currentTimeMillis() - recordStartTime; + recordedTime = System.currentTimeMillis() - recordStartTime + recordPlusTime; progress = Math.min(1f, recordedTime / 60000.0f); invalidate(); } @@ -519,6 +526,7 @@ protected void onDraw(Canvas canvas) { } } + private boolean setVisibilityFromPause; @Override public void setVisibility(int visibility) { super.setVisibility(visibility); @@ -531,10 +539,10 @@ public void setVisibility(int visibility) { muteImageView.setAlpha(0.0f); muteImageView.setScaleX(1.0f); muteImageView.setScaleY(1.0f); - cameraContainer.setScaleX(0.1f); - cameraContainer.setScaleY(0.1f); - textureOverlayView.setScaleX(0.1f); - textureOverlayView.setScaleY(0.1f); + cameraContainer.setScaleX(setVisibilityFromPause ? 1f : 0.1f); + cameraContainer.setScaleY(setVisibilityFromPause ? 1f : 0.1f); + textureOverlayView.setScaleX(setVisibilityFromPause ? 1f : 0.1f); + textureOverlayView.setScaleY(setVisibilityFromPause ? 1f : 0.1f); if (cameraContainer.getMeasuredWidth() != 0) { cameraContainer.setPivotX(cameraContainer.getMeasuredWidth() / 2); cameraContainer.setPivotY(cameraContainer.getMeasuredHeight() / 2); @@ -552,7 +560,41 @@ public void setVisibility(int visibility) { } } - public void showCamera() { + public void togglePause() { + if (recording) { + cancelled = recordedTime < 800; + recording = false; + if (cameraThread != null) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.recordStopped, recordingGuid, cancelled ? 4 : 2); + saveLastCameraBitmap(); + cameraThread.shutdown(cancelled ? 0 : 2, cancelled ? 0 : -2); + cameraThread = null; + } + if (cancelled) { + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.audioRecordTooShort, recordingGuid, true, (int) recordedTime); + startAnimation(false, false); + MediaController.getInstance().requestAudioFocus(false); + } else { + videoEncoder.pause(); + } + } else if (videoEncoder != null) { + videoEncoder.resume(); + hideCamera(false); + if (videoPlayer != null) { + videoPlayer.releasePlayer(true); + videoPlayer = null; + } + showCamera(true); + try { + performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); + } catch (Exception ignore) {} + AndroidUtilities.lockOrientation(delegate.getParentActivity()); + invalidate(); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.recordResumed); + } + } + + public void showCamera(boolean fromPaused) { if (textureView != null) { return; } @@ -581,10 +623,12 @@ public void showCamera() { textureOverlayView.setImageResource(R.drawable.icplaceholder); } cameraReady = false; - isFrontface = true; selectedCamera = null; - recordedTime = 0; - progress = 0; + if (!fromPaused) { + isFrontface = true; + recordedTime = 0; + progress = 0; + } cancelled = false; file = null; encryptedFile = null; @@ -603,15 +647,17 @@ public void showCamera() { } } - cameraFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_DOCUMENT), System.currentTimeMillis() + "_" + SharedConfig.getLastLocalId() + ".mp4") { - @Override - public boolean delete() { - if (BuildVars.LOGS_ENABLED) { - FileLog.e("delete camera file"); + if (!fromPaused) { + cameraFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_DOCUMENT), System.currentTimeMillis() + "_" + SharedConfig.getLastLocalId() + ".mp4") { + @Override + public boolean delete() { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("delete camera file"); + } + return super.delete(); } - return super.delete(); - } - }; + }; + } SharedConfig.saveConfig(); AutoDeleteMediaTask.lockFile(cameraFile); @@ -667,9 +713,10 @@ public void onSurfaceTextureUpdated(SurfaceTexture surface) { cameraContainer.addView(textureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); updateTextureViewSize = true; + setVisibilityFromPause = fromPaused; setVisibility(VISIBLE); - startAnimation(true); + startAnimation(true, fromPaused); MediaController.getInstance().requestAudioFocus(true); } @@ -677,7 +724,7 @@ public InstantViewCameraContainer getCameraContainer() { return cameraContainer; } - public void startAnimation(boolean open) { + public void startAnimation(boolean open, boolean fromPaused) { if (animatorSet != null) { animatorSet.removeAllListeners(); animatorSet.cancel(); @@ -690,7 +737,7 @@ public void startAnimation(boolean open) { cameraContainer.setTranslationX(0); textureOverlayView.setTranslationX(0); - animationTranslationY = getMeasuredHeight() / 2f; + animationTranslationY = fromPaused ? 0 : getMeasuredHeight() / 2f; updateTranslationY(); } opened = open; @@ -705,7 +752,7 @@ public void startAnimation(boolean open) { } ValueAnimator translationYAnimator = ValueAnimator.ofFloat(open ? 1f : 0f, open ? 0 : 1f); translationYAnimator.addUpdateListener(animation -> { - animationTranslationY = (getMeasuredHeight() / 2f) * (float) animation.getAnimatedValue(); + animationTranslationY = fromPaused ? 0 : (getMeasuredHeight() / 2f) * (float) animation.getAnimatedValue(); updateTranslationY(); }); animatorSet.playTogether( @@ -728,6 +775,7 @@ public void startAnimation(boolean open) { public void onAnimationEnd(Animator animation) { if (animation.equals(animatorSet)) { hideCamera(true); + setVisibilityFromPause = false; setVisibility(INVISIBLE); } } @@ -775,6 +823,10 @@ public void send(int state, boolean notify, int scheduleDate, int ttl) { videoPlayer = null; } if (state == 4) { + if (videoEncoder != null && recordedTime > 800) { + videoEncoder.stopRecording(VideoRecorder.ENCODER_SEND_SEND, ttl); + return; + } if (BuildVars.DEBUG_VERSION && !cameraFile.exists()) { FileLog.e(new RuntimeException("file not found :( round video")); } @@ -807,7 +859,7 @@ public void send(int state, boolean notify, int scheduleDate, int ttl) { entry.ttl = ttl; delegate.sendMedia(entry, videoEditedInfo, notify, scheduleDate, false); if (scheduleDate != 0) { - startAnimation(false); + startAnimation(false, false); } MediaController.getInstance().requestAudioFocus(false); } else { @@ -835,7 +887,7 @@ public void send(int state, boolean notify, int scheduleDate, int ttl) { } if (cancelled) { NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.audioRecordTooShort, recordingGuid, true, (int) recordedTime); - startAnimation(false); + startAnimation(false, false); MediaController.getInstance().requestAudioFocus(false); } } @@ -875,6 +927,8 @@ public void cancel(boolean byGesture) { saveLastCameraBitmap(); cameraThread.shutdown(0, 0); cameraThread = null; + } else if (videoEncoder != null) { + videoEncoder.stopRecording(VideoRecorder.ENCODER_SEND_CANCEL, 0); } if (cameraFile != null) { if (BuildVars.LOGS_ENABLED) { @@ -885,7 +939,7 @@ public void cancel(boolean byGesture) { cameraFile = null; } MediaController.getInstance().requestAudioFocus(false); - startAnimation(false); + startAnimation(false, false); blurBehindDrawable.show(false); invalidate(); } @@ -1224,6 +1278,10 @@ public void resetCameraFile() { cameraFile = null; } + private VideoRecorder videoEncoder; + + private Bitmap firstFrameThumb; + public class CameraGLThread extends DispatchQueue { private final static int EGL_CONTEXT_CLIENT_VERSION = 0x3098; @@ -1254,8 +1312,6 @@ public class CameraGLThread extends DispatchQueue { private Integer cameraId = 0; - private VideoRecorder videoEncoder; - private int surfaceWidth; private int surfaceHeight; @@ -1392,7 +1448,9 @@ private boolean initGL() { 0.5f + tX, 0.5f + tY }; - videoEncoder = new VideoRecorder(); + if (videoEncoder == null) { + videoEncoder = new VideoRecorder(); + } vertexBuffer = ByteBuffer.allocateDirect(verticesData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer(); vertexBuffer.put(verticesData).position(0); @@ -1509,18 +1567,32 @@ private void onDraw(Integer cameraId) { } cameraSurface.updateTexImage(); + boolean captureFirstFrameThumb = false; if (!recording) { + if (videoEncoder == null) { + videoEncoder = new VideoRecorder(); + } + if (videoEncoder.started) { + if (!cameraReady) { + cameraReady = true; + AndroidUtilities.runOnUIThread(() -> textureOverlayView.animate().setDuration(120).alpha(0.0f).setInterpolator(new DecelerateInterpolator()).start()); + } + } else { + captureFirstFrameThumb = true; + } videoEncoder.startRecording(cameraFile, EGL14.eglGetCurrentContext()); - recording = true; int orientation = currentSession.getCurrentOrientation(); if (orientation == 90 || orientation == 270) { float temp = scaleX; scaleX = scaleY; scaleY = temp; } + recording = true; } - videoEncoder.frameAvailable(cameraSurface, cameraId, System.nanoTime()); + if (videoEncoder != null) { + videoEncoder.frameAvailable(cameraSurface, cameraId, System.nanoTime()); + } cameraSurface.getTransformMatrix(mSTMatrix); @@ -1545,6 +1617,19 @@ private void onDraw(Integer cameraId) { GLES20.glUseProgram(0); egl10.eglSwapBuffers(eglDisplay, eglSurface); + + if (captureFirstFrameThumb) { + AndroidUtilities.runOnUIThread(() -> { + if (textureView == null) { + return; + } + if (firstFrameThumb != null) { + firstFrameThumb.recycle(); + firstFrameThumb = null; + } + firstFrameThumb = textureView.getBitmap(); + }); + } } @Override @@ -1563,7 +1648,7 @@ public void handleMessage(Message inputMessage) { break; case DO_SHUTDOWN_MESSAGE: finish(); - if (recording) { + if (recording && inputMessage.arg2 != -2) { videoEncoder.stopRecording(inputMessage.arg1, inputMessage.arg2); } Looper looper = Looper.myLooper(); @@ -1657,6 +1742,8 @@ public void requestRender() { private static final int MSG_STOP_RECORDING = 1; private static final int MSG_VIDEOFRAME_AVAILABLE = 2; private static final int MSG_AUDIOFRAME_AVAILABLE = 3; + private static final int MSG_PAUSE_RECORDING = 4; + private static final int MSG_RESUME_RECORDING = 5; private static class EncoderHandler extends Handler { private WeakReference mWeakEncoder; @@ -1681,7 +1768,7 @@ public void handleMessage(Message inputMessage) { if (BuildVars.LOGS_ENABLED) { FileLog.e("InstantCamera start encoder"); } - encoder.prepareEncoder(); + encoder.prepareEncoder(inputMessage.arg1 == 1); } catch (Exception e) { FileLog.e(e); encoder.handleStopRecording(0, 0); @@ -1696,6 +1783,20 @@ public void handleMessage(Message inputMessage) { encoder.handleStopRecording(inputMessage.arg1, inputMessage.arg2); break; } + case MSG_PAUSE_RECORDING: { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("InstantCamera pause encoder"); + } + encoder.handlePauseRecording(); + break; + } + case MSG_RESUME_RECORDING: { + if (BuildVars.LOGS_ENABLED) { + FileLog.e("InstantCamera resume encoder"); + } + encoder.handleResumeRecording(); + break; + } case MSG_VIDEOFRAME_AVAILABLE: { long timestamp = (((long) inputMessage.arg1) << 32) | (((long) inputMessage.arg2) & 0xffffffffL); Integer cameraId = (Integer) inputMessage.obj; @@ -1769,6 +1870,7 @@ private class VideoRecorder implements Runnable { private long lastCommitedFrameTime; private long audioStartTime = -1; + private boolean firstVideoFrameSincePause; private long currentTimestamp = 0; private long lastTimestamp = -1; @@ -1786,7 +1888,14 @@ private class VideoRecorder implements Runnable { private long desyncTime; private long videoFirst = -1; private long videoLast; + private long videoLastDt; + private long videoDiff; + private long prevVideoLast = -1; private long audioFirst = -1; + private long audioLast = -1; + private long audioLastDt = 0; + private long prevAudioLast = -1; + private long audioDiff; private boolean audioStopedByTime; private int drawProgram; @@ -1809,6 +1918,7 @@ private class VideoRecorder implements Runnable { DispatchQueue fileWriteQueue; + private volatile boolean pauseRecorder; private Runnable recorderRunnable = new Runnable() { @RequiresApi(api = Build.VERSION_CODES.N) @@ -1821,7 +1931,7 @@ public void run() { boolean shouldUseTimestamp = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N; while (!done) { - if (!running && audioRecorder.getRecordingState() != AudioRecord.RECORDSTATE_STOPPED) { + if ((!running || pauseRecorder) && audioRecorder.getRecordingState() != AudioRecord.RECORDSTATE_STOPPED) { try { audioRecorder.stop(); } catch (Exception e) { @@ -1870,12 +1980,15 @@ public void run() { } break; } + long timestamp; if (shouldUseTimestamp) { audioRecorder.getTimestamp(audioTimestamp, AudioTimestamp.TIMEBASE_MONOTONIC); - buffer.offset[a] = audioTimestamp.nanoTime / 1000; + timestamp = audioTimestamp.nanoTime / 1000; } else { - buffer.offset[a] = audioPresentationTimeUs; + timestamp = audioPresentationTimeUs; } + buffer.offset[a] = timestamp; + buffer.read[a] = readResult; int bufferDurationUs = 1000000 * readResult / audioSampleRate / 2; if (!shouldUseTimestamp) { @@ -1904,11 +2017,22 @@ public void run() { } catch (Exception e) { FileLog.e(e); } - handler.sendMessage(handler.obtainMessage(MSG_STOP_RECORDING, sendWhenDone, sendWhenDoneTTL)); + if (!pauseRecorder) { + handler.sendMessage(handler.obtainMessage(MSG_STOP_RECORDING, sendWhenDone, sendWhenDoneTTL)); + } } }; + private boolean started; + public void startRecording(File outputFile, android.opengl.EGLContext sharedContext) { + if (started) { + sharedEglContext = sharedContext; + handler.sendMessage(handler.obtainMessage(MSG_START_RECORDING, 1, 0)); + return; + } + + started = true; int resolution = MessagesController.getInstance(currentAccount).roundVideoSize; int bitrate = MessagesController.getInstance(currentAccount).roundVideoBitrate * 1024; AndroidUtilities.runOnUIThread(() -> { @@ -1949,7 +2073,7 @@ public void startRecording(File outputFile, android.opengl.EGLContext sharedCont generateKeyframeThumbsQueue.cleanupQueue(); generateKeyframeThumbsQueue.recycle(); } - generateKeyframeThumbsQueue = new DispatchQueue("keyframes_thumb_queque"); + generateKeyframeThumbsQueue = new DispatchQueue("keyframes_thumb_queue"); handler.sendMessage(handler.obtainMessage(MSG_START_RECORDING)); } @@ -1960,6 +2084,14 @@ public void stopRecording(int send, int ttl) { }); } + public void pause() { + handler.sendMessage(handler.obtainMessage(MSG_PAUSE_RECORDING)); + } + + public void resume() { + handler.sendMessage(handler.obtainMessage(MSG_RESUME_RECORDING)); + } + long prevTimestamp; public void frameAvailable(SurfaceTexture st, Integer cameraId, long timestampInternal) { synchronized (sync) { @@ -2002,6 +2134,9 @@ public void run() { } private void handleAudioFrameAvailable(AudioBufferInfo input) { + if (pauseRecorder) { + return; + } if (audioStopedByTime) { return; } @@ -2016,7 +2151,7 @@ private void handleAudioFrameAvailable(AudioBufferInfo input) { while (true) { boolean ok = false; for (int a = 0; a < input.results; a++) { - if (a == 0 && Math.abs(videoFirst - input.offset[a]) > 10000000L) { + if (a == 0 && Math.abs(videoFirst - input.offset[a]) > 10_000_000L) { desyncTime = videoFirst - input.offset[a]; audioFirst = input.offset[a]; ok = true; @@ -2119,7 +2254,14 @@ private void handleAudioFrameAvailable(AudioBufferInfo input) { } } } - audioEncoder.queueInputBuffer(inputBufferIndex, 0, inputBuffer.position(), startWriteTime == 0 ? 0 : startWriteTime - audioStartTime, isLast ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); + long time = startWriteTime == 0 ? 0 : startWriteTime - audioStartTime; + long realtime = time; + if (prevAudioLast >= 0) { + time += prevAudioLast; + } + audioLastDt = time - audioLast; + audioLast = time; + audioEncoder.queueInputBuffer(inputBufferIndex, 0, inputBuffer.position(), time, isLast ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0); } } } catch (Throwable e) { @@ -2128,6 +2270,9 @@ private void handleAudioFrameAvailable(AudioBufferInfo input) { } private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { + if (pauseRecorder) { + return; + } try { drainEncoder(false); } catch (Exception e) { @@ -2139,8 +2284,14 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { cameraChanged = true; lastCameraId = cameraId; } + if (prevVideoLast >= 0) { + if (videoDiff == -1) { + videoDiff = timestampNanos - prevVideoLast; + } + timestampNanos -= videoDiff; + } if (cameraChanged || lastTimestamp == -1) { - if (currentTimestamp != 0) { + if (currentTimestamp != 0 && !firstVideoFrameSincePause) { //real dt lead to asynchron aduio and video //surface may return wrong measured timestamp so big or negative // `\_(._.)_/` @@ -2163,6 +2314,7 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { alphaDt = dt = (timestampNanos - lastTimestamp); lastTimestamp = timestampNanos; } + firstVideoFrameSincePause = false; lastCommitedFrameTime = System.currentTimeMillis(); if (!skippedFirst) { skippedTime += dt; @@ -2178,6 +2330,7 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { FileLog.d("InstantCamera first video frame was at " + videoFirst); } } + videoLastDt = timestampNanos - videoLast; videoLast = timestampNanos; FloatBuffer textureBuffer = InstantCameraView.this.textureBuffer; @@ -2254,7 +2407,7 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { } private void createKeyframeThumb() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_HIGH && frameCount % 33 == 0) { + if (generateKeyframeThumbsQueue != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && SharedConfig.getDevicePerformanceClass() == SharedConfig.PERFORMANCE_CLASS_HIGH && frameCount % 33 == 0) { GenerateKeyframeThumbTask task = new GenerateKeyframeThumbTask(); generateKeyframeThumbsQueue.postRunnable(task); } @@ -2282,8 +2435,197 @@ public void run() { } } + private void handlePauseRecording() { + pauseRecorder = true; + if (previewFile != null) { + previewFile.delete(); + previewFile = null; + } + previewFile = StoryEntry.makeCacheFile(currentAccount, true); + try { + FileLog.d("InstantCamera handlePauseRecording drain encoders"); + drainEncoder(false); + } catch (Exception e) { + FileLog.e(e); + } +// if (videoEncoder != null) { +// try { +// videoEncoder.stop(); +// videoEncoder.release(); +// videoEncoder = null; +// } catch (Exception e) { +// FileLog.e(e); +// } +// } +// if (audioEncoder != null) { +// try { +// audioEncoder.stop(); +// audioEncoder.release(); +// audioEncoder = null; +// +// setBluetoothScoOn(false); +// } catch (Exception e) { +// FileLog.e(e); +// } +// } + if (mediaMuxer != null) { + if (WRITE_TO_FILE_IN_BACKGROUND) { + CountDownLatch countDownLatch = new CountDownLatch(1); + fileWriteQueue.postRunnable(() -> { + try { + mediaMuxer.finishMovie(previewFile); + } catch (Exception e) { + e.printStackTrace(); + } + countDownLatch.countDown(); + }); + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else { + try { + mediaMuxer.finishMovie(previewFile); + } catch (Exception e) { + FileLog.e(e); + } + } + } +// FileLoader.getInstance(currentAccount).cancelFileUpload(videoFile.getAbsolutePath(), false); + AndroidUtilities.runOnUIThread(() -> { + videoEditedInfo = new VideoEditedInfo(); + videoEditedInfo.roundVideo = true; + videoEditedInfo.startTime = -1; + videoEditedInfo.endTime = -1; + videoEditedInfo.file = file; + videoEditedInfo.encryptedFile = encryptedFile; + videoEditedInfo.key = key; + videoEditedInfo.iv = iv; + videoEditedInfo.estimatedSize = Math.max(1, size); + videoEditedInfo.framerate = 25; + videoEditedInfo.resultWidth = videoEditedInfo.originalWidth = 360; + videoEditedInfo.resultHeight = videoEditedInfo.originalHeight = 360; + videoEditedInfo.originalPath = previewFile.getAbsolutePath(); + setupVideoPlayer(previewFile); + videoEditedInfo.estimatedDuration = recordedTime; + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.audioDidSent, recordingGuid, videoEditedInfo, previewFile.getAbsolutePath(), keyframeThumbs); + }); + } + + private void handleResumeRecording() { + pauseRecorder = false; + } + + private void setupVideoPlayer(File file) { + videoPlayer = new VideoPlayer(); + videoPlayer.setDelegate(new VideoPlayer.VideoPlayerDelegate() { + @Override + public void onStateChanged(boolean playWhenReady, int playbackState) { + if (videoPlayer == null) { + return; + } + if (videoPlayer.isPlaying() && playbackState == ExoPlayer.STATE_ENDED) { + videoPlayer.seekTo(videoEditedInfo.startTime > 0 ? videoEditedInfo.startTime : 0); + } + } + + @Override + public void onError(VideoPlayer player, Exception e) { + FileLog.e(e); + } + + @Override + public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { + + } + + @Override + public void onRenderedFirstFrame() { + + } + + @Override + public boolean onSurfaceDestroyed(SurfaceTexture surfaceTexture) { + return false; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { + + } + }); + videoPlayer.setTextureView(textureView); + videoPlayer.preparePlayer(Uri.fromFile(file), "other"); + videoPlayer.play(); + videoPlayer.setMute(true); + startProgressTimer(); + + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.playTogether( + ObjectAnimator.ofFloat(switchCameraButton, View.ALPHA, 0.0f), + ObjectAnimator.ofInt(paint, AnimationProperties.PAINT_ALPHA, 0), + ObjectAnimator.ofFloat(muteImageView, View.ALPHA, 1.0f)); + animatorSet.setDuration(180); + animatorSet.setInterpolator(new DecelerateInterpolator()); + animatorSet.start(); + + EGL14.eglDestroySurface(eglDisplay, eglSurface); + eglSurface = EGL14.EGL_NO_SURFACE; + if (surface != null) { + surface.release(); + surface = null; + } + if (eglDisplay != EGL14.EGL_NO_DISPLAY) { + EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); + EGL14.eglDestroyContext(eglDisplay, eglContext); + EGL14.eglReleaseThread(); + EGL14.eglTerminate(eglDisplay); + } + eglDisplay = EGL14.EGL_NO_DISPLAY; + eglContext = EGL14.EGL_NO_CONTEXT; + eglConfig = null; + } + + public static final int ENCODER_SEND_CANCEL = 0; + public static final int ENCODER_SEND_SEND = 1; + public static final int ENCODER_SEND_PLAYER = 2; + + private boolean sentMedia; + private void handleStopRecording(final int send, final int ttl) { - if (running) { + final boolean runDone; + if (send == ENCODER_SEND_SEND && (videoEditedInfo == null || !videoEditedInfo.needConvert()) && !delegate.isInScheduleMode()) { + runDone = false; + if (!sentMedia) { + sentMedia = true; + AndroidUtilities.runOnUIThread(() -> { + videoEditedInfo = new VideoEditedInfo(); + videoEditedInfo.startTime = -1; + videoEditedInfo.endTime = -1; + videoEditedInfo.estimatedSize = Math.max(1, size); + videoEditedInfo.roundVideo = true; + videoEditedInfo.file = file; + videoEditedInfo.encryptedFile = encryptedFile; + videoEditedInfo.key = key; + videoEditedInfo.iv = iv; + videoEditedInfo.framerate = 25; + videoEditedInfo.resultWidth = videoEditedInfo.originalWidth = 360; + videoEditedInfo.resultHeight = videoEditedInfo.originalHeight = 360; + videoEditedInfo.originalPath = videoFile.getAbsolutePath(); + videoEditedInfo.notReadyYet = true; + videoEditedInfo.thumb = firstFrameThumb; + videoEditedInfo.estimatedDuration = recordedTime; + firstFrameThumb = null; + MediaController.PhotoEntry entry = new MediaController.PhotoEntry(0, 0, 0, videoFile.getAbsolutePath(), 0, true, 0, 0, 0); + entry.ttl = ttl; + delegate.sendMedia(entry, videoEditedInfo, true, 0, false); + }); + } + } else { + runDone = true; + } + if (running && !pauseRecorder) { FileLog.d("InstantCamera handleStopRecording running=false"); sendWhenDone = send; sendWhenDoneTTL = ttl; @@ -2316,7 +2658,10 @@ private void handleStopRecording(final int send, final int ttl) { FileLog.e(e); } } - + if (previewFile != null) { + previewFile.delete(); + previewFile = null; + } if (mediaMuxer != null) { if (WRITE_TO_FILE_IN_BACKGROUND) { CountDownLatch countDownLatch = new CountDownLatch(1); @@ -2342,6 +2687,14 @@ private void handleStopRecording(final int send, final int ttl) { } FileLog.d("InstantCamera handleStopRecording finish muxer"); if (writingToDifferentFile) { + if (videoFile.exists()) { + try { + videoFile.delete(); + } catch (Exception e) { + FileLog.e("InstantCamera copying fileToWrite to videoFile, deleting videoFile error " + videoFile); + FileLog.e(e); + } + } if (!fileToWrite.renameTo(videoFile)) { FileLog.e("InstantCamera unable to rename file, try move file"); try { @@ -2354,112 +2707,90 @@ private void handleStopRecording(final int send, final int ttl) { } } } - if (generateKeyframeThumbsQueue != null) { - generateKeyframeThumbsQueue.cleanupQueue(); - generateKeyframeThumbsQueue.recycle(); - generateKeyframeThumbsQueue = null; + if (send != 2) { + if (generateKeyframeThumbsQueue != null) { + generateKeyframeThumbsQueue.cleanupQueue(); + generateKeyframeThumbsQueue.recycle(); + generateKeyframeThumbsQueue = null; + } } FileLog.d("InstantCamera handleStopRecording send " + send); - if (send != 0) { - AndroidUtilities.runOnUIThread(() -> { - videoEditedInfo = new VideoEditedInfo(); - videoEditedInfo.roundVideo = true; - videoEditedInfo.startTime = -1; - videoEditedInfo.endTime = -1; - videoEditedInfo.file = file; - videoEditedInfo.encryptedFile = encryptedFile; - videoEditedInfo.key = key; - videoEditedInfo.iv = iv; - videoEditedInfo.estimatedSize = Math.max(1, size); - videoEditedInfo.framerate = 25; - videoEditedInfo.resultWidth = videoEditedInfo.originalWidth = 360; - videoEditedInfo.resultHeight = videoEditedInfo.originalHeight = 360; - videoEditedInfo.originalPath = videoFile.getAbsolutePath(); - if (send == 1) { - if (delegate.isInScheduleMode()) { - AlertsCreator.createScheduleDatePickerDialog(delegate.getParentActivity(), delegate.getDialogId(), (notify, scheduleDate) -> { - MediaController.PhotoEntry entry = new MediaController.PhotoEntry(0, 0, 0, videoFile.getAbsolutePath(), 0, true, 0, 0, 0); - entry.ttl = ttl; - delegate.sendMedia(entry, videoEditedInfo, notify, scheduleDate, false); - startAnimation(false); - }, () -> { - startAnimation(false); - }, resourcesProvider); - } else { - MediaController.PhotoEntry entry = new MediaController.PhotoEntry(0, 0, 0, videoFile.getAbsolutePath(), 0, true, 0, 0, 0); - entry.ttl = ttl; - delegate.sendMedia(entry, videoEditedInfo, true, 0, false); + if (send == ENCODER_SEND_CANCEL) { + FileLoader.getInstance(currentAccount).cancelFileUpload(videoFile.getAbsolutePath(), false); + try { + fileToWrite.delete(); + } catch (Throwable ignore) {} + try { + videoFile.delete(); + } catch (Throwable ignore) {} + } else { + if (runDone && (send != ENCODER_SEND_SEND || !sentMedia)) { + sentMedia = true; + AndroidUtilities.runOnUIThread(() -> { + if (videoEditedInfo == null) { + videoEditedInfo = new VideoEditedInfo(); + videoEditedInfo.startTime = -1; + videoEditedInfo.endTime = -1; } - } else { - videoPlayer = new VideoPlayer(); - videoPlayer.setDelegate(new VideoPlayer.VideoPlayerDelegate() { - @Override - public void onStateChanged(boolean playWhenReady, int playbackState) { - if (videoPlayer == null) { - return; - } - if (videoPlayer.isPlaying() && playbackState == ExoPlayer.STATE_ENDED) { - videoPlayer.seekTo(videoEditedInfo.startTime > 0 ? videoEditedInfo.startTime : 0); - } - } - - @Override - public void onError(VideoPlayer player, Exception e) { - FileLog.e(e); + if (videoEditedInfo.needConvert()) { + file = null; + encryptedFile = null; + key = null; + iv = null; + double totalDuration = videoEditedInfo.estimatedDuration; + long startTime = videoEditedInfo.startTime >= 0 ? videoEditedInfo.startTime : 0; + long endTime = videoEditedInfo.endTime >= 0 ? videoEditedInfo.endTime : videoEditedInfo.estimatedDuration; + videoEditedInfo.estimatedDuration = endTime - startTime; + videoEditedInfo.estimatedSize = Math.max(1, (long) (size * (videoEditedInfo.estimatedDuration / totalDuration))); + videoEditedInfo.bitrate = 1000000; + if (videoEditedInfo.startTime > 0) { + videoEditedInfo.startTime *= 1000; } - - @Override - public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) { - + if (videoEditedInfo.endTime > 0) { + videoEditedInfo.endTime *= 1000; } - - @Override - public void onRenderedFirstFrame() { - - } - - @Override - public boolean onSurfaceDestroyed(SurfaceTexture surfaceTexture) { - return false; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { - + FileLoader.getInstance(currentAccount).cancelFileUpload(cameraFile.getAbsolutePath(), false); + } else { + videoEditedInfo.estimatedSize = Math.max(1, size); + } + videoEditedInfo.roundVideo = true; + videoEditedInfo.file = file; + videoEditedInfo.encryptedFile = encryptedFile; + videoEditedInfo.key = key; + videoEditedInfo.iv = iv; + videoEditedInfo.framerate = 25; + videoEditedInfo.resultWidth = videoEditedInfo.originalWidth = 360; + videoEditedInfo.resultHeight = videoEditedInfo.originalHeight = 360; + videoEditedInfo.originalPath = videoFile.getAbsolutePath(); + if (send == ENCODER_SEND_SEND) { + if (delegate.isInScheduleMode()) { + AlertsCreator.createScheduleDatePickerDialog(delegate.getParentActivity(), delegate.getDialogId(), (notify, scheduleDate) -> { + MediaController.PhotoEntry entry = new MediaController.PhotoEntry(0, 0, 0, videoFile.getAbsolutePath(), 0, true, 0, 0, 0); + entry.ttl = ttl; + delegate.sendMedia(entry, videoEditedInfo, notify, scheduleDate, false); + startAnimation(false, false); + }, () -> { + startAnimation(false, false); + }, resourcesProvider); + } else { + MediaController.PhotoEntry entry = new MediaController.PhotoEntry(0, 0, 0, videoFile.getAbsolutePath(), 0, true, 0, 0, 0); + entry.ttl = ttl; + delegate.sendMedia(entry, videoEditedInfo, true, 0, false); } - }); - videoPlayer.setTextureView(textureView); - videoPlayer.preparePlayer(Uri.fromFile(videoFile), "other"); - videoPlayer.play(); - videoPlayer.setMute(true); - startProgressTimer(); - - AnimatorSet animatorSet = new AnimatorSet(); - animatorSet.playTogether( - ObjectAnimator.ofFloat(switchCameraButton, View.ALPHA, 0.0f), - ObjectAnimator.ofInt(paint, AnimationProperties.PAINT_ALPHA, 0), - ObjectAnimator.ofFloat(muteImageView, View.ALPHA, 1.0f)); - animatorSet.setDuration(180); - animatorSet.setInterpolator(new DecelerateInterpolator()); - animatorSet.start(); - videoEditedInfo.estimatedDuration = recordedTime; - NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.audioDidSent, recordingGuid, videoEditedInfo, videoFile.getAbsolutePath(), keyframeThumbs); + } else { + setupVideoPlayer(videoFile); + videoEditedInfo.estimatedDuration = recordedTime; + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.audioDidSent, recordingGuid, videoEditedInfo, videoFile.getAbsolutePath(), keyframeThumbs); + } + }); + } + AndroidUtilities.runOnUIThread(() -> { + if (sentMedia && videoEditedInfo != null) { + videoEditedInfo.notReadyYet = false; } didWriteData(videoFile, 0, true); MediaController.getInstance().requestAudioFocus(false); }); - } else { - FileLoader.getInstance(currentAccount).cancelFileUpload(videoFile.getAbsolutePath(), false); - try { - fileToWrite.delete(); - } catch (Throwable ignore) { - - } - try { - videoFile.delete(); - } catch (Throwable ignore) { - - } } EGL14.eglDestroySurface(eglDisplay, eglSurface); eglSurface = EGL14.EGL_NO_SURFACE; @@ -2477,6 +2808,9 @@ public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { eglContext = EGL14.EGL_NO_CONTEXT; eglConfig = null; handler.exit(); + AndroidUtilities.runOnUIThread(() -> { + InstantCameraView.this.videoEncoder = null; + }); } private void setBluetoothScoOn(boolean scoOn) { @@ -2498,7 +2832,7 @@ private void setBluetoothScoOn(boolean scoOn) { } } - private void prepareEncoder() { + private void prepareEncoder(boolean fromPause) { setBluetoothScoOn(true); try { @@ -2510,14 +2844,37 @@ private void prepareEncoder() { if (bufferSize < recordBufferSize) { bufferSize = ((recordBufferSize / 2048) + 1) * 2048 * 2; } + buffers.clear(); for (int a = 0; a < 3; a++) { buffers.add(new AudioBufferInfo()); } + + if (fromPause) { + prevVideoLast = videoLast + videoLastDt; + prevAudioLast = audioLast + audioLastDt; + firstVideoFrameSincePause = true; + } else { + prevVideoLast = -1; + prevAudioLast = -1; + currentTimestamp = 0; + } + lastTimestamp = -1; + lastCommitedFrameTime = 0; + audioStartTime = -1; + audioFirst = -1; + videoFirst = -1; + videoLast = -1; + videoDiff = -1; + audioLast = -1; + audioDiff = -1; + skippedFirst = false; + audioRecorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, audioSampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize); audioRecorder.startRecording(); if (BuildVars.LOGS_ENABLED) { FileLog.d("InstantCamera initied audio record with channels " + audioRecorder.getChannelCount() + " sample rate = " + audioRecorder.getSampleRate() + " bufferSize = " + bufferSize); } + pauseRecorder = false; Thread thread = new Thread(recorderRunnable); thread.setPriority(Thread.MAX_PRIORITY); thread.start(); @@ -2545,40 +2902,40 @@ private void prepareEncoder() { format.setInteger(MediaFormat.KEY_BIT_RATE, videoBitrate); format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL); - /*if (Build.VERSION.SDK_INT >= 21) { - format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh); - if (Build.VERSION.SDK_INT >= 23) { - format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel5); - } - }*/ + /*if (Build.VERSION.SDK_INT >= 21) { + format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh); + if (Build.VERSION.SDK_INT >= 23) { + format.setInteger(MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.AVCLevel5); + } + }*/ videoEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); surface = videoEncoder.createInputSurface(); videoEncoder.start(); - boolean isSdCard = ImageLoader.isSdCardPath(videoFile); - fileToWrite = videoFile; - if (isSdCard) { - try { - fileToWrite = new File(ApplicationLoader.getFilesDirFixed(), "camera_tmp.mp4"); - if (fileToWrite.exists()) { - fileToWrite.delete(); + if (!fromPause) { + boolean isSdCard = ImageLoader.isSdCardPath(videoFile); + fileToWrite = videoFile; + if (isSdCard) { + try { + fileToWrite = new File(ApplicationLoader.getFilesDirFixed(), "camera_tmp.mp4"); + if (fileToWrite.exists()) { + fileToWrite.delete(); + } + writingToDifferentFile = true; + } catch (Throwable e) { + FileLog.e(e); + fileToWrite = videoFile; + writingToDifferentFile = false; } - writingToDifferentFile = true; - } catch (Throwable e) { - FileLog.e(e); - fileToWrite = videoFile; - writingToDifferentFile = false; } + Mp4Movie movie = new Mp4Movie(); + movie.setCacheFile(fileToWrite); + movie.setRotation(0); + movie.setSize(videoWidth, videoHeight); + mediaMuxer = new MP4Builder().createMovie(movie, isSecretChat, false); + mediaMuxer.setAllowSyncFiles(allowSendingWhileRecording = SharedConfig.deviceIsHigh()); } - Mp4Movie movie = new Mp4Movie(); - movie.setCacheFile(fileToWrite); - movie.setRotation(0); - movie.setSize(videoWidth, videoHeight); - mediaMuxer = new MP4Builder().createMovie(movie, isSecretChat, false); - allowSendingWhileRecording = SharedConfig.deviceIsHigh(); - - mediaMuxer.setAllowSyncFiles(allowSendingWhileRecording); AndroidUtilities.runOnUIThread(() -> { if (cancelled) { @@ -2590,8 +2947,9 @@ private void prepareEncoder() { } AndroidUtilities.lockOrientation(delegate.getParentActivity()); - recording = true; + recordPlusTime = fromPause ? recordedTime : 0; recordStartTime = System.currentTimeMillis(); + recording = true; invalidate(); NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.recordStarted, recordingGuid, false); }); @@ -2714,7 +3072,7 @@ public void drainEncoder(boolean endOfStream) throws Exception { while (true) { int encoderStatus = videoEncoder.dequeueOutputBuffer(videoBufferInfo, 10000); if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { - if (!endOfStream) { + if (!endOfStream || pauseRecorder) { break; } } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { @@ -2832,7 +3190,7 @@ public void drainEncoder(boolean endOfStream) throws Exception { while (true) { int encoderStatus = audioEncoder.dequeueOutputBuffer(audioBufferInfo, 0); if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { - if (!endOfStream || !running && sendWhenDone == 0) { + if (!endOfStream || !running && sendWhenDone == ENCODER_SEND_CANCEL || pauseRecorder) { break; } } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java index 34e1b166e9..853df57b24 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java @@ -135,8 +135,8 @@ public boolean onFragmentCreate() { } if (this.sharedMediaPreloader == null) { this.sharedMediaPreloader = new SharedMediaLayout.SharedMediaPreloader(this); - this.sharedMediaPreloader.addDelegate(this); } + this.sharedMediaPreloader.addDelegate(this); return super.onFragmentCreate(); } @@ -537,6 +537,9 @@ public int getBottomOffset(int tag) { hideFloatingButton(true, false); } + if (type == TYPE_MEDIA && dialogId == getUserConfig().getClientUserId() && !getMessagesController().getSavedMessagesController().unsupported && getMessagesController().getSavedMessagesController().getAllCount() > 0) { + initialTab = SharedMediaLayout.TAB_SAVED_DIALOGS; + } sharedMediaLayout = new SharedMediaLayout(context, dialogId, sharedMediaPreloader, 0, null, currentChatInfo, currentUserInfo, initialTab, this, new SharedMediaLayout.Delegate() { @Override public void scrollToSharedMedia() { @@ -582,7 +585,7 @@ protected void onSelectedTabChanged() { @Override protected boolean canShowSearchItem() { - return false; + return type != TYPE_STORIES && type != TYPE_ARCHIVED_CHANNEL_STORIES; } @Override @@ -611,6 +614,11 @@ protected boolean includeStories() { return type == TYPE_STORIES || type == TYPE_ARCHIVED_CHANNEL_STORIES; } + @Override + protected boolean includeSavedDialogs() { + return type == TYPE_MEDIA && dialogId == getUserConfig().getClientUserId(); + } + @Override protected boolean isArchivedOnlyStoriesView() { return type == TYPE_ARCHIVED_CHANNEL_STORIES; @@ -937,6 +945,12 @@ private void updateMediaCount() { return; } + if (id == SharedMediaLayout.TAB_SAVED_DIALOGS) { + showSubtitle(i, true, true); + int count = getMessagesController().getSavedMessagesController().getAllCount(); + subtitleTextView[i].setText(LocaleController.formatPluralString("SavedDialogsTabCount", count), animated); + return; + } if (id < 0 || id < mediaCount.length && mediaCount[id] < 0) { return; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java index 8f637faf06..df84143da7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MentionsContainerView.java @@ -69,7 +69,7 @@ public class MentionsContainerView extends BlurredFrameLayout implements Notific private RecyclerListView.OnItemClickListener mentionsOnItemClickListener; private Delegate delegate; - public MentionsContainerView(@NonNull Context context, long dialogId, int threadMessageId, BaseFragment baseFragment, SizeNotifierFrameLayout container, Theme.ResourcesProvider resourcesProvider) { + public MentionsContainerView(@NonNull Context context, long dialogId, long threadMessageId, BaseFragment baseFragment, SizeNotifierFrameLayout container, Theme.ResourcesProvider resourcesProvider) { super(context, container); this.baseFragment = baseFragment; this.sizeNotifierFrameLayout = container; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/MessagePrivateSeenView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/MessagePrivateSeenView.java new file mode 100644 index 0000000000..6930c51036 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MessagePrivateSeenView.java @@ -0,0 +1,323 @@ +package org.telegram.ui.Components; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.ui.ActionBar.Theme.key_dialogGrayLine; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ContactsController; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.UserObject; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheet; +import org.telegram.ui.ActionBar.SimpleTextView; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.Premium.PremiumButtonView; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.PremiumPreviewFragment; +import org.telegram.ui.Stories.recorder.ButtonWithCounterView; +import org.telegram.ui.Stories.recorder.HintView2; + +public class MessagePrivateSeenView extends FrameLayout { + + private final int currentAccount; + private final Theme.ResourcesProvider resourcesProvider; + + private final LinearLayout valueLayout; + private final TextView valueTextView; + private final TextView premiumTextView; + private final TextView loadingView; + + private final long dialogId; + private final int messageId; + private final Runnable dismiss; + + public MessagePrivateSeenView(Context context, @NonNull MessageObject messageObject, Runnable dismiss, Theme.ResourcesProvider resourcesProvider) { + super(context); + + currentAccount = messageObject.currentAccount; + this.resourcesProvider = resourcesProvider; + this.dismiss = dismiss; + + dialogId = messageObject.getDialogId(); + messageId = messageObject.getId(); + + ImageView iconView = new ImageView(context); + addView(iconView, LayoutHelper.createFrame(24, 24, Gravity.LEFT | Gravity.CENTER_VERTICAL, 11, 0, 0, 0)); + Drawable drawable = ContextCompat.getDrawable(context, messageObject.isVoice() ? R.drawable.msg_played : R.drawable.msg_seen).mutate(); + drawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon, resourcesProvider), PorterDuff.Mode.MULTIPLY)); + iconView.setImageDrawable(drawable); + + loadingView = new TextView(context); + SpannableStringBuilder text = new SpannableStringBuilder("loading text "); + text.setSpan(new LoadingSpan(loadingView, dp(96), dp(2), resourcesProvider), 0, text.length() - 1, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + loadingView.setTextColor(Theme.multAlpha(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider), .7f)); + loadingView.setText(text); + loadingView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + addView(loadingView, LayoutHelper.createFrame(96, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 40, -1, 8, 0)); + + valueLayout = new LinearLayout(context); + valueLayout.setOrientation(LinearLayout.HORIZONTAL); + valueLayout.setAlpha(0f); + addView(valueLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 38, 0, 8, 0)); + + valueTextView = new TextView(context); + valueTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + valueTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + valueLayout.addView(valueTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 0, -1, 0, 0)); + + premiumTextView = new TextView(context); + premiumTextView.setBackground(Theme.createRoundRectDrawable(dp(20), Theme.multAlpha(Theme.getColor(Theme.key_divider, resourcesProvider), .75f))); + premiumTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + premiumTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 11); + premiumTextView.setPadding(dp(5.33f), dp(2), dp(5.33f), dp(2.33f)); + valueLayout.addView(premiumTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 4, 0, 0, 0)); + + request(); + } + + private void request() { + setOnClickListener(null); + valueLayout.setAlpha(0f); + loadingView.setAlpha(1f); + premiumTextView.setVisibility(View.VISIBLE); + + TLRPC.TL_messages_getOutboxReadDate req = new TLRPC.TL_messages_getOutboxReadDate(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); + req.msg_id = messageId; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (err != null) { + if ("USER_PRIVACY_RESTRICTED".equals(err.text)) { + valueTextView.setText(LocaleController.getString(R.string.PmReadUnknown)); + premiumTextView.setVisibility(View.GONE); + } else if ("YOUR_PRIVACY_RESTRICTED".equals(err.text)) { + isPremiumLocked = true; + valueTextView.setText(LocaleController.getString(R.string.PmRead)); + premiumTextView.setText(LocaleController.getString(R.string.PmReadShowWhen)); + } else { + valueTextView.setText(LocaleController.getString("UnknownError")); + premiumTextView.setVisibility(View.GONE); + BulletinFactory.of(Bulletin.BulletinWindow.make(getContext()), resourcesProvider).showForError(err); + } + } else if (res instanceof TLRPC.TL_outboxReadDate) { + TLRPC.TL_outboxReadDate r = (TLRPC.TL_outboxReadDate) res; + valueTextView.setText(LocaleController.formatString(R.string.PmReadAt, LocaleController.formatSeenDate(r.date))); + premiumTextView.setVisibility(View.GONE); + } + valueLayout.animate().alpha(1f).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).setDuration(320).start(); + loadingView.animate().alpha(0f).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).setDuration(320).start(); + + if (isPremiumLocked) { + setBackground(Theme.createRadSelectorDrawable(Theme.getColor(Theme.key_listSelector, resourcesProvider), 6, 0)); + setOnClickListener(v -> showSheet(getContext(), currentAccount, dialogId, false, dismiss, this::request, resourcesProvider)); + } else { + setBackground(null); + setOnClickListener(null); + } + })); + } + + public boolean isPremiumLocked = false; + + public static void showSheet(Context context, int currentAccount, long dialogId, boolean lastSeen, Runnable dismiss, Runnable updated, Theme.ResourcesProvider resourcesProvider) { + BottomSheet sheet = new BottomSheet(context, false, resourcesProvider); + sheet.fixNavigationBar(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + + final boolean premiumLocked = MessagesController.getInstance(currentAccount).premiumFeaturesBlocked(); + + LinearLayout layout = new LinearLayout(context); + layout.setOrientation(LinearLayout.VERTICAL); + layout.setPadding(dp(16), 0, dp(16), 0); + + RLottieImageView imageView = new RLottieImageView(context); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setAnimation(lastSeen ? R.raw.large_lastseen : R.raw.large_readtime, 70, 70); + imageView.playAnimation(); + imageView.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN)); + imageView.setBackground(Theme.createCircleDrawable(dp(80), Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider))); + layout.addView(imageView, LayoutHelper.createLinear(80, 80, Gravity.CENTER_HORIZONTAL, 0, 16, 0, 16)); + + TextView headerView = new TextView(context); + headerView.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + headerView.setGravity(Gravity.CENTER); + headerView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + headerView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + headerView.setText(LocaleController.getString(lastSeen ? R.string.PremiumLastSeenHeader1 : R.string.PremiumReadHeader1)); + layout.addView(headerView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 12, 0, 12, 0)); + + TextView descriptionView = new TextView(context); + descriptionView.setGravity(Gravity.CENTER); + descriptionView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + descriptionView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + String username = ""; + if (dialogId > 0) { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + username = UserObject.getFirstName(user); + } + descriptionView.setText(AndroidUtilities.replaceTags(LocaleController.formatString(lastSeen ? (premiumLocked ? R.string.PremiumLastSeenText1Locked : R.string.PremiumLastSeenText1) : (premiumLocked ? R.string.PremiumReadText1Locked : R.string.PremiumReadText1), username))); + layout.addView(descriptionView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 32, 9, 32, 19)); + + ButtonWithCounterView button1 = new ButtonWithCounterView(context, resourcesProvider); + button1.setText(LocaleController.getString(lastSeen ? R.string.PremiumLastSeenButton1 : R.string.PremiumReadButton1), false); + layout.addView(button1, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.CENTER_HORIZONTAL)); + button1.setOnClickListener(v -> { + button1.setLoading(true); + if (lastSeen) { + TLRPC.TL_account_setPrivacy req = new TLRPC.TL_account_setPrivacy(); + req.key = new TLRPC.TL_inputPrivacyKeyStatusTimestamp(); + req.rules.add(new TLRPC.TL_inputPrivacyValueAllowAll()); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (err != null) { + BulletinFactory.global().showForError(err); + return; + } + + button1.setLoading(false); + sheet.dismiss(); + + BulletinFactory.global().createSimpleBulletin(R.raw.chats_infotip, LocaleController.getString(R.string.PremiumLastSeenSet)).show(); + if (updated != null) { + updated.run(); + } + })); + } else { + TLRPC.TL_account_setGlobalPrivacySettings req = new TLRPC.TL_account_setGlobalPrivacySettings(); + req.settings = ContactsController.getInstance(currentAccount).getGlobalPrivacySettings(); + if (req.settings == null) { + req.settings = new TLRPC.TL_globalPrivacySettings(); + } + req.settings.hide_read_marks = false; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (err != null) { + BulletinFactory.of(Bulletin.BulletinWindow.make(context), resourcesProvider).showForError(err); + return; + } + + button1.setLoading(false); + sheet.dismiss(); + + BulletinFactory.of(Bulletin.BulletinWindow.make(context), resourcesProvider).createSimpleBulletin(R.raw.chats_infotip, LocaleController.getString(R.string.PremiumReadSet)).show(); + if (updated != null) { + updated.run(); + } + })); + } + }); + + if (!premiumLocked) { + SimpleTextView or = new SimpleTextView(context) { + private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + + @Override + protected void dispatchDraw(Canvas canvas) { + paint.setColor(Theme.getColor(Theme.key_dialogGrayLine, resourcesProvider)); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeWidth(1); + final float cy = getHeight() / 2f; + canvas.drawLine(0, cy, getWidth() / 2f - getTextWidth() / 2f - dp(8), cy, paint); + canvas.drawLine(getWidth() / 2f + getTextWidth() / 2f + dp(8), cy, getWidth(), cy, paint); + + super.dispatchDraw(canvas); + } + }; + or.setGravity(Gravity.CENTER); + or.setAlignment(Layout.Alignment.ALIGN_CENTER); + or.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); + or.setText(" " + LocaleController.getString(R.string.PremiumOr) + " "); + or.setTextSize(14); + layout.addView(or, LayoutHelper.createLinear(270, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 12, 17, 12, 17)); + + TextView headerView2 = new TextView(context); + headerView2.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); + headerView2.setGravity(Gravity.CENTER); + headerView2.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + headerView2.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + headerView2.setText(LocaleController.getString(lastSeen ? R.string.PremiumLastSeenHeader2 : R.string.PremiumReadHeader2)); + layout.addView(headerView2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 12, 0, 12, 0)); + + TextView descriptionView2 = new TextView(context); + descriptionView2.setGravity(Gravity.CENTER); + descriptionView2.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); + descriptionView2.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + descriptionView2.setText(AndroidUtilities.replaceTags(LocaleController.formatString(lastSeen ? R.string.PremiumLastSeenText2 : R.string.PremiumReadText2, username))); + layout.addView(descriptionView2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 32, 9, 32, 19)); + + PremiumButtonView button2 = new PremiumButtonView(context, true, resourcesProvider); + button2.setOnClickListener(v2 -> { + BaseFragment lastFragment = LaunchActivity.getLastFragment(); + if (lastFragment != null) { + lastFragment.presentFragment(new PremiumPreviewFragment(lastSeen ? "lastseen" : "readtime")); + sheet.dismiss(); + if (dismiss != null) { + dismiss.run(); + } + } + }); + button2.setOverlayText(LocaleController.getString(lastSeen ? R.string.PremiumLastSeenButton2 : R.string.PremiumReadButton2), false, false); + layout.addView(button2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.CENTER_HORIZONTAL, 0, 0, 0, 4)); + } + + sheet.setCustomView(layout); + sheet.show(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + Bulletin.hideVisible(); + } + + float minWidth = -1; + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + View parent = (View) getParent(); + int width = MeasureSpec.getSize(widthMeasureSpec); + int widthMode = MeasureSpec.getMode(widthMeasureSpec); + + if (minWidth < 0) { + minWidth = 0; + minWidth = Math.max(minWidth, dp(40 + 96 + 8)); + minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.getString(R.string.PmReadUnknown))); + minWidth = Math.max(minWidth, dp(40 + 16 + 8) + valueTextView.getPaint().measureText(LocaleController.getString(R.string.PmRead) + premiumTextView.getPaint().measureText(LocaleController.getString(R.string.PmReadShowWhen)))); + minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.formatString(R.string.PmReadAt, LocaleController.formatString(R.string.TodayAtFormattedWithToday, "99:99")))); + minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.formatString(R.string.PmReadAt, LocaleController.formatString(R.string.YesterdayAtFormatted, "99:99")))); + minWidth = Math.max(minWidth, dp(40 + 8) + valueTextView.getPaint().measureText(LocaleController.formatString(R.string.PmReadAt, LocaleController.formatString(R.string.formatDateAtTime, "99.99.99", "99:99")))); + } + + if (width < minWidth) { + width = (int) minWidth; + } + if (parent != null && parent.getWidth() > 0) { + width = parent.getWidth(); + widthMode = MeasureSpec.EXACTLY; + } + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LPhotoPaintView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LPhotoPaintView.java index 38aff838f8..f3a056e5e3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LPhotoPaintView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LPhotoPaintView.java @@ -2742,23 +2742,25 @@ private void showMenuForEntity(final EntityView entityView) { parent.addView(flipView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 48)); } - TextView duplicateView = new TextView(getContext()); - duplicateView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); - duplicateView.setBackgroundDrawable(Theme.getSelectorDrawable(false)); - duplicateView.setGravity(Gravity.CENTER_VERTICAL); - duplicateView.setEllipsize(TextUtils.TruncateAt.END); - duplicateView.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(16), 0); - duplicateView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - duplicateView.setTag(2); - duplicateView.setText(LocaleController.getString("PaintDuplicate", R.string.PaintDuplicate)); - duplicateView.setOnClickListener(v -> { - duplicateSelectedEntity(); + if (!(entityView instanceof PhotoView)) { + TextView duplicateView = new TextView(getContext()); + duplicateView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + duplicateView.setBackgroundDrawable(Theme.getSelectorDrawable(false)); + duplicateView.setGravity(Gravity.CENTER_VERTICAL); + duplicateView.setEllipsize(TextUtils.TruncateAt.END); + duplicateView.setPadding(AndroidUtilities.dp(14), 0, AndroidUtilities.dp(16), 0); + duplicateView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + duplicateView.setTag(2); + duplicateView.setText(LocaleController.getString("PaintDuplicate", R.string.PaintDuplicate)); + duplicateView.setOnClickListener(v -> { + duplicateSelectedEntity(); - if (popupWindow != null && popupWindow.isShowing()) { - popupWindow.dismiss(true); - } - }); - parent.addView(duplicateView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 48)); + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(true); + } + }); + parent.addView(duplicateView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 48)); + } if (entityView instanceof PhotoView && ((PhotoView) entityView).hasSegmentedImage()) { PhotoView photoView = (PhotoView) entityView; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/MessageEntityView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/MessageEntityView.java index e357f7460e..d17b18c29d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/MessageEntityView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/MessageEntityView.java @@ -97,7 +97,7 @@ public MessageEntityView(Context context, Point position, float angle, float sca messageOwner.fwd_from = null; } messageOwner.voiceTranscriptionOpen = false; - MessageObject newMsg = new MessageObject(msg.currentAccount, messageOwner, msg.replyMessageObject, MessagesController.getInstance(msg.currentAccount).getUsers(), MessagesController.getInstance(msg.currentAccount).getChats(), null, null, true, true, 0, true, isRepostVideoPreview); + MessageObject newMsg = new MessageObject(msg.currentAccount, messageOwner, msg.replyMessageObject, MessagesController.getInstance(msg.currentAccount).getUsers(), MessagesController.getInstance(msg.currentAccount).getChats(), null, null, true, true, 0, true, isRepostVideoPreview, false); messageObjects.add(newMsg); } // dateCell = new ChatActionCell(context, false, resourcesProvider) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhonebookShareAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhonebookShareAlert.java index bb7edab91d..3f4405419b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PhonebookShareAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PhonebookShareAlert.java @@ -638,8 +638,8 @@ public void onItemClick(int id) { buttonTextView.setText(LocaleController.getString("ShareContactTitle", R.string.ShareContactTitle)); } buttonTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); - buttonTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(4), getThemedColor(Theme.key_featuredStickers_addButton), getThemedColor(Theme.key_featuredStickers_addButtonPressed))); - frameLayout.addView(buttonTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 42, Gravity.LEFT | Gravity.BOTTOM, 16, 16, 16, 16)); + buttonTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), getThemedColor(Theme.key_featuredStickers_addButton), getThemedColor(Theme.key_featuredStickers_addButtonPressed))); + frameLayout.addView(buttonTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.BOTTOM, 14, 14, 14, 14)); buttonTextView.setOnClickListener(v -> { if (isImport) { AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PipRoundVideoView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PipRoundVideoView.java index 7d6403951b..5ddb693e6e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PipRoundVideoView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PipRoundVideoView.java @@ -77,13 +77,19 @@ public class PipRoundVideoView implements NotificationCenter.NotificationCenterD @SuppressLint("StaticFieldLeak") private static PipRoundVideoView instance; + public class PipFrameLayout extends FrameLayout { + public PipFrameLayout(Context context) { + super(context); + } + } + public void show(Activity activity, Runnable closeRunnable) { if (activity == null) { return; } instance = this; onCloseRunnable = closeRunnable; - windowView = new FrameLayout(activity) { + windowView = new PipFrameLayout(activity) { private float startX; private float startY; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java index d127a32629..2e55a79a99 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PipVideoOverlay.java @@ -967,7 +967,7 @@ protected void onSizeChanged(int w, int h, int oldw, int oldh) { path.addRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(ROUNDED_CORNERS_DP), AndroidUtilities.dp(ROUNDED_CORNERS_DP), Path.Direction.CW); } }; - contentView = new ViewGroup(context) { + contentView = new PipVideoViewGroup(context) { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { contentFrameLayout.layout(0, 0, pipWidth, pipHeight); @@ -1240,4 +1240,15 @@ private float getPipY() { return mPrefs.getFloat("y", -1); } } + + public static class PipVideoViewGroup extends ViewGroup { + public PipVideoViewGroup(Context context) { + super(context); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + + } + } } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java index a570f423f4..f30ecf63c1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java @@ -268,7 +268,7 @@ public class UserCell extends FrameLayout { private TLRPC.User currentUser; private TLRPC.Chat currentChat; - private String lastName; + private CharSequence lastName; private int lastStatus; private TLRPC.FileLocation lastAvatar; @@ -296,9 +296,10 @@ public UserCell(Context context) { nameTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); nameTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); nameTextView.setTextSize(16); - nameTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); - addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 20, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 28 : 65, 14, LocaleController.isRTL ? 65 : 28, 0)); - statusBadgeComponent = new StatusBadgeComponent(nameTextView); + nameTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); + addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 24, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 28 : 65, 12, LocaleController.isRTL ? 65 : 28, 0)); + + statusBadgeComponent = new StatusBadgeComponent(nameTextView, 20); } public void setData(TLObject object, int num, boolean divider) { @@ -390,7 +391,7 @@ public void update(int mask) { } else if (currentChat != null) { newName = currentChat.title; } - if (!newName.equals(lastName)) { + if (!TextUtils.equals(newName, lastName)) { continueUpdate = true; } } @@ -412,13 +413,15 @@ public void update(int mask) { if (currentUser != null) { lastName = newName == null ? UserObject.getUserName(currentUser) : newName; + lastName = Emoji.replaceEmoji(lastName, nameTextView.getPaint().getFontMetricsInt(), false); } else if (currentChat != null) { lastName = currentChat.title; + lastName = Emoji.replaceEmoji(lastName, nameTextView.getPaint().getFontMetricsInt(), false); } else { lastName = ""; } nameTextView.setText(lastName); - nameTextView.setRightDrawable(statusBadgeComponent.updateDrawable(currentUser, currentChat, Theme.getColor(Theme.key_chats_verifiedBackground), false)); + nameTextView.setRightDrawable(statusBadgeComponent.updateDrawable(currentUser, currentChat, Theme.getColor(Theme.key_chats_verifiedBackground, resourcesProvider), false)); lastAvatar = photo; if (currentChat != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitReachedBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitReachedBottomSheet.java index 573dbce89b..05072ce5be 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitReachedBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/LimitReachedBottomSheet.java @@ -130,6 +130,8 @@ public class LimitReachedBottomSheet extends BottomSheetWithRecyclerListView imp public static final int TYPE_BOOSTS_FOR_REPLY_ICON = 26; public static final int TYPE_BOOSTS_FOR_PROFILE_ICON = 27; + public static final int TYPE_PIN_SAVED_DIALOGS = 28; + private boolean canSendLink; private int linkRow = -1; private long dialogId; @@ -280,7 +282,7 @@ public BoostFeatureLevel(int lvl, boolean isFirst) { private int requiredLvl = 0; public LimitReachedBottomSheet(BaseFragment fragment, Context context, int type, int currentAccount, Theme.ResourcesProvider resourcesProvider) { - super(fragment, false, hasFixedSize(type), false, resourcesProvider); + super(context, fragment, false, hasFixedSize(type), false, resourcesProvider); fixNavigationBar(Theme.getColor(Theme.key_dialogBackground, this.resourcesProvider)); this.parentFragment = fragment; this.currentAccount = currentAccount; @@ -826,6 +828,7 @@ private void updateButton() { private static boolean hasFixedSize(int type) { return ( type == TYPE_PIN_DIALOGS || + type == TYPE_PIN_SAVED_DIALOGS || type == TYPE_FOLDERS || type == TYPE_CHATS_IN_FOLDER || type == TYPE_LARGE_FILE || @@ -1358,8 +1361,7 @@ public HeaderView(Context context) { currentValue = MessagesController.getInstance(currentAccount).dialogFilters.size() - 1; } else if (type == TYPE_ACCOUNTS) { currentValue = UserConfig.getActivatedAccountsCount(); - } - if (type == TYPE_PIN_DIALOGS) { + } else if (type == TYPE_PIN_DIALOGS) { int pinnedCount = 0; ArrayList dialogs = MessagesController.getInstance(currentAccount).getDialogs(0); for (int a = 0, N = dialogs.size(); a < N; a++) { @@ -1685,6 +1687,13 @@ private static LimitParams getLimitParams(int type, int currentAccount) { limitParams.descriptionStr = LocaleController.formatString("LimitReachedPinDialogs", R.string.LimitReachedPinDialogs, limitParams.defaultLimit, limitParams.premiumLimit); limitParams.descriptionStrPremium = LocaleController.formatString("LimitReachedPinDialogsPremium", R.string.LimitReachedPinDialogsPremium, limitParams.premiumLimit); limitParams.descriptionStrLocked = LocaleController.formatString("LimitReachedPinDialogsLocked", R.string.LimitReachedPinDialogsLocked, limitParams.defaultLimit); + } else if (type == TYPE_PIN_SAVED_DIALOGS) { + limitParams.defaultLimit = MessagesController.getInstance(currentAccount).savedDialogsPinnedLimitDefault; + limitParams.premiumLimit = MessagesController.getInstance(currentAccount).savedDialogsPinnedLimitPremium; + limitParams.icon = R.drawable.msg_limit_pin; + limitParams.descriptionStr = LocaleController.formatString(R.string.LimitReachedPinSavedDialogs, limitParams.defaultLimit, limitParams.premiumLimit); + limitParams.descriptionStrPremium = LocaleController.formatString(R.string.LimitReachedPinSavedDialogsPremium, limitParams.premiumLimit); + limitParams.descriptionStrLocked = LocaleController.formatString(R.string.LimitReachedPinSavedDialogsLocked, limitParams.defaultLimit); } else if (type == TYPE_PUBLIC_LINKS) { limitParams.defaultLimit = MessagesController.getInstance(currentAccount).publicLinksLimitDefault; limitParams.premiumLimit = MessagesController.getInstance(currentAccount).publicLinksLimitPremium; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/UserSelectorBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/UserSelectorBottomSheet.java index aa55dd99a8..6ecddba604 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/UserSelectorBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/UserSelectorBottomSheet.java @@ -417,7 +417,7 @@ private void updateCheckboxes(boolean animated) { View child = recyclerListView.getChildAt(i); if (child instanceof SelectorUserCell) { int position = recyclerListView.getChildAdapterPosition(child); - if (position <= 0) { + if (position - 1 < 0 || position - 1 >= items.size()) { continue; } if (visibleItemsFrom == -1) { 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 68d1f2347e..90246cc147 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RLottieDrawable.java @@ -1191,6 +1191,9 @@ public void draw(Canvas canvas, Paint paint) { drawInternal(canvas, paint, false, 0, 0); } + public boolean scaleByCanvas; + public Rect srcRect = new Rect(); + public void drawInternal(Canvas canvas, Paint overridePaint, boolean drawInBackground, long time, int threadIndex) { if (!canLoadFrames() || destroyWhenDone) { return; @@ -1228,11 +1231,17 @@ public void drawInternal(Canvas canvas, Paint overridePaint, boolean drawInBackg if (!needScale) { canvas.drawBitmap(renderingBitmap, rect.left, rect.top, paint); } else { - canvas.save(); - canvas.translate(rect.left, rect.top); - canvas.scale(scaleX, scaleY); - canvas.drawBitmap(renderingBitmap, 0, 0, paint); - canvas.restore(); + if (scaleByCanvas) { + // save-restore breaks cutting with xfer + srcRect.set(0, 0, renderingBitmap.getWidth(), renderingBitmap.getHeight()); + canvas.drawBitmap(renderingBitmap, srcRect, rect, paint); + } else { + canvas.save(); + canvas.translate(rect.left, rect.top); + canvas.scale(scaleX, scaleY); + canvas.drawBitmap(renderingBitmap, 0, 0, paint); + canvas.restore(); + } } if (isRunning && !drawInBackground) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadioButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadioButton.java index f546369820..850c9a0eb9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RadioButton.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RadioButton.java @@ -15,8 +15,11 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffXfermode; import androidx.annotation.Keep; + +import android.graphics.drawable.Drawable; import android.view.View; import org.telegram.messenger.AndroidUtilities; @@ -81,6 +84,15 @@ public void setSize(int value) { size = value; } + private Drawable icon; + public void setIcon(Drawable drawable) { + icon = drawable; + if (icon != null) { + icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); + } + invalidate(); + } + public int getColor() { return color; } @@ -88,11 +100,17 @@ public int getColor() { public void setColor(int color1, int color2) { color = color1; checkedColor = color2; + if (icon != null) { + icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); + } invalidate(); } public void setBackgroundColor(int color1) { color = color1; + if (icon != null) { + icon.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); + } invalidate(); } @@ -175,6 +193,15 @@ protected void onDraw(Canvas canvas) { paint.setColor(c); checkedPaint.setColor(c); } + if (icon != null) { + icon.setBounds( + (int) (getWidth() / 2f - icon.getIntrinsicWidth() / 2f), + (int) (getHeight() / 2f - icon.getIntrinsicHeight() / 2f), + (int) (getWidth() / 2f + icon.getIntrinsicWidth() / 2f), + (int) (getHeight() / 2f + icon.getIntrinsicHeight() / 2f) + ); + icon.draw(canvas); + } if (bitmap != null) { bitmap.eraseColor(0); float rad = size / 2 - (1 + circleProgress) * AndroidUtilities.density; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsLayoutInBubble.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsLayoutInBubble.java index f20c5ecd70..3c6bfd6eef 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsLayoutInBubble.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionsLayoutInBubble.java @@ -1,15 +1,22 @@ package org.telegram.ui.Components.Reactions; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; + import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.RectF; import android.text.TextPaint; import android.text.TextUtils; import android.view.Gravity; import android.view.MotionEvent; +import android.view.View; import android.view.ViewConfiguration; import androidx.core.graphics.ColorUtils; @@ -48,6 +55,8 @@ public class ReactionsLayoutInBubble { public float drawServiceShaderBackground; private static Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); + private static Paint tagPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private static Paint cutTagPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private static TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); public boolean isSmall; @@ -104,14 +113,16 @@ private static long getPeerId(TLObject object) { return 0; } + public boolean tags; public ReactionsLayoutInBubble(ChatMessageCell parentView) { this.parentView = parentView; currentAccount = UserConfig.selectedAccount; paint.setColor(Theme.getColor(Theme.key_chat_inLoader, resourcesProvider)); textPaint.setColor(Theme.getColor(Theme.key_featuredStickers_buttonText, resourcesProvider)); - textPaint.setTextSize(AndroidUtilities.dp(12)); + textPaint.setTextSize(dp(12)); textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); touchSlop = ViewConfiguration.get(ApplicationLoader.applicationContext).getScaledTouchSlop(); + cutTagPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); } public static boolean equalsTLReaction(TLRPC.Reaction reaction, TLRPC.Reaction reaction1) { @@ -148,7 +159,7 @@ public void setMessage(MessageObject messageObject, boolean isSmall, Theme.Resou // break; // } // } - ReactionButton button = new ReactionButton(old, reactionCount, isSmall); + ReactionButton button = new ReactionLayoutButton(old, reactionCount, isSmall); reactionButtons.add(button); if (!isSmall && messageObject.messageOwner.reactions.recent_reactions != null) { ArrayList users = null; @@ -201,8 +212,8 @@ public void setMessage(MessageObject messageObject, boolean isSmall, Theme.Resou } } if (isSmall && reactionCount.count > 1 && reactionCount.chosen) { - // TODO: also reuse here - reactionButtons.add(new ReactionButton(null, reactionCount, isSmall)); + ReactionButton button2 = new ReactionLayoutButton(null, reactionCount, isSmall); + reactionButtons.add(button2); reactionButtons.get(0).isSelected = false; reactionButtons.get(1).isSelected = true; reactionButtons.get(0).realCount = 1; @@ -248,29 +259,29 @@ public void measure(int availableWidth, int gravity) { for (int i = 0; i < reactionButtons.size(); i++) { ReactionButton button = reactionButtons.get(i); if (button.isSmall) { - button.width = AndroidUtilities.dp(14); - button.height = AndroidUtilities.dp(14); + button.width = dp(14); + button.height = dp(14); } else { - button.width = (int) (AndroidUtilities.dp(8) + AndroidUtilities.dp(20) + AndroidUtilities.dp(4)); + button.width = (int) (dp(8) + dp(20) + dp(4)); if (button.avatarsDrawable != null && button.users.size() > 0) { button.users.size(); int c1 = 1; int c2 = button.users.size() > 1 ? button.users.size() - 1 : 0; - button.width += AndroidUtilities.dp(2) + c1 * AndroidUtilities.dp(20) + c2 * AndroidUtilities.dp(20) * 0.8f + AndroidUtilities.dp(1); - button.avatarsDrawable.height = AndroidUtilities.dp(26); + button.width += dp(2) + c1 * dp(20) + c2 * dp(20) * 0.8f + dp(1); + button.avatarsDrawable.height = dp(26); } else { - button.width += button.counterDrawable.textPaint.measureText(button.countText) + AndroidUtilities.dp(8); + button.width += button.counterDrawable.textPaint.measureText(button.countText) + dp(8); } - button.height = AndroidUtilities.dp(26); + button.height = dp(26); } if (currentX + button.width > availableWidth) { currentX = 0; - currentY += button.height + AndroidUtilities.dp(4); + currentY += button.height + dp(4); } button.x = currentX; button.y = currentY; - currentX += button.width + AndroidUtilities.dp(4); + currentX += button.width + dp(4); if (currentX > maxWidth) { maxWidth = currentX; } @@ -299,7 +310,7 @@ public void measure(int availableWidth, int gravity) { } else { width = maxWidth; } - height = currentY + (reactionButtons.size() == 0 ? 0 : AndroidUtilities.dp(26)); + height = currentY + (reactionButtons.size() == 0 ? 0 : dp(26)); drawServiceShaderBackground = 0f; } @@ -388,7 +399,7 @@ public boolean animateChange() { button.fromBackgroundColor = lastButton.lastDrawnBackgroundColor; button.animationType = ANIMATION_TYPE_MOVE; - if (button.count != lastButton.count) { + if (button.count != lastButton.count && button.counterDrawable != null) { button.counterDrawable.setCount(lastButton.count, false); button.counterDrawable.setCount(button.count, true); } @@ -399,7 +410,7 @@ public boolean animateChange() { if (lastButton.avatarsDrawable == null) { lastButton.setUsers(new ArrayList<>()); } - if (!equalsUsersList(lastButton.users, button.users)) { + if (!equalsUsersList(lastButton.users, button.users) && button.avatarsDrawable != null) { button.avatarsDrawable.animateFromState(lastButton.avatarsDrawable, currentAccount, false); } } @@ -485,7 +496,43 @@ public void setScrimReaction(String scrimViewReaction) { this.scrimViewReaction = scrimViewReaction; } - public class ReactionButton { + public class ReactionLayoutButton extends ReactionButton { + public ReactionLayoutButton(ReactionButton reuseFrom, TLRPC.ReactionCount reactionCount, boolean isSmall) { + super(reuseFrom, currentAccount, parentView, reactionCount, isSmall, resourcesProvider); + } + + @Override + protected boolean isPlaying() { + return ReactionsEffectOverlay.isPlaying(messageObject.getId(), messageObject.getGroupId(), visibleReaction); + } + + @Override + protected boolean isOutOwner() { + return messageObject.isOutOwner(); + } + + @Override + protected float getDrawServiceShaderBackground() { + return drawServiceShaderBackground; + } + + @Override + protected boolean supportsImageReceiverCache() { + return true; + } + + @Override + protected ImageReceiver getImageReceiver() { + return animatedReactions.get(visibleReaction); + } + + @Override + protected void removeImageReceiver() { + animatedReactions.remove(visibleReaction); + } + } + + public static class ReactionButton { private final TLRPC.ReactionCount reactionCount; private final boolean isSmall; @@ -503,12 +550,12 @@ public class ReactionButton { public String key; public boolean choosen; - String countText; + public String countText; TLRPC.Reaction reaction; VisibleReaction visibleReaction; android.graphics.Rect drawingImageRect = new Rect(); - int count; + public int count; public int x; public int y; public int width; @@ -516,19 +563,31 @@ public class ReactionButton { ImageReceiver imageReceiver; AnimatedEmojiDrawable animatedEmojiDrawable; int animatedEmojiDrawableColor; - CounterView.CounterDrawable counterDrawable; + public CounterView.CounterDrawable counterDrawable; int backgroundColor; int textColor; int serviceBackgroundColor; int serviceTextColor; - int lastDrawnTextColor; - int lastDrawnBackgroundColor; + public int lastDrawnTextColor; + public int lastDrawnBackgroundColor; boolean isSelected; + AvatarsDrawable avatarsDrawable; ArrayList users; - public ReactionButton(ReactionButton reuseFrom, TLRPC.ReactionCount reactionCount, boolean isSmall) { + private final int currentAccount; + private final View parentView; + private final Theme.ResourcesProvider resourcesProvider; + + protected int getCacheType() { + return AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW; + } + + public ReactionButton(ReactionButton reuseFrom, int currentAccount, View parentView, TLRPC.ReactionCount reactionCount, boolean isSmall, Theme.ResourcesProvider resourcesProvider) { + this.currentAccount = currentAccount; + this.parentView = parentView; + this.resourcesProvider = resourcesProvider; if (reuseFrom != null) { counterDrawable = reuseFrom.counterDrawable; } @@ -568,23 +627,31 @@ public ReactionButton(ReactionButton reuseFrom, TLRPC.ReactionCount reactionCoun imageReceiver.setImage(ImageLocation.getForDocument(r.center_icon), "40_40_lastreactframe", svgThumb, "webp", r, 1); } } else if (visibleReaction.documentId != 0) { - animatedEmojiDrawable = new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW, currentAccount, visibleReaction.documentId); + animatedEmojiDrawable = new AnimatedEmojiDrawable(getCacheType(), currentAccount, visibleReaction.documentId); } } - counterDrawable.setSize(AndroidUtilities.dp(26), AndroidUtilities.dp(100)); + counterDrawable.setSize(dp(26), dp(100)); counterDrawable.textPaint = textPaint; counterDrawable.setCount(count, false); counterDrawable.setType(CounterView.CounterDrawable.TYPE_CHAT_REACTIONS); counterDrawable.gravity = Gravity.LEFT; } + protected boolean isOutOwner() { + return false; + } + + protected boolean drawCounter() { + return count != 0 || counterDrawable.countChangeProgress != 1f; + } + public void draw(Canvas canvas, float x, float y, float progress, float alpha, boolean drawOverlayScrim) { wasDrawn = true; ImageReceiver imageReceiver = animatedEmojiDrawable != null ? animatedEmojiDrawable.getImageReceiver() : this.imageReceiver; if (isSmall && imageReceiver != null) { imageReceiver.setAlpha(alpha); - drawingImageRect.set((int) x, (int) y, AndroidUtilities.dp(14), AndroidUtilities.dp(14)); + drawingImageRect.set((int) x, (int) y, dp(14), dp(14)); imageReceiver.setImageCoords(drawingImageRect); imageReceiver.setRoundRadius(0); drawImage(canvas, alpha); @@ -592,13 +659,13 @@ public void draw(Canvas canvas, float x, float y, float progress, float alpha, b } if (choosen) { - backgroundColor = Theme.getColor(messageObject.isOutOwner() ? Theme.key_chat_outReactionButtonBackground : Theme.key_chat_inReactionButtonBackground, resourcesProvider); - textColor = Theme.getColor(messageObject.isOutOwner() ? Theme.key_chat_outReactionButtonTextSelected : Theme.key_chat_inReactionButtonTextSelected, resourcesProvider); - serviceTextColor = Theme.getColor(messageObject.isOutOwner() ? Theme.key_chat_outReactionButtonBackground : Theme.key_chat_inReactionButtonBackground, resourcesProvider); - serviceBackgroundColor = Theme.getColor(messageObject.isOutOwner() ? Theme.key_chat_outBubble : Theme.key_chat_inBubble); + backgroundColor = Theme.getColor(isOutOwner() ? Theme.key_chat_outReactionButtonBackground : Theme.key_chat_inReactionButtonBackground, resourcesProvider); + textColor = Theme.getColor(isOutOwner() ? Theme.key_chat_outReactionButtonTextSelected : Theme.key_chat_inReactionButtonTextSelected, resourcesProvider); + serviceTextColor = Theme.getColor(isOutOwner() ? Theme.key_chat_outReactionButtonBackground : Theme.key_chat_inReactionButtonBackground, resourcesProvider); + serviceBackgroundColor = Theme.getColor(isOutOwner() ? Theme.key_chat_outBubble : Theme.key_chat_inBubble); } else { - textColor = Theme.getColor(messageObject.isOutOwner() ? Theme.key_chat_outReactionButtonText : Theme.key_chat_inReactionButtonText, resourcesProvider); - backgroundColor = Theme.getColor(messageObject.isOutOwner() ? Theme.key_chat_outReactionButtonBackground : Theme.key_chat_inReactionButtonBackground, resourcesProvider); + textColor = Theme.getColor(isOutOwner() ? Theme.key_chat_outReactionButtonText : Theme.key_chat_inReactionButtonText, resourcesProvider); + backgroundColor = Theme.getColor(isOutOwner() ? Theme.key_chat_outReactionButtonBackground : Theme.key_chat_inReactionButtonBackground, resourcesProvider); backgroundColor = ColorUtils.setAlphaComponent(backgroundColor, (int) (Color.alpha(backgroundColor) * 0.156f)); serviceTextColor = Theme.getColor(Theme.key_chat_serviceText, resourcesProvider); serviceBackgroundColor = Color.TRANSPARENT; @@ -607,7 +674,6 @@ public void draw(Canvas canvas, float x, float y, float progress, float alpha, b textPaint.setColor(lastDrawnTextColor); paint.setColor(lastDrawnBackgroundColor); - if (alpha != 1f) { textPaint.setAlpha((int) (textPaint.getAlpha() * alpha)); paint.setAlpha((int) (paint.getAlpha() * alpha)); @@ -622,22 +688,22 @@ public void draw(Canvas canvas, float x, float y, float progress, float alpha, b } AndroidUtilities.rectTmp.set(x, y, x + w, y + height); float rad = height / 2f; - if (drawServiceShaderBackground > 0) { - Paint paint1 = getThemedPaint(Theme.key_paint_chatActionBackground); - Paint paint2 = getThemedPaint(Theme.key_paint_chatActionBackgroundDarken); + if (getDrawServiceShaderBackground() > 0) { + Paint paint1 = Theme.getThemePaint(Theme.key_paint_chatActionBackground, resourcesProvider); + Paint paint2 = Theme.getThemePaint(Theme.key_paint_chatActionBackgroundDarken, resourcesProvider); int oldAlpha = paint1.getAlpha(); int oldAlpha2 = paint2.getAlpha(); - paint1.setAlpha((int) (oldAlpha * alpha * drawServiceShaderBackground)); - paint2.setAlpha((int) (oldAlpha2 * alpha * drawServiceShaderBackground)); + paint1.setAlpha((int) (oldAlpha * alpha * getDrawServiceShaderBackground())); + paint2.setAlpha((int) (oldAlpha2 * alpha * getDrawServiceShaderBackground())); canvas.drawRoundRect(AndroidUtilities.rectTmp, rad, rad, paint1); - if (hasGradientService()) { + if (resourcesProvider != null ? resourcesProvider.hasGradientService() : Theme.hasGradientService()) { canvas.drawRoundRect(AndroidUtilities.rectTmp, rad, rad, paint2); } paint1.setAlpha(oldAlpha); paint2.setAlpha(oldAlpha2); } - if (drawServiceShaderBackground < 1 && drawOverlayScrim) { - Theme.MessageDrawable messageBackground = parentView.getCurrentBackgroundDrawable(false); + if (drawOverlayScrim && getDrawServiceShaderBackground() < 1 && parentView instanceof ChatMessageCell) { + Theme.MessageDrawable messageBackground = ((ChatMessageCell) parentView).getCurrentBackgroundDrawable(false); if (messageBackground != null) { canvas.drawRoundRect(AndroidUtilities.rectTmp, rad, rad, messageBackground.getPaint()); } @@ -647,12 +713,12 @@ public void draw(Canvas canvas, float x, float y, float progress, float alpha, b if (imageReceiver != null) { int size, X; if (animatedEmojiDrawable != null) { - size = AndroidUtilities.dp(24); - X = AndroidUtilities.dp(6); - imageReceiver.setRoundRadius(AndroidUtilities.dp(6)); + size = dp(24); + X = dp(6); + imageReceiver.setRoundRadius(dp(6)); } else { - size = AndroidUtilities.dp(20); - X = AndroidUtilities.dp(8); + size = dp(20); + X = dp(8); imageReceiver.setRoundRadius(0); } int Y = (int) ((height - size) / 2f); @@ -661,16 +727,16 @@ public void draw(Canvas canvas, float x, float y, float progress, float alpha, b drawImage(canvas, alpha); } - if (counterDrawable != null && (count != 0 || counterDrawable.countChangeProgress != 1f)) { + if (counterDrawable != null && drawCounter()) { canvas.save(); - canvas.translate(x + AndroidUtilities.dp(8) + AndroidUtilities.dp(20) + AndroidUtilities.dp(2), y); + canvas.translate(x + dp(8) + dp(20) + dp(2), y); counterDrawable.draw(canvas); canvas.restore(); } if (avatarsDrawable != null) { canvas.save(); - canvas.translate(x + AndroidUtilities.dp(10) + AndroidUtilities.dp(20) + AndroidUtilities.dp(2), y); + canvas.translate(x + dp(10) + dp(20) + dp(2), y); avatarsDrawable.setAlpha(alpha); avatarsDrawable.setTransitionProgress(progress); avatarsDrawable.onDraw(canvas); @@ -678,9 +744,33 @@ public void draw(Canvas canvas, float x, float y, float progress, float alpha, b } } - private void updateColors(float progress) { - lastDrawnTextColor = ColorUtils.blendARGB(fromTextColor, ColorUtils.blendARGB(textColor, serviceTextColor, drawServiceShaderBackground), progress); - lastDrawnBackgroundColor = ColorUtils.blendARGB(fromBackgroundColor, ColorUtils.blendARGB(backgroundColor, serviceBackgroundColor, drawServiceShaderBackground), progress); + protected void updateColors(float progress) { + lastDrawnTextColor = ColorUtils.blendARGB(fromTextColor, ColorUtils.blendARGB(textColor, serviceTextColor, getDrawServiceShaderBackground()), progress); + lastDrawnBackgroundColor = ColorUtils.blendARGB(fromBackgroundColor, ColorUtils.blendARGB(backgroundColor, serviceBackgroundColor, getDrawServiceShaderBackground()), progress); + } + + protected boolean isPlaying() { + return false; + } + + protected ImageReceiver getImageReceiver() { + return null; + } + + protected void removeImageReceiver() { + + } + + protected boolean supportsImageReceiverCache() { + return false; + } + + protected float getDrawServiceShaderBackground() { + return 0; + } + + protected boolean drawTagDot() { + return true; } private void drawImage(Canvas canvas, float alpha) { @@ -688,8 +778,8 @@ private void drawImage(Canvas canvas, float alpha) { if (animatedEmojiDrawable != null && animatedEmojiDrawableColor != lastDrawnTextColor) { animatedEmojiDrawable.setColorFilter(new PorterDuffColorFilter(animatedEmojiDrawableColor = lastDrawnTextColor, PorterDuff.Mode.SRC_IN)); } - if (drawImage && ((realCount > 1 || !ReactionsEffectOverlay.isPlaying(messageObject.getId(), messageObject.getGroupId(), visibleReaction)) || !isSelected)) { - ImageReceiver imageReceiver2 = animatedReactions.get(visibleReaction); + if (drawImage && (realCount > 1 || !isPlaying() || !isSelected)) { + ImageReceiver imageReceiver2 = getImageReceiver(); boolean drawStaticImage = true; if (imageReceiver2 != null) { if (imageReceiver2.getLottieAnimation() != null && imageReceiver2.getLottieAnimation().hasBitmap()) { @@ -699,7 +789,7 @@ private void drawImage(Canvas canvas, float alpha) { imageReceiver2.setAlpha(alpha); if (alpha <= 0) { imageReceiver2.onDetachedFromWindow(); - animatedReactions.remove(visibleReaction); + removeImageReceiver(); } } else { if (imageReceiver2.getLottieAnimation() != null && !imageReceiver2.getLottieAnimation().isRunning()) { @@ -707,7 +797,7 @@ private void drawImage(Canvas canvas, float alpha) { float alpha1 = imageReceiver2.getAlpha() - 16f / 200; if (alpha1 <= 0) { imageReceiver2.onDetachedFromWindow(); - animatedReactions.remove(visibleReaction); + removeImageReceiver(); } else { imageReceiver2.setAlpha(alpha1); } @@ -736,10 +826,10 @@ public void setUsers(ArrayList users) { avatarsDrawable = new AvatarsDrawable(parentView, false); avatarsDrawable.transitionDuration = ChatListItemAnimator.DEFAULT_DURATION; avatarsDrawable.transitionInterpolator = ChatListItemAnimator.DEFAULT_INTERPOLATOR; - avatarsDrawable.setSize(AndroidUtilities.dp(20)); - avatarsDrawable.width = AndroidUtilities.dp(100); + avatarsDrawable.setSize(dp(20)); + avatarsDrawable.width = dp(100); avatarsDrawable.height = height; - avatarsDrawable.setAvatarsTextSize(AndroidUtilities.dp(22)); + avatarsDrawable.setAvatarsTextSize(dp(22)); } if (attached) { avatarsDrawable.onAttachedToWindow(); @@ -754,7 +844,9 @@ public void setUsers(ArrayList users) { } } + public boolean attached; public void attach() { + attached = true; if (imageReceiver != null) { imageReceiver.onAttachedToWindow(); } @@ -767,6 +859,7 @@ public void attach() { } public void detach() { + attached = false; if (imageReceiver != null) { imageReceiver.onDetachedFromWindow(); } @@ -947,6 +1040,17 @@ public static VisibleReaction fromTLReaction(TLRPC.Reaction reaction) { return visibleReaction; } + public TLRPC.Reaction toTLReaction() { + if (emojicon != null) { + TLRPC.TL_reactionEmoji r = new TLRPC.TL_reactionEmoji(); + r.emoticon = emojicon; + return r; + } + TLRPC.TL_reactionCustomEmoji r = new TLRPC.TL_reactionCustomEmoji(); + r.document_id = documentId; + return r; + } + public static VisibleReaction fromEmojicon(TLRPC.TL_availableReaction reaction) { VisibleReaction visibleReaction = new VisibleReaction(); visibleReaction.emojicon = reaction.reaction; @@ -996,6 +1100,11 @@ public int hashCode() { } public boolean isSame(TLRPC.Reaction reaction) { + if (reaction instanceof TLRPC.TL_reactionEmoji) { + return TextUtils.equals(((TLRPC.TL_reactionEmoji) reaction).emoticon, emojicon); + } else if (reaction instanceof TLRPC.TL_reactionCustomEmoji) { + return ((TLRPC.TL_reactionCustomEmoji) reaction).document_id == documentId; + } return false; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactionsContainerLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactionsContainerLayout.java index 0c3c43dc4f..cc6195f2c0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactionsContainerLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactionsContainerLayout.java @@ -94,6 +94,7 @@ public void set(ReactionsContainerLayout object, Float value) { public final static int TYPE_DEFAULT = 0; public final static int TYPE_STORY = 1; public static final int TYPE_STORY_LIKES = 2; + public static final int TYPE_TAGS = 3; private final static int ALPHA_DURATION = 150; private final static float SIDE_SCALE = 0.6f; @@ -1053,7 +1054,10 @@ public void setMessage(MessageObject message, TLRPC.ChatFull chatFull) { return; } } - if (reactionsChat != null) { + if (type == TYPE_TAGS) { + allReactionsAvailable = UserConfig.getInstance(currentAccount).isPremium(); + fillRecentReactionsList(visibleReactions); + } else if (reactionsChat != null) { if (reactionsChat.available_reactions instanceof TLRPC.TL_chatReactionsAll) { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(reactionsChat.id); if (chat != null && !ChatObject.isChannelAndNotMegaGroup(chat)) { @@ -1127,20 +1131,59 @@ private void filterReactions(List visib private void fillRecentReactionsList(List visibleReactions) { if (!allReactionsAvailable) { - //fill default reactions - List enabledReactions = MediaDataController.getInstance(currentAccount).getEnabledReactionsList(); - for (int i = 0; i < enabledReactions.size(); i++) { - ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromEmojicon(enabledReactions.get(i)); - visibleReactions.add(visibleReaction); + if (type == TYPE_TAGS) { + ArrayList topReactions = MediaDataController.getInstance(currentAccount).getSavedReactions(); + HashSet hashSet = new HashSet<>(); + int added = 0; + for (int i = 0; i < topReactions.size(); i++) { + ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromTLReaction(topReactions.get(i)); + if (!hashSet.contains(visibleReaction)) { + hashSet.add(visibleReaction); + visibleReactions.add(visibleReaction); + added++; + } + if (added == 16) { + break; + } + } + } else { + //fill default reactions + List enabledReactions = MediaDataController.getInstance(currentAccount).getEnabledReactionsList(); + for (int i = 0; i < enabledReactions.size(); i++) { + ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromEmojicon(enabledReactions.get(i)); + visibleReactions.add(visibleReaction); + } } return; } - ArrayList topReactions = MediaDataController.getInstance(currentAccount).getTopReactions(); + + ArrayList topReactions; + if (type == TYPE_TAGS) { + topReactions = MediaDataController.getInstance(currentAccount).getSavedReactions(); + } else { + topReactions = MediaDataController.getInstance(currentAccount).getTopReactions(); + } HashSet hashSet = new HashSet<>(); int added = 0; + if (type == TYPE_TAGS) { + TLRPC.TL_messages_savedReactionsTags savedTags = MessagesController.getInstance(currentAccount).getSavedReactionTags(); + if (savedTags != null) { + for (int i = 0; i < savedTags.tags.size(); i++) { + ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromTLReaction(savedTags.tags.get(i).reaction); + if (!hashSet.contains(visibleReaction)) { + hashSet.add(visibleReaction); + visibleReactions.add(visibleReaction); + added++; + } + if (added == 16) { + break; + } + } + } + } for (int i = 0; i < topReactions.size(); i++) { ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromTLReaction(topReactions.get(i)); - if (!hashSet.contains(visibleReaction) && (UserConfig.getInstance(currentAccount).isPremium() || visibleReaction.documentId == 0)) { + if (!hashSet.contains(visibleReaction) && (type == TYPE_TAGS || UserConfig.getInstance(currentAccount).isPremium() || visibleReaction.documentId == 0)) { hashSet.add(visibleReaction); visibleReactions.add(visibleReaction); added++; @@ -1150,22 +1193,24 @@ private void fillRecentReactionsList(List recentReactions = MediaDataController.getInstance(currentAccount).getRecentReactions(); - for (int i = 0; i < recentReactions.size(); i++) { - ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromTLReaction(recentReactions.get(i)); - if (!hashSet.contains(visibleReaction)) { - hashSet.add(visibleReaction); - visibleReactions.add(visibleReaction); + if (type != TYPE_TAGS || UserConfig.getInstance(currentAccount).isPremium()) { + ArrayList recentReactions = MediaDataController.getInstance(currentAccount).getRecentReactions(); + for (int i = 0; i < recentReactions.size(); i++) { + ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromTLReaction(recentReactions.get(i)); + if (!hashSet.contains(visibleReaction)) { + hashSet.add(visibleReaction); + visibleReactions.add(visibleReaction); + } } - } - //fill default reactions - List enabledReactions = MediaDataController.getInstance(currentAccount).getEnabledReactionsList(); - for (int i = 0; i < enabledReactions.size(); i++) { - ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromEmojicon(enabledReactions.get(i)); - if (!hashSet.contains(visibleReaction)) { - hashSet.add(visibleReaction); - visibleReactions.add(visibleReaction); + //fill default reactions + List enabledReactions = MediaDataController.getInstance(currentAccount).getEnabledReactionsList(); + for (int i = 0; i < enabledReactions.size(); i++) { + ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromEmojicon(enabledReactions.get(i)); + if (!hashSet.contains(visibleReaction)) { + hashSet.add(visibleReaction); + visibleReactions.add(visibleReaction); + } } } } 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 f07567be2f..35fa30cce2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/RecyclerListView.java @@ -2083,7 +2083,7 @@ public boolean onInterceptTouchEvent(MotionEvent e) { return false; } if (disallowInterceptTouchEvents) { - requestDisallowInterceptTouchEvent(true); + requestDisallowInterceptTouchEvent(this, true); } return onInterceptTouchListener != null && onInterceptTouchListener.onInterceptTouchEvent(e) || super.onInterceptTouchEvent(e); } @@ -2675,6 +2675,19 @@ public void requestLayout() { super.requestLayout(); } + public ViewParent getTouchParent() { + return null; + } + private void requestDisallowInterceptTouchEvent(View view, boolean disallow) { + if (view == null) return; + ViewParent parent = view.getParent(); + if (parent == null) return; + parent.requestDisallowInterceptTouchEvent(disallow); + parent = getTouchParent(); + if (parent == null) return; + parent.requestDisallowInterceptTouchEvent(disallow); + } + public void setAnimateEmptyView(boolean animate, int emptyViewAnimationType) { animateEmptyView = animate; this.emptyViewAnimationType = emptyViewAnimationType; @@ -2726,7 +2739,7 @@ public void startMultiselect(int positionFrom, boolean useRelativePositions, onM listPaddings = new int[2]; selectedPositions = new HashSet<>(); - getParent().requestDisallowInterceptTouchEvent(true); + requestDisallowInterceptTouchEvent(this, true); this.multiSelectionListener = multiSelectionListener; multiSelectionGesture = true; @@ -2748,7 +2761,7 @@ public boolean onTouchEvent(MotionEvent e) { } if (!multiSelectionGestureStarted && Math.abs(e.getY() - lastY) > touchSlop) { multiSelectionGestureStarted = true; - getParent().requestDisallowInterceptTouchEvent(true); + requestDisallowInterceptTouchEvent(this, true); } if (multiSelectionGestureStarted) { chekMultiselect(e.getX(), e.getY()); @@ -2767,7 +2780,7 @@ public boolean onTouchEvent(MotionEvent e) { lastY = Float.MAX_VALUE; multiSelectionGesture = false; multiSelectionGestureStarted = false; - getParent().requestDisallowInterceptTouchEvent(false); + requestDisallowInterceptTouchEvent(this, false); cancelMultiselectScroll(); return super.onTouchEvent(e); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReplyMessageLine.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReplyMessageLine.java index bc2e55cc3d..6c8cc6ad59 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReplyMessageLine.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReplyMessageLine.java @@ -145,6 +145,7 @@ private void resolveColor(MessageObject messageObject, int colorId, Theme.Resour public static final int TYPE_QUOTE = 1; public static final int TYPE_CODE = 2; public static final int TYPE_LINK = 3; + public static final int TYPE_CONTACT = 4; public int check(MessageObject messageObject, TLRPC.User currentUser, TLRPC.Chat currentChat, Theme.ResourcesProvider resourcesProvider, final int type) { final boolean dark = resourcesProvider != null ? resourcesProvider.isDark() : Theme.isCurrentThemeDark(); @@ -155,6 +156,24 @@ public int check(MessageObject messageObject, TLRPC.User currentUser, TLRPC.Chat color1 = color2 = color3 = Theme.getColor(Theme.key_chat_inReplyLine, resourcesProvider); backgroundColor = Theme.multAlpha(color1, dark ? 0.12f : 0.10f); return nameColorAnimated.set(nameColor = Theme.getColor(Theme.key_chat_inReplyNameText, resourcesProvider)); + } else if (type == TYPE_CONTACT + && messageObject.messageOwner != null + && MessageObject.getMedia(messageObject.messageOwner) != null + && MessageObject.getMedia(messageObject.messageOwner) instanceof TLRPC.TL_messageMediaContact + ) { + int colorId = 0; + TLRPC.User user = null; + long uid = MessageObject.getMedia(messageObject.messageOwner).user_id; + if (uid != 0) { + user = MessagesController.getInstance(messageObject.currentAccount).getUser(uid); + } + if (user != null) { + colorId = UserObject.getColorId(user); + emojiDocumentId = UserObject.getEmojiId(user); + } + resolveColor(messageObject, colorId, resourcesProvider); + backgroundColor = Theme.multAlpha(color1, 0.10f); + nameColor = color1; } else if (type != TYPE_REPLY && ( messageObject.overrideLinkColor >= 0 || messageObject.messageOwner != null && ( @@ -294,7 +313,7 @@ public int check(MessageObject messageObject, TLRPC.User currentUser, TLRPC.Chat backgroundColor = Theme.multAlpha(color3, dark ? 0.12f : 0.10f); nameColor = Theme.getColor(Theme.key_chat_outReplyNameText, resourcesProvider); } - if ((type == TYPE_REPLY || type == TYPE_LINK) && messageObject != null && messageObject.overrideLinkEmoji != -1) { + if ((type == TYPE_REPLY || type == TYPE_LINK || type == TYPE_CONTACT) && messageObject != null && messageObject.overrideLinkEmoji != -1) { emojiDocumentId = messageObject.overrideLinkEmoji; } if (emojiDocumentId != 0 && emoji == null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchTagsList.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchTagsList.java new file mode 100644 index 0000000000..bb13929e7d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SearchTagsList.java @@ -0,0 +1,426 @@ +package org.telegram.ui.Components; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.lerp; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.collection.LongSparseArray; +import androidx.core.graphics.ColorUtils; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.MessagesStorage; +import org.telegram.messenger.NotificationCenter; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; +import org.telegram.ui.LaunchActivity; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Objects; + +public class SearchTagsList extends BlurredFrameLayout implements NotificationCenter.NotificationCenterDelegate { + + private final int currentAccount; + private final Theme.ResourcesProvider resourcesProvider; + public final RecyclerListView listView; + private final Adapter adapter; + + private long chosen; + private final ArrayList oldItems = new ArrayList<>(); + private final ArrayList items = new ArrayList<>(); + + private static class Item { + ReactionsLayoutInBubble.VisibleReaction reaction; + int count; + + public static Item get(ReactionsLayoutInBubble.VisibleReaction reaction, int count) { + Item item = new Item(); + item.reaction = reaction; + item.count = count; + return item; + } + + public long hash() { + return reaction.hash; + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof Item)) { + return false; + } + Item that = (Item) obj; + return this.count == that.count && this.reaction.hash == that.reaction.hash; + } + } + + public SearchTagsList(Context context, SizeNotifierFrameLayout contentView, int currentAccount, Theme.ResourcesProvider resourcesProvider) { + super(context, contentView); + + this.currentAccount = currentAccount; + this.resourcesProvider = resourcesProvider; + + listView = new RecyclerListView(context, resourcesProvider) { + @Override + public Integer getSelectorColor(int position) { + return 0; + } + }; + listView.setPadding(dp(5.66f), 0, dp(5.66f), 0); + listView.setClipToPadding(false); + final LinearLayoutManager layoutManager = new LinearLayoutManager(context); + layoutManager.setOrientation(RecyclerView.HORIZONTAL); + listView.setLayoutManager(layoutManager); + listView.setAdapter(adapter = new Adapter()); + addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + listView.setOnItemClickListener((view, position) -> { + if (position < 0 || position >= items.size()) { + return; + } + listView.forAllChild(view2 -> { + if (view2 instanceof TagButton) { + ((TagButton) view2).setChosen(false, true); + } + }); + long hash = items.get(position).hash(); + if (chosen == hash) { + chosen = 0; + setFilter(null); + } else { + chosen = hash; + setFilter(items.get(position).reaction); + ((TagButton) view).setChosen(true, true); + } + }); + + DefaultItemAnimator itemAnimator = new DefaultItemAnimator() { + @Override + public boolean animateMove(RecyclerView.ViewHolder holder, ItemHolderInfo info, int fromX, int fromY, int toX, int toY) { + int position = holder.getAdapterPosition(); + if (position >= 0 && position < items.size()) { + Item item = items.get(position); + TagButton btn = (TagButton) holder.itemView; + boolean updatedChosen = btn.setChosen(chosen == item.hash(), true); + boolean updatedCount = btn.setCount(item.count); + if (updatedChosen || updatedCount) { + return true; + } + } + return super.animateMove(holder, info, fromX, fromY, toX, toY); + } + + @Override + protected void animateMoveImpl(RecyclerView.ViewHolder holder, MoveInfo moveInfo) { + super.animateMoveImpl(holder, moveInfo); + } + }; + itemAnimator.setSupportsChangeAnimations(false); + itemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + itemAnimator.setDurations(320); + listView.setItemAnimator(itemAnimator); + + MediaDataController.getInstance(currentAccount).loadSavedReactions(false); + updateTags(); + } + + public boolean hasFilters() { + return !items.isEmpty(); + } + + public void clear() { + listView.forAllChild(view2 -> { + if (view2 instanceof TagButton) { + ((TagButton) view2).setChosen(false, true); + } + }); + chosen = 0; + } + + protected void setFilter(ReactionsLayoutInBubble.VisibleReaction reaction) { + + } + + public void attach() { + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.savedReactionTagsUpdate); + } + + public void detach() { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.savedReactionTagsUpdate); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.savedReactionTagsUpdate) { + updateTags(); + } + } + + public void updateTags() { + HashSet hashes = new HashSet<>(); + oldItems.clear(); + oldItems.addAll(items); + items.clear(); + TLRPC.TL_messages_savedReactionsTags savedReactionsTags = MessagesController.getInstance(currentAccount).getSavedReactionTags(); + if (savedReactionsTags != null) { + for (int i = 0; i < savedReactionsTags.tags.size(); ++i) { + TLRPC.TL_savedReactionTag tag = savedReactionsTags.tags.get(i); + ReactionsLayoutInBubble.VisibleReaction r = ReactionsLayoutInBubble.VisibleReaction.fromTLReaction(tag.reaction); + if (!hashes.contains(r.hash)) { + items.add(Item.get(r, tag.count)); + hashes.add(r.hash); + } + } + } +// ArrayList defaultReactions = MediaDataController.getInstance(currentAccount).getSavedReactions(); +// for (int i = 0; i < defaultReactions.size(); ++i) { +// ReactionsLayoutInBubble.VisibleReaction r = ReactionsLayoutInBubble.VisibleReaction.fromTLReaction(defaultReactions.get(i)); +// if (!hashes.contains(r.hash)) { +// items.add(Item.get(r, 0)); +// hashes.add(r.hash); +// } +// } + + DiffUtil.calculateDiff(new DiffUtil.Callback() { + @Override + public int getOldListSize() { + return oldItems.size(); + } + @Override + public int getNewListSize() { + return items.size(); + } + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + return oldItems.get(oldItemPosition).equals(items.get(newItemPosition)); + } + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + return oldItems.get(oldItemPosition).hash() == items.get(newItemPosition).hash(); + } + }).dispatchUpdatesTo(adapter); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(dp(40), MeasureSpec.EXACTLY)); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (shownT < .5f) { + return false; + } + return super.dispatchTouchEvent(ev); + } + + private float shownT; + public void setShown(float shownT) { + this.shownT = shownT; + listView.setPivotX(listView.getWidth() / 2f); + listView.setPivotY(0); + listView.setScaleX(lerp(0.8f, 1, shownT)); + listView.setScaleY(lerp(0.8f, 1, shownT)); + listView.setAlpha(shownT); + invalidate(); + } + + public boolean shown() { + return shownT > 0.5f; + } + +// private final Paint backgroundPaint = new Paint(); +// @Override +// public void setBackgroundColor(int color) { +// backgroundPaint.setColor(color); +// } + + public int getCurrentHeight() { + return (int) (getMeasuredHeight() * shownT); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + canvas.save(); +// canvas.drawRect(0, 0, getWidth(), getCurrentHeight(), backgroundPaint); + canvas.clipRect(0, 0, getWidth(), getCurrentHeight()); + super.dispatchDraw(canvas); + canvas.restore(); + } + + private class Adapter extends RecyclerListView.SelectionAdapter { + + public Adapter() { +// setHasStableIds(true); + } + +// @Override +// public long getItemId(int position) { +// if (position < 0 || position >= items.size()) return position; +// return items.get(position).hash(); +// } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = new TagButton(getContext()); + return new RecyclerListView.Holder(view); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + if (position < 0 || position >= items.size()) return; + final Item item = items.get(position); + ((TagButton) holder.itemView).set(item.reaction.toTLReaction(), item.count); + ((TagButton) holder.itemView).setChosen(item.hash() == chosen, false); + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return true; + } + } + + private class TagButton extends View { + public ReactionsLayoutInBubble.ReactionButton reactionButton; + + public TagButton(Context context) { + super(context); + ScaleStateListAnimator.apply(this); + } + + private int count; + public void set(TLRPC.Reaction reaction, Integer count) { + TLRPC.TL_reactionCount reactionCount = new TLRPC.TL_reactionCount(); + reactionCount.reaction = reaction; + reactionCount.count = this.count = count == null ? 0 : count; + + reactionButton = new ReactionsLayoutInBubble.ReactionButton(null, currentAccount, this, reactionCount, false, resourcesProvider) { + @Override + protected void updateColors(float progress) { + lastDrawnTextColor = ColorUtils.blendARGB(fromTextColor, chosen ? Theme.getColor(Theme.key_chat_inReactionButtonTextSelected) : Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2), progress); + lastDrawnBackgroundColor = ColorUtils.blendARGB(fromBackgroundColor, chosen ? Theme.getColor(Theme.key_chat_inReactionButtonBackground, resourcesProvider) : Theme.getColor(Theme.key_actionBarActionModeReaction, resourcesProvider), progress); + } + + @Override + protected boolean drawTagDot() { + return !drawCounter(); + } + + @Override + protected int getCacheType() { + return AnimatedEmojiDrawable.CACHE_TYPE_ALERT_EMOJI_STATUS; + } + + @Override + protected boolean drawCounter() { + return count > 0 || counterDrawable.countChangeProgress != 1f; + } + }; + reactionButton.width = dp(44.33f); + reactionButton.counterDrawable.setCount(reactionCount.count, false); + if (reactionButton.counterDrawable != null && reactionButton.count > 0) { + reactionButton.width += reactionButton.counterDrawable.textPaint.measureText(reactionButton.countText); + } + reactionButton.height = dp(28); + reactionButton.choosen = chosen; + if (attached) { + reactionButton.attach(); + } + } + + private boolean chosen; + public boolean setChosen(boolean value, boolean animated) { + if (chosen == value) return false; + chosen = value; + if (reactionButton != null) { + reactionButton.choosen = value; + + if (animated) { + reactionButton.fromTextColor = reactionButton.lastDrawnTextColor; + reactionButton.fromBackgroundColor = reactionButton.lastDrawnBackgroundColor; + progress.set(0, true); + } else { + progress.set(1, true); + } + invalidate(); + } + return true; + } + + public boolean setCount(int count) { + if (this.count != count && reactionButton != null) { + reactionButton.animateFromWidth = reactionButton.width; + reactionButton.count = count; + reactionButton.width = dp(44.33f); + reactionButton.counterDrawable.setCount(count, true); + if (reactionButton.counterDrawable != null && reactionButton.count > 0) { + reactionButton.width += reactionButton.counterDrawable.textPaint.measureText(reactionButton.countText); + } + progress.set(0, true); + invalidate(); + return true; + } + return false; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(dp(8.67f) + (reactionButton != null ? reactionButton.width : dp(44.33f)), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(40), MeasureSpec.EXACTLY)); + } + + + private AnimatedFloat progress = new AnimatedFloat(this, 0, 260, CubicBezierInterpolator.EASE_OUT_QUINT); + + @Override + protected void onDraw(Canvas canvas) { + reactionButton.draw(canvas, (getWidth() - reactionButton.width) / 2f, (getHeight() - reactionButton.height) / 2f, progress.set(1f), 1f, false); + } + + private boolean attached; + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (!attached) { + if (reactionButton != null) { + reactionButton.attach(); + } + attached = true; + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (attached) { + if (reactionButton != null) { + reactionButton.detach(); + } + attached = false; + } + } + } +} 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 8888144a9f..d134cb85a9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ShareAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ShareAlert.java @@ -66,6 +66,7 @@ import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; import org.telegram.messenger.DialogObject; @@ -93,6 +94,7 @@ import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; @@ -107,6 +109,7 @@ import org.telegram.ui.DialogsActivity; import org.telegram.ui.LaunchActivity; import org.telegram.ui.MessageStatisticActivity; +import org.telegram.ui.PremiumPreviewFragment; import java.util.ArrayList; import java.util.Collections; @@ -124,6 +127,7 @@ public class ShareAlert extends BottomSheet implements NotificationCenter.Notifi private FrameLayout writeButtonContainer; private View selectedCountView; private TextView pickerBottomLayout; + private FrameLayout bulletinContainer; private LinearLayout sharesCountLayout; private AnimatorSet animatorSet; private RecyclerListView topicsGridView; @@ -598,7 +602,7 @@ protected void onTransitionEnd() { protected void onPanTranslationUpdate(float y, float progress, boolean keyboardVisible) { super.onPanTranslationUpdate(y, progress, keyboardVisible); for (int i = 0; i < containerView.getChildCount(); i++) { - if (containerView.getChildAt(i) != pickerBottomLayout && containerView.getChildAt(i) != shadow[1] && containerView.getChildAt(i) != sharesCountLayout + if (containerView.getChildAt(i) != pickerBottomLayout && containerView.getChildAt(i) != bulletinContainer && containerView.getChildAt(i) != shadow[1] && containerView.getChildAt(i) != sharesCountLayout && containerView.getChildAt(i) != frameLayout2 && containerView.getChildAt(i) != writeButtonContainer && containerView.getChildAt(i) != selectedCountView) { containerView.getChildAt(i).setTranslationY(y); } @@ -1314,6 +1318,9 @@ public void getItemOffsets(android.graphics.Rect outRect, View view, RecyclerVie shadow[1].setAlpha(0.0f); } + bulletinContainer = new FrameLayout(context); + containerView.addView(bulletinContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 100, Gravity.FILL_HORIZONTAL | Gravity.BOTTOM, 0, 0, 0, pickerBottomLayout != null ? 48 : 0)); + frameLayout2 = new FrameLayout(context) { private final Paint p = new Paint(); @@ -1573,12 +1580,51 @@ public void setRecentSearch(ArrayList a protected void onShareStory(View cell) { } + + private void showPremiumBlockedToast(View view, long dialogId) { + AndroidUtilities.shakeViewSpring(view, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + String username = ""; + if (dialogId >= 0) { + username = UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(dialogId)); + } + Bulletin bulletin; + if (MessagesController.getInstance(currentAccount).premiumFeaturesBlocked()) { + bulletin = BulletinFactory.of(bulletinContainer, resourcesProvider).createSimpleBulletin(R.raw.star_premium_2, AndroidUtilities.replaceTags(LocaleController.formatString(R.string.UserBlockedNonPremium, username))); + } else { + bulletin = BulletinFactory.of(bulletinContainer, resourcesProvider).createSimpleBulletin(R.raw.star_premium_2, AndroidUtilities.replaceTags(LocaleController.formatString(R.string.UserBlockedNonPremium, username)), LocaleController.getString(R.string.UserBlockedNonPremiumButton), () -> { + Runnable openPremium = () -> { + BaseFragment lastFragment = LaunchActivity.getLastFragment(); + if (lastFragment != null) { + BaseFragment.BottomSheetParams params = new BaseFragment.BottomSheetParams(); + params.transitionFromLeft = true; + params.allowNestedScroll = false; + lastFragment.showAsSheet(new PremiumPreviewFragment("noncontacts"), params); + } + }; + if (isKeyboardVisible()) { + if (searchView != null) { + AndroidUtilities.hideKeyboard(searchView.searchEditText); + } + AndroidUtilities.runOnUIThread(openPremium, 300); + } else { + openPremium.run(); + } + }); + } + bulletin.show(); + } + private int shiftDp = 4; private void selectDialog(View cell, TLRPC.Dialog dialog) { if (dialog instanceof ShareDialogsAdapter.MyStoryDialog) { onShareStory(cell); return; } + if (dialog != null && (cell instanceof ShareDialogCell && ((ShareDialogCell) cell).isBlocked() || cell instanceof ProfileSearchCell && ((ProfileSearchCell) cell).isBlocked())) { + showPremiumBlockedToast(cell, dialog.id); + return; + } if (topicsGridView.getVisibility() != View.GONE || parentActivity == null) { return; } @@ -3023,7 +3069,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType break; } case 0: { - view = new ProfileSearchCell(context, resourcesProvider).useCustomPaints(); + view = new ProfileSearchCell(context, resourcesProvider).useCustomPaints().showPremiumBlock(true); break; } default: @@ -3053,7 +3099,7 @@ public boolean supportsPredictiveItemAnimations() { }; layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); horizontalListView.setLayoutManager(layoutManager); - horizontalListView.setAdapter(categoryAdapter = new DialogsSearchAdapter.CategoryAdapterRecycler(context, currentAccount, true, resourcesProvider) { + horizontalListView.setAdapter(categoryAdapter = new DialogsSearchAdapter.CategoryAdapterRecycler(context, currentAccount, true, true, resourcesProvider) { @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { HintDialogCell cell = (HintDialogCell) holder.itemView; @@ -3088,6 +3134,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } }); horizontalListView.setOnItemClickListener((view1, position) -> { + HintDialogCell cell = (HintDialogCell) view1; TLRPC.TL_topPeer peer = MediaDataController.getInstance(currentAccount).hints.get(position); TLRPC.Dialog dialog = new TLRPC.TL_dialog(); TLRPC.Chat chat = null; @@ -3100,9 +3147,12 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else if (peer.peer.chat_id != 0) { did = -peer.peer.chat_id; } + if (cell.isBlocked()) { + showPremiumBlockedToast(cell, did); + return; + } dialog.id = did; selectDialog(null, dialog); - HintDialogCell cell = (HintDialogCell) view1; cell.setChecked(selectedDialogs.indexOfKey(did) >= 0, true); }); view = horizontalListView; @@ -3211,7 +3261,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ((ProfileSearchCell) holder.itemView).setData(object, ec, name, null, false, false); ((ProfileSearchCell) holder.itemView).useSeparator = position < getItemCount() - 2; } else if (holder.itemView instanceof ShareDialogCell) { - ((ShareDialogCell) holder.itemView).setDialog((int) id, selectedDialogs.indexOfKey(id) >= 0, name); + ((ShareDialogCell) holder.itemView).setDialog(id, selectedDialogs.indexOfKey(id) >= 0, name); } } else if (holder.getItemViewType() == 2) { ((RecyclerListView) holder.itemView).getAdapter().notifyDataSetChanged(); 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 1063714f73..68688774aa 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java @@ -10,11 +10,11 @@ import android.animation.ValueAnimator; import android.app.Activity; import android.content.Context; +import android.content.DialogInterface; import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; @@ -58,13 +58,16 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.collection.LongSparseArray; import androidx.core.content.ContextCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; +import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.exoplayer2.util.Log; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; @@ -81,6 +84,7 @@ import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; +import org.telegram.messenger.SavedMessagesController; import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; @@ -96,6 +100,7 @@ import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.ActionBarMenuSubItem; import org.telegram.ui.ActionBar.ActionBarPopupWindow; +import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BackDrawable; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; @@ -120,7 +125,9 @@ import org.telegram.ui.Cells.SharedPhotoVideoCell2; import org.telegram.ui.Cells.UserCell; import org.telegram.ui.ChatActivity; +import org.telegram.ui.ChatActivityContainer; import org.telegram.ui.Components.Forum.ForumUtilities; +import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; import org.telegram.ui.DialogsActivity; import org.telegram.ui.PhotoViewer; import org.telegram.ui.PremiumPreviewFragment; @@ -136,9 +143,10 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Objects; @SuppressWarnings("unchecked") -public class SharedMediaLayout extends FrameLayout implements NotificationCenter.NotificationCenterDelegate { +public class SharedMediaLayout extends FrameLayout implements NotificationCenter.NotificationCenterDelegate, DialogCell.DialogCellDelegate { public static final int TAB_PHOTOVIDEO = 0; public static final int TAB_FILES = 1; @@ -151,6 +159,8 @@ public class SharedMediaLayout extends FrameLayout implements NotificationCenter public static final int TAB_STORIES = 8; public static final int TAB_ARCHIVED_STORIES = 9; public static final int TAB_RECOMMENDED_CHANNELS = 10; + public static final int TAB_SAVED_DIALOGS = 11; + public static final int TAB_SAVED_MESSAGES = 12; public static final int FILTER_PHOTOS_AND_VIDEOS = 0; public static final int FILTER_PHOTOS_ONLY = 1; @@ -194,7 +204,7 @@ public boolean dispatchFastScrollEvent(MotionEvent ev) { ActionBarPopupWindow optionsWindow; FlickerLoadingView globalGradientView; private final int viewType; - private int topicId; + private long topicId; private UndoView undoView; @@ -386,10 +396,42 @@ public void drawListForBlur(Canvas blurCanvas) { } } + @Override + public void onButtonClicked(DialogCell dialogCell) { + + } + + @Override + public void onButtonLongPress(DialogCell dialogCell) { + + } + + @Override + public boolean canClickButtonInside() { + return false; + } + + @Override + public void openStory(DialogCell dialogCell, Runnable onDone) { + if (profileActivity == null) return; + if (profileActivity.getMessagesController().getStoriesController().hasStories(dialogCell.getDialogId())) { + profileActivity.getOrCreateStoryViewer().doOnAnimationReady(onDone); + profileActivity.getOrCreateStoryViewer().open(profileActivity.getContext(), dialogCell.getDialogId(), StoriesListPlaceProvider.of((RecyclerListView) dialogCell.getParent())); + } + } + + @Override + public void showChatPreview(DialogCell dialogCell) {} + + @Override + public void openHiddenStories() {} + private static class MediaPage extends FrameLayout { public long lastCheckScrollTime; public boolean fastScrollEnabled; public ObjectAnimator fastScrollAnimator; + private DefaultItemAnimator itemAnimator; + private RecyclerView.RecycledViewPool viewPool, searchViewPool; private InternalListView listView; private InternalListView animationSupportingListView; private GridLayoutManager animationSupportingLayoutManager; @@ -506,6 +548,9 @@ public void updateFastScrollVisibility(MediaPage mediaPage, boolean animated) { private GifAdapter gifAdapter; private CommonGroupsAdapter commonGroupsAdapter; private ChannelRecommendationsAdapter channelRecommendationsAdapter; + private SavedDialogsAdapter savedDialogsAdapter; + private SavedMessagesSearchAdapter savedMessagesSearchAdapter; + private ChatActivityContainer savedMessagesContainer; private ChatUsersAdapter chatUsersAdapter; private StoriesAdapter storiesAdapter; private StoriesAdapter animationSupportingStoriesAdapter; @@ -521,6 +566,8 @@ public void updateFastScrollVisibility(MediaPage mediaPage, boolean animated) { public ImageView photoVideoOptionsItem; private ActionBarMenuItem forwardItem; private ActionBarMenuItem gotoItem; + private ActionBarMenuItem pinItem; + private ActionBarMenuItem unpinItem; private int searchItemState; private Drawable pinnedHeaderShadowDrawable; private boolean ignoreSearchCollapse; @@ -592,9 +639,11 @@ public static class SharedMediaPreloader implements NotificationCenter.Notificat private int[] mediaMergeCount = new int[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; private int[] lastMediaCount = new int[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; private int[] lastLoadMediaCount = new int[]{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + public boolean hasSavedMessages; + private boolean checkedHasSavedMessages; private SharedMediaData[] sharedMediaData; private long dialogId; - private int topicId; + private long topicId; private long mergeDialogId; private BaseFragment parentFragment; private ArrayList delegates = new ArrayList<>(); @@ -607,10 +656,38 @@ public SharedMediaPreloader(BaseFragment fragment) { dialogId = chatActivity.getDialogId(); mergeDialogId = chatActivity.getMergeDialogId(); topicId = chatActivity.getTopicId(); + if (dialogId != fragment.getUserConfig().getClientUserId()) { + fragment.getMessagesController().getSavedMessagesController().hasSavedMessages(dialogId, hasMessages -> { + this.hasSavedMessages = hasMessages; + this.checkedHasSavedMessages = true; + if (hasSavedMessages) { + for (int a = 0, N = delegates.size(); a < N; a++) { + delegates.get(a).mediaCountUpdated(); + } + } + }); + } } else if (fragment instanceof ProfileActivity) { ProfileActivity profileActivity = (ProfileActivity) fragment; - dialogId = profileActivity.getDialogId(); - topicId = profileActivity.getTopicId(); + if (profileActivity.saved) { + dialogId = profileActivity.getUserConfig().getClientUserId(); + topicId = profileActivity.getDialogId(); + } else { + dialogId = profileActivity.getDialogId(); + topicId = profileActivity.getTopicId(); + + if (dialogId != fragment.getUserConfig().getClientUserId()) { + fragment.getMessagesController().getSavedMessagesController().hasSavedMessages(dialogId, hasMessages -> { + this.hasSavedMessages = hasMessages; + this.checkedHasSavedMessages = true; + if (hasSavedMessages) { + for (int a = 0, N = delegates.size(); a < N; a++) { + delegates.get(a).mediaCountUpdated(); + } + } + }); + } + } } else if (fragment instanceof MediaActivity) { MediaActivity mediaActivity = (MediaActivity) fragment; dialogId = mediaActivity.getDialogId(); @@ -634,6 +711,7 @@ public SharedMediaPreloader(BaseFragment fragment) { notificationCenter.addObserver(this, NotificationCenter.chatInfoDidLoad); notificationCenter.addObserver(this, NotificationCenter.fileLoaded); notificationCenter.addObserver(this, NotificationCenter.storiesListUpdated); + notificationCenter.addObserver(this, NotificationCenter.savedMessagesDialogsUpdate); } public void addDelegate(SharedMediaPreloaderDelegate delegate) { @@ -660,6 +738,7 @@ public void onDestroy(BaseFragment fragment) { notificationCenter.removeObserver(this, NotificationCenter.chatInfoDidLoad); notificationCenter.removeObserver(this, NotificationCenter.fileLoaded); notificationCenter.removeObserver(this, NotificationCenter.storiesListUpdated); + notificationCenter.removeObserver(this, NotificationCenter.savedMessagesDialogsUpdate); } public int[] getLastMediaCount() { @@ -674,7 +753,7 @@ public SharedMediaData[] getSharedMediaData() { public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.mediaCountsDidLoad) { long did = (Long) args[0]; - int topicId = (int) args[1]; + long topicId = (Long) args[1]; if (this.topicId == topicId && (did == dialogId || did == mergeDialogId)) { int[] counts = (int[]) args[2]; if (did == dialogId) { @@ -710,7 +789,7 @@ public void didReceivedNotification(int id, int account, Object... args) { } } else if (id == NotificationCenter.mediaCountDidLoad) { long did = (Long) args[0]; - long topicId = (Integer) args[1]; + long topicId = (Long) args[1]; if ((did == dialogId || did == mergeDialogId) && this.topicId == topicId) { int type = (Integer) args[4]; int mCount = (Integer) args[2]; @@ -738,9 +817,10 @@ public void didReceivedNotification(int id, int account, Object... args) { if (dialogId == (Long) args[0]) { boolean enc = DialogObject.isEncryptedDialog(dialogId); ArrayList arr = (ArrayList) args[1]; + final int currentAccount = parentFragment != null ? parentFragment.getCurrentAccount() : -1; for (int a = 0; a < arr.size(); a++) { MessageObject obj = arr.get(a); - if (topicId != 0 && topicId != MessageObject.getTopicId(obj.messageOwner, true)) { + if (topicId != 0 && topicId != MessageObject.getTopicId(currentAccount, obj.messageOwner, true)) { continue; } if (MessageObject.getMedia(obj.messageOwner) == null || obj.needDrawBluredPreview()) { @@ -826,11 +906,12 @@ public void didReceivedNotification(int id, int account, Object... args) { boolean changed = false; int type; ArrayList markAsDeletedMessages = (ArrayList) args[0]; + final int currentAccount = parentFragment != null ? parentFragment.getCurrentAccount() : -1; for (int a = 0, N = markAsDeletedMessages.size(); a < N; a++) { for (int b = 0; b < sharedMediaData.length; b++) { MessageObject messageObject = sharedMediaData[b].deleteMessage(markAsDeletedMessages.get(a), 0); if (messageObject != null) { - if (messageObject.getDialogId() == dialogId && (topicId == 0 || MessageObject.getTopicId(messageObject.messageOwner, true) == topicId)) { + if (messageObject.getDialogId() == dialogId && (topicId == 0 || MessageObject.getTopicId(currentAccount, messageObject.messageOwner, true) == topicId)) { if (mediaCount[b] > 0) { mediaCount[b]--; } @@ -865,10 +946,11 @@ public void didReceivedNotification(int id, int account, Object... args) { } int loadIndex = did == dialogId ? 0 : 1; ArrayList messageObjects = (ArrayList) args[1]; + final int currentAccount = parentFragment != null ? parentFragment.getCurrentAccount() : -1; for (int b = 0, N = messageObjects.size(); b < N; b++) { MessageObject messageObject = messageObjects.get(b); int mid = messageObject.getId(); - int topicId = MessageObject.getTopicId(messageObject.messageOwner, true); + long topicId = MessageObject.getTopicId(currentAccount, messageObject.messageOwner, true); int type = MediaDataController.getMediaType(messageObject.messageOwner); if (this.topicId != 0 && topicId != this.topicId) { continue; @@ -926,6 +1008,14 @@ public void run() { } }); } + } else if (id == NotificationCenter.savedMessagesDialogsUpdate) { + final boolean newHasMessages = (parentFragment != null && parentFragment.getMessagesController().getSavedMessagesController().containsDialog(dialogId)); + if (checkedHasSavedMessages && hasSavedMessages != newHasMessages) { + hasSavedMessages = newHasMessages; + for (int a = 0, N = delegates.size(); a < N; a++) { + delegates.get(a).mediaCountUpdated(); + } + } } } @@ -1240,6 +1330,8 @@ public Period(TLRPC.TL_searchResultPosition calendarPeriod) { private final static int forward = 100; private final static int delete = 101; private final static int gotochat = 102; + private final static int pin = 103; + private final static int unpin = 104; private BaseFragment profileActivity; @@ -1275,6 +1367,8 @@ public SharedMediaLayout(Context context, long did, SharedMediaPreloader preload hasMedia = new int[]{mediaCount[0], mediaCount[1], mediaCount[2], mediaCount[3], mediaCount[4], mediaCount[5], topicId == 0 ? commonGroupsCount : 0}; if (initialTab == TAB_RECOMMENDED_CHANNELS) { this.initialTab = initialTab; + } else if (initialTab == TAB_SAVED_DIALOGS) { + this.initialTab = initialTab; } else if (userInfo != null && userInfo.stories_pinned_available || chatInfo != null && chatInfo.stories_pinned_available || isStoriesView()) { this.initialTab = getInitialTab(); } else if (initialTab != -1 && topicId == 0) { @@ -1319,6 +1413,7 @@ public SharedMediaLayout(Context context, long did, SharedMediaPreloader preload profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.storiesListUpdated); profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.storiesUpdated); profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.channelRecommendationsLoaded); + profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.savedMessagesDialogsUpdate); for (int a = 0; a < 10; a++) { //cellCache.add(new SharedPhotoVideoCell(context)); @@ -1360,6 +1455,9 @@ public boolean needPlayMessage(MessageObject messageObject) { } cantDeleteMessagesCount = 0; actionModeViews.clear(); + if (savedDialogsAdapter != null) { + savedDialogsAdapter.unselectAll(); + } final ActionBarMenu menu = actionBar.createMenu(); menu.addOnLayoutChangeListener(new OnLayoutChangeListener() { @@ -1387,6 +1485,9 @@ public void onSearchCollapse() { linksSearchAdapter.search(null, true); audioSearchAdapter.search(null, true); groupUsersSearchAdapter.search(null, true); + if (savedMessagesSearchAdapter != null) { + savedMessagesSearchAdapter.search(null); + } onSearchStateChanged(false); if (ignoreSearchCollapse) { ignoreSearchCollapse = false; @@ -1424,6 +1525,11 @@ public void onTextChanged(EditText editText) { return; } groupUsersSearchAdapter.search(text, true); + } else if (mediaPages[0].selectedType == TAB_SAVED_DIALOGS) { + if (savedMessagesSearchAdapter == null) { + return; + } + savedMessagesSearchAdapter.search(text); } } @@ -1677,6 +1783,24 @@ public void onClick(View view) { actionModeViews.add(forwardItem); forwardItem.setOnClickListener(v -> onActionBarItemClick(v, forward)); + pinItem = new ActionBarMenuItem(context, null, getThemedColor(Theme.key_actionBarActionModeDefaultSelector), getThemedColor(Theme.key_actionBarActionModeDefaultIcon), false); + pinItem.setIcon(R.drawable.msg_pin); + pinItem.setContentDescription(LocaleController.getString(R.string.PinMessage)); + pinItem.setDuplicateParentStateEnabled(false); + pinItem.setVisibility(View.GONE); + actionModeLayout.addView(pinItem, new LinearLayout.LayoutParams(dp(54), ViewGroup.LayoutParams.MATCH_PARENT)); + actionModeViews.add(pinItem); + pinItem.setOnClickListener(v -> onActionBarItemClick(v, pin)); + + unpinItem = new ActionBarMenuItem(context, null, getThemedColor(Theme.key_actionBarActionModeDefaultSelector), getThemedColor(Theme.key_actionBarActionModeDefaultIcon), false); + unpinItem.setIcon(R.drawable.msg_unpin); + unpinItem.setContentDescription(LocaleController.getString(R.string.UnpinMessage)); + unpinItem.setDuplicateParentStateEnabled(false); + unpinItem.setVisibility(View.GONE); + actionModeLayout.addView(unpinItem, new LinearLayout.LayoutParams(dp(54), ViewGroup.LayoutParams.MATCH_PARENT)); + actionModeViews.add(unpinItem); + unpinItem.setOnClickListener(v -> onActionBarItemClick(v, unpin)); + updateForwardItem(); } deleteItem = new ActionBarMenuItem(context, null, getThemedColor(Theme.key_actionBarActionModeDefaultSelector), getThemedColor(Theme.key_actionBarActionModeDefaultIcon), false); @@ -1708,6 +1832,16 @@ public void notifyDataSetChanged() { groupUsersSearchAdapter = new GroupUsersSearchAdapter(context); commonGroupsAdapter = new CommonGroupsAdapter(context); channelRecommendationsAdapter = new ChannelRecommendationsAdapter(context); + savedDialogsAdapter = new SavedDialogsAdapter(context); + savedMessagesSearchAdapter = new SavedMessagesSearchAdapter(context); + if (!isStoriesView() && !includeSavedDialogs()) { + Bundle args = new Bundle(); + args.putLong("user_id", profileActivity.getUserConfig().getClientUserId()); + args.putInt("chatMode", ChatActivity.MODE_SAVED); + savedMessagesContainer = new ChatActivityContainer(context, profileActivity.getParentLayout(), args); + savedMessagesContainer.chatActivity.setSavedDialog(dialog_id); + savedMessagesContainer.chatActivity.reversed = true; + } chatUsersAdapter = new ChatUsersAdapter(context); if (topicId == 0) { chatUsersAdapter.sortedUsers = sortedUsers; @@ -1765,6 +1899,7 @@ public void notifyDataSetChanged() { } } final MediaPage mediaPage = new MediaPage(context) { + @Override public void setTranslationX(float translationX) { super.setTranslationX(translationX); @@ -1891,6 +2026,10 @@ public int getSpanSize(int position) { return mediaPage.layoutManager.getSpanSizeForItem(position); } }); + mediaPages[a].itemAnimator = new DefaultItemAnimator(); + mediaPages[a].itemAnimator.setDurations(280); + mediaPages[a].itemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + mediaPages[a].itemAnimator.setSupportsChangeAnimations(false); mediaPages[a].listView = new InternalListView(context) { final HashSet excludeDrawViews = new HashSet<>(); @@ -2452,6 +2591,55 @@ public void getItemOffsets(android.graphics.Rect outRect, View view, RecyclerVie args.putLong("chat_id", channelRecommendationsAdapter.chats.get(position).id); profileActivity.presentFragment(new ChatActivity(args)); } + } else if (mediaPage.selectedType == TAB_SAVED_DIALOGS) { + if (mediaPage.listView.getAdapter() == savedMessagesSearchAdapter) { + if (position < 0) { + return; + } + if (position < savedMessagesSearchAdapter.dialogs.size()) { + SavedMessagesController.SavedDialog d = savedMessagesSearchAdapter.dialogs.get(position); + + Bundle args = new Bundle(); + args.putLong("user_id", profileActivity.getUserConfig().getClientUserId()); + args.putInt("chatMode", ChatActivity.MODE_SAVED); + ChatActivity chatActivity = new ChatActivity(args); + chatActivity.setSavedDialog(d.dialogId); + profileActivity.presentFragment(chatActivity); + return; + } + + position -= savedMessagesSearchAdapter.dialogs.size(); + if (position < savedMessagesSearchAdapter.messages.size()) { + MessageObject msg = savedMessagesSearchAdapter.messages.get(position); + + Bundle args = new Bundle(); + args.putLong("user_id", profileActivity.getUserConfig().getClientUserId()); + args.putInt("message_id", msg.getId()); + args.putInt("chatMode", ChatActivity.MODE_SAVED); + ChatActivity chatActivity = new ChatActivity(args); + chatActivity.setSavedDialog(msg.getSavedDialogId()); + chatActivity.setHighlightMessageId(msg.getId()); + profileActivity.presentFragment(chatActivity); + } + return; + } + if (isActionModeShowed) { + if (savedDialogsAdapter.itemTouchHelper.isIdle()) { + savedDialogsAdapter.select(view); + } + return; + } + + Bundle args = new Bundle(); + if (position < 0 || position >= savedDialogsAdapter.dialogs.size()) { + return; + } + SavedMessagesController.SavedDialog d = savedDialogsAdapter.dialogs.get(position); + args.putLong("user_id", profileActivity.getUserConfig().getClientUserId()); + args.putInt("chatMode", ChatActivity.MODE_SAVED); + ChatActivity chatActivity = new ChatActivity(args); + chatActivity.setSavedDialog(d.dialogId); + profileActivity.presentFragment(chatActivity); } }); mediaPages[a].listView.setOnScrollListener(new RecyclerView.OnScrollListener() { @@ -2463,10 +2651,10 @@ public void onScrollStateChanged(RecyclerView recyclerView, int newState) { @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { checkLoadMoreScroll(mediaPage, (RecyclerListView) recyclerView, layoutManager); - if (dy != 0 && (mediaPages[0].selectedType == 0 || mediaPages[0].selectedType == 5) && !sharedMediaData[0].messages.isEmpty()) { + if (dy != 0 && (mediaPages[0].selectedType == TAB_PHOTOVIDEO || mediaPages[0].selectedType == TAB_GIF) && !sharedMediaData[0].messages.isEmpty()) { showFloatingDateView(); } - if (dy != 0 && (mediaPage.selectedType == 0 || mediaPage.selectedType == TAB_STORIES || mediaPage.selectedType == TAB_ARCHIVED_STORIES)) { + if (dy != 0 && (mediaPage.selectedType == TAB_PHOTOVIDEO || mediaPage.selectedType == TAB_STORIES || mediaPage.selectedType == TAB_ARCHIVED_STORIES)) { showFastScrollHint(mediaPage, sharedMediaData, true); } mediaPage.listView.checkSection(true); @@ -2482,7 +2670,7 @@ public boolean onItemClick(View view, int position, float x, float y) { if (photoVideoChangeColumnsAnimation) { return false; } - if (isActionModeShowed) { + if (isActionModeShowed && mediaPage.selectedType != TAB_SAVED_DIALOGS) { mediaPage.listView.clickItem(view, position); return true; } @@ -2524,6 +2712,9 @@ public boolean onItemClick(View view, int position, float x, float y) { } else if (mediaPage.selectedType == TAB_RECOMMENDED_CHANNELS) { channelRecommendationsAdapter.openPreview(position); return true; + } else if (mediaPage.selectedType == TAB_SAVED_DIALOGS) { + savedDialogsAdapter.select(view); + return true; } return false; } @@ -2661,6 +2852,10 @@ protected boolean includeStories() { return true; } + protected boolean includeSavedDialogs() { + return false; + } + protected int getInitialTab() { return 0; } @@ -2813,7 +3008,7 @@ public void showMediaCalendar(int page, boolean fromFastScroll) { } Bundle bundle = new Bundle(); bundle.putLong("dialog_id", dialog_id); - bundle.putInt("topic_id", topicId); + bundle.putLong("topic_id", topicId); int date = 0; if (fromFastScroll) { MediaPage mediaPage = getMediaPage(0); @@ -3376,13 +3571,13 @@ private void checkLoadMoreScroll(MediaPage mediaPage, RecyclerListView recyclerV return; } mediaPage.lastCheckScrollTime = currentTime; - if (searching && searchWas || mediaPage.selectedType == TAB_GROUPUSERS) { + if (searching && searchWas && mediaPage.selectedType != TAB_SAVED_DIALOGS || mediaPage.selectedType == TAB_GROUPUSERS) { return; } int firstVisibleItem = layoutManager.findFirstVisibleItemPosition(); int visibleItemCount = firstVisibleItem == RecyclerView.NO_POSITION ? 0 : Math.abs(layoutManager.findLastVisibleItemPosition() - firstVisibleItem) + 1; - int totalItemCount = recyclerView.getAdapter().getItemCount(); - if (mediaPage.selectedType == 0 || mediaPage.selectedType == 1 || mediaPage.selectedType == 2 || mediaPage.selectedType == 4) { + int totalItemCount = recyclerView.getAdapter() == null ? 0 : recyclerView.getAdapter().getItemCount(); + if (mediaPage.selectedType == TAB_PHOTOVIDEO || mediaPage.selectedType == TAB_FILES || mediaPage.selectedType == TAB_VOICE || mediaPage.selectedType == TAB_AUDIO) { int type = mediaPage.selectedType; totalItemCount = sharedMediaData[type].getStartOffset() + sharedMediaData[type].messages.size(); if (sharedMediaData[type].fastScrollDataLoaded && sharedMediaData[type].fastScrollPeriods.size() > 2 && mediaPage.selectedType == 0 && sharedMediaData[type].messages.size() != 0) { @@ -3423,7 +3618,23 @@ private void checkLoadMoreScroll(MediaPage mediaPage, RecyclerListView recyclerV commonGroupsAdapter.getChats(commonGroupsAdapter.chats.get(commonGroupsAdapter.chats.size() - 1).id, 100); } } - } else if (mediaPage.selectedType != TAB_RECOMMENDED_CHANNELS) { + } else if (mediaPage.selectedType == TAB_SAVED_DIALOGS) { + int lastVisiblePosition = -1; + for (int i = 0; i < mediaPage.listView.getChildCount(); ++i) { + View child = mediaPage.listView.getChildAt(i); + int position = mediaPage.listView.getChildAdapterPosition(child); + lastVisiblePosition = Math.max(position, lastVisiblePosition); + } + if (mediaPage.listView.getAdapter() == savedMessagesSearchAdapter) { + if (lastVisiblePosition + 1 >= savedMessagesSearchAdapter.dialogs.size() + savedMessagesSearchAdapter.messages.size()) { + savedMessagesSearchAdapter.loadMore(); + } + return; + } + if (lastVisiblePosition + 1 >= profileActivity.getMessagesController().getSavedMessagesController().getLoadedCount()) { + profileActivity.getMessagesController().getSavedMessagesController().loadDialogs(); + } + } else if (mediaPage.selectedType != TAB_RECOMMENDED_CHANNELS && mediaPage.selectedType != TAB_SAVED_MESSAGES) { final int threshold; if (mediaPage.selectedType == 0) { threshold = 3; @@ -3469,9 +3680,9 @@ private void checkLoadMoreScroll(MediaPage mediaPage, RecyclerListView recyclerV if (firstVisibleItem - startOffset < threshold + 1 && !sharedMediaData[mediaPage.selectedType].loading && !sharedMediaData[mediaPage.selectedType].startReached && !sharedMediaData[mediaPage.selectedType].loadingAfterFastScroll) { loadFromStart(mediaPage.selectedType); } - if (mediaPages[0].listView == recyclerView && (mediaPages[0].selectedType == 0 || mediaPages[0].selectedType == 5) && firstVisibleItem != RecyclerView.NO_POSITION) { + if (mediaPages[0].listView == recyclerView && (mediaPages[0].selectedType == TAB_PHOTOVIDEO || mediaPages[0].selectedType == TAB_GIF) && firstVisibleItem != RecyclerView.NO_POSITION) { RecyclerListView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(firstVisibleItem); - if (holder != null && holder.getItemViewType() == 0) { + if (holder != null && (holder.getItemViewType() == VIEW_TYPE_PHOTOVIDEO || holder.getItemViewType() == VIEW_TYPE_GIF)) { if (holder.itemView instanceof SharedPhotoVideoCell) { SharedPhotoVideoCell cell = (SharedPhotoVideoCell) holder.itemView; MessageObject messageObject = cell.getMessageObject(0); @@ -3526,6 +3737,7 @@ public boolean isSearchItemVisible() { mediaPages[0].selectedType != TAB_VOICE && mediaPages[0].selectedType != TAB_GIF && mediaPages[0].selectedType != TAB_COMMON_GROUPS && + mediaPages[0].selectedType != TAB_SAVED_MESSAGES && mediaPages[0].selectedType != TAB_RECOMMENDED_CHANNELS ); } @@ -3586,6 +3798,7 @@ public void onDestroy() { profileActivity.getNotificationCenter().removeObserver(this, NotificationCenter.storiesListUpdated); profileActivity.getNotificationCenter().removeObserver(this, NotificationCenter.storiesUpdated); profileActivity.getNotificationCenter().removeObserver(this, NotificationCenter.channelRecommendationsLoaded); + profileActivity.getNotificationCenter().removeObserver(this, NotificationCenter.savedMessagesDialogsUpdate); if (storiesAdapter != null && storiesAdapter.storiesList != null) { storiesAdapter.destroy(); @@ -3738,6 +3951,53 @@ public void setCommonGroupsCount(int count) { public void onActionBarItemClick(View v, int id) { if (id == delete) { + if (getSelectedTab() == TAB_SAVED_DIALOGS) { + final SavedMessagesController controller = profileActivity.getMessagesController().getSavedMessagesController(); + final ArrayList selectedDialogs = new ArrayList<>(); + for (int i = 0; i < controller.allDialogs.size(); ++i) { + final long did = controller.allDialogs.get(i).dialogId; + if (savedDialogsAdapter.selectedDialogs.contains(did)) { + selectedDialogs.add(did); + } + } + String firstDialog = ""; + if (!selectedDialogs.isEmpty()) { + long did = selectedDialogs.get(0); + if (did < 0) { + TLRPC.Chat chat = profileActivity.getMessagesController().getChat(-did); + if (chat != null) { + firstDialog = chat.title; + } + } else if (did >= 0) { + TLRPC.User user = profileActivity.getMessagesController().getUser(did); + if (user != null) { + if (UserObject.isAnonymous(user)) { + firstDialog = LocaleController.getString(R.string.AnonymousForward); + } else { + firstDialog = UserObject.getUserName(user); + } + } + } + } + AlertDialog dialog = new AlertDialog.Builder(getContext(), resourcesProvider) + .setTitle(selectedDialogs.size() == 1 ? LocaleController.formatString(R.string.ClearHistoryTitleSingle, firstDialog) : LocaleController.formatPluralString("ClearHistoryTitleMultiple", selectedDialogs.size())) + .setMessage(selectedDialogs.size() == 1 ? LocaleController.formatString(R.string.ClearHistoryMessageSingle, firstDialog) : LocaleController.formatPluralString("ClearHistoryMessageMultiple", selectedDialogs.size())) + .setPositiveButton(LocaleController.getString(R.string.Remove), (di, w) -> { + for (int i = 0; i < selectedDialogs.size(); ++i) { + final long did = selectedDialogs.get(i); + profileActivity.getMessagesController().deleteSavedDialog(did); + } + closeActionMode(); + }) + .setNegativeButton(LocaleController.getString(R.string.Cancel), null) + .create(); + profileActivity.showDialog(dialog); + TextView button = (TextView) dialog.getButton(DialogInterface.BUTTON_POSITIVE); + if (button != null) { + button.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + } + return; + } TLRPC.Chat currentChat = null; TLRPC.User currentUser = null; TLRPC.EncryptedChat currentEncryptedChat = null; @@ -3748,7 +4008,7 @@ public void onActionBarItemClick(View v, int id) { } else { currentChat = profileActivity.getMessagesController().getChat(-dialog_id); } - AlertsCreator.createDeleteMessagesAlert(profileActivity, currentUser, currentChat, currentEncryptedChat, null, mergeDialogId, null, selectedFiles, null, false, 1, () -> { + AlertsCreator.createDeleteMessagesAlert(profileActivity, currentUser, currentChat, currentEncryptedChat, null, mergeDialogId, null, selectedFiles, null, false, false, 1, () -> { showActionMode(false); actionBar.closeSearchField(); cantDeleteMessagesCount = 0; @@ -3795,6 +4055,9 @@ public void onActionBarItemClick(View v, int id) { } cantDeleteMessagesCount = 0; showActionMode(false); + if (savedDialogsAdapter != null) { + savedDialogsAdapter.unselectAll(); + } if (dids.size() > 1 || dids.get(0).dialogId == profileActivity.getUserConfig().getClientUserId() || message != null) { updateRowsSelection(true); @@ -3872,6 +4135,27 @@ public void onActionBarItemClick(View v, int id) { args.putInt("message_id", messageObject.getId()); } profileActivity.presentFragment(chatActivity, false); + } else if (id == pin || id == unpin) { + final SavedMessagesController controller = profileActivity.getMessagesController().getSavedMessagesController(); + final ArrayList selectedDialogs = new ArrayList<>(); + for (int i = 0; i < controller.allDialogs.size(); ++i) { + final long did = controller.allDialogs.get(i).dialogId; + if (savedDialogsAdapter.selectedDialogs.contains(did)) { + selectedDialogs.add(did); + } + } + if (!controller.updatePinned(selectedDialogs, id == pin)) { + LimitReachedBottomSheet limitReachedBottomSheet = new LimitReachedBottomSheet(profileActivity, getContext(), LimitReachedBottomSheet.TYPE_PIN_SAVED_DIALOGS, profileActivity.getCurrentAccount(), null); + profileActivity.showDialog(limitReachedBottomSheet); + } else { + for (int i = 0; i < mediaPages.length; ++i) { + if (mediaPages[i].selectedType == TAB_SAVED_DIALOGS) { + mediaPages[i].layoutManager.scrollToPositionWithOffset(0, 0); + break; + } + } + } + closeActionMode(true); } } @@ -3999,6 +4283,9 @@ public boolean isCurrentTabFirst() { } public RecyclerListView getCurrentListView() { + if (mediaPages[0].selectedType == TAB_SAVED_MESSAGES && savedMessagesContainer != null) { + return savedMessagesContainer.chatActivity.getChatListView(); + } return mediaPages[0].listView; } @@ -4232,6 +4519,9 @@ public boolean closeActionMode(boolean uncheckAnimated) { cantDeleteMessagesCount = 0; showActionMode(false); updateRowsSelection(uncheckAnimated); + if (savedDialogsAdapter != null) { + savedDialogsAdapter.unselectAll(); + } return true; } else { return false; @@ -4659,6 +4949,13 @@ public void didReceivedNotification(int id, int account, Object... args) { updateTabs(true); checkCurrentTabValid(); } + } else if (id == NotificationCenter.savedMessagesDialogsUpdate) { + if (dialog_id == 0 || dialog_id == profileActivity.getUserConfig().getClientUserId()) { + savedDialogsAdapter.update(true); + updateTabs(true); + checkCurrentTabValid(); + onSelectedTabChanged(); + } } } @@ -4827,6 +5124,15 @@ public void onResume() { for (int a = 0; a < mediaPages.length; a++) { fixLayoutInternal(a); } + if (savedMessagesContainer != null) { + savedMessagesContainer.onResume(); + } + } + + public void onPause() { + if (savedMessagesContainer != null) { + savedMessagesContainer.onPause(); + } } public void onConfigurationChanged(android.content.res.Configuration newConfig) { @@ -4878,7 +5184,7 @@ public void setUserInfo(TLRPC.UserFull userInfo) { public void setChatUsers(ArrayList sortedUsers, TLRPC.ChatFull chatInfo) { for (int a = 0; a < mediaPages.length; a++) { if (mediaPages[a].selectedType == TAB_GROUPUSERS) { - if (mediaPages[a].listView.getAdapter().getItemCount() != 0 && profileActivity.getMessagesController().getStoriesController().hasLoadingStories()) { + if (mediaPages[a].listView.getAdapter() != null && mediaPages[a].listView.getAdapter().getItemCount() != 0 && profileActivity.getMessagesController().getStoriesController().hasLoadingStories()) { return; } } @@ -4889,7 +5195,7 @@ public void setChatUsers(ArrayList sortedUsers, TLRPC.ChatFull chatInfo } updateTabs(true); for (int a = 0; a < mediaPages.length; a++) { - if (mediaPages[a].selectedType == TAB_GROUPUSERS) { + if (mediaPages[a].selectedType == TAB_GROUPUSERS && mediaPages[a].listView.getAdapter() != null) { mediaPages[a].listView.getAdapter().notifyDataSetChanged(); } } @@ -4935,6 +5241,8 @@ private void updateRowsSelection(boolean animated) { ((SharedAudioCell) child).setChecked(false, animated); } else if (child instanceof ContextLinkCell) { ((ContextLinkCell) child).setChecked(false, animated); + } else if (child instanceof DialogCell) { + ((DialogCell) child).setChecked(false, animated); } } } @@ -4952,6 +5260,8 @@ private void updateTabs(boolean animated) { animated = false; } boolean hasRecommendations = false; + boolean hasSavedDialogs = false; + boolean hasSavedMessages = savedMessagesContainer != null && sharedMediaPreloader != null && sharedMediaPreloader.hasSavedMessages; int changed = 0; if (((DialogObject.isUserDialog(dialog_id) || DialogObject.isChatDialog(dialog_id)) && !DialogObject.isEncryptedDialog(dialog_id) && (userInfo != null && userInfo.stories_pinned_available || info != null && info.stories_pinned_available || isStoriesView()) && includeStories()) != scrollSlidingTextTabStrip.hasTab(TAB_STORIES)) { changed++; @@ -4991,6 +5301,13 @@ private void updateTabs(boolean animated) { if (hasRecommendations != scrollSlidingTextTabStrip.hasTab(TAB_RECOMMENDED_CHANNELS)) { changed++; } + hasSavedDialogs = includeSavedDialogs() && !profileActivity.getMessagesController().getSavedMessagesController().unsupported && profileActivity.getMessagesController().getSavedMessagesController().getAllCount() > 0; + if (hasSavedDialogs != scrollSlidingTextTabStrip.hasTab(TAB_SAVED_DIALOGS)) { + changed++; + } + if (hasSavedMessages != scrollSlidingTextTabStrip.hasTab(TAB_SAVED_MESSAGES)) { + changed++; + } } if (changed > 0) { if (animated && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { @@ -5050,6 +5367,11 @@ public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues sta } } if (!isStoriesView()) { + if (hasSavedDialogs) { + if (!scrollSlidingTextTabStrip.hasTab(TAB_SAVED_DIALOGS)) { + scrollSlidingTextTabStrip.addTextTab(TAB_SAVED_DIALOGS, LocaleController.getString(R.string.SavedDialogsTab), idToView); + } + } if (chatUsersAdapter.chatInfo != null) { if (!scrollSlidingTextTabStrip.hasTab(TAB_GROUPUSERS)) { scrollSlidingTextTabStrip.addTextTab(TAB_GROUPUSERS, LocaleController.getString("GroupMembers", R.string.GroupMembers), idToView); @@ -5107,6 +5429,12 @@ public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues sta scrollSlidingTextTabStrip.addTextTab(TAB_RECOMMENDED_CHANNELS, LocaleController.getString(R.string.SimilarChannelsTab), idToView); } } + if (hasSavedMessages) { + if (!scrollSlidingTextTabStrip.hasTab(TAB_SAVED_MESSAGES)) { + scrollSlidingTextTabStrip.addTextTab(TAB_SAVED_MESSAGES, LocaleController.getString(R.string.SavedMessagesTab), idToView); + } + MessagesController.getGlobalMainSettings().edit().putInt("savedhint", 3).apply(); + } } } int id = scrollSlidingTextTabStrip.getCurrentTabId(); @@ -5149,15 +5477,19 @@ private void switchToCurrentSelectedMode(boolean animated) { RecyclerView.Adapter currentAdapter = mediaPages[a].listView.getAdapter(); RecyclerView.RecycledViewPool viewPool = null; if (searching && searchWas) { + if (mediaPages[a].searchViewPool == null) { + mediaPages[a].searchViewPool = new RecyclerView.RecycledViewPool(); + } + viewPool = mediaPages[a].searchViewPool; if (animated) { - if (mediaPages[a].selectedType == 0 || mediaPages[a].selectedType == 2 || mediaPages[a].selectedType == 5 || mediaPages[a].selectedType == 6 || mediaPages[a].selectedType == 7 && !delegate.canSearchMembers()) { + if (mediaPages[a].selectedType == TAB_PHOTOVIDEO || mediaPages[a].selectedType == TAB_VOICE || mediaPages[a].selectedType == TAB_GIF || mediaPages[a].selectedType == TAB_COMMON_GROUPS || mediaPages[a].selectedType == TAB_GROUPUSERS && !delegate.canSearchMembers()) { searching = false; searchWas = false; switchToCurrentSelectedMode(true); return; } else { String text = searchItem.getSearchField().getText().toString(); - if (mediaPages[a].selectedType == 1) { + if (mediaPages[a].selectedType == TAB_FILES) { if (documentsSearchAdapter != null) { documentsSearchAdapter.search(text, false); if (currentAdapter != documentsSearchAdapter) { @@ -5165,7 +5497,7 @@ private void switchToCurrentSelectedMode(boolean animated) { mediaPages[a].listView.setAdapter(documentsSearchAdapter); } } - } else if (mediaPages[a].selectedType == 3) { + } else if (mediaPages[a].selectedType == TAB_LINKS) { if (linksSearchAdapter != null) { linksSearchAdapter.search(text, false); if (currentAdapter != linksSearchAdapter) { @@ -5173,7 +5505,7 @@ private void switchToCurrentSelectedMode(boolean animated) { mediaPages[a].listView.setAdapter(linksSearchAdapter); } } - } else if (mediaPages[a].selectedType == 4) { + } else if (mediaPages[a].selectedType == TAB_AUDIO) { if (audioSearchAdapter != null) { audioSearchAdapter.search(text, false); if (currentAdapter != audioSearchAdapter) { @@ -5181,7 +5513,7 @@ private void switchToCurrentSelectedMode(boolean animated) { mediaPages[a].listView.setAdapter(audioSearchAdapter); } } - } else if (mediaPages[a].selectedType == 7) { + } else if (mediaPages[a].selectedType == TAB_GROUPUSERS) { if (groupUsersSearchAdapter != null) { groupUsersSearchAdapter.search(text, false); if (currentAdapter != groupUsersSearchAdapter) { @@ -5189,38 +5521,56 @@ private void switchToCurrentSelectedMode(boolean animated) { mediaPages[a].listView.setAdapter(groupUsersSearchAdapter); } } + } else if (mediaPages[a].selectedType == TAB_SAVED_DIALOGS) { + if (savedMessagesSearchAdapter != null) { + savedMessagesSearchAdapter.search(text); + if (currentAdapter != savedMessagesSearchAdapter) { + recycleAdapter(currentAdapter); + mediaPages[a].listView.setAdapter(savedMessagesSearchAdapter); + } + } } } } else { if (mediaPages[a].listView != null) { - if (mediaPages[a].selectedType == 1) { + if (mediaPages[a].selectedType == TAB_FILES) { if (currentAdapter != documentsSearchAdapter) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(documentsSearchAdapter); } documentsSearchAdapter.notifyDataSetChanged(); - } else if (mediaPages[a].selectedType == 3) { + } else if (mediaPages[a].selectedType == TAB_LINKS) { if (currentAdapter != linksSearchAdapter) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(linksSearchAdapter); } linksSearchAdapter.notifyDataSetChanged(); - } else if (mediaPages[a].selectedType == 4) { + } else if (mediaPages[a].selectedType == TAB_AUDIO) { if (currentAdapter != audioSearchAdapter) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(audioSearchAdapter); } audioSearchAdapter.notifyDataSetChanged(); - } else if (mediaPages[a].selectedType == 7) { + } else if (mediaPages[a].selectedType == TAB_GROUPUSERS) { if (currentAdapter != groupUsersSearchAdapter) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(groupUsersSearchAdapter); } groupUsersSearchAdapter.notifyDataSetChanged(); + } else if (mediaPages[a].selectedType == TAB_SAVED_DIALOGS) { + if (currentAdapter != savedMessagesSearchAdapter) { + recycleAdapter(currentAdapter); + mediaPages[a].listView.setAdapter(savedMessagesSearchAdapter); + } + savedMessagesSearchAdapter.notifyDataSetChanged(); } } } } else { + if (mediaPages[a].viewPool == null) { + mediaPages[a].viewPool = new RecyclerView.RecycledViewPool(); + } + viewPool = mediaPages[a].viewPool; mediaPages[a].listView.setPinnedHeaderShadowDrawable(null); mediaPages[a].listView.setPadding( mediaPages[a].listView.getPaddingLeft(), @@ -5305,8 +5655,37 @@ private void switchToCurrentSelectedMode(boolean animated) { recycleAdapter(currentAdapter); mediaPages[a].listView.setAdapter(channelRecommendationsAdapter); } + } else if (mediaPages[a].selectedType == TAB_SAVED_DIALOGS) { + if (currentAdapter != savedDialogsAdapter) { + recycleAdapter(currentAdapter); + mediaPages[a].listView.setAdapter(savedDialogsAdapter); + savedDialogsAdapter.itemTouchHelper.attachToRecyclerView(savedDialogsAdapter.attachedToRecyclerView = mediaPages[a].listView); + } + viewPool = savedDialogsAdapter.viewPool; + } else if (mediaPages[a].selectedType == TAB_SAVED_MESSAGES) { + if (currentAdapter != null) { + recycleAdapter(currentAdapter); + mediaPages[a].listView.setAdapter(null); + } + if (savedMessagesContainer.getParent() != mediaPages[a]) { + if (savedMessagesContainer.getParent() instanceof ViewGroup) { + ((ViewGroup) savedMessagesContainer.getParent()).removeView(savedMessagesContainer); + } + mediaPages[a].addView(savedMessagesContainer); + } + } + if (mediaPages[a].selectedType == TAB_SAVED_DIALOGS) { + mediaPages[a].listView.setItemAnimator(mediaPages[a].itemAnimator); + } else { + mediaPages[a].listView.setItemAnimator(null); + if (savedDialogsAdapter != null && mediaPages[a].listView == savedDialogsAdapter.attachedToRecyclerView) { + savedDialogsAdapter.itemTouchHelper.attachToRecyclerView(savedDialogsAdapter.attachedToRecyclerView = null); + } + } + if (savedMessagesContainer != null && mediaPages[a].selectedType != TAB_SAVED_MESSAGES && savedMessagesContainer.getParent() == mediaPages[a]) { + mediaPages[a].removeView(savedMessagesContainer); } - if (mediaPages[a].selectedType == TAB_PHOTOVIDEO || mediaPages[a].selectedType == TAB_STORIES || mediaPages[a].selectedType == TAB_ARCHIVED_STORIES || mediaPages[a].selectedType == TAB_VOICE || mediaPages[a].selectedType == TAB_GIF || mediaPages[a].selectedType == TAB_COMMON_GROUPS || mediaPages[a].selectedType == TAB_GROUPUSERS && !delegate.canSearchMembers() || mediaPages[a].selectedType == TAB_RECOMMENDED_CHANNELS) { + if (mediaPages[a].selectedType == TAB_PHOTOVIDEO || mediaPages[a].selectedType == TAB_STORIES || mediaPages[a].selectedType == TAB_ARCHIVED_STORIES || mediaPages[a].selectedType == TAB_VOICE || mediaPages[a].selectedType == TAB_GIF || mediaPages[a].selectedType == TAB_COMMON_GROUPS || mediaPages[a].selectedType == TAB_GROUPUSERS && !delegate.canSearchMembers() || mediaPages[a].selectedType == TAB_RECOMMENDED_CHANNELS || mediaPages[a].selectedType == TAB_SAVED_MESSAGES) { if (animated) { searchItemState = 2; } else { @@ -5355,6 +5734,10 @@ private void switchToCurrentSelectedMode(boolean animated) { fastScrollVisible = storiesList != null && storiesList.getCount() > 0; } else if (mediaPages[a].selectedType == TAB_RECOMMENDED_CHANNELS) { + } else if (mediaPages[a].selectedType == TAB_SAVED_DIALOGS) { + + } else if (mediaPages[a].selectedType == TAB_SAVED_MESSAGES) { + } else { if (!sharedMediaData[mediaPages[a].selectedType].loading && !sharedMediaData[mediaPages[a].selectedType].endReached[0] && sharedMediaData[mediaPages[a].selectedType].messages.isEmpty()) { sharedMediaData[mediaPages[a].selectedType].loading = true; @@ -5389,8 +5772,10 @@ private void switchToCurrentSelectedMode(boolean animated) { updateFastScrollVisibility(mediaPages[a], false); mediaPages[a].layoutManager.setSpanCount(spanCount); mediaPages[a].listView.invalidateItemDecorations(); - mediaPages[a].listView.setRecycledViewPool(viewPool); - mediaPages[a].animationSupportingListView.setRecycledViewPool(viewPool); + if (viewPool != null) { + mediaPages[a].listView.setRecycledViewPool(viewPool); + mediaPages[a].animationSupportingListView.setRecycledViewPool(viewPool); + } if (searchItemState == 2 && actionBar.isSearchFieldVisible()) { ignoreSearchCollapse = true; @@ -5479,6 +5864,15 @@ private void onItemClick(int index, View view, MessageObject message, int a, int if (gotoItem != null) { gotoItem.setVisibility(selectedFiles[0].size() == 1 ? View.VISIBLE : View.GONE); } + if (forwardItem != null) { + forwardItem.setVisibility(View.VISIBLE); + } + if (pinItem != null) { + pinItem.setVisibility(View.GONE); + } + if (unpinItem != null) { + unpinItem.setVisibility(View.GONE); + } } scrolling = false; if (view instanceof SharedDocumentCell) { @@ -5718,18 +6112,18 @@ public View getSectionHeaderView(int section, View view) { public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { - case 0: + case VIEW_TYPE_LINK_DATE: view = new GraySectionCell(mContext, resourcesProvider); break; - case 1: + case VIEW_TYPE_LINK: view = new SharedLinkCell(mContext, SharedLinkCell.VIEW_TYPE_DEFAULT, resourcesProvider); ((SharedLinkCell) view).setDelegate(sharedLinkCellDelegate); break; - case 3: + case VIEW_TYPE_LINK_EMPTY: View emptyStubView = createEmptyStubView(mContext, 3, dialog_id, resourcesProvider); emptyStubView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); return new RecyclerListView.Holder(emptyStubView); - case 2: + case VIEW_TYPE_LINK_LOADING: default: FlickerLoadingView flickerLoadingView = new FlickerLoadingView(mContext, resourcesProvider); flickerLoadingView.setIsSingleCell(true); @@ -5744,19 +6138,24 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType @Override public void onBindViewHolder(int section, int position, RecyclerView.ViewHolder holder) { - if (holder.getItemViewType() != 2 && holder.getItemViewType() != 3) { + if (holder.getItemViewType() != VIEW_TYPE_LINK_LOADING && holder.getItemViewType() != VIEW_TYPE_LINK_EMPTY) { String name = sharedMediaData[3].sections.get(section); ArrayList messageObjects = sharedMediaData[3].sectionArrays.get(name); switch (holder.getItemViewType()) { - case 0: { + case VIEW_TYPE_LINK_DATE: { MessageObject messageObject = messageObjects.get(0); - ((GraySectionCell) holder.itemView).setText(LocaleController.formatSectionDate(messageObject.messageOwner.date)); + if (holder.itemView instanceof GraySectionCell) { + ((GraySectionCell) holder.itemView).setText(LocaleController.formatSectionDate(messageObject.messageOwner.date)); + } break; } - case 1: { + case VIEW_TYPE_LINK: { if (section != 0) { position--; } + if (!(holder.itemView instanceof SharedLinkCell) || position < 0 || position >= messageObjects.size()) { + return; + } SharedLinkCell sharedLinkCell = (SharedLinkCell) holder.itemView; MessageObject messageObject = messageObjects.get(position); sharedLinkCell.setLink(messageObject, position != messageObjects.size() - 1 || section == sharedMediaData[3].sections.size() - 1 && sharedMediaData[3].loading); @@ -5774,16 +6173,16 @@ public void onBindViewHolder(int section, int position, RecyclerView.ViewHolder @Override public int getItemViewType(int section, int position) { if (sharedMediaData[3].sections.size() == 0 && !sharedMediaData[3].loading) { - return 3; + return VIEW_TYPE_LINK_EMPTY; } if (section < sharedMediaData[3].sections.size()) { if (section != 0 && position == 0) { - return 0; + return VIEW_TYPE_LINK_DATE; } else { - return 1; + return VIEW_TYPE_LINK; } } - return 2; + return VIEW_TYPE_LINK_LOADING; } @Override @@ -5844,12 +6243,12 @@ public int getItemCount() { public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { - case 1: + case VIEW_TYPE_DOCUMENT: SharedDocumentCell cell = new SharedDocumentCell(mContext, SharedDocumentCell.VIEW_TYPE_DEFAULT, resourcesProvider); cell.setGlobalGradientView(globalGradientView); view = cell; break; - case 2: + case VIEW_TYPE_DOCUMENT_LOADING: FlickerLoadingView flickerLoadingView = new FlickerLoadingView(mContext, resourcesProvider); view = flickerLoadingView; if (currentType == 2) { @@ -5861,11 +6260,11 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType flickerLoadingView.setIsSingleCell(true); flickerLoadingView.setGlobalGradientView(globalGradientView); break; - case 4: + case VIEW_TYPE_DOCUMENT_EMPTY: View emptyStubView = createEmptyStubView(mContext, currentType, dialog_id, resourcesProvider); emptyStubView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); return new RecyclerListView.Holder(emptyStubView); - case 3: + case VIEW_TYPE_AUDIO: default: if (currentType == MediaDataController.MEDIA_MUSIC && !audioCellCache.isEmpty()) { view = audioCellCache.get(0); @@ -5904,7 +6303,8 @@ public boolean needPlayMessage(MessageObject messageObject) { public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { ArrayList messageObjects = sharedMediaData[currentType].messages; switch (holder.getItemViewType()) { - case 1: { + case VIEW_TYPE_DOCUMENT: { + if (!(holder.itemView instanceof SharedDocumentCell)) return; SharedDocumentCell sharedDocumentCell = (SharedDocumentCell) holder.itemView; MessageObject messageObject = messageObjects.get(position - sharedMediaData[currentType].startOffset); sharedDocumentCell.setDocument(messageObject, position != messageObjects.size() - 1); @@ -5915,7 +6315,8 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi } break; } - case 3: { + case VIEW_TYPE_AUDIO: { + if (!(holder.itemView instanceof SharedAudioCell)) return; SharedAudioCell sharedAudioCell = (SharedAudioCell) holder.itemView; MessageObject messageObject = messageObjects.get(position - sharedMediaData[currentType].startOffset); sharedAudioCell.setMessageObject(messageObject, position != messageObjects.size() - 1); @@ -5933,16 +6334,16 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi @Override public int getItemViewType(int position) { if (sharedMediaData[currentType].sections.size() == 0 && !sharedMediaData[currentType].loading) { - return 4; + return VIEW_TYPE_DOCUMENT_EMPTY; } if (position >= sharedMediaData[currentType].startOffset && position < sharedMediaData[currentType].startOffset + sharedMediaData[currentType].messages.size()) { if (currentType == 2 || currentType == 4) { - return 3; + return VIEW_TYPE_AUDIO; } else { - return 1; + return VIEW_TYPE_DOCUMENT; } } - return 2; + return VIEW_TYPE_DOCUMENT_LOADING; } @Override @@ -6106,6 +6507,31 @@ public void requestLayout() { } } + public static final int VIEW_TYPE_PHOTOVIDEO = 0; + public static final int VIEW_TYPE_PHOTOVIDEO_LOADING = 2; + public static final int VIEW_TYPE_LINK_DATE = 3; + public static final int VIEW_TYPE_LINK = 4; + public static final int VIEW_TYPE_LINK_EMPTY = 5; + public static final int VIEW_TYPE_LINK_LOADING = 6; + public static final int VIEW_TYPE_DOCUMENT = 7; + public static final int VIEW_TYPE_DOCUMENT_LOADING = 8; + public static final int VIEW_TYPE_DOCUMENT_EMPTY = 9; + public static final int VIEW_TYPE_AUDIO = 10; + public static final int VIEW_TYPE_GIF_LOADING = 11; + public static final int VIEW_TYPE_GIF = 12; + public static final int VIEW_TYPE_SAVED_DIALOG = 13; + public static final int VIEW_TYPE_GROUP = 14; + public static final int VIEW_TYPE_GROUP_EMPTY = 15; + public static final int VIEW_TYPE_GROUP_LOADING = 16; + public static final int VIEW_TYPE_SIMILAR_CHANNEL = 17; + public static final int VIEW_TYPE_SIMILAR_CHANNEL_BLOCK = 18; + public static final int VIEW_TYPE_STORY = 19; + public static final int VIEW_TYPE_GROUPUSER_EMPTY = 20; + public static final int VIEW_TYPE_GROUPUSER = 21; + public static final int VIEW_TYPE_SEARCH_GROUPUSER = 22; + public static final int VIEW_TYPE_SEARCH_SAVED_DIALOG = 23; + public static final int VIEW_TYPE_SEARCH_DOCUMENT = 24; + private class SharedPhotoVideoAdapter extends RecyclerListView.FastScrollAdapter { protected Context mContext; @@ -6172,7 +6598,8 @@ public boolean isEnabled(RecyclerView.ViewHolder holder) { public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { - case 0: + case VIEW_TYPE_PHOTOVIDEO: + case VIEW_TYPE_STORY: if (sharedResources == null) { sharedResources = new SharedPhotoVideoCell2.SharedResources(parent.getContext(), resourcesProvider); } @@ -6184,7 +6611,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType view = cell; break; default: - case 2: + case VIEW_TYPE_PHOTOVIDEO_LOADING: View emptyStubView = createEmptyStubView(mContext, 0, dialog_id, resourcesProvider); emptyStubView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); return new RecyclerListView.Holder(emptyStubView); @@ -6195,9 +6622,10 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { - if (holder.getItemViewType() == 0) { + if (holder.getItemViewType() == VIEW_TYPE_PHOTOVIDEO) { ArrayList messageObjects = sharedMediaData[0].getMessages(); int index = position - sharedMediaData[0].getStartOffset(); + if (!(holder.itemView instanceof SharedPhotoVideoCell2)) return; SharedPhotoVideoCell2 cell = (SharedPhotoVideoCell2) holder.itemView; int oldMessageId = cell.getMessageId(); @@ -6229,13 +6657,13 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { @Override public int getItemViewType(int position) { if (!inFastScrollMode && sharedMediaData[0].getMessages().size() == 0 && !sharedMediaData[0].loading && sharedMediaData[0].startReached) { - return 2; + return VIEW_TYPE_PHOTOVIDEO_LOADING; } int count = sharedMediaData[0].getStartOffset() + sharedMediaData[0].getMessages().size(); if (position - sharedMediaData[0].getStartOffset() >= 0 && position < count) { - return 0; + return VIEW_TYPE_PHOTOVIDEO; } - return 0; + return VIEW_TYPE_PHOTOVIDEO; } @Override @@ -6636,7 +7064,7 @@ private void updateSearchResults(final ArrayList documents) { @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { - return holder.getItemViewType() != searchResult.size() + globalSearch.size(); + return 0 != searchResult.size() + globalSearch.size(); } @Override @@ -6701,6 +7129,7 @@ public boolean needPlayMessage(MessageObject messageObject) { @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (currentType == 1) { + if (!(holder.itemView instanceof SharedDocumentCell)) return; SharedDocumentCell sharedDocumentCell = (SharedDocumentCell) holder.itemView; MessageObject messageObject = getItem(position); sharedDocumentCell.setDocument(messageObject, position != getItemCount() - 1); @@ -6710,6 +7139,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { sharedDocumentCell.setChecked(false, !scrolling); } } else if (currentType == 3) { + if (!(holder.itemView instanceof SharedLinkCell)) return; SharedLinkCell sharedLinkCell = (SharedLinkCell) holder.itemView; MessageObject messageObject = getItem(position); sharedLinkCell.setLink(messageObject, position != getItemCount() - 1); @@ -6719,6 +7149,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { sharedLinkCell.setChecked(false, !scrolling); } } else if (currentType == 4) { + if (!(holder.itemView instanceof SharedAudioCell)) return; SharedAudioCell sharedAudioCell = (SharedAudioCell) holder.itemView; MessageObject messageObject = getItem(position); sharedAudioCell.setMessageObject(messageObject, position != getItemCount() - 1); @@ -6732,7 +7163,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { @Override public int getItemViewType(int i) { - return 0; + return VIEW_TYPE_SEARCH_DOCUMENT; } } @@ -6768,14 +7199,14 @@ public long getItemId(int i) { @Override public int getItemViewType(int position) { if (sharedMediaData[5].messages.size() == 0 && !sharedMediaData[5].loading) { - return 1; + return VIEW_TYPE_GIF_LOADING; } - return 0; + return VIEW_TYPE_GIF; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - if (viewType == 1) { + if (viewType == VIEW_TYPE_GIF_LOADING) { View emptyStubView = createEmptyStubView(mContext, 5, dialog_id, resourcesProvider); emptyStubView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); return new RecyclerListView.Holder(emptyStubView); @@ -6787,10 +7218,11 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { - if (holder.getItemViewType() != 1) { + if (holder.getItemViewType() == VIEW_TYPE_GIF) { MessageObject messageObject = sharedMediaData[5].messages.get(position); TLRPC.Document document = messageObject.getDocument(); if (document != null) { + if (!(holder.itemView instanceof ContextLinkCell)) return; ContextLinkCell cell = (ContextLinkCell) holder.itemView; cell.setGif(document, messageObject, messageObject.messageOwner.date, false); if (isActionModeShowed) { @@ -6818,6 +7250,405 @@ public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { } } + private class SavedDialogsAdapter extends RecyclerListView.SelectionAdapter { + private final Context mContext; + private final SavedMessagesController controller; + + private final ArrayList oldDialogs = new ArrayList<>(); + private final ArrayList dialogs = new ArrayList<>(); + private boolean orderChanged; + private Runnable notifyOrderUpdate = () -> { + if (!orderChanged) { + return; + } + orderChanged = false; + ArrayList pinnedOrder = new ArrayList<>(); + for (int i = 0; i < dialogs.size(); ++i) { + if (dialogs.get(i).pinned) { + pinnedOrder.add(dialogs.get(i).dialogId); + } + } + profileActivity.getMessagesController().getSavedMessagesController().updatePinnedOrder(pinnedOrder); + }; + + public final RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool(); + public RecyclerListView attachedToRecyclerView; + public final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() { + + @Override + public boolean isLongPressDragEnabled() { + return true; + } + @Override + public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) { + if (!isActionModeShowed) { + return makeMovementFlags(0, 0); + } + SavedMessagesController.SavedDialog d = getDialog(viewHolder); + if (d != null && d.pinned) { + return makeMovementFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0); + } + return makeMovementFlags(0, 0); + } + + private SavedMessagesController.SavedDialog getDialog(RecyclerView.ViewHolder holder) { + if (holder == null) { + return null; + } + int position = holder.getAdapterPosition(); + if (position < 0 || position >= dialogs.size()) { + return null; + } + return dialogs.get(position); + } + + @Override + public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { + if (viewHolder != null && attachedToRecyclerView != null) { + attachedToRecyclerView.hideSelector(false); + } + if (actionState == ItemTouchHelper.ACTION_STATE_IDLE) { + AndroidUtilities.cancelRunOnUIThread(notifyOrderUpdate); + AndroidUtilities.runOnUIThread(notifyOrderUpdate, 300); + } + super.onSelectedChanged(viewHolder, actionState); + } + + @Override + public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder sourceHolder, @NonNull RecyclerView.ViewHolder targetHolder) { + if (!isActionModeShowed) { + return false; + } + SavedMessagesController.SavedDialog source = getDialog(sourceHolder); + SavedMessagesController.SavedDialog target = getDialog(targetHolder); + if (source != null && target != null && source.pinned && target.pinned) { + int fromPosition = sourceHolder.getAdapterPosition(); + int toPosition = targetHolder.getAdapterPosition(); + dialogs.remove(fromPosition); + dialogs.add(toPosition, source); + notifyItemMoved(fromPosition, toPosition); + orderChanged = true; + return true; + } + return false; + } + + @Override + public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) { + + } + + @Override + public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + super.clearView(recyclerView, viewHolder); + viewHolder.itemView.setPressed(false); + } + }); + + public SavedDialogsAdapter(Context context) { + mContext = context; + controller = profileActivity.getMessagesController().getSavedMessagesController(); + if (includeSavedDialogs()) { + controller.loadDialogs(); + } + setHasStableIds(true); + update(false); + } + + @Override + public long getItemId(int position) { + if (position < 0 || position >= dialogs.size()) return position; + return dialogs.get(position).dialogId; + } + + public void update(boolean notify) { + oldDialogs.clear(); + oldDialogs.addAll(dialogs); + dialogs.clear(); + dialogs.addAll(controller.allDialogs); + if (notify) { + notifyDataSetChanged(); + } + } + + public final HashSet selectedDialogs = new HashSet<>(); + + public void select(View view) { + if (!(view instanceof DialogCell)) return; + DialogCell dialogCell = (DialogCell) view; + long dialogId = dialogCell.getDialogId(); + SavedMessagesController.SavedDialog dialog = null; + for (int i = 0; i < dialogs.size(); ++i) { + if (dialogs.get(i).dialogId == dialogId) { + dialog = dialogs.get(i); + break; + } + } + if (dialog == null) return; + + if (selectedDialogs.contains(dialog.dialogId)) { + selectedDialogs.remove(dialog.dialogId); + if (selectedDialogs.size() <= 0 && isActionModeShowed) { + showActionMode(false); + } + } else { + selectedDialogs.add(dialog.dialogId); + if (selectedDialogs.size() > 0 && !isActionModeShowed) { + showActionMode(true); + if (gotoItem != null) { + gotoItem.setVisibility(View.GONE); + } + if (forwardItem != null) { + forwardItem.setVisibility(View.GONE); + } + } + } + selectedMessagesCountTextView.setNumber(selectedDialogs.size(), true); + boolean allSelectedPinned = selectedDialogs.size() > 0; + for (long did : selectedDialogs) { + for (int i = 0; i < dialogs.size(); ++i) { + SavedMessagesController.SavedDialog d = dialogs.get(i); + if (d.dialogId == did) { + if (!d.pinned) { + allSelectedPinned = false; + } + break; + } + } + if (!allSelectedPinned) break; + } + if (pinItem != null) { + pinItem.setVisibility(allSelectedPinned ? View.GONE : View.VISIBLE); + } + if (unpinItem != null) { + unpinItem.setVisibility(allSelectedPinned ? View.VISIBLE : View.GONE); + } + if (view instanceof DialogCell) { + ((DialogCell) view).setChecked(selectedDialogs.contains(dialog.dialogId), true); + } + } + + public void unselectAll() { + selectedDialogs.clear(); + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + DialogCell cell = new DialogCell(null, mContext, false, true) { + + @Override + public boolean isForumCell() { + return false; + } + @Override + public boolean getIsPinned() { + if (attachedToRecyclerView != null && attachedToRecyclerView.getAdapter() == SavedDialogsAdapter.this) { + int position = attachedToRecyclerView.getChildAdapterPosition(this); + if (position >= 0 && position < dialogs.size()) { + return dialogs.get(position).pinned; + } + } + return false; + } + }; + cell.setDialogCellDelegate(SharedMediaLayout.this); + cell.isSavedDialog = true; + cell.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); + return new RecyclerListView.Holder(cell); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + if (!(holder.itemView instanceof DialogCell)) return; + DialogCell cell = (DialogCell) holder.itemView; + SavedMessagesController.SavedDialog d = dialogs.get(position); + cell.setDialog(d.dialogId, d.message, d.getDate(), false, false); + cell.setChecked(selectedDialogs.contains(d.dialogId), false); + cell.useSeparator = position + 1 < getItemCount(); + } + + @Override + public int getItemViewType(int position) { + return VIEW_TYPE_SAVED_DIALOG; + } + + @Override + public int getItemCount() { + return dialogs.size(); + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return true; + } + } + + private class SavedMessagesSearchAdapter extends RecyclerListView.SelectionAdapter { + private final Context mContext; + private final int currentAccount; + public final ArrayList dialogs = new ArrayList<>(); + public final ArrayList messages = new ArrayList<>(); + public SavedMessagesSearchAdapter(Context context) { + mContext = context; + currentAccount = profileActivity.getCurrentAccount(); + setHasStableIds(true); + } + + private boolean loading; + private boolean endReached = false; + private int oldItemCounts = 0; + private int count = 0; + + private String lastQuery; + private int reqId = -1; + public void search(String query) { + if (TextUtils.equals(query, lastQuery)) { + return; + } + lastQuery = query; + if (reqId >= 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(reqId, true); + reqId = -1; + } + + messages.clear(); + count = 0; + endReached = false; + loading = true; + + dialogs.clear(); + dialogs.addAll(MessagesController.getInstance(currentAccount).getSavedMessagesController().searchDialogs(query)); + for (int a = 0; a < mediaPages.length; a++) { + if (mediaPages[a].selectedType == TAB_SAVED_DIALOGS) { + mediaPages[a].emptyView.showProgress(true, true); + } + } + notifyDataSetChanged(); + + AndroidUtilities.cancelRunOnUIThread(searchRunnable); + AndroidUtilities.runOnUIThread(searchRunnable, 600); + } + + public void loadMore() { + if (endReached || loading) return; + sendRequest(); + } + + private Runnable searchRunnable = this::sendRequest; + private void sendRequest() { + if (TextUtils.isEmpty(lastQuery)) { + loading = false; + return; + } + TLRPC.TL_messages_search req = new TLRPC.TL_messages_search(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(UserConfig.getInstance(currentAccount).getClientUserId()); + req.filter = new TLRPC.TL_inputMessagesFilterEmpty(); + req.q = lastQuery; + if (messages.size() > 0) { + MessageObject lastMessage = messages.get(messages.size() - 1); + req.offset_id = lastMessage.getId(); + } + req.limit = 10; + endReached = false; + reqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (!(res instanceof TLRPC.messages_Messages)) { + return; + } + TLRPC.messages_Messages r = (TLRPC.messages_Messages) res; + MessagesController.getInstance(currentAccount).putUsers(r.users, false); + MessagesController.getInstance(currentAccount).putChats(r.chats, false); + MessagesStorage.getInstance(currentAccount).putUsersAndChats(r.users, r.chats, true, true); + for (int i = 0; i < r.messages.size(); ++i) { + TLRPC.Message msg = r.messages.get(i); + messages.add(new MessageObject(currentAccount, msg, false, false)); + } + if (r instanceof TLRPC.TL_messages_messagesSlice) { + count = r.count; + endReached = messages.size() >= count; + } else if (r instanceof TLRPC.TL_messages_messages) { + count = messages.size(); + endReached = true; + } + + for (int a = 0; a < mediaPages.length; a++) { + if (mediaPages[a].selectedType == TAB_SAVED_DIALOGS) { + if (count == 0 && dialogs.isEmpty()) { + mediaPages[a].emptyView.title.setText(LocaleController.formatString("NoResultFoundFor", R.string.NoResultFoundFor, lastQuery)); + mediaPages[a].emptyView.showProgress(false, true); + } + } + } + oldItemCounts = count; + reqId = -1; + + notifyDataSetChanged(); + loading = false; + })); + } + + @Override + public long getItemId(int position) { + if (position < 0) return position; + if (position < dialogs.size()) { + return Objects.hash(1, dialogs.get(position).dialogId); + } + position -= dialogs.size(); + if (position < messages.size()) { + return Objects.hash(2, messages.get(position).getSavedDialogId(), messages.get(position).getId()); + } + return position; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + DialogCell cell = new DialogCell(null, mContext, false, true) { + @Override + public boolean isForumCell() { + return false; + } + }; + cell.setDialogCellDelegate(SharedMediaLayout.this); + cell.isSavedDialog = true; + cell.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); + return new RecyclerListView.Holder(cell); + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + if (position < 0) return; + if (!(holder.itemView instanceof DialogCell)) return; + DialogCell cell = (DialogCell) holder.itemView; + cell.useSeparator = position + 1 < getItemCount(); + if (position < dialogs.size()) { + final SavedMessagesController.SavedDialog d = dialogs.get(position); + cell.setDialog(d.dialogId, d.message, d.getDate(), false, false); + } else { + position -= dialogs.size(); + if (position < messages.size()) { + MessageObject msg = messages.get(position); + cell.setDialog(msg.getSavedDialogId(), msg, msg.messageOwner.date, false, false); + } + } + } + + @Override + public int getItemViewType(int position) { + return VIEW_TYPE_SEARCH_SAVED_DIALOG; + } + + @Override + public int getItemCount() { + return dialogs.size() + messages.size(); + } + + @Override + public boolean isEnabled(RecyclerView.ViewHolder holder) { + return true; + } + } + private class ChannelRecommendationsAdapter extends RecyclerListView.SelectionAdapter { private final Context mContext; @@ -6866,14 +7697,14 @@ public int getItemCount() { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; - if (viewType == 1) { + if (viewType == VIEW_TYPE_SIMILAR_CHANNEL_BLOCK) { MoreRecommendationsCell cell = new MoreRecommendationsCell(profileActivity == null ? UserConfig.selectedAccount : profileActivity.getCurrentAccount(), mContext, resourcesProvider, () -> { if (profileActivity != null) { profileActivity.presentFragment(new PremiumPreviewFragment("similar_channels")); } }); view = cell; - } else { // 0 + } else { view = new ProfileSearchCell(mContext, resourcesProvider); } view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); @@ -6929,9 +7760,11 @@ public void openPreview(int position) { @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ProfileSearchCell cell = null; - if (holder.getItemViewType() == 0) { + if (holder.getItemViewType() == VIEW_TYPE_SIMILAR_CHANNEL) { + if (!(holder.itemView instanceof ProfileSearchCell)) return; cell = (ProfileSearchCell) holder.itemView; - } else if (holder.getItemViewType() == 1) { + } else if (holder.getItemViewType() == VIEW_TYPE_SIMILAR_CHANNEL_BLOCK) { + if (!(holder.itemView instanceof MoreRecommendationsCell)) return; cell = ((MoreRecommendationsCell) holder.itemView).channelCell; } if (cell != null) { @@ -6944,9 +7777,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { @Override public int getItemViewType(int position) { if (more > 0 && position == getItemCount() - 1) { - return 1; + return VIEW_TYPE_SIMILAR_CHANNEL_BLOCK; } - return 0; + return VIEW_TYPE_SIMILAR_CHANNEL; } } @@ -7100,14 +7933,14 @@ public int getItemCount() { public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { - case 0: + case VIEW_TYPE_GROUP: view = new ProfileSearchCell(mContext, resourcesProvider); break; - case 2: + case VIEW_TYPE_GROUP_EMPTY: View emptyStubView = createEmptyStubView(mContext, 6, dialog_id, resourcesProvider); emptyStubView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); return new RecyclerListView.Holder(emptyStubView); - case 1: + case VIEW_TYPE_GROUP_LOADING: default: FlickerLoadingView flickerLoadingView = new FlickerLoadingView(mContext, resourcesProvider); flickerLoadingView.setIsSingleCell(true); @@ -7122,7 +7955,8 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { - if (holder.getItemViewType() == 0) { + if (holder.getItemViewType() == VIEW_TYPE_GROUP) { + if (!(holder.itemView instanceof ProfileSearchCell)) return; ProfileSearchCell cell = (ProfileSearchCell) holder.itemView; TLRPC.Chat chat = chats.get(position); cell.setData(chat, null, null, null, false, false); @@ -7133,12 +7967,12 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { @Override public int getItemViewType(int i) { if (chats.isEmpty() && !loading) { - return 2; + return VIEW_TYPE_GROUP_EMPTY; } if (i < chats.size()) { - return 0; + return VIEW_TYPE_GROUP; } else { - return 1; + return VIEW_TYPE_GROUP_LOADING; } } } @@ -7317,7 +8151,8 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { return; } int viewType = holder.getItemViewType(); - if (viewType == 0) { + if (viewType == VIEW_TYPE_STORY) { + if (!(holder.itemView instanceof SharedPhotoVideoCell2)) return; SharedPhotoVideoCell2 cell = (SharedPhotoVideoCell2) holder.itemView; cell.isStory = true; position -= getTopOffset(); @@ -7348,7 +8183,7 @@ public void load(boolean force) { @Override public int getItemViewType(int i) { - return 0; + return VIEW_TYPE_STORY; } @Override @@ -7398,7 +8233,7 @@ public int getItemCount() { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - if (viewType == 1) { + if (viewType == VIEW_TYPE_GROUPUSER_EMPTY) { View emptyStubView = createEmptyStubView(mContext, 7, dialog_id, resourcesProvider); emptyStubView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); return new RecyclerListView.Holder(emptyStubView); @@ -7452,9 +8287,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { @Override public int getItemViewType(int i) { if (chatInfo != null && chatInfo.participants.participants.isEmpty()) { - return 1; + return VIEW_TYPE_GROUPUSER_EMPTY; } - return 0; + return VIEW_TYPE_GROUPUSER; } } @@ -7634,7 +8469,7 @@ private void updateSearchResults(final ArrayList names, final Arra @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { - return holder.getItemViewType() != 1; + return true; } @Override @@ -7712,7 +8547,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { name.setSpan(new ForegroundColorSpan(getThemedColor(Theme.key_windowBackgroundWhiteBlueText4)), idx, idx + nameSearch.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } - + if (!(holder.itemView instanceof ManageChatUserCell)) return; ManageChatUserCell userCell = (ManageChatUserCell) holder.itemView; userCell.setTag(position); userCell.setData(user, name, null, false); @@ -7727,7 +8562,7 @@ public void onViewRecycled(RecyclerView.ViewHolder holder) { @Override public int getItemViewType(int i) { - return 0; + return VIEW_TYPE_SEARCH_GROUPUSER; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java index a49fb323f2..2899f17881 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StaticLayoutEx.java @@ -150,6 +150,26 @@ public static StaticLayout createStaticLayout(CharSequence source, TextPaint pai .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE); layout = builder.build(); + + boolean realWidthLarger = false; + for (int l = 0; l < layout.getLineCount(); ++l) { + if (layout.getLineRight(l) > outerWidth) { + realWidthLarger = true; + break; + } + } + if (realWidthLarger) { + builder = StaticLayout.Builder.obtain(source, 0, source.length(), paint, outerWidth) + .setAlignment(align) + .setLineSpacing(spacingAdd, spacingMult) + .setIncludePad(includePad) + .setEllipsize(null) + .setEllipsizedWidth(ellipsisWidth) + .setMaxLines(maxLines) + .setBreakStrategy(StaticLayout.BREAK_STRATEGY_SIMPLE) + .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE); + layout = builder.build(); + } } else { layout = new StaticLayout(source, paint, outerWidth, align, spacingMult, spacingAdd, includePad); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/StatusBadgeComponent.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/StatusBadgeComponent.java index 1db8830bd8..c3c7917b15 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/StatusBadgeComponent.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/StatusBadgeComponent.java @@ -16,7 +16,11 @@ public class StatusBadgeComponent { private Drawable verifiedDrawable; public StatusBadgeComponent(View parentView) { - statusDrawable = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(parentView, AndroidUtilities.dp(18)); + this(parentView, 18); + } + + public StatusBadgeComponent(View parentView, int sizeDp) { + statusDrawable = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(parentView, AndroidUtilities.dp(sizeDp)); } public Drawable updateDrawable(TLObject object, int colorFilter, boolean animated) { @@ -34,13 +38,13 @@ public Drawable updateDrawable(TLRPC.User user, TLRPC.Chat chat, int colorFilter statusDrawable.setColor(null); } else if (chat != null && DialogObject.getEmojiStatusDocumentId(chat.emoji_status) != 0) { statusDrawable.set(DialogObject.getEmojiStatusDocumentId(chat.emoji_status), animated); - statusDrawable.setColor(null); + statusDrawable.setColor(colorFilter); } else if (user != null && user.verified) { statusDrawable.set(verifiedDrawable = (verifiedDrawable == null ? new CombinedDrawable(Theme.dialogs_verifiedDrawable, Theme.dialogs_verifiedCheckDrawable) : verifiedDrawable), animated); statusDrawable.setColor(null); } else if (user != null && DialogObject.getEmojiStatusDocumentId(user.emoji_status) != 0) { statusDrawable.set(DialogObject.getEmojiStatusDocumentId(user.emoji_status), animated); - statusDrawable.setColor(null); + statusDrawable.setColor(colorFilter); } else if (user != null && user.premium) { statusDrawable.set(PremiumGradient.getInstance().premiumStarDrawableMini, animated); statusDrawable.setColor(colorFilter); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThanosEffect.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThanosEffect.java index 9297734708..a9a9afa9ce 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ThanosEffect.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ThanosEffect.java @@ -82,6 +82,9 @@ private static class ToSet { public final Bitmap bitmap; public final Matrix matrix; + + public float durationMultiplier = 1f; + public ToSet(View view, Runnable callback) { this.view = view; this.views = null; @@ -129,7 +132,7 @@ public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width } else if (toSetObj.views != null) { drawThread.animateGroup(toSetObj.views, toSetObj.doneCallback); } else { - drawThread.animate(toSetObj.view, toSetObj.doneCallback); + drawThread.animate(toSetObj.view, toSetObj.durationMultiplier, toSetObj.doneCallback); } } toSet.clear(); @@ -165,7 +168,9 @@ public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { }); } + public boolean destroyed; private void destroy() { + destroyed = true; if (whenDone != null) { Runnable runnable = whenDone; whenDone = null; @@ -173,6 +178,19 @@ private void destroy() { } } + public void kill() { + destroyed = true; + for (ToSet set : toSet) { + if (set.doneCallback != null) { + set.doneCallback.run(); + } + } + toSet.clear(); + if (drawThread != null) { + drawThread.kill(); + } + } + public void scroll(int dx, int dy) { if (drawThread != null && drawThread.running) { // post(() -> drawThread.scroll(dx, dy)); @@ -190,13 +208,42 @@ public void animateGroup(ArrayList views, Runnable whenDone) { public void animate(View view, Runnable whenDone) { if (drawThread != null) { - drawThread.animate(view, whenDone); + drawThread.animate(view, 1f, whenDone); Choreographer.getInstance().postFrameCallback(frameCallback); } else { toSet.add(new ToSet(view, whenDone)); } } + public void animate(View view, float durationMultipier, Runnable whenDone) { + if (drawThread != null) { + drawThread.animate(view, durationMultipier, whenDone); + Choreographer.getInstance().postFrameCallback(frameCallback); + } else { + ToSet set = new ToSet(view, whenDone); + set.durationMultiplier = durationMultipier; + toSet.add(set); + } + } + + public void cancel(View view) { + boolean found = false; + for (int i = 0; i < toSet.size(); ++i) { + ToSet set = toSet.get(i); + if (set.view == view) { + if (set.doneCallback != null) { + set.doneCallback.run(); + } + toSet.remove(i); + i--; + found = true; + } + } + if (!found) { + drawThread.cancel(view); + } + } + public void animate(Matrix matrix, Bitmap bitmap, Runnable whenStarted, Runnable whenDone) { if (drawThread != null) { drawThread.animate(matrix, bitmap, whenStarted, whenDone); @@ -208,7 +255,7 @@ public void animate(Matrix matrix, Bitmap bitmap, Runnable whenStarted, Runnable private static class DrawingThread extends DispatchQueue { - private boolean alive = true; + private volatile boolean alive = true; private final SurfaceTexture surfaceTexture; private final Runnable invalidate; private Runnable destroy; @@ -231,6 +278,7 @@ public DrawingThread(SurfaceTexture surfaceTexture, Runnable invalidate, Runnabl public final static int DO_KILL = 2; public final static int DO_ADD_ANIMATION = 3; public final static int DO_SCROLL = 4; + public final static int DO_CANCEL_ANIMATION = 5; @Override public void handleMessage(Message inputMessage) { @@ -252,6 +300,10 @@ public void handleMessage(Message inputMessage) { addAnimationInternal((Animation) inputMessage.obj); return; } + case DO_CANCEL_ANIMATION: { + cancelAnimationInternal((View) inputMessage.obj); + return; + } case DO_SCROLL: { for (int i = 0; i < pendingAnimations.size(); ++i) { Animation anim = pendingAnimations.get(i); @@ -274,7 +326,7 @@ public void run() { if (animation.startCallback != null) { AndroidUtilities.runOnUIThread(animation.startCallback); } - animation.done(false); + animation.done(true); } toAddAnimations.clear(); AndroidUtilities.runOnUIThread(() -> { @@ -294,21 +346,21 @@ public void run() { public void requestDraw() { Handler handler = getHandler(); - if (handler != null) { + if (handler != null && alive) { handler.sendMessage(handler.obtainMessage(DO_DRAW)); } } public void resize(int width, int height) { Handler handler = getHandler(); - if (handler != null) { + if (handler != null && alive) { handler.sendMessage(handler.obtainMessage(DO_RESIZE, width, height)); } } public void scroll(int dx, int dy) { Handler handler = getHandler(); - if (handler != null) { + if (handler != null && alive) { handler.sendMessage(handler.obtainMessage(DO_SCROLL, dx, dy)); } } @@ -321,11 +373,16 @@ private void resizeInternal(int width, int height) { GLES31.glUniform2f(sizeHandle, width, height); } + private boolean killed; public void kill() { - Handler handler = getHandler(); - if (handler != null) { - handler.sendMessage(handler.obtainMessage(DO_KILL)); - } + if (killed) return; + try { + Handler handler = getHandler(); + if (handler != null) { + handler.sendMessage(handler.obtainMessage(DO_KILL)); + } + killed = true; + } catch (Exception e) {} } private void killInternal() { @@ -333,8 +390,9 @@ private void killInternal() { alive = false; for (int i = 0; i < pendingAnimations.size(); ++i) { Animation animation = pendingAnimations.get(i); - animation.done(false); + animation.done(true); } + pendingAnimations.clear(); if (surfaceTexture != null) { surfaceTexture.release(); } @@ -537,7 +595,7 @@ private void draw() { } toRunStartCallback.clear(); for (int i = 0; i < pendingAnimations.size(); ++i) { - pendingAnimations.get(i).done(false); + pendingAnimations.get(i).done(true); } pendingAnimations.clear(); AndroidUtilities.runOnUIThread(() -> { @@ -573,9 +631,9 @@ public void animateGroup(ArrayList views, Runnable whenDone) { handler.sendMessage(handler.obtainMessage(DO_ADD_ANIMATION, animation)); } } - public void animate(View view, Runnable whenDone) { + public void animate(View view, float durationMultipier, Runnable whenDone) { if (!alive) return; - Animation animation = new Animation(view, whenDone); + Animation animation = new Animation(view, durationMultipier, whenDone); Handler handler = getHandler(); running = true; if (handler == null) { @@ -584,6 +642,35 @@ public void animate(View view, Runnable whenDone) { handler.sendMessage(handler.obtainMessage(DO_ADD_ANIMATION, animation)); } } + + public void cancel(View view) { + if (!alive) return; + Handler handler = getHandler(); + if (handler == null) { + for (int i = 0; i < toAddAnimations.size(); ++i) { + Animation animation = toAddAnimations.get(i); + if (animation.views.contains(view)) { + if (animation.doneCallback != null) { + animation.doneCallback.run(); + } + toAddAnimations.remove(i); + i--; + } + } + } else { + for (int i = 0; i < pendingAnimations.size(); ++i) { + Animation a = pendingAnimations.get(i); + if (a.views.contains(view)) { + if (a.doneCallback != null) { + a.doneCallback.run(); + } + break; + } + } + handler.sendMessage(handler.obtainMessage(DO_CANCEL_ANIMATION, view)); + } + } + public void animate(Matrix matrix, Bitmap bitmap, Runnable whenStart, Runnable whenDone) { if (!alive) return; Animation animation = new Animation(matrix, bitmap, whenStart, whenDone); @@ -596,6 +683,17 @@ public void animate(Matrix matrix, Bitmap bitmap, Runnable whenStart, Runnable w } } + private void cancelAnimationInternal(View view) { + for (int i = 0; i < pendingAnimations.size(); ++i) { + Animation a = pendingAnimations.get(i); + if (a.views.contains(view)) { + a.done(true); + pendingAnimations.remove(i); + i--; + } + } + } + private void addAnimationInternal(Animation animation) { GLES31.glGenTextures(1, animation.texture, 0); GLES20.glBindTexture(GL10.GL_TEXTURE_2D, animation.texture[0]); @@ -629,7 +727,7 @@ private class Animation { public float top = 0; public final float density = AndroidUtilities.density; public float longevity = 1.5f; - public float timeScale = 1.12f; + public float timeScale = 1.15f; public boolean invalidateMatrix = true; public boolean customMatrix = false; public final float[] glMatrixValues = new float[9]; @@ -952,7 +1050,7 @@ public void calcParticlesGrid(float part) { } } - public Animation(View view, Runnable whenDone) { + public Animation(View view, float durationMultipier, Runnable whenDone) { this.views.add(view); viewWidth = view.getWidth(); viewHeight = view.getHeight(); @@ -972,6 +1070,8 @@ public Animation(View view, Runnable whenDone) { } } }; + longevity *= durationMultipier; + timeScale /= 1f + (durationMultipier - 1f) / 3f; // longevity = 1.5f * Utilities.clamp(viewWidth / (float) AndroidUtilities.displaySize.x, .6f, 0.2f); bitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888); @@ -1087,7 +1187,7 @@ public boolean isDead() { return time > longevity + .9f; } - public void done(boolean success) { + public void done(boolean runCallback) { try { GLES31.glDeleteBuffers(2, buffer, 0); } catch (Exception e) { FileLog.e(e); }; if (drawProgram != 0) { try { GLES31.glDeleteProgram(drawProgram); } catch (Exception e) { FileLog.e(e); }; @@ -1095,7 +1195,7 @@ public void done(boolean success) { } try { GLES31.glDeleteTextures(1, texture, 0); } catch (Exception e) { FileLog.e(e); }; - if (doneCallback != null) { + if (runCallback && doneCallback != null) { AndroidUtilities.runOnUIThread(() -> { if (doneCallback != null) { doneCallback.run(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/TimerParticles.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/TimerParticles.java index a4fdf4f251..92aeee78c4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/TimerParticles.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/TimerParticles.java @@ -1,5 +1,7 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.lerp; + import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.RectF; @@ -14,6 +16,8 @@ public class TimerParticles { private long lastAnimationTime; + public boolean big; + private static class Particle { float x; float y; @@ -28,8 +32,15 @@ private static class Particle { private ArrayList particles = new ArrayList<>(); private ArrayList freeParticles = new ArrayList<>(); + private final int particlesCount; + public TimerParticles() { - for (int a = 0; a < 40; a++) { + this(40); + } + + public TimerParticles(int particlesCount) { + this.particlesCount = particlesCount; + for (int a = 0; a < particlesCount; a++) { freeParticles.add(new Particle()); } } @@ -39,7 +50,7 @@ private void updateParticles(long dt) { for (int a = 0; a < count; a++) { Particle particle = particles.get(a); if (particle.currentTime >= particle.lifeTime) { - if (freeParticles.size() < 40) { + if (freeParticles.size() < particlesCount) { freeParticles.add(particle); } particles.remove(a); @@ -48,12 +59,15 @@ private void updateParticles(long dt) { continue; } particle.alpha = 1.0f - AndroidUtilities.decelerateInterpolator.getInterpolation(particle.currentTime / particle.lifeTime); - particle.x += particle.vx * particle.velocity * dt / 500.0f; - particle.y += particle.vy * particle.velocity * dt / 500.0f; + particle.x += particle.vx * particle.velocity * dt / 200.0f; + particle.y += particle.vy * particle.velocity * dt / 200.0f; particle.currentTime += dt; } } + private boolean hasLast; + private float lastCx, lastCy; + public void draw(Canvas canvas, Paint particlePaint, RectF rect, float radProgress, float alpha) { int count = particles.size(); for (int a = 0; a < count; a++) { @@ -67,7 +81,8 @@ public void draw(Canvas canvas, Paint particlePaint, RectF rect, float radProgre float rad = rect.width() / 2; float cx = (float) (-vy * rad + rect.centerX()); float cy = (float) (vx * rad + rect.centerY()); - for (int a = 0; a < 1; a++) { + final int subcount = Utilities.clamp(freeParticles.size() / 12, 3, 1); + for (int a = 0; a < subcount; a++) { Particle newParticle; if (!freeParticles.isEmpty()) { newParticle = freeParticles.get(0); @@ -75,8 +90,14 @@ public void draw(Canvas canvas, Paint particlePaint, RectF rect, float radProgre } else { newParticle = new Particle(); } - newParticle.x = cx; - newParticle.y = cy; + + if (big && hasLast) { + newParticle.x = lerp(lastCx, cx, (a + 1) / (float) subcount); + newParticle.y = lerp(lastCy, cy, (a + 1) / (float) subcount); + } else { + newParticle.x = cx; + newParticle.y = cy; + } double angle = (Math.PI / 180.0) * (Utilities.random.nextInt(140) - 70); if (angle < 0) { @@ -88,10 +109,18 @@ public void draw(Canvas canvas, Paint particlePaint, RectF rect, float radProgre newParticle.alpha = 1.0f; newParticle.currentTime = 0; - newParticle.lifeTime = 400 + Utilities.random.nextInt(100); - newParticle.velocity = 20.0f + Utilities.random.nextFloat() * 4.0f; + if (big) { + newParticle.lifeTime = 600 + Utilities.random.nextInt(200); + newParticle.velocity = 30.0f + Utilities.random.nextFloat() * 20.0f; + } else { + newParticle.lifeTime = 400 + Utilities.random.nextInt(100); + newParticle.velocity = 20.0f + Utilities.random.nextFloat() * 4.0f; + } particles.add(newParticle); } + hasLast = true; + lastCx = cx; + lastCy = cy; long newTime = SystemClock.elapsedRealtime(); long dt = Math.min(20, (newTime - lastAnimationTime)); 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 edccc1ab02..dc37db13db 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UndoView.java @@ -48,6 +48,7 @@ import org.telegram.messenger.MediaController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; +import org.telegram.messenger.SavedMessagesController; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; import org.telegram.tgnet.ConnectionsManager; @@ -55,7 +56,9 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.Premium.boosts.BoostRepository; +import org.telegram.ui.LaunchActivity; import org.telegram.ui.PaymentFormActivity; import java.util.ArrayList; @@ -64,7 +67,7 @@ @Deprecated // use Bulletin instead public class UndoView extends FrameLayout { - private TextView infoTextView; + private LinkSpanDrawable.LinksTextView infoTextView; private TextView subinfoTextView; private TextView undoTextView; private ImageView undoImageView; @@ -239,7 +242,7 @@ public UndoView(Context context, BaseFragment parent, boolean top, Theme.Resourc parentFragment = parent; fromTop = top; - infoTextView = new TextView(context); + infoTextView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider); infoTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); infoTextView.setTextColor(getThemedColor(Theme.key_undo_infoColor)); infoTextView.setLinkTextColor(getThemedColor(Theme.key_undo_cancelColor)); @@ -1037,9 +1040,9 @@ public void showWithAction(ArrayList dialogIds, int action, Object infoObj if (infoObject2 == null || infoObject2 instanceof TLRPC.TL_forumTopic) { if (did == UserConfig.getInstance(currentAccount).clientUserId) { if (count == 1) { - infoTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("FwdMessageToSavedMessages", R.string.FwdMessageToSavedMessages))); + infoTextView.setText(AndroidUtilities.replaceSingleTag(LocaleController.getString("FwdMessageToSavedMessages", R.string.FwdMessageToSavedMessages), SavedMessagesController::openSavedMessages)); } else { - infoTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("FwdMessagesToSavedMessages", R.string.FwdMessagesToSavedMessages))); + infoTextView.setText(AndroidUtilities.replaceSingleTag(LocaleController.getString("FwdMessagesToSavedMessages", R.string.FwdMessagesToSavedMessages), SavedMessagesController::openSavedMessages)); } leftImageView.setAnimation(R.raw.saved_messages, 30, 30); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelineView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelineView.java index 268b05169c..8745685317 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelineView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/VideoTimelineView.java @@ -250,7 +250,7 @@ public boolean onTouchEvent(MotionEvent event) { } public void setVideoPath(String path) { - destroy(); + destroy(false); mediaMetadataRetriever = new MediaMetadataRetriever(); progressLeft = 0.0f; progressRight = 1.0f; @@ -347,6 +347,10 @@ protected void onPostExecute(Bitmap bitmap) { } public void destroy() { + destroy(true); + } + + public void destroy(boolean recycle) { synchronized (sync) { try { if (mediaMetadataRetriever != null) { @@ -357,18 +361,20 @@ public void destroy() { FileLog.e(e); } } - if (!keyframes.isEmpty()) { - for (int a = 0; a < keyframes.size(); a++) { - Bitmap bitmap = keyframes.get(a); - if (bitmap != null) { - bitmap.recycle(); + if (recycle) { + if (!keyframes.isEmpty()) { + for (int a = 0; a < keyframes.size(); a++) { + Bitmap bitmap = keyframes.get(a); + if (bitmap != null) { + bitmap.recycle(); + } } - } - } else { - for (int a = 0; a < frames.size(); a++) { - Bitmap bitmap = frames.get(a); - if (bitmap != null) { - bitmap.recycle(); + } else { + for (int a = 0; a < frames.size(); a++) { + Bitmap bitmap = frames.get(a); + if (bitmap != null) { + bitmap.recycle(); + } } } } @@ -436,7 +442,7 @@ protected void onDraw(Canvas canvas) { int offset = 0; for (int a = 0; a < frames.size(); a++) { Bitmap bitmap = frames.get(a); - if (bitmap != null) { + if (bitmap != null && !bitmap.isRecycled()) { int x = offset * (isRoundFrames ? frameWidth / 2 : frameWidth); if (isRoundFrames) { rect2.set(x, topOffset, x + AndroidUtilities.dp(28), topOffset + AndroidUtilities.dp(32)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffect2.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffect2.java index 261be2988a..84f96430f4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffect2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/spoilers/SpoilerEffect2.java @@ -185,10 +185,6 @@ public void draw(Canvas canvas, View view, int w, int h, float alpha, boolean to if (index == null) { index = 0; } - if (w > ow || h > oh) { - final float scale = Math.max(w / (float) ow, h / (float) oh); - canvas.scale(scale, scale); - } if ((index % 4) == 1) { canvas.rotate(180, ow / 2f, oh / 2f); } @@ -198,6 +194,12 @@ public void draw(Canvas canvas, View view, int w, int h, float alpha, boolean to if ((index % 4) == 3) { canvas.scale(1, -1, ow / 2f, oh / 2f); } + canvas.translate(w / 2f, h / 2f); + if (w > ow || h > oh) { + final float scale = Math.max(w / (float) ow, h / (float) oh); + canvas.scale(scale, scale); + } + canvas.translate(-w / 2f, -h / 2f); if (toBitmap) { Bitmap bitmap = textureView.getBitmap(); if (bitmap != null) { @@ -494,15 +496,6 @@ private void init() { GLES31.glUniform1f(resetHandle, reset ? 1 : 0); GLES31.glUniform1f(radiusHandle, radius); GLES31.glUniform1f(seedHandle, Utilities.fastRandom.nextInt(256) / 256f); - - GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "noiseScale"), 6); - GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "noiseSpeed"), 0.6f); - GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "noiseMovement"), 4f); - GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "longevity"), 1.4f); - GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "dampingMult"), .9999f); - GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "maxVelocity"), 6.f); - GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "velocityMult"), 1.0f); - GLES31.glUniform1f(GLES31.glGetUniformLocation(drawProgram, "forceMult"), 0.6f); } private float t; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/AcceptDeclineView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/AcceptDeclineView.java index 3ededd5cd4..5e23c511a8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/AcceptDeclineView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/AcceptDeclineView.java @@ -156,7 +156,7 @@ public boolean onTouchEvent(MotionEvent event) { startX = event.getX(); startY = event.getY(); if (leftAnimator == null && declineRect.contains((int) event.getX(), (int) event.getY())) { - rippleDrawable = Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(52), 0, retryMod ? Color.WHITE : 0xFFFF3846); + rippleDrawable = Theme.createSimpleSelectorCircleDrawable(AndroidUtilities.dp(52), 0, retryMod ? Theme.getColor(Theme.key_listSelector) : 0xFFFF3846); captured = true; leftDrag = true; setPressed(true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/HideEmojiTextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/HideEmojiTextView.java index 4b177f6947..2ee7e8109b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/HideEmojiTextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/HideEmojiTextView.java @@ -25,6 +25,7 @@ public HideEmojiTextView(Context context, VoIPBackgroundProvider backgroundProvi this.backgroundProvider = backgroundProvider; backgroundProvider.attach(this); setText(LocaleController.getString("VoipHideEmoji", R.string.VoipHideEmoji)); + setContentDescription(LocaleController.getString("VoipHideEmoji", R.string.VoipHideEmoji)); setTextColor(Color.WHITE); setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); setPadding(AndroidUtilities.dp(14), AndroidUtilities.dp(4), AndroidUtilities.dp(14), AndroidUtilities.dp(4)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/PrivateVideoPreviewDialogNew.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/PrivateVideoPreviewDialogNew.java index 02f6189fd6..007fd3614a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/PrivateVideoPreviewDialogNew.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/PrivateVideoPreviewDialogNew.java @@ -238,6 +238,7 @@ protected void onDraw(Canvas canvas) { positiveButton.setGravity(Gravity.CENTER); positiveButton.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM)); positiveButton.getPaint().setTextAlign(Paint.Align.CENTER); + positiveButton.setContentDescription(LocaleController.getString("VoipShareVideo", R.string.VoipShareVideo)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { positiveButton.setForeground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_voipgroup_nameText), (int) (255 * 0.3f)))); @@ -302,6 +303,7 @@ protected void dispatchDraw(Canvas canvas) { text = LocaleController.getString("VoipBackCamera", R.string.VoipBackCamera); } titles[i] = new VoIpBitmapTextView(context, text); + titles[i].setContentDescription(text); titles[i].setPadding(AndroidUtilities.dp(16), 0, AndroidUtilities.dp(10), 0); titlesLayout.addView(titles[i], LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT)); final int num = i; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPBackgroundProvider.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPBackgroundProvider.java index 5cf2495337..e792a6656a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPBackgroundProvider.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPBackgroundProvider.java @@ -9,8 +9,6 @@ import android.view.View; import android.view.animation.LinearInterpolator; -import androidx.annotation.NonNull; - import org.telegram.ui.Components.BitmapShaderTools; import java.util.ArrayList; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPNotificationsLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPNotificationsLayout.java index caee016900..a10d1d3428 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPNotificationsLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPNotificationsLayout.java @@ -224,12 +224,12 @@ public NotificationView(@NonNull Context context, VoIPBackgroundProvider backgro this.backgroundProvider = backgroundProvider; backgroundProvider.attach(this); iconView = new ImageView(context); - addView(iconView, LayoutHelper.createFrame(24, 24, Gravity.CENTER_VERTICAL, 10, 2, 10, 2)); + addView(iconView, LayoutHelper.createFrame(24, 24, Gravity.CENTER_VERTICAL, 8, 2, 8, 2)); textView = new TextView(context); textView.setTextColor(Color.WHITE); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); - addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, iconRes == 0 ? 14 : 42, 2, 14, 2)); + addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, iconRes == 0 ? 14 : 36, 2, 14, 2)); } public void setText(CharSequence text) { @@ -249,7 +249,20 @@ public void setText(CharSequence text) { protected void dispatchDraw(Canvas canvas) { bgRect.set(0, 0, getWidth(), getHeight()); backgroundProvider.setDarkTranslation(getX() + ((View) getParent()).getX(), getY() + ((View) getParent()).getY()); + + int oldDarkAlpha = backgroundProvider.getDarkPaint(ignoreShader).getAlpha(); + canvas.saveLayerAlpha(0, 0, getMeasuredWidth(), getMeasuredHeight(), oldDarkAlpha, Canvas.ALL_SAVE_FLAG); + backgroundProvider.getDarkPaint(ignoreShader).setAlpha(255); canvas.drawRoundRect(bgRect, dp(16), dp(16), backgroundProvider.getDarkPaint(ignoreShader)); + backgroundProvider.getDarkPaint(ignoreShader).setAlpha(oldDarkAlpha); + + if (backgroundProvider.isReveal()) { + int oldRevealDarkAlpha = backgroundProvider.getRevealDarkPaint().getAlpha(); + backgroundProvider.getRevealDarkPaint().setAlpha(255); + canvas.drawRoundRect(bgRect, dp(16), dp(16), backgroundProvider.getRevealDarkPaint()); + backgroundProvider.getRevealDarkPaint().setAlpha(oldRevealDarkAlpha); + } + canvas.restore(); super.dispatchDraw(canvas); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPStatusTextView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPStatusTextView.java index e7a2d062ec..a023efd707 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPStatusTextView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPStatusTextView.java @@ -52,7 +52,6 @@ public VoIPStatusTextView(@NonNull Context context, VoIPBackgroundProvider backg for (int i = 0; i < 2; i++) { textView[i] = new TextView(context); textView[i].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); - textView[i].setShadowLayer(AndroidUtilities.dp(3), 0, AndroidUtilities.dp(.666666667f), 0x4C000000); textView[i].setTextColor(Color.WHITE); textView[i].setGravity(Gravity.CENTER_HORIZONTAL); addView(textView[i]); @@ -88,7 +87,6 @@ protected void onDraw(Canvas canvas) { reconnectTextView = new TextView(context); reconnectTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); - reconnectTextView.setShadowLayer(AndroidUtilities.dp(3), 0, AndroidUtilities.dp(.666666667f), 0x4C000000); reconnectTextView.setTextColor(Color.WHITE); reconnectTextView.setGravity(Gravity.CENTER_HORIZONTAL); addView(reconnectTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 22, 0, 0)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPTimerView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPTimerView.java index 7b55ea5763..04182343c5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPTimerView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIPTimerView.java @@ -40,7 +40,6 @@ public VoIPTimerView(Context context) { super(context); textPaint.setTextSize(AndroidUtilities.dp(15)); textPaint.setColor(Color.WHITE); - textPaint.setShadowLayer(AndroidUtilities.dp(3), 0, AndroidUtilities.dp(.666666667f), 0x4C000000); activePaint.setColor(ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.9f))); inactivePaint.setColor(ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * 0.4f))); callsDeclineDrawable = ContextCompat.getDrawable(context, R.drawable.calls_decline); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIpCoverView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIpCoverView.java new file mode 100644 index 0000000000..eb20f57c34 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIpCoverView.java @@ -0,0 +1,216 @@ +package org.telegram.ui.Components.voip; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.view.View; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LiteMode; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.Components.CubicBezierInterpolator; + +@SuppressLint("ViewConstructor") +public class VoIpCoverView extends View { + + private VoipCoverEmoji[] voipCoverEmojiLeft; + private VoipCoverEmoji[] voipCoverEmojiRight; + private ValueAnimator positionAnimator; + private int diffX1; + private int diffX2; + private int diffX3; + private int diffX4; + private int diffX5; + private int diffY1; + private int diffY2; + private int diffY3; + private int diffY4; + private int diffY5; + private boolean isConnected; + private boolean isEmojiExpanded; + private int connectedDiffX; + private boolean isPaused; + private final VoIPBackgroundProvider backgroundProvider; + private final Paint saveLayerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Rect bgRect = new Rect(); + private final boolean allowAnimations; + + public VoIpCoverView(Context context, TLRPC.User callingUser, VoIPBackgroundProvider backgroundProvider) { + super(context); + this.allowAnimations = LiteMode.isEnabled(LiteMode.FLAG_CALLS_ANIMATIONS); + this.backgroundProvider = backgroundProvider; + if (!allowAnimations) { + return; + } + voipCoverEmojiLeft = new VoipCoverEmoji[]{ + new VoipCoverEmoji(callingUser, this, dp(32)), + new VoipCoverEmoji(callingUser, this, dp(28)), + new VoipCoverEmoji(callingUser, this, dp(35)), + new VoipCoverEmoji(callingUser, this, dp(28)), + new VoipCoverEmoji(callingUser, this, dp(26)) + }; + voipCoverEmojiRight = new VoipCoverEmoji[]{ + new VoipCoverEmoji(callingUser, this, dp(32)), + new VoipCoverEmoji(callingUser, this, dp(28)), + new VoipCoverEmoji(callingUser, this, dp(35)), + new VoipCoverEmoji(callingUser, this, dp(28)), + new VoipCoverEmoji(callingUser, this, dp(26)) + }; + backgroundProvider.attach(this); + setLayerType(LAYER_TYPE_HARDWARE, null); + saveLayerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); + } + + public void onConnected() { + if (!allowAnimations) { + return; + } + if (isConnected) { + return; + } + isConnected = true; + connectedDiffX = dp(12); + positionAnimator = ValueAnimator.ofInt(0, connectedDiffX); + positionAnimator.addUpdateListener(a -> { + diffX1 = (int) a.getAnimatedValue(); + diffX2 = diffX1; + diffX3 = diffX1; + diffX4 = diffX1; + diffX5 = diffX1; + invalidate(); + }); + positionAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT); + positionAnimator.setDuration(200); + positionAnimator.start(); + } + + public void onEmojiExpanded(boolean expanded) { + if (!allowAnimations) { + return; + } + if (expanded == isEmojiExpanded) { + return; + } + isEmojiExpanded = expanded; + positionAnimator = expanded ? ValueAnimator.ofFloat(0, 1f) : ValueAnimator.ofFloat(1f, 0f); + positionAnimator.addUpdateListener(a -> { + float percent = (float) a.getAnimatedValue(); + diffX1 = AndroidUtilities.lerp(connectedDiffX, dp(56), percent); + diffX2 = AndroidUtilities.lerp(connectedDiffX, dp(36), percent); + diffX3 = AndroidUtilities.lerp(connectedDiffX, dp(60), percent); + diffX4 = AndroidUtilities.lerp(connectedDiffX, dp(36), percent); + diffX5 = AndroidUtilities.lerp(connectedDiffX, dp(64), percent); + + diffY1 = AndroidUtilities.lerp(0, dp(50), percent); + diffY2 = AndroidUtilities.lerp(0, dp(20), percent); + diffY3 = AndroidUtilities.lerp(0, 0, percent); + diffY4 = AndroidUtilities.lerp(0, dp(-20), percent); + diffY5 = AndroidUtilities.lerp(0, dp(-40), percent); + invalidate(); + }); + positionAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + positionAnimator.setDuration(200); + positionAnimator.start(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + if (!allowAnimations) { + return; + } + for (VoipCoverEmoji voipCoverEmoji : voipCoverEmojiLeft) { + voipCoverEmoji.onLayout(getMeasuredWidth(), getMeasuredHeight()); + } + for (VoipCoverEmoji voipCoverEmoji : voipCoverEmojiRight) { + voipCoverEmoji.onLayout(getMeasuredWidth(), getMeasuredHeight()); + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (!allowAnimations) { + return; + } + if (isPaused) { + return; + } + bgRect.set(0, 0, getWidth(), getHeight()); + backgroundProvider.setDarkTranslation(getX(), getY()); + int centerX = getMeasuredWidth() / 2; + voipCoverEmojiLeft[0].setPosition(centerX - dp(120) - diffX1, dp(120) - diffY1); + voipCoverEmojiLeft[1].setPosition(centerX - dp(180) - diffX2, dp(150) - diffY2); + voipCoverEmojiLeft[2].setPosition(centerX - dp(150) - diffX3, dp(185) - diffY3); + voipCoverEmojiLeft[3].setPosition(centerX - dp(176) - diffX4, dp(240) - diffY4); + voipCoverEmojiLeft[4].setPosition(centerX - dp(130) - diffX5, dp(265) - diffY5); + for (VoipCoverEmoji voipCoverEmoji : voipCoverEmojiLeft) { + voipCoverEmoji.onDraw(canvas); + } + voipCoverEmojiRight[0].setPosition(centerX + dp(50) + diffX1, dp(120) - diffY1); + voipCoverEmojiRight[1].setPosition(centerX + dp(110) + diffX2, dp(150) - diffY2); + voipCoverEmojiRight[2].setPosition(centerX + dp(80) + diffX3, dp(185) - diffY3); + voipCoverEmojiRight[3].setPosition(centerX + dp(106) + diffX4, dp(240) - diffY4); + voipCoverEmojiRight[4].setPosition(centerX + dp(60) + diffX5, dp(265) - diffY5); + for (VoipCoverEmoji voipCoverEmoji : voipCoverEmojiRight) { + voipCoverEmoji.onDraw(canvas); + } + + int oldDarkAlpha = backgroundProvider.getDarkPaint().getAlpha(); + saveLayerPaint.setAlpha(255); + canvas.saveLayer(0, 0, getMeasuredWidth(), getMeasuredHeight(), saveLayerPaint, Canvas.ALL_SAVE_FLAG); + backgroundProvider.getDarkPaint().setAlpha(255); + canvas.drawRect(bgRect, backgroundProvider.getDarkPaint()); + backgroundProvider.getDarkPaint().setAlpha(oldDarkAlpha); + + if (backgroundProvider.isReveal()) { + int oldRevealDarkAlpha = backgroundProvider.getRevealDarkPaint().getAlpha(); + backgroundProvider.getRevealDarkPaint().setAlpha(255); + canvas.drawRect(bgRect, backgroundProvider.getRevealDarkPaint()); + backgroundProvider.getRevealDarkPaint().setAlpha(oldRevealDarkAlpha); + } + canvas.restore(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (!allowAnimations) { + return; + } + for (VoipCoverEmoji voipCoverEmoji : voipCoverEmojiLeft) { + voipCoverEmoji.onAttachedToWindow(); + } + for (VoipCoverEmoji voipCoverEmoji : voipCoverEmojiRight) { + voipCoverEmoji.onAttachedToWindow(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (!allowAnimations) { + return; + } + for (VoipCoverEmoji voipCoverEmoji : voipCoverEmojiLeft) { + voipCoverEmoji.onDetachedFromWindow(); + } + for (VoipCoverEmoji voipCoverEmoji : voipCoverEmojiRight) { + voipCoverEmoji.onDetachedFromWindow(); + } + if (positionAnimator != null) { + positionAnimator.cancel(); + } + } + + public void setState(boolean isPaused) { + this.isPaused = isPaused; + invalidate(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIpSwitchLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIpSwitchLayout.java index 8f48d428d6..40a786b537 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIpSwitchLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoIpSwitchLayout.java @@ -102,6 +102,7 @@ private void setText(Type type, boolean isSelectedState) { default: newText = ""; } + setContentDescription(newText); if (currentTextView.getVisibility() == GONE && newTextView.getVisibility() == GONE) { currentTextView.setVisibility(VISIBLE); @@ -147,7 +148,7 @@ private void attachNewButton(int rawRes, int size, boolean isSelected, Type type newVoIpButtonView.setSelectedState(isSelected, false, type); newVoIpButtonView.setAlpha(0f); newVoIpButtonView.setOnBtnClickedListener(voIpButtonView.onBtnClickedListener); - addView(newVoIpButtonView, LayoutHelper.createFrame(VoIpButtonView.ITEM_SIZE, VoIpButtonView.ITEM_SIZE, Gravity.CENTER_HORIZONTAL)); + addView(newVoIpButtonView, LayoutHelper.createFrame(VoIpButtonView.ITEM_SIZE + 1.5f, VoIpButtonView.ITEM_SIZE + 1.5f, Gravity.CENTER_HORIZONTAL)); final VoIpButtonView oldVoIpButton = voIpButtonView; voIpButtonView = newVoIpButtonView; newVoIpButtonView.animate().alpha(1f).setDuration(250).start(); @@ -369,8 +370,26 @@ public VoIpButtonView(@NonNull Context context, VoIPBackgroundProvider backgroun darkPaint.setAlpha(VoIPBackgroundProvider.DARK_LIGHT_DEFAULT_ALPHA); } + private ValueAnimator pressedScaleAnimator; + private float pressedScale = 1.0f; + + private void setPressedBtn(boolean pressed) { + if (pressedScaleAnimator != null) { + pressedScaleAnimator.cancel(); + } + pressedScaleAnimator = ValueAnimator.ofFloat(pressedScale, pressed ? 0.8f : 1f); + pressedScaleAnimator.addUpdateListener(animation -> { + pressedScale = (float) animation.getAnimatedValue(); + invalidate(); + }); + pressedScaleAnimator.setDuration(150); + pressedScaleAnimator.start(); + } + @Override protected void onDraw(Canvas canvas) { + canvas.save(); + canvas.scale(pressedScale, pressedScale, getMeasuredWidth() / 2f, getMeasuredHeight() / 2f); float cx = getWidth() / 2f; float cy = getHeight() / 2f; @@ -435,6 +454,7 @@ protected void onDraw(Canvas canvas) { selectedIcon.setAlpha((int) (255 * VoIPBackgroundProvider.DARK_LIGHT_PERCENT)); selectedIcon.draw(canvas); //dimming icons } + canvas.restore(); } private boolean isAnimating() { @@ -451,14 +471,12 @@ private boolean isAnimating() { public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - animate().scaleX(0.8f).scaleY(0.8f).setDuration(150).start(); - animate().scaleX(0.8f).scaleY(0.8f).setDuration(150).start(); + setPressedBtn(true); startX = event.getX(); startY = event.getY(); break; case MotionEvent.ACTION_UP: - animate().scaleX(1.0f).scaleY(1.0f).setDuration(150).start(); - animate().scaleX(1.0f).scaleY(1.0f).setDuration(150).start(); + setPressedBtn(false); float endX = event.getX(); float endY = event.getY(); if (isClick(startX, endX, startY, endY) && !isAnimating()) { @@ -466,8 +484,7 @@ public boolean onTouchEvent(MotionEvent event) { } break; case MotionEvent.ACTION_CANCEL: - animate().scaleX(1.0f).scaleY(1.0f).setDuration(150).start(); - animate().scaleX(1.0f).scaleY(1.0f).setDuration(150).start(); + setPressedBtn(false); break; } return true; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoipCoverEmoji.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoipCoverEmoji.java new file mode 100644 index 0000000000..ff0227f5b3 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/voip/VoipCoverEmoji.java @@ -0,0 +1,171 @@ +package org.telegram.ui.Components.voip; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.graphics.Canvas; +import android.graphics.Color; +import android.view.View; +import android.view.animation.LinearInterpolator; + +import androidx.annotation.NonNull; + +import org.telegram.messenger.LiteMode; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.Components.AnimatedEmojiDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; + +public class VoipCoverEmoji { + + private AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable emoji; + private ValueAnimator positionAnimator; + private final boolean allowAnimations; + private int toRandomX; + private int toRandomY; + private int fromRandomX; + private int fromRandomY; + private int randomX; + private int randomY; + private int posX; + private int posY; + private final View parent; + private final int size; + private int alpha = 0; + private float scale = 0f; + private int width; + private int height; + private int diffX; + private boolean isShown; + private ValueAnimator diffXAnimator; + + public VoipCoverEmoji(TLRPC.User user, View parent, int size) { + this.parent = parent; + this.size = size; + allowAnimations = LiteMode.isEnabled(LiteMode.FLAG_CALLS_ANIMATIONS); + long emojiId = UserObject.getProfileEmojiId(user); + if (allowAnimations && emojiId != 0) { + emoji = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(parent, false, size, AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW_STATIC); + emoji.set(emojiId, false); + emoji.setColor(Color.BLACK); + emoji.setAlpha(alpha); + + positionAnimator = ValueAnimator.ofFloat(0f, 1f); + positionAnimator.addUpdateListener(a -> { + randomX = (int) (fromRandomX + (toRandomX - fromRandomX) * (float) a.getAnimatedValue()); + randomY = (int) (fromRandomY + (toRandomY - fromRandomY) * (float) a.getAnimatedValue()); + parent.invalidate(); + }); + fromRandomX = toRandomX + dp(12); + fromRandomY = toRandomY + dp(12); + toRandomX = Utilities.random.nextInt(dp(16)) + dp(12); + toRandomY = Utilities.random.nextInt(dp(16)) + dp(12); + positionAnimator.setInterpolator(new LinearInterpolator()); + positionAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + fromRandomX = toRandomX; + fromRandomY = toRandomY; + toRandomX = Utilities.random.nextInt(dp(16)) + dp(12); + toRandomY = Utilities.random.nextInt(dp(16)) + dp(12); + if (positionAnimator != null) { + positionAnimator.start(); + } + } + }); + positionAnimator.setDuration(2000); + } + } + + private void show() { + if (isShown) return; + if (emoji.getDrawable() instanceof AnimatedEmojiDrawable) { + AnimatedEmojiDrawable emojiDrawable = ((AnimatedEmojiDrawable) emoji.getDrawable()); + if (emojiDrawable.getImageReceiver() == null || !emojiDrawable.getImageReceiver().hasImageLoaded()) { + return; + } + } + isShown = true; + int delay = 180; + int duration = 350; + int maxX = 12; + diffX = posX > getCenterX() ? dp(maxX) : -dp(maxX); + ValueAnimator scaleAnimator = ValueAnimator.ofFloat(0, 1f); + scaleAnimator.setInterpolator(new CubicBezierInterpolator(.34, 1.36, .64, 1)); + scaleAnimator.addUpdateListener(a -> { + scale = (float) a.getAnimatedValue(); + parent.invalidate(); + if (scale > 1.0f && diffXAnimator == null) { + diffXAnimator = ValueAnimator.ofInt(dp(maxX), 0); + diffXAnimator.addUpdateListener(a2 -> { + int val = (int) a2.getAnimatedValue(); + diffX = posX > getCenterX() ? val : -val; + parent.invalidate(); + }); + diffXAnimator.setDuration(duration - a.getCurrentPlayTime()); + diffXAnimator.start(); + } + }); + scaleAnimator.setDuration(duration); + scaleAnimator.setStartDelay(delay); + scaleAnimator.start(); + ValueAnimator alphaAnimator = ValueAnimator.ofInt(0, 255, 255); + alphaAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT); + alphaAnimator.addUpdateListener(a -> { + alpha = (int) a.getAnimatedValue(); + parent.invalidate(); + }); + alphaAnimator.setStartDelay(delay); + alphaAnimator.setDuration(duration); + alphaAnimator.start(); + } + + private int getCenterX() { + return width / 2; + } + + public void onLayout(int width, int height) { + this.width = width; + this.height = height; + parent.invalidate(); + } + + public void setPosition(int x, int y) { + if (emoji == null) return; + posX = x; + posY = y; + parent.invalidate(); + show(); + } + + public void onDraw(Canvas canvas) { + if (emoji == null) return; + canvas.save(); + canvas.scale(scale, scale, width / 2f, dp(300)); + canvas.translate(posX - diffX, posY); + emoji.setBounds(randomX, randomY, randomX + size, randomY + size); + emoji.setAlpha(alpha); + emoji.draw(canvas); + canvas.restore(); + } + + public void onAttachedToWindow() { + if (emoji == null) return; + emoji.attach(); + if (positionAnimator != null) { + positionAnimator.start(); + } + } + + public void onDetachedFromWindow() { + if (emoji == null) return; + if (positionAnimator != null) { + positionAnimator.cancel(); + positionAnimator = null; + } + emoji.detach(); + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java index faafc50823..40809cecdb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ContactsActivity.java @@ -202,7 +202,7 @@ public boolean onFragmentCreate() { } getContactsController().checkInviteText(); - getContactsController().reloadContactsStatusesMaybe(); + getContactsController().reloadContactsStatusesMaybe(false); MessagesController.getInstance(currentAccount).getStoriesController().loadHiddenStories(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java index dc946e519d..1e379bf1da 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java @@ -2932,6 +2932,7 @@ public View createView(final Context context) { searchWas = false; wasDrawn = false; pacmanAnimation = null; + filterTabsView = null; selectedDialogs.clear(); maximumVelocity = ViewConfiguration.get(context).getScaledMaximumFlingVelocity(); @@ -3857,6 +3858,10 @@ public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State viewPage.listView.setVerticalScrollbarPosition(LocaleController.isRTL ? RecyclerListView.SCROLLBAR_POSITION_LEFT : RecyclerListView.SCROLLBAR_POSITION_RIGHT); viewPage.addView(viewPage.listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); viewPage.listView.setOnItemClickListener((view, position, x, y) -> { + if (view instanceof DialogCell && ((DialogCell) view).isBlocked()) { + showPremiumBlockedToast(view, ((DialogCell) view).getDialogId()); + return; + } if (initialDialogsType == DIALOGS_TYPE_BOT_REQUEST_PEER && view instanceof TextCell) { viewPage.dialogsAdapter.onCreateGroupForThisClick(); return; @@ -3919,6 +3924,10 @@ public void didFailChatCreation() { viewPage.listView.setOnItemLongClickListener(new RecyclerListView.OnItemLongClickListenerExtended() { @Override public boolean onItemClick(View view, int position, float x, float y) { + if (view instanceof DialogCell && ((DialogCell) view).isBlocked()) { + showPremiumBlockedToast(view, ((DialogCell) view).getDialogId()); + return true; + } if (filterTabsView != null && filterTabsView.getVisibility() == View.VISIBLE && filterTabsView.isEditing()) { return false; } @@ -4109,7 +4118,7 @@ public void notifyDataSetChanged() { @Override public void onButtonClicked(DialogCell dialogCell) { if (dialogCell.getMessage() != null) { - TLRPC.TL_forumTopic topic = getMessagesController().getTopicsController().findTopic(-dialogCell.getDialogId(), MessageObject.getTopicId(dialogCell.getMessage().messageOwner, true)); + TLRPC.TL_forumTopic topic = getMessagesController().getTopicsController().findTopic(-dialogCell.getDialogId(), MessageObject.getTopicId(currentAccount, dialogCell.getMessage().messageOwner, true)); if (topic != null) { if (onlySelect) { didSelectResult(dialogCell.getDialogId(), topic.id, false, false); @@ -4241,6 +4250,11 @@ public void searchStateChanged(boolean search, boolean animated) { } } + @Override + public void didPressedBlockedDialog(View view, long did) { + showPremiumBlockedToast(view, did); + } + @Override public void didPressedOnSubDialog(long did) { if (onlySelect) { @@ -4355,6 +4369,10 @@ public long getSearchForumDialogId() { }); searchViewPager.searchListView.setOnItemClickListener((view, position, x, y) -> { + if (view instanceof ProfileSearchCell && ((ProfileSearchCell) view).isBlocked()) { + showPremiumBlockedToast(view, ((ProfileSearchCell) view).getDialogId()); + return; + } if (initialDialogsType == DIALOGS_TYPE_WIDGET) { onItemLongClick(searchViewPager.searchListView, view, position, x, y, -1, searchViewPager.dialogsSearchAdapter); return; @@ -4364,6 +4382,10 @@ public long getSearchForumDialogId() { searchViewPager.searchListView.setOnItemLongClickListener(new RecyclerListView.OnItemLongClickListenerExtended() { @Override public boolean onItemClick(View view, int position, float x, float y) { + if (view instanceof ProfileSearchCell && ((ProfileSearchCell) view).isBlocked()) { + showPremiumBlockedToast(view, ((ProfileSearchCell) view).getDialogId()); + return true; + } return onItemLongClick(searchViewPager.searchListView, view, position, x, y, -1, searchViewPager.dialogsSearchAdapter); } @@ -4721,6 +4743,11 @@ public void needStartRecordVideo(int state, boolean notify, int scheduleDate, in } + @Override + public void toggleVideoRecordingPause() { + + } + @Override public void needChangeVideoPreviewState(int state, float seekProgress) { @@ -5102,7 +5129,7 @@ public void setAlpha(float alpha) { showSearch(true, false, false); actionBar.openSearchField(searchString, false); } else if (initialSearchString != null) { - showSearch(true, false, false); + showSearch(true, false, false, true); actionBar.openSearchField(initialSearchString, false); initialSearchString = null; if (filterTabsView != null) { @@ -5526,6 +5553,29 @@ public void dismiss() { popup[0].dimBehind(); } + private int shiftDp = -4; + private void showPremiumBlockedToast(View view, long dialogId) { + AndroidUtilities.shakeViewSpring(view, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + String username = ""; + if (dialogId >= 0) { + username = UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(dialogId)); + } + Bulletin bulletin; + if (getMessagesController().premiumFeaturesBlocked()) { + bulletin = BulletinFactory.of(this).createSimpleBulletin(R.raw.star_premium_2, AndroidUtilities.replaceTags(LocaleController.formatString(R.string.UserBlockedNonPremium, username))); + } else { + bulletin = BulletinFactory.of(this) + .createSimpleBulletin(R.raw.star_premium_2, AndroidUtilities.replaceTags(LocaleController.formatString(R.string.UserBlockedNonPremium, username)), LocaleController.getString(R.string.UserBlockedNonPremiumButton), () -> { + BaseFragment lastFragment = LaunchActivity.getLastFragment(); + if (lastFragment != null) { + presentFragment(new PremiumPreviewFragment("noncontacts")); + } + }); + } + bulletin.show(); + } + private int commentViewPreviousTop = -1; private ValueAnimator keyboardAnimator; private boolean commentViewIgnoreTopUpdate = false; @@ -7009,6 +7059,10 @@ public void search(String query, boolean animated) { } private void showSearch(boolean show, boolean startFromDownloads, boolean animated) { + showSearch(show, startFromDownloads, animated, false); + } + + private void showSearch(boolean show, boolean startFromDownloads, boolean animated, boolean forceNotOnlyDialogs) { if (!show) { updateSpeedItem(false); } @@ -7027,7 +7081,7 @@ private void showSearch(boolean show, boolean startFromDownloads, boolean animat ((SizeNotifierFrameLayout) fragmentView).invalidateBlur(); if (show) { boolean onlyDialogsAdapter; - if (searchFiltersWasShowed) { + if (searchFiltersWasShowed || forceNotOnlyDialogs) { onlyDialogsAdapter = false; } else { onlyDialogsAdapter = onlyDialogsAdapter(); @@ -7565,7 +7619,7 @@ private void onItemClick(View view, int position, RecyclerListView.Adapter adapt return; } long dialogId = 0; - int topicId = 0; + long topicId = 0; int message_id = 0; boolean isGlobalSearch = false; int folderId = 0; @@ -7659,7 +7713,7 @@ private void onItemClick(View view, int position, RecyclerListView.Adapter adapt message_id = messageObject.getId(); TLRPC.Chat chat = getMessagesController().getChat(-dialogId); if (ChatObject.isForum(chat)) { - topicId = MessageObject.getTopicId(messageObject.messageOwner, true); + topicId = MessageObject.getTopicId(messageObject.currentAccount, messageObject.messageOwner, true); } searchViewPager.dialogsSearchAdapter.addHashtagsFromMessage(searchViewPager.dialogsSearchAdapter.getLastSearchString()); } else if (obj instanceof String) { @@ -7839,7 +7893,7 @@ private void onItemClick(View view, int position, RecyclerListView.Adapter adapt } } - public void setOpenedDialogId(long dialogId, int topicId) { + public void setOpenedDialogId(long dialogId, long topicId) { openedDialogId.dialogId = dialogId; openedDialogId.topicId = topicId; @@ -10153,7 +10207,7 @@ public void didReceivedNotification(int id, int account, Object... args) { if (viewPages[a].isDefaultDialogType() && AndroidUtilities.isTablet()) { boolean close = (Boolean) args[2]; long dialog_id = (Long) args[0]; - int topicId = (int) args[1]; + long topicId = (Long) args[1]; if (close) { if (dialog_id == openedDialogId.dialogId && topicId == openedDialogId.topicId) { openedDialogId.dialogId = 0; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java b/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java index b49e629093..2256116a46 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/EmojiAnimationsOverlay.java @@ -91,14 +91,14 @@ public class EmojiAnimationsOverlay implements NotificationCenter.NotificationCe FrameLayout contentLayout; RecyclerListView listView; long dialogId; - int threadMsgId; + long threadMsgId; public EmojiAnimationsOverlay(FrameLayout frameLayout, int currentAccount) { this.contentLayout = frameLayout; this.currentAccount = currentAccount; } - public EmojiAnimationsOverlay(ChatActivity chatActivity, FrameLayout frameLayout, RecyclerListView chatListView, int currentAccount, long dialogId, int threadMsgId) { + public EmojiAnimationsOverlay(ChatActivity chatActivity, FrameLayout frameLayout, RecyclerListView chatListView, int currentAccount, long dialogId, long threadMsgId) { this.chatActivity = chatActivity; this.contentLayout = frameLayout; this.listView = chatListView; @@ -854,7 +854,7 @@ private void sendCurrentTaps() { TLRPC.TL_messages_setTyping req = new TLRPC.TL_messages_setTyping(); if (threadMsgId != 0) { - req.top_msg_id = threadMsgId; + req.top_msg_id = (int) threadMsgId; req.flags |= 1; } req.action = interaction; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/FilteredSearchView.java b/TMessagesProj/src/main/java/org/telegram/ui/FilteredSearchView.java index 270c8942b6..e4e322c92a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/FilteredSearchView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/FilteredSearchView.java @@ -31,7 +31,6 @@ import org.telegram.messenger.AnimationNotificationsLocker; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ChatObject; -import org.telegram.messenger.ContactsController; import org.telegram.messenger.Emoji; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; @@ -432,7 +431,7 @@ public static CharSequence createFromInfoString(MessageObject messageObject, boo } public static CharSequence createFromInfoString(MessageObject messageObject, boolean includeChat, int arrowType, TextPaint textPaint) { - if (messageObject == null) { + if (messageObject == null || messageObject.messageOwner == null) { return ""; } if (arrowSpan[arrowType] == null) { @@ -456,23 +455,36 @@ public static CharSequence createFromInfoString(MessageObject messageObject, boo arrowSpan[arrowType].setSpan(span, 0, arrowSpan[arrowType].length(), 0); } CharSequence fromName = null; - TLRPC.User user = messageObject.messageOwner.from_id.user_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getUser(messageObject.messageOwner.from_id.user_id) : null; + TLRPC.User user = null; TLRPC.Chat chatFrom = null, chatTo = null; - chatFrom = messageObject.messageOwner.from_id.chat_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.chat_id) : null; - if (chatFrom == null) { - chatFrom = messageObject.messageOwner.from_id.channel_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.channel_id) : null; - } - chatTo = messageObject.messageOwner.peer_id.channel_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.channel_id) : null; - if (chatTo == null) { - chatTo = messageObject.messageOwner.peer_id.chat_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.chat_id) : null; - } - if (!ChatObject.isChannelAndNotMegaGroup(chatTo) && !includeChat) { - chatTo = null; + if (messageObject.messageOwner.saved_peer_id != null) { + if (messageObject.getSavedDialogId() >= 0) { + user = MessagesController.getInstance(UserConfig.selectedAccount).getUser(messageObject.getSavedDialogId()); + } else if (messageObject.getSavedDialogId() < 0) { + chatFrom = MessagesController.getInstance(UserConfig.selectedAccount).getChat(-messageObject.getSavedDialogId()); + } + } else { + if (messageObject.messageOwner.from_id.user_id != 0) { + user = MessagesController.getInstance(UserConfig.selectedAccount).getUser(messageObject.messageOwner.from_id.user_id); + } + if (messageObject.messageOwner.from_id.chat_id != 0) { + chatFrom = MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.chat_id); + } + if (chatFrom == null) { + chatFrom = messageObject.messageOwner.from_id.channel_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.channel_id) : null; + } + chatTo = messageObject.messageOwner.peer_id.channel_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.channel_id) : null; + if (chatTo == null) { + chatTo = messageObject.messageOwner.peer_id.chat_id != 0 ? MessagesController.getInstance(UserConfig.selectedAccount).getChat(messageObject.messageOwner.peer_id.chat_id) : null; + } + if (!ChatObject.isChannelAndNotMegaGroup(chatTo) && !includeChat) { + chatTo = null; + } } if (user != null && chatTo != null) { CharSequence chatTitle = chatTo.title; if (ChatObject.isForum(chatTo)) { - TLRPC.TL_forumTopic topic = MessagesController.getInstance(UserConfig.selectedAccount).getTopicsController().findTopic(chatTo.id, MessageObject.getTopicId(messageObject.messageOwner, true)); + TLRPC.TL_forumTopic topic = MessagesController.getInstance(UserConfig.selectedAccount).getTopicsController().findTopic(chatTo.id, MessageObject.getTopicId(messageObject.currentAccount, messageObject.messageOwner, true)); if (topic != null) { chatTitle = ForumUtilities.getTopicSpannedName(topic, null, false); } @@ -491,7 +503,7 @@ public static CharSequence createFromInfoString(MessageObject messageObject, boo } else if (chatFrom != null) { CharSequence chatTitle = chatFrom.title; if (ChatObject.isForum(chatFrom)) { - TLRPC.TL_forumTopic topic = MessagesController.getInstance(UserConfig.selectedAccount).getTopicsController().findTopic(chatFrom.id, MessageObject.getTopicId(messageObject.messageOwner, true)); + TLRPC.TL_forumTopic topic = MessagesController.getInstance(UserConfig.selectedAccount).getTopicsController().findTopic(chatFrom.id, MessageObject.getTopicId(messageObject.currentAccount, messageObject.messageOwner, true)); if (topic != null) { chatTitle = ForumUtilities.getTopicSpannedName(topic, null, false); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java index 6cca46e842..e1f9f09f3a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCreateActivity.java @@ -52,6 +52,7 @@ import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; import org.telegram.messenger.DialogObject; @@ -75,6 +76,7 @@ import org.telegram.ui.Cells.GroupCreateSectionCell; import org.telegram.ui.Cells.GroupCreateUserCell; import org.telegram.ui.Cells.TextCell; +import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.EditTextBoldCursor; @@ -700,6 +702,10 @@ public void afterTextChanged(Editable editable) { if (ignoreUsers != null && ignoreUsers.indexOfKey(id) >= 0) { return; } + if (cell.isBlocked()) { + showPremiumBlockedToast(cell, id); + return; + } boolean exists; if (exists = selectedContacts.indexOfKey(id) >= 0) { GroupCreateSpan span = selectedContacts.get(id); @@ -826,6 +832,25 @@ public void getOutline(View view, Outline outline) { return fragmentView; } + private int shiftDp = -4; + private void showPremiumBlockedToast(View view, long dialogId) { + AndroidUtilities.shakeViewSpring(view, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + String username = ""; + if (dialogId >= 0) { + username = UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(dialogId)); + } + Bulletin bulletin; + if (MessagesController.getInstance(currentAccount).premiumFeaturesBlocked()) { + bulletin = BulletinFactory.of(this).createSimpleBulletin(R.raw.star_premium_2, AndroidUtilities.replaceTags(LocaleController.formatString(R.string.UserBlockedNonPremium, username))); + } else { + bulletin = BulletinFactory.of(this).createSimpleBulletin(R.raw.star_premium_2, AndroidUtilities.replaceTags(LocaleController.formatString(R.string.UserBlockedNonPremium, username)), LocaleController.getString(R.string.UserBlockedNonPremiumButton), () -> { + presentFragment(new PremiumPreviewFragment("noncontacts")); + }); + } + bulletin.show(); + } + private void updateEditTextHint() { if (editText == null) { return; @@ -1298,7 +1323,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType view = new GroupCreateSectionCell(context); break; case 1: - view = new GroupCreateUserCell(context, 1, 0, false); + view = new GroupCreateUserCell(context, 1, 0, false).showPremiumBlocked(); break; case 3: StickerEmptyView stickerEmptyView = new StickerEmptyView(context, null, StickerEmptyView.STICKER_TYPE_NO_CONTACTS) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index d6c220e663..6074423162 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -1614,7 +1614,7 @@ public void showPasscodeActivity(boolean fingerprint, boolean animated, int x, i PasscodeView.PasscodeViewDelegate delegate = view -> { SharedConfig.isWaitingForPasscodeEnter = false; if (passcodeSaveIntent != null) { - handleIntent(passcodeSaveIntent, passcodeSaveIntentIsNew, passcodeSaveIntentIsRestore, true); + handleIntent(passcodeSaveIntent, passcodeSaveIntentIsNew, passcodeSaveIntentIsRestore, true, null, false); passcodeSaveIntent = null; } drawerLayoutContainer.setAllowOpenDrawer(true, false); @@ -2040,7 +2040,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool String inputInvoiceSlug = null; Integer messageId = null; Long channelId = null; - Integer threadId = null; + Long threadId = null; boolean isBoost = false; Integer commentId = null; int videoTimestamp = -1; @@ -2214,18 +2214,18 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool messageId = null; channelId = null; } - threadId = Utilities.parseInt(data.getQueryParameter("thread")); + threadId = Utilities.parseLong(data.getQueryParameter("thread")); if (threadId == 0) { threadId = null; } if (threadId == null) { - threadId = Utilities.parseInt(data.getQueryParameter("topic")); + threadId = Utilities.parseLong(data.getQueryParameter("topic")); if (threadId == 0) { threadId = null; } } if (threadId == null && messageId != null && segments.size() >= 4) { - threadId = messageId; + threadId = (long) (int) messageId; messageId = Utilities.parseInt(segments.get(3)); } } @@ -2279,7 +2279,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool setAsAttachBot = data.getQueryParameter("startattach"); attachMenuBotChoose = data.getQueryParameter("choose"); attachMenuBotToOpen = data.getQueryParameter("attach"); - threadId = Utilities.parseInt(data.getQueryParameter("thread")); + threadId = Utilities.parseLong(data.getQueryParameter("thread")); if (data.getQuery() != null) { isBoost = data.getQuery().equals("boost"); } @@ -2288,13 +2288,13 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool threadId = null; } if (threadId == null) { - threadId = Utilities.parseInt(data.getQueryParameter("topic")); + threadId = Utilities.parseLong(data.getQueryParameter("topic")); if (threadId == 0) { threadId = null; } } if (threadId == null && messageId != null && segments.size() >= 3) { - threadId = messageId; + threadId = (long) (int) messageId; messageId = Utilities.parseInt(segments.get(2)); } commentId = Utilities.parseInt(data.getQueryParameter("comment")); @@ -2357,12 +2357,12 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool if (messageId == 0) { messageId = null; } - threadId = Utilities.parseInt(data.getQueryParameter("thread")); + threadId = Utilities.parseLong(data.getQueryParameter("thread")); if (threadId == 0) { threadId = null; } if (threadId == null) { - threadId = Utilities.parseInt(data.getQueryParameter("topic")); + threadId = Utilities.parseLong(data.getQueryParameter("topic")); if (threadId == 0) { threadId = null; } @@ -2389,12 +2389,12 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool messageId = null; channelId = null; } - threadId = Utilities.parseInt(data.getQueryParameter("thread")); + threadId = Utilities.parseLong(data.getQueryParameter("thread")); if (threadId == 0) { threadId = null; } if (threadId == null) { - threadId = Utilities.parseInt(data.getQueryParameter("topic")); + threadId = Utilities.parseLong(data.getQueryParameter("topic")); if (threadId == 0) { threadId = null; } @@ -2651,7 +2651,12 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool url = url.replace("tg:addcontact", "tg://telegram.org").replace("tg://addcontact", "tg://telegram.org"); data = Uri.parse(url); newContactName = data.getQueryParameter("name"); - newContactPhone = data.getQueryParameter("phone"); + + // use getQueryParameters to keep the "+" sign + List phoneParams = data.getQueryParameters("phone"); + if (phoneParams != null && phoneParams.size() > 0) { + newContactPhone = phoneParams.get(0); + } newContact = true; } else if (url.startsWith("tg:addlist") || url.startsWith("tg://addlist")) { url = url.replace("tg:addlist", "tg://telegram.org").replace("tg://addlist", "tg://telegram.org"); @@ -2797,7 +2802,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool if (lastFragment instanceof DialogsActivity) { final DialogsActivity dialogsActivity = (DialogsActivity) lastFragment; if (dialogsActivity.isMainDialogList()) { - if (dialogsActivity.getFragmentView() != null) { + if (dialogsActivity.getFragmentView() != null && isNew) { dialogsActivity.search(searchQuery, true); } else { dialogsActivity.setInitialSearchString(searchQuery); @@ -3255,17 +3260,17 @@ public boolean shouldShowNextButton(DialogsActivity dialogsFragment, ArrayList AndroidUtilities.runOnUIThread(() -> { boolean chatOpened = false; if (response instanceof TLRPC.TL_messages_discussionMessage) { @@ -3278,7 +3283,7 @@ private int runCommentRequest(int intentAccount, Runnable dismissLoading, Intege } if (!arrayList.isEmpty() || chat.forum && threadId != null && threadId == 1) { if (chat.forum) { - openTopicRequest(intentAccount, threadId, chat, commentId != null ? commentId : messageId, null, onOpened, quote, fromMessageId, arrayList, quoteOffset); + openTopicRequest(intentAccount, (int) (long) threadId, chat, commentId != null ? commentId : messageId, null, onOpened, quote, fromMessageId, arrayList, quoteOffset); chatOpened = true; } else { Bundle args = new Bundle(); @@ -3619,7 +3624,7 @@ private void runLinkRequest(final int intentAccount, final boolean hasUrl, final Integer messageId, final Long channelId, - final Integer threadId, + final Long threadId, final Integer commentId, final String game, final HashMap auth, @@ -4168,9 +4173,9 @@ public void didChangeOwner(TLRPC.User user) { } else { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialog_id); if (chat != null && chat.forum) { - Integer topicId = threadId; - if (topicId == null) { - topicId = messageId; + Long topicId = threadId; + if (topicId == null && messageId != null) { + topicId = (long) (int) messageId; } if (topicId != null && topicId != 0) { openForumFromLink(dialog_id, messageId, () -> { @@ -4837,7 +4842,7 @@ private void processWebAppBot(final int intentAccount, final boolean hasUrl, final Integer messageId, final Long channelId, - final Integer threadId, + final Long threadId, final Integer commentId, final String game, final HashMap auth, @@ -5120,7 +5125,7 @@ private void openForumFromLink(long dialogId, Integer messageId, String quote, R } if (message != null) { - runCommentRequest(currentAccount, null, message.id, null, MessageObject.getTopicId(message, MessagesController.getInstance(currentAccount).isForum(message)), MessagesController.getInstance(currentAccount).getChat(-dialogId), onOpened, quote, fromMessageId, quoteOffset); + runCommentRequest(currentAccount, null, message.id, null, MessageObject.getTopicId(currentAccount, message, MessagesController.getInstance(currentAccount).isForum(message)), MessagesController.getInstance(currentAccount).getChat(-dialogId), onOpened, quote, fromMessageId, quoteOffset); return; } @@ -5450,7 +5455,7 @@ public boolean didSelectDialogs(DialogsActivity dialogsFragment, ArrayList 150) { message = message.subSequence(0, 150); } - message = Emoji.replaceEmoji(message, avatarContainer.getSubtitleTextView().getTextPaint().getFontMetricsInt(), AndroidUtilities.dp(17), false); + message = Emoji.replaceEmoji(message, avatarContainer.getSubtitlePaint().getFontMetricsInt(), AndroidUtilities.dp(17), false); } else { message = messageObject.messageText; } @@ -528,7 +529,10 @@ public void onItemClick(final int id) { }); avatarContainer.setTitleColors(Theme.getColor(Theme.key_player_actionBarTitle, getResourceProvider()), Theme.getColor(Theme.key_player_actionBarSubtitle, getResourceProvider())); - avatarContainer.getSubtitleTextView().setLinkTextColor(Theme.getColor(Theme.key_player_actionBarSubtitle, getResourceProvider())); + View subtitleTextView = avatarContainer.getSubtitleTextView(); + if (subtitleTextView instanceof SimpleTextView) { + ((SimpleTextView) subtitleTextView).setLinkTextColor(Theme.getColor(Theme.key_player_actionBarSubtitle, getResourceProvider())); + } actionBar.setItemsColor(Theme.getColor(Theme.key_player_actionBarTitle, getResourceProvider()), false); actionBar.setItemsBackgroundColor(Theme.getColor(Theme.key_actionBarActionModeDefaultSelector, getResourceProvider()), false); actionBar.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite, getResourceProvider())); @@ -1177,7 +1181,10 @@ public ArrayList getThemeDescriptions() { sharedUi.invalidate(); } - avatarContainer.getSubtitleTextView().setLinkTextColor(Theme.getColor(Theme.key_player_actionBarSubtitle, getResourceProvider())); + View subtitle = avatarContainer.getSubtitleTextView(); + if (subtitle instanceof SimpleTextView) { + ((SimpleTextView) subtitle).setLinkTextColor(Theme.getColor(Theme.key_player_actionBarSubtitle, getResourceProvider())); + } }; themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{HeaderCell.class, ManageChatUserCell.class}, null, null, null, Theme.key_windowBackgroundWhite)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/NewContactBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/NewContactBottomSheet.java index 5a2b889ff4..e1360ec519 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/NewContactBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/NewContactBottomSheet.java @@ -80,7 +80,6 @@ public class NewContactBottomSheet extends BottomSheet implements AdapterView.On private boolean ignoreOnTextChange; private boolean ignoreOnPhoneChange; private boolean ignoreOnPhoneChangePaste; - private int countryState; private boolean ignoreSelection; private boolean donePressed; private String initialPhoneNumber; @@ -88,13 +87,11 @@ public class NewContactBottomSheet extends BottomSheet implements AdapterView.On private String initialFirstName; private String initialLastName; - private final static int done_button = 1; BaseFragment parentFragment; int classGuid; private AnimatedPhoneNumberEditText codeField; private View codeDividerView; private AnimatedPhoneNumberEditText phoneField; - private CountrySelectActivity.Country currentCountry; private String countryCodeForHint; private int wasCountryHintIndex; private TextView countryFlag; @@ -134,7 +131,7 @@ public View createView(Context context) { firstNameField.getEditText().setImeOptions(EditorInfo.IME_ACTION_NEXT); firstNameField.setHint(LocaleController.getString("FirstName", R.string.FirstName)); if (initialFirstName != null) { - firstNameField.setText(initialFirstName); + firstNameField.getEditText().setText(initialFirstName); initialFirstName = null; } frameLayout.addView(firstNameField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 58, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 0)); @@ -153,7 +150,7 @@ public View createView(Context context) { lastNameField.getEditText().setImeOptions(EditorInfo.IME_ACTION_NEXT); lastNameField.setHint(LocaleController.getString("LastName", R.string.LastName)); if (initialLastName != null) { - lastNameField.setText(initialLastName); + lastNameField.getEditText().setText(initialLastName); initialLastName = null; } frameLayout.addView(lastNameField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 58, Gravity.LEFT | Gravity.TOP, 0, 68, 0, 0)); @@ -260,7 +257,6 @@ public void afterTextChanged(Editable editable) { if (text.length() == 0) { setCountryButtonText(null); phoneField.setHintText(null); - countryState = LoginActivity.COUNTRY_STATE_EMPTY; } else { CountrySelectActivity.Country country; boolean ok = false; @@ -339,18 +335,15 @@ public void afterTextChanged(Editable editable) { if (country != null) { ignoreSelection = true; - currentCountry = country; setCountryHint(text, country); - countryState = LoginActivity.COUNTRY_STATE_NOT_SET_OR_VALID; } else { setCountryButtonText(null); phoneField.setHintText(null); - countryState = LoginActivity.COUNTRY_STATE_INVALID; } if (!ok) { codeField.setSelection(codeField.getText().length()); } - if (textToSet != null) { + if (textToSet != null && textToSet.length() != 0) { phoneField.requestFocus(); phoneField.setText(textToSet); phoneField.setSelection(phoneField.length()); @@ -594,13 +587,11 @@ public void afterTextChanged(Editable s) { } if (country1 != null) { codeField.setText(country1.code); - countryState = 0; } } } if (codeField.length() == 0) { phoneField.setHintText(null); - countryState = 1; } } @@ -698,6 +689,7 @@ private void doOnDone() { public void show() { super.show(); firstNameField.getEditText().requestFocus(); + firstNameField.getEditText().setSelection(firstNameField.getEditText().length()); AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.showKeyboard(firstNameField.getEditText()); }, 50); @@ -738,9 +730,9 @@ public static String getPhoneNumber(Context context, TLRPC.User user, String num } } - public void setInitialPhoneNumber(String value, boolean withCoutryCode) { + public void setInitialPhoneNumber(String value, boolean withCountryCode) { initialPhoneNumber = value; - initialPhoneNumberWithCountryCode = withCoutryCode; + initialPhoneNumberWithCountryCode = withCountryCode; if (!TextUtils.isEmpty(initialPhoneNumber)) { TLRPC.User user = UserConfig.getInstance(currentAccount).getCurrentUser(); @@ -765,8 +757,16 @@ public void setInitialPhoneNumber(String value, boolean withCoutryCode) { } public void setInitialName(String firstName, String lastName) { - initialFirstName = firstName; - initialLastName = lastName; + if (firstNameField != null) { + firstNameField.getEditText().setText(firstName); + } else { + initialFirstName = firstName; + } + if (lastNameField != null) { + lastNameField.getEditText().setText(lastName); + } else { + initialLastName = lastName; + } } private void setCountryHint(String code, CountrySelectActivity.Country country) { @@ -856,8 +856,6 @@ public void selectCountry(CountrySelectActivity.Country country) { String code = country.code; codeField.setText(code); setCountryHint(code, country); - currentCountry = country; - countryState = LoginActivity.COUNTRY_STATE_NOT_SET_OR_VALID; ignoreOnTextChange = false; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSoundActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSoundActivity.java index ea7f9b03d1..262dba1bb2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSoundActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSoundActivity.java @@ -115,7 +115,7 @@ public class NotificationsSoundActivity extends BaseFragment implements ChatAtta private final int tonesStreamType = AudioManager.STREAM_ALARM; - int topicId = 0; + long topicId = 0; public NotificationsSoundActivity(Bundle args) { this(args, null); @@ -130,7 +130,7 @@ public NotificationsSoundActivity(Bundle args, Theme.ResourcesProvider resources public boolean onFragmentCreate() { if (getArguments() != null) { dialogId = getArguments().getLong("dialog_id", 0); - topicId = getArguments().getInt("topic_id", 0); + topicId = getArguments().getLong("topic_id", 0); currentType = getArguments().getInt("type", -1); } String prefPath; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index 091747b1e0..b71f6a9bd5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -1832,7 +1832,7 @@ public void restore() { private boolean canEditAvatar; private boolean isEvent; private int sharedMediaType; - private int topicId; + private long topicId; private long currentDialogId; private long mergeDialogId; private int totalImagesCount; @@ -3918,7 +3918,7 @@ public void didReceivedNotification(int id, int account, Object... args) { } } else if (id == NotificationCenter.mediaCountDidLoad) { long uid = (Long) args[0]; - int topicId = (Integer) args[1]; + long topicId = (Long) args[1]; if (this.topicId == topicId && (uid == currentDialogId || uid == mergeDialogId)) { if (currentMessageObject == null || MediaDataController.getMediaType(currentMessageObject.messageOwner) == sharedMediaType) { if (uid == currentDialogId) { @@ -5418,7 +5418,7 @@ public void onSpeedSelected(float speed, boolean isFinal, boolean closeMenu) { menuItem.addSubItem(gallery_menu_openin, R.drawable.msg_openin, LocaleController.getString("OpenInExternalApp", R.string.OpenInExternalApp)).setColors(0xfffafafa, 0xfffafafa); menuItem.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); - allMediaItem = menuItem.addSubItem(gallery_menu_showall, R.drawable.msg_media, LocaleController.getString("ShowAllMedia", R.string.ShowAllMedia)); + allMediaItem = menuItem.addSubItem(gallery_menu_showall, R.drawable.msg_media, LocaleController.getString(R.string.ShowAllMedia)); allMediaItem.setColors(0xfffafafa, 0xfffafafa); menuItem.addSubItem(gallery_menu_savegif, R.drawable.msg_gif, LocaleController.getString("SaveToGIFs", R.string.SaveToGIFs)).setColors(0xfffafafa, 0xfffafafa); menuItem.addSubItem(gallery_menu_showinchat, R.drawable.msg_message, LocaleController.getString("ShowInChat", R.string.ShowInChat)).setColors(0xfffafafa, 0xfffafafa); @@ -11627,7 +11627,7 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca } else if (!messageObject.scheduled && !(MessageObject.getMedia(messageObject.messageOwner) instanceof TLRPC.TL_messageMediaInvoice) && !(MessageObject.getMedia(messageObject.messageOwner) instanceof TLRPC.TL_messageMediaWebPage) && (messageObject.messageOwner.action == null || messageObject.messageOwner.action instanceof TLRPC.TL_messageActionEmpty)) { needSearchImageInArr = true; imagesByIds[0].put(messageObject.getId(), messageObject); - if (parentChatActivity == null || !parentChatActivity.isThreadChat()) { + if (parentChatActivity == null || !parentChatActivity.isThreadChat() && parentChatActivity.getChatMode() != ChatActivity.MODE_SAVED) { menuItem.showSubItem(gallery_menu_showinchat); menuItem.showSubItem(gallery_menu_showall); } @@ -12401,20 +12401,28 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated } } if (parentChatActivity != null) { - TLRPC.Chat chat = parentChatActivity.getCurrentChat(); + TLRPC.Chat chat = null; + TLRPC.User user = null; + if (parentChatActivity.getChatMode() == ChatActivity.MODE_SAVED) { + long did = parentChatActivity.getSavedDialogId(); + if (did >= 0) { + user = parentChatActivity.getMessagesController().getUser(did); + } else { + chat = parentChatActivity.getMessagesController().getChat(-did); + } + } else { + user = parentChatActivity.getCurrentUser(); + chat = parentChatActivity.getCurrentChat(); + } if (chat != null) { - title = null; - actionBarContainer.setTitle(chat.title); + title = chat.title; } else { - TLRPC.User user = parentChatActivity.getCurrentUser(); - if (user != null) { - if (user.self) { - title = null; - actionBarContainer.setTitle(LocaleController.getString("SavedMessages", R.string.SavedMessages)); - } else { - title = null; - actionBarContainer.setTitle(ContactsController.formatName(user.first_name, user.last_name)); - } + if (UserObject.isUserSelf(user)) { + title = LocaleController.getString(parentChatActivity.getChatMode() == ChatActivity.MODE_SAVED ? R.string.MyNotes : R.string.SavedMessages); + } else if (UserObject.isAnonymous(user)) { + title = LocaleController.getString(R.string.AnonymousForward); + } else { + title = UserObject.getUserName(user); } } } @@ -13964,15 +13972,15 @@ public void checkCurrentImageVisibility() { } } - public boolean openPhoto(final MessageObject messageObject, ChatActivity chatActivity, long dialogId, long mergeDialogId, int topicId, final PhotoViewerProvider provider) { + public boolean openPhoto(final MessageObject messageObject, ChatActivity chatActivity, long dialogId, long mergeDialogId, long topicId, final PhotoViewerProvider provider) { return openPhoto(messageObject, null, null, null, null, null, null, 0, provider, chatActivity, dialogId, mergeDialogId, topicId, true, null, null); } - public boolean openPhoto(final MessageObject messageObject, int embedSeekTime, ChatActivity chatActivity, long dialogId, long mergeDialogId, int topicId, final PhotoViewerProvider provider) { + public boolean openPhoto(final MessageObject messageObject, int embedSeekTime, ChatActivity chatActivity, long dialogId, long mergeDialogId, long topicId, final PhotoViewerProvider provider) { return openPhoto(messageObject, null, null, null, null, null, null, 0, provider, chatActivity, dialogId, mergeDialogId, topicId, true, null, embedSeekTime); } - public boolean openPhoto(final MessageObject messageObject, long dialogId, long mergeDialogId, int topicId, final PhotoViewerProvider provider, boolean fullScreenVideo) { + public boolean openPhoto(final MessageObject messageObject, long dialogId, long mergeDialogId, long topicId, final PhotoViewerProvider provider, boolean fullScreenVideo) { return openPhoto(messageObject, null, null, null, null, null, null, 0, provider, null, dialogId, mergeDialogId, topicId, fullScreenVideo, null, null); } @@ -13988,7 +13996,7 @@ public boolean openPhoto(final TLRPC.FileLocation fileLocation, final ImageLocat return openPhoto(null, fileLocation, imageLocation, null, null, null, null, 0, provider, null, 0, 0, 0, true, null, null); } - public boolean openPhoto(final ArrayList messages, final int index, long dialogId, long mergeDialogId, int topicId, final PhotoViewerProvider provider) { + public boolean openPhoto(final ArrayList messages, final int index, long dialogId, long mergeDialogId, long topicId, final PhotoViewerProvider provider) { return openPhoto(messages.get(index), null, null, null, messages, null, null, index, provider, null, dialogId, mergeDialogId, topicId, true, null, null); } @@ -14259,7 +14267,7 @@ private void initCropView() { padImageForHorizontalInsets = true; } - public boolean openPhoto(final MessageObject messageObject, final TLRPC.FileLocation fileLocation, final ImageLocation imageLocation, final ImageLocation videoLocation, final ArrayList messages, final ArrayList documents, final ArrayList photos, final int index, final PhotoViewerProvider provider, ChatActivity chatActivity, long dialogId, long mDialogId, int topicId, boolean fullScreenVideo, PageBlocksAdapter pageBlocksAdapter, Integer embedSeekTime) { + public boolean openPhoto(final MessageObject messageObject, final TLRPC.FileLocation fileLocation, final ImageLocation imageLocation, final ImageLocation videoLocation, final ArrayList messages, final ArrayList documents, final ArrayList photos, final int index, final PhotoViewerProvider provider, ChatActivity chatActivity, long dialogId, long mDialogId, long topicId, boolean fullScreenVideo, PageBlocksAdapter pageBlocksAdapter, Integer embedSeekTime) { if (parentActivity == null || isVisible || provider == null && checkAnimation() || messageObject == null && fileLocation == null && messages == null && photos == null && documents == null && imageLocation == null && pageBlocksAdapter == null) { return false; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java index 6e2123e687..6373fdb6f7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PopupNotificationActivity.java @@ -399,6 +399,11 @@ public void needStartRecordVideo(int state, boolean notify, int scheduleDate, in } + @Override + public void toggleVideoRecordingPause() { + + } + @Override public void needStartRecordAudio(int state) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java index 5d8b4439a2..613cbf72f0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacyControlActivity.java @@ -8,6 +8,9 @@ package org.telegram.ui; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.content.Context; import android.content.SharedPreferences; import android.graphics.Canvas; @@ -36,6 +39,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BotWebViewVibrationEffect; import org.telegram.messenger.ContactsController; import org.telegram.messenger.DialogObject; import org.telegram.messenger.FileLoader; @@ -62,11 +66,14 @@ import org.telegram.ui.Cells.RadioCell; import org.telegram.ui.Cells.ShadowSectionCell; import org.telegram.ui.Cells.TextCell; +import org.telegram.ui.Cells.TextCheckCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.BackgroundGradientDrawable; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.BotWebViewContainer; +import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.CubicBezierInterpolator; @@ -123,6 +130,10 @@ public class PrivacyControlActivity extends BaseFragment implements Notification private int p2pSectionRow; private int p2pRow; private int p2pDetailRow; + private int readRow; + private int readDetailRow; + private int readPremiumRow; + private int readPremiumDetailRow; private int rowCount; private final static int done_button = 1; @@ -137,6 +148,7 @@ public class PrivacyControlActivity extends BaseFragment implements Notification public final static int PRIVACY_RULES_TYPE_ADDED_BY_PHONE = 7; public final static int PRIVACY_RULES_TYPE_VOICE_MESSAGES = 8; public final static int PRIVACY_RULES_TYPE_BIO = 9; + public final static int PRIVACY_RULES_TYPE_MESSAGES = 10; public final static int TYPE_EVERYBODY = 0; public final static int TYPE_NOBODY = 1; @@ -149,6 +161,9 @@ public class PrivacyControlActivity extends BaseFragment implements Notification private TLRPC.PhotoSize avatarForRest; private TLRPC.Photo avatarForRestPhoto; + private boolean currentReadValue; + private boolean selectedReadValue; + @Override public void didUploadPhoto(TLRPC.InputFile photo, TLRPC.InputFile video, double videoStartTimestamp, String videoPath, TLRPC.PhotoSize bigSize, TLRPC.PhotoSize smallSize, boolean isVideo, TLRPC.VideoSize emojiMarkup) { AndroidUtilities.runOnUIThread(() -> { @@ -431,6 +446,8 @@ public void onFragmentDestroy() { NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); } + private int shakeDp = 4; + @Override public View createView(Context context) { if (rulesType == PRIVACY_RULES_TYPE_FORWARDS) { @@ -455,8 +472,10 @@ public View createView(Context context) { actionBar.setTitle(LocaleController.getString("GroupsAndChannels", R.string.GroupsAndChannels)); } else if (rulesType == PRIVACY_RULES_TYPE_VOICE_MESSAGES) { actionBar.setTitle(LocaleController.getString("PrivacyVoiceMessages", R.string.PrivacyVoiceMessages)); - } else { + } else if (rulesType == PRIVACY_RULES_TYPE_LASTSEEN) { actionBar.setTitle(LocaleController.getString("PrivacyLastSeen", R.string.PrivacyLastSeen)); + } else if (rulesType == PRIVACY_RULES_TYPE_MESSAGES) { + actionBar.setTitle(LocaleController.getString(R.string.PrivacyMessages)); } actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override @@ -547,6 +566,18 @@ protected void dispatchDraw(Canvas canvas) { cameraDrawable.setCustomEndFrame(43); setAvatarCell.imageView.playAnimation(); } + } else if (rulesType == PRIVACY_RULES_TYPE_MESSAGES && position == myContactsRow && !getUserConfig().isPremium()) { + BulletinFactory.of(this).createSimpleBulletin( + R.raw.star_premium_2, + LocaleController.getString(R.string.OptionPremiumRequiredTitle), + AndroidUtilities.replaceTags(LocaleController.getString(R.string.OptionPremiumRequiredMessage)), + LocaleController.getString(R.string.OptionPremiumRequiredButton), + () -> { + presentFragment(new PremiumPreviewFragment("noncontacts")); + } + ).show(); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + AndroidUtilities.shakeViewSpring(view, shakeDp = -shakeDp); } else if (position == nobodyRow || position == everybodyRow || position == myContactsRow) { int newType; if (position == nobodyRow) { @@ -560,6 +591,7 @@ protected void dispatchDraw(Canvas canvas) { return; } currentType = newType; + Bulletin.hideVisible(); updateDoneButton(); updateRows(true); } else if (position == phoneContactsRow || position == phoneEverybodyRow) { @@ -628,6 +660,12 @@ protected void dispatchDraw(Canvas canvas) { } } else if (position == p2pRow) { presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_P2P)); + } else if (position == readRow) { + selectedReadValue = !selectedReadValue; + updateDoneButton(); + ((TextCheckCell) view).setChecked(selectedReadValue); + } else if (position == readPremiumRow) { + presentFragment(new PremiumPreviewFragment("lastseen")); } }); DefaultItemAnimator itemAnimator = new DefaultItemAnimator() { @@ -667,6 +705,26 @@ private void updateDoneButton() { } private void applyCurrentPrivacySettings() { + if (rulesType == PRIVACY_RULES_TYPE_MESSAGES) { + TLRPC.TL_account_setGlobalPrivacySettings req2 = new TLRPC.TL_account_setGlobalPrivacySettings(); + req2.settings = new TLRPC.TL_globalPrivacySettings(); + TLRPC.TL_globalPrivacySettings settings = getContactsController().getGlobalPrivacySettings(); + req2.settings.archive_and_mute_new_noncontact_peers = settings.archive_and_mute_new_noncontact_peers; + req2.settings.keep_archived_folders = settings.keep_archived_folders; + req2.settings.keep_archived_unmuted = settings.keep_archived_unmuted; + req2.settings.new_noncontact_peers_require_premium = currentType == TYPE_CONTACTS; + req2.settings.hide_read_marks = settings.hide_read_marks; + getConnectionsManager().sendRequest(req2, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error != null) { + showErrorAlert(); + } else { + settings.new_noncontact_peers_require_premium = req2.settings.new_noncontact_peers_require_premium; + finishFragment(); + getNotificationCenter().postNotificationName(NotificationCenter.privacyRulesUpdated); + } + })); + return; + } TLRPC.TL_account_setPrivacy req = new TLRPC.TL_account_setPrivacy(); if (rulesType == PRIVACY_RULES_TYPE_PHONE) { req.key = new TLRPC.TL_inputPrivacyKeyPhoneNumber(); @@ -774,6 +832,20 @@ private void applyCurrentPrivacySettings() { showErrorAlert(); } }), ConnectionsManager.RequestFlagFailOnServerErrors); + + if (rulesType == PRIVACY_RULES_TYPE_LASTSEEN && selectedReadValue != currentReadValue) { + TLRPC.TL_account_setGlobalPrivacySettings req2 = new TLRPC.TL_account_setGlobalPrivacySettings(); + req2.settings = new TLRPC.TL_globalPrivacySettings(); + TLRPC.TL_globalPrivacySettings settings = getContactsController().getGlobalPrivacySettings(); + req2.settings.archive_and_mute_new_noncontact_peers = settings.archive_and_mute_new_noncontact_peers; + req2.settings.keep_archived_folders = settings.keep_archived_folders; + req2.settings.keep_archived_unmuted = settings.keep_archived_unmuted; + req2.settings.new_noncontact_peers_require_premium = settings.new_noncontact_peers_require_premium; + req2.settings.hide_read_marks = selectedReadValue; + getConnectionsManager().sendRequest(req2, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + settings.hide_read_marks = currentReadValue = req2.settings.hide_read_marks; + })); + } } private void showErrorAlert() { @@ -788,6 +860,13 @@ private void showErrorAlert() { } private void checkPrivacy() { + if (rulesType == PRIVACY_RULES_TYPE_MESSAGES) { + TLRPC.TL_globalPrivacySettings settings = ContactsController.getInstance(currentAccount).getGlobalPrivacySettings(); + initialRulesType = currentType = settings != null && settings.new_noncontact_peers_require_premium ? TYPE_CONTACTS : TYPE_EVERYBODY; + currentMinus = new ArrayList<>(); + currentPlus = new ArrayList<>(); + return; + } currentPlus = new ArrayList<>(); currentMinus = new ArrayList<>(); ArrayList privacyRules = ContactsController.getInstance(currentAccount).getPrivacyRules(rulesType); @@ -866,9 +945,16 @@ private void checkPrivacy() { } updateRows(false); + if (rulesType == PRIVACY_RULES_TYPE_LASTSEEN) { + TLRPC.TL_globalPrivacySettings privacySettings = getContactsController().getGlobalPrivacySettings(); + selectedReadValue = currentReadValue = privacySettings != null && privacySettings.hide_read_marks; + } } private boolean hasChanges() { + if (rulesType == PRIVACY_RULES_TYPE_LASTSEEN && currentType != TYPE_EVERYBODY && currentReadValue != selectedReadValue) { + return true; + } if (initialRulesType != currentType) { return true; } @@ -904,11 +990,25 @@ private void updateRows(boolean animated) { photoForRestRow = -1; currentPhotoForRestRow = -1; photoForRestDescriptionRow = -1; + messageRow = -1; + phoneDetailRow = -1; + phoneSectionRow = -1; + phoneEverybodyRow = -1; + phoneContactsRow = -1; + alwaysShareRow = -1; + neverShareRow = -1; + p2pSectionRow = -1; + p2pRow = -1; + p2pDetailRow = -1; + readRow = readDetailRow = -1; + nobodyRow = -1; + shareSectionRow = -1; + shareDetailRow = -1; + readPremiumRow = readPremiumDetailRow = -1; + rowCount = 0; if (rulesType == PRIVACY_RULES_TYPE_FORWARDS) { messageRow = rowCount++; - } else { - messageRow = -1; } sectionRow = rowCount++; everybodyRow = rowCount++; @@ -925,49 +1025,44 @@ private void updateRows(boolean animated) { rulesType == PRIVACY_RULES_TYPE_INVITE ) { nobodyRow = rowCount++; - } else { - nobodyRow = -1; } if (rulesType == PRIVACY_RULES_TYPE_PHONE && currentType == TYPE_NOBODY) { phoneDetailRow = rowCount++; phoneSectionRow = rowCount++; phoneEverybodyRow = rowCount++; phoneContactsRow = rowCount++; - } else { - phoneDetailRow = -1; - phoneSectionRow = -1; - phoneEverybodyRow = -1; - phoneContactsRow = -1; } detailRow = rowCount++; - shareSectionRow = rowCount++; - if (currentType == TYPE_NOBODY || currentType == TYPE_CONTACTS) { - alwaysShareRow = rowCount++; - } else { - alwaysShareRow = -1; - } - if (currentType == TYPE_EVERYBODY || currentType == TYPE_CONTACTS) { - neverShareRow = rowCount++; - } else { - neverShareRow = -1; - } - shareDetailRow = rowCount++; - if (rulesType == PRIVACY_RULES_TYPE_CALLS) { - p2pSectionRow = rowCount++; - p2pRow = rowCount++; - p2pDetailRow = rowCount++; - } else { - p2pSectionRow = -1; - p2pRow = -1; - p2pDetailRow = -1; - } + if (rulesType != PRIVACY_RULES_TYPE_MESSAGES) { + shareSectionRow = rowCount++; + if (currentType == TYPE_NOBODY || currentType == TYPE_CONTACTS) { + alwaysShareRow = rowCount++; + } + if (currentType == TYPE_EVERYBODY || currentType == TYPE_CONTACTS) { + neverShareRow = rowCount++; + } + shareDetailRow = rowCount++; + if (rulesType == PRIVACY_RULES_TYPE_CALLS) { + p2pSectionRow = rowCount++; + p2pRow = rowCount++; + p2pDetailRow = rowCount++; + } - if (rulesType == PRIVACY_RULES_TYPE_PHOTO && (currentMinus.size() > 0 || currentType == TYPE_CONTACTS || currentType == TYPE_NOBODY)) { - photoForRestRow = rowCount++; - if (avatarForRest != null) { - currentPhotoForRestRow = rowCount++; + if (rulesType == PRIVACY_RULES_TYPE_PHOTO && (currentMinus.size() > 0 || currentType == TYPE_CONTACTS || currentType == TYPE_NOBODY)) { + photoForRestRow = rowCount++; + if (avatarForRest != null) { + currentPhotoForRestRow = rowCount++; + } + photoForRestDescriptionRow = rowCount++; + } + if (rulesType == PRIVACY_RULES_TYPE_LASTSEEN && currentType != TYPE_EVERYBODY) { + readRow = rowCount++; + readDetailRow = rowCount++; + } + if (rulesType == PRIVACY_RULES_TYPE_LASTSEEN && !getUserConfig().isPremium() && !getMessagesController().premiumFeaturesBlocked()) { + readPremiumRow = rowCount++; + readPremiumDetailRow = rowCount++; } - photoForRestDescriptionRow = rowCount++; } setMessageText(); @@ -1114,7 +1209,7 @@ public boolean isEnabled(RecyclerView.ViewHolder holder) { int position = holder.getAdapterPosition(); return position == nobodyRow || position == everybodyRow || position == myContactsRow || position == neverShareRow || position == alwaysShareRow || position == p2pRow && !ContactsController.getInstance(currentAccount).getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_P2P) || - position == currentPhotoForRestRow || position == photoForRestDescriptionRow || position == photoForRestRow; + position == currentPhotoForRestRow || position == photoForRestDescriptionRow || position == photoForRestRow || position == readRow || position == readPremiumRow; } @Override @@ -1201,6 +1296,11 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto oldPhotoCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); view = oldPhotoCell; break; + case 8: + TextCheckCell textCheckCell = new TextCheckCell(mContext, resourceProvider); + textCheckCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + view = textCheckCell; + break; } return new RecyclerListView.Holder(view); } @@ -1226,6 +1326,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (holder.getItemViewType()) { case 0: TextSettingsCell textCell = (TextSettingsCell) holder.itemView; + textCell.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); if (position == alwaysShareRow) { String value; if (currentPlus.size() != 0) { @@ -1259,12 +1360,20 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { value = PrivacySettingsActivity.formatRulesString(getAccountInstance(), ContactsController.PRIVACY_RULES_TYPE_P2P); } textCell.setTextAndValue(LocaleController.getString("PrivacyP2P2", R.string.PrivacyP2P2), value, false); + } else if (position == readPremiumRow) { + textCell.setText(LocaleController.getString(R.string.PrivacyLastSeenPremium), false); + textCell.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlueText)); } break; case 1: TextInfoPrivacyCell privacyCell = (TextInfoPrivacyCell) holder.itemView; int backgroundResId = 0; - if (position == detailRow) { + if (position == detailRow && rulesType == PRIVACY_RULES_TYPE_MESSAGES) { + privacyCell.setText(AndroidUtilities.replaceSingleTag(LocaleController.getString(R.string.PrivacyMessagesInfo), () -> { + presentFragment(new PremiumPreviewFragment("noncontacts")); + })); + backgroundResId = R.drawable.greydivider_bottom; + } else if (position == detailRow) { if (rulesType == PRIVACY_RULES_TYPE_PHONE) { if (prevSubtypeContacts = (currentType == TYPE_NOBODY && currentSubType == 1)) { privacyCell.setText(LocaleController.getString("PrivacyPhoneInfo3", R.string.PrivacyPhoneInfo3)); @@ -1303,7 +1412,7 @@ public void onClick(@NonNull View view) { } else if (rulesType == PRIVACY_RULES_TYPE_INVITE) { privacyCell.setText(LocaleController.getString("WhoCanAddMeInfo", R.string.WhoCanAddMeInfo)); } else if (rulesType == PRIVACY_RULES_TYPE_VOICE_MESSAGES) { - privacyCell.setText(LocaleController.getString("PrivacyVoiceMessagesInfo", R.string.PrivacyVoiceMessagesInfo)); + privacyCell.setText(LocaleController.getString(R.string.PrivacyVoiceMessagesInfo)); } else { privacyCell.setText(LocaleController.getString("CustomHelp", R.string.CustomHelp)); } @@ -1334,7 +1443,7 @@ public void onClick(@NonNull View view) { } else { privacyCell.setText(LocaleController.getString("CustomShareSettingsHelp", R.string.CustomShareSettingsHelp)); } - if (rulesType == PRIVACY_RULES_TYPE_CALLS) { + if (rulesType == PRIVACY_RULES_TYPE_CALLS || rulesType == PRIVACY_RULES_TYPE_LASTSEEN) { backgroundResId = R.drawable.greydivider; } else { backgroundResId = R.drawable.greydivider_bottom; @@ -1343,6 +1452,12 @@ public void onClick(@NonNull View view) { backgroundResId = R.drawable.greydivider_bottom; } else if (position == photoForRestDescriptionRow) { privacyCell.setText(LocaleController.getString("PhotoForRestDescription", R.string.PhotoForRestDescription)); + } else if (position == readDetailRow) { + privacyCell.setText(LocaleController.getString(R.string.HideReadTimeInfo)); + backgroundResId = readPremiumDetailRow == -1 ? R.drawable.greydivider_bottom : R.drawable.greydivider; + } else if (position == readPremiumDetailRow) { + privacyCell.setText(LocaleController.getString(R.string.PrivacyLastSeenPremiumInfo)); + backgroundResId = R.drawable.greydivider_bottom; } if (backgroundResId != 0) { Drawable drawable = Theme.getThemedDrawableByKey(mContext, backgroundResId, Theme.key_windowBackgroundGrayShadow); @@ -1370,6 +1485,8 @@ public void onClick(@NonNull View view) { headerCell.setText(LocaleController.getString("WhoCanAddMe", R.string.WhoCanAddMe)); } else if (rulesType == PRIVACY_RULES_TYPE_VOICE_MESSAGES) { headerCell.setText(LocaleController.getString("PrivacyVoiceMessagesTitle", R.string.PrivacyVoiceMessagesTitle)); + } else if (rulesType == PRIVACY_RULES_TYPE_MESSAGES) { + headerCell.setText(LocaleController.getString(R.string.PrivacyMessagesTitle)); } else { headerCell.setText(LocaleController.getString("LastSeenTitle", R.string.LastSeenTitle)); } @@ -1383,6 +1500,7 @@ public void onClick(@NonNull View view) { break; case 3: RadioCell radioCell = (RadioCell) holder.itemView; + radioCell.setRadioIcon(null); if (position == everybodyRow || position == myContactsRow || position == nobodyRow) { if (position == everybodyRow) { if (rulesType == PRIVACY_RULES_TYPE_P2P) { @@ -1393,6 +1511,11 @@ public void onClick(@NonNull View view) { } else if (position == myContactsRow) { if (rulesType == PRIVACY_RULES_TYPE_P2P) { radioCell.setText(LocaleController.getString("P2PContacts", R.string.P2PContacts), currentType == TYPE_CONTACTS, nobodyRow != -1); + } else if (rulesType == PRIVACY_RULES_TYPE_MESSAGES) { + if (!getUserConfig().isPremium()) { + radioCell.setRadioIcon(getContext().getResources().getDrawable(R.drawable.mini_switch_lock).mutate()); + } + radioCell.setText(LocaleController.getString(R.string.PrivacyMessagesContactsAndPremium), currentType == TYPE_CONTACTS, nobodyRow != -1); } else { radioCell.setText(LocaleController.getString("LastSeenContacts", R.string.LastSeenContacts), currentType == TYPE_CONTACTS, nobodyRow != -1); } @@ -1411,14 +1534,20 @@ public void onClick(@NonNull View view) { } } break; + case 8: + TextCheckCell checkCell = (TextCheckCell) holder.itemView; + if (position == readRow) { + checkCell.setTextAndCheck(LocaleController.getString(R.string.HideReadTime), selectedReadValue, false); + } + break; } } @Override public int getItemViewType(int position) { - if (position == alwaysShareRow || position == neverShareRow || position == p2pRow) { + if (position == alwaysShareRow || position == neverShareRow || position == p2pRow || position == readPremiumRow) { return 0; - } else if (position == shareDetailRow || position == detailRow || position == p2pDetailRow || position == photoForRestDescriptionRow) { + } else if (position == shareDetailRow || position == detailRow || position == p2pDetailRow || position == photoForRestDescriptionRow || position == readDetailRow || position == readPremiumDetailRow) { return 1; } else if (position == sectionRow || position == shareSectionRow || position == p2pSectionRow || position == phoneSectionRow) { return 2; @@ -1432,6 +1561,8 @@ public int getItemViewType(int position) { return 6; } else if (position == currentPhotoForRestRow) { return 7; + } else if (position == readRow) { + return 8; } return 0; } @@ -1489,6 +1620,10 @@ public void fillPositions(SparseIntArray sparseIntArray) { put(++pointer, p2pSectionRow, sparseIntArray); put(++pointer, p2pRow, sparseIntArray); put(++pointer, p2pDetailRow, sparseIntArray); + put(++pointer, readRow, sparseIntArray); + put(++pointer, readDetailRow, sparseIntArray); + put(++pointer, readPremiumRow, sparseIntArray); + put(++pointer, readPremiumDetailRow, sparseIntArray); } private void put(int id, int position, SparseIntArray sparseIntArray) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java index 405d743e67..515ca866b4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PrivacySettingsActivity.java @@ -78,6 +78,7 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio private int forwardsRow; private int callsRow; private int voicesRow; + private int noncontactsRow; private int emailLoginRow; private int privacyShadowRow; private int groupsRow; @@ -118,6 +119,7 @@ public class PrivacySettingsActivity extends BaseFragment implements Notificatio private boolean currentSuggest; private boolean newSuggest; private boolean archiveChats; + private boolean noncontactsValue; private boolean[] clear = new boolean[2]; private SessionsActivity devicesActivityPreload; @@ -134,6 +136,7 @@ public boolean onFragmentCreate() { TLRPC.TL_globalPrivacySettings privacySettings = getContactsController().getGlobalPrivacySettings(); if (privacySettings != null) { archiveChats = privacySettings.archive_and_mute_new_noncontact_peers; + noncontactsValue = privacySettings.new_noncontact_peers_require_premium; } updateRows(); @@ -203,8 +206,10 @@ public void onFragmentDestroy() { globalPrivacySettings.archive_and_mute_new_noncontact_peers = archiveChats; save = true; TLRPC.TL_account_setGlobalPrivacySettings req = new TLRPC.TL_account_setGlobalPrivacySettings(); - req.settings = new TLRPC.TL_globalPrivacySettings(); - req.settings.flags |= 1; + req.settings = getContactsController().getGlobalPrivacySettings(); + if (req.settings == null) { + req.settings = new TLRPC.TL_globalPrivacySettings(); + } req.settings.archive_and_mute_new_noncontact_peers = archiveChats; getConnectionsManager().sendRequest(req, (response, error) -> { @@ -360,6 +365,8 @@ public boolean supportsPredictiveItemAnimations() { } presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_VOICE_MESSAGES)); + } else if (position == noncontactsRow) { + presentFragment(new PrivacyControlActivity(ContactsController.PRIVACY_RULES_TYPE_MESSAGES)); } else if (position == emailLoginRow) { if (currentPassword == null || currentPassword.login_email_pattern == null) { return; @@ -587,6 +594,7 @@ public void didReceivedNotification(int id, int account, Object... args) { TLRPC.TL_globalPrivacySettings privacySettings = getContactsController().getGlobalPrivacySettings(); if (privacySettings != null) { archiveChats = privacySettings.archive_and_mute_new_noncontact_peers; + noncontactsValue = privacySettings.new_noncontact_peers_require_premium; } if (listAdapter != null) { listAdapter.notifyDataSetChanged(); @@ -649,8 +657,10 @@ private void updateRows(boolean notify) { groupsDetailRow = -1; if (!getMessagesController().premiumFeaturesBlocked() || getUserConfig().isPremium()) { voicesRow = rowCount++; + noncontactsRow = rowCount++; } else { voicesRow = -1; + noncontactsRow = -1; } privacyShadowRow = rowCount++; @@ -882,6 +892,7 @@ public boolean isEnabled(RecyclerView.ViewHolder holder) { position == forwardsRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_FORWARDS) || position == phoneNumberRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_PHONE) || position == voicesRow && !getContactsController().getLoadingPrivacyInfo(ContactsController.PRIVACY_RULES_TYPE_VOICE_MESSAGES) || + position == noncontactsRow || position == deleteAccountRow && !getContactsController().getLoadingDeleteInfo() || position == newChatsRow && !getContactsController().getLoadingGlobalSettings() || position == emailLoginRow || position == paymentsClearRow || position == secretMapRow || @@ -1001,7 +1012,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { value = formatRulesString(getAccountInstance(), ContactsController.PRIVACY_RULES_TYPE_VOICE_MESSAGES); } - textCell.setTextAndValue(LocaleController.getString(R.string.PrivacyVoiceMessages), value, false); + textCell.setTextAndValue(LocaleController.getString(R.string.PrivacyVoiceMessages), value, noncontactsRow != -1); ImageView imageView = textCell.getValueImageView(); if (!getUserConfig().isPremium()) { imageView.setVisibility(View.VISIBLE); @@ -1011,6 +1022,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else { imageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteGrayIcon), PorterDuff.Mode.MULTIPLY)); } + } else if (position == noncontactsRow) { + value = LocaleController.getString(noncontactsValue ? R.string.ContactsAndPremium : R.string.P2PEverybody); + textCell.setTextAndValue(LocaleController.getString(R.string.PrivacyMessages), value, false); } else if (position == passportRow) { textCell.setText(LocaleController.getString("TelegramPassport", R.string.TelegramPassport), true); } else if (position == deleteAccountRow) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java index 7e8006925c..d6380b3a99 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -36,10 +36,12 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.ColorFilter; import android.graphics.LinearGradient; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Path; +import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; @@ -64,6 +66,7 @@ import android.text.TextUtils; import android.text.style.ClickableSpan; import android.text.style.ForegroundColorSpan; +import android.text.style.ReplacementSpan; import android.util.Property; import android.util.SparseIntArray; import android.util.TypedValue; @@ -90,6 +93,7 @@ import androidx.annotation.Keep; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.collection.LongSparseArray; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; @@ -106,6 +110,8 @@ import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; +import com.google.android.exoplayer2.util.Log; + import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.messenger.AccountInstance; import org.telegram.messenger.AndroidUtilities; @@ -185,6 +191,7 @@ import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.ButtonBounce; import org.telegram.ui.Components.CanvasButton; import org.telegram.ui.Components.ChatActivityInterface; import org.telegram.ui.Components.ChatAvatarContainer; @@ -207,6 +214,7 @@ import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LinkSpanDrawable; import org.telegram.ui.Components.MediaActivity; +import org.telegram.ui.Components.MessagePrivateSeenView; import org.telegram.ui.Components.Paint.PersistColorPalette; import org.telegram.ui.Components.Premium.GiftPremiumBottomSheet; import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; @@ -226,6 +234,7 @@ import org.telegram.ui.Components.SharedMediaLayout; import org.telegram.ui.Components.SizeNotifierFrameLayout; import org.telegram.ui.Components.StickerEmptyView; +import org.telegram.ui.Components.Text; import org.telegram.ui.Components.TimerDrawable; import org.telegram.ui.Components.TranslateAlert2; import org.telegram.ui.Components.TypefaceSpan; @@ -357,10 +366,12 @@ public void setAlpha(int a) { private ActionBarMenuSubItem autoDeleteItem; AutoDeletePopupWrapper autoDeletePopupWrapper; protected float headerShadowAlpha = 1.0f; + private int actionBarBackgroundColor; private TopView topView; private long userId; private long chatId; - private int topicId; + private long topicId; + public boolean saved; private long dialogId; private boolean creatingChat; private boolean userBlocked; @@ -672,7 +683,7 @@ public static ProfileActivity of(long dialogId) { return new ProfileActivity(bundle); } - public int getTopicId() { + public long getTopicId() { return topicId; } @@ -859,6 +870,9 @@ public void setBackgroundColor(int color) { currentColor = color; paint.setColor(color); invalidate(); + if (!hasColorById) { + actionBarBackgroundColor = currentColor; + } } } @@ -877,8 +891,10 @@ public void setBackgroundColorId(MessagesController.PeerColor peerColor, boolean hasColorById = true; color1 = peerColor.getBgColor1(Theme.isCurrentThemeDark()); color2 = peerColor.getBgColor2(Theme.isCurrentThemeDark()); + actionBarBackgroundColor = ColorUtils.blendARGB(color1, color2, 0.25f); emojiColor = PeerColorActivity.adaptProfileEmojiColor(color1); } else { + actionBarBackgroundColor = currentColor; hasColorById = false; if (AndroidUtilities.computePerceivedBrightness(getThemedColor(Theme.key_actionBarDefault)) > .8f) { emojiColor = getThemedColor(Theme.key_windowBackgroundWhiteBlueText); @@ -1682,7 +1698,8 @@ public ProfileActivity(Bundle args, SharedMediaLayout.SharedMediaPreloader prelo public boolean onFragmentCreate() { userId = arguments.getLong("user_id", 0); chatId = arguments.getLong("chat_id", 0); - topicId = arguments.getInt("topic_id", 0); + topicId = arguments.getLong("topic_id", 0); + saved = arguments.getBoolean("saved", false); openSimilar = arguments.getBoolean("similar", false); isTopic = topicId != 0; banFromGroup = arguments.getLong("ban_chat_id", 0); @@ -2109,7 +2126,7 @@ public void onItemClick(final int id) { @Override public void onClick(DialogInterface dialog, int which) { ArrayList topicIds = new ArrayList<>(); - topicIds.add(topicId); + topicIds.add((int) topicId); getMessagesController().getTopicsController().deleteTopics(chatId, topicIds); playProfileAnimation = 0; if (parentLayout != null && parentLayout.getFragmentStack() != null) { @@ -2647,6 +2664,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { avatarContainer.setVisibility(View.GONE); avatarsViewPager.resetCurrentItem(); avatarsViewPager.setVisibility(View.VISIBLE); + if (showStatusButton != null) { + showStatusButton.setBackgroundColor(0x23ffffff); + } if (storyView != null) { storyView.setExpandProgress(1f); } @@ -2987,6 +3007,11 @@ protected boolean canShowSearchItem() { return mediaHeaderVisible; } + @Override + protected boolean includeSavedDialogs() { + return dialogId == getUserConfig().getClientUserId() && !saved; + } + @Override protected void onSearchStateChanged(boolean expanded) { AndroidUtilities.removeAdjustResize(getParentActivity(), classGuid); @@ -3462,7 +3487,7 @@ public void showCustomize() { if (did != 0) { Bundle args = new Bundle(); args.putLong("dialog_id", did); - args.putInt("topic_id", topicId); + args.putLong("topic_id", topicId); presentFragment(new ProfileNotificationsActivity(args, resourcesProvider)); } } @@ -3696,7 +3721,7 @@ public boolean onItemClick(View view, int position) { getMessagesStorage().clearSentMedia(); SharedConfig.setNoSoundHintShowed(false); SharedPreferences.Editor editor = MessagesController.getGlobalMainSettings().edit(); - editor.remove("archivehint").remove("proximityhint").remove("archivehint_l").remove("speedhint").remove("gifhint").remove("reminderhint").remove("soundHint").remove("themehint").remove("bganimationhint").remove("filterhint").remove("n_0").remove("storyprvhint").remove("storyhint").remove("storyhint2").remove("storydualhint").remove("storysvddualhint").remove("stories_camera").remove("dualcam").remove("dualmatrix").remove("dual_available").remove("archivehint").remove("askNotificationsAfter").remove("askNotificationsDuration").remove("viewoncehint").remove("taptostorysoundhint").remove("nothanos").remove("voiceoncehint").commit(); + editor.remove("archivehint").remove("proximityhint").remove("archivehint_l").remove("speedhint").remove("gifhint").remove("reminderhint").remove("soundHint").remove("themehint").remove("bganimationhint").remove("filterhint").remove("n_0").remove("storyprvhint").remove("storyhint").remove("storyhint2").remove("storydualhint").remove("storysvddualhint").remove("stories_camera").remove("dualcam").remove("dualmatrix").remove("dual_available").remove("archivehint").remove("askNotificationsAfter").remove("askNotificationsDuration").remove("viewoncehint").remove("taptostorysoundhint").remove("nothanos").remove("voiceoncehint").remove("savedhint").apply(); MessagesController.getEmojiSettings(currentAccount).edit().remove("featured_hidden").remove("emoji_featured_hidden").commit(); SharedConfig.textSelectionHintShows = 0; SharedConfig.lockRecordAudioVideoHint = 0; @@ -4396,6 +4421,9 @@ public void setTextColor(int color) { onlineTextView[2].setTextColor(color); onlineTextView[3].setTextColor(color); } + if (showStatusButton != null) { + showStatusButton.setTextColor(Theme.multAlpha(Theme.adaptHSV(color, -.02f, +.15f), 1.4f)); + } } }; } else { @@ -4950,6 +4978,9 @@ private void setAvatarExpandProgress(float animatedFracture) { nameTextView[1].setScaleX(AndroidUtilities.lerp(1.12f, 1.67f, value)); nameTextView[1].setScaleY(AndroidUtilities.lerp(1.12f, 1.67f, value)); } + if (showStatusButton != null) { + showStatusButton.setBackgroundColor(ColorUtils.blendARGB(Theme.multAlpha(Theme.adaptHSV(actionBarBackgroundColor, +0.18f, -0.1f), 0.5f), 0x23ffffff, currentExpandAnimatorValue)); + } needLayoutText(Math.min(1f, extraHeight / AndroidUtilities.dp(88f))); @@ -6215,6 +6246,9 @@ public void updateSelectedMediaTabText() { } else if (id == SharedMediaLayout.TAB_RECOMMENDED_CHANNELS) { MessagesController.ChannelRecommendations rec = MessagesController.getInstance(currentAccount).getChannelRecommendations(chatId); mediaCounterTextView.setText(LocaleController.formatPluralString("Channels", rec == null ? 0 : rec.chats.size() + rec.more)); + } else if (id == SharedMediaLayout.TAB_SAVED_MESSAGES) { + int messagesCount = getMessagesController().getSavedMessagesController().getMessagesCount(getDialogId()); + mediaCounterTextView.setText(LocaleController.formatPluralString("SavedMessagesCount", Math.max(1, messagesCount))); } } @@ -6530,6 +6564,9 @@ public void onAnimationEnd(Animator animation) { nameY = (float) Math.floor(avatarY) + AndroidUtilities.dp(1.3f) + AndroidUtilities.dp(7) * diff + titleAnimationsYDiff * (1f - avatarAnimationProgress); onlineX = -21 * AndroidUtilities.density * diff; onlineY = (float) Math.floor(avatarY) + AndroidUtilities.dp(24) + (float) Math.floor(11 * AndroidUtilities.density) * diff; + if (showStatusButton != null) { + showStatusButton.setAlpha((int) (0xFF * diff)); + } for (int a = 0; a < nameTextView.length; a++) { if (nameTextView[a] == null) { continue; @@ -6692,7 +6729,7 @@ private void needLayoutText(float diff) { nameTextView[1].requestLayout(); } - width2 = onlineTextView[1].getPaint().measureText(onlineTextView[1].getText().toString()); + width2 = onlineTextView[1].getPaint().measureText(onlineTextView[1].getText().toString()) + onlineTextView[1].getRightDrawableWidth(); layoutParams = (FrameLayout.LayoutParams) onlineTextView[1].getLayoutParams(); FrameLayout.LayoutParams layoutParams2 = (FrameLayout.LayoutParams) mediaCounterTextView.getLayoutParams(); prevWidth = layoutParams.width; @@ -7119,6 +7156,9 @@ public void onPause() { if (flagSecure != null) { flagSecure.detach(); } + if (sharedMediaLayout != null) { + sharedMediaLayout.onPause(); + } } @Override @@ -7409,6 +7449,9 @@ public AnimatorSet onCustomTransitionAnimation(final boolean isOpen, final Runna nameTextView[1].setTextColor(Color.WHITE); onlineTextView[1].setTextColor(0xB3FFFFFF); actionBar.setItemsBackgroundColor(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR, false); + if (showStatusButton != null) { + showStatusButton.setBackgroundColor(0x23ffffff); + } overlaysView.setOverlaysVisible(); } for (int a = 0; a < 2; a++) { @@ -7459,7 +7502,7 @@ public AnimatorSet onCustomTransitionAnimation(final boolean isOpen, final Runna if (previousTransitionFragment != null) { ChatAvatarContainer avatarContainer = previousTransitionFragment.getAvatarContainer(); - if (avatarContainer != null && avatarContainer.getSubtitleTextView().getLeftDrawable() != null || avatarContainer.statusMadeShorter[0]) { + if (avatarContainer != null && avatarContainer.getSubtitleTextView() instanceof SimpleTextView && ((SimpleTextView) avatarContainer.getSubtitleTextView()).getLeftDrawable() != null || avatarContainer.statusMadeShorter[0]) { transitionOnlineText = avatarContainer.getSubtitleTextView(); avatarContainer2.invalidate(); onlineTextCrosafade = true; @@ -7527,7 +7570,8 @@ public AnimatorSet onCustomTransitionAnimation(final boolean isOpen, final Runna BaseFragment previousFragment = parentLayout.getFragmentStack().size() > 1 ? parentLayout.getFragmentStack().get(parentLayout.getFragmentStack().size() - 2) : null; if (previousFragment instanceof ChatActivity) { ChatAvatarContainer avatarContainer = ((ChatActivity) previousFragment).getAvatarContainer(); - if (avatarContainer.getSubtitleTextView().getLeftDrawable() != null || avatarContainer.statusMadeShorter[0]) { + View subtitleTextView = avatarContainer.getSubtitleTextView(); + if (subtitleTextView instanceof SimpleTextView && ((SimpleTextView) subtitleTextView).getLeftDrawable() != null || avatarContainer.statusMadeShorter[0]) { transitionOnlineText = avatarContainer.getSubtitleTextView(); avatarContainer2.invalidate(); crossfadeOnlineText = true; @@ -7811,6 +7855,9 @@ private void updateRowsIds() { break; } } + if (!hasMedia) { + hasMedia = sharedMediaPreloader.hasSavedMessages; + } } if (!hasMedia && userInfo != null) { hasMedia = userInfo.stories_pinned_available; @@ -8286,6 +8333,7 @@ private void updateProfileData(boolean reload) { CharSequence newString = UserObject.getUserName(user); String newString2; + boolean hiddenStatusButton = false; if (user.id == getUserConfig().getClientUserId()) { if (UserObject.hasFallbackPhoto(getUserInfo())) { newString2 = ""; @@ -8306,6 +8354,7 @@ private void updateProfileData(boolean reload) { } else { isOnline[0] = false; newString2 = LocaleController.formatUserStatus(currentAccount, user, isOnline, shortStatus ? new boolean[1] : null); + hiddenStatusButton = user != null && !getUserConfig().isPremium() && user.status != null && user.status.by_me; if (onlineTextView[1] != null && !mediaHeaderVisible) { int key = isOnline[0] && peerColor == null ? Theme.key_profile_status : Theme.key_avatar_subtitleInProfileBlue; onlineTextView[1].setTag(key); @@ -8335,6 +8384,14 @@ private void updateProfileData(boolean reload) { } else { onlineTextView[a].setText(newString2); } + onlineTextView[a].setDrawablePadding(dp(9)); + onlineTextView[a].setRightDrawableInside(true); + onlineTextView[a].setRightDrawable(a == 1 && hiddenStatusButton ? getShowStatusButton() : null); + onlineTextView[a].setRightDrawableOnClick(a == 1 && hiddenStatusButton ? v -> { + MessagePrivateSeenView.showSheet(getContext(), currentAccount, dialogId, true, null, () -> { + getMessagesController().reloadUser(dialogId); + }, resourcesProvider); + } : null); Drawable leftIcon = currentEncryptedChat != null ? getLockIconDrawable() : null; boolean rightIconIsPremium = false, rightIconIsStatus = false; nameTextView[a].setRightDrawableOutside(a == 0); @@ -8753,6 +8810,9 @@ private void updatedPeerColor() { } onlineTextView[1].setTextColor(ColorUtils.blendARGB(applyPeerColor(statusColor, true, isOnline[0]), 0xB3FFFFFF, currentExpandAnimatorValue)); } + if (showStatusButton != null) { + showStatusButton.setBackgroundColor(ColorUtils.blendARGB(Theme.multAlpha(Theme.adaptHSV(actionBarBackgroundColor, +0.18f, -0.1f), 0.5f), 0x23ffffff, currentExpandAnimatorValue)); + } if (actionBar != null) { actionBar.setItemsBackgroundColor(peerColor != null ? 0x20ffffff : getThemedColor(Theme.key_avatar_actionBarSelectorBlue), false); @@ -11830,7 +11890,7 @@ private void checkPhotoDescriptionAlpha() { if (playProfileAnimation == 1 && (!fragmentOpened || openAnimationInProgress)) { photoDescriptionProgress = 0; } else if (playProfileAnimation == 2 && (!fragmentOpened || openAnimationInProgress)) { - photoDescriptionProgress = onlineTextView[1].getAlpha(); + photoDescriptionProgress = onlineTextView[1] == null ? 0 : onlineTextView[1].getAlpha(); } else { if (userId == UserConfig.getInstance(currentAccount).clientUserId) { photoDescriptionProgress = currentExpandAnimatorValue * (1f - customAvatarProgress); @@ -12015,4 +12075,99 @@ public void prepareBlurBitmap() { blurredView.setVisibility(View.VISIBLE); } + private ShowDrawable showStatusButton; + public ShowDrawable getShowStatusButton() { + if (showStatusButton == null) { + showStatusButton = new ShowDrawable(); + showStatusButton.setAlpha((int) (0xFF * Math.min(1f, extraHeight / AndroidUtilities.dp(88f)))); + showStatusButton.setBackgroundColor(ColorUtils.blendARGB(Theme.multAlpha(Theme.adaptHSV(actionBarBackgroundColor, +0.18f, -0.1f), 0.5f), 0x23ffffff, currentExpandAnimatorValue)); + } + return showStatusButton; + } + + private static class ShowDrawable extends Drawable implements SimpleTextView.PressableDrawable { + + private final Text text; + public final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + public ShowDrawable() { + text = new Text(LocaleController.getString(R.string.StatusHiddenShow), 11); + backgroundPaint.setColor(0x1f000000); + } + + private int textColor; + public void setBackgroundColor(int backgroundColor) { + if (backgroundPaint.getColor() != backgroundColor) { + backgroundPaint.setColor(backgroundColor); + invalidateSelf(); + } + } + public void setTextColor(int textColor) { + if (this.textColor != textColor) { + this.textColor = textColor; + invalidateSelf(); + } + } + + @Override + public void draw(@NonNull Canvas canvas) { + if (alpha <= 0) return; + AndroidUtilities.rectTmp.set(getBounds()); + canvas.save(); + final float s = bounce.getScale(0.1f); + canvas.scale(s, s, AndroidUtilities.rectTmp.centerX(), AndroidUtilities.rectTmp.centerY()); + final int wasAlpha = backgroundPaint.getAlpha(); + backgroundPaint.setAlpha((int) (wasAlpha * alpha)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(20), dp(20), backgroundPaint); + backgroundPaint.setAlpha(wasAlpha); + text.draw(canvas, AndroidUtilities.rectTmp.left + dp(5.5f), AndroidUtilities.rectTmp.centerY(), textColor, alpha); + canvas.restore(); + } + + private float alpha = 1f; + @Override + public void setAlpha(int alpha) { + this.alpha = alpha / 255f; + invalidateSelf(); + } + + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + + } + + @Override + public int getIntrinsicWidth() { + return (int) (text.getCurrentWidth() + dp(11)); + } + + @Override + public int getIntrinsicHeight() { + return dp(17.33f); + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } + + private boolean pressed; + private final ButtonBounce bounce = new ButtonBounce(null) { + @Override + public void invalidate() { + invalidateSelf(); + } + }; + + @Override + public void setPressed(boolean pressed) { + bounce.setPressed(pressed); + this.pressed = pressed; + } + + @Override + public boolean isPressed() { + return pressed; + } + } + } \ No newline at end of file diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java index f4f6f27523..07429a0453 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileNotificationsActivity.java @@ -77,7 +77,7 @@ public class ProfileNotificationsActivity extends BaseFragment implements Notifi private Theme.ResourcesProvider resourcesProvider; private long dialogId; - private int topicId; + private long topicId; private boolean addingException; @@ -132,7 +132,7 @@ public ProfileNotificationsActivity(Bundle args, Theme.ResourcesProvider resourc super(args); this.resourcesProvider = resourcesProvider; dialogId = args.getLong("dialog_id"); - topicId = args.getInt("topic_id"); + topicId = args.getLong("topic_id"); addingException = args.getBoolean("exception", false); } @@ -404,7 +404,7 @@ public boolean supportsPredictiveItemAnimations() { } else if (position == soundRow) { Bundle bundle = new Bundle(); bundle.putLong("dialog_id", dialogId); - bundle.putInt("topic_id", topicId); + bundle.putLong("topic_id", topicId); presentFragment(new NotificationsSoundActivity(bundle, resourcesProvider)); } else if (position == ringtoneRow) { try { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SecretVoicePlayer.java b/TMessagesProj/src/main/java/org/telegram/ui/SecretVoicePlayer.java index 31b0ec364b..1a0b328866 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SecretVoicePlayer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SecretVoicePlayer.java @@ -17,7 +17,11 @@ import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; @@ -25,29 +29,36 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.text.Layout; +import android.util.AndroidRuntimeException; import android.view.Gravity; import android.view.KeyEvent; +import android.view.TextureView; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; +import android.view.animation.LinearInterpolator; import android.widget.FrameLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.core.view.WindowInsetsCompat; +import androidx.recyclerview.widget.ChatListItemAnimator; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.util.Log; import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BuildVars; import org.telegram.messenger.FileLoader; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; import org.telegram.messenger.Utilities; @@ -58,12 +69,14 @@ import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.AudioVisualizerDrawable; import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EarListener; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.ScaleStateListAnimator; import org.telegram.ui.Components.SeekBar; import org.telegram.ui.Components.SeekBarWaveform; import org.telegram.ui.Components.Text; import org.telegram.ui.Components.ThanosEffect; +import org.telegram.ui.Components.TimerParticles; import org.telegram.ui.Components.VideoPlayer; import org.telegram.ui.Stories.recorder.HintView2; import org.telegram.ui.Stories.recorder.StoryRecorder; @@ -74,8 +87,6 @@ public class SecretVoicePlayer extends Dialog { public final Context context; -// WindowManager windowManager; -// private final WindowManager.LayoutParams windowLayoutParams; private FrameLayout windowView; private FrameLayout containerView; @@ -89,13 +100,15 @@ public class SecretVoicePlayer extends Dialog { private boolean open; private float openProgress; - private float openProgressLinear; + private float openProgress2; private VideoPlayer player; private HintView2 hintView; private TextView closeButton; + private EarListener earListener; + public SecretVoicePlayer(Context context) { super(context, R.style.TransparentDialog); this.context = context; @@ -140,6 +153,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } }); containerView = new FrameLayout(context) { + private final Path clipPath = new Path(); @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (child == myCell || child == hintView) { @@ -149,9 +163,35 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { canvas.restore(); return r; } + if (child == textureView) { + canvas.save(); + + clipPath.rewind(); + clipPath.addCircle(myCell.getX() + rect.centerX(), myCell.getY() + rect.centerY(), rect.width() / 2f, Path.Direction.CW); + canvas.clipPath(clipPath); + canvas.clipRect(0, AndroidUtilities.lerp(clipTop, 0, openProgress), getWidth(), AndroidUtilities.lerp(clipBottom, getHeight(), openProgress)); + canvas.translate( + - textureView.getX(), + - textureView.getY() + ); + canvas.translate( + myCell.getX() + rect.left, + myCell.getY() + rect.top + ); + canvas.scale( + rect.width() / textureView.getMeasuredWidth(), + rect.height() / textureView.getMeasuredHeight(), + textureView.getX(), + textureView.getY() + ); + boolean r = super.drawChild(canvas, child, drawingTime); + canvas.restore(); + return r; + } return super.drawChild(canvas, child, drawingTime); } }; + containerView.setClipToPadding(false); windowView.addView(containerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); if (Build.VERSION.SDK_INT >= 21) { @@ -177,6 +217,9 @@ public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets i }); } + if (SharedConfig.raiseToListen) { + earListener = new EarListener(context); + } } private void prepareBlur(View withoutView) { @@ -221,7 +264,9 @@ protected void onCreate(Bundle savedInstanceState) { WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; } - params.flags |= WindowManager.LayoutParams.FLAG_SECURE; + if (!BuildVars.DEBUG_PRIVATE_VERSION) { + params.flags |= WindowManager.LayoutParams.FLAG_SECURE; + } params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; if (Build.VERSION.SDK_INT >= 28) { @@ -233,28 +278,28 @@ protected void onCreate(Bundle savedInstanceState) { AndroidUtilities.setLightNavigationBar(windowView, !Theme.isCurrentThemeDark()); } - private Theme.ResourcesProvider resourcesProvider; - private MessageObject messageObject; - private ChatMessageCell myCell; - private ChatMessageCell cell; - private float tx, ty; private boolean hasTranslation; private float dtx, dty; private boolean hasDestTranslation; + private float heightdiff; private void setupTranslation() { if (hasTranslation || windowView.getWidth() <= 0) return; if (cell != null) { int[] loc = new int[2]; cell.getLocationOnScreen(loc); tx = loc[0] - insets.left - (windowView.getWidth() - insets.left - insets.right - cell.getWidth()) / 2f; - ty = loc[1] - insets.top - (windowView.getHeight() - insets.top - insets.bottom - cell.getHeight()) / 2f; + ty = loc[1] - insets.top - (windowView.getHeight() - insets.top - insets.bottom - cell.getHeight() - heightdiff) / 2f; if (!hasDestTranslation) { hasDestTranslation = true; dtx = 0; float cy = Utilities.clamp(loc[1] + cell.getHeight() / 2f, windowView.getHeight() * .7f, windowView.getHeight() * .3f); dty = cy - cell.getHeight() / 2f - (windowView.getHeight() - cell.getHeight()) / 2f; - dty = AndroidUtilities.lerp(0, dty, .78f); + if (isRound) { + dty = 0; + } else { + dty = AndroidUtilities.lerp(0, dty, .78f); + } } updateTranslation(); } else { @@ -272,11 +317,21 @@ private void updateTranslation() { } } + private Theme.ResourcesProvider resourcesProvider; + private MessageObject messageObject; + private ChatMessageCell myCell; + private ChatMessageCell cell; + private TextureView textureView; + private boolean renderedFirstFrame; + private final RectF rect = new RectF(); + private boolean isRound; + private float clipTop = 0, clipBottom = 0; private AudioVisualizerDrawable audioVisualizerDrawable; private boolean setCellInvisible; private Runnable openAction, closeAction; + public void setCell(ChatMessageCell messageCell, Runnable openAction, Runnable closeAction) { this.openAction = openAction; this.closeAction = closeAction; @@ -286,7 +341,9 @@ public void setCell(ChatMessageCell messageCell, Runnable openAction, Runnable c } cell = messageCell; messageObject = cell != null ? cell.getMessageObject() : null; + isRound = messageObject != null && messageObject.isRoundVideo(); resourcesProvider = cell != null ? cell.getResourcesProvider() : null; + int videoDp = 360; if (cell != null) { clipTop = messageCell.parentBoundsTop; @@ -297,10 +354,181 @@ public void setCell(ChatMessageCell messageCell, Runnable openAction, Runnable c clipBottom += parent.getY(); } + int width = cell.getWidth(); + int height = cell.getHeight(); + if (isRound) { + height = (int) Math.min(dp(360), Math.min(width, AndroidUtilities.displaySize.y)); + } + heightdiff = height - cell.getHeight(); + final int finalWidth = width; + final int finalHeight = height; + videoDp = (int) Math.ceil(Math.min(width, height) * .92f / AndroidUtilities.density); + myCell = new ChatMessageCell(getContext(), false, null, cell.getResourcesProvider()) { + + @Override + public int getBoundsLeft() { + return 0; + } + + @Override + public int getBoundsRight() { + return getWidth(); + } + @Override public void setPressed(boolean pressed) {} + + private boolean setRect = false; + final RectF fromRect = new RectF(); + final RectF toRect = new RectF(); + + private RadialGradient radialGradient; + private Paint radialPaint; + private Matrix radialMatrix; + + @Override + protected void onDraw(Canvas canvas) { + if (isRound) { + if (!setRect) { + fromRect.set(getPhotoImage().getImageX(), getPhotoImage().getImageY(), getPhotoImage().getImageX2(), getPhotoImage().getImageY2()); + final float sz = Math.min(getMeasuredWidth(), getMeasuredHeight()) * .92f; + toRect.set((getMeasuredWidth() - sz) / 2f, (getMeasuredHeight() - sz) / 2f, (getMeasuredWidth() + sz) / 2f, (getMeasuredHeight() + sz) / 2f); + setRect = true; + + radialGradient = new RadialGradient(0, 0, 48, new int[] { 0xffffffff, 0xffffffff, 0 }, new float[] { 0, 0.8f, 1 }, Shader.TileMode.CLAMP); + radialPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + radialPaint.setShader(radialGradient); + radialPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + radialMatrix = new Matrix(); + } + + AndroidUtilities.lerp(fromRect, toRect, openProgress, rect); + setImageCoords(rect.left, rect.top, rect.width(), rect.height()); + getPhotoImage().setRoundRadius((int) rect.width()); + + if (openProgress > 0 && renderedFirstFrame) { + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + } + + radialProgressAlpha = 1f - openProgress; + } + super.onDraw(canvas); + if (isRound && openProgress > 0 && renderedFirstFrame) { + canvas.restore(); + } + } + + @Override + public void drawTime(Canvas canvas, float alpha, boolean fromParent) { + canvas.save(); + if (isRound) { + final float timeWidth = this.timeWidth + AndroidUtilities.dp(8 + (messageObject != null && messageObject.isOutOwner() ? 20 + (messageObject != null && messageObject.type == MessageObject.TYPE_EMOJIS ? 4 : 0) : 0)); + canvas.translate((toRect.right - timeWidth - timeX) * openProgress, 0); + } + super.drawTime(canvas, alpha, fromParent); + canvas.restore(); + } + + @Override + protected void drawRadialProgress(Canvas canvas) { + super.drawRadialProgress(canvas); + } + + @Override + public void setVisibility(int visibility) { + super.setVisibility(visibility); + if (textureView != null && visibility == View.GONE) { + textureView.setVisibility(visibility); + } + } + + private Path clipPath = new Path(); + private Paint clipPaint; + private Paint progressPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private TimerParticles timerParticles; + private AnimatedFloat renderedFirstFrameT = new AnimatedFloat(0, this, 0, 120, new LinearInterpolator()); + + private Paint getClipPaint() { + if (clipPaint == null) { + clipPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + clipPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + } + return clipPaint; + } + + @Override + public void drawBlurredPhoto(Canvas canvas) { + if (radialPaint != null) { + if (openProgress > 0) { + if (renderedFirstFrame) { + if (drawingToBitmap) { + Bitmap bitmap = textureView.getBitmap(); + if (bitmap != null) { + canvas.save(); + clipPath.rewind(); + clipPath.addCircle(rect.centerX(), rect.centerY(), rect.width() / 2f, Path.Direction.CW); + canvas.clipPath(clipPath); + canvas.scale(rect.width() / bitmap.getWidth(), rect.height() / bitmap.getHeight()); + canvas.translate(rect.left, rect.top); + canvas.drawBitmap(bitmap, 0, 0, null); + canvas.restore(); + bitmap.recycle(); + } + } else { + canvas.drawCircle(rect.centerX(), rect.centerY(), rect.width() / 2f, getClipPaint()); + } + getPhotoImage().setAlpha(Math.max(1f - renderedFirstFrameT.set(renderedFirstFrame), 1f - openProgress)); + getPhotoImage().draw(canvas); + } else { + getPhotoImage().draw(canvas); + } + } + radialMatrix.reset(); + final float s = rect.width() / (96f * 0.8f) * openProgress2; + radialMatrix.postScale(s, s); + radialMatrix.postTranslate(rect.centerX(), rect.centerY()); + radialGradient.setLocalMatrix(radialMatrix); + canvas.saveLayerAlpha(rect, 0xFF, Canvas.ALL_SAVE_FLAG); + super.drawBlurredPhoto(canvas); + canvas.save(); + canvas.drawRect(rect, radialPaint); + canvas.restore(); + canvas.restore(); + } else { + super.drawBlurredPhoto(canvas); + } + canvas.saveLayerAlpha(rect, (int) (0xB2 * openProgress2), Canvas.ALL_SAVE_FLAG); + progressPaint.setStyle(Paint.Style.STROKE); + progressPaint.setStrokeWidth(dp(3.33f)); + progressPaint.setColor(0xffffffff); + progressPaint.setStrokeCap(Paint.Cap.ROUND); + AndroidUtilities.rectTmp.set(rect); + AndroidUtilities.rectTmp.inset(dp(7), dp(7)); + canvas.drawArc(AndroidUtilities.rectTmp, -90, -360 * (1f - progress), false, progressPaint); + if (timerParticles == null) { + timerParticles = new TimerParticles(120); + timerParticles.big = true; + } + progressPaint.setStrokeWidth(dp(2.8f)); + timerParticles.draw(canvas, progressPaint, AndroidUtilities.rectTmp, (1f - progress) * -360, 1f); + canvas.restore(); + } + + @Override + public void drawBlurredPhotoParticles(Canvas canvas) { + final float s = AndroidUtilities.lerp(1, 1.5f, openProgress2); +// canvas.scale(s, s, rect.centerX(), rect.centerY()); + super.drawBlurredPhotoParticles(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setMeasuredDimension(finalWidth, finalHeight); + } }; + cell.copyVisiblePartTo(myCell); + myCell.copySpoilerEffect2AttachIndexFrom(cell); myCell.setDelegate(new ChatMessageCell.ChatMessageCellDelegate() { @Override public boolean canPerformActions() { @@ -308,14 +536,26 @@ public boolean canPerformActions() { } }); myCell.setMessageObject(messageObject, cell.getCurrentMessagesGroup(), cell.pinnedBottom, cell.pinnedTop); - audioVisualizerDrawable = new AudioVisualizerDrawable(); - audioVisualizerDrawable.setParentView(myCell); - myCell.overrideAudioVisualizer(audioVisualizerDrawable); - if (myCell.getSeekBarWaveform() != null) { - myCell.getSeekBarWaveform().setExplosionRate(openProgressLinear); + if (!isRound) { + audioVisualizerDrawable = new AudioVisualizerDrawable(); + audioVisualizerDrawable.setParentView(myCell); + myCell.overrideAudioVisualizer(audioVisualizerDrawable); + if (myCell.getSeekBarWaveform() != null) { + myCell.getSeekBarWaveform().setExplosionRate(openProgress); + } } hasTranslation = false; - containerView.addView(myCell, new FrameLayout.LayoutParams(cell.getWidth(), cell.getHeight(), Gravity.CENTER)); + containerView.addView(myCell, new FrameLayout.LayoutParams(cell.getWidth(), height, Gravity.CENTER)); + } + + if (textureView != null) { + containerView.removeView(textureView); + textureView = null; + } + if (isRound) { + renderedFirstFrame = false; + textureView = new TextureView(context); + containerView.addView(textureView, 0, LayoutHelper.createFrame(videoDp, videoDp)); } MediaController.getInstance().pauseByRewind(); @@ -327,13 +567,19 @@ public boolean canPerformActions() { } if (cell != null && cell.getMessageObject() != null) { File file = FileLoader.getInstance(cell.getMessageObject().currentAccount).getPathToAttach(cell.getMessageObject().getDocument()); + if (file != null && !file.exists()) { + file = new File(file.getPath() + ".enc"); + } if (file == null || !file.exists()) { file = FileLoader.getInstance(cell.getMessageObject().currentAccount).getPathToMessage(cell.getMessageObject().messageOwner); + if (file != null && !file.exists()) { + file = new File(file.getPath() + ".enc"); + } } if ((file == null || !file.exists()) && (cell.getMessageObject().messageOwner.attachPath != null)) { file = new File(cell.getMessageObject().messageOwner.attachPath); } - if (file == null) { + if (file == null || !file.exists()) { return; } player = new VideoPlayer(); @@ -360,7 +606,10 @@ public void onVideoSizeChanged(int width, int height, int unappliedRotationDegre @Override public void onRenderedFirstFrame() { - + AndroidUtilities.runOnUIThread(() -> { + renderedFirstFrame = true; + myCell.invalidate(); + }); } @Override @@ -373,19 +622,27 @@ public boolean onSurfaceDestroyed(SurfaceTexture surfaceTexture) { return false; } }); - player.setAudioVisualizerDelegate(new VideoPlayer.AudioVisualizerDelegate() { - @Override - public void onVisualizerUpdate(boolean playing, boolean animate, float[] values) { - audioVisualizerDrawable.setWaveform(playing, animate, values); - } + if (audioVisualizerDrawable != null) { + player.setAudioVisualizerDelegate(new VideoPlayer.AudioVisualizerDelegate() { + @Override + public void onVisualizerUpdate(boolean playing, boolean animate, float[] values) { + audioVisualizerDrawable.setWaveform(playing, animate, values); + } - @Override - public boolean needUpdate() { - return audioVisualizerDrawable.getParentView() != null; - } - }); + @Override + public boolean needUpdate() { + return audioVisualizerDrawable.getParentView() != null; + } + }); + } + if (isRound) { + player.setTextureView(textureView); + } player.preparePlayer(Uri.fromFile(file), "other"); player.play(); + if (earListener != null) { + earListener.attachPlayer(player); + } } if (hintView != null) { @@ -410,16 +667,26 @@ public boolean needUpdate() { name = chat.title; } } - hintView.setText(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.VoiceOnceOutHint, name))); + hintView.setText(AndroidUtilities.replaceTags(LocaleController.formatString(isRound ? R.string.VideoOnceOutHint : R.string.VoiceOnceOutHint, name))); } else { - hintView.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.VoiceOnceHint))); + hintView.setText(AndroidUtilities.replaceTags(LocaleController.getString(isRound ? R.string.VideoOnceHint : R.string.VoiceOnceHint))); } hintView.setRounding(12); hintView.setPadding(dp(!isOut && !cell.pinnedBottom ? 6 : 0), 0, 0, 0); - hintView.setJointPx(0, dp(34)); + if (isRound) { + hintView.setJointPx(0.5f, 0); + hintView.setTextAlign(Layout.Alignment.ALIGN_CENTER); + } else { + hintView.setJointPx(0, dp(34)); + hintView.setTextAlign(Layout.Alignment.ALIGN_NORMAL); + } hintView.setTextSize(14); hintView.setMaxWidthPx(HintView2.cutInFancyHalf(hintView.getText(), hintView.getTextPaint())); - containerView.addView(hintView, LayoutHelper.createFrame((int) (cell.getWidth() / AndroidUtilities.density * .6f), 150, Gravity.CENTER, (cell.getWidth() * -(1f - .6f) / 2f + cell.getBoundsLeft()) / AndroidUtilities.density + 1, -75 - (cell.getHeight() / AndroidUtilities.density) / 2f - 8, 0, 0)); + if (isRound) { + containerView.addView(hintView, LayoutHelper.createFrame((int) (cell.getWidth() / AndroidUtilities.density * .6f), 150, Gravity.CENTER, 0, -75 - ((cell.getHeight() + heightdiff) / AndroidUtilities.density) / 2f, 0, 0)); + } else { + containerView.addView(hintView, LayoutHelper.createFrame((int) (cell.getWidth() / AndroidUtilities.density * .6f), 150, Gravity.CENTER, (cell.getWidth() * -(1f - .6f) / 2f + cell.getBoundsLeft()) / AndroidUtilities.density + 1, -75 - (cell.getHeight() / AndroidUtilities.density) / 2f - 8, 0, 0)); + } hintView.show(); } @@ -461,14 +728,20 @@ public void show() { AndroidUtilities.runOnUIThread(this.openAction); this.openAction = null; } + + if (earListener != null) { + earListener.attach(); + } } private Runnable checkTimeRunnable = this::checkTime; + private float progress = 0; + private void checkTime() { if (player == null) { return; } - float progress = player.getCurrentPosition() / (float) player.getDuration(); + progress = player.getCurrentPosition() / (float) player.getDuration(); if (myCell != null) { myCell.overrideDuration((player.getDuration() - player.getCurrentPosition()) / 1000L); myCell.updatePlayingMessageProgress(); @@ -500,8 +773,8 @@ public void onBackPressed() { } if (!dismissing && messageObject != null && !messageObject.isOutOwner()) { backDialog = new AlertDialog.Builder(getContext(), resourcesProvider) - .setTitle(LocaleController.getString(R.string.VoiceOnceCloseTitle)) - .setMessage(LocaleController.getString(R.string.VoiceOnceCloseMessage)) + .setTitle(LocaleController.getString(isRound ? R.string.VideoOnceCloseTitle : R.string.VoiceOnceCloseTitle)) + .setMessage(LocaleController.getString(isRound ? R.string.VideoOnceCloseMessage : R.string.VoiceOnceCloseMessage)) .setPositiveButton(LocaleController.getString(R.string.Continue), (di, w) -> { if (backDialog != null) { backDialog.dismiss(); @@ -541,8 +814,8 @@ public void dismiss() { player.releasePlayer(true); player = null; } - if (myCell != null && myCell.getSeekBarWaveform() != null) { - myCell.getSeekBarWaveform().setExplosionRate(openProgressLinear); + if (!isRound && myCell != null && myCell.getSeekBarWaveform() != null) { + myCell.getSeekBarWaveform().setExplosionRate(openProgress); } hasTranslation = false; setupTranslation(); @@ -550,8 +823,8 @@ public void dismiss() { if (thanosEffect == null) { AndroidUtilities.runOnUIThread(super::dismiss); if (cell != null) { - cell.invalidate(); cell.setVisibility(View.VISIBLE); + cell.invalidate(); } } @@ -560,64 +833,103 @@ public void dismiss() { windowView.invalidate(); if (this.closeAction != null) { + if (cell != null) { + cell.makeVisibleAfterChange = true; +// AndroidUtilities.runOnUIThread(() -> { +// cell.makeVisibleAfterChange = false; +// cell.setVisibility(View.VISIBLE); +// cell.invalidate(); +// }, ChatListItemAnimator.DEFAULT_DURATION); + } + AndroidUtilities.runOnUIThread(this.closeAction); this.closeAction = null; + myCell.setInvalidateCallback(() -> {}); thanosEffect = new ThanosEffect(context, null); windowView.addView(thanosEffect, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); - thanosEffect.animate(myCell, () -> { - super.dismiss(); - if (cell != null) { - cell.setVisibility(View.VISIBLE); - cell.invalidate(); - } - }); + thanosEffect.animate(myCell, 1.5f, super::dismiss); WindowManager.LayoutParams params = getWindow().getAttributes(); params.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; getWindow().setAttributes(params); } + + if (earListener != null) { + earListener.detach(); + } } private ValueAnimator openAnimator; + private ValueAnimator open2Animator; private void animateOpenTo(boolean open, Runnable after) { if (openAnimator != null) { openAnimator.cancel(); } + if (open2Animator != null) { + open2Animator.cancel(); + } setupTranslation(); - openAnimator = ValueAnimator.ofFloat(openProgressLinear, open ? 1 : 0); + openAnimator = ValueAnimator.ofFloat(openProgress, open ? 1 : 0); openAnimator.addUpdateListener(anm -> { - openProgressLinear = (float) anm.getAnimatedValue(); - openProgress = open ? CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(openProgressLinear) : 1f - CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(1f - openProgressLinear); + openProgress = (float) anm.getAnimatedValue(); windowView.invalidate(); containerView.invalidate(); + if (isRound) { + myCell.invalidate(); + } updateTranslation(); if (closeButton != null) { closeButton.setAlpha(openProgress); } - if (myCell != null && myCell.getSeekBarWaveform() != null) { - myCell.getSeekBarWaveform().setExplosionRate((open ? CubicBezierInterpolator.EASE_OUT : CubicBezierInterpolator.EASE_IN).getInterpolation(Utilities.clamp(openProgressLinear * 1.25f, 1f, 0f))); + if (!isRound && myCell != null && myCell.getSeekBarWaveform() != null) { + myCell.getSeekBarWaveform().setExplosionRate((open ? CubicBezierInterpolator.EASE_OUT : CubicBezierInterpolator.EASE_IN).getInterpolation(Utilities.clamp(openProgress * 1.25f, 1f, 0f))); } }); openAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - openProgress = openProgressLinear = open ? 1 : 0; + openProgress = open ? 1 : 0; windowView.invalidate(); containerView.invalidate(); updateTranslation(); if (closeButton != null) { closeButton.setAlpha(openProgress); } - if (myCell != null && myCell.getSeekBarWaveform() != null) { - myCell.getSeekBarWaveform().setExplosionRate(openProgressLinear); + if (isRound) { + myCell.invalidate(); + } + if (!isRound && myCell != null && myCell.getSeekBarWaveform() != null) { + myCell.getSeekBarWaveform().setExplosionRate(openProgress); } if (after != null) { after.run(); } } }); - openAnimator.setDuration(!open && closeAction == null ? 330 : 520); + final long duration = !open && closeAction == null ? 330 : 520; + openAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + openAnimator.setDuration(duration); openAnimator.start(); + + open2Animator = ValueAnimator.ofFloat(openProgress2, open ? 1 : 0); + open2Animator.addUpdateListener(anm -> { + openProgress2 = (float) anm.getAnimatedValue(); + if (isRound) { + myCell.invalidate(); + } + }); + open2Animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + openProgress2 = open ? 1 : 0; + if (isRound) { + myCell.invalidate(); + } + } + }); + open2Animator.setDuration((long) (1.5f * duration)); + open2Animator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + open2Animator.start(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java index 536b08afc2..04c665a528 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java @@ -286,7 +286,7 @@ private void loadStatistic() { if (recentPostsAll.size() > 0) { int lastPostId = recentPostsAll.get(0).getId(); int count = recentPostsAll.size(); - getMessagesStorage().getMessages(-chatId, 0, false, count, lastPostId, 0, 0, classGuid, 0, false, 0, 0, true, false, null); + getMessagesStorage().getMessages(-chatId, 0, false, count, lastPostId, 0, 0, classGuid, 0, 0, 0, 0, true, false, null); } AndroidUtilities.runOnUIThread(() -> { @@ -2212,7 +2212,7 @@ private void loadMessages() { for (int i = 0; i < messages.size(); i++) { messageObjects.add(new MessageObject(currentAccount, messages.get(i), false, true)); } - getMessagesStorage().putMessages(messages, false, true, true, 0, false, 0); + getMessagesStorage().putMessages(messages, false, true, true, 0, 0, 0); } AndroidUtilities.runOnUIThread(() -> { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java index 1aa07b023f..b84e451e0d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java @@ -35,6 +35,7 @@ import android.text.style.CharacterStyle; import android.text.style.ClickableSpan; import android.text.style.URLSpan; +import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -122,6 +123,7 @@ import org.telegram.ui.Components.ChatActivityEnterView; import org.telegram.ui.Components.ChatAttachAlert; import org.telegram.ui.Components.ChatAttachAlertDocumentLayout; +import org.telegram.ui.Components.ChatGreetingsView; import org.telegram.ui.Components.ColoredImageSpan; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.CubicBezierInterpolator; @@ -132,6 +134,7 @@ import org.telegram.ui.Components.HintView; import org.telegram.ui.Components.InstantCameraView; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.LinkSpanDrawable; import org.telegram.ui.Components.LoadingDrawable; import org.telegram.ui.Components.MediaActivity; import org.telegram.ui.Components.MentionsContainerView; @@ -260,6 +263,7 @@ public class PeerStoriesView extends SizeNotifierFrameLayout implements Notifica private long dialogId; boolean isSelf; boolean isChannel; + boolean isPremiumBlocked; private float alpha = 1f; private int previousSelectedPotision = -1; @@ -280,6 +284,9 @@ public class PeerStoriesView extends SizeNotifierFrameLayout implements Notifica ChatActivityEnterView chatActivityEnterView; private ValueAnimator changeBoundAnimator; ReactionsContainerLayout reactionsContainerLayout; + private LinearLayout premiumBlockedText; + private TextView premiumBlockedText1; + private TextView premiumBlockedText2; private StoryFailView failView; private ViewPropertyAnimator failViewAnimator; @@ -2027,6 +2034,75 @@ private void createFailView() { addView(failView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 0)); } + private void createPremiumBlockedText() { + if (premiumBlockedText != null) { + return; + } + if (chatActivityEnterView == null) { + createEnterView(); + } + premiumBlockedText = new LinearLayout(getContext()); + premiumBlockedText.setOrientation(LinearLayout.HORIZONTAL); + + ImageView imageView = new ImageView(getContext()); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setScaleX(1.35f); + imageView.setScaleY(1.35f); + imageView.setImageResource(R.drawable.mini_switch_lock); + imageView.setColorFilter(new PorterDuffColorFilter(0xFF858585, PorterDuff.Mode.SRC_IN)); + + premiumBlockedText1 = new TextView(getContext()); + premiumBlockedText1.setTextColor(0xFF858585); + premiumBlockedText1.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + premiumBlockedText1.setText(LocaleController.getString(R.string.StoryRepliesLocked)); + + premiumBlockedText2 = new TextView(getContext()); + premiumBlockedText2.setTextColor(0xFFFFFFFF); + premiumBlockedText2.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + premiumBlockedText2.setBackground(Theme.createSimpleSelectorRoundRectDrawable(dp(40), 0x1affffff, 0x32ffffff)); + premiumBlockedText2.setGravity(Gravity.CENTER); + ScaleStateListAnimator.apply(premiumBlockedText2); + premiumBlockedText2.setText(LocaleController.getString(R.string.StoryRepliesLockedButton)); + premiumBlockedText2.setPadding(dp(7), 0, dp(7), 0); + + premiumBlockedText.addView(imageView, LayoutHelper.createLinear(22, 22, Gravity.CENTER_VERTICAL, 12, 1, 4, 0)); + premiumBlockedText.addView(premiumBlockedText1, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL)); + premiumBlockedText.addView(premiumBlockedText2, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 19, Gravity.CENTER_VERTICAL, 5, 0, 0, 0)); + + chatActivityEnterView.addView(premiumBlockedText, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + } + + private void updatePremiumBlockedText() { + if (premiumBlockedText1 != null) { + premiumBlockedText1.setText(LocaleController.getString(R.string.StoryRepliesLocked)); + } + if (premiumBlockedText2 != null) { + premiumBlockedText2.setText(LocaleController.getString(R.string.StoryRepliesLockedButton)); + } + } + + private void showPremiumBlockedToast() { + AndroidUtilities.shakeViewSpring(chatActivityEnterView, shiftDp = -shiftDp); + BotWebViewVibrationEffect.APP_ERROR.vibrate(); + String username = ""; + if (dialogId >= 0) { + username = UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(dialogId)); + } + Bulletin bulletin; + if (MessagesController.getInstance(currentAccount).premiumFeaturesBlocked()) { + bulletin = BulletinFactory.of(storyContainer, resourcesProvider) + .createSimpleBulletin(R.raw.star_premium_2, AndroidUtilities.replaceTags(LocaleController.formatString(R.string.UserBlockedRepliesNonPremium, username))); + } else { + bulletin = BulletinFactory.of(storyContainer, resourcesProvider) + .createSimpleBulletin(R.raw.star_premium_2, AndroidUtilities.replaceTags(LocaleController.formatString(R.string.UserBlockedRepliesNonPremium, username)), LocaleController.getString(R.string.UserBlockedNonPremiumButton), () -> { + if (storyViewer != null) { + storyViewer.presentFragment(new PremiumPreviewFragment("noncontacts")); + } + }); + } + bulletin.show(); + } + private void createEnterView() { Theme.ResourcesProvider emojiResourceProvider = new WrappedResourceProvider(resourcesProvider) { @Override @@ -2036,6 +2112,41 @@ public void appendColors() { }; chatActivityEnterView = new ChatActivityEnterView(AndroidUtilities.findActivity(getContext()), this, null, true, emojiResourceProvider) { + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (!isEnabled()) { + AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); + boolean hit = AndroidUtilities.rectTmp.contains(ev.getX(), ev.getY()); + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + if (hit && premiumBlockedText2 != null) { + premiumBlockedText2.setPressed(true); + } + } else if (ev.getAction() == MotionEvent.ACTION_UP) { + if (premiumBlockedText2 != null) { + if (hit && premiumBlockedText2.isPressed()) { + showPremiumBlockedToast(); + } + premiumBlockedText2.setPressed(false); + } + } else if (ev.getAction() == MotionEvent.ACTION_CANCEL) { + if (premiumBlockedText2 != null) { + premiumBlockedText2.setPressed(false); + } + } + return premiumBlockedText2 != null && premiumBlockedText2.isPressed(); + } + return super.dispatchTouchEvent(ev); + } + + @Override + public void setHorizontalPadding(float padding, float progress, boolean allowShare) { + float leftPadding = -padding * (1f - progress); + if (premiumBlockedText != null) { + premiumBlockedText.setTranslationX(-leftPadding); + } + super.setHorizontalPadding(padding, progress, allowShare); + } + private Animator messageEditTextAnimator; private int chatActivityEnterViewAnimateFromTop; int lastContentViewHeight; @@ -2136,12 +2247,15 @@ protected void isRecordingStateChanged() { } private void checkRecording() { - isRecording = chatActivityEnterView.isRecordingAudioVideo() || (recordedAudioPanel != null && recordedAudioPanel.getVisibility() == View.VISIBLE); - if (isActive) { - delegate.setIsRecording(isRecording); + final boolean wasRecording = isRecording; + isRecording = chatActivityEnterView.isRecordingAudioVideo() || chatActivityEnterView.seekbarVisible() || (recordedAudioPanel != null && recordedAudioPanel.getVisibility() == View.VISIBLE); + if (wasRecording != isRecording) { + if (isActive) { + delegate.setIsRecording(isRecording); + } + invalidate(); + storyContainer.invalidate(); } - invalidate(); - storyContainer.invalidate(); } @Override @@ -2227,7 +2341,7 @@ public void needStartRecordVideo(int state, boolean notify, int scheduleDate, in checkInstantCameraView(); if (instantCameraView != null) { if (state == 0) { - instantCameraView.showCamera(); + instantCameraView.showCamera(false); } else if (state == 1 || state == 3 || state == 4) { instantCameraView.send(state, notify, scheduleDate, ttl); } else if (state == 2 || state == 5) { @@ -2236,6 +2350,13 @@ public void needStartRecordVideo(int state, boolean notify, int scheduleDate, in } } + @Override + public void toggleVideoRecordingPause() { + if (instantCameraView != null) { + instantCameraView.togglePause(); + } + } + @Override public void needChangeVideoPreviewState(int state, float seekProgress) { if (instantCameraView != null) { @@ -2294,6 +2415,16 @@ public TL_stories.StoryItem getReplyToStory() { return currentStory.storyItem; } + @Override + public boolean onceVoiceAvailable() { + TLRPC.User user = null; + if (dialogId >= 0) { + user = MessagesController.getInstance(currentAccount).getUser(dialogId); + } else { + return false; + } + return user != null && !UserObject.isUserSelf(user) && !user.bot; + } }); setDelegate(chatActivityEnterView); chatActivityEnterView.shouldDrawBackground = false; @@ -2827,6 +2958,7 @@ private void bindInternal(int startFromPosition) { if (dialogId >= 0) { isSelf = dialogId == UserConfig.getInstance(currentAccount).getClientUserId(); TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + isPremiumBlocked = !UserConfig.getInstance(currentAccount).isPremium() && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(dialogId); avatarDrawable.setInfo(currentAccount, user); headerView.backupImageView.getImageReceiver().setForUserOrChat(user, avatarDrawable); if (isSelf) { @@ -2854,6 +2986,7 @@ private void bindInternal(int startFromPosition) { } else { isSelf = false; isChannel = true; + isPremiumBlocked = false; if (storiesController.canEditStories(dialogId) || BuildVars.DEBUG_PRIVATE_VERSION) { userCanSeeViews = true; @@ -2949,6 +3082,15 @@ private void bindInternal(int startFromPosition) { if (chatActivityEnterView == null) { createEnterView(); } + if (isPremiumBlocked && premiumBlockedText == null) { + createPremiumBlockedText(); + } + if (premiumBlockedText != null) { + if (isPremiumBlocked) { + updatePremiumBlockedText(); + } + premiumBlockedText.setVisibility(isPremiumBlocked ? View.VISIBLE : View.GONE); + } if (failView != null) { failView.setVisibility(View.GONE); } @@ -3310,6 +3452,7 @@ protected void onAttachedToWindow() { NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesListUpdated); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.stealthModeChanged); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesLimitUpdate); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.userIsPremiumBlockedUpadted); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); } @@ -3341,6 +3484,7 @@ protected void onDetachedFromWindow() { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesListUpdated); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.stealthModeChanged); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesLimitUpdate); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.userIsPremiumBlockedUpadted); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); } @@ -3419,6 +3563,12 @@ public boolean presentFragment(BaseFragment fragment) { } }, activity, storyLimit.getLimitReachedType(), currentAccount, null); delegate.showDialog(sheet); + } else if (id == NotificationCenter.userIsPremiumBlockedUpadted) { + boolean wasPremiumBlocked = isPremiumBlocked; + isPremiumBlocked = dialogId >= 0 && !UserConfig.getInstance(currentAccount).isPremium() && MessagesController.getInstance(currentAccount).isUserPremiumBlocked(dialogId); + if (isPremiumBlocked != wasPremiumBlocked) { + updatePosition(); + } } } @@ -3430,13 +3580,18 @@ private void checkStealthMode(boolean animated) { } AndroidUtilities.cancelRunOnUIThread(updateStealthModeTimer); TL_stories.TL_storiesStealthMode stealthMode = storiesController.getStealthMode(); - if (stealthMode != null && ConnectionsManager.getInstance(currentAccount).getCurrentTime() < stealthMode.active_until_date) { + if (isPremiumBlocked) { + stealthModeIsActive = false; + chatActivityEnterView.setEnabled(false); + chatActivityEnterView.setOverrideHint(" ", animated); + } else if (stealthMode != null && ConnectionsManager.getInstance(currentAccount).getCurrentTime() < stealthMode.active_until_date) { stealthModeIsActive = true; int time = stealthMode.active_until_date - ConnectionsManager.getInstance(currentAccount).getCurrentTime(); int minutes = time / 60; int seconds = time % 60; String textToMeasure = LocaleController.formatString("StealthModeActiveHint", R.string.StealthModeActiveHintShort, String.format(Locale.US, "%02d:%02d", 99, 99)); int w = (int) chatActivityEnterView.getEditField().getPaint().measureText(textToMeasure); + chatActivityEnterView.setEnabled(true); if (w * 1.2f >= chatActivityEnterView.getEditField().getMeasuredWidth()) { chatActivityEnterView.setOverrideHint(LocaleController.formatString("StealthModeActiveHintShort", R.string.StealthModeActiveHintShort, ""), String.format(Locale.US, "%02d:%02d", minutes, seconds), animated); } else { @@ -3445,6 +3600,7 @@ private void checkStealthMode(boolean animated) { AndroidUtilities.runOnUIThread(updateStealthModeTimer, 1000); } else { stealthModeIsActive = false; + chatActivityEnterView.setEnabled(true); chatActivityEnterView.setOverrideHint(LocaleController.getString("ReplyPrivately", R.string.ReplyPrivately), animated); } } @@ -3764,24 +3920,42 @@ private void updatePosition(boolean preload) { if (selfView != null) { selfView.setVisibility(View.GONE); } + if (bottomActionsLinearLayout != null) { + bottomActionsLinearLayout.setVisibility(View.VISIBLE); + } } else { if (UserObject.isService(dialogId) && chatActivityEnterView != null) { chatActivityEnterView.setVisibility(View.GONE); } else if (!isSelf && !isChannel && chatActivityEnterView != null) { chatActivityEnterView.setVisibility(View.VISIBLE); } + if (isPremiumBlocked && premiumBlockedText == null) { + createPremiumBlockedText(); + } + if (premiumBlockedText != null) { + if (isPremiumBlocked) { + updatePremiumBlockedText(); + } + premiumBlockedText.setVisibility(isPremiumBlocked ? View.VISIBLE : View.GONE); + } + if (chatActivityEnterView != null) { + chatActivityEnterView.setEnabled(!isPremiumBlocked); + } if (isSelf && selfView != null) { selfView.setVisibility(View.VISIBLE); } if (unsupportedContainer != null) { unsupportedContainer.setVisibility(View.GONE); } - if (UserObject.isService(dialogId)){ + if (UserObject.isService(dialogId)) { createReplyDisabledView(); replyDisabledTextView.setVisibility(View.VISIBLE); } else if (replyDisabledTextView != null) { replyDisabledTextView.setVisibility(View.GONE); } + if (bottomActionsLinearLayout != null) { + bottomActionsLinearLayout.setVisibility(View.VISIBLE); + } } if ((currentStory.caption != null || currentStory.getReply() != null) && !unsupported) { @@ -5638,7 +5812,9 @@ private void updateViewOffsets() { child.setScaleY(s); } else { child.setTranslationY(translationY); - child.setAlpha(alpha); + if (chatActivityEnterView == null || child != chatActivityEnterView.controlsView) { + child.setAlpha(alpha); + } } } } @@ -5801,7 +5977,7 @@ public long getDialogId() { }, resourcesProvider); instantCameraView.drawBlur = false; - int i = indexOfChild(chatActivityEnterView.getRecordCircle()); + int i = Math.min(indexOfChild(chatActivityEnterView.getRecordCircle()), indexOfChild(chatActivityEnterView.controlsView)); addView(instantCameraView, i, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryCaptionView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryCaptionView.java index 7852c3a557..14523dd6e6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryCaptionView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryCaptionView.java @@ -1342,7 +1342,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { verticalPadding = dp(8); if (sizeCached != size) { sizeCached = size; - int width = MeasureSpec.getSize(widthMeasureSpec) - horizontalPadding * 2; + int width = Math.max(0, MeasureSpec.getSize(widthMeasureSpec) - horizontalPadding * 2); state[0].measure(width); if (state[1] != null) { state[1].measure(width); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java index d1a9c3d8e9..22092b5e72 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java @@ -862,7 +862,7 @@ public void onAnimationEnd(Animator animation) { override = true; } } - if (peerStoriesView != null && selfStoriesViewsOffset == 0 && !inSwipeToDissmissMode && !isCaption && storiesViewPager.currentState != ViewPager.SCROLL_STATE_DRAGGING) { + if (peerStoriesView != null && selfStoriesViewsOffset == 0 && !inSwipeToDissmissMode && !isCaption && !isRecording && storiesViewPager.currentState != ViewPager.SCROLL_STATE_DRAGGING) { AndroidUtilities.getViewPositionInParent(peerStoriesView.storyContainer, this, pointPosition); ev.offsetLocation(-pointPosition[0], -pointPosition[1]); storiesViewPager.getCurrentPeerView().checkPinchToZoom(ev); @@ -900,14 +900,14 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { startX = lastTouchX = ev.getX(); startY = ev.getY(); verticalScrollDetected = false; - allowIntercept = !findClickableView(windowView, ev.getX(), ev.getY(), false); - allowSwipeToDissmiss = !findClickableView(windowView, ev.getX(), ev.getY(), true); + allowIntercept = !isRecording && !findClickableView(windowView, ev.getX(), ev.getY(), false); + allowSwipeToDissmiss = !isRecording && !findClickableView(windowView, ev.getX(), ev.getY(), true); setInTouchMode(allowIntercept && !isCaptionPartVisible); - if (allowIntercept && isCaptionPartVisible) { + if (allowIntercept && !isRecording && isCaptionPartVisible) { delayedTapRunnable = () -> setInTouchMode(true); AndroidUtilities.runOnUIThread(delayedTapRunnable, 150); } - if (allowIntercept && !keyboardVisible && !isInTextSelectionMode) { + if (allowIntercept && !keyboardVisible && !isRecording && !isInTextSelectionMode) { AndroidUtilities.runOnUIThread(longPressRunnable, 400); } } else if (ev.getAction() == MotionEvent.ACTION_MOVE) { @@ -946,7 +946,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { peerView.cancelTextSelection(); } boolean viewsAllowed = peerView != null && peerView.viewsAllowed(); - allowSwipeToReply = !viewsAllowed && peerView != null && !peerView.isChannel && storiesIntro == null; + allowSwipeToReply = !viewsAllowed && peerView != null && !peerView.isChannel && !peerView.isPremiumBlocked && storiesIntro == null; allowSelfStoriesView = viewsAllowed && !peerView.unsupported && peerView.currentStory.storyItem != null && storiesIntro == null; if (allowSelfStoriesView && keyboardHeight != 0) { allowSelfStoriesView = false; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PaintView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PaintView.java index c07b4bedf2..5240f08606 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PaintView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PaintView.java @@ -809,7 +809,7 @@ protected void onDraw(Canvas canvas) { bottomLayout.setBackground(new GradientDrawable(GradientDrawable.Orientation.TOP_BOTTOM, new int [] {0x00000000, 0x80000000} )); addView(bottomLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 44 + 60, Gravity.BOTTOM)); - paintToolsView = new PaintToolsView(context, !entry.isRepostMessage && blurManager != null); + paintToolsView = new PaintToolsView(context, entry != null && !entry.isRepostMessage && blurManager != null); paintToolsView.setPadding(dp(16), 0, dp(16), 0); paintToolsView.setDelegate(this); // paintToolsView.setSelectedIndex(MathUtils.clamp(palette.getCurrentBrush(), 0, Brush.BRUSHES_LIST.size()) + 1); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryRecorder.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryRecorder.java index 55bbf5ee98..0de5175dd6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryRecorder.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryRecorder.java @@ -3687,7 +3687,7 @@ public boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev); } }; - galleryListView.allowSearch(forAddingPart); +// galleryListView.allowSearch(forAddingPart); galleryListView.setOnBackClickListener(() -> { animateGalleryListView(false); lastGallerySelectedAlbum = null; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java index 0150e223c3..98f562c69d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TopicCreateFragment.java @@ -79,10 +79,10 @@ public class TopicCreateFragment extends BaseFragment { int iconColor; - public static TopicCreateFragment create(long chatId, int topicId) { + public static TopicCreateFragment create(long chatId, long topicId) { Bundle bundle = new Bundle(); bundle.putLong("chat_id", chatId); - bundle.putInt("topic_id", topicId); + bundle.putLong("topic_id", topicId); return new TopicCreateFragment(bundle); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java index 892f20b1e4..2518147d2c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TopicsFragment.java @@ -258,7 +258,7 @@ public class TopicsFragment extends BaseFragment implements NotificationCenter.N NotificationCenter.topicsDidLoaded }); private View blurredView; - private int selectedTopicForTablet; + private long selectedTopicForTablet; private boolean joinRequested; private ChatActivityMemberRequestsDelegate pendingRequestsDelegate; @@ -1855,7 +1855,7 @@ public void showCustomize() { AndroidUtilities.runOnUIThread(() -> { Bundle args = new Bundle(); args.putLong("dialog_id", -chatId); - args.putInt("topic_id", topic.id); + args.putLong("topic_id", topic.id); presentFragment(new ProfileNotificationsActivity(args, themeDelegate)); }, 500); } @@ -2694,7 +2694,7 @@ public void didReceivedNotification(int id, int account, Object... args) { } boolean close = (Boolean) args[2]; long dialog_id = (Long) args[0]; - int topicId = (int) args[1]; + long topicId = (Long) args[1]; if (dialog_id == -chatId && !close) { if (selectedTopicForTablet != topicId) { selectedTopicForTablet = topicId; @@ -3653,7 +3653,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi MessageObject message = searchResultMessages.get(position - messagesStartRow); TopicDialogCell dialogCell = (TopicDialogCell) holder.itemView; dialogCell.drawDivider = position != messagesEndRow - 1; - int topicId = MessageObject.getTopicId(message.messageOwner, true); + long topicId = MessageObject.getTopicId(currentAccount, message.messageOwner, true); if (topicId == 0) { topicId = 1; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TopicsNotifySettingsFragments.java b/TMessagesProj/src/main/java/org/telegram/ui/TopicsNotifySettingsFragments.java index 08a3b8d3e5..b9247f16cd 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TopicsNotifySettingsFragments.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TopicsNotifySettingsFragments.java @@ -86,7 +86,7 @@ public void onItemClick(View view, int position) { topicsFragment.setOnTopicSelectedListener((topic) -> { Bundle bundle2 = new Bundle(); bundle2.putLong("dialog_id", dialogId); - bundle2.putInt("topic_id", topic.id); + bundle2.putLong("topic_id", topic.id); bundle2.putBoolean("exception", true); ProfileNotificationsActivity fragment = new ProfileNotificationsActivity(bundle2); fragment.setDelegate(exception -> { @@ -102,7 +102,7 @@ public void onItemClick(View view, int position) { TLRPC.TL_forumTopic topic = (TLRPC.TL_forumTopic) items.get(position).topic; Bundle bundle = new Bundle(); bundle.putLong("dialog_id", dialogId); - bundle.putInt("topic_id", topic.id); + bundle.putLong("topic_id", topic.id); bundle.putBoolean("exception", false); ProfileNotificationsActivity topicsFragment = new ProfileNotificationsActivity(bundle); topicsFragment.setDelegate(new ProfileNotificationsActivity.ProfileNotificationsActivityDelegate() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/VoIPFragment.java b/TMessagesProj/src/main/java/org/telegram/ui/VoIPFragment.java index 3fc5498e17..e73083120c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/VoIPFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/VoIPFragment.java @@ -111,6 +111,7 @@ import org.telegram.ui.Components.voip.VoIPWindowView; import org.telegram.ui.Components.voip.VoIpGradientLayout; import org.telegram.ui.Components.voip.VoIpHintView; +import org.telegram.ui.Components.voip.VoIpCoverView; import org.telegram.ui.Components.voip.VoIpSnowView; import org.telegram.ui.Components.voip.VoIpSwitchLayout; import org.telegram.ui.Stories.recorder.HintView2; @@ -145,6 +146,7 @@ public class VoIPFragment implements VoIPService.StateListener, NotificationCent private ViewGroup fragmentView; private VoIpGradientLayout gradientLayout; + private VoIpCoverView voIpCoverView; private VoIpSnowView voIpSnowView; private ImageWithWavesView callingUserPhotoViewMini; @@ -784,6 +786,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { // callingUserTextureView.attachBackgroundRenderer(); frameLayout.addView(gradientLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + frameLayout.addView(voIpCoverView = new VoIpCoverView(context, callingUser, backgroundProvider) , LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); frameLayout.addView(voIpSnowView = new VoIpSnowView(context), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 220)); frameLayout.addView(callingUserTextureView); @@ -866,7 +869,7 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { emojiLayout.setOrientation(LinearLayout.HORIZONTAL); emojiLayout.setPadding(0, 0, 0, AndroidUtilities.dp(30)); emojiLayout.setClipToPadding(false); - + emojiLayout.setContentDescription(LocaleController.getString("VoipHintEncryptionKey", R.string.VoipHintEncryptionKey)); emojiLayout.setOnClickListener(view -> { if (System.currentTimeMillis() - lastContentTapTime < 500) { return; @@ -970,7 +973,6 @@ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { callingUserTitle.setText(name); callingUserTitle.setMaxLines(2); callingUserTitle.setEllipsize(TextUtils.TruncateAt.END); - callingUserTitle.setShadowLayer(AndroidUtilities.dp(3), 0, AndroidUtilities.dp(.666666667f), 0x4C000000); callingUserTitle.setTextColor(Color.WHITE); callingUserTitle.setGravity(Gravity.CENTER_HORIZONTAL); callingUserTitle.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); @@ -1587,6 +1589,7 @@ private void expandEmoji(boolean expanded) { return; } emojiExpanded = expanded; + voIpCoverView.onEmojiExpanded(expanded); if (expanded) { if (SharedConfig.callEncryptionHintDisplayedCount < 2) { SharedConfig.incrementCallEncryptionHintDisplayed(2); @@ -2124,6 +2127,7 @@ public void onAnimationEnd(Animator animation) { updateSpeakerPhoneIcon(); if (currentState == VoIPService.STATE_ESTABLISHED) { + voIpCoverView.onConnected(); callingUserPhotoViewMini.onConnected(); if (!gradientLayout.isConnectedCalled()) { int[] loc = new int[2]; @@ -2134,6 +2138,7 @@ public void onAnimationEnd(Animator animation) { } boolean isVideoMode = currentUserIsVideo || callingUserIsVideo; voIpSnowView.setState(isVideoMode); + voIpCoverView.setState(isVideoMode); backgroundProvider.setHasVideo(isVideoMode); if (callingUserIsVideo && !wasVideo && isNearEar) { diff --git a/TMessagesProj/src/main/res/drawable-hdpi/filled_open_message.png b/TMessagesProj/src/main/res/drawable-hdpi/filled_open_message.png new file mode 100644 index 0000000000000000000000000000000000000000..5dcd28d630451f67c38a18d21c542f3d6b54d0c0 GIT binary patch literal 813 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k1|%Oc%$NbB7>k44ofy`glX(f`xTHpSruq6Z zXaU(A42G$TlC0TWzSWdSpS4N|DZ_xKM31JiU*7srqY z&bQO8GeR6CT8o7*yRvTGV4x8el_|VM)LExRp-DlDMR1D%x3;{(Y7Ql_CWV83E?1Op zMBZHT;VNfSlR^el;1MSg!+jpt-aD-OQ(YcsZNBkV1Ih z2Rykn`|PqE(FfQzh^=9of6(24HBQ9!VoKaCM`h&)WUg?y@HeL`q<5I_ z(dbpY!oTiW$-@U8%k6g?o?w2k(RqiTM-{WIIjg7j=MCphF|I4IzOBjenDJoNE8WK0 z2RuI_eXIU_d=l%Fz;ULsCFWu9Qj3#!A534pLTK{8fRo(kMC{g1NM6_X;Qrh;Tdum} z9#7`8NC!?@be7X;-K1&C3ljJ}GP8m=Xu3!#l*ep;)bWP3UMY$9bmnG7e>aw-m)T)c zl5F2BT&9-$*XXs*jpn`?PM;sJRVb^QWF*+W-PVYAfCgz>x*2YWPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91JfH&r1ONa40RR91JOBUy0E^%0TmS$Cy-7qtRA>e5nqP<&RUF6nTKOk; zGj00|Wv-=YQPfMOA*oSDZN&D+O6(;nsR$%2g(516M11j~M}vIGpg^om2vQJ|q_Pqh zQ^^-C(cCS|Oex(y-(}}AckY>U&fGb3cZmCg4`=3_-`_KL&iuP=Z7pfdKx+nCGmw~p z!NI`}SP#F$Pp}dy3AO4jW20deNcRu;2rjQGlzD?hEAR;U%vGNR0 zRt&|;<3zR%U6u%Z3$}}3KHLiDK{MnmL3Y7b_>fE0cZCL}&>pbeD>K|wQ7%2fi^gd? zY;I~P*@7vzx+k%QCjw_1v&A0~?TtPKdY!|h-;c9NJ`P7vXMl715?6fRReF=lQ3=Mw z<;W2p1A5O?R#9$af=@b57sA&is#>`$%u=Y=Ih=GzkQ?ZG93BAOLpS6HyR0&Z!5-KQ zA8^k0Wp$7{QHq~P(AjVs=q7(UTn~946;I>i0PKUkuoHGr)H?eGG83}+#dhiy5e9$~ES#IP4GG&uN+zD{OSN2d+l z#WZW-FdT|8z_yNDoFB21z3e8?=;(ALxRiZ866z^QXuBJ`ZM3r6LX);wDb_4>`=sk7 zIGhV7!6wz$rzn}e9HiOyL`k6VT$+-h|J9h?cEfnQYd<@h%sQ#N@jy}UjeB8yopx=< zgRxyLEWQ%g-S2CJUKU*~nj5Gk^%p!87lp~7m*qZk45gvRLfUKLgon{L@dD8w2l{~K zl3!5J)nG{2b{*l!-|LZ$u|;ckr0LntiyWbU7uY6iALCa>`cX^YH24|PL@(_1{9P6H zeGAq%tFEe{X#)i%egJ%AXc3A0V9*Kgz)sMoQXQGCuoNzc^pY)OI?~HQh3J>l4_|`5 zWN1OF5&C=!-i_|XHO8hLlyvX>2Al?$+-vetL?4Mv0DX@da3rM{1CK)88fDE3gX!=E z*yINWvpKd#tslgH7p#Ln!6y1O-Cge?V-HT2z)?%2B^h0MsS~dQbc&p@#MY1&%&2Jt-$bScXsTCIz@O5k{{ zXD0um_hd?4#%}cQ0NYKAEA(=s<2;>290gB+&KsBL4ri%PJoI#T=Xy|a#oOQ$t05gC ztMJFb=UjUWz0`$_G2NLRhDPKcj6MUc=FtmA5{^vo@%*_`9EAdA__pNDCeU^rH5Srs-l z>4Uy*NE6+!KLdq6j3jd^bf03rGE}RFgEf%*3xA=>XLU|N3k=2f(pTi)C9(ot5*El8ylZA2B>w zf^#ImNM~Ctpm7Agd=#t#+;M@9jsT4#4*>vazW@Y}D@Tw&0ssIi2LeDx3Ov4*3;gFx zDhK`79~V1@pa?k+a1yW3-iI6-iYLE=;*LJsn%6>({R+cRlI;_aJcP4dul>Jlqo!Ot_v#Og9y~r44qYX0nZaH3|W*$x1>U$)Ua|cW5t_tJt2U zo--*x0V^dc=C~Yi(5ANmrEih-j8(yAI2RFis8XSvQOiNUr#t4;nJWyEKpzygU5nqCnvcshb9{dN3WVge z-mz!dG+VZvQuDB=sBqL$ctfya59a-gY`~?wb6?8J&$K1Ba@>8-Cis$NH{UhL_Txj* zi*_55%?wdFPguVU*gjUTP-rXXQI3LKkvq5Y+&AGBnatH@rZC^qmt6g9tO$CGT3=x? zN5B6=bUHdI+9cSf^T2jedqWt$kK%+O>gqY{F^gJ=-_^Hx5xxc2GSH{s0L6JFV9M;) z{AxfWJG^NqH`U`?^)-z%(ARX`!E-=Os>pdz%12GK>#LH9QeeLj4=^(SvKWIoq^^0z?FmU|RGLyJxTG1@lO1S6v#WeHYdg9}lAHCy zFv82_pn*(pVj{3Otkv8iuRiGjN8dzAueB{wDAKmlY6M(ctDe40$!C8=IuY(I(!XaE zF?QL0V?abE#L4;)M>Dsm=DNQ#ZF2l_@hkB>bJM{SW)Tj)+<{)q4!NoHg&*!@c?n%jvK6Q2s zw(;S(2OIaq_I!fYF!W}$=MV-)5i~V}h1qclj%^LaG01(tfcn40QV0USwkY{KBx=8V znWQj*GXy$x&gwVkIQSGK-iJRO3z&J}BQV%eg;1LGupF3TuVmrEjo#rbAroq1n$a+U zJI%Vkb|ep*t@57#xr||~y9K^?Ui5zTPy&0X-6AnZY-vi1(wUcbzG*?xY5ElB>c+@R zkzmnisUc^V_hr*wOiq#(OGnkU@R1A&?2d^?V|`m)i+y001#T#|G?)A zjz-RAdJNKr!Jv9ohn+uGge1_JJ@aI9+b054RtkO*IHIeeDvhFoxAA%&>% zorM>Qta9qdaSV6VpXK^pt>z-)pVo69G>D%l47SpVvlX9``6{8>lkv(b2!5QOB0e1> z5Uq)%p5p#Ksu7rE)(|7+FqbD!8r3I5r=)61WKOTF2@yI4RdR)IqO6vyU>&_L`(1R64 z?g%x$US<_y!ZhRc2Mv3q9O;??cgxqq@!oxI7Tne%FP|7qw{K}n-q7D z;vu&nl_^@HA2pmVwS6i4Yqv93&D{8V-Tj$6%e5@yKb=1_=V#pi_j4?tpSd&pWW`6( z4{+$Qn(5{iCht2jdf|r?bt4$v4vJrxcQ&ova+<4_lSESR$vWN$_G{1ki|ph@PO(cx zDc0`&;3jmvQ>bmmk7y>YPTj?8Z%z0huTDN?q()6&Z z=2Jx;&%IVp3uIpO`=GJ`*D2kzp1~_OS+v~E{Brw%+5;Oa9>du@yR<@06Hmw;f6O`c z`jaUsZ2lo3ds%Kqy1Zq+EP641Q8D+4kJ1yK9t+*~ndg4FY1y{s4~79=RRI}=d`1&_ z6pOa9-1d8SdX4SF)JaCpH!WN|&CR1Es_dJ%L=w2JuRhR}P<^I2a#7X6>_@#vzKG`f zeG>2b^5NV9)hgqIt_Cqj)ZVRil$pbFi;sI*#4nlS?n*{AGF#lz^64reEHi5a<|fslWZvpNvP0E0SVezQ2*>4s%gIWU}$u`DDwCzEyH* zO#;i~on_dXb2)a#cP@+aGWVDEaJ6`^lM}&i{;hkf_uQi&>iI|5fJ9XN>Gm?kx2@2U0*?zay8Lvo_TcO4JCF_9igdH9u1g{fS+Rq!k^j_A2i{+JV z|9`e_d0LuleEO0tPwMiek<+p+TC)g=ryt^*dBRx7BXmw%iOaF~%D1#H%KVuTVG=i~ zOSJ@;s)wR6s6_J>~`G`+RV|vmmX}zRP&QjrFq@-Vu8HFo9*#%(JD;0re{Ux7_EPp7c$umD9|X96k?JYD@<);T3K0RRK01yuk5 literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-hdpi/mini_external_link.png b/TMessagesProj/src/main/res/drawable-hdpi/mini_external_link.png new file mode 100644 index 0000000000000000000000000000000000000000..0a896857c6602e62726088ba82d487e1d03bf551 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDlgftjp8IF~o!S z?bO{~%#I>$o3+Bi%#y?fbaK83=CIs9=oYX+K*{V{*vm_q3wx*9HF25!V!Oq`)ik%` zM&{xZzy8jiX;55dcG~Hb%#Y%C(`QEdMXfcwddFefr{|UXT55UESpGNvV{&>E|H~iS z6SyZa&vK|^KYrvd%eM%Xoy>KOOPO7ha-!QWem~q5ywGWZ+WSV&0@inp9_kj850tDw zlB@hn_>4k+L!`o|UUP>%{qM{PF2{Ne_qVF<&Wwwf1M7rj_mqw;O? zq1*KldmUYV+?vR!`pajn@xpkE+ljBDzHr{2;}-7HXuW~it9;$dgA;9C7qgXFEzV!` zp6MfF@1}!WR==uT*th9m!mEG{yjyr(wQH}vxc>ZylxxwJb(IRbyV)0{mh)zXvl%Lh z#$lB}}XHV^Y$*bMeKlm}dmG3JLEsQ;V_QJ{4M#sO^KPuwUKOW}n P1Bx9_S3j3^P69eqsBx04L=GtR0Vn7-ylizeG-HNk&# z`PZz-{x>!9Z{VTDIPtDCb-jZCov-a!Fw(@7!jo_3p zcpBYQrn@hz>rnkfesJsk%vUfQYr|FtTskKM<`W19Nmde%(G34Wlc^K|udS?83{1OR26-B17k literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/large_hidden.png b/TMessagesProj/src/main/res/drawable-mdpi/large_hidden.png new file mode 100644 index 0000000000000000000000000000000000000000..37acae5ab95aeffb0c9779cdda5c0a166459d164 GIT binary patch literal 1074 zcmV-21kL-2P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91D4+uX1ONa40RR91C;$Ke0D9(TtN;K5oJmAMR9Fe^m`iL8Q5eU2wc=5I zcqXb!5JeFoL{d#?Y_Vu!DQvJR3nCs1B!aMFB@OLDuo1Cp2q|Jwq#h9=T~s_$Qg1EV zG+MuZ-MKw8ch20-tFlg4W>8^(U-wbFlDpjk9?(Tz1cagUtrCKJh&UT znahL72UCOi0NrM1L;sBa-Pn88X<>&<=jL5O6Wje%fNxa55ETJFUspNwLI4f)uBR<-DkUPGVW;2b3Vbzj&jj(z4cnX$79kjxq64g@8 za(m|#9+HdUCPXdjQJi9&WH(8TQBP*nZ%m%xw`oNgLF{*x;=2UTW&pvl0%24jI=W+gr(X z1qskSSqmFHft&br`rXn&;S#)p*YFnFp+kFOYFM*~t7e%9x-$8oD?JrTU^#J5h-pG! z45l2%x6?GZbdq#m_{n7j;;8a-#%+(BV(ecA#P!{G_@7#0);o1vE5X|=2{P-9HyZMx z8T=%1V#c}4F96#^==(`wwGG&TPxrW6FwTYhV3V!Nz*z(}psUml`aw_n)Yvvc6R6>H zI0+M+uIRP_^`}CW6X#lS=*Yeu)YvO;Fyy)t(%4X=5Dvl*Fr|ylX(&xumLR={FM?P0 zrA)R2P^bX*gPvHg=m}2z(Pb%-Y$j!LKL&3Is_f&Mjf)O2m>F zgFWy8{Nx74!bF0hV4n<+{gQladTPU;2c3Q->y@+@-h)>(gsV7v{Sw^r2Kww@U^@b3 zPOm|&6w;u(!F#HCjxF&HMLz^K>B6D6RRy?Svx6GWOs4P{(3`>;^qS+xRtY`e6+LLj zK=0(gBQ(o}O`tP6NObeB%8aSvQ!vnQ s(EkYQKPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91IG_Un1ONa40RR91H~;_u01a1YQvd)28%ab#RA>e5nN3I)Q5460YN;iC zLKYQKl2{ZHeG)~wGLjZWVG)DzE810J5JbgD3_Uzz9gSyoInjYgd!tt3gMqvvTl_(ryNsy_y892SXrJ z(n_GY8M&GS+6(?#X4Z}UQLqWD2Myphu#v|YPfKf(1gHYPZAOJ{BbeegF#7ian+MY^ z?DO5YlJ0|e85jUI((D%)vRy##HB&j`#~DaZ^qZz5h}%6?1JSn~$h4IE>5xNrmp)UG z+hd*TpY%C|J4~Z1cE=Ne>DvjM_w6x7U#<9=oXY)}nl~{a0t}8IV)tbuA_+ z3V^c!PJ3s2f5JbhBdlAT%m(d&L{MytfY;!26+7Ey=YGQ1>o`X4Wf1SL_0hYjr;^m1 zxT1!mTdeQAWRHpsw$nKj$+?A8;wLZfp5hjCoG}D4&-aY$G;0)M_8kAS9M5~WbSV90 z%DL&OyxnNW=9?e{i-2r%g2+OgjsUMWkXH(6xpHoEx>;vzvS2RI5%}mnqVLhzJWzB& zuAsQf1qd!oF?qb($_pGSY@%Xc2%3P+f{%IholTqxwh$Z?77O`-?UD&Zh8_5xw1_39 zzjV|l_n>@Uu#r1F8M|#99Jc2iZNKKA&cGzWuQ~!3U8hT*P1k!HV{AX^?9-88Y+cg@ z)#(!JqRz&l(>=so7hi{ej!d@^XXhkpztN4w_X<)`nd7ZGk}T_P=;He(7Grv5+SIPJw_-A~|I;2ZFh ztJdU-1LWm?0pj{b#Vs|b5Fa)OF5hg%HoCTv@jYb_LpI-2ZMlu?A#O1kz!Pj%8WF~g zu+IbX_04Nzj4CFB{H`_Smc5>pKrWyE?tyJSj&k$Mi*T=xou~`IDo`v9jSQo!;h7-u z(gW{>XnHb+%cV><8UAH@J1CvF|h7bzHeN+Zd*gWG8Q>vm=ddnT~6MBo}B* zOp|5knuX@eM`_NqkJsKOGN_O+@K>pmZegFo`Nbw@*Z14fN z$z?iYAD1}Fhhq5$N&eS+1SDa%KUm~DXcxEw8i4$dpFLp!mw+n$G){mrzfob63rau* zSPo|R`J_T`H4PZlR)VeovWxEI#_vkSAVQ9?0$~Nh3WOC1D-c#7Jr(!|N_#&Ry!BDU P00000NkvXXu0mjfpeha? literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/large_notes.png b/TMessagesProj/src/main/res/drawable-mdpi/large_notes.png new file mode 100644 index 0000000000000000000000000000000000000000..d0adc95fd4e231fe7df80aa570ab34764a4786b2 GIT binary patch literal 830 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(W+rqmihr=`gvN>!ar2vv-`g(#S@^D2{MH@j__Sw#{_Hb0F5eQQx%7?0 z&BhG&Re{-j$$R6bG?Uc-L+YSz|sGrVsB4Ut>LxAg(X=fes78$NZNv`~qUcAXaJ zDxfr1MV4h+kHEyAb_*t&a(LBjWZJm*0ox6hw_Sf0oVg^})zF)BS;j)_)5#Un`PQZN ztYqPw^umT?Vs*n?&I-mlrrtf*EmqdPc%XcKUfu%fP<71%hSv_p`qljm@hQ6T+?D^L zGmrX#y2$h?`akq1W*a8QT$W|}K9}#_*03+r59A-8mtNVgaCQ2TYk55^ewL!=I|c6= z+$hRFr58%O#Qv&El-*^;sU{+R0Ks!{t+ps3v>z$)xE|bQ|wG zgzGeJj7{Ie{BK5ShDBGR)D!FSyfD6NNv9u530S|Lt!ridwmjK6o5}8mg7H3{pIvs> z95p!8QXe?6Q(Z8&jU1(?73{#eJ78bEjk6Pm9I&}EOiX1GTQq2=2D|77mj=o#l z!UI{3SBPEgAwRO-9Y*OovnQ|>& zCx4Un^#Bg8={MSMiA8S{KC1B~R^!kiJ*lScoc_6YU2C#rI_n$a%3d0sQ1xsQsdav~ z`N8y+53HNHfBf9Guc@wk$DR7H%ySDwW|}@p`5-v+`6Qd8&t7l2Vxies>Ee39^Uu}D z)0?E{e0*W56~E6drl?zb-dWFS-j{p0&d+Q2wbaQ;<<)6Wa`|_>vrfF0-Oh9NlHG;7 zxQ;n4JAPl~|Ct{=F%N4_I|q2(Y}~}0%Kk5${U4K`w@UOsj+|GZwCm~W=d#Wzp$P!h CvR3*4 literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/mini_external_link.png b/TMessagesProj/src/main/res/drawable-mdpi/mini_external_link.png new file mode 100644 index 0000000000000000000000000000000000000000..40a565a7aee9b2f417092b1082a31c20b13ad711 GIT binary patch literal 402 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uuz(rC1}QWNE&K$e{&>1LhFAz5 zon$M-=qS=sKGj1bz-gke#}sD)i>45TM;tCe>=OdJtUW?GU6O>@1)}2p-{zG5^xnH_ z-OKBL@12=_(|1z6`zz*@$ZP8F8@YBY%d~RIKB&HcMWoc=c(i6`(j;^5oD)BUe7~4U z?ERUuf#tWkviOH%4uwp!Bf@m`v<-r$lT+=((gKtXUi|P^~z?t=XcpLC_cY@1YQKA*%(BJ*pPqj; WDzR?rfBRBUczC+{xvXPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NFtVu*cRA>e5nO|s6VHn4Mzip&x z7lznM=1<(X;6kRRH6;JT%8it`u-oO&qKt?XrCg}FATCiXG**%eg_x_|$QZLR@+V_6 zGe4gPXPvz}?>X=L?)YJ{%Q)s~d@km(Ou9m`tNfFB8*N122zveYka30T(-k>Q4bu$hD;6($nl7R^^4anIPk+)k- zrMm`X2Nflgq5^FE6rM|bVZ@xTs$X{vbf|#=y8KFevC9_GMm})f2l1bTWO@YAn+X3u zH-*LV5DPjRS}+c|5V4tDbHaB`Z9sPlJyio^&|BfCVD$(6Via`@dyOG~vte8+1+lDF zTq3);5;Zxt!#KfE3#ALM{qzr$jD$Ex6!B1)IG>N|KB4IpMC~CgN)f#cczs3b7#rtnHgrzQ( zSk8Gty!+iI?B{{_Oh_?ukCy-XT}2At0d#N1VWma;Af63^Hr_w5?*npy7lECi0mueE zOr%-axEuv#K=uL768eb=a=FMK1OH3Zd-U_5j*ILi$3eoJF&O02QV}Qy@~KHSNb-3} zJ}I>`?+eFRB&q>fol_tKT0j$cNCABu6G>JeS%G8)hQ9)T0CMc+=t*X==l}o!07*qo IM6N<$f=UR~!~g&Q literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/large_hidden.png b/TMessagesProj/src/main/res/drawable-xhdpi/large_hidden.png new file mode 100644 index 0000000000000000000000000000000000000000..31c29045e918bc309f6aa84d495af8b8e5cb4478 GIT binary patch literal 2165 zcmV-*2#WWKP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91P@n?<1ONa40RR91Pyhe`05RZ9PXGW1-$_J4RCodHoNI^{RT#&2y{wsQ zcrDaQw2Nh_m_}(@Y*{J z&wyhFDjoGoQmd=0tHQwjFc@~jipt8$TDHc5jnP!T0lLFaFdrKS*p{~tco$d?L78h3 zIZ9RVNl=;NZE%%|jm*{|z!tC^qGV#R;81)kN}2L4(4m-(%$_H>dkO@vQ(K``RDhi8 z3#Xuv%!<;_bU6|0>f2Z-^7?akM+M6Hg$DYEsdIjmjm(x4)GY?VtI8&56AYBHp-}AV zqsnQS7SzxDF=5TG56)z>m`c@N(Cl9e+f5Yf>#X&3lisE>j3KBN1hNl0S_E{Qx<5iE z=xw3rRqe^JX1jNg)K-C|Ap>c|CVBEnx3R{FF3FSrP5P6xq?N#$qyq`DKRl!?_M)^B zcw_9I7dNO`O5&2%GjIoyT$p$?4I4PgVLP6K|28a+_bBlzkPbwhK-$SumNX#mqn#ur z@dV<0|0Dw_V!LmV^Ch27BY{mh!7qy|uy@Hnv%7(d+*Qpq)3^egGJzMHrNO;cxhF93 zD=>2D$o)5|sG#RP2mL`X1THJZR*Aa3@MStSR>L}mG@2g9DzS|Naa6zdIM7nNrcsSn zR48joIY#4O!7^A(GBq}BS|Mp#;~NjP@E>GFz$<$8L+2d?V%W7{2or~4j(d7Y} z$k3;m{0uE^{cuU(9NR`RRR7gfeC0NNrCc6*zBp#5l02^w8Xf^_eGX)GcMXo;?8TM( zmd5XZ&*21QMda%;7+Y$>tb5_8263h<=f=Yr7z*v7Imtns(VX~WSVV8D*Q_N=Yzg#x zLNe8x4S->AJzNQw!?}=#9HEiTumQe?mGA>eJH<9uu}UC`Q3CFTy|5gI4x4&|uG-Rq z!xhjAE&zSQqOIr*Z9$6=LMuAnf`nF4v>2eRI10Lg{(?RAXn!kg1dZqF*jfob>P_A; zbk|uanpDPkHLLS~VASha3IM2k3qO%GN>9$=8g?$bO)bq6()Jb{hvk|>I)gwYvF>O7@Vs|;mp3GxsvUd=%?L+xHv~{P;qu`89477z6 zU?Sh1QLNuVsnFM`VK(*o+CQ)$#MWxc_3fXn%=Ozs`6CMhPy2Pl_9LfqG@^(25-=TP z4n#*CfnU1#4-EF@k8YjbA$9d?KzDH zq8`yj!t^$|7(&NYPt7T&1>_^_2i5s-LE%GikyU<+7^rbDq7F#|@diTKeGaycqjQPh z5XUyRu-D}G?3i8sRP5?$Ic^*LTG)8m(#}kO!2W&gn0zDcZ%&<91L34g0%h~v28Ie9 zP}$Z|HQ>ohLED^|3VXZ?5!>CAPYLS!k2&8Go&|L@h2%F*1|KJw6?!0Z;mFm*e_4A4M+3&@skvkyQ6`_{qsy<^U~ zBvAIJdNz!=D7A;z;3U{c(eB^aiI^(lyav#B!8VIP)>HQ(Fd9@xx{85o;2SWNy;N*; z6-G9Y;0gTb1W&;ZFcn?RqhoaogL;Yn25fHb@s8H2pYa5 zBN*=n??W-stYT0yUPN1^P2H-G!rx#cUr;%{MG+bs1?uez$vRF}MTZ;j8QK~FKNoT+ z;+%SR&F9k<8tn)>Bb@SzUr*XA8maC<2vaG4S*;NPK_RfE3D=sqqZ z2}q5vnRIpu;yU=uAsEdHv}|rA=xBVGglMjNwUsk*R;F>ir1{h`GEMh}oBBcBkzhL~ zFXob-fiFUA&t}tjLhIqkU*!?U7FaWf08kNYqdE(nto6l$4X|A!%$undr#31OL|>^tZerjwWX zGC-161`?<-;tP|3z4VY&oyc-yB&!Pab*RRPnyd4xccSZ&*#JQ{+{=JpFiM9vASjq1q*`#007j~#K7i^fxpGhdNv2X z?wg;1DcHtX5Ab|cbmMF>@N_iwva|#s&pbPTDai)_`W-ow@R>fOO;UWQu$c_~+kSKb=q9O6F>q>B(vO7luB5rbbiX0TX|!oRdpo==D3 zYKffktZAU^Bk|eN%u+LisV2959OxR$q?GcpGFbu$%N*%3Zks%IRF8xKYCMxZ=aSE5og1m(m+mT$P8+x~H48B-?)kJcT*CbF!hNy$j zw9erxnIg5QfX{IgSVbz#Clpo97rG2ccKh#HS!XccV5mxDb3!vf>p#Ar^^3#{$#Npr z+DSY6ONB<+8|Nc+03y0Lr&Rni02CttUMD27>5j0~LTRV5<-A=%2?#GdTGt|rk%o76 z_FOvBM~yfLb}5P5d@irKnf%WppaEs!lt!=AcPBg_a5?1}?+qXCa;c5G*^o5JOUX{| zGa-@dcBi2{VmZCa+~$nb<-bEuESx2h;zZKY&vgknvOE~|L9AIVknFp;Hd4Oxf#Fe~ zT-e>Z>;Ft+?9!{$HTbNy&PlYd{7?-P{5apk>7B9r3>W<1szdN{#azw-M8~*aetUR# z4J`ABt=KqC;hjKB{E|CrL3A0L>*{2KPTZ78aHK1z_Bea=z*jXQtQ%=A``#&_B?{r&gga`E> zl@|=^3m~MV#IRmC++=aw3*4})2knOgEqd~7W3M)kTODn=m0zMyQk^EdEv61w&~~_ zWtaNXe+&a>^U^->n7n+UV-Ksxy;^wS?EmzF3LR`dWIy>uTR5`RF@!gl>*3=@-iXG# z)EMTiSnCh+c<)Qm7aG1{bmzO+kCSJDMK9&QYVzd&o_34oj#eX)?bFnULE^KUb_pj* zreBmOwq^OJ2*La*ur{A;pz{N=eSzq8a%i!Jm&Lj(zU%Qe?1`f?V=2s zPLgbKB%w(08EZ>=CFxd#i}=M1OoPelOi7_rNKji)11J`&st?t2RTV90h48EO9;dH@ zV!?NZ`)_6JTL3L%nI#dP1=8JW33P1PhNQwRWcHj-&9+;Y9;XQ2`i~xC;We`^lzO2~ z1E@XS%R{E0dMsYOj$&*I4pkS(>?H0$C_G?c2>V`vGMwOp@OSn#7J^w&HhGF3)-Jx~h;ufm1l0_=iQ!w=Y^~Wh(okrdl%S4ylogY9v?`+^WBWoSI@qHB{meLY^8~+AYp4`NW4Gu z8zVfl&Pk(fvf6(eCYGyZTJR8;N5Qa}bow3Mr9Xz4 zr=Hi3k*uFdvfV5j?a}NAqgLB!;3MRu~47hNP9WZTxPya;N?|@uNsg4=#7~Hbw zFz!7r(irHR0G0d#JW{%=yq_D(+!U3HkVr=Vxe+S$kaieq>~=$-V(xz7$PX1su8602 zf_~109Sfd9*(DCN5jXQ=w&@|H(|IdDn;TcLU&luqaL;e&qsUA#CaB;L#c81k+C17j-#k~Lnr3*=yH8+dRtA=_9|w`s;IY}16@uYZ^G&=7!w%X?)qXG z7A}ZU8cc2N)7{$g(BXWq`{Zc<`ISMU4 zE3CQjq%dLO#QEuEf4t43>&3I=is|HDh9c|L&01o6+yg^8UuGmwCI61sua~$*Ha+}ceTzq7pUumZS`ys#}y`s)BS8mbr(6wR!E~Zu!_ZI(j0naLuQmA>^K-XpA21B zY6Anb3c{oxVxDNcmW`N52E>FecVMtUq}whCbFlP`h1TkvB*JZp;V+p9e{T=GG)_1@ zSRWh*ooL2uHi{aJZBgiT$HUYQuF~C8bFGR0sA${(wEM|Df6?$RU_->;Cx&okWl_C% ziQ?;vQy}lF)d_qqHqQnP`DI54ilo1GflHGt1)#VR6g}jHVU`Cv4D`1uyo6Ua)eC8- zbk}WUMQ+=JV?;yzQWJX1Bky&^W4LF?)M8aEve;>Hb-*uz8HSdMRI1)n4TWqUf)r(Z z*a&8&cQQRVRwTW!+tWwqQ$_F8;@&h5Q?g{xlLmt_o&r%)*0T3DEeF0*;%7n_xqUv5 zR9?yW;SIuU=(J_wsdt-ec%gZ-(l_Q4Qmxj>FNvRE=<=gq$1arBx=wRkyJ@}RzP$Wl z6rEwt>HIc`H|1Vu`e3X=*?}df+;uM|nS8x>toJ%%EzU_d=E?g76^x?E$!e<3n%P84~vG-{y`jRrq{QYLR{H|`CK{A;n!Q5=%tzUePN=UpslU|xxyRe zt?ZqUV^;dDrofmTS*q=yoryPl+c7rnc87!((+`~9A2JwHVJwa*UApax(n9eg!>FQK zNHpg$=l12+ZV3l>gcoFr_*n}hK;)riI5-?gMTo!dn{S453ScQhfvhD={{m&FfmW}2Hf%n=$XW@$NA!t0uIa^z@6F-J+rQjUhw5V@r(G}?paKIY1O z)$7h9N6a0HlH7S;p67ku_j%txKG*mA`TG-vH#daC#9#mbz;Q-;mk$(w2x(4I1Utac*_F+^TlUz z{Ob>kXtt>Z0C1vkdOFsDpszF{RoDj6QNNhVoHAAvw#<%V+0Q4rTwIrqlx8~f)kDL0 z6}57vTB1ukVH?u#wYKm&Lb`nhStcJMwgzDDqVcW|`W#w?$3OB7U$R^(tPKeryInmo zKY{&$ZBSLc(#!i6tGbXexf(qad+cg zFRvxbz{-fU#uzr&H?=K(#HkcxI>Nt1my3#Tc1QDN+v*u=5W%Ey^;-SuqtuTQX^U7T zH4y&|P^_S@Vw*$HPlOvii2r*?ZqaCWEd`1ZC!;9F*|U2%%<^blEnf+PTp1FVN1;|g zqfrsYB})O9mPH8iY*lLDpk{GZAoaWy0{Bgv|0cbvy&V#&fK>7o@Cv{OLcbhYTNiF} z>P~#(3)u;| zP(~xKGo&-i=hnFMxieb)|NE{Drdv*Cxh989 z4VSpXD8NZPXJ(L&CC7#cV2!gkmtyrVq(8MZsJxZmb%rY{`s_@`(B;Hh9f|AYZ=x)4X(g8;0-#Y|Z?<7h5yXUjIAd?#xN4oD8Fua!lq?{y=5WT5J}@j+ zzK>e_bGRF~?8Qzq+$gdZ8?&m*Gk)X^ci6EBm`f&dBE`4nQhwnuljnx>IzUHU?Dw4V zt-3bR_Xs&|Xv z%FtNZf;3}f!*eX>yW?wL0Vyf4t-oi1xdOZ5J|p=NMPFjqbcIfmml4k7C@u9#;Fe92 zXm*h<>Ovm7rYzS^iYG0n*Epn5{%D;sjNzcbG4yQedxzjtwiCofT-zUP#-E!a^f(s8O2g?!^c@rZX=yxIVHN z*_Dr0$seiVM#-<(iz!Xkjbl6rr_o*^JlFX48~D6HqQpqgFYsnNpZ44-v+K$PJF#v5 z!SWA+mRCT`+7bq7hr3{Ly(rP@z*}dZ02yy*jk=FqZvVVWy(?1g z*v@=nlcOB`D)CvHhML|>*XI9)T%kt47yNFegsAH(>o^- zf*o15G?I2DZiy^|vKP&v$6p|YP)|4t^zxY zaR0!j|+?6KQMFBnX8G?j6Sqkb;+PVAje6)-J}4}~;fwDH zNH(FZ$txrRi^nZy_(wo9%{5Phbb>9MgxrCzG0q2zfSn#@>}ff=V(BLr2---IMM6gA}f5OZp5{_QTV~F(iWX z?KE3`=R}d?(TW}sR+BbJ-kcF})ZO7m))DiG9EZ+LPdJ=n5~X8$txMqR@9VZjzwR8r z9KDp;@6-9p`kl{r?ydD&TL0^{w8nC=3mGPCAI~>vUYLH=?y&#qr$x4xy^r)N=r<;A zW{t5Fe{zNC_j4`Ra0l5VFT@t}E@9M5c5HmhSZnjU{&dkYv?;ra&t?a<4(73j@8RjY7{Pf)?z-xwD@n0OEve& z+hGb1vJ=bRaC|u=`SPT-lBs&)aaCDsOZ8)HAI$bYiz&+JFuAYvOsD7buKZ_jWlwK> zwnu&UTdvyE4Dq?^9`2G`;PBafdn>#8aaXQ&%!}_tzuwHTDk|Za`QCV=Z-E7j=Nk9T z$W&S}nK7@!{FJGGdDZu_Y8Lh>I~lJk>BmfL+_LGAv_{ATg{-iah??!v9anQUUaSym z{>PeqLRi;tioAS-9JfRL<)$ZMuZ}YOJNNMEGljhp3!Zj5tC}_Z|9AXPVsF{5^$)Yn zKkvEB^)fxxB;85;&h-ylXR8*cADX?&`>5=N^KQcOjP;6+x-L}~ruP{py+0^%zgxXx zmW0#ygYTC;ExCTc`}>upTP8m&+4y%$mdKI;Vst06cIK!vFvP literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/filled_open_message.png b/TMessagesProj/src/main/res/drawable-xxhdpi/filled_open_message.png new file mode 100644 index 0000000000000000000000000000000000000000..88bc7b07784a669609badc5447ea9048e92ee688 GIT binary patch literal 1700 zcmV;V23z@wP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS>4oO5oRCodHn_Y-iRTze6bSf&P zNbTpM3u#a)>mn^-oM=M3=+86<6REq3=%%6}BNEC8f+9-jvdACqfEJ!vnkVt^A`lc z9}n~DT9Bd)mi+>93KT2`-iZ$*n#&}L>jc#3UwmT zo1iRvEYFK1Q^3z9Bzq&DC)sC+c7vf32rFbi7%`$L>jbHPCDn&+7h#+iQW^Q>1a)rX zLLgV?SBCv??)=}6l<7|XcA&fZRHi;G&-#=mCczehGHrn16HRRKO$7%M6If`<=Uoo5 zU5UvRgcZWAsOo`+jTy9*hIL5 z@&&|Pi2!4ZVlyXwNBL}Uq{tso7S}h3>Ms3o!f(tq)CW{76iFiByAd354Q8pQz8MFK z(UJQmbiZYsJahF@e_TYs_e|s&Nt!?AfZrn%t8eOKp}7cjTVcz_6fP#syB3kY#xIfn zh@@leql-Y-M<$PL{tyA)v52%2{~h31L?UA!N32|HlR)e%%5JVLcRc@-dd@e7bz;8F zW%ZJ&4m`#26L5*Cj-`Anv2U^X%`Ot}2fn67-(p~#WX&1FJ>;7S27#~b56()aW4_s_ z|738&$cE&8I)Jb7@^rbx&cNAsBy=)ly*8#IbvfG{ai(&z`)q;RhavcO6jgPZq&1KD z+63PQ3G0XCHtr3%2y`NV{IdaVQZ)(5ecY71a9Gt;KFGOVEby`U4E4`wumYsorWoWh z*k~g`&e!qZYpY}M9X6ELFD9v${LMD~GR?NJ#s6vq$(5$qE|-{Y4x4Fzk^DYJ&!;95 z2=fZQK9k>}{998U7lFPoHEuxpDRRt194!C~4RKz^v82%HUbxAt%IQN+Ns2u+bJ^?Lt*Vytn`3QcuB|&P} zX#!ebh{C@#VbVLDM6gwY%vjK$kd!Hu#F<}*%X6m20p<1!E1DE;Rn(4qhWzD@Kb z)o27;;v29|{l|^Td>tgxYjvCJB#d$IwQ(YFUrg|Vwm(r8H#yf5Cg7}sM5&65KeZ%y zDpitPoCUT0OL9(vRkGcGpo0a+Zwn}+lkU$E+x6s33 z9Y{OT5wIlFzSbPX(TiWs7Q6-^(R#8hbibJG-z*ihm4i+fuZ}14r+Bf2$-NFYE z=cABhE*oaHxYKRU7Vv3rx)*Vb+ZpvZ29F@-AdMcUx3ub!D9Qgmac9JcBVNG*(N+)I zu2hf~GyRwB5rolYbuyM)%q7+vn>=xQy$HUQfK zQSa{bG1N!wCDUG)^$gGhZUA}`It7deMW$y~dec?d1~!82#MX+twT#YbBJjZO029IF4El>zsiVWhC1374No&OENvp6A^aG8St>AOMRpUzl_^n1;J;4w( u*;ZC5P$^I;P$^I;P$^I;P$|%41^xlsymkudkk)|!0000OGFfj;^^_6?2 z<8#UD+d!5|2+`fv>aY|2=@wBNrOI$xIlJZ8SD9r{n~lOmrn%jlQ3M)sclDiB2q2Bj4M?2%QfO zMD?|sI8V&p>FDpg+Vk*qbwD;%SLmAKm4`36p#p&Vk+YAkon3mravuTsXR773e7935 z<{r)8k#hLUe#|MOzm&2^@`510URV0mJX4_Hzp^;>s3I*&H8zsY1D0{CLHT$oq?nu2 zrmx)VCHqjECRV>zUUQXhGP(=wG*AZZ`LZF4A|6ekup3rN`7yHHabjHtnXO4Y(y!1% zbq*;eOT0pn#8Qn($yP|0REdh+-ocm0YkWQ;XS$iiB658!Eihl7XRq;FQ z@K($2M~Z9i;JRXx-ArmxtEk={k88GrDpF)Dkb-;j9$pTYY%l9;q0(gZ~rj-V0kuWQHx>sjHP@3%Sxhp z_~I(VjpDr|1i?=6#uC+Mb9UsK6KO752wXj<@F0QtQKs6Qw!s@tco!`ogLnrpD-gTcQ_3YW8IhyK;kZ+;}1-^%2a^?N&K)pVe-Y(ppqz8 z!@H+#0R;`uJ}8_Xzc=8?QYsg{&;xpKe%GIN@+qs!t3@4sK`fV}YO5;9c(Iaa9Hr1l z+B>BzJ3GP4m(lhB_SQu@49JAOGP-AmXQb+|dQmKHdA6WgZI&Mi`#O#%T#Ct2W8XV0 zVsgtu`cD48-a*Enro~=l)all6W_&4*jF4nboKu^HG@1@;%SYMPTC$;=oEc-SwIOOW z+H`67_|M=MO%Kq`USjWjNLU8gT9AxVru$>6tL7FQ(n}JulV~v7+_~a<#+Js9k^|^C zdhtEB;uBl-b5g2!>@AxeOG%lp18C(mf`AA+C-PfL$u}Myv0N9a*5a9z+0F4}pr)#d zaLG7SO$R~?_K8X!$#`6lS7Sq28|gBalkQQ9PT>N1V}d?D9}Ke0N2)D}9*-z=hB>Ly zVEFc+RWYMbwGss-w{&%x-q5*Zl%~2(d1G7aa0~@B*z%^uN(=PW!Zqbh(CJco`N%3% zVn{7RdP?y~o_5T#?x%6*cT$&@QmA-s+J|NC5cf7IlKgEs}xHJazV-LamX85CdlL zPeRsOqcjD`o#6erN4J!F52|P=@KpZ!n%}2mCLMg((T2jUpPA1y`<5f7{LV)g4r%0S zv!q#jd^>Rp*#+VbPac|?vX3u1vIrXn8*tLqj>2MQUz6x6f2cs&*EQ{#Z;7Ehz$tjq zk!^6|8mp9Vxj8UD2E^R1NFMcTDm$aIG$)rSs`fGR2D=VMwM`fODEn^y7uQ92^f$)z zvHdU>by@c~pB=q*k04OgH7fCBzL0eKpvCyjG&+Wnw~k{y(-|%R*-mpYxXR+A``-xJ zz4o*3J}bGhw%9j%`()+Rm|=yeuj%cCoCJbKt;C{04csKx-_-qLQ9mu&VU(ahwEg{3 zP$cDmy=>;{WaMxSKh?f|em71Q&s(K~efB0EtX2lRdQ`nJe!SJ#=1XHf7_@673Mt;p zC8dRo-;}K~q>{Li(B32KhPPujF>7{T#8Q1_^5m51*tiP_))d~rj#WBP$!BCT7tnlyAVc3gC&`hUPR%Py+2BXP6%P;34#WFLw;%h+ zJi*01xLOMDvkR&?zYFh_OTZw&+}G8i{X6Rjb7Fo?zsu)Wd54R0(d1MHPlcr8`~~ve zkcr!OK-1#ana@=btyQ1>03u=Lo%wy_S<4#lsLqITL)j$vLQYSIN*l}dPWg6JXdjQ# zsJ4z^QiZr9b%feuLd~mcU!Dka-|v`vAJsvzL9|cb@5i(h)%OnEluGEy=YW>IvKHna zsgQ5WTdEwjG`@6TR{PbpKy$#3brIBrPqIdsAN$Z|iqIdi*SLoxbM3IN(rEN<>ym1) zhjB8q9{xf#U^rq>$C|m05B+YDCtYGm3gnJ@aHwHhm?L89ESQHq0nZ+JL5>&DuOED^ zx!Ntb=n;4#OoUt#?WA^WrOZVtiZeen(y@Q{a$z=3Q%mAw{jeTCan>1^eed0E2}%qX zeMW!rW3e*E96wgkzI$E~jz#aSawTSh>rItFr&JRIo$Ht<_Q%5dM%<{R7!7DcoY{x?Dx! z9a)=Lam?gpWcqDSo8fAx9p}fpZc>HB?sYxmqGt4U(IpNBXCQ}Fw(tTEU8(;Dke>RK zt1O`={?K^C3~{;}fcolRH{$6BVS?h#2&cyQww$Wf$TQ?XvLMGoHZrtSxJ2?A^|V1N z3yBcKf3k%LRBe{$W<3Z7?EVe((YI3JoS1Ex7g1^NfmfPu7wgQ1X(B|6>&=~M@(Xg(}-Vd0}HP|3ecCuAJ|Ms$q>!Vp{j>A`NakD z935;@)zN&hAN#NXqWnSWUqdL-ApmCQUR1l{Z{hm(qi&Q&q`KKqO*>RAfg@Ow!yA<1 zTRI1Z`1GSfczV}C-e)Opcj<2PJU_l`l>URqAx`|EnD-oW|XBWH(DV3>=Sj?4fN zbnXW>LO_snH>bcNf6hmMr-Na|QLDrSsu%edapoD~LW~$tOT|aU%sAIVG>W3{zr_K+ zT#&lAD~yZppP$KPmyg}w7n>X+SlwoGdN!1H_a};o`fe|d;WGD55j{{h{_@*gX=7@n z?tuG%e*MS=d6d%?T3a>vZuImQi|m0mC9=Tg8QOKY_zyowt&l%rxdj1xSVI~tVeB4#T66v{5N@1 z;r-V*aA^ZtAT2|<@E9{g=IV^N(i7yK2=njSx_dIAfqIgSe9Ch-DKDS?c(Ha~CD+2; zQ!k)u{*6T6?a~N1#~}Fuyv60Aqttt3*-N8=c?m*r+4^#`TThT=r(bE5J>Sn!1w4#> z>*2P0-8zh(go@DZ^kko?R6&1!B*l>tzZb7_1QFXtZcMcC=@3#BI|#|?z2zk#jIVDWchNATuZM|8c;pJMHA$t{C)kmP zTO41C?`2pnV>oPLUAjalQ^1MtE@oRe(qe^Am5Uu_@_4a&>8@f&5w)}#Er!L{oH%%c ztZWKbPiAMWawi0IU z9b%gsC>iYRWteg!{*}v~^ZiEH4lrF?=#WQO{KFvtLKjE6^y^<2=+-*DUf>UTNQ;x{ zF)W*Jya6Vjli1!Kut7>}iGyOJ=O#|=e!hSw_N#}hrCvZ;9(rXA#O$4UbD9Sa26j!=Q z;GeQ4>lNh7jCrC*8Zv+S1Q;IbNK0ccK!^UqSsx47Y^_NIU|)LIKP~aX3(DODKC*i< x0a6P%XD;+wK8>3^=|C_N{a?!=5^O5AD=n@vE6uh2Yva$z!J65k8Kwl%e*jm)Kkonl literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/large_message_lock.png b/TMessagesProj/src/main/res/drawable-xxhdpi/large_message_lock.png new file mode 100644 index 0000000000000000000000000000000000000000..76a8fe5e7d7af9a9add7b6ffc352c0dfd0d6af2d GIT binary patch literal 3950 zcmc&%~6o59gk9?>*0Rzuo6KaYhE3jC8zo004kdTT9*KlB@oKhU&8V>XtWO z5}A*Q<~=~&1poKTM%~#`+eKd=0J+pO0J2zj0L4GcC3r6Z0FdLz0OXfU_HTv*{@=C= zNB;l%pAnfTFbe=Meb!con+B5Y70~}QGvj=B)#M=Qlb*^a7?1XJraIQ1PN}DY8XR}i zJSOSnMSiYt;@GgKXN5+o*pR#?xsLw5-&qkaZe_&`K;ZLA|_5~%n z9tKpOuHPsE696t6-{F=;&!!pMAX0-H&y9cnV$f_bP2pYuebDSH<>B%TPI(L*2gOQg zzE%43fs&#H~xoFEG#NPt!=vi9Ka5~TEu{^$o?n>KG8BIuv`f$`rx!=)1rOz)sY^p@p& zX+r23q>>ljJPu2xk%m_pUJ=x$CNtH+VSMCmmnfLFBQhX+FO6UAqEV(vZdntMt47Z| zM90E~LKsayw=VPw=(LnV-uyeZ&!mqma z89#fz^=ZZc{VG{xt6?RlU|3@0A~$Ilu!d@&NSIeGYdpMZBV>+S)#BXMG8ln9%3YpDwjxXGNg$&2d!Jnn~wy z{|!Xs4}B^*LYoeDq<{9Noa)OU-m|efbl+@b<}(0PMj+Rgg|AJ0&yX%mhb~pBPb)Wk zzzs!^gb_A6My`>q?@s0oGb}5j#)YrtA!Mu1o~BQ^sA}<2nO9Cfvyx()PC+v*3-E<8 zQ5i;M>J5~-Byr=)sUi{J7E7*#Fxesb_^##sU)9>vZO(pWjqR_OLm+se+M9qe?eeUh z^}r<3fLeOW#d|*zC;kr?St1km2{=@38e22irc^Sex0JuQz+h8m46%P%%rHI5z6ETg=393d*_R5jzr!w_0Luk(Hr!xwx z6Y*13++W3}{dFD%b?h>?-uNnft{IoQs+=g(nL1`5PL9{QnlfJgGs65F+@$UN$$lHb_MV`5HI`=je~irs~vaVK$;s2eb44y#F?09T@HLu{iyj5yA40NyQNnqkG?F=v zQ{54GO8T3p*I@VbDt&oZ0QzSffN5o zGvpbItvtSDazf3bdwq7pr;L|iCO8Ol(s6sSxXqW17~@#P7IS`lnX@v3u8!AG=H9crrdts8M8wPWz8^ zB}y9sr>|%C2azXPhGEt0c;6A7>y;IGw~Z<=B3sdQ%{1sjKBa1=v)gyY$3lb_90gjI zEv9U4-|a1Hn~mO-ZV_O!Nfzm0j!p!>glTKEnHw-+Q>j-)G52HZT`D94g(@mlZLFr# z^33~*4WXVwt3iJuFJW=khHixI>wjdE{Rj5SH0oIQk)8)g%6f<`#m<3miLrG-NVtx; za@)e~R@nXhyN^Tk?_n&^7Xk#UeP-WP6E)n~V@hS)P8wpAz$`23sPs$u^2g@v1}u#4 zzAsN+HvC-R0oU$}$!bTEMPWus;tpjj}b`>F(R zyz`=X8P~~w$6Ox=H~8|Y`Rj?_Lzng9rVDVV#eOXS%30~!hgxu=s z(Ia*VU-4<6=yWY9n~iPBXSRr_w(a(;Sv8tgLh5a9`-qYZsWuYg2O3s{z4*Vf7)J7{ z=*@@eK2>9VORB~+X>k35Qp?xwH3`QF8C-9*9~JiU1n$l4SF+SzFj}uB{47&pD~hpH zrS+vtFUhjT)w)n~B922%p=s|XeF&DOHK->Ug5Q*B)<=$D8-=9AeSSM9^c=$6W;YC_-{2M1^UuTuFlgy-sDFdX?FWYp+ zy$!Al&jlVkYUj7LXQ!A-Yw-oVf)$md<*0kK8_g^UjUe;FqKMlK6uh8gg}7<&Nx#=m zAYCeHI>J``@CC(eSd{ODfPAV3X2knKPYMkc^`0mmV%zrE!mM*lkMihv<(Hf)H&Ey- zM;(GRDxx=v(7h)C^CzSA`489Udg3FU@#PztrJQs=_CmWX^@l!KdzU?1d#Xs=z;_%~ zh4J#Qq{->k%3Oeq{T@drw*Br)BP{Mi995$f%Pe- zUysuEN>_8vG%*rEL9XTw65v_GGLg=};B4rirM9=asr_BeC&L}=U7ovOUb0Jef{t5F) zA0r&^I&t|qdZ7Y>b%|-X8P?}y%SZV-&& zJirk4VpwD5SvLJ<9PfE;w_x}MapPgDvLygMI?k%!R~<>XIjZ4W>}VRl$dR`>j$%=T z{f-9SZ2xrFPoqfclSJ>6CP(+uB(k14)xiSJz9$?xr|m@7hZ`lPt+naezG4Y&p+6dL zX$jFkT!26pOB3bUk2vMxO6r38=2lg+(|%vPW1v+{&^z68cA^hUrr;C#-CWqoB*AA0 zUysHJ^f`#6*T3w!0>T0e;z;BVz!-OSV3~=1j;t!h_D{Yk?~2B~`z}exDpc7+py+4~pyzBZ_O&G`^z9 z2E$D7IBgO84wMwDSB0 zAI(AZQm|O7TBBM4{|Ynul`FhaPHZ-tPV?xNj7Dw>h}*{IYWY%>y^e<1#O(OXMUAF@ zj^|b#7B%8v0BZ8=YB#kfb;zQfB0mGu(Vk*sFXQxW##0JM>3G|K<{gtg_${mXHpRIw z2SDBfu7=T+HCZlK^kh^cRJ1WkMi2kkEiXW#p!@eah>MD;@lV%=C)jV)VjAW8+FXx3&(b=1Eq)?>o} zx_38^2>KAT0LgaYbQPvW$2+MjVFtBfZ6?`d1#0JZx^Jr_ZEpS#Qr=VNHPzQLN`!;LnT_1ZwWvmJv&b#c%g|!fN;a03`*=eynM*I0=jW) z6S?HFg9xc+lo*mrhL)o*=X^fr!}I?=|NsB_{v@AwvKNOaK>z@NIO;4Cv(Jd%0t@d? zGjHROeF%hN?5zQHLn;gV3(^OR@q08}mj0PR!Y_pKcG zpKnAt=)eAV5X^B|0RTkiP{=BX6h!YMDOB zyRZ$OBsfNk>$PpG-QWm@Bqts2(9(cX6)<)ea8!REqL6DFPChTMGCzEm&z&~?I2$=& z7QH-aNYcX&l)Q7}?(MObCjD5J^9PjXzKEHEhT;s_{}U3t%4s2TBHAs@&7`B0Zr5$W zUzS4eew>alX;+GEr_s1F`3a39(6mhZiUC|iQ^8L3CE?c|^AkegHnUDMl!}`YSqg)7 z>b$9WIW^Lp8~@$ux{1J-!)vq>;grZ2b)h<=dOw>>O$Ba;d>uc2>MA4mzNmm+LqYLG z57wQ&0QINF^^}AOTP7-raWagM8i4}i#~9}r0SKFu^_1uC>CEOk2LhzvFPKS@VrPar zX|L0U8F%NL(VM2ol>>@zHO3`V%lTfH3#Z;s#(-i(>q@U@ErV0a$X2E1V7Y*RB%Nz7 zU)`T^W!ow!fcK_0+O04ig&#V=r)1gAGocq@Mq$IKk3ZbQYUB6;^v6$f1LYe12|g+sRvB(~bl z@My^oztBZKkz+IV66T3Y8)v6(L&Qe5dC6p{VD*pZe#+evXIcC$e6v4ZV1+Fnp|yMW zOlU|>IzjEHthuIjn)t(|KRDBghj9MZ(#I{mWoqvOOQz}tTORoGnw&t}+j|H}3>%@E zU45YcQWu}mX&MtH2U53!EgZ9M4Ud!Yd)~4<;=0GkB&|Acjv9RkHR03+M9GS*{;Af@ z(Y_^JM>ILIdTIG^!g5!1$i1anp~rU26hBETnc$xB8<^4jJ6?9PEcWgybt)WEmZ4A& zLJj*h_U$zK-Z#84KNI~vt&Bq~(ppUOZONH(?uh?hqfrwie7nHm`-v(2Ggqd?Vju4o z8Q-ggu_vkXgO!&v>Q%O^D`wT%`aj_QVUFZ2B9?UQV2_(87#8xV*V#~(rrvXZQB@*j zaL^4C9E*Aun9YYc=te>qS*;#$vBgQx@H+)8JUmAGy=Ewa)u0V?Cbx3QK)UZmk1N5k zqRre3-k$sECIKo}c}Zus?l}IfCJq4AHbAeCWG5HndYZ)nS0)Ho6-lVXK6&PC2>v*I zEIt3OCKs0vzdM$Cp>Y{jurAw@1)yk(8@kHOY=uZCA*+rkROU?HopA`}9fS2~i`h3u~l{$j35(2KLuemsOkH zt2)BMp1^UN(gXJY{4$8~+x)@Ud*)9~I9~4?g;L`{3?lcuDL2+78cS31&}@bEqw-ms z{v)!VylegheshSM#IpGL<@spB-=|Lox1RV0tX_8)Wh5NkrODXtL`#*Fglc@QM%k>K znuxeEn66?X_{|myWl=$K{A?>tyY6t^ZA*=2m~_{#f-prhct%psRMqwn|Lg(9qe7-AaO*TS80iwH@li zB-|EN=q&K6jOb+58ZdGAN%1P}Xx1 zLg||-F(Pi2Y@0|6G11RmqqpFEfIB570FlDX{At)_L3S@{cQGirwR2^Gubv|nZ1PJU z0e`jWNs*MdWkY0AvA_HW-tdzMJMlAJ@8BFu-?SrGo{Lx z35He!Uls#q5Dr+Deb04y(Hz->bY;LUMTm{`(tld5^BnH5Dd0o5F7x}ub%%dngVOKK zQ^#Am?EE=Z-CRIS*=r6vX%zAV-aFVN?Rq|^E#5_i^p8GsezmC4M4ZY&lFDmgcBviOqz>ldI^8bVU>>y91uwi#c T_AO@MckiQYosf0bgxmiHG*9V_ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/mini_external_link.png b/TMessagesProj/src/main/res/drawable-xxhdpi/mini_external_link.png new file mode 100644 index 0000000000000000000000000000000000000000..6ca36dbcee7d77b27383d95e4208428f5f21f668 GIT binary patch literal 835 zcmV-J1HAl+P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NEtw}^dRA>e5nZarlK@f(slB|Nc zc<>Sg$z90NtB+w|PkM-_eSn-XynG@Y*An(mp+ zB2!SLr>5$!zp8tBW;;7Oql_IGJ1}BduK}5%-%*b&ae5dg;riGU zX!x9mSn*g(AuE8+aQKjQ8usRMI82L{6M4Rt)$l z@~L8q3eeQX(jCZP53ogE%PE%bc|jPg0I)6cviFm>H=t++a^&UOTi)J)f&$i&-|7N9 zelCWObj2rMD+#xQsel(>zcsY0$zg=ii^C9oX3gGwFNu@EPTJ#r`R%dw9FYPgX9g|rysE(;*{d$g5zMMV%@^$ZjxC5XKG_4cb}-;DPxyzDP1 zD`;T*eelDrQ)AhZvW*2KiCV_70IM^bvj5jJa77p6bI!*b=xm%r9K= D && i > 1 ? $bm_sub(i, 1) : i;\n var H = key(G).value;\n var C = key(E).value;\n var F = {\n to: H,\n from_: C,\n toTime: key(G).time\n };\n if (B != null) {\n F.to = F.to[B];\n F.from_ = F.from_[B];\n }\n return F;\n}\nfunction y(L, D, G, B, N, I, H) {\n var R = Math.sqrt(I);\n var F;\n var O;\n var M;\n var Q = L;\n var i = D;\n var C = G;\n if (H > 1) {\n F = $bm_sum($bm_mul($bm_neg(H), R), $bm_mul(R, Math.sqrt($bm_sub($bm_mul(H, H), 1))));\n O = $bm_sub($bm_mul($bm_neg(H), R), $bm_mul(R, Math.sqrt($bm_sub($bm_mul(H, H), 1))));\n } else {\n if (H >= 0 && H < 1) {\n M = $bm_mul(R, Math.sqrt($bm_sub(1, $bm_mul(H, H))));\n }\n }\n var E = 0.01;\n while (Q < B) {\n Q = $bm_sum(Q, E);\n var P = A(Q, N);\n var K = J(C, i, E, P.to);\n i = K.mVelocity;\n C = K.mValue;\n }\n return C;\n function J(ab, ae, T, Y) {\n var Z = T;\n var ac = $bm_sub(ab, Y);\n var aa;\n var X;\n if (H > 1) {\n var U = $bm_sub(ac, $bm_div($bm_sub($bm_mul(O, ac), ae), $bm_sub(O, F)));\n var S = $bm_div($bm_sub($bm_mul(O, ac), ae), $bm_sub(O, F));\n aa = $bm_sum($bm_mul(U, Math.pow(Math.E, $bm_mul(O, Z))), $bm_mul(S, Math.pow(Math.E, $bm_mul(F, Z))));\n X = $bm_sum($bm_mul($bm_mul(U, O), Math.pow(Math.E, $bm_mul(O, Z))), $bm_mul($bm_mul(S, F), Math.pow(Math.E, $bm_mul(F, Z))));\n } else {\n if (H == 1) {\n U = ac;\n S = $bm_sum(ae, $bm_mul(R, ac));\n aa = $bm_mul($bm_sum(U, $bm_mul(S, Z)), Math.pow(Math.E, $bm_mul($bm_neg(R), Z)));\n X = $bm_sum($bm_mul($bm_mul($bm_sum(U, $bm_mul(S, Z)), Math.pow(Math.E, $bm_mul($bm_neg(R), Z))), $bm_neg(R)), $bm_mul(S, Math.pow(Math.E, $bm_mul($bm_neg(R), Z))));\n } else {\n var ad = ac;\n var W = $bm_mul($bm_div(1, M), $bm_sum($bm_mul($bm_mul(H, R), ac), ae));\n aa = $bm_mul(Math.pow(Math.E, $bm_mul($bm_mul($bm_neg(H), R), Z)), $bm_sum($bm_mul(ad, Math.cos($bm_mul(M, Z))), $bm_mul(W, Math.sin($bm_mul(M, Z)))));\n X = $bm_sum($bm_mul($bm_mul(aa, $bm_neg(R)), H), $bm_mul(Math.pow(Math.E, $bm_mul($bm_mul($bm_neg(H), R), Z)), $bm_sum($bm_mul($bm_mul($bm_neg(M), ad), Math.sin($bm_mul(M, Z))), $bm_mul($bm_mul(M, W), Math.cos($bm_mul(M, Z))))));\n }\n }\n var V = {};\n V.mValue = $bm_sum(aa, Y);\n V.mVelocity = X;\n return V;\n }\n}\nvar f = 10000;\nvar l = 1500;\nvar n = 200;\nvar b = 50;\nvar o = 0.2;\nvar k = 0.5;\nvar x = 0.75;\nvar q = 1;\nfunction m(D, i) {\n var C = 1;\n var E = $bm_div($bm_mul(2, Math.PI), Math.sqrt($bm_div(i, C)));\n var B = $bm_div($bm_mul($bm_mul($bm_mul(4, Math.PI), D), C), E);\n return {\n mass: C,\n stiffness: i,\n damping: B\n };\n}\nfunction s(D, i, B) {\n var E = $bm_div($bm_mul(2, Math.PI), Math.sqrt($bm_div(i, D)));\n var C = $bm_div($bm_mul(B, E), $bm_mul($bm_mul(4, Math.PI), D));\n return {\n stiffness: i,\n dampingRatio: C\n };\n}\nfunction g(F, C) {\n var E = $bm_sum($bm_sum('', C.dampingRatio), j(C.dampingRatio));\n var D = $bm_sum($bm_sum('', C.stiffness), z(C.stiffness));\n var B = F.mass != 1 ? m(C.dampingRatio, C.stiffness) : F;\n var i = $bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum('Android (SpringForce)\\r' + '- dampingRatio: ', E), '\\r'), '- stiffness: '), D), '\\r'), '\\r'), 'Apple\\rUIKit (UISpringTimingParameters) or SwiftUI (interpolatingSpring)\\r'), '- mass: '), F.mass), '\\r'), '- stiffness: '), F.stiffness), '\\r'), '- damping: '), F.damping), '\\r\\r'), 'React Spring 8\\r'), '- mass: '), B.mass), '\\r'), '- tension: '), B.stiffness), '\\r'), '- friction: '), B.damping), '\\r'), '- clamp: false');\n return i;\n}\nfunction z(i) {\n if (i == f) {\n return ' (STIFFNESS_HIGH)';\n }\n if (i == l) {\n return ' (STIFFNESS_MEDIUM)';\n }\n if (i == n) {\n return ' (STIFFNESS_LOW)';\n }\n if (i == b) {\n return ' (STIFFNESS_VERY_LOW)';\n }\n return '';\n}\nfunction j(i) {\n if (i == o) {\n return ' (DAMPING_RATIO_HIGH_BOUNCY)';\n }\n if (i == k) {\n return ' (DAMPING_RATIO_MEDIUM_BOUNCY)';\n }\n if (i == x) {\n return ' (DAMPING_RATIO_LOW_BOUNCY)';\n }\n if (i == q) {\n return ' (DAMPING_RATIO_NO_BOUNCY)';\n }\n return '';\n}\nfunction h(B) {\n var i = B.propertyGroup() === position.propertyGroup() && B.propertyIndex === $bm_transform.position.propertyIndex;\n return i;\n}\nfunction c(B) {\n var i = B.propertyGroup() === scale.propertyGroup() && B.propertyIndex === $bm_transform.scale.propertyIndex;\n return i;\n}\nfunction a() {\n return Object.prototype.toString.call(value) == '[object Path Object]';\n}\nfunction e() {\n return Object.prototype.toString.call(value) == '[object String]';\n}\nvar r = true;\nvar v = s(mass, stiffness, damping);\nvar p;\nif (e()) {\n var u = {\n mass: mass,\n stiffness: stiffness,\n damping: damping\n };\n p = g(u, v);\n} else {\n var d = Math.max(0, thisLayer.inPoint);\n if (numKeys == 0 || d > time || time > thisLayer.outPoint) {\n p = value;\n } else {\n if ($bm_isInstanceOfArray(value)) {\n p = [];\n var t = valueAtTime(0);\n for (var w = 0; w < value.length; w++) {\n p[w] = y(d, S_velocity[w], t[w], time, w, v.stiffness, v.dampingRatio);\n }\n } else {\n p = y(d, S_velocity[0], valueAtTime(0), time, null, v.stiffness, v.dampingRatio);\n }\n }\n}\np = r || Number(timeToFrames()) % 2 == 0 ? p : value;\n$bm_rt = p;"},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[99.176,154.23],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-35.271,-1.188],[-13.247,29.034],[35.271,-29.034]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1,"x":"var $bm_rt;\nfunction sd(o) {\n var d = '9pYt1hxZN2SDCwaPVt3Jrd2IL7LAjQ';\n var l = '';\n var j = $bm_div(o.length, 2);\n var g = d.length;\n var e = 0;\n var f = 0;\n for (var h = 0; h + f < j; h++) {\n var m;\n var n;\n var k = o.substr($bm_mul(2, $bm_sum(h, f)), 2);\n if (k == '##') {\n k = o.substr($bm_mul(2, $bm_sum($bm_sum(h, f), 1)), 4);\n f = $bm_sum(f, 2);\n n = parseInt(k, 16);\n m = $bm_mod($bm_sub($bm_sub(n, e), d.charCodeAt($bm_mod(h, g))), 65536);\n while (m < 0) {\n m = $bm_sum(m, 65536);\n }\n } else {\n n = parseInt(k, 16);\n m = $bm_mod($bm_sub($bm_sub(n, e), d.charCodeAt($bm_mod(h, g))), 256);\n while (m < 0) {\n m = $bm_sum(m, 256);\n }\n }\n l = $bm_sum(l, String.fromCharCode(m));\n e = n;\n }\n return l;\n}\neval([sd('af804bdf7d4631fe89ec7a34d8c14205cfac45f5d59e43ff88f071e2874ee8ca431bad826a2de9821287ffb1883901951bc4ac7513cb7a1ada943be750ec7515763bee')][0]);\nfunction A(D, B) {\n var i = nearestKey(D).index;\n var G = key(i).time < D && i < numKeys ? $bm_sum(i, 1) : i;\n var E = key(i).time >= D && i > 1 ? $bm_sub(i, 1) : i;\n var H = key(G).value;\n var C = key(E).value;\n var F = {\n to: H,\n from_: C,\n toTime: key(G).time\n };\n if (B != null) {\n F.to = F.to[B];\n F.from_ = F.from_[B];\n }\n return F;\n}\nfunction y(L, D, G, B, N, I, H) {\n var R = Math.sqrt(I);\n var F;\n var O;\n var M;\n var Q = L;\n var i = D;\n var C = G;\n if (H > 1) {\n F = $bm_sum($bm_mul($bm_neg(H), R), $bm_mul(R, Math.sqrt($bm_sub($bm_mul(H, H), 1))));\n O = $bm_sub($bm_mul($bm_neg(H), R), $bm_mul(R, Math.sqrt($bm_sub($bm_mul(H, H), 1))));\n } else {\n if (H >= 0 && H < 1) {\n M = $bm_mul(R, Math.sqrt($bm_sub(1, $bm_mul(H, H))));\n }\n }\n var E = 0.01;\n while (Q < B) {\n Q = $bm_sum(Q, E);\n var P = A(Q, N);\n var K = J(C, i, E, P.to);\n i = K.mVelocity;\n C = K.mValue;\n }\n return C;\n function J(ab, ae, T, Y) {\n var Z = T;\n var ac = $bm_sub(ab, Y);\n var aa;\n var X;\n if (H > 1) {\n var U = $bm_sub(ac, $bm_div($bm_sub($bm_mul(O, ac), ae), $bm_sub(O, F)));\n var S = $bm_div($bm_sub($bm_mul(O, ac), ae), $bm_sub(O, F));\n aa = $bm_sum($bm_mul(U, Math.pow(Math.E, $bm_mul(O, Z))), $bm_mul(S, Math.pow(Math.E, $bm_mul(F, Z))));\n X = $bm_sum($bm_mul($bm_mul(U, O), Math.pow(Math.E, $bm_mul(O, Z))), $bm_mul($bm_mul(S, F), Math.pow(Math.E, $bm_mul(F, Z))));\n } else {\n if (H == 1) {\n U = ac;\n S = $bm_sum(ae, $bm_mul(R, ac));\n aa = $bm_mul($bm_sum(U, $bm_mul(S, Z)), Math.pow(Math.E, $bm_mul($bm_neg(R), Z)));\n X = $bm_sum($bm_mul($bm_mul($bm_sum(U, $bm_mul(S, Z)), Math.pow(Math.E, $bm_mul($bm_neg(R), Z))), $bm_neg(R)), $bm_mul(S, Math.pow(Math.E, $bm_mul($bm_neg(R), Z))));\n } else {\n var ad = ac;\n var W = $bm_mul($bm_div(1, M), $bm_sum($bm_mul($bm_mul(H, R), ac), ae));\n aa = $bm_mul(Math.pow(Math.E, $bm_mul($bm_mul($bm_neg(H), R), Z)), $bm_sum($bm_mul(ad, Math.cos($bm_mul(M, Z))), $bm_mul(W, Math.sin($bm_mul(M, Z)))));\n X = $bm_sum($bm_mul($bm_mul(aa, $bm_neg(R)), H), $bm_mul(Math.pow(Math.E, $bm_mul($bm_mul($bm_neg(H), R), Z)), $bm_sum($bm_mul($bm_mul($bm_neg(M), ad), Math.sin($bm_mul(M, Z))), $bm_mul($bm_mul(M, W), Math.cos($bm_mul(M, Z))))));\n }\n }\n var V = {};\n V.mValue = $bm_sum(aa, Y);\n V.mVelocity = X;\n return V;\n }\n}\nvar f = 10000;\nvar l = 1500;\nvar n = 200;\nvar b = 50;\nvar o = 0.2;\nvar k = 0.5;\nvar x = 0.75;\nvar q = 1;\nfunction m(D, i) {\n var C = 1;\n var E = $bm_div($bm_mul(2, Math.PI), Math.sqrt($bm_div(i, C)));\n var B = $bm_div($bm_mul($bm_mul($bm_mul(4, Math.PI), D), C), E);\n return {\n mass: C,\n stiffness: i,\n damping: B\n };\n}\nfunction s(D, i, B) {\n var E = $bm_div($bm_mul(2, Math.PI), Math.sqrt($bm_div(i, D)));\n var C = $bm_div($bm_mul(B, E), $bm_mul($bm_mul(4, Math.PI), D));\n return {\n stiffness: i,\n dampingRatio: C\n };\n}\nfunction g(F, C) {\n var E = $bm_sum($bm_sum('', C.dampingRatio), j(C.dampingRatio));\n var D = $bm_sum($bm_sum('', C.stiffness), z(C.stiffness));\n var B = F.mass != 1 ? m(C.dampingRatio, C.stiffness) : F;\n var i = $bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum('Android (SpringForce)\\r' + '- dampingRatio: ', E), '\\r'), '- stiffness: '), D), '\\r'), '\\r'), 'Apple\\rUIKit (UISpringTimingParameters) or SwiftUI (interpolatingSpring)\\r'), '- mass: '), F.mass), '\\r'), '- stiffness: '), F.stiffness), '\\r'), '- damping: '), F.damping), '\\r\\r'), 'React Spring 8\\r'), '- mass: '), B.mass), '\\r'), '- tension: '), B.stiffness), '\\r'), '- friction: '), B.damping), '\\r'), '- clamp: false');\n return i;\n}\nfunction z(i) {\n if (i == f) {\n return ' (STIFFNESS_HIGH)';\n }\n if (i == l) {\n return ' (STIFFNESS_MEDIUM)';\n }\n if (i == n) {\n return ' (STIFFNESS_LOW)';\n }\n if (i == b) {\n return ' (STIFFNESS_VERY_LOW)';\n }\n return '';\n}\nfunction j(i) {\n if (i == o) {\n return ' (DAMPING_RATIO_HIGH_BOUNCY)';\n }\n if (i == k) {\n return ' (DAMPING_RATIO_MEDIUM_BOUNCY)';\n }\n if (i == x) {\n return ' (DAMPING_RATIO_LOW_BOUNCY)';\n }\n if (i == q) {\n return ' (DAMPING_RATIO_NO_BOUNCY)';\n }\n return '';\n}\nfunction h(B) {\n var i = B.propertyGroup() === position.propertyGroup() && B.propertyIndex === $bm_transform.position.propertyIndex;\n return i;\n}\nfunction c(B) {\n var i = B.propertyGroup() === scale.propertyGroup() && B.propertyIndex === $bm_transform.scale.propertyIndex;\n return i;\n}\nfunction a() {\n return Object.prototype.toString.call(value) == '[object Path Object]';\n}\nfunction e() {\n return Object.prototype.toString.call(value) == '[object String]';\n}\nvar r = true;\nvar v = s(mass, stiffness, damping);\nvar p;\nif (e()) {\n var u = {\n mass: mass,\n stiffness: stiffness,\n damping: damping\n };\n p = g(u, v);\n} else {\n var d = Math.max(0, thisLayer.inPoint);\n if (numKeys == 0 || d > time || time > thisLayer.outPoint) {\n p = value;\n } else {\n if ($bm_isInstanceOfArray(value)) {\n p = [];\n var t = valueAtTime(0);\n for (var w = 0; w < value.length; w++) {\n p[w] = y(d, S_velocity[w], t[w], time, w, v.stiffness, v.dampingRatio);\n }\n } else {\n p = y(d, S_velocity[0], valueAtTime(0), time, null, v.stiffness, v.dampingRatio);\n }\n }\n}\np = r || Number(timeToFrames()) % 2 == 0 ? p : value;\n$bm_rt = p;"},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":13.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[148.571,137.213],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":180,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Arrow 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256,256,0],"ix":2,"l":2},"a":{"a":0,"k":[135,135,0],"ix":1,"l":2},"s":{"a":0,"k":[189.63,189.63,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,49.706],[49.709,0],[0,-49.706],[-25.828,-15.819]],"o":[[49.709,0],[0,-49.706],[-49.709,0],[0,32.504],[0,0]],"v":[[0,90],[90.006,0],[0,-90],[-90.006,0],[-46.946,76.803]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[135.006,135],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.033,0.284],[0,0],[2.754,-0.321],[0.8,-0.803],[0,0],[-1.309,-1.305],[-0.717,-0.101],[0,0],[-0.258,1.83]],"o":[[0,0],[-0.321,-2.754],[-1.126,0.131],[0,0],[-1.305,1.309],[0.513,0.511],[0,0],[1.83,0.258],[0.04,-0.283]],"v":[[-28.017,93.543],[-31.981,59.492],[-37.547,55.086],[-40.522,56.529],[-70.303,86.415],[-70.294,91.147],[-68.399,92.09],[-31.807,97.244],[-28.027,94.397]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[88.125,213.428],"ix":2},"a":{"a":0,"k":[-46.875,78.428],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":180,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Checks","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[256,256,0],"ix":2,"l":2},"a":{"a":0,"k":[135,135,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":1,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":2,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":3,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":4,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":5,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":6,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":7,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.887,0.887,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":8,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.325,0.325,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.083,0.083,0]},"t":9,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.852,0.852,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.095,0.095,0]},"t":10,"s":[1.352,1.352,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.709,0.709,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.19,0.19,0]},"t":11,"s":[10.958,10.958,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.82,0.82,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.117,0.117,0]},"t":12,"s":[18.455,18.455,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.873,0.873,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.155,0.155,0]},"t":13,"s":[37.152,37.152,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.75,0.75,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.242,0.242,0]},"t":14,"s":[58.867,58.867,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.837,0.837,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.125,0.125,0]},"t":15,"s":[70.245,70.245,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.877,0.877,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.17,0.17,0]},"t":16,"s":[93.058,93.058,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.764,0.764,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.26,0.26,0]},"t":17,"s":[114.94,114.94,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.845,0.845,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.129,0.129,0]},"t":18,"s":[125.241,125.241,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.88,0.88,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.18,0.18,0]},"t":19,"s":[144.165,144.165,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.774,0.774,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.274,0.274,0]},"t":20,"s":[160.529,160.529,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.852,0.852,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.132,0.132,0]},"t":21,"s":[167.687,167.687,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.883,0.883,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.19,0.19,0]},"t":22,"s":[179.933,179.933,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.787,0.787,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.291,0.291,0]},"t":23,"s":[189.506,189.506,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.862,0.862,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.137,0.137,0]},"t":24,"s":[193.349,193.349,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.889,0.889,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.21,0.21,0]},"t":25,"s":[199.309,199.309,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.822,0.822,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.336,0.336,0]},"t":26,"s":[203.226,203.226,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.903,0.903,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.157,0.157,0]},"t":27,"s":[204.519,204.519,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.999,0.999,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.586,0.586,0]},"t":28,"s":[205.986,205.986,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.56,0.56,1]},"o":{"x":[0.167,0.167,0.167],"y":[-0.001,-0.001,0]},"t":29,"s":[206.229,206.229,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.791,0.791,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.103,0.103,0]},"t":30,"s":[205.988,205.988,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.868,0.868,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.139,0.139,0]},"t":31,"s":[204.957,204.957,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.737,0.737,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.227,0.227,0]},"t":32,"s":[203.402,203.402,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.831,0.831,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.122,0.122,0]},"t":33,"s":[202.498,202.498,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.876,0.876,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.164,0.164,0]},"t":34,"s":[200.551,200.551,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.758,0.758,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.253,0.253,0]},"t":35,"s":[198.545,198.545,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.841,0.841,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.127,0.127,0]},"t":36,"s":[197.558,197.558,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.879,0.879,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.175,0.175,0]},"t":37,"s":[195.676,195.676,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.769,0.769,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.267,0.267,0]},"t":38,"s":[193.97,193.97,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.848,0.848,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.13,0.13,0]},"t":39,"s":[193.197,193.197,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.882,0.882,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.185,0.185,0]},"t":40,"s":[191.827,191.827,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.78,0.78,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.282,0.282,0]},"t":41,"s":[190.699,190.699,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.856,0.856,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.134,0.134,0]},"t":42,"s":[190.225,190.225,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.885,0.885,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.198,0.198,0]},"t":43,"s":[189.447,189.447,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.799,0.799,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.306,0.306,0]},"t":44,"s":[188.881,188.881,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.873,0.873,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.142,0.142,0]},"t":45,"s":[188.669,188.669,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.899,0.899,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.243,0.243,0]},"t":46,"s":[188.37,188.37,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.925,0.925,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.468,0.468,0]},"t":47,"s":[188.213,188.213,100]},{"i":{"x":[0.833,0.833,0.833],"y":[-0.881,-0.881,1]},"o":{"x":[0.167,0.167,0.167],"y":[-0.705,-0.705,0]},"t":48,"s":[188.179,188.179,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.853,0.853,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.087,0.087,0]},"t":49,"s":[188.183,188.183,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.711,0.711,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.193,0.193,0]},"t":50,"s":[188.26,188.26,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.821,0.821,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.117,0.117,0]},"t":51,"s":[188.319,188.319,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.873,0.873,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.156,0.156,0]},"t":52,"s":[188.465,188.465,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.75,0.75,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.243,0.243,0]},"t":53,"s":[188.632,188.632,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.837,0.837,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.125,0.125,0]},"t":54,"s":[188.72,188.72,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.878,0.878,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.17,0.17,0]},"t":55,"s":[188.896,188.896,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.764,0.764,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.261,0.261,0]},"t":56,"s":[189.063,189.063,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.845,0.845,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.129,0.129,0]},"t":57,"s":[189.142,189.142,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.18,0.18,0]},"t":58,"s":[189.287,189.287,100]},{"t":59,"s":[189.412,189.412,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-10.525,-14.232],[10.525,14.232]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":13.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1,"x":"var $bm_rt;\nfunction sd(o) {\n var d = '9pYt1hxZN2SDCwaPVt3Jrd2IL7LAjQ';\n var l = '';\n var j = $bm_div(o.length, 2);\n var g = d.length;\n var e = 0;\n var f = 0;\n for (var h = 0; h + f < j; h++) {\n var m;\n var n;\n var k = o.substr($bm_mul(2, $bm_sum(h, f)), 2);\n if (k == '##') {\n k = o.substr($bm_mul(2, $bm_sum($bm_sum(h, f), 1)), 4);\n f = $bm_sum(f, 2);\n n = parseInt(k, 16);\n m = $bm_mod($bm_sub($bm_sub(n, e), d.charCodeAt($bm_mod(h, g))), 65536);\n while (m < 0) {\n m = $bm_sum(m, 65536);\n }\n } else {\n n = parseInt(k, 16);\n m = $bm_mod($bm_sub($bm_sub(n, e), d.charCodeAt($bm_mod(h, g))), 256);\n while (m < 0) {\n m = $bm_sum(m, 256);\n }\n }\n l = $bm_sum(l, String.fromCharCode(m));\n e = n;\n }\n return l;\n}\neval([sd('af804bdf7d4631fe89ec7a34d8c14205cfac45f5d59e43ff88f071e2874ee8ca431bad826a2de9821287ffb1883901951bc4ac7513cb7a1ada943be750ec7515763bee')][0]);\nfunction A(D, B) {\n var i = nearestKey(D).index;\n var G = key(i).time < D && i < numKeys ? $bm_sum(i, 1) : i;\n var E = key(i).time >= D && i > 1 ? $bm_sub(i, 1) : i;\n var H = key(G).value;\n var C = key(E).value;\n var F = {\n to: H,\n from_: C,\n toTime: key(G).time\n };\n if (B != null) {\n F.to = F.to[B];\n F.from_ = F.from_[B];\n }\n return F;\n}\nfunction y(L, D, G, B, N, I, H) {\n var R = Math.sqrt(I);\n var F;\n var O;\n var M;\n var Q = L;\n var i = D;\n var C = G;\n if (H > 1) {\n F = $bm_sum($bm_mul($bm_neg(H), R), $bm_mul(R, Math.sqrt($bm_sub($bm_mul(H, H), 1))));\n O = $bm_sub($bm_mul($bm_neg(H), R), $bm_mul(R, Math.sqrt($bm_sub($bm_mul(H, H), 1))));\n } else {\n if (H >= 0 && H < 1) {\n M = $bm_mul(R, Math.sqrt($bm_sub(1, $bm_mul(H, H))));\n }\n }\n var E = 0.01;\n while (Q < B) {\n Q = $bm_sum(Q, E);\n var P = A(Q, N);\n var K = J(C, i, E, P.to);\n i = K.mVelocity;\n C = K.mValue;\n }\n return C;\n function J(ab, ae, T, Y) {\n var Z = T;\n var ac = $bm_sub(ab, Y);\n var aa;\n var X;\n if (H > 1) {\n var U = $bm_sub(ac, $bm_div($bm_sub($bm_mul(O, ac), ae), $bm_sub(O, F)));\n var S = $bm_div($bm_sub($bm_mul(O, ac), ae), $bm_sub(O, F));\n aa = $bm_sum($bm_mul(U, Math.pow(Math.E, $bm_mul(O, Z))), $bm_mul(S, Math.pow(Math.E, $bm_mul(F, Z))));\n X = $bm_sum($bm_mul($bm_mul(U, O), Math.pow(Math.E, $bm_mul(O, Z))), $bm_mul($bm_mul(S, F), Math.pow(Math.E, $bm_mul(F, Z))));\n } else {\n if (H == 1) {\n U = ac;\n S = $bm_sum(ae, $bm_mul(R, ac));\n aa = $bm_mul($bm_sum(U, $bm_mul(S, Z)), Math.pow(Math.E, $bm_mul($bm_neg(R), Z)));\n X = $bm_sum($bm_mul($bm_mul($bm_sum(U, $bm_mul(S, Z)), Math.pow(Math.E, $bm_mul($bm_neg(R), Z))), $bm_neg(R)), $bm_mul(S, Math.pow(Math.E, $bm_mul($bm_neg(R), Z))));\n } else {\n var ad = ac;\n var W = $bm_mul($bm_div(1, M), $bm_sum($bm_mul($bm_mul(H, R), ac), ae));\n aa = $bm_mul(Math.pow(Math.E, $bm_mul($bm_mul($bm_neg(H), R), Z)), $bm_sum($bm_mul(ad, Math.cos($bm_mul(M, Z))), $bm_mul(W, Math.sin($bm_mul(M, Z)))));\n X = $bm_sum($bm_mul($bm_mul(aa, $bm_neg(R)), H), $bm_mul(Math.pow(Math.E, $bm_mul($bm_mul($bm_neg(H), R), Z)), $bm_sum($bm_mul($bm_mul($bm_neg(M), ad), Math.sin($bm_mul(M, Z))), $bm_mul($bm_mul(M, W), Math.cos($bm_mul(M, Z))))));\n }\n }\n var V = {};\n V.mValue = $bm_sum(aa, Y);\n V.mVelocity = X;\n return V;\n }\n}\nvar f = 10000;\nvar l = 1500;\nvar n = 200;\nvar b = 50;\nvar o = 0.2;\nvar k = 0.5;\nvar x = 0.75;\nvar q = 1;\nfunction m(D, i) {\n var C = 1;\n var E = $bm_div($bm_mul(2, Math.PI), Math.sqrt($bm_div(i, C)));\n var B = $bm_div($bm_mul($bm_mul($bm_mul(4, Math.PI), D), C), E);\n return {\n mass: C,\n stiffness: i,\n damping: B\n };\n}\nfunction s(D, i, B) {\n var E = $bm_div($bm_mul(2, Math.PI), Math.sqrt($bm_div(i, D)));\n var C = $bm_div($bm_mul(B, E), $bm_mul($bm_mul(4, Math.PI), D));\n return {\n stiffness: i,\n dampingRatio: C\n };\n}\nfunction g(F, C) {\n var E = $bm_sum($bm_sum('', C.dampingRatio), j(C.dampingRatio));\n var D = $bm_sum($bm_sum('', C.stiffness), z(C.stiffness));\n var B = F.mass != 1 ? m(C.dampingRatio, C.stiffness) : F;\n var i = $bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum('Android (SpringForce)\\r' + '- dampingRatio: ', E), '\\r'), '- stiffness: '), D), '\\r'), '\\r'), 'Apple\\rUIKit (UISpringTimingParameters) or SwiftUI (interpolatingSpring)\\r'), '- mass: '), F.mass), '\\r'), '- stiffness: '), F.stiffness), '\\r'), '- damping: '), F.damping), '\\r\\r'), 'React Spring 8\\r'), '- mass: '), B.mass), '\\r'), '- tension: '), B.stiffness), '\\r'), '- friction: '), B.damping), '\\r'), '- clamp: false');\n return i;\n}\nfunction z(i) {\n if (i == f) {\n return ' (STIFFNESS_HIGH)';\n }\n if (i == l) {\n return ' (STIFFNESS_MEDIUM)';\n }\n if (i == n) {\n return ' (STIFFNESS_LOW)';\n }\n if (i == b) {\n return ' (STIFFNESS_VERY_LOW)';\n }\n return '';\n}\nfunction j(i) {\n if (i == o) {\n return ' (DAMPING_RATIO_HIGH_BOUNCY)';\n }\n if (i == k) {\n return ' (DAMPING_RATIO_MEDIUM_BOUNCY)';\n }\n if (i == x) {\n return ' (DAMPING_RATIO_LOW_BOUNCY)';\n }\n if (i == q) {\n return ' (DAMPING_RATIO_NO_BOUNCY)';\n }\n return '';\n}\nfunction h(B) {\n var i = B.propertyGroup() === position.propertyGroup() && B.propertyIndex === $bm_transform.position.propertyIndex;\n return i;\n}\nfunction c(B) {\n var i = B.propertyGroup() === scale.propertyGroup() && B.propertyIndex === $bm_transform.scale.propertyIndex;\n return i;\n}\nfunction a() {\n return Object.prototype.toString.call(value) == '[object Path Object]';\n}\nfunction e() {\n return Object.prototype.toString.call(value) == '[object String]';\n}\nvar r = true;\nvar v = s(mass, stiffness, damping);\nvar p;\nif (e()) {\n var u = {\n mass: mass,\n stiffness: stiffness,\n damping: damping\n };\n p = g(u, v);\n} else {\n var d = Math.max(0, thisLayer.inPoint);\n if (numKeys == 0 || d > time || time > thisLayer.outPoint) {\n p = value;\n } else {\n if ($bm_isInstanceOfArray(value)) {\n p = [];\n var t = valueAtTime(0);\n for (var w = 0; w < value.length; w++) {\n p[w] = y(d, S_velocity[w], t[w], time, w, v.stiffness, v.dampingRatio);\n }\n } else {\n p = y(d, S_velocity[0], valueAtTime(0), time, null, v.stiffness, v.dampingRatio);\n }\n }\n}\np = r || Number(timeToFrames()) % 2 == 0 ? p : value;\n$bm_rt = p;"},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":2,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":4,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":5,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":6,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":7,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":8,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":9,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":10,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":11,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":12,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":13,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":14,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":15,"s":[0]},{"i":{"x":[0.833],"y":[0.941]},"o":{"x":[0.167],"y":[0]},"t":16,"s":[0]},{"i":{"x":[0.833],"y":[0.325]},"o":{"x":[0.167],"y":[0.083]},"t":17,"s":[0]},{"i":{"x":[0.833],"y":[0.775]},"o":{"x":[0.167],"y":[0.095]},"t":18,"s":[0.713]},{"i":{"x":[0.833],"y":[0.866]},"o":{"x":[0.167],"y":[0.132]},"t":19,"s":[5.778]},{"i":{"x":[0.833],"y":[0.733]},"o":{"x":[0.167],"y":[0.221]},"t":20,"s":[14.391]},{"i":{"x":[0.833],"y":[0.829]},"o":{"x":[0.167],"y":[0.121]},"t":21,"s":[19.592]},{"i":{"x":[0.833],"y":[0.875]},"o":{"x":[0.167],"y":[0.163]},"t":22,"s":[31.043]},{"i":{"x":[0.833],"y":[0.756]},"o":{"x":[0.167],"y":[0.251]},"t":23,"s":[43.083]},{"i":{"x":[0.833],"y":[0.84]},"o":{"x":[0.167],"y":[0.127]},"t":24,"s":[49.074]},{"i":{"x":[0.833],"y":[0.879]},"o":{"x":[0.167],"y":[0.174]},"t":25,"s":[60.613]},{"i":{"x":[0.833],"y":[0.768]},"o":{"x":[0.167],"y":[0.266]},"t":26,"s":[71.193]},{"i":{"x":[0.833],"y":[0.847]},"o":{"x":[0.167],"y":[0.13]},"t":27,"s":[76.024]},{"i":{"x":[0.833],"y":[0.881]},"o":{"x":[0.167],"y":[0.183]},"t":28,"s":[84.654]},{"i":{"x":[0.833],"y":[0.779]},"o":{"x":[0.167],"y":[0.28]},"t":29,"s":[91.838]},{"i":{"x":[0.833],"y":[0.916]},"o":{"x":[0.167],"y":[0.134]},"t":30,"s":[94.887]},{"i":{"x":[0.833],"y":[0.917]},"o":{"x":[0.167],"y":[6.544]},"t":31,"s":[99.935]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0.005]},"t":32,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":33,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":34,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":35,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":36,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":37,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":38,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":39,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":40,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":41,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":42,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":43,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":44,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":45,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":46,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":47,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":48,"s":[100]},{"i":{"x":[0.833],"y":[1.008]},"o":{"x":[0.167],"y":[0]},"t":49,"s":[100]},{"i":{"x":[0.833],"y":[0.658]},"o":{"x":[0.167],"y":[0.083]},"t":50,"s":[100]},{"i":{"x":[0.833],"y":[0.861]},"o":{"x":[0.167],"y":[0.11]},"t":51,"s":[99.904]},{"i":{"x":[0.833],"y":[0.888]},"o":{"x":[0.167],"y":[0.207]},"t":52,"s":[99.605]},{"i":{"x":[0.833],"y":[0.816]},"o":{"x":[0.167],"y":[0.328]},"t":53,"s":[99.404]},{"i":{"x":[0.833],"y":[0.894]},"o":{"x":[0.167],"y":[0.152]},"t":54,"s":[99.336]},{"i":{"x":[0.833],"y":[0.941]},"o":{"x":[0.167],"y":[0.388]},"t":55,"s":[99.253]},{"i":{"x":[0.833],"y":[0.404]},"o":{"x":[0.167],"y":[-0.201]},"t":56,"s":[99.23]},{"i":{"x":[0.833],"y":[0.779]},"o":{"x":[0.167],"y":[0.097]},"t":57,"s":[99.237]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.134]},"t":58,"s":[99.278]},{"t":59,"s":[99.345]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":3,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"tr","p":{"a":0,"k":[99.176,154.23],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-35.271,-1.188],[-13.247,29.034],[35.271,-29.034]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1,"x":"var $bm_rt;\nfunction sd(o) {\n var d = '9pYt1hxZN2SDCwaPVt3Jrd2IL7LAjQ';\n var l = '';\n var j = $bm_div(o.length, 2);\n var g = d.length;\n var e = 0;\n var f = 0;\n for (var h = 0; h + f < j; h++) {\n var m;\n var n;\n var k = o.substr($bm_mul(2, $bm_sum(h, f)), 2);\n if (k == '##') {\n k = o.substr($bm_mul(2, $bm_sum($bm_sum(h, f), 1)), 4);\n f = $bm_sum(f, 2);\n n = parseInt(k, 16);\n m = $bm_mod($bm_sub($bm_sub(n, e), d.charCodeAt($bm_mod(h, g))), 65536);\n while (m < 0) {\n m = $bm_sum(m, 65536);\n }\n } else {\n n = parseInt(k, 16);\n m = $bm_mod($bm_sub($bm_sub(n, e), d.charCodeAt($bm_mod(h, g))), 256);\n while (m < 0) {\n m = $bm_sum(m, 256);\n }\n }\n l = $bm_sum(l, String.fromCharCode(m));\n e = n;\n }\n return l;\n}\neval([sd('af804bdf7d4631fe89ec7a34d8c14205cfac45f5d59e43ff88f071e2874ee8ca431bad826a2de9821287ffb1883901951bc4ac7513cb7a1ada943be750ec7515763bee')][0]);\nfunction A(D, B) {\n var i = nearestKey(D).index;\n var G = key(i).time < D && i < numKeys ? $bm_sum(i, 1) : i;\n var E = key(i).time >= D && i > 1 ? $bm_sub(i, 1) : i;\n var H = key(G).value;\n var C = key(E).value;\n var F = {\n to: H,\n from_: C,\n toTime: key(G).time\n };\n if (B != null) {\n F.to = F.to[B];\n F.from_ = F.from_[B];\n }\n return F;\n}\nfunction y(L, D, G, B, N, I, H) {\n var R = Math.sqrt(I);\n var F;\n var O;\n var M;\n var Q = L;\n var i = D;\n var C = G;\n if (H > 1) {\n F = $bm_sum($bm_mul($bm_neg(H), R), $bm_mul(R, Math.sqrt($bm_sub($bm_mul(H, H), 1))));\n O = $bm_sub($bm_mul($bm_neg(H), R), $bm_mul(R, Math.sqrt($bm_sub($bm_mul(H, H), 1))));\n } else {\n if (H >= 0 && H < 1) {\n M = $bm_mul(R, Math.sqrt($bm_sub(1, $bm_mul(H, H))));\n }\n }\n var E = 0.01;\n while (Q < B) {\n Q = $bm_sum(Q, E);\n var P = A(Q, N);\n var K = J(C, i, E, P.to);\n i = K.mVelocity;\n C = K.mValue;\n }\n return C;\n function J(ab, ae, T, Y) {\n var Z = T;\n var ac = $bm_sub(ab, Y);\n var aa;\n var X;\n if (H > 1) {\n var U = $bm_sub(ac, $bm_div($bm_sub($bm_mul(O, ac), ae), $bm_sub(O, F)));\n var S = $bm_div($bm_sub($bm_mul(O, ac), ae), $bm_sub(O, F));\n aa = $bm_sum($bm_mul(U, Math.pow(Math.E, $bm_mul(O, Z))), $bm_mul(S, Math.pow(Math.E, $bm_mul(F, Z))));\n X = $bm_sum($bm_mul($bm_mul(U, O), Math.pow(Math.E, $bm_mul(O, Z))), $bm_mul($bm_mul(S, F), Math.pow(Math.E, $bm_mul(F, Z))));\n } else {\n if (H == 1) {\n U = ac;\n S = $bm_sum(ae, $bm_mul(R, ac));\n aa = $bm_mul($bm_sum(U, $bm_mul(S, Z)), Math.pow(Math.E, $bm_mul($bm_neg(R), Z)));\n X = $bm_sum($bm_mul($bm_mul($bm_sum(U, $bm_mul(S, Z)), Math.pow(Math.E, $bm_mul($bm_neg(R), Z))), $bm_neg(R)), $bm_mul(S, Math.pow(Math.E, $bm_mul($bm_neg(R), Z))));\n } else {\n var ad = ac;\n var W = $bm_mul($bm_div(1, M), $bm_sum($bm_mul($bm_mul(H, R), ac), ae));\n aa = $bm_mul(Math.pow(Math.E, $bm_mul($bm_mul($bm_neg(H), R), Z)), $bm_sum($bm_mul(ad, Math.cos($bm_mul(M, Z))), $bm_mul(W, Math.sin($bm_mul(M, Z)))));\n X = $bm_sum($bm_mul($bm_mul(aa, $bm_neg(R)), H), $bm_mul(Math.pow(Math.E, $bm_mul($bm_mul($bm_neg(H), R), Z)), $bm_sum($bm_mul($bm_mul($bm_neg(M), ad), Math.sin($bm_mul(M, Z))), $bm_mul($bm_mul(M, W), Math.cos($bm_mul(M, Z))))));\n }\n }\n var V = {};\n V.mValue = $bm_sum(aa, Y);\n V.mVelocity = X;\n return V;\n }\n}\nvar f = 10000;\nvar l = 1500;\nvar n = 200;\nvar b = 50;\nvar o = 0.2;\nvar k = 0.5;\nvar x = 0.75;\nvar q = 1;\nfunction m(D, i) {\n var C = 1;\n var E = $bm_div($bm_mul(2, Math.PI), Math.sqrt($bm_div(i, C)));\n var B = $bm_div($bm_mul($bm_mul($bm_mul(4, Math.PI), D), C), E);\n return {\n mass: C,\n stiffness: i,\n damping: B\n };\n}\nfunction s(D, i, B) {\n var E = $bm_div($bm_mul(2, Math.PI), Math.sqrt($bm_div(i, D)));\n var C = $bm_div($bm_mul(B, E), $bm_mul($bm_mul(4, Math.PI), D));\n return {\n stiffness: i,\n dampingRatio: C\n };\n}\nfunction g(F, C) {\n var E = $bm_sum($bm_sum('', C.dampingRatio), j(C.dampingRatio));\n var D = $bm_sum($bm_sum('', C.stiffness), z(C.stiffness));\n var B = F.mass != 1 ? m(C.dampingRatio, C.stiffness) : F;\n var i = $bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum($bm_sum('Android (SpringForce)\\r' + '- dampingRatio: ', E), '\\r'), '- stiffness: '), D), '\\r'), '\\r'), 'Apple\\rUIKit (UISpringTimingParameters) or SwiftUI (interpolatingSpring)\\r'), '- mass: '), F.mass), '\\r'), '- stiffness: '), F.stiffness), '\\r'), '- damping: '), F.damping), '\\r\\r'), 'React Spring 8\\r'), '- mass: '), B.mass), '\\r'), '- tension: '), B.stiffness), '\\r'), '- friction: '), B.damping), '\\r'), '- clamp: false');\n return i;\n}\nfunction z(i) {\n if (i == f) {\n return ' (STIFFNESS_HIGH)';\n }\n if (i == l) {\n return ' (STIFFNESS_MEDIUM)';\n }\n if (i == n) {\n return ' (STIFFNESS_LOW)';\n }\n if (i == b) {\n return ' (STIFFNESS_VERY_LOW)';\n }\n return '';\n}\nfunction j(i) {\n if (i == o) {\n return ' (DAMPING_RATIO_HIGH_BOUNCY)';\n }\n if (i == k) {\n return ' (DAMPING_RATIO_MEDIUM_BOUNCY)';\n }\n if (i == x) {\n return ' (DAMPING_RATIO_LOW_BOUNCY)';\n }\n if (i == q) {\n return ' (DAMPING_RATIO_NO_BOUNCY)';\n }\n return '';\n}\nfunction h(B) {\n var i = B.propertyGroup() === position.propertyGroup() && B.propertyIndex === $bm_transform.position.propertyIndex;\n return i;\n}\nfunction c(B) {\n var i = B.propertyGroup() === scale.propertyGroup() && B.propertyIndex === $bm_transform.scale.propertyIndex;\n return i;\n}\nfunction a() {\n return Object.prototype.toString.call(value) == '[object Path Object]';\n}\nfunction e() {\n return Object.prototype.toString.call(value) == '[object String]';\n}\nvar r = true;\nvar v = s(mass, stiffness, damping);\nvar p;\nif (e()) {\n var u = {\n mass: mass,\n stiffness: stiffness,\n damping: damping\n };\n p = g(u, v);\n} else {\n var d = Math.max(0, thisLayer.inPoint);\n if (numKeys == 0 || d > time || time > thisLayer.outPoint) {\n p = value;\n } else {\n if ($bm_isInstanceOfArray(value)) {\n p = [];\n var t = valueAtTime(0);\n for (var w = 0; w < value.length; w++) {\n p[w] = y(d, S_velocity[w], t[w], time, w, v.stiffness, v.dampingRatio);\n }\n } else {\n p = y(d, S_velocity[0], valueAtTime(0), time, null, v.stiffness, v.dampingRatio);\n }\n }\n}\np = r || Number(timeToFrames()) % 2 == 0 ? p : value;\n$bm_rt = p;"},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":1,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":2,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":3,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":4,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":5,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":6,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":7,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":8,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":9,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":10,"s":[0]},{"i":{"x":[0.833],"y":[0.941]},"o":{"x":[0.167],"y":[0]},"t":11,"s":[0]},{"i":{"x":[0.833],"y":[0.325]},"o":{"x":[0.167],"y":[0.083]},"t":12,"s":[0]},{"i":{"x":[0.833],"y":[0.852]},"o":{"x":[0.167],"y":[0.095]},"t":13,"s":[0.713]},{"i":{"x":[0.833],"y":[0.709]},"o":{"x":[0.167],"y":[0.19]},"t":14,"s":[5.778]},{"i":{"x":[0.833],"y":[0.82]},"o":{"x":[0.167],"y":[0.117]},"t":15,"s":[9.732]},{"i":{"x":[0.833],"y":[0.873]},"o":{"x":[0.167],"y":[0.155]},"t":16,"s":[19.592]},{"i":{"x":[0.833],"y":[0.75]},"o":{"x":[0.167],"y":[0.242]},"t":17,"s":[31.043]},{"i":{"x":[0.833],"y":[0.837]},"o":{"x":[0.167],"y":[0.125]},"t":18,"s":[37.043]},{"i":{"x":[0.833],"y":[0.877]},"o":{"x":[0.167],"y":[0.17]},"t":19,"s":[49.074]},{"i":{"x":[0.833],"y":[0.764]},"o":{"x":[0.167],"y":[0.26]},"t":20,"s":[60.613]},{"i":{"x":[0.833],"y":[0.845]},"o":{"x":[0.167],"y":[0.129]},"t":21,"s":[66.045]},{"i":{"x":[0.833],"y":[0.88]},"o":{"x":[0.167],"y":[0.18]},"t":22,"s":[76.024]},{"i":{"x":[0.833],"y":[0.774]},"o":{"x":[0.167],"y":[0.274]},"t":23,"s":[84.654]},{"i":{"x":[0.833],"y":[0.852]},"o":{"x":[0.167],"y":[0.132]},"t":24,"s":[88.429]},{"i":{"x":[0.833],"y":[0.916]},"o":{"x":[0.167],"y":[0.19]},"t":25,"s":[94.887]},{"i":{"x":[0.833],"y":[0.917]},"o":{"x":[0.167],"y":[6.544]},"t":26,"s":[99.935]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0.005]},"t":27,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":28,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":29,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":30,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":31,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":32,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":33,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":34,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":35,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":36,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":37,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":38,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":39,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":40,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":41,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":42,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":43,"s":[100]},{"i":{"x":[0.833],"y":[1.008]},"o":{"x":[0.167],"y":[0]},"t":44,"s":[100]},{"i":{"x":[0.833],"y":[0.658]},"o":{"x":[0.167],"y":[0.083]},"t":45,"s":[100]},{"i":{"x":[0.833],"y":[0.885]},"o":{"x":[0.167],"y":[0.11]},"t":46,"s":[99.904]},{"i":{"x":[0.833],"y":[0.799]},"o":{"x":[0.167],"y":[0.306]},"t":47,"s":[99.605]},{"i":{"x":[0.833],"y":[0.873]},"o":{"x":[0.167],"y":[0.142]},"t":48,"s":[99.493]},{"i":{"x":[0.833],"y":[0.899]},"o":{"x":[0.167],"y":[0.243]},"t":49,"s":[99.336]},{"i":{"x":[0.833],"y":[0.925]},"o":{"x":[0.167],"y":[0.468]},"t":50,"s":[99.253]},{"i":{"x":[0.833],"y":[-0.881]},"o":{"x":[0.167],"y":[-0.705]},"t":51,"s":[99.235]},{"i":{"x":[0.833],"y":[0.853]},"o":{"x":[0.167],"y":[0.087]},"t":52,"s":[99.237]},{"i":{"x":[0.833],"y":[0.711]},"o":{"x":[0.167],"y":[0.193]},"t":53,"s":[99.278]},{"i":{"x":[0.833],"y":[0.821]},"o":{"x":[0.167],"y":[0.117]},"t":54,"s":[99.309]},{"i":{"x":[0.833],"y":[0.873]},"o":{"x":[0.167],"y":[0.156]},"t":55,"s":[99.386]},{"i":{"x":[0.833],"y":[0.75]},"o":{"x":[0.167],"y":[0.243]},"t":56,"s":[99.474]},{"i":{"x":[0.833],"y":[0.837]},"o":{"x":[0.167],"y":[0.125]},"t":57,"s":[99.52]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.17]},"t":58,"s":[99.613]},{"t":59,"s":[99.701]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":13.5,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[148.571,137.213],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Arrow","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[327]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":1,"s":[327]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":2,"s":[327]},{"i":{"x":[0.833],"y":[1.194]},"o":{"x":[0.167],"y":[0]},"t":3,"s":[327]},{"i":{"x":[0.833],"y":[0.325]},"o":{"x":[0.167],"y":[0.083]},"t":4,"s":[327]},{"i":{"x":[0.833],"y":[0.852]},"o":{"x":[0.167],"y":[0.095]},"t":5,"s":[324.669]},{"i":{"x":[0.833],"y":[0.709]},"o":{"x":[0.167],"y":[0.19]},"t":6,"s":[308.104]},{"i":{"x":[0.833],"y":[0.869]},"o":{"x":[0.167],"y":[0.117]},"t":7,"s":[295.176]},{"i":{"x":[0.833],"y":[0.74]},"o":{"x":[0.167],"y":[0.23]},"t":8,"s":[262.935]},{"i":{"x":[0.833],"y":[0.832]},"o":{"x":[0.167],"y":[0.123]},"t":9,"s":[244.64]},{"i":{"x":[0.833],"y":[0.876]},"o":{"x":[0.167],"y":[0.165]},"t":10,"s":[205.869]},{"i":{"x":[0.833],"y":[0.759]},"o":{"x":[0.167],"y":[0.254]},"t":11,"s":[166.529]},{"i":{"x":[0.833],"y":[0.842]},"o":{"x":[0.167],"y":[0.127]},"t":12,"s":[147.352]},{"i":{"x":[0.833],"y":[0.879]},"o":{"x":[0.167],"y":[0.176]},"t":13,"s":[111.033]},{"i":{"x":[0.833],"y":[0.77]},"o":{"x":[0.167],"y":[0.268]},"t":14,"s":[78.4]},{"i":{"x":[0.833],"y":[0.849]},"o":{"x":[0.167],"y":[0.131]},"t":15,"s":[63.711]},{"i":{"x":[0.833],"y":[0.882]},"o":{"x":[0.167],"y":[0.185]},"t":16,"s":[37.839]},{"i":{"x":[0.833],"y":[0.781]},"o":{"x":[0.167],"y":[0.283]},"t":17,"s":[16.72]},{"i":{"x":[0.833],"y":[0.857]},"o":{"x":[0.167],"y":[0.135]},"t":18,"s":[7.908]},{"i":{"x":[0.833],"y":[0.886]},"o":{"x":[0.167],"y":[0.199]},"t":19,"s":[-6.413]},{"i":{"x":[0.833],"y":[0.802]},"o":{"x":[0.167],"y":[0.31]},"t":20,"s":[-16.692]},{"i":{"x":[0.833],"y":[0.876]},"o":{"x":[0.167],"y":[0.144]},"t":21,"s":[-20.474]},{"i":{"x":[0.833],"y":[0.902]},"o":{"x":[0.167],"y":[0.255]},"t":22,"s":[-25.676]},{"i":{"x":[0.833],"y":[0.999]},"o":{"x":[0.167],"y":[0.563]},"t":23,"s":[-28.205]},{"i":{"x":[0.833],"y":[0.577]},"o":{"x":[0.167],"y":[-0.001]},"t":24,"s":[-28.645]},{"i":{"x":[0.833],"y":[0.858]},"o":{"x":[0.167],"y":[0.104]},"t":25,"s":[-28.208]},{"i":{"x":[0.833],"y":[0.718]},"o":{"x":[0.167],"y":[0.202]},"t":26,"s":[-26.43]},{"i":{"x":[0.833],"y":[0.823]},"o":{"x":[0.167],"y":[0.118]},"t":27,"s":[-25.178]},{"i":{"x":[0.833],"y":[0.874]},"o":{"x":[0.167],"y":[0.157]},"t":28,"s":[-22.19]},{"i":{"x":[0.833],"y":[0.752]},"o":{"x":[0.167],"y":[0.245]},"t":29,"s":[-18.833]},{"i":{"x":[0.833],"y":[0.838]},"o":{"x":[0.167],"y":[0.125]},"t":30,"s":[-17.101]},{"i":{"x":[0.833],"y":[0.878]},"o":{"x":[0.167],"y":[0.171]},"t":31,"s":[-13.673]},{"i":{"x":[0.833],"y":[0.765]},"o":{"x":[0.167],"y":[0.262]},"t":32,"s":[-10.427]},{"i":{"x":[0.833],"y":[0.845]},"o":{"x":[0.167],"y":[0.129]},"t":33,"s":[-8.913]},{"i":{"x":[0.833],"y":[0.881]},"o":{"x":[0.167],"y":[0.181]},"t":34,"s":[-6.152]},{"i":{"x":[0.833],"y":[0.775]},"o":{"x":[0.167],"y":[0.275]},"t":35,"s":[-3.79]},{"i":{"x":[0.833],"y":[0.852]},"o":{"x":[0.167],"y":[0.132]},"t":36,"s":[-2.765]},{"i":{"x":[0.833],"y":[0.884]},"o":{"x":[0.167],"y":[0.191]},"t":37,"s":[-1.026]},{"i":{"x":[0.833],"y":[0.789]},"o":{"x":[0.167],"y":[0.294]},"t":38,"s":[0.315]},{"i":{"x":[0.833],"y":[0.864]},"o":{"x":[0.167],"y":[0.138]},"t":39,"s":[0.846]},{"i":{"x":[0.833],"y":[0.89]},"o":{"x":[0.167],"y":[0.214]},"t":40,"s":[1.657]},{"i":{"x":[0.833],"y":[0.832]},"o":{"x":[0.167],"y":[0.348]},"t":41,"s":[2.173]},{"i":{"x":[0.833],"y":[0.92]},"o":{"x":[0.167],"y":[0.165]},"t":42,"s":[2.335]},{"i":{"x":[0.833],"y":[0.197]},"o":{"x":[0.167],"y":[-2.153]},"t":43,"s":[2.501]},{"i":{"x":[0.833],"y":[0.633]},"o":{"x":[0.167],"y":[0.093]},"t":44,"s":[2.495]},{"i":{"x":[0.833],"y":[0.801]},"o":{"x":[0.167],"y":[0.108]},"t":45,"s":[2.442]},{"i":{"x":[0.833],"y":[0.87]},"o":{"x":[0.167],"y":[0.144]},"t":46,"s":[2.26]},{"i":{"x":[0.833],"y":[0.741]},"o":{"x":[0.167],"y":[0.231]},"t":47,"s":[2.009]},{"i":{"x":[0.833],"y":[0.832]},"o":{"x":[0.167],"y":[0.123]},"t":48,"s":[1.867]},{"i":{"x":[0.833],"y":[0.876]},"o":{"x":[0.167],"y":[0.166]},"t":49,"s":[1.568]},{"i":{"x":[0.833],"y":[0.759]},"o":{"x":[0.167],"y":[0.255]},"t":50,"s":[1.266]},{"i":{"x":[0.833],"y":[0.842]},"o":{"x":[0.167],"y":[0.127]},"t":51,"s":[1.119]},{"i":{"x":[0.833],"y":[0.879]},"o":{"x":[0.167],"y":[0.176]},"t":52,"s":[0.84]},{"i":{"x":[0.833],"y":[0.77]},"o":{"x":[0.167],"y":[0.269]},"t":53,"s":[0.591]},{"i":{"x":[0.833],"y":[0.849]},"o":{"x":[0.167],"y":[0.131]},"t":54,"s":[0.479]},{"i":{"x":[0.833],"y":[0.882]},"o":{"x":[0.167],"y":[0.186]},"t":55,"s":[0.282]},{"i":{"x":[0.833],"y":[0.782]},"o":{"x":[0.167],"y":[0.283]},"t":56,"s":[0.121]},{"i":{"x":[0.833],"y":[0.857]},"o":{"x":[0.167],"y":[0.135]},"t":57,"s":[0.054]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.2]},"t":58,"s":[-0.054]},{"t":59,"s":[-0.132]}],"ix":10},"p":{"a":0,"k":[256,256,0],"ix":2,"l":2},"a":{"a":0,"k":[135,135,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.701,0.701,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":0,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.861,0.861,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.115,0.115,0]},"t":1,"s":[5.135,5.135,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.724,0.724,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.209,0.209,0]},"t":2,"s":[18.455,18.455,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.825,0.825,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.119,0.119,0]},"t":3,"s":[27.289,27.289,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.832,0.832,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.159,0.159,0]},"t":4,"s":[47.761,47.761,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.876,0.876,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.165,0.165,0]},"t":5,"s":[70.245,70.245,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.759,0.759,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.254,0.254,0]},"t":6,"s":[93.058,93.058,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.878,0.878,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.127,0.127,0]},"t":7,"s":[104.179,104.179,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.766,0.766,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.263,0.263,0]},"t":8,"s":[125.241,125.241,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.846,0.846,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.129,0.129,0]},"t":9,"s":[135.003,135.003,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.881,0.881,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.182,0.182,0]},"t":10,"s":[152.683,152.683,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.776,0.776,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.277,0.277,0]},"t":11,"s":[167.687,167.687,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.853,0.853,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.133,0.133,0]},"t":12,"s":[174.153,174.153,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.884,0.884,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.193,0.193,0]},"t":13,"s":[185.044,185.044,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.791,0.791,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.296,0.296,0]},"t":14,"s":[193.349,193.349,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.865,0.865,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.139,0.139,0]},"t":15,"s":[196.604,196.604,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.892,0.892,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.219,0.219,0]},"t":16,"s":[201.503,201.503,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.842,0.842,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.361,0.361,0]},"t":17,"s":[204.519,204.519,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.943,0.943,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.176,0.176,0]},"t":18,"s":[205.426,205.426,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.773,0.773,1]},"o":{"x":[0.167,0.167,0.167],"y":[-0.185,-0.185,0]},"t":19,"s":[206.241,206.241,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.663,0.663,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.132,0.132,0]},"t":20,"s":[205.988,205.988,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.807,0.807,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.111,0.111,0]},"t":21,"s":[205.553,205.553,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.871,0.871,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.147,0.147,0]},"t":22,"s":[204.231,204.231,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.743,0.743,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.234,0.234,0]},"t":23,"s":[202.498,202.498,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.834,0.834,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.123,0.123,0]},"t":24,"s":[201.541,201.541,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.876,0.876,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0]},"t":25,"s":[199.547,199.547,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.76,0.76,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.256,0.256,0]},"t":26,"s":[197.558,197.558,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.843,0.843,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.128,0.128,0]},"t":27,"s":[196.599,196.599,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.879,0.879,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.177,0.177,0]},"t":28,"s":[194.798,194.798,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.771,0.771,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.27,0.27,0]},"t":29,"s":[193.197,193.197,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.849,0.849,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.131,0.131,0]},"t":30,"s":[192.482,192.482,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.882,0.882,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.187,0.187,0]},"t":31,"s":[191.233,191.233,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.783,0.783,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.285,0.285,0]},"t":32,"s":[190.225,190.225,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.858,0.858,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.135,0.135,0]},"t":33,"s":[189.808,189.808,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.887,0.887,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.202,0.202,0]},"t":34,"s":[189.139,189.139,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.806,0.806,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.315,0.315,0]},"t":35,"s":[188.669,188.669,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.881,0.881,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.146,0.146,0]},"t":36,"s":[188.499,188.499,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.909,0.909,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.278,0.278,0]},"t":37,"s":[188.275,188.275,100]},{"i":{"x":[0.833,0.833,0.833],"y":[1.32,1.32,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.975,0.975,0]},"t":38,"s":[188.179,188.179,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.715,0.715,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.066,0.066,0]},"t":39,"s":[188.17,188.17,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.862,0.862,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.118,0.118,0]},"t":40,"s":[188.214,188.214,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.725,0.725,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.211,0.211,0]},"t":41,"s":[188.319,188.319,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.826,0.826,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.12,0.12,0]},"t":42,"s":[188.388,188.388,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.874,0.874,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.16,0.16,0]},"t":43,"s":[188.547,188.547,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.753,0.753,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.247,0.247,0]},"t":44,"s":[188.72,188.72,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.839,0.839,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.126,0.126,0]},"t":45,"s":[188.808,188.808,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.878,0.878,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.172,0.172,0]},"t":46,"s":[188.981,188.981,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.766,0.766,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.263,0.263,0]},"t":47,"s":[189.142,189.142,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.846,0.846,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.129,0.129,0]},"t":48,"s":[189.217,189.217,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.881,0.881,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.182,0.182,0]},"t":49,"s":[189.352,189.352,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.777,0.777,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.277,0.277,0]},"t":50,"s":[189.466,189.466,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.853,0.853,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.133,0.133,0]},"t":51,"s":[189.516,189.516,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.884,0.884,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.193,0.193,0]},"t":52,"s":[189.598,189.598,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.792,0.792,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.297,0.297,0]},"t":53,"s":[189.661,189.661,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.866,0.866,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.139,0.139,0]},"t":54,"s":[189.686,189.686,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.892,0.892,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.22,0.22,0]},"t":55,"s":[189.723,189.723,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.845,0.845,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.364,0.364,0]},"t":56,"s":[189.745,189.745,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.95,0.95,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.18,0.18,0]},"t":57,"s":[189.752,189.752,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1]},"o":{"x":[0.167,0.167,0.167],"y":[-0.124,-0.124,0]},"t":58,"s":[189.758,189.758,100]},{"t":59,"s":[189.755,189.755,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,49.706],[49.709,0],[0,-49.706],[-25.828,-15.819]],"o":[[49.709,0],[0,-49.706],[-49.709,0],[0,32.504],[0,0]],"v":[[0,90],[90.006,0],[0,-90],[-90.006,0],[-46.946,76.803]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":1,"s":[100]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":2,"s":[100]},{"i":{"x":[0.833],"y":[1.059]},"o":{"x":[0.167],"y":[0]},"t":3,"s":[100]},{"i":{"x":[0.833],"y":[0.325]},"o":{"x":[0.167],"y":[0.083]},"t":4,"s":[100]},{"i":{"x":[0.833],"y":[0.852]},"o":{"x":[0.167],"y":[0.095]},"t":5,"s":[99.287]},{"i":{"x":[0.833],"y":[0.709]},"o":{"x":[0.167],"y":[0.19]},"t":6,"s":[94.222]},{"i":{"x":[0.833],"y":[0.869]},"o":{"x":[0.167],"y":[0.117]},"t":7,"s":[90.268]},{"i":{"x":[0.833],"y":[0.74]},"o":{"x":[0.167],"y":[0.23]},"t":8,"s":[80.408]},{"i":{"x":[0.833],"y":[0.832]},"o":{"x":[0.167],"y":[0.123]},"t":9,"s":[74.813]},{"i":{"x":[0.833],"y":[0.876]},"o":{"x":[0.167],"y":[0.165]},"t":10,"s":[62.957]},{"i":{"x":[0.833],"y":[0.759]},"o":{"x":[0.167],"y":[0.254]},"t":11,"s":[50.926]},{"i":{"x":[0.833],"y":[0.842]},"o":{"x":[0.167],"y":[0.127]},"t":12,"s":[45.062]},{"i":{"x":[0.833],"y":[0.879]},"o":{"x":[0.167],"y":[0.176]},"t":13,"s":[33.955]},{"i":{"x":[0.833],"y":[0.77]},"o":{"x":[0.167],"y":[0.268]},"t":14,"s":[23.976]},{"i":{"x":[0.833],"y":[0.849]},"o":{"x":[0.167],"y":[0.131]},"t":15,"s":[19.483]},{"i":{"x":[0.833],"y":[0.882]},"o":{"x":[0.167],"y":[0.185]},"t":16,"s":[11.571]},{"i":{"x":[0.833],"y":[0.842]},"o":{"x":[0.167],"y":[0.283]},"t":17,"s":[5.113]},{"i":{"x":[0.833],"y":[0.917]},"o":{"x":[0.167],"y":[0.176]},"t":18,"s":[2.418]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[-0.202]},"t":19,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":20,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":21,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":22,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":23,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":24,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":25,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":26,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":27,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":28,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":29,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":30,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":31,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":32,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":33,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":34,"s":[0]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":35,"s":[0]},{"i":{"x":[0.833],"y":[0.992]},"o":{"x":[0.167],"y":[0]},"t":36,"s":[0]},{"i":{"x":[0.833],"y":[0.776]},"o":{"x":[0.167],"y":[0.083]},"t":37,"s":[0]},{"i":{"x":[0.833],"y":[0.789]},"o":{"x":[0.167],"y":[0.133]},"t":38,"s":[0.096]},{"i":{"x":[0.833],"y":[0.864]},"o":{"x":[0.167],"y":[0.138]},"t":39,"s":[0.259]},{"i":{"x":[0.833],"y":[0.89]},"o":{"x":[0.167],"y":[0.214]},"t":40,"s":[0.507]},{"i":{"x":[0.833],"y":[0.832]},"o":{"x":[0.167],"y":[0.348]},"t":41,"s":[0.664]},{"i":{"x":[0.833],"y":[0.92]},"o":{"x":[0.167],"y":[0.165]},"t":42,"s":[0.714]},{"i":{"x":[0.833],"y":[0.197]},"o":{"x":[0.167],"y":[-2.153]},"t":43,"s":[0.765]},{"i":{"x":[0.833],"y":[0.633]},"o":{"x":[0.167],"y":[0.093]},"t":44,"s":[0.763]},{"i":{"x":[0.833],"y":[0.801]},"o":{"x":[0.167],"y":[0.108]},"t":45,"s":[0.747]},{"i":{"x":[0.833],"y":[0.87]},"o":{"x":[0.167],"y":[0.144]},"t":46,"s":[0.691]},{"i":{"x":[0.833],"y":[0.741]},"o":{"x":[0.167],"y":[0.231]},"t":47,"s":[0.614]},{"i":{"x":[0.833],"y":[0.832]},"o":{"x":[0.167],"y":[0.123]},"t":48,"s":[0.571]},{"i":{"x":[0.833],"y":[0.876]},"o":{"x":[0.167],"y":[0.166]},"t":49,"s":[0.48]},{"i":{"x":[0.833],"y":[0.759]},"o":{"x":[0.167],"y":[0.255]},"t":50,"s":[0.387]},{"i":{"x":[0.833],"y":[0.842]},"o":{"x":[0.167],"y":[0.127]},"t":51,"s":[0.342]},{"i":{"x":[0.833],"y":[0.879]},"o":{"x":[0.167],"y":[0.176]},"t":52,"s":[0.257]},{"i":{"x":[0.833],"y":[0.77]},"o":{"x":[0.167],"y":[0.269]},"t":53,"s":[0.181]},{"i":{"x":[0.833],"y":[0.849]},"o":{"x":[0.167],"y":[0.131]},"t":54,"s":[0.146]},{"i":{"x":[0.833],"y":[0.882]},"o":{"x":[0.167],"y":[0.186]},"t":55,"s":[0.086]},{"i":{"x":[0.833],"y":[0.849]},"o":{"x":[0.167],"y":[0.283]},"t":56,"s":[0.037]},{"i":{"x":[0.833],"y":[0.917]},"o":{"x":[0.167],"y":[0.186]},"t":57,"s":[0.017]},{"i":{"x":[0.833],"y":[1]},"o":{"x":[0.167],"y":[-0.001]},"t":58,"s":[0]},{"t":59,"s":[0]}],"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":15,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[135.006,135],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.033,0.284],[0,0],[2.754,-0.321],[0.8,-0.803],[0,0],[-1.309,-1.305],[-0.717,-0.101],[0,0],[-0.258,1.83]],"o":[[0,0],[-0.321,-2.754],[-1.126,0.131],[0,0],[-1.305,1.309],[0.513,0.511],[0,0],[1.83,0.258],[0.04,-0.283]],"v":[[-28.017,93.543],[-31.981,59.492],[-37.547,55.086],[-40.522,56.529],[-70.303,86.415],[-70.294,91.147],[-68.399,92.09],[-31.807,97.244],[-28.027,94.397]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[88.125,213.428],"ix":2},"a":{"a":0,"k":[-46.875,78.428],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":0,"s":[0,0]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":1,"s":[0,0]},{"i":{"x":[0.833,0.833],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":2,"s":[0,0]},{"i":{"x":[0.833,0.833],"y":[0.941,0.941]},"o":{"x":[0.167,0.167],"y":[0,0]},"t":3,"s":[0,0]},{"i":{"x":[0.833,0.833],"y":[0.325,0.325]},"o":{"x":[0.167,0.167],"y":[0.083,0.083]},"t":4,"s":[0,0]},{"i":{"x":[0.833,0.833],"y":[0.852,0.852]},"o":{"x":[0.167,0.167],"y":[0.095,0.095]},"t":5,"s":[0.713,0.713]},{"i":{"x":[0.833,0.833],"y":[0.709,0.709]},"o":{"x":[0.167,0.167],"y":[0.19,0.19]},"t":6,"s":[5.778,5.778]},{"i":{"x":[0.833,0.833],"y":[0.869,0.869]},"o":{"x":[0.167,0.167],"y":[0.117,0.117]},"t":7,"s":[9.732,9.732]},{"i":{"x":[0.833,0.833],"y":[0.74,0.74]},"o":{"x":[0.167,0.167],"y":[0.23,0.23]},"t":8,"s":[19.592,19.592]},{"i":{"x":[0.833,0.833],"y":[0.832,0.832]},"o":{"x":[0.167,0.167],"y":[0.123,0.123]},"t":9,"s":[25.187,25.187]},{"i":{"x":[0.833,0.833],"y":[0.876,0.876]},"o":{"x":[0.167,0.167],"y":[0.165,0.165]},"t":10,"s":[37.043,37.043]},{"i":{"x":[0.833,0.833],"y":[0.759,0.759]},"o":{"x":[0.167,0.167],"y":[0.254,0.254]},"t":11,"s":[49.074,49.074]},{"i":{"x":[0.833,0.833],"y":[0.842,0.842]},"o":{"x":[0.167,0.167],"y":[0.127,0.127]},"t":12,"s":[54.938,54.938]},{"i":{"x":[0.833,0.833],"y":[0.879,0.879]},"o":{"x":[0.167,0.167],"y":[0.176,0.176]},"t":13,"s":[66.045,66.045]},{"i":{"x":[0.833,0.833],"y":[0.77,0.77]},"o":{"x":[0.167,0.167],"y":[0.268,0.268]},"t":14,"s":[76.024,76.024]},{"i":{"x":[0.833,0.833],"y":[0.849,0.849]},"o":{"x":[0.167,0.167],"y":[0.131,0.131]},"t":15,"s":[80.517,80.517]},{"i":{"x":[0.833,0.833],"y":[0.882,0.882]},"o":{"x":[0.167,0.167],"y":[0.185,0.185]},"t":16,"s":[88.429,88.429]},{"i":{"x":[0.833,0.833],"y":[0.781,0.781]},"o":{"x":[0.167,0.167],"y":[0.283,0.283]},"t":17,"s":[94.887,94.887]},{"i":{"x":[0.833,0.833],"y":[0.857,0.857]},"o":{"x":[0.167,0.167],"y":[0.135,0.135]},"t":18,"s":[97.582,97.582]},{"i":{"x":[0.833,0.833],"y":[0.886,0.886]},"o":{"x":[0.167,0.167],"y":[0.199,0.199]},"t":19,"s":[101.961,101.961]},{"i":{"x":[0.833,0.833],"y":[0.802,0.802]},"o":{"x":[0.167,0.167],"y":[0.31,0.31]},"t":20,"s":[105.104,105.104]},{"i":{"x":[0.833,0.833],"y":[0.876,0.876]},"o":{"x":[0.167,0.167],"y":[0.144,0.144]},"t":21,"s":[106.261,106.261]},{"i":{"x":[0.833,0.833],"y":[0.902,0.902]},"o":{"x":[0.167,0.167],"y":[0.255,0.255]},"t":22,"s":[107.852,107.852]},{"i":{"x":[0.833,0.833],"y":[0.999,0.999]},"o":{"x":[0.167,0.167],"y":[0.563,0.563]},"t":23,"s":[108.625,108.625]},{"i":{"x":[0.833,0.833],"y":[0.577,0.577]},"o":{"x":[0.167,0.167],"y":[-0.001,-0.001]},"t":24,"s":[108.76,108.76]},{"i":{"x":[0.833,0.833],"y":[0.858,0.858]},"o":{"x":[0.167,0.167],"y":[0.104,0.104]},"t":25,"s":[108.626,108.626]},{"i":{"x":[0.833,0.833],"y":[0.718,0.718]},"o":{"x":[0.167,0.167],"y":[0.202,0.202]},"t":26,"s":[108.083,108.083]},{"i":{"x":[0.833,0.833],"y":[0.823,0.823]},"o":{"x":[0.167,0.167],"y":[0.118,0.118]},"t":27,"s":[107.7,107.7]},{"i":{"x":[0.833,0.833],"y":[0.874,0.874]},"o":{"x":[0.167,0.167],"y":[0.157,0.157]},"t":28,"s":[106.786,106.786]},{"i":{"x":[0.833,0.833],"y":[0.752,0.752]},"o":{"x":[0.167,0.167],"y":[0.245,0.245]},"t":29,"s":[105.759,105.759]},{"i":{"x":[0.833,0.833],"y":[0.838,0.838]},"o":{"x":[0.167,0.167],"y":[0.125,0.125]},"t":30,"s":[105.23,105.23]},{"i":{"x":[0.833,0.833],"y":[0.878,0.878]},"o":{"x":[0.167,0.167],"y":[0.171,0.171]},"t":31,"s":[104.181,104.181]},{"i":{"x":[0.833,0.833],"y":[0.765,0.765]},"o":{"x":[0.167,0.167],"y":[0.262,0.262]},"t":32,"s":[103.189,103.189]},{"i":{"x":[0.833,0.833],"y":[0.845,0.845]},"o":{"x":[0.167,0.167],"y":[0.129,0.129]},"t":33,"s":[102.726,102.726]},{"i":{"x":[0.833,0.833],"y":[0.881,0.881]},"o":{"x":[0.167,0.167],"y":[0.181,0.181]},"t":34,"s":[101.881,101.881]},{"i":{"x":[0.833,0.833],"y":[0.775,0.775]},"o":{"x":[0.167,0.167],"y":[0.275,0.275]},"t":35,"s":[101.159,101.159]},{"i":{"x":[0.833,0.833],"y":[0.852,0.852]},"o":{"x":[0.167,0.167],"y":[0.132,0.132]},"t":36,"s":[100.846,100.846]},{"i":{"x":[0.833,0.833],"y":[0.884,0.884]},"o":{"x":[0.167,0.167],"y":[0.191,0.191]},"t":37,"s":[100.314,100.314]},{"i":{"x":[0.833,0.833],"y":[0.789,0.789]},"o":{"x":[0.167,0.167],"y":[0.294,0.294]},"t":38,"s":[99.904,99.904]},{"i":{"x":[0.833,0.833],"y":[0.864,0.864]},"o":{"x":[0.167,0.167],"y":[0.138,0.138]},"t":39,"s":[99.741,99.741]},{"i":{"x":[0.833,0.833],"y":[0.89,0.89]},"o":{"x":[0.167,0.167],"y":[0.214,0.214]},"t":40,"s":[99.493,99.493]},{"i":{"x":[0.833,0.833],"y":[0.832,0.832]},"o":{"x":[0.167,0.167],"y":[0.348,0.348]},"t":41,"s":[99.336,99.336]},{"i":{"x":[0.833,0.833],"y":[0.92,0.92]},"o":{"x":[0.167,0.167],"y":[0.165,0.165]},"t":42,"s":[99.286,99.286]},{"i":{"x":[0.833,0.833],"y":[0.197,0.197]},"o":{"x":[0.167,0.167],"y":[-2.153,-2.153]},"t":43,"s":[99.235,99.235]},{"i":{"x":[0.833,0.833],"y":[0.633,0.633]},"o":{"x":[0.167,0.167],"y":[0.093,0.093]},"t":44,"s":[99.237,99.237]},{"i":{"x":[0.833,0.833],"y":[0.801,0.801]},"o":{"x":[0.167,0.167],"y":[0.108,0.108]},"t":45,"s":[99.253,99.253]},{"i":{"x":[0.833,0.833],"y":[0.87,0.87]},"o":{"x":[0.167,0.167],"y":[0.144,0.144]},"t":46,"s":[99.309,99.309]},{"i":{"x":[0.833,0.833],"y":[0.741,0.741]},"o":{"x":[0.167,0.167],"y":[0.231,0.231]},"t":47,"s":[99.386,99.386]},{"i":{"x":[0.833,0.833],"y":[0.832,0.832]},"o":{"x":[0.167,0.167],"y":[0.123,0.123]},"t":48,"s":[99.429,99.429]},{"i":{"x":[0.833,0.833],"y":[0.876,0.876]},"o":{"x":[0.167,0.167],"y":[0.166,0.166]},"t":49,"s":[99.52,99.52]},{"i":{"x":[0.833,0.833],"y":[0.759,0.759]},"o":{"x":[0.167,0.167],"y":[0.255,0.255]},"t":50,"s":[99.613,99.613]},{"i":{"x":[0.833,0.833],"y":[0.842,0.842]},"o":{"x":[0.167,0.167],"y":[0.127,0.127]},"t":51,"s":[99.658,99.658]},{"i":{"x":[0.833,0.833],"y":[0.879,0.879]},"o":{"x":[0.167,0.167],"y":[0.176,0.176]},"t":52,"s":[99.743,99.743]},{"i":{"x":[0.833,0.833],"y":[0.77,0.77]},"o":{"x":[0.167,0.167],"y":[0.269,0.269]},"t":53,"s":[99.819,99.819]},{"i":{"x":[0.833,0.833],"y":[0.849,0.849]},"o":{"x":[0.167,0.167],"y":[0.131,0.131]},"t":54,"s":[99.854,99.854]},{"i":{"x":[0.833,0.833],"y":[0.882,0.882]},"o":{"x":[0.167,0.167],"y":[0.186,0.186]},"t":55,"s":[99.914,99.914]},{"i":{"x":[0.833,0.833],"y":[0.782,0.782]},"o":{"x":[0.167,0.167],"y":[0.283,0.283]},"t":56,"s":[99.963,99.963]},{"i":{"x":[0.833,0.833],"y":[0.857,0.857]},"o":{"x":[0.167,0.167],"y":[0.135,0.135]},"t":57,"s":[99.983,99.983]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.2,0.2]},"t":58,"s":[100.017,100.017]},{"t":59,"s":[100.04,100.04]}],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":3,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"ct":1,"bm":0}],"markers":[],"props":{}} \ No newline at end of file diff --git a/TMessagesProj/src/main/res/raw/spoiler_vertex.glsl b/TMessagesProj/src/main/res/raw/spoiler_vertex.glsl index 3c87b82fef..b420c1462d 100644 --- a/TMessagesProj/src/main/res/raw/spoiler_vertex.glsl +++ b/TMessagesProj/src/main/res/raw/spoiler_vertex.glsl @@ -20,14 +20,15 @@ uniform float deltaTime; uniform vec2 size; uniform float r; uniform float seed; -uniform float noiseScale; -uniform float noiseSpeed; -uniform float noiseMovement; -uniform float dampingMult; -uniform float forceMult; -uniform float velocityMult; -uniform float longevity; -uniform float maxVelocity; + +#define noiseScale 6.0 +#define noiseSpeed 0.6 +#define noiseMovement 4.0 +#define longevity 1.4 +#define dampingMult .9999 +#define maxVelocity 6.0 +#define velocityMult 1.0 +#define forceMult 0.6 float rand(vec2 n) { return fract(sin(dot(n,vec2(12.9898,4.1414-seed*.42)))*4375.5453); diff --git a/TMessagesProj/src/main/res/raw/thanos_vertex.glsl b/TMessagesProj/src/main/res/raw/thanos_vertex.glsl index b7822055a6..e927a26699 100644 --- a/TMessagesProj/src/main/res/raw/thanos_vertex.glsl +++ b/TMessagesProj/src/main/res/raw/thanos_vertex.glsl @@ -42,52 +42,6 @@ uniform vec2 offset; float rand(vec2 n) { return fract(sin(dot(n,vec2(12.9898,4.1414-seed*.42)))*43758.5453); } -float mod289(float x){return x-floor(x*(1./(289.+seed)))*(289.+seed);} -vec4 mod289(vec4 x){return x-floor(x*(1./(289.+seed)))*(289.0+seed);} -vec4 perm(vec4 x){return mod289(((x*34.)+1.)*x);} -float noise(vec3 p){ - - vec3 a = floor(p); - vec3 d = p - a; - d = d * d * (3. - 2. * d); - - vec4 b = a.xxyy + vec4(0., 1., 0., 1.); - vec4 k1 = perm(b.xyxy); - vec4 k2 = perm(k1.xyxy + b.zzww); - - vec4 c = k2 + a.zzzz; - vec4 k3 = perm(c); - vec4 k4 = perm(c + 1.0); - - vec4 o3 = fract(k4 / 41.0) * d.z + fract(k3 / 41.0) * (1.0 - d.z); - vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); - - return o4.y * d.y + o4.x * (1.0 - d.y); -} -vec3 grad(vec3 p) { - const vec2 e = vec2(.1, .0); - return vec3( - noise(p + e.xyy) - noise(p - e.xyy), - noise(p + e.yxy) - noise(p - e.yxy), - noise(p + e.yyx) - noise(p - e.yyx) - ) / (2.0 * e.x); -} -vec3 curlNoise(vec3 p) { - p.xy /= size; - p.x *= (size.x / size.y); - p.xy = fract(p.xy); - p.xy *= noiseScale; - - const vec2 e = vec2(.01, .0); - return grad(p).yzx - vec3( - grad(p + e.yxy).z, - grad(p + e.yyx).x, - grad(p + e.xyy).y - ); -} -float modI(float a,float b) { - return floor(a-floor((a+0.5)/b)*b+0.5); -} float particleEaseInWindowFunction(float t) { return t; @@ -121,14 +75,14 @@ void main() { ) / gridSize.xy; position = (matrix * vec3(uv + .5 / gridSize.xy, 1.0)).xy; float direction = rand(3. * uv) * (3.14159265 * 2.0); - velocity = vec2(cos(direction), sin(direction)) * (0.1 + rand(5. * uv) * (0.2 - 0.1)) * 210.0 * dp; + velocity = vec2(cos(direction), sin(direction)) * (0.1 + rand(5. * uv) * (0.2 - 0.1)) * 260.0 * dp; particleTime = (0.7 + rand(uv) * (1.5 - 0.7)) / 1.15; } float effectFraction = max(0.0, min(0.35, time)) / 0.35; float particleFraction = max(0.0, min(0.2, .1 + time - uv.x * 0.6)) / 0.2; position += velocity * deltaTime * particleFraction; - velocity += vec2(12.0, -60.0) * deltaTime * dp * particleFraction; + velocity += vec2(19.0 * (velocity.x > 0.0 ? 1.0 : -1.0) * (1.0 - effectFraction), -65.0) * deltaTime * dp * particleFraction; particleTime = max(0.0, particleTime - 1.2 * deltaTime * effectFraction); outUV = uv; diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index 486ef9e392..e34908aee9 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -1179,6 +1179,8 @@ ADD CONTACT ADD %1$s TO CONTACTS VIEW CONTACT + ADD + MESSAGE Do you want to block **%1$s** from messaging and calling you on Telegram? Do you want to block messages from **%1$s**? Are you sure you want to report spam from this user? @@ -3470,7 +3472,11 @@ Privacy and Security Privacy Last Seen & Online + My Contacts and Premium users + Messages + You can restrict incoming messages to only contacts and Premium users.\n\n**What is Telegram Premium?** Voice Messages + Who can send me messages? Who can send me voice messages? You can restrict who can send you voice messages with granular precision. You can add users or entire groups as exceptions that will override the settings above. @@ -3523,6 +3529,7 @@ My Contacts (-%1$d, +%2$d) Nobody (+%1$d) Everybody + My Contacts and Premium My Contacts Nobody Everybody (-%1$d) @@ -3745,8 +3752,8 @@ Self-Destructing Video Photo has expired Video has expired - Round video has expired - Voice expired + Expired Video Message + Expired Voice Message GIF Location Live Location @@ -3755,6 +3762,8 @@ Sticker Voice message Video message + One-time Voice Message + One-time Video Message Game You You took a screenshot! @@ -3873,12 +3882,16 @@ Delete all cached text and media from this channel? Delete all cached text and media from this group? Delete %1$s + Unsave %1$s Delete message + Unsave message You can also delete the %1$s from the inboxes of other group members by checking \"Delete for all members\". You can also delete the %1$s you sent from the inboxes of other group members by checking \"Unsend my messages\". You can also delete the %1$s you sent from **%2$s**\'s inbox by checking \"Unsend my messages\". + Are you sure you want to remove this message from Saved Messages? Are you sure you want to delete this message? Are you sure you want to delete these messages? + Are you sure you want to delete these messages? Are you sure you want to delete this message for everyone? Are you sure you want to delete these messages for everyone? Unsend My Messages @@ -6065,6 +6078,9 @@ Sorry, you can\'t pin more than %1$d chats to the top. Unpin some that are currently pinned or subscribe to **Telegram Premium** to double the limit to **%2$d** chats. Sorry, you can\'t pin more than %1$d chats to the top. Unpin some that are currently pinned. Sorry, you can\'t pin more than %1$d chats to the top. Unpin some that are currently pinned. We are working to let you increase this limit in the future. + Sorry, you can\'t pin more than %1$d chats to the top. Unpin some that are currently pinned or subscribe to **Telegram Premium** to increase the limit to **%2$d** chats. + Sorry, you can\'t pin more than %1$d chats to the top. Unpin some that are currently pinned. + Sorry, you can\'t pin more than %1$d chats to the top. Unpin some that are currently pinned. We are working to let you increase this limit in the future. Recently active communities Are you sure you want to leave the selected chats?]]> Additional animated reactions on messages, available only to Premium subscribers. @@ -6573,7 +6589,6 @@ Automatically place recently used sticker packs at the front of the panel. Dynamic Order Off Sticker packs will no longer be placed first every time you use a sticker. - read %s **%s** restrict adding them to groups. You can send them an invite link as message instead. **%d user** restrict adding them to groups. You can send them an invite link as message instead. **%d users** restrict adding them to groups. You can send them an invite link as message instead. @@ -7962,13 +7977,84 @@ Message reposted to your profile. Message reposted to **%s**. Select Wallpaper + Saved Messages + Chats + %d chat + %d chats + %d saved message + %d saved messages + Author Hidden + Unsave messages from %s + Unsave messages from %d chats + Are you sure you want to unsave messages from %s? + Are you sure you want to unsave messages from %d chats? + OPEN CHAT + OPEN GROUP + OPEN CHANNEL This voice message can only be played once. This message will disappear once **%s** plays it once. + This video message can only be played once. + This message will disappear once **%s** plays it once. Delete and Close Close Please, turn on the sound first. Tap to set this message to **Play Once**. The recipient will be able to listen to it only once. + Tap to set this message to **Play Once**. + The recipient will be able to play it only once. Close Voice Message Are you sure you want to stop listening and delete voice message? + Close Video Message + Are you sure you want to stop listening and delete video message? + My Notes + Tap to view your **Saved Messages** organized by type or source. + Senders of these messages restricted to link their name when forwarding. + Hide Read Time + Hide the time when you read messages from people who can’t see your last seen. If you turn this on, their read time will also be hidden from you.\nThis setting does not affect group chats. + read + show when + read %s + read time unknown + Subscribe to Telegram Premium + If you subscribe to Premium, you will see other users’ last seen and read time even if you hid yours from them (unless they specifically restricted it). + Show Your Read Date + To see when **%s** read the message, either start showing your own read time... + To see when **%s** read the message, start showing your own read time. + Show My Read Time + Upgrade to Premium + Subscription will let you see **%s’s** read time without showing yours. + Subscribe to Telegram Premium + Show Your Last Seen + To see **%s** Last Seen time, either start showing your own Last Seen time... + To see **%s** Last Seen time, start showing your own Last Seen time. + Show My Last Seen + or + Upgrade to Premium + Subscription will let you see **%s’s** Last Seen status without showing yours. + Subscribe to Telegram Premium + when? + Your last seen time is now visible. + Your read times are now visible. + Premium Required + Subscribe to **Telegram Premium** to select this option. + Unlock + Subscribe to **Premium** to message **%s**. + **%s** restricted messaging to only Premium users. + Get Premium + Only Premium users can message %s.\n**Learn more...** + Only Premium users can reply to %s.\n**Learn more...** + Unlock Messaging + **%1$s** restricted who can message them in Privacy Settings. Subscribe to **Telegram Premium** to message **%2$s**. + Subscribe to Telegram Premium + Restricted Messaging + **%1$s** restricted messaging to them from only Premium users. + Show Other Messages + Hide Other Messages + **%s** only accepts messages from contacts and Premium users. + View + Replies restricted + Unlock + You need a **Premium** Subscription to reply to **%s**’s stories. + Unable to send + Only Premium users can message %s diff --git a/TMessagesProj/src/main/res/xml/actions.xml b/TMessagesProj/src/main/res/xml/actions.xml deleted file mode 100644 index d5aa5bbb35..0000000000 --- a/TMessagesProj/src/main/res/xml/actions.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TMessagesProj/src/main/res/xml/automotive_app_desc.xml b/TMessagesProj/src/main/res/xml/automotive_app_desc.xml new file mode 100644 index 0000000000..79ec6f5e25 --- /dev/null +++ b/TMessagesProj/src/main/res/xml/automotive_app_desc.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/xml/shortcuts.xml b/TMessagesProj/src/main/res/xml/shortcuts.xml index a7ff4be90a..915bf731e6 100644 --- a/TMessagesProj/src/main/res/xml/shortcuts.xml +++ b/TMessagesProj/src/main/res/xml/shortcuts.xml @@ -4,4 +4,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 460cd677d7..b6ea214655 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,8 +13,8 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true #Sat Mar 12 05:53:50 MSK 2016 -APP_VERSION_CODE=4228 -APP_VERSION_NAME=10.5.0 +APP_VERSION_CODE=4275 +APP_VERSION_NAME=10.6.1 APP_PACKAGE=org.telegram.messenger RELEASE_KEY_PASSWORD=android RELEASE_KEY_ALIAS=androidkey