From 59a0bc1af3985a4efa340b430dad867ae0e8d073 Mon Sep 17 00:00:00 2001 From: dkaraush Date: Sun, 30 Jun 2024 18:27:03 +0400 Subject: [PATCH] update to 10.14.2 (4911) --- TMessagesProj/jni/TgNetWrapper.cpp | 8 +- .../jni/tgnet/ConnectionsManager.cpp | 7 +- TMessagesProj/jni/tgnet/Defines.h | 2 +- .../telegram/messenger/AndroidUtilities.java | 131 +- .../telegram/messenger/BillingController.java | 142 +- .../ChatMessagesMetadataController.java | 2 +- .../messenger/DownloadController.java | 92 + .../telegram/messenger/FileRefController.java | 145 +- .../org/telegram/messenger/ImageLoader.java | 142 +- .../org/telegram/messenger/ImageLocation.java | 4 +- .../messenger/LocationController.java | 131 +- .../telegram/messenger/MediaController.java | 49 +- .../messenger/MediaDataController.java | 31 +- .../org/telegram/messenger/MessageObject.java | 219 ++- .../messenger/MessagesController.java | 72 +- .../telegram/messenger/MessagesStorage.java | 13 +- .../messenger/NotificationCenter.java | 3 + .../messenger/NotificationsController.java | 25 + .../messenger/PushListenerController.java | 33 + .../messenger/SendMessagesHelper.java | 623 ++++-- .../org/telegram/messenger/SharedConfig.java | 13 +- .../messenger/TranslateController.java | 10 + .../telegram/messenger/VideoEditedInfo.java | 60 +- .../messenger/utils/BillingUtilities.java | 3 +- .../messenger/video/TextureRenderer.java | 24 +- .../telegram/messenger/voip/VoIPService.java | 13 + .../telegram/tgnet/ConnectionsManager.java | 14 +- .../main/java/org/telegram/tgnet/TLRPC.java | 660 ++++++- .../org/telegram/tgnet/tl/TL_stories.java | 372 +++- .../org/telegram/ui/ActionBar/ActionBar.java | 4 + .../ui/ActionBar/ActionBarLayout.java | 342 +++- .../telegram/ui/ActionBar/BaseFragment.java | 192 +- .../telegram/ui/ActionBar/BottomSheet.java | 5 +- .../ui/ActionBar/BottomSheetTabs.java | 692 +++++++ .../ui/ActionBar/BottomSheetTabsOverlay.java | 1214 ++++++++++++ .../ui/ActionBar/DrawerLayoutContainer.java | 2 +- .../ui/ActionBar/INavigationLayout.java | 17 +- .../ui/Adapters/BaseLocationAdapter.java | 114 +- .../ui/Adapters/LocationActivityAdapter.java | 67 +- .../ui/Adapters/MessagesSearchAdapter.java | 181 +- .../java/org/telegram/ui/ArticleViewer.java | 105 +- .../main/java/org/telegram/ui/AvatarSpan.java | 16 + .../java/org/telegram/ui/BoostsActivity.java | 2 +- .../java/org/telegram/ui/BubbleActivity.java | 2 +- .../ui/Business/BusinessIntroActivity.java | 4 +- .../org/telegram/ui/CameraScanActivity.java | 2 +- .../java/org/telegram/ui/Cells/BaseCell.java | 1 + .../telegram/ui/Cells/ChatMessageCell.java | 360 +++- .../org/telegram/ui/Cells/DialogCell.java | 118 +- .../telegram/ui/Cells/DialogsHintCell.java | 6 +- .../org/telegram/ui/Cells/EditTextCell.java | 13 +- .../org/telegram/ui/Cells/GroupMedia.java | 1493 ++++++++++++++ .../org/telegram/ui/Cells/LocationCell.java | 6 +- .../ui/Cells/PhotoAttachPhotoCell.java | 57 +- .../telegram/ui/Cells/ProfileSearchCell.java | 4 +- .../ui/Cells/ReactedUserHolderView.java | 70 +- .../org/telegram/ui/Cells/SessionCell.java | 4 + .../ui/Cells/SharedPhotoVideoCell2.java | 43 +- .../ui/Cells/TextSelectionHelper.java | 8 +- .../telegram/ui/Cells/TopicSearchCell.java | 4 +- .../java/org/telegram/ui/Cells/UserCell.java | 8 +- .../org/telegram/ui/ChannelBoostLayout.java | 4 +- .../ui/ChannelMonetizationLayout.java | 1054 ++++++++-- .../org/telegram/ui/Charts/BarChartView.java | 7 +- .../org/telegram/ui/Charts/BaseChartView.java | 4 +- .../ui/Charts/LinearBarChartView.java | 3 +- .../telegram/ui/Charts/data/ChartData.java | 3 + .../ui/Charts/view_data/BarViewData.java | 9 +- .../view_data/ChartHorizontalLinesData.java | 13 +- .../Charts/view_data/LegendSignatureView.java | 15 +- .../java/org/telegram/ui/ChatActivity.java | 1056 +++++++--- .../org/telegram/ui/ChatEditActivity.java | 92 +- .../ui/Components/AnimatedEmojiDrawable.java | 24 +- .../ui/Components/BackupImageView.java | 64 + .../ui/Components/BlurringShader.java | 13 +- .../ui/Components/BulletinFactory.java | 26 +- .../ui/Components/ChatActivityEnterView.java | 204 +- .../ui/Components/ChatAttachAlert.java | 51 +- .../ChatAttachAlertDocumentLayout.java | 1 + .../ChatAttachAlertLocationLayout.java | 2 +- .../ChatAttachAlertPhotoLayout.java | 159 +- .../ChatAttachAlertPhotoLayoutPreview.java | 61 +- .../ui/Components/ChatAvatarContainer.java | 12 +- .../ui/Components/EmbedBottomSheet.java | 2 +- .../ui/Components/GroupedPhotosListView.java | 15 +- .../ui/Components/InstantCameraView.java | 21 +- .../telegram/ui/Components/ItemOptions.java | 4 +- .../telegram/ui/Components/LoadingSpan.java | 16 +- .../telegram/ui/Components/MediaActivity.java | 81 +- .../ui/Components/MessagePrivateSeenView.java | 7 +- .../Components/OutlineTextContainerView.java | 75 +- .../OverlayActionBarLayoutDialog.java | 2 +- .../ui/Components/Paint/RenderView.java | 2 +- .../telegram/ui/Components/Paint/Texture.java | 26 +- .../ui/Components/Paint/Views/EntityView.java | 3 +- .../Paint/Views/LPhotoPaintView.java | 1 + .../Components/Paint/Views/LinkPreview.java | 530 +++++ .../ui/Components/Paint/Views/LinkView.java | 220 +++ .../Paint/Views/LocationMarker.java | 9 +- .../Components/Paint/Views/LocationView.java | 2 +- .../Paint/Views/MessageEntityView.java | 10 +- .../Paint/Views/StoryLinkPreviewDialog.java | 423 ++++ .../telegram/ui/Components/PasscodeView.java | 10 +- .../ui/Components/PollVotesAlert.java | 2 +- .../boosts/cells/msg/GiveawayMessageCell.java | 4 +- .../cells/msg/GiveawayResultsMessageCell.java | 4 +- .../org/telegram/ui/Components/QuoteSpan.java | 2 +- .../ui/Components/ReactedUsersListView.java | 7 +- .../Reactions/ReactionImageHolder.java | 2 +- .../Reactions/ReactionsLayoutInBubble.java | 131 +- .../telegram/ui/Components/ScrimOptions.java | 116 +- .../ui/Components/SharedMediaLayout.java | 74 +- .../java/org/telegram/ui/Components/Text.java | 44 +- .../org/telegram/ui/Components/UItem.java | 6 + .../ui/Components/UniversalAdapter.java | 63 +- .../ui/Components/ViewPagerFixed.java | 14 + .../Components/spoilers/SpoilerEffect2.java | 42 +- .../java/org/telegram/ui/DialogsActivity.java | 2 +- .../telegram/ui/ExternalActionActivity.java | 4 +- .../org/telegram/ui/GroupCallActivity.java | 2 +- .../org/telegram/ui/ImageReceiverSpan.java | 108 + .../java/org/telegram/ui/LaunchActivity.java | 153 +- .../org/telegram/ui/LocationActivity.java | 272 ++- .../java/org/telegram/ui/LoginActivity.java | 13 +- .../org/telegram/ui/MessageSendPreview.java | 213 +- .../org/telegram/ui/PaymentFormActivity.java | 11 +- .../org/telegram/ui/PhotoPickerActivity.java | 2 +- .../java/org/telegram/ui/PhotoViewer.java | 17 +- .../java/org/telegram/ui/ProfileActivity.java | 36 +- .../org/telegram/ui/SecretVoicePlayer.java | 2 +- .../telegram/ui/Stars/BotStarsActivity.java | 765 ++++++++ .../telegram/ui/Stars/BotStarsController.java | 250 +++ .../telegram/ui/Stars/StarsController.java | 171 +- .../telegram/ui/Stars/StarsIntroActivity.java | 804 +++++++- .../org/telegram/ui/StatisticActivity.java | 3 +- .../telegram/ui/Stories/PeerStoriesView.java | 57 +- .../ui/Stories/SelfStoryViewsPage.java | 2 +- .../ui/Stories/StoriesController.java | 150 +- .../ui/Stories/StoriesListPlaceProvider.java | 6 +- .../telegram/ui/Stories/StoriesViewPager.java | 8 +- .../ui/Stories/StoryMediaAreasView.java | 163 +- .../org/telegram/ui/Stories/StoryViewer.java | 74 +- .../recorder/ButtonWithCounterView.java | 6 +- .../ui/Stories/recorder/DownloadButton.java | 2 +- .../ui/Stories/recorder/EmojiBottomSheet.java | 53 +- .../ui/Stories/recorder/HintView2.java | 9 + .../ui/Stories/recorder/PaintView.java | 279 ++- .../ui/Stories/recorder/PreviewView.java | 1 + .../ui/Stories/recorder/StoryLinkSheet.java | 555 ++++++ .../ui/Stories/recorder/StoryRecorder.java | 5 +- .../ui/TwoStepVerificationActivity.java | 23 +- .../org/telegram/ui/UserInfoActivity.java | 6 +- .../ui/VoiceMessageEnterTransition.java | 3 +- .../org/telegram/ui/bots/BotBiometry.java | 67 +- .../ui/bots/BotCommandsMenuContainer.java | 36 +- .../telegram/ui/bots/BotCommandsMenuView.java | 15 +- .../ui/bots/BotWebViewAttachedSheet.java | 1736 +++++++++++++++++ .../telegram/ui/bots/BotWebViewContainer.java | 911 ++++++--- .../ui/bots/BotWebViewMenuContainer.java | 279 ++- .../org/telegram/ui/bots/BotWebViewSheet.java | 996 ++++++---- .../bots/ChatAttachAlertBotWebViewLayout.java | 44 +- .../res/drawable-hdpi/large_locked_post.png | Bin 0 -> 1092 bytes .../res/drawable-hdpi/menu_feature_paid.png | Bin 0 -> 1083 bytes .../res/drawable-hdpi/menu_instant_view.png | Bin 0 -> 1016 bytes .../res/drawable-hdpi/menu_premium_main.png | Bin 0 -> 1068 bytes .../src/main/res/drawable-hdpi/msg_ton.png | Bin 0 -> 1161 bytes .../res/drawable-mdpi/large_locked_post.png | Bin 0 -> 756 bytes .../res/drawable-mdpi/menu_feature_paid.png | Bin 0 -> 706 bytes .../res/drawable-mdpi/menu_instant_view.png | Bin 0 -> 663 bytes .../res/drawable-mdpi/menu_premium_main.png | Bin 0 -> 728 bytes .../src/main/res/drawable-mdpi/msg_ton.png | Bin 0 -> 746 bytes .../res/drawable-xhdpi/large_locked_post.png | Bin 0 -> 1386 bytes .../res/drawable-xhdpi/menu_feature_paid.png | Bin 0 -> 1635 bytes .../res/drawable-xhdpi/menu_instant_view.png | Bin 0 -> 1575 bytes .../res/drawable-xhdpi/menu_premium_main.png | Bin 0 -> 1441 bytes .../src/main/res/drawable-xhdpi/msg_ton.png | Bin 0 -> 1727 bytes .../res/drawable-xxhdpi/large_locked_post.png | Bin 0 -> 2112 bytes .../res/drawable-xxhdpi/menu_feature_paid.png | Bin 0 -> 2400 bytes .../res/drawable-xxhdpi/menu_instant_view.png | Bin 0 -> 2098 bytes .../res/drawable-xxhdpi/menu_premium_main.png | Bin 0 -> 2101 bytes .../src/main/res/drawable-xxhdpi/msg_ton.png | Bin 0 -> 2435 bytes .../src/main/res/drawable/story_link.xml | 12 + TMessagesProj/src/main/res/values/ids.xml | 1 + TMessagesProj/src/main/res/values/strings.xml | 130 +- gradle.properties | 4 +- 185 files changed, 19085 insertions(+), 2422 deletions(-) create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabs.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabsOverlay.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupMedia.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LinkPreview.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LinkView.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/StoryLinkPreviewDialog.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/ImageReceiverSpan.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsActivity.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsController.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryLinkSheet.java create mode 100644 TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewAttachedSheet.java create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/large_locked_post.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/menu_feature_paid.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/menu_instant_view.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/menu_premium_main.png create mode 100644 TMessagesProj/src/main/res/drawable-hdpi/msg_ton.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/large_locked_post.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/menu_feature_paid.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/menu_instant_view.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/menu_premium_main.png create mode 100644 TMessagesProj/src/main/res/drawable-mdpi/msg_ton.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/large_locked_post.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/menu_feature_paid.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/menu_instant_view.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/menu_premium_main.png create mode 100644 TMessagesProj/src/main/res/drawable-xhdpi/msg_ton.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/large_locked_post.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/menu_feature_paid.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/menu_instant_view.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/menu_premium_main.png create mode 100644 TMessagesProj/src/main/res/drawable-xxhdpi/msg_ton.png create mode 100644 TMessagesProj/src/main/res/drawable/story_link.xml diff --git a/TMessagesProj/jni/TgNetWrapper.cpp b/TMessagesProj/jni/TgNetWrapper.cpp index 029e9edcf3..5c8a8f8bb3 100644 --- a/TMessagesProj/jni/TgNetWrapper.cpp +++ b/TMessagesProj/jni/TgNetWrapper.cpp @@ -366,9 +366,11 @@ class Delegate : public ConnectiosManagerDelegate { jniEnv[instanceNum]->CallStaticVoidMethod(jclass_ConnectionsManager, jclass_ConnectionsManager_onPremiumFloodWait, instanceNum, requestToken, isUpload); } - void onIntegrityCheckClassic(int32_t instanceNum, int32_t requestToken, std::string nonce) { + void onIntegrityCheckClassic(int32_t instanceNum, int32_t requestToken, std::string project, std::string nonce) { + jstring projectStr = jniEnv[instanceNum]->NewStringUTF(project.c_str()); jstring nonceStr = jniEnv[instanceNum]->NewStringUTF(nonce.c_str()); - jniEnv[instanceNum]->CallStaticVoidMethod(jclass_ConnectionsManager, jclass_ConnectionsManager_onIntegrityCheckClassic, instanceNum, requestToken, nonceStr); + jniEnv[instanceNum]->CallStaticVoidMethod(jclass_ConnectionsManager, jclass_ConnectionsManager_onIntegrityCheckClassic, instanceNum, requestToken, projectStr, nonceStr); + jniEnv[instanceNum]->DeleteLocalRef(projectStr); jniEnv[instanceNum]->DeleteLocalRef(nonceStr); } @@ -627,7 +629,7 @@ extern "C" int registerNativeTgNetFunctions(JavaVM *vm, JNIEnv *env) { if (jclass_ConnectionsManager_onPremiumFloodWait == 0) { return JNI_FALSE; } - jclass_ConnectionsManager_onIntegrityCheckClassic = env->GetStaticMethodID(jclass_ConnectionsManager, "onIntegrityCheckClassic", "(IILjava/lang/String;)V"); + jclass_ConnectionsManager_onIntegrityCheckClassic = env->GetStaticMethodID(jclass_ConnectionsManager, "onIntegrityCheckClassic", "(IILjava/lang/String;Ljava/lang/String;)V"); if (jclass_ConnectionsManager_onIntegrityCheckClassic == 0) { return JNI_FALSE; } diff --git a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp index 916b7a91e3..b324373592 100644 --- a/TMessagesProj/jni/tgnet/ConnectionsManager.cpp +++ b/TMessagesProj/jni/tgnet/ConnectionsManager.cpp @@ -1307,12 +1307,15 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag } } else if (error->error_code == 403 && error->error_message.find(integrityCheckClassic) != std::string::npos) { discardResponse = true; - std::string nonce = error->error_message.substr(integrityCheckClassic.size(), error->error_message.size() - integrityCheckClassic.size()); + std::string err = error->error_message; + int index = err.find('_', integrityCheckClassic.size()); + std::string project = err.substr(integrityCheckClassic.size(), index - integrityCheckClassic.size()); + std::string nonce = err.substr(integrityCheckClassic.size() + project.size() + 1, err.size() - (integrityCheckClassic.size() + project.size() + 1)); request->awaitingIntegrityCheck = true; request->startTime = 0; request->startTimeMillis = 0; if (delegate != nullptr) { - delegate->onIntegrityCheckClassic(instanceNum, request->requestToken, nonce); + delegate->onIntegrityCheckClassic(instanceNum, request->requestToken, project, nonce); } } else { bool failServerErrors = (request->requestFlags & RequestFlagFailOnServerErrors) == 0 || processEvenFailed; diff --git a/TMessagesProj/jni/tgnet/Defines.h b/TMessagesProj/jni/tgnet/Defines.h index 4a88d2561e..4949d183d2 100644 --- a/TMessagesProj/jni/tgnet/Defines.h +++ b/TMessagesProj/jni/tgnet/Defines.h @@ -154,7 +154,7 @@ typedef struct ConnectiosManagerDelegate { virtual void getHostByName(std::string domain, int32_t instanceNum, ConnectionSocket *socket) = 0; virtual int32_t getInitFlags(int32_t instanceNum) = 0; virtual void onPremiumFloodWait(int32_t instanceNum, int32_t requestToken, bool isUpload) = 0; - virtual void onIntegrityCheckClassic(int32_t instanceNum, int32_t requestToken, std::string nonce) = 0; + virtual void onIntegrityCheckClassic(int32_t instanceNum, int32_t requestToken, std::string project, std::string nonce) = 0; } ConnectiosManagerDelegate; typedef struct HandshakeDelegate { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java index 2e935d837d..3fec62d5fb 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/AndroidUtilities.java @@ -46,6 +46,7 @@ import android.graphics.drawable.Drawable; import android.graphics.fonts.Font; import android.graphics.fonts.FontFamily; +import android.graphics.fonts.FontStyle; import android.graphics.fonts.SystemFonts; import android.net.Uri; import android.os.Build; @@ -100,7 +101,9 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; +import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; @@ -304,15 +307,25 @@ public static Typeface bold() { public static Pattern LONG_BAD_CHARS_PATTERN = null; public static Pattern BAD_CHARS_MESSAGE_PATTERN = null; public static Pattern BAD_CHARS_MESSAGE_LONG_PATTERN = null; + public static Pattern REMOVE_MULTIPLE_DIACRITICS = null; private static Pattern singleTagPatter = null; + public static String removeDiacritics(String str) { + if (str == null) return null; + if (REMOVE_MULTIPLE_DIACRITICS == null) return str; + Matcher matcher = REMOVE_MULTIPLE_DIACRITICS.matcher(str); + if (matcher == null) return str; + return matcher.replaceAll("$1"); + } + static { try { final String GOOD_IRI_CHAR = "a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"; BAD_CHARS_PATTERN = Pattern.compile("[\u2500-\u25ff]"); LONG_BAD_CHARS_PATTERN = Pattern.compile("[\u4e00-\u9fff]"); - BAD_CHARS_MESSAGE_LONG_PATTERN = Pattern.compile("[\u0300-\u036f\u2066-\u2067]+"); + BAD_CHARS_MESSAGE_LONG_PATTERN = Pattern.compile("[\u0300-\u036f\u2066-\u2067]"); BAD_CHARS_MESSAGE_PATTERN = Pattern.compile("[\u2066-\u2067]+"); + REMOVE_MULTIPLE_DIACRITICS = Pattern.compile("([\\u0300-\\u036f]{1,2})[\\u0300-\\u036f]+"); final Pattern IP_ADDRESS = Pattern.compile( "((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]" + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]" @@ -564,6 +577,34 @@ public void updateDrawState(TextPaint textPaint) { return spannableStringBuilder; } + + public static SpannableStringBuilder replaceMultipleTags(String str, Runnable ...runnables) { + SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(str); + for (int i = 0; i < runnables.length; ++i) { + Runnable runnable = runnables[i]; + + int start = charSequenceIndexOf(spannableStringBuilder, "**"); + int end = charSequenceIndexOf(spannableStringBuilder, "**", start + 2); + if (start < 0 || end < 0) break; + + spannableStringBuilder.delete(start, start + 2); + end = end - 2; + spannableStringBuilder.delete(end, end + 2); + spannableStringBuilder.setSpan(new ClickableSpan() { + @Override + public void updateDrawState(@NonNull TextPaint ds) { + super.updateDrawState(ds); + ds.setUnderlineText(false); + } + @Override + public void onClick(@NonNull View widget) { + if (runnable != null) runnable.run(); + } + }, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + return spannableStringBuilder; + } + public static SpannableStringBuilder replaceSingleLink(String str, int color) { return replaceSingleLink(str, color, null); } @@ -1840,13 +1881,17 @@ public String getRawType(boolean first) { } public String getType() { - if (type == 5) { - return LocaleController.getString("ContactBirthday", R.string.ContactBirthday); + if (type == 4) { + return LocaleController.getString(R.string.ContactNote); + } else if (type == 3) { + return LocaleController.getString(R.string.ContactUrl); + } else if (type == 5) { + return LocaleController.getString(R.string.ContactBirthday); } else if (type == 6) { if ("ORG".equalsIgnoreCase(getRawType(true))) { - return LocaleController.getString("ContactJob", R.string.ContactJob); + return LocaleController.getString(R.string.ContactJob); } else { - return LocaleController.getString("ContactJobTitle", R.string.ContactJobTitle); + return LocaleController.getString(R.string.ContactJobTitle); } } int idx = fullData.indexOf(':'); @@ -4674,6 +4719,15 @@ public static void lerp(Rect a, Rect b, float f, Rect to) { } } + public static void lerpCentered(RectF a, RectF b, float f, RectF to) { + if (to == null) return; + final float cx = lerp(a.centerX(), b.centerX(), f); + final float cy = lerp(a.centerY(), b.centerY(), f); + final float hw = lerp(a.width(), b.width(), Math.min(1, f)) / 2f; + final float hh = lerp(a.height(), b.height(), Math.min(1, f)) / 2f; + to.set(cx - hw, cy - hh, cx + hw, cy + hh); + } + public static void lerp(int[] a, int[] b, float f, int[] to) { if (to == null) return; for (int i = 0; i < to.length; ++i) { @@ -5840,4 +5894,71 @@ public static void vibrate(View view) { } catch (Exception ignore) {} } + public static void applySpring(Animator anim, float stiffness, float damping) { + applySpring(anim, stiffness, damping, 1); + } + + public static void applySpring(Animator anim, float stiffness, float damping, float mass) { + final double delta = damping / (2.0 * Math.sqrt(stiffness * mass)); + final double undampedFrequency = Math.sqrt(stiffness / mass); + final double omega_0 = Math.sqrt(stiffness / mass); + final double zeta = damping / (2 * Math.sqrt(stiffness * mass)); + final double threshold = 0.0025; + final double duration = Math.log(threshold) / (-zeta * omega_0); + anim.setDuration((long) (duration * 1000L)); + anim.setInterpolator(new Interpolator() { + @Override + public float getInterpolation(float t) { + if (delta < 1) { + final double dampedFrequency = undampedFrequency * Math.sqrt(1 - delta * delta); + return (float) (1 - Math.exp(-delta * undampedFrequency * t) * + (Math.cos(dampedFrequency * t) + (delta * undampedFrequency / dampedFrequency) * Math.sin(dampedFrequency * t))); + } else { + final double a = -delta * undampedFrequency * t; + return (float) (1 - (1 + a) * Math.exp(a)); + } + } + }); + } + + public static boolean isWebAppLink(String url) { + if (url == null) return false; + try { + Uri uri = Uri.parse(url); + final String scheme = uri.getScheme(); + if (scheme == null) return false; + final String path = uri.getPath(); + if (path == null) return false; + switch (scheme) { + case "http": + case "https": { + if (path.isEmpty()) return false; + ArrayList segments = new ArrayList<>(uri.getPathSegments()); + if (segments.size() > 0 && segments.get(0).equals("s")) { + segments.remove(0); + } + if (segments.size() > 0) { + if (segments.size() >= 3 && "s".equals(segments.get(1))) { + return false; + } else if (segments.size() > 1) { + return !TextUtils.isEmpty(segments.get(1)); + } else if (segments.size() == 1) { + return !TextUtils.isEmpty(uri.getQueryParameter("startapp")); + } + } + break; + } + case "tg": { + if (url.startsWith("tg:resolve") || url.startsWith("tg://resolve")) { + return !TextUtils.isEmpty(uri.getQueryParameter("appname")); + } + break; + } + } + } catch (Exception e) { + FileLog.e(e); + } + return false; + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java b/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java index 887564260d..d97777a8cf 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/BillingController.java @@ -30,6 +30,7 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.PremiumPreviewFragment; +import org.telegram.ui.Stars.StarsController; import java.text.NumberFormat; import java.util.ArrayList; @@ -273,48 +274,137 @@ public void onPurchasesUpdated(@NonNull BillingResult billing, @Nullable List payload = BillingUtilities.extractDeveloperPayload(purchase); - if (payload == null) { + Pair opayload = BillingUtilities.extractDeveloperPayload(purchase); + if (opayload == null || opayload.first == null) { continue; } if (!purchase.isAcknowledged()) { requestingTokens.add(purchase.getPurchaseToken()); - TLRPC.TL_payments_assignPlayMarketTransaction req = new TLRPC.TL_payments_assignPlayMarketTransaction(); - req.receipt = new TLRPC.TL_dataJSON(); - req.receipt.data = purchase.getOriginalJson(); - req.purpose = payload.second; + retrievePurpose(purchase, opayload, payload -> { + TLRPC.TL_payments_assignPlayMarketTransaction req = new TLRPC.TL_payments_assignPlayMarketTransaction(); + req.receipt = new TLRPC.TL_dataJSON(); + req.receipt.data = purchase.getOriginalJson(); + req.purpose = payload.second; - final AlertDialog progressDialog = new AlertDialog(ApplicationLoader.applicationContext, AlertDialog.ALERT_TYPE_SPINNER); - AndroidUtilities.runOnUIThread(() -> progressDialog.showDelayed(500)); + final AlertDialog progressDialog = new AlertDialog(ApplicationLoader.applicationContext, AlertDialog.ALERT_TYPE_SPINNER); + AndroidUtilities.runOnUIThread(() -> progressDialog.showDelayed(500)); - AccountInstance acc = payload.first; - acc.getConnectionsManager().sendRequest(req, (response, error) -> { - AndroidUtilities.runOnUIThread(progressDialog::dismiss); + AccountInstance acc = payload.first; + acc.getConnectionsManager().sendRequest(req, (response, error) -> { + AndroidUtilities.runOnUIThread(progressDialog::dismiss); - requestingTokens.remove(purchase.getPurchaseToken()); + requestingTokens.remove(purchase.getPurchaseToken()); - if (response instanceof TLRPC.Updates) { - acc.getMessagesController().processUpdates((TLRPC.Updates) response, false); + if (response instanceof TLRPC.Updates) { + acc.getMessagesController().processUpdates((TLRPC.Updates) response, false); - for (String productId : purchase.getProducts()) { - Consumer listener = resultListeners.remove(productId); - if (listener != null) { - listener.accept(billing); + for (String productId : purchase.getProducts()) { + Consumer listener = resultListeners.remove(productId); + if (listener != null) { + listener.accept(billing); + } } + + consumeGiftPurchase(purchase, req.purpose); + } else if (error != null) { + if (onCanceled != null) { + onCanceled.run(); + onCanceled = null; + } + NotificationCenter.getGlobalInstance().postNotificationNameOnUIThread(NotificationCenter.billingConfirmPurchaseError, req, error); } + }, ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagFailOnServerErrorsExceptFloodWait | ConnectionsManager.RequestFlagInvokeAfter); + }); + } else { + consumeGiftPurchase(purchase, opayload.second); + } + } + } + } - consumeGiftPurchase(purchase, req.purpose); - } else if (error != null) { - if (onCanceled != null) { - onCanceled.run(); - onCanceled = null; + private boolean retrievePurpose(Purchase purchase, Pair payload, Utilities.Callback> whenPayload) { + if (payload == null || payload.first == null) { + FileLog.d("retrievePurpose: payload or account is null"); + return false; + } + if (payload.second != null) { + FileLog.d("retrievePurpose: already has purpose"); + whenPayload.run(payload); + return true; + } + if (purchase == null || purchase.getProducts().isEmpty()) { + FileLog.d("retrievePurpose: no products found for purpose!"); + whenPayload.run(payload); + return false; + } else { + final int currentAccount = payload.first.getCurrentAccount(); + final String productId = purchase.getProducts().get(0); + + if (productId == null) { + FileLog.d("retrievePurpose: first product is null!"); + whenPayload.run(payload); + return false; + } + + ArrayList options = StarsController.getInstance(currentAccount).getOptionsCached(); + if (options == null) { + ConnectionsManager.getInstance(currentAccount).sendRequest(new TLRPC.TL_payments_getStarsTopupOptions(), (res, err) -> AndroidUtilities.runOnUIThread(() -> { + ArrayList loadedOptions = new ArrayList<>(); + if (res instanceof TLRPC.Vector) { + TLRPC.Vector vector = (TLRPC.Vector) res; + for (Object object : vector.objects) { + if (object instanceof TLRPC.TL_starsTopupOption) { + TLRPC.TL_starsTopupOption option = (TLRPC.TL_starsTopupOption) object; + loadedOptions.add(option); } - NotificationCenter.getGlobalInstance().postNotificationNameOnUIThread(NotificationCenter.billingConfirmPurchaseError, req, error); } - }, ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagFailOnServerErrorsExceptFloodWait | ConnectionsManager.RequestFlagInvokeAfter); + } else if (err != null) { + FileLog.d("retrievePopup: getStarsTopupOptions gives error! " + err.code + ": " + err.text); + } + + TLRPC.TL_starsTopupOption foundOption = null; + for (int i = 0; i < loadedOptions.size(); ++i) { + if (productId.equals(loadedOptions.get(i).store_product)) { + foundOption = loadedOptions.get(i); + break; + } + } + + if (foundOption != null) { + TLRPC.TL_inputStorePaymentStars purpose = new TLRPC.TL_inputStorePaymentStars(); + purpose.amount = foundOption.amount; + purpose.currency = foundOption.currency; + purpose.stars = foundOption.stars; + FileLog.d("retrievePurpose: found stars option of " + productId + " from stars loaded options!"); + whenPayload.run(new Pair(payload.first, purpose)); + } else { + FileLog.d("retrievePurpose: failed to find option of " + productId + " from stars loaded options"); + whenPayload.run(payload); + } + })); + return true; + } else { + TLRPC.TL_starsTopupOption foundOption = null; + for (int i = 0; i < options.size(); ++i) { + if (productId.equals(options.get(i).store_product)) { + foundOption = options.get(i); + break; + } + } + + if (foundOption != null) { + TLRPC.TL_inputStorePaymentStars purpose = new TLRPC.TL_inputStorePaymentStars(); + purpose.amount = foundOption.amount; + purpose.currency = foundOption.currency; + purpose.stars = foundOption.stars; + FileLog.d("retrievePurpose: found stars option of " + productId + " from stars options!"); + whenPayload.run(new Pair(payload.first, purpose)); + return true; } else { - consumeGiftPurchase(purchase, payload.second); + FileLog.d("retrievePurpose: failed to find option of " + productId + " from stars options"); + whenPayload.run(payload); + return false; } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ChatMessagesMetadataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ChatMessagesMetadataController.java index e51e5c2a92..b1976116a3 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ChatMessagesMetadataController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ChatMessagesMetadataController.java @@ -42,7 +42,7 @@ public void checkMessages(ChatActivity.ChatActivityAdapter chatAdapter, int maxA messageObject.reactionsLastCheckTime = currentTime; reactionsToCheck.add(messageObject); } - if (chatActivity.getThreadMessage() != messageObject && messageObject.getId() > 0 && messageObject.hasExtendedMediaPreview() && (currentTime - messageObject.extendedMediaLastCheckTime) > 30000L) { + if (chatActivity.getThreadMessage() != messageObject && messageObject.getId() > 0 && (messageObject.hasExtendedMediaPreview() || messageObject.hasPaidMediaPreview()) && (currentTime - messageObject.extendedMediaLastCheckTime) > 30000L) { messageObject.extendedMediaLastCheckTime = currentTime; extendedMediaToCheck.add(messageObject); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java b/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java index b5b908396d..64197de0a0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/DownloadController.java @@ -23,6 +23,7 @@ import org.telegram.SQLite.SQLitePreparedStatement; import org.telegram.tgnet.NativeByteBuffer; import org.telegram.tgnet.TLRPC; +import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.Components.Bulletin; import org.telegram.ui.LaunchActivity; @@ -600,6 +601,15 @@ public void checkAutodownloadSettings() { } public boolean canDownloadMedia(MessageObject messageObject) { + if (messageObject.type == MessageObject.TYPE_STORY) { + if (!SharedConfig.isAutoplayVideo()) return false; + TLRPC.TL_messageMediaStory mediaStory = (TLRPC.TL_messageMediaStory) MessageObject.getMedia(messageObject); + TL_stories.StoryItem storyItem = mediaStory.storyItem; + if (storyItem == null || storyItem.media == null || storyItem.media.document == null || !storyItem.isPublic) { + return false; + } + return true; + } return canDownloadMedia(messageObject.messageOwner) == 1; } @@ -709,6 +719,88 @@ public int canDownloadMedia(TLRPC.Message message) { } } + public int canDownloadMedia(TLRPC.Message message, TLRPC.MessageMedia media) { + if (message == null || media instanceof TLRPC.TL_messageMediaStory) { + return canPreloadStories() ? 2 : 0; + } + int type; + boolean isVideo = false; + if (MessageObject.isVideoDocument(media.document)) { + isVideo = true; + type = AUTODOWNLOAD_TYPE_VIDEO; + } else if (MessageObject.isVoiceDocument(media.document)) { + type = AUTODOWNLOAD_TYPE_AUDIO; + } else if (media instanceof TLRPC.TL_messageMediaPhoto) { + type = AUTODOWNLOAD_TYPE_PHOTO; + } else if (media.document != null) { + type = AUTODOWNLOAD_TYPE_DOCUMENT; + } else { + return 0; + } + int index; + TLRPC.Peer peer = message.peer_id; + if (peer != null) { + if (peer.user_id != 0) { + if (getContactsController().contactsDict.containsKey(peer.user_id)) { + index = 0; + } else { + index = 1; + } + } else if (peer.chat_id != 0) { + if (message.from_id instanceof TLRPC.TL_peerUser && getContactsController().contactsDict.containsKey(message.from_id.user_id)) { + index = 0; + } else { + index = 2; + } + } else { + TLRPC.Chat chat = message.peer_id.channel_id != 0 ? getMessagesController().getChat(message.peer_id.channel_id) : null; + if (ChatObject.isChannel(chat) && chat.megagroup) { + if (message.from_id instanceof TLRPC.TL_peerUser && getContactsController().contactsDict.containsKey(message.from_id.user_id)) { + index = 0; + } else { + index = 2; + } + } else { + index = 3; + } + } + } else { + index = 1; + } + Preset preset; + int networkType = ApplicationLoader.getAutodownloadNetworkType(); + if (networkType == StatsController.TYPE_WIFI) { + if (!wifiPreset.enabled) { + return 0; + } + preset = getCurrentWiFiPreset(); + + } else if (networkType == StatsController.TYPE_ROAMING) { + if (!roamingPreset.enabled) { + return 0; + } + preset = getCurrentRoamingPreset(); + } else { + if (!mobilePreset.enabled) { + return 0; + } + preset = getCurrentMobilePreset(); + } + int mask = preset.mask[index]; + long maxSize; + if (type == AUTODOWNLOAD_TYPE_AUDIO) { + maxSize = Math.max(512 * 1024, preset.sizes[typeToIndex(type)]); + } else { + maxSize = preset.sizes[typeToIndex(type)]; + } + long size = MessageObject.getMessageSize(message); + if (isVideo && preset.preloadVideo && size > maxSize && maxSize > 2 * 1024 * 1024) { + return (mask & type) != 0 ? 2 : 0; + } else { + return (type == AUTODOWNLOAD_TYPE_PHOTO || size != 0 && size <= maxSize) && (type == AUTODOWNLOAD_TYPE_AUDIO || (mask & type) != 0) ? 1 : 0; + } + } + protected boolean canDownloadNextTrack() { int networkType = ApplicationLoader.getAutodownloadNetworkType(); if (networkType == StatsController.TYPE_WIFI) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java index 33c3dbea37..1ed983c259 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileRefController.java @@ -41,7 +41,7 @@ public Waiter(String loc, String parent) { private HashMap> locationRequester = new HashMap<>(); private HashMap> parentRequester = new HashMap<>(); private HashMap responseCache = new HashMap<>(); - private HashMap multiMediaCache = new HashMap<>(); + private HashMap multiMediaCache = new HashMap<>(); private long lastCleanupTime = SystemClock.elapsedRealtime(); @@ -91,6 +91,9 @@ public static String getKeyForParentObject(Object parentObject) { } else if (parentObject instanceof MessageObject) { MessageObject messageObject = (MessageObject) parentObject; long channelId = messageObject.getChannelId(); + if (messageObject.type == MessageObject.TYPE_PAID_MEDIA && messageObject.messageOwner != null && messageObject.messageOwner.fwd_from != null && messageObject.messageOwner.fwd_from.from_id != null) { + channelId = DialogObject.getPeerDialogId(messageObject.messageOwner.fwd_from.from_id); + } return "message" + messageObject.getRealId() + "_" + channelId + "_" + messageObject.scheduled + "_" + messageObject.getQuickReplyId(); } else if (parentObject instanceof TLRPC.Message) { TLRPC.Message message = (TLRPC.Message) parentObject; @@ -155,6 +158,16 @@ public void requestReference(Object parentObject, Object... args) { sendErrorToObject(args, 0); return; } + } else if (args[0] instanceof TLRPC.TL_inputMediaDocument) { + TLRPC.TL_inputMediaDocument mediaDocument = (TLRPC.TL_inputMediaDocument) args[0]; + locationKey = "file_" + mediaDocument.id.id; + location = new TLRPC.TL_inputDocumentFileLocation(); + location.id = mediaDocument.id.id; + } else if (args[0] instanceof TLRPC.TL_inputMediaPhoto) { + TLRPC.TL_inputMediaPhoto mediaPhoto = (TLRPC.TL_inputMediaPhoto) args[0]; + locationKey = "photo_" + mediaPhoto.id.id; + location = new TLRPC.TL_inputPhotoFileLocation(); + location.id = mediaPhoto.id.id; } else if (args[0] instanceof TLRPC.TL_messages_sendMultiMedia) { TLRPC.TL_messages_sendMultiMedia req = (TLRPC.TL_messages_sendMultiMedia) args[0]; ArrayList parentObjects = (ArrayList) parentObject; @@ -180,6 +193,40 @@ public void requestReference(Object parentObject, Object... args) { locationKey = "photo_" + mediaPhoto.id.id; location = new TLRPC.TL_inputPhotoFileLocation(); location.id = mediaPhoto.id.id; + } else if (req.media instanceof TLRPC.TL_inputMediaPaidMedia) { + TLRPC.TL_inputMediaPaidMedia paidMedia = (TLRPC.TL_inputMediaPaidMedia) req.media; + if (parentObject instanceof ArrayList) { + ArrayList parentObjects = (ArrayList) parentObject; + multiMediaCache.put(req, args); + for (int a = 0, size = paidMedia.extended_media.size(); a < size; a++) { + TLRPC.InputMedia media = paidMedia.extended_media.get(a); + parentObject = parentObjects.get(a); + if (parentObject == null) { + continue; + } + requestReference(parentObject, media, req); + } + return; + } else if (paidMedia.extended_media.size() == 1) { + TLRPC.InputMedia inputMedia = paidMedia.extended_media.get(0); + if (inputMedia instanceof TLRPC.TL_inputMediaDocument) { + TLRPC.TL_inputMediaDocument mediaDocument = (TLRPC.TL_inputMediaDocument) inputMedia; + locationKey = "file_" + mediaDocument.id.id; + location = new TLRPC.TL_inputDocumentFileLocation(); + location.id = mediaDocument.id.id; + } else if (inputMedia instanceof TLRPC.TL_inputMediaPhoto) { + TLRPC.TL_inputMediaPhoto mediaPhoto = (TLRPC.TL_inputMediaPhoto) inputMedia; + locationKey = "photo_" + mediaPhoto.id.id; + location = new TLRPC.TL_inputPhotoFileLocation(); + location.id = mediaPhoto.id.id; + } else { + sendErrorToObject(args, 0); + return; + } + } else { + sendErrorToObject(args, 0); + return; + } } else { sendErrorToObject(args, 0); return; @@ -600,6 +647,49 @@ private boolean onUpdateObjectReference(Requester requester, byte[] file_referen multiMediaCache.remove(multiMedia); AndroidUtilities.runOnUIThread(() -> getSendMessagesHelper().performSendMessageRequestMulti(multiMedia, (ArrayList) objects[1], (ArrayList) objects[2], null, (SendMessagesHelper.DelayedMessage) objects[4], (Boolean) objects[5])); } + } else if (requester.args.length >= 2 && requester.args[1] instanceof TLRPC.TL_messages_sendMedia && ((TLRPC.TL_messages_sendMedia) requester.args[1]).media instanceof TLRPC.TL_inputMediaPaidMedia && (requester.args[0] instanceof TLRPC.TL_inputMediaPhoto || requester.args[0] instanceof TLRPC.TL_inputMediaDocument)) { + TLRPC.TL_messages_sendMedia sendMedia = (TLRPC.TL_messages_sendMedia) requester.args[1]; + Object[] objects = multiMediaCache.get(sendMedia); + if (objects == null) { + return true; + } + + TLRPC.InputMedia inputMedia = null; + if (requester.args[0] instanceof TLRPC.TL_inputMediaDocument) { + TLRPC.TL_inputMediaDocument mediaDocument = (TLRPC.TL_inputMediaDocument) requester.args[0]; + inputMedia = mediaDocument; + if (fromCache && isSameReference(mediaDocument.id.file_reference, file_reference)) { + return false; + } + mediaDocument.id.file_reference = file_reference; + } else if (requester.args[0] instanceof TLRPC.TL_inputMediaPhoto) { + TLRPC.TL_inputMediaPhoto mediaPhoto = (TLRPC.TL_inputMediaPhoto) requester.args[0]; + inputMedia = mediaPhoto; + if (fromCache && isSameReference(mediaPhoto.id.file_reference, file_reference)) { + return false; + } + mediaPhoto.id.file_reference = file_reference; + } + + TLRPC.TL_inputMediaPaidMedia paidMedia = (TLRPC.TL_inputMediaPaidMedia) sendMedia.media; + + int index = paidMedia.extended_media.indexOf(inputMedia); + if (index < 0) { + return true; + } + ArrayList parentObjects = (ArrayList) objects[3]; + parentObjects.set(index, null); + + boolean done = true; + for (int a = 0, size; a < parentObjects.size(); a++) { + if (parentObjects.get(a) != null) { + done = false; + } + } + if (done) { + multiMediaCache.remove(sendMedia); + AndroidUtilities.runOnUIThread(() -> getSendMessagesHelper().performSendMessageRequestMulti(sendMedia, (ArrayList) objects[1], (ArrayList) objects[2], null, (SendMessagesHelper.DelayedMessage) objects[4], (Boolean) objects[5])); + } } else if (requester.args[0] instanceof TLRPC.TL_messages_sendMedia) { TLRPC.TL_messages_sendMedia req = (TLRPC.TL_messages_sendMedia) requester.args[0]; if (req.media instanceof TLRPC.TL_inputMediaDocument) { @@ -729,7 +819,14 @@ private void sendErrorToObject(Object[] args, int reason) { multiMediaCache.remove(req); AndroidUtilities.runOnUIThread(() -> getSendMessagesHelper().performSendMessageRequestMulti(req, (ArrayList) objects[1], (ArrayList) objects[2], null, (SendMessagesHelper.DelayedMessage) objects[4], (Boolean) objects[5])); } - } else if (args[0] instanceof TLRPC.TL_messages_sendMedia || args[0] instanceof TLRPC.TL_messages_editMessage) { + } else if ((args[0] instanceof TLRPC.TL_inputMediaDocument || args[0] instanceof TLRPC.TL_inputMediaPhoto) && args[1] instanceof TLRPC.TL_messages_sendMedia) { + TLRPC.TL_messages_sendMedia req = (TLRPC.TL_messages_sendMedia) args[1]; + Object[] objects = multiMediaCache.get(req); + if (objects != null) { + multiMediaCache.remove(req); + AndroidUtilities.runOnUIThread(() -> getSendMessagesHelper().performSendMessageRequestMulti(req, (ArrayList) objects[1], (ArrayList) objects[2], null, (SendMessagesHelper.DelayedMessage) objects[4], (Boolean) objects[5])); + } + } else if (args[0] instanceof TLRPC.TL_messages_sendMedia && !(((TLRPC.TL_messages_sendMedia) args[0]).media instanceof TLRPC.TL_inputMediaPaidMedia) || args[0] instanceof TLRPC.TL_messages_editMessage) { AndroidUtilities.runOnUIThread(() -> getSendMessagesHelper().performSendMessageRequest((TLObject) args[0], (MessageObject) args[1], (String) args[2], (SendMessagesHelper.DelayedMessage) args[3], (Boolean) args[4], (SendMessagesHelper.DelayedMessage) args[5], null, null, (Boolean) args[6])); } else if (args[0] instanceof TLRPC.TL_messages_saveGif) { TLRPC.TL_messages_saveGif req = (TLRPC.TL_messages_saveGif) args[0]; @@ -817,7 +914,32 @@ private boolean onRequestComplete(String locationKey, String parentKey, TLObject if (!res.messages.isEmpty()) { for (int i = 0, size3 = res.messages.size(); i < size3; i++) { TLRPC.Message message = res.messages.get(i); - if (message.media != null) { + if (message.media instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) message.media; + for (int j = 0; j < paidMedia.extended_media.size(); ++j) { + TLRPC.MessageExtendedMedia extendedMedia = paidMedia.extended_media.get(j); + if (extendedMedia instanceof TLRPC.TL_messageExtendedMedia) { + TLRPC.MessageMedia media = ((TLRPC.TL_messageExtendedMedia) extendedMedia).media; + if (media != null) { + if (media.document != null) { + result = getFileReference(media.document, requester.location, needReplacement, locationReplacement); + } else if (media.game != null) { + result = getFileReference(media.game.document, requester.location, needReplacement, locationReplacement); + if (result == null) { + result = getFileReference(media.game.photo, requester.location, needReplacement, locationReplacement); + } + } else if (media.photo != null) { + result = getFileReference(media.photo, requester.location, needReplacement, locationReplacement); + } else if (media.webpage != null) { + result = getFileReference(media.webpage, requester.location, needReplacement, locationReplacement); + } + } + } + if (result != null) { + break; + } + } + } else if (message.media != null) { if (message.media.document != null) { result = getFileReference(message.media.document, requester.location, needReplacement, locationReplacement); } else if (message.media.game != null) { @@ -1398,6 +1520,21 @@ private byte[] getFileReference(TLRPC.WebPage webpage, TLRPC.InputFileLocation l } public static boolean isFileRefError(String error) { - return "FILEREF_EXPIRED".equals(error) || "FILE_REFERENCE_EXPIRED".equals(error) || "FILE_REFERENCE_EMPTY".equals(error) || error != null && error.startsWith("FILE_REFERENCE_"); + return ( + "FILEREF_EXPIRED".equals(error) || + "FILE_REFERENCE_EXPIRED".equals(error) || + "FILE_REFERENCE_EMPTY".equals(error) || + error != null && error.startsWith("FILE_REFERENCE_") + ); + } + + public static int getFileRefErrorIndex(String error) { + if (error == null) return -1; + if (!error.startsWith("FILE_REFERENCE_") || !error.endsWith("_EXPIRED")) return -1; + try { + return Integer.parseInt(error.substring("FILE_REFERENCE_".length(), error.length() - "_EXPIRED".length())); + } catch (Exception e) { + return -1; + } } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java index b1951ad651..1f102aaffa 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLoader.java @@ -4281,6 +4281,17 @@ public static void saveMessageThumbs(TLRPC.Message message) { if (message.media == null) { return; } + if (message.media instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) message.media; + for (int i = 0; i < paidMedia.extended_media.size(); ++i) { + TLRPC.MessageExtendedMedia emedia = paidMedia.extended_media.get(i); + if (emedia instanceof TLRPC.TL_messageExtendedMedia) { + saveMessageThumbs(message, ((TLRPC.TL_messageExtendedMedia) emedia).media); + } + } + return; + } + TLRPC.PhotoSize photoSize = findPhotoCachedSize(message); if (photoSize != null && photoSize.bytes != null && photoSize.bytes.length != 0) { @@ -4367,6 +4378,97 @@ public static void saveMessageThumbs(TLRPC.Message message) { } } + + public static void saveMessageThumbs(TLRPC.Message message, TLRPC.MessageMedia media) { + if (message == null || media == null) { + return; + } + TLRPC.PhotoSize photoSize = findPhotoCachedSize(media); + + if (photoSize != null && photoSize.bytes != null && photoSize.bytes.length != 0) { + TLRPC.PhotoSize newPhotoSize; + if (photoSize.location == null || photoSize.location instanceof TLRPC.TL_fileLocationUnavailable) { + photoSize.location = new TLRPC.TL_fileLocationToBeDeprecated(); + photoSize.location.volume_id = Integer.MIN_VALUE; + photoSize.location.local_id = SharedConfig.getLastLocalId(); + } + if (photoSize.h <= 50 && photoSize.w <= 50) { + newPhotoSize = new TLRPC.TL_photoStrippedSize(); + newPhotoSize.location = photoSize.location; + newPhotoSize.bytes = photoSize.bytes; + newPhotoSize.h = photoSize.h; + newPhotoSize.w = photoSize.w; + } else { + File file = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(photoSize, true); + boolean isEncrypted = false; + if (MessageObject.shouldEncryptPhotoOrVideo(UserConfig.selectedAccount, message)) { + file = new File(file.getAbsolutePath() + ".enc"); + isEncrypted = true; + } + if (!file.exists()) { + try { + if (isEncrypted) { + File keyPath = new File(FileLoader.getInternalCacheDir(), file.getName() + ".key"); + RandomAccessFile keyFile = new RandomAccessFile(keyPath, "rws"); + long len = keyFile.length(); + byte[] encryptKey = new byte[32]; + byte[] encryptIv = new byte[16]; + if (len > 0 && len % 48 == 0) { + keyFile.read(encryptKey, 0, 32); + keyFile.read(encryptIv, 0, 16); + } else { + Utilities.random.nextBytes(encryptKey); + Utilities.random.nextBytes(encryptIv); + keyFile.write(encryptKey); + keyFile.write(encryptIv); + } + keyFile.close(); + Utilities.aesCtrDecryptionByteArray(photoSize.bytes, encryptKey, encryptIv, 0, photoSize.bytes.length, 0); + } + RandomAccessFile writeFile = new RandomAccessFile(file, "rws"); + writeFile.write(photoSize.bytes); + writeFile.close(); + } catch (Exception e) { + FileLog.e(e); + } + } + + newPhotoSize = new TLRPC.TL_photoSize_layer127(); + newPhotoSize.w = photoSize.w; + newPhotoSize.h = photoSize.h; + newPhotoSize.location = photoSize.location; + newPhotoSize.size = photoSize.size; + newPhotoSize.type = photoSize.type; + } + + if (media instanceof TLRPC.TL_messageMediaPhoto) { + for (int a = 0, count = media.photo.sizes.size(); a < count; a++) { + TLRPC.PhotoSize size = media.photo.sizes.get(a); + if (size instanceof TLRPC.TL_photoCachedSize) { + media.photo.sizes.set(a, newPhotoSize); + break; + } + } + } else if (media instanceof TLRPC.TL_messageMediaDocument) { + for (int a = 0, count = media.document.thumbs.size(); a < count; a++) { + TLRPC.PhotoSize size = media.document.thumbs.get(a); + if (size instanceof TLRPC.TL_photoCachedSize) { + media.document.thumbs.set(a, newPhotoSize); + break; + } + } + } else if (media instanceof TLRPC.TL_messageMediaWebPage) { + for (int a = 0, count = media.webpage.photo.sizes.size(); a < count; a++) { + TLRPC.PhotoSize size = media.webpage.photo.sizes.get(a); + if (size instanceof TLRPC.TL_photoCachedSize) { + media.webpage.photo.sizes.set(a, newPhotoSize); + break; + } + } + } + } + } + private static TLRPC.PhotoSize findPhotoCachedSize(TLRPC.Message message) { TLRPC.PhotoSize photoSize = null; if (message.media instanceof TLRPC.TL_messageMediaPhoto) { @@ -4397,8 +4499,44 @@ private static TLRPC.PhotoSize findPhotoCachedSize(TLRPC.Message message) { } } } - } else if (message.media instanceof TLRPC.TL_messageMediaInvoice && message.media.extended_media instanceof TLRPC.TL_messageExtendedMediaPreview) { - photoSize = ((TLRPC.TL_messageExtendedMediaPreview) message.media.extended_media).thumb; + } else if (message.media instanceof TLRPC.TL_messageMediaInvoice && !message.media.extended_media.isEmpty() && message.media.extended_media.get(0) instanceof TLRPC.TL_messageExtendedMediaPreview) { + photoSize = ((TLRPC.TL_messageExtendedMediaPreview) message.media.extended_media.get(0)).thumb; + } + return photoSize; + } + + private static TLRPC.PhotoSize findPhotoCachedSize(TLRPC.MessageMedia media) { + TLRPC.PhotoSize photoSize = null; + if (media instanceof TLRPC.TL_messageMediaPhoto) { + for (int a = 0, count = media.photo.sizes.size(); a < count; a++) { + TLRPC.PhotoSize size = media.photo.sizes.get(a); + if (size instanceof TLRPC.TL_photoCachedSize) { + photoSize = size; + break; + } + } + } else if (media instanceof TLRPC.TL_messageMediaDocument) { + if (media.document != null) { + for (int a = 0, count = media.document.thumbs.size(); a < count; a++) { + TLRPC.PhotoSize size = media.document.thumbs.get(a); + if (size instanceof TLRPC.TL_photoCachedSize) { + photoSize = size; + break; + } + } + } + } else if (media instanceof TLRPC.TL_messageMediaWebPage) { + if (media.webpage.photo != null) { + for (int a = 0, count = media.webpage.photo.sizes.size(); a < count; a++) { + TLRPC.PhotoSize size = media.webpage.photo.sizes.get(a); + if (size instanceof TLRPC.TL_photoCachedSize) { + photoSize = size; + break; + } + } + } + } else if (media instanceof TLRPC.TL_messageMediaInvoice && !media.extended_media.isEmpty() && media.extended_media.get(0) instanceof TLRPC.TL_messageExtendedMediaPreview) { + photoSize = ((TLRPC.TL_messageExtendedMediaPreview) media.extended_media.get(0)).thumb; } return photoSize; } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLocation.java b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLocation.java index e93a139885..eac715367c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/ImageLocation.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/ImageLocation.java @@ -352,7 +352,7 @@ private static ImageLocation getForPhoto(TLRPC.FileLocation location, int size, } public static String getStrippedKey(Object parentObject, Object fullObject, Object strippedObject) { - if (parentObject instanceof TLRPC.WebPage) { + if (parentObject instanceof TLRPC.WebPage || parentObject instanceof MessageObject && ((MessageObject) parentObject).type == MessageObject.TYPE_PAID_MEDIA) { if (fullObject instanceof ImageLocation) { ImageLocation imageLocation = (ImageLocation) fullObject; if (imageLocation.document != null) { @@ -391,7 +391,7 @@ public String getKey(Object parentObject, Object fullObject, boolean url) { return secureDocument.secureFile.dc_id + "_" + secureDocument.secureFile.id; } else if (photoSize instanceof TLRPC.TL_photoStrippedSize || photoSize instanceof TLRPC.TL_photoPathSize) { if (photoSize.bytes.length > 0) { - return getStrippedKey(parentObject, fullObject, photoSize); + return getStrippedKey(parentObject, fullObject == null ? this : fullObject, photoSize); } } else if (location != null) { return location.volume_id + "_" + location.local_id; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/LocationController.java b/TMessagesProj/src/main/java/org/telegram/messenger/LocationController.java index 1a7667552e..9c9517d8fe 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/LocationController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/LocationController.java @@ -30,6 +30,7 @@ import org.telegram.tgnet.NativeByteBuffer; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; +import org.telegram.tgnet.tl.TL_stories; import java.util.ArrayList; import java.util.HashMap; @@ -1004,6 +1005,30 @@ public interface LocationFetchCallback { } public static final int TYPE_BIZ = 1; + public static final int TYPE_STORY = 2; + + // google geocoder thinks that "unnamed road" is a street name + public static String[] unnamedRoads = { + "Unnamed Road", + "Вulicya bez nazvi", + "Нeizvestnaya doroga", + "İsimsiz Yol", + "Ceļš bez nosaukuma", + "Kelias be pavadinimo", + "Droga bez nazwy", + "Cesta bez názvu", + "Silnice bez názvu", + "Drum fără nume", + "Route sans nom", + "Vía sin nombre", + "Estrada sem nome", + "Οdos xoris onomasia", + "Rrugë pa emër", + "Пat bez ime", + "Нeimenovani put", + "Strada senza nome", + "Straße ohne Straßennamen" + }; private static HashMap callbacks = new HashMap<>(); public static void fetchLocationAddress(Location location, LocationFetchCallback callback) { @@ -1024,22 +1049,42 @@ public static void fetchLocationAddress(Location location, int type, LocationFet } Locale locale; + Locale englishLocale; try { locale = LocaleController.getInstance().getCurrentLocale(); } catch (Exception ignore) { locale = LocaleController.getInstance().getSystemDefaultLocale(); } + if (locale.getLanguage().contains("en")) { + englishLocale = locale; + } else { + englishLocale = Locale.US; + } final Locale finalLocale = locale; Utilities.globalQueue.postRunnable(fetchLocationRunnable = () -> { - String name, displayName, city, street, countryCode = null; + String name, displayName, city, street, countryCode = null, locality = null, feature = null, engFeature = null; + String engState = null, engCity = null; + StringBuilder engStreet = new StringBuilder(); boolean onlyCountry = true; TLRPC.TL_messageMediaVenue cityLocation = null; + TL_stories.TL_geoPointAddress cityAddress = new TL_stories.TL_geoPointAddress(); TLRPC.TL_messageMediaVenue streetLocation = null; + TL_stories.TL_geoPointAddress streetAddress = new TL_stories.TL_geoPointAddress(); try { Geocoder gcd = new Geocoder(ApplicationLoader.applicationContext, finalLocale); List
addresses = gcd.getFromLocation(location.getLatitude(), location.getLongitude(), 1); + List
engAddresses = null; + if (type == TYPE_STORY) { + if (englishLocale == finalLocale) { + engAddresses = addresses; + } else { + Geocoder gcd2 = new Geocoder(ApplicationLoader.applicationContext, englishLocale); + engAddresses = gcd2.getFromLocation(location.getLatitude(), location.getLongitude(), 1); + } + } if (addresses.size() > 0) { Address address = addresses.get(0); + Address engAddress = engAddresses != null && engAddresses.size() >= 1 ? engAddresses.get(0) : null; if (type == TYPE_BIZ) { ArrayList parts = new ArrayList<>(); @@ -1097,8 +1142,6 @@ public static void fetchLocationAddress(Location location, int type, LocationFet StringBuilder cityBuilder = new StringBuilder(); StringBuilder streetBuilder = new StringBuilder(); - String locality = null; - String feature = null; // String addressLine = null; // try { // addressLine = address.getAddressLine(0); @@ -1119,15 +1162,30 @@ public static void fetchLocationAddress(Location location, int type, LocationFet //// feature = parts[0].replace(",", "").trim(); // } // } + if (TextUtils.isEmpty(locality)) { locality = address.getLocality(); } if (TextUtils.isEmpty(locality)) { - locality = address.getSubAdminArea(); + locality = address.getAdminArea(); } if (TextUtils.isEmpty(locality)) { - locality = address.getAdminArea(); + locality = address.getSubAdminArea(); } + if (engAddress != null) { + if (TextUtils.isEmpty(engCity)) { + engCity = engAddress.getLocality(); + } + if (TextUtils.isEmpty(engCity)) { + engCity = engAddress.getAdminArea(); + } + if (TextUtils.isEmpty(engCity)) { + engCity = engAddress.getSubAdminArea(); + } + + engState = engAddress.getAdminArea(); + } + if (TextUtils.isEmpty(feature) && !TextUtils.equals(address.getThoroughfare(), locality) && !TextUtils.equals(address.getThoroughfare(), address.getCountryName())) { feature = address.getThoroughfare(); } @@ -1145,6 +1203,41 @@ public static void fetchLocationAddress(Location location, int type, LocationFet } else { streetBuilder = null; } + + if (engAddress != null) { + if (TextUtils.isEmpty(engFeature) && !TextUtils.equals(engAddress.getThoroughfare(), locality) && !TextUtils.equals(engAddress.getThoroughfare(), engAddress.getCountryName())) { + engFeature = engAddress.getThoroughfare(); + } + if (TextUtils.isEmpty(engFeature) && !TextUtils.equals(engAddress.getSubLocality(), locality) && !TextUtils.equals(engAddress.getSubLocality(), engAddress.getCountryName())) { + engFeature = engAddress.getSubLocality(); + } + if (TextUtils.isEmpty(engFeature) && !TextUtils.equals(engAddress.getLocality(), locality) && !TextUtils.equals(engAddress.getLocality(), engAddress.getCountryName())) { + engFeature = engAddress.getLocality(); + } + if (!TextUtils.isEmpty(engFeature) && !TextUtils.equals(engFeature, engState) && !TextUtils.equals(engFeature, engAddress.getCountryName())) { + if (engStreet.length() > 0) { + engStreet.append(", "); + } + engStreet.append(engFeature); + } else { + engStreet = null; + } + + if (!TextUtils.isEmpty(engStreet)) { + boolean isUnnamed = false; + for (int i = 0; i < unnamedRoads.length; ++i) { + if (unnamedRoads[i].equalsIgnoreCase(engStreet.toString())) { + isUnnamed = true; + break; + } + } + if (isUnnamed) { + engStreet = null; + streetBuilder = null; + } + } + } + if (!TextUtils.isEmpty(locality)) { if (cityBuilder.length() > 0) { cityBuilder.append(", "); @@ -1275,6 +1368,19 @@ public static void fetchLocationAddress(Location location, int type, LocationFet cityLocation.icon = onlyCountry ? "https://ss3.4sqi.net/img/categories_v2/building/government_capitolbuilding_64.png" : "https://ss3.4sqi.net/img/categories_v2/travel/hotel_64.png"; cityLocation.emoji = countryCodeToEmoji(countryCode); cityLocation.address = onlyCountry ? LocaleController.getString("Country", R.string.Country) : LocaleController.getString("PassportCity", R.string.PassportCity); + + cityLocation.geoAddress = cityAddress; + cityAddress.country_iso2 = countryCode; + if (!onlyCountry) { + if (!TextUtils.isEmpty(engState)) { + cityAddress.flags |= 1; + cityAddress.state = engState; + } + if (!TextUtils.isEmpty(engCity)) { + cityAddress.flags |= 2; + cityAddress.city = engCity; + } + } } if (!TextUtils.isEmpty(street)) { streetLocation = new TLRPC.TL_messageMediaVenue(); @@ -1285,6 +1391,21 @@ public static void fetchLocationAddress(Location location, int type, LocationFet streetLocation.title = street; streetLocation.icon = "pin"; streetLocation.address = LocaleController.getString("PassportStreet1", R.string.PassportStreet1); + + streetLocation.geoAddress = streetAddress; + streetAddress.country_iso2 = countryCode; + if (!TextUtils.isEmpty(engState)) { + streetAddress.flags |= 1; + streetAddress.state = engState; + } + if (!TextUtils.isEmpty(engCity)) { + streetAddress.flags |= 2; + streetAddress.city = engCity; + } + if (!TextUtils.isEmpty(engStreet)) { + streetAddress.flags |= 4; + streetAddress.street = engStreet.toString(); + } } if (cityLocation == null && streetLocation == null && location != null) { String ocean = detectOcean(location.getLongitude(), location.getLatitude()); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java index a855480862..3b7e205021 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java @@ -50,6 +50,7 @@ import android.os.Environment; import android.os.PowerManager; import android.os.SystemClock; +import android.provider.DocumentsContract; import android.provider.MediaStore; import android.provider.OpenableColumns; import android.telephony.PhoneStateListener; @@ -64,6 +65,8 @@ import android.webkit.MimeTypeMap; import android.widget.FrameLayout; +import androidx.core.content.FileProvider; + import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; @@ -457,6 +460,7 @@ public static class PhotoEntry extends MediaEditState { public boolean isMuted; public boolean canDeleteAfter; public boolean hasSpoiler; + public long starsAmount; public String emoji; public int videoOrientation = -1; @@ -514,6 +518,7 @@ public PhotoEntry setOrientation(int rotation, int invert) { public void copyFrom(MediaEditState state) { super.copyFrom(state); this.hasSpoiler = state instanceof PhotoEntry && ((PhotoEntry) state).hasSpoiler; + this.starsAmount = state instanceof PhotoEntry ? ((PhotoEntry) state).starsAmount : 0; } public PhotoEntry clone() { @@ -522,6 +527,7 @@ public PhotoEntry clone() { photoEntry.isMuted = isMuted; photoEntry.canDeleteAfter = canDeleteAfter; photoEntry.hasSpoiler = hasSpoiler; + photoEntry.starsAmount = starsAmount; photoEntry.isChatPreviewSpoilerRevealed = isChatPreviewSpoilerRevealed; photoEntry.isAttachSpoilerRevealed = isAttachSpoilerRevealed; photoEntry.emojiMarkup = emojiMarkup; @@ -545,6 +551,7 @@ public void reset() { } } hasSpoiler = false; + starsAmount = 0; super.reset(); } @@ -4221,28 +4228,28 @@ public void cleanRecording(boolean delete) { } private void stopRecordingInternal(final int send, boolean notify, int scheduleDate, boolean once) { - if (send != 0) { + if (send != 0 && recordingAudioFile != null) { final TLRPC.TL_document audioToSend = recordingAudio; final File recordingAudioFileToSend = recordingAudioFile; if (BuildVars.LOGS_ENABLED) { - FileLog.d("stop recording internal filename " + recordingAudioFile.getPath()); + FileLog.d("stop recording internal filename " + (recordingAudioFileToSend == null ? "null" : recordingAudioFileToSend.getPath())); } fileEncodingQueue.postRunnable(() -> { stopRecord(false); if (BuildVars.LOGS_ENABLED) { - FileLog.d("stop recording internal in queue " + recordingAudioFileToSend.exists() + " " + recordingAudioFileToSend.length()); + FileLog.d("stop recording internal in queue " + (recordingAudioFileToSend == null ? "null" : recordingAudioFileToSend.exists() + " " + recordingAudioFileToSend.length())); } AndroidUtilities.runOnUIThread(() -> { if (BuildVars.LOGS_ENABLED) { - FileLog.d("stop recording internal " + recordingAudioFileToSend.exists() + " " + recordingAudioFileToSend.length() + " " + " recordTimeCount " + recordTimeCount + " writedFrames" + writedFrame); + FileLog.d("stop recording internal " + (recordingAudioFileToSend == null ? "null" : recordingAudioFileToSend.exists() + " " + recordingAudioFileToSend.length() + " " + " recordTimeCount " + recordTimeCount + " writedFrames" + writedFrame)); } - boolean fileExist = recordingAudioFileToSend.exists(); + boolean fileExist = recordingAudioFileToSend != null && recordingAudioFileToSend.exists(); if (!fileExist && BuildVars.DEBUG_VERSION) { FileLog.e(new RuntimeException("file not found :( recordTimeCount " + recordTimeCount + " writedFrames" + writedFrame)); } MediaDataController.getInstance(recordingCurrentAccount).pushDraftVoiceMessage(recordDialogId, recordTopicId, null); audioToSend.date = ConnectionsManager.getInstance(recordingCurrentAccount).getCurrentTime(); - audioToSend.size = (int) recordingAudioFileToSend.length(); + audioToSend.size = recordingAudioFileToSend == null ? 0 : (int) recordingAudioFileToSend.length(); TLRPC.TL_documentAttributeAudio attributeAudio = new TLRPC.TL_documentAttributeAudio(); attributeAudio.voice = true; attributeAudio.waveform = getWaveform2(recordSamples, recordSamples.length); @@ -4264,8 +4271,10 @@ private void stopRecordingInternal(final int send, boolean notify, int scheduleD NotificationCenter.getInstance(recordingCurrentAccount).postNotificationName(NotificationCenter.audioDidSent, recordingGuid, send == 2 ? audioToSend : null, send == 2 ? recordingAudioFileToSend.getAbsolutePath() : null); } else { NotificationCenter.getInstance(recordingCurrentAccount).postNotificationName(NotificationCenter.audioRecordTooShort, recordingGuid, false, (int) duration); - AutoDeleteMediaTask.unlockFile(recordingAudioFileToSend); - recordingAudioFileToSend.delete(); + if (recordingAudioFileToSend != null) { + AutoDeleteMediaTask.unlockFile(recordingAudioFileToSend); + recordingAudioFileToSend.delete(); + } } requestAudioFocus(false); }); @@ -4387,19 +4396,19 @@ public void start() { path = null; } } - if (path == null || path.length() == 0) { - path = null; - TLRPC.Document document = message.getDocument(); - if (!TextUtils.isEmpty(FileLoader.getDocumentFileName(document)) && !(message.messageOwner instanceof TLRPC.TL_message_secret) && FileLoader.canSaveAsFile(message)) { - String filename = FileLoader.getDocumentFileName(document); - File newDir = FileLoader.getDirectory(FileLoader.MEDIA_DIR_FILES); - if (newDir != null) { - path = new File(newDir, filename).getAbsolutePath(); - } - } - if (path == null) { + if (TextUtils.isEmpty(path)) { +// path = null; +// TLRPC.Document document = message.getDocument(); +// if (!TextUtils.isEmpty(FileLoader.getDocumentFileName(document)) && !(message.messageOwner instanceof TLRPC.TL_message_secret) && FileLoader.canSaveAsFile(message)) { +// String filename = FileLoader.getDocumentFileName(document); +// File newDir = FileLoader.getDirectory(FileLoader.MEDIA_DIR_FILES); +// if (newDir != null) { +// path = new File(newDir, filename).getAbsolutePath(); +// } +// } +// if (path == null) { path = FileLoader.getInstance(currentAccount.getCurrentAccount()).getPathToMessage(message.messageOwner).toString(); - } +// } } File sourceFile = new File(path); if (!sourceFile.exists()) { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java index f3e67c9d92..781b0fc0b5 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java @@ -7145,8 +7145,8 @@ public static boolean stringsEqual(CharSequence a, CharSequence b) { if (a == null && b == null) return true; if (a == null || b == null) return false; if (!TextUtils.equals(a, b)) return false; - CharSequence[] A = new CharSequence[] { a }; - CharSequence[] B = new CharSequence[] { b }; + CharSequence[] A = new CharSequence[] { new SpannableStringBuilder(a) }; + CharSequence[] B = new CharSequence[] { new SpannableStringBuilder(b) }; ArrayList ae = getInstance(UserConfig.selectedAccount).getEntities(A, true); ArrayList be = getInstance(UserConfig.selectedAccount).getEntities(B, true); return entitiesEqual(ae, be); @@ -7185,8 +7185,9 @@ private CharSequence parsePattern(CharSequence cs, Pattern pattern, ArrayList entities, TLRPC.Message replyToMessage, boolean noWebpage) { - saveDraft(dialogId, threadId, message, entities, replyToMessage, null, noWebpage, false); + public void saveDraft(long dialogId, int threadId, CharSequence message, ArrayList entities, TLRPC.Message replyToMessage, boolean noWebpage, long effectId) { + saveDraft(dialogId, threadId, message, entities, replyToMessage, null, effectId, noWebpage, false); } - public void saveDraft(long dialogId, long 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, long effectId, boolean noWebpage, boolean clean) { TLRPC.DraftMessage draftMessage; if (getMessagesController().isForum(dialogId) && threadId == 0) { replyToMessage = null; @@ -7308,6 +7309,10 @@ public void saveDraft(long dialogId, long threadId, CharSequence message, ArrayL draftMessage.date = (int) (System.currentTimeMillis() / 1000); draftMessage.message = message == null ? "" : message.toString(); draftMessage.no_webpage = noWebpage; + if (effectId != 0) { + draftMessage.flags |= 128; + draftMessage.effect = effectId; + } if (replyToMessage != null) { draftMessage.reply_to = new TLRPC.TL_inputReplyToMessage(); draftMessage.flags |= 16; @@ -7350,12 +7355,14 @@ public void saveDraft(long dialogId, long threadId, CharSequence message, ArrayL sameDraft = ( currentDraft.message.equals(draftMessage.message) && replyToEquals(currentDraft.reply_to, draftMessage.reply_to) && - currentDraft.no_webpage == draftMessage.no_webpage + currentDraft.no_webpage == draftMessage.no_webpage && + currentDraft.effect == draftMessage.effect ); } else { sameDraft = ( TextUtils.isEmpty(draftMessage.message) && - (draftMessage.reply_to == null || draftMessage.reply_to.reply_to_msg_id == 0) + (draftMessage.reply_to == null || draftMessage.reply_to.reply_to_msg_id == 0) && + draftMessage.effect == 0 ); } if (sameDraft) { @@ -7382,6 +7389,10 @@ public void saveDraft(long dialogId, long threadId, CharSequence message, ArrayL req.entities = draftMessage.entities; req.flags |= 8; } + if ((draftMessage.flags & 128) != 0) { + req.effect = draftMessage.effect; + req.flags |= 128; + } getConnectionsManager().sendRequest(req, (response, error) -> { }); @@ -7696,7 +7707,7 @@ public void cleanDraft(long dialogId, long threadId, boolean replyOnly) { draftMessage.reply_to.reply_to_msg_id = 0; } draftMessage.flags &= ~1; - saveDraft(dialogId, threadId, draftMessage.message, draftMessage.entities, null, null, draftMessage.no_webpage, true); + saveDraft(dialogId, threadId, draftMessage.message, draftMessage.entities, null, null, 0, draftMessage.no_webpage, true); } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java index 41983bf4ff..fecd15f35c 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessageObject.java @@ -130,6 +130,7 @@ public class MessageObject { public static final int TYPE_GIVEAWAY = 26; public static final int TYPE_JOINED_CHANNEL = 27; // recommendations list public static final int TYPE_GIVEAWAY_RESULTS = 28; + public static final int TYPE_PAID_MEDIA = 29; // messageMediaPaidMedia with stars public int localType; public String localName; @@ -994,6 +995,9 @@ public static class GroupedMessagePosition { public int flags; public float[] siblingHeights; + public int photoWidth; + public int photoHeight; + public float top; // sum of ph of media above public float left; // sum of pw of media on the left side @@ -1194,7 +1198,11 @@ public void calculate() { float minH = dp(100) / maxSizeHeight; - if (!forceCalc && (count == 2 || count == 3 || count == 4)) { + if (count == 1) { + MessageObject.GroupedMessagePosition position1 = posArray.get(0); + float height = Math.round(Math.min(maxSizeWidth / position1.aspectRatio, Math.min(maxSizeWidth / position1.aspectRatio, maxSizeHeight / 2.0f))) / maxSizeHeight; + position1.set(0, 0, 0, 0, maxSizeWidth, height, POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT | POSITION_FLAG_TOP | POSITION_FLAG_BOTTOM); + } else if (!forceCalc && (count == 2 || count == 3 || count == 4)) { if (count == 2) { GroupedMessagePosition position1 = posArray.get(0); GroupedMessagePosition position2 = posArray.get(1); @@ -1581,7 +1589,8 @@ public MessageObject(int accountNum, TL_stories.StoryItem storyItem) { this.storyItem = storyItem; if (storyItem != null) { messageOwner = new TLRPC.TL_message(); - messageOwner.id = storyItem.id; + messageOwner.id = storyItem.messageId; + messageOwner.realId = storyItem.id; messageOwner.date = storyItem.date; messageOwner.dialog_id = storyItem.dialogId; messageOwner.message = storyItem.caption; @@ -3298,6 +3307,7 @@ public boolean updateTranslation(boolean force) { if ( TranslateController.isTranslatable(this) && translateController.isTranslatingDialog(getDialogId()) && + !translateController.isTranslateDialogHidden(getDialogId()) && messageOwner != null && messageOwner.translatedText != null && TextUtils.equals(translateController.getDialogTranslateTo(getDialogId()), messageOwner.translatedToLanguage) @@ -3470,6 +3480,10 @@ public void generatePinMessageText(TLRPC.User fromUser, TLRPC.Chat chat) { } } else if (getMedia(replyMessageObject) instanceof TLRPC.TL_messageMediaPhoto) { messageText = replaceWithLink(LocaleController.getString("ActionPinnedPhoto", R.string.ActionPinnedPhoto), "un1", fromUser != null ? fromUser : chat); + } else if (getMedia(replyMessageObject) instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) getMedia(replyMessageObject); + String username = chat != null ? chat.title : UserObject.getUserName(fromUser); + messageText = LocaleController.formatPluralString("NotificationPinnedPaidMedia", (int) paidMedia.stars_amount, username); } else if (getMedia(replyMessageObject) instanceof TLRPC.TL_messageMediaGame) { messageText = replaceWithLink(LocaleController.formatString("ActionPinnedGame", R.string.ActionPinnedGame, "\uD83C\uDFAE " + getMedia(replyMessageObject).game.title), "un1", fromUser != null ? fromUser : chat); messageText = Emoji.replaceEmoji(messageText, Theme.chat_msgTextPaint.getFontMetricsInt(), dp(20), false); @@ -3480,7 +3494,7 @@ public void generatePinMessageText(TLRPC.User fromUser, TLRPC.Chat chat) { mess = mess.subSequence(0, 20); ellipsize = true; } - mess = Emoji.replaceEmoji(mess, Theme.chat_msgTextPaint.getFontMetricsInt(), dp(20), false); + mess = Emoji.replaceEmoji(mess, Theme.chat_msgTextPaint.getFontMetricsInt(), dp(20), true); if (replyMessageObject != null && replyMessageObject.messageOwner != null) { mess = replyMessageObject.replaceAnimatedEmoji(mess, Theme.chat_msgTextPaint.getFontMetricsInt()); } @@ -3802,7 +3816,7 @@ 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() || isVideoSticker())) { + if ((param = messageOwner.params.get("ve")) != null && (isVideo() || isNewGif() || isRoundVideo() || isVideoSticker() || isPaidVideo(getMedia(this)))) { videoEditedInfo = new VideoEditedInfo(); if (!videoEditedInfo.parseString(param)) { videoEditedInfo = null; @@ -3829,6 +3843,21 @@ public void createMessageSendInfo() { } } + public static boolean isPaidVideo(TLRPC.MessageMedia m) { + return m instanceof TLRPC.TL_messageMediaPaidMedia && m.extended_media.size() == 1 && isExtendedVideo(m.extended_media.get(0)); + } + + public static boolean isExtendedVideo(TLRPC.MessageExtendedMedia em) { + if (em instanceof TLRPC.TL_messageExtendedMedia) { + TLRPC.TL_messageExtendedMedia eem = (TLRPC.TL_messageExtendedMedia) em; + return eem.media instanceof TLRPC.TL_messageMediaDocument && isVideoDocument(eem.media.document); + } else if (em instanceof TLRPC.TL_messageExtendedMediaPreview) { + TLRPC.TL_messageExtendedMediaPreview eem = (TLRPC.TL_messageExtendedMediaPreview) em; + return (eem.flags & 4) != 0; + } + return false; + } + public boolean hasInlineBotButtons() { return !isRestrictedMessage && !isRepostPreview && messageOwner != null && messageOwner.reply_markup instanceof TLRPC.TL_replyInlineMarkup && !messageOwner.reply_markup.rows.isEmpty(); } @@ -4764,6 +4793,22 @@ private void updateMessageText(AbstractMap users, AbstractMap< messageText = LocaleController.getString(R.string.AttachOnceAudio); } else if (isRoundOnce()) { messageText = LocaleController.getString(R.string.AttachOnceRound); + } else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) getMedia(messageOwner); + final int count = paidMedia.extended_media.size(); + boolean video = false; + for (int i = 0; i < count; ++i) { + TLRPC.MessageExtendedMedia emedia = paidMedia.extended_media.get(i); + if (emedia instanceof TLRPC.TL_messageExtendedMedia) { + video = ((TLRPC.TL_messageExtendedMedia) emedia).media instanceof TLRPC.TL_messageMediaDocument && + isVideoDocument(((TLRPC.TL_messageExtendedMedia) emedia).media.document); + } else if (emedia instanceof TLRPC.TL_messageExtendedMediaPreview) { + video = (((TLRPC.TL_messageExtendedMediaPreview) emedia).flags & 4) != 0; + } + if (video) break; + } + messageText = count == 1 ? LocaleController.getString(video ? R.string.AttachVideo : R.string.AttachPhoto) : LocaleController.formatPluralString(video ? "Media" : "Photos", count); + messageText = StarsIntroActivity.replaceStars(LocaleController.formatString(R.string.AttachPaidMedia, messageText)); } 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); @@ -4876,6 +4921,22 @@ public CharSequence getMediaTitle(TLRPC.MessageMedia media) { } else { return LocaleController.getString("Poll", R.string.Poll); } + } else if (media instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) media; + final int count = paidMedia.extended_media.size(); + boolean video = false; + for (int i = 0; i < count; ++i) { + TLRPC.MessageExtendedMedia emedia = paidMedia.extended_media.get(i); + if (emedia instanceof TLRPC.TL_messageExtendedMedia) { + video = ((TLRPC.TL_messageExtendedMedia) emedia).media instanceof TLRPC.TL_messageMediaDocument && + isVideoDocument(((TLRPC.TL_messageExtendedMedia) emedia).media.document); + } else if (emedia instanceof TLRPC.TL_messageExtendedMediaPreview) { + video = (((TLRPC.TL_messageExtendedMediaPreview) emedia).flags & 4) != 0; + } + if (video) break; + } + CharSequence s = count == 1 ? LocaleController.getString(video ? R.string.AttachVideo : R.string.AttachPhoto) : LocaleController.formatPluralString(video ? "Media" : "Photos", count); + return StarsIntroActivity.replaceStars(LocaleController.formatString(R.string.AttachPaidMedia, s)); } else if (media instanceof TLRPC.TL_messageMediaPhoto) { if (media.ttl_seconds != 0 && !(messageOwner instanceof TLRPC.TL_message_secret)) { return LocaleController.getString("AttachDestructingPhoto", R.string.AttachDestructingPhoto); @@ -4947,22 +5008,46 @@ public static TLRPC.MessageMedia getMedia(MessageObject messageObject) { } public static TLRPC.MessageMedia getMedia(TLRPC.Message messageOwner) { - if (messageOwner.media != null && messageOwner.media.extended_media instanceof TLRPC.TL_messageExtendedMedia) { - return ((TLRPC.TL_messageExtendedMedia) messageOwner.media.extended_media).media; + if ( + messageOwner.media != null && + !(messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia) && + !messageOwner.media.extended_media.isEmpty() && + messageOwner.media.extended_media.get(0) instanceof TLRPC.TL_messageExtendedMedia + ) { + return ((TLRPC.TL_messageExtendedMedia) messageOwner.media.extended_media.get(0)).media; } return messageOwner.media; } public boolean hasRevealedExtendedMedia() { - return messageOwner.media != null && messageOwner.media.extended_media instanceof TLRPC.TL_messageExtendedMedia; + return ( + messageOwner.media != null && + !(messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia) && + !messageOwner.media.extended_media.isEmpty() && + messageOwner.media.extended_media.get(0) instanceof TLRPC.TL_messageExtendedMedia + ); } public boolean hasExtendedMedia() { - return messageOwner.media != null && messageOwner.media.extended_media != null; + return messageOwner.media != null && !(messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia) && !messageOwner.media.extended_media.isEmpty(); + } + + public boolean hasPaidMediaPreview() { + return ( + messageOwner.media != null && + messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia && + !messageOwner.media.extended_media.isEmpty() && + messageOwner.media.extended_media.get(0) instanceof TLRPC.TL_messageExtendedMediaPreview + ); } public boolean hasExtendedMediaPreview() { - return messageOwner.media != null && messageOwner.media.extended_media instanceof TLRPC.TL_messageExtendedMediaPreview; + return ( + messageOwner.media != null && + !(messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia) && + !messageOwner.media.extended_media.isEmpty() && + messageOwner.media.extended_media.get(0) instanceof TLRPC.TL_messageExtendedMediaPreview + ); } private boolean hasNonEmojiEntities() { @@ -4990,6 +5075,8 @@ public void setType() { } else { type = TYPE_ANIMATED_STICKER; } + } else if (messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia) { + type = TYPE_PAID_MEDIA; } else if (isMediaEmpty(false) && !isDice() && !isSponsored() && emojiOnlyCount >= 1 && !hasUnwrappedEmoji && messageOwner != null && !hasNonEmojiEntities()) { type = TYPE_EMOJIS; } else if (isMediaEmpty()) { @@ -5317,7 +5404,7 @@ public static boolean isSystemSignUp(MessageObject message) { public void generateThumbs(boolean update) { if (hasExtendedMediaPreview()) { - TLRPC.TL_messageExtendedMediaPreview preview = (TLRPC.TL_messageExtendedMediaPreview) messageOwner.media.extended_media; + TLRPC.TL_messageExtendedMediaPreview preview = (TLRPC.TL_messageExtendedMediaPreview) messageOwner.media.extended_media.get(0); if (!update) { photoThumbs = new ArrayList<>(Collections.singletonList(preview.thumb)); } else { @@ -5595,6 +5682,23 @@ public static String getFileName(TLRPC.Message messageOwner) { return ""; } + public static String getFileName(TLRPC.MessageMedia media) { + if (media instanceof TLRPC.TL_messageMediaDocument) { + return FileLoader.getAttachFileName(media.document); + } else if (media instanceof TLRPC.TL_messageMediaPhoto) { + ArrayList sizes = media.photo.sizes; + if (sizes.size() > 0) { + TLRPC.PhotoSize sizeFull = FileLoader.getClosestPhotoSizeWithSize(sizes, AndroidUtilities.getPhotoSize()); + if (sizeFull != null) { + return FileLoader.getAttachFileName(sizeFull); + } + } + } else if (media instanceof TLRPC.TL_messageMediaWebPage && media.webpage != null) { + return FileLoader.getAttachFileName(media.webpage.document); + } + return ""; + } + public int getMediaType() { if (isVideo()) { return FileLoader.MEDIA_DIR_VIDEO; @@ -6195,7 +6299,14 @@ public static Spannable replaceAnimatedEmoji(CharSequence text, ArrayList entities, boolean out, boolean usernames, boolean photoViewer, boolean useManualParse) { + return addEntitiesToText(text, entities, out, usernames, photoViewer, useManualParse, ENTITIES_ALL); + } + + public static boolean addEntitiesToText(CharSequence text, ArrayList entities, boolean out, boolean usernames, boolean photoViewer, boolean useManualParse, int allowed) { if (!(text instanceof Spannable)) { return false; } @@ -6261,6 +6372,9 @@ public static boolean addEntitiesToText(CharSequence text, ArrayList= Build.VERSION_CODES.N) { StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0, text.length(), paint, width) @@ -7487,6 +7599,8 @@ public void updateDrawState(TextPaint tp) { hasNonRTL = true; } + + textRealMaxWidth = Math.max(textRealMaxWidth, lineWidth); textRealMaxWidthWithLeft = Math.max(textRealMaxWidthWithLeft, lineWidth + lineLeft); linesMaxWidth = Math.max(linesMaxWidth, (int) Math.ceil(lineWidth)); @@ -7846,13 +7960,17 @@ public int getRealId() { } public static long getMessageSize(TLRPC.Message message) { + return getMediaSize(getMedia(message)); + } + + public static long getMediaSize(TLRPC.MessageMedia media) { TLRPC.Document document; - if (getMedia(message) instanceof TLRPC.TL_messageMediaWebPage) { - document = getMedia(message).webpage.document; - } else if (getMedia(message) instanceof TLRPC.TL_messageMediaGame) { - document = getMedia(message).game.document; + if (media instanceof TLRPC.TL_messageMediaWebPage && media.webpage != null) { + document = media.webpage.document; + } else if (media instanceof TLRPC.TL_messageMediaGame) { + document = media.game.document; } else { - document = getMedia(message) != null ? getMedia(message).document : null; + document = media != null ? media.document : null; } if (document != null) { return document.size; @@ -8322,6 +8440,15 @@ public static TLRPC.Document getDocument(TLRPC.Message message) { return getMedia(message).webpage.document; } else if (getMedia(message) instanceof TLRPC.TL_messageMediaGame) { return getMedia(message).game.document; + } else if (getMedia(message) instanceof TLRPC.TL_messageMediaStory) { + TLRPC.TL_messageMediaStory story = (TLRPC.TL_messageMediaStory) getMedia(message); + if (story.storyItem != null && story.storyItem.media != null && story.storyItem.media.document != null) + return story.storyItem.media.document; + } else if (getMedia(message) instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) getMedia(message); + if (paidMedia.extended_media.size() == 1 && paidMedia.extended_media.get(0) instanceof TLRPC.TL_messageExtendedMedia) { + return ((TLRPC.TL_messageExtendedMedia) paidMedia.extended_media.get(0)).media.document; + } } return getMedia(message) != null ? getMedia(message).document : null; } @@ -9165,7 +9292,7 @@ public boolean needDrawForwarded() { if (fromId == 0) return savedId >= 0 && savedId != UserObject.ANONYMOUS; return savedId != fromId && fromId != selfId; } - return (messageOwner.flags & TLRPC.MESSAGE_FLAG_FWD) != 0 && messageOwner.fwd_from != null && !messageOwner.fwd_from.imported && (messageOwner.fwd_from.saved_from_peer == null || !(messageOwner.fwd_from.from_id instanceof TLRPC.TL_peerChannel) || messageOwner.fwd_from.saved_from_peer.channel_id != messageOwner.fwd_from.from_id.channel_id) && UserConfig.getInstance(currentAccount).getClientUserId() != getDialogId(); + return ((messageOwner.flags & TLRPC.MESSAGE_FLAG_FWD) != 0 || getMedia(this) instanceof TLRPC.TL_messageMediaPaidMedia) && messageOwner.fwd_from != null && !messageOwner.fwd_from.imported && (messageOwner.fwd_from.saved_from_peer == null || !(messageOwner.fwd_from.from_id instanceof TLRPC.TL_peerChannel) || messageOwner.fwd_from.saved_from_peer.channel_id != messageOwner.fwd_from.from_id.channel_id) && UserConfig.getInstance(currentAccount).getClientUserId() != getDialogId(); } public static boolean isForwardedMessage(TLRPC.Message message) { @@ -9297,10 +9424,11 @@ public static boolean canEditMessage(int currentAccount, TLRPC.Message message, if (chat != null && (chat.left || chat.kicked) && (!chat.megagroup || !chat.has_link)) { return false; } - if (message == null || message.peer_id == null || getMedia(message) != null && (isRoundVideoDocument(getMedia(message).document) || isStickerDocument(getMedia(message).document) || isAnimatedStickerDocument(getMedia(message).document, true) || isLocationMessage(message)) || message.action != null && !(message.action instanceof TLRPC.TL_messageActionEmpty) || isForwardedMessage(message) || message.via_bot_id != 0 || message.id < 0) { + TLRPC.MessageMedia media = getMedia(message); + if (message == null || message.peer_id == null || media != null && (isRoundVideoDocument(media.document) || isStickerDocument(media.document) || isAnimatedStickerDocument(media.document, true) || isLocationMessage(message)) || message.action != null && !(message.action instanceof TLRPC.TL_messageActionEmpty) || isForwardedMessage(message) || message.via_bot_id != 0 || message.id < 0) { return false; } - if (message.from_id instanceof TLRPC.TL_peerUser && message.from_id.user_id == message.peer_id.user_id && message.from_id.user_id == UserConfig.getInstance(currentAccount).getClientUserId() && !isLiveLocationMessage(message) && !(getMedia(message) instanceof TLRPC.TL_messageMediaContact)) { + if (message.from_id instanceof TLRPC.TL_peerUser && message.from_id.user_id == message.peer_id.user_id && message.from_id.user_id == UserConfig.getInstance(currentAccount).getClientUserId() && !isLiveLocationMessage(message) && !(media instanceof TLRPC.TL_messageMediaContact)) { return true; } if (chat == null && message.peer_id.channel_id != 0) { @@ -9309,7 +9437,14 @@ public static boolean canEditMessage(int currentAccount, TLRPC.Message message, return false; } } - if (getMedia(message) != null && !(getMedia(message) instanceof TLRPC.TL_messageMediaEmpty) && !(getMedia(message) instanceof TLRPC.TL_messageMediaPhoto) && !(getMedia(message) instanceof TLRPC.TL_messageMediaDocument) && !(getMedia(message) instanceof TLRPC.TL_messageMediaWebPage)) { + if ( + media != null && + !(media instanceof TLRPC.TL_messageMediaEmpty) && + !(media instanceof TLRPC.TL_messageMediaPhoto) && + !(media instanceof TLRPC.TL_messageMediaDocument) && + !(media instanceof TLRPC.TL_messageMediaWebPage) && + !(media instanceof TLRPC.TL_messageMediaPaidMedia) + ) { return false; } if (ChatObject.isChannel(chat) && !chat.megagroup && (chat.creator || chat.admin_rights != null && chat.admin_rights.edit_messages)) { @@ -9322,18 +9457,22 @@ public static boolean canEditMessage(int currentAccount, TLRPC.Message message, return false; } if (message.peer_id.channel_id == 0) { - return (message.out || message.from_id instanceof TLRPC.TL_peerUser && message.from_id.user_id == UserConfig.getInstance(currentAccount).getClientUserId()) && (getMedia(message) instanceof TLRPC.TL_messageMediaPhoto || - getMedia(message) instanceof TLRPC.TL_messageMediaDocument && !isStickerMessage(message) && !isAnimatedStickerMessage(message) || - getMedia(message) instanceof TLRPC.TL_messageMediaEmpty || - getMedia(message) instanceof TLRPC.TL_messageMediaWebPage || - getMedia(message) == null); + return (message.out || message.from_id instanceof TLRPC.TL_peerUser && message.from_id.user_id == UserConfig.getInstance(currentAccount).getClientUserId()) && ( + media instanceof TLRPC.TL_messageMediaPhoto || + media instanceof TLRPC.TL_messageMediaDocument && !isStickerMessage(message) && !isAnimatedStickerMessage(message) || + media instanceof TLRPC.TL_messageMediaEmpty || + media instanceof TLRPC.TL_messageMediaWebPage || + media instanceof TLRPC.TL_messageMediaPaidMedia || + media == null); } if (chat != null && chat.megagroup && message.out || chat != null && !chat.megagroup && (chat.creator || chat.admin_rights != null && (chat.admin_rights.edit_messages || message.out && chat.admin_rights.post_messages)) && message.post) { - if (getMedia(message) instanceof TLRPC.TL_messageMediaPhoto || - getMedia(message) instanceof TLRPC.TL_messageMediaDocument && !isStickerMessage(message) && !isAnimatedStickerMessage(message) || - getMedia(message) instanceof TLRPC.TL_messageMediaEmpty || - getMedia(message) instanceof TLRPC.TL_messageMediaWebPage || - getMedia(message) == null) { + if (media instanceof TLRPC.TL_messageMediaPhoto || + media instanceof TLRPC.TL_messageMediaDocument && !isStickerMessage(message) && !isAnimatedStickerMessage(message) || + media instanceof TLRPC.TL_messageMediaEmpty || + media instanceof TLRPC.TL_messageMediaWebPage || + media instanceof TLRPC.TL_messageMediaPaidMedia || + media == null + ) { return true; } } @@ -9515,7 +9654,7 @@ public void checkMediaExistance(boolean useFileDatabaseQueue) { attachPathExists = false; mediaExists = false; if (type == TYPE_EXTENDED_MEDIA_PREVIEW) { - TLRPC.TL_messageExtendedMediaPreview preview = (TLRPC.TL_messageExtendedMediaPreview) messageOwner.media.extended_media; + TLRPC.TL_messageExtendedMediaPreview preview = (TLRPC.TL_messageExtendedMediaPreview) messageOwner.media.extended_media.get(0); if (preview.thumb != null) { File file = FileLoader.getInstance(currentAccount).getPathToAttach(preview.thumb, useFileDatabaseQueue); if (!mediaExists) { @@ -10081,12 +10220,12 @@ public static CharSequence peerNameWithIcon(int currentAccount, long did, boolea if (did >= 0) { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); if (user != null) { - return UserObject.getUserName(user); + return AndroidUtilities.removeDiacritics(UserObject.getUserName(user)); } } else { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); if (chat != null) { - return new SpannableStringBuilder(ChatObject.isChannelAndNotMegaGroup(chat) ? channelSpan() : groupSpan()).append(" ").append(chat.title); + return new SpannableStringBuilder(ChatObject.isChannelAndNotMegaGroup(chat) ? channelSpan() : groupSpan()).append(" ").append(AndroidUtilities.removeDiacritics(chat.title)); } } return ""; @@ -10477,4 +10616,8 @@ public boolean hasEntitiesFromServer() { return false; } + public boolean isAlbumSingle() { + return getMedia(this) instanceof TLRPC.TL_messageMediaPaidMedia; + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java index aac9077d6b..81417e17e0 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/MessagesController.java @@ -81,6 +81,7 @@ import org.telegram.ui.PremiumPreviewFragment; import org.telegram.ui.ProfileActivity; import org.telegram.ui.SecretMediaViewer; +import org.telegram.ui.Stars.BotStarsController; import org.telegram.ui.Stars.StarsController; import org.telegram.ui.Stories.StoriesController; import org.telegram.ui.TopicsFragment; @@ -541,6 +542,7 @@ protected boolean useCache(Integer arguments) { public int recommendedChannelsLimitPremium; public int boostsChannelLevelMax; public int channelRestrictSponsoredLevelMin; + public Set webAppAllowedProtocols; public int channelsLimitDefault; public int channelsLimitPremium; @@ -609,6 +611,8 @@ protected boolean useCache(Integer arguments) { public boolean showAnnualPerMonth = false; public boolean canEditFactcheck; public int factcheckLengthLimit; + public long starsRevenueWithdrawalMin; + public long starsPaidPostAmountMax; public int savedDialogsPinnedLimitDefault; public int savedDialogsPinnedLimitPremium; @@ -1544,11 +1548,14 @@ public MessagesController(int num) { newNoncontactPeersRequirePremiumWithoutOwnpremium = mainPreferences.getBoolean("newNoncontactPeersRequirePremiumWithoutOwnpremium", false); reactionsUniqMax = mainPreferences.getInt("reactionsUniqMax", 11); premiumManageSubscriptionUrl = mainPreferences.getString("premiumManageSubscriptionUrl", ApplicationLoader.isStandaloneBuild() ? "https://t.me/premiumbot?start=status" : "https://play.google.com/store/account/subscriptions?sku=telegram_premium&package=org.telegram.messenger"); - androidDisableRoundCamera2 = mainPreferences.getBoolean("androidDisableRoundCamera2", false); + androidDisableRoundCamera2 = mainPreferences.getBoolean("androidDisableRoundCamera2", true); storiesPinnedToTopCountMax = mainPreferences.getInt("storiesPinnedToTopCountMax", 3); showAnnualPerMonth = mainPreferences.getBoolean("showAnnualPerMonth", false); canEditFactcheck = mainPreferences.getBoolean("canEditFactcheck", false); factcheckLengthLimit = mainPreferences.getInt("factcheckLengthLimit", 1024); + starsRevenueWithdrawalMin = mainPreferences.getLong("starsRevenueWithdrawalMin", 1000); + starsPaidPostAmountMax = mainPreferences.getLong("starsPaidPostAmountMax", 10_000); + webAppAllowedProtocols = mainPreferences.getStringSet("webAppAllowedProtocols", new HashSet<>(Arrays.asList("http", "https"))); scheduleTranscriptionUpdate(); BuildVars.GOOGLE_AUTH_CLIENT_ID = mainPreferences.getString("googleAuthClientId", BuildVars.GOOGLE_AUTH_CLIENT_ID); if (mainPreferences.contains("dcDomainName2")) { @@ -4122,6 +4129,47 @@ private void applyAppConfig(TLRPC.TL_jsonObject object) { } break; } + case "stars_revenue_withdrawal_min": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value; + if ((long) num.value != starsRevenueWithdrawalMin) { + starsRevenueWithdrawalMin = (long) num.value; + editor.putLong("starsRevenueWithdrawalMin", starsRevenueWithdrawalMin); + changed = true; + } + } + break; + } + case "stars_paid_post_amount_max": { + if (value.value instanceof TLRPC.TL_jsonNumber) { + TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value; + if ((long) num.value != starsPaidPostAmountMax) { + starsPaidPostAmountMax = (long) num.value; + editor.putLong("starsPaidPostAmountMax", starsPaidPostAmountMax); + changed = true; + } + } + break; + } + case "web_app_allowed_protocols": { + HashSet newProtocols = new HashSet<>(); + if (value.value instanceof TLRPC.TL_jsonArray) { + TLRPC.TL_jsonArray array = (TLRPC.TL_jsonArray) value.value; + for (int b = 0, N2 = array.value.size(); b < N2; b++) { + TLRPC.JSONValue val = array.value.get(b); + if (val instanceof TLRPC.TL_jsonString) { + TLRPC.TL_jsonString string = (TLRPC.TL_jsonString) val; + newProtocols.add(string.value.toLowerCase()); + } + } + } + if (!webAppAllowedProtocols.equals(newProtocols)) { + webAppAllowedProtocols = newProtocols; + editor.putStringSet("webAppAllowedProtocols", webAppAllowedProtocols); + changed = true; + } + break; + } } } @@ -5068,13 +5116,13 @@ public String getPeerName(long dialogId, boolean firstName) { if (dialogId >= 0) { TLRPC.User user = getUser(dialogId); if (firstName) { - return UserObject.getFirstName(user, true); + return AndroidUtilities.removeDiacritics(UserObject.getFirstName(user, true)); } else { - return UserObject.getUserName(user); + return AndroidUtilities.removeDiacritics(UserObject.getUserName(user)); } } else { TLRPC.Chat chat = getChat(-dialogId); - return chat == null ? "" : chat.title; + return AndroidUtilities.removeDiacritics(chat == null ? "" : chat.title); } } @@ -9573,9 +9621,9 @@ private String getUserNameForTyping(TLRPC.User user) { return ""; } if (user.first_name != null && user.first_name.length() > 0) { - return user.first_name; + return AndroidUtilities.removeDiacritics(user.first_name); } else if (user.last_name != null && user.last_name.length() > 0) { - return user.last_name; + return AndroidUtilities.removeDiacritics(user.last_name); } return ""; } @@ -17385,10 +17433,10 @@ public boolean processUpdateArray(ArrayList updates, ArrayList(extendedMedia.extended_media); if (messagesArr == null) { messagesArr = new ArrayList<>(); } @@ -17585,6 +17633,8 @@ public boolean processUpdateArray(ArrayList updates, ArrayList updates, ArrayList updates, ArrayList { if (response != null) { TLRPC.Updates updates1 = (TLRPC.Updates) response; @@ -18144,6 +18196,7 @@ public boolean processUpdateArray(ArrayList updates, ArrayList updates, ArrayList 0 AND m.send_state = 3) ORDER BY m.mid DESC LIMIT " + count); while (cursor.next()) { @@ -8001,7 +8002,11 @@ public void getUnsentMessages(int count) { message.seq_in = cursor.intValue(7); message.seq_out = cursor.intValue(8); message.ttl = cursor.intValue(9); - messages.add(message); + if (message.media instanceof TLRPC.TL_messageMediaPaidMedia) { + toDelete.add(message); // TODO: actually send again + } else { + messages.add(message); + } messageHashMap.put(message.id, message); if (DialogObject.isEncryptedDialog(message.dialog_id)) { @@ -8030,6 +8035,12 @@ public void getUnsentMessages(int count) { cursor.dispose(); cursor = null; + if (!toDelete.isEmpty()) { + for (TLRPC.Message msg : toDelete) { + database.executeFast("DELETE FROM messages_v2 WHERE uid = " + msg.dialog_id + " AND mid = " + msg.id).stepThis().dispose(); + } + } + cursor = database.queryFinalized("SELECT m.data, m.send_state, m.mid, m.date, r.random_id, m.uid, m.ttl FROM scheduled_messages_v2 as m LEFT JOIN randoms_v2 as r ON r.mid = m.mid AND r.uid = m.uid WHERE (m.mid < 0 AND m.send_state = 1) OR (m.mid > 0 AND m.send_state = 3) ORDER BY date ASC"); while (cursor.next()) { NativeByteBuffer data = cursor.byteBufferValue(0); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java index d162343ad1..cc9a314263 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationCenter.java @@ -253,6 +253,9 @@ public class NotificationCenter { public static final int starBalanceUpdated = totalEvents++; public static final int starTransactionsLoaded = totalEvents++; public static final int factCheckLoaded = totalEvents++; + public static final int botStarsUpdated = totalEvents++; + public static final int botStarsTransactionsLoaded = totalEvents++; + public static final int channelStarsUpdated = totalEvents++; //global public static final int pushMessagesUpdated = totalEvents++; diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java index 3b01efaba1..a86e87b096 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/NotificationsController.java @@ -73,6 +73,7 @@ import org.telegram.ui.Components.spoilers.SpoilerEffect; import org.telegram.ui.LaunchActivity; import org.telegram.ui.PopupNotificationActivity; +import org.telegram.ui.Stars.StarsIntroActivity; import org.telegram.ui.Stories.recorder.StoryEntry; import java.io.File; @@ -2150,6 +2151,21 @@ private String getShortStringForMessage(MessageObject messageObject, String[] us } else { return LocaleController.getString("Message", R.string.Message); } + } else if (messageObject.type == MessageObject.TYPE_PAID_MEDIA && MessageObject.getMedia(messageObject) instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) MessageObject.getMedia(messageObject); + final int count = paidMedia.extended_media.size(); + boolean video = false; + for (int i = 0; i < count; ++i) { + TLRPC.MessageExtendedMedia emedia = paidMedia.extended_media.get(i); + if (emedia instanceof TLRPC.TL_messageExtendedMedia) { + video = ((TLRPC.TL_messageExtendedMedia) emedia).media instanceof TLRPC.TL_messageMediaDocument && + MessageObject.isVideoDocument(((TLRPC.TL_messageExtendedMedia) emedia).media.document); + } else if (emedia instanceof TLRPC.TL_messageExtendedMediaPreview) { + video = (((TLRPC.TL_messageExtendedMediaPreview) emedia).flags & 4) != 0; + } + if (video) break; + } + return LocaleController.formatString(R.string.AttachPaidMedia, count == 1 ? LocaleController.getString(video ? R.string.AttachVideo : R.string.AttachPhoto) : LocaleController.formatPluralString(video ? "Media" : "Photos", count)); } else if (messageObject.isVoiceOnce()) { return LocaleController.getString(R.string.AttachOnceAudio); } else if (messageObject.isRoundOnce()) { @@ -2787,6 +2803,9 @@ private String getStringForMessage(MessageObject messageObject, boolean shortMes } else { msg = LocaleController.formatString("ChannelMessageNoText", R.string.ChannelMessageNoText, name); } + } else if (messageObject.type == MessageObject.TYPE_PAID_MEDIA && MessageObject.getMedia(messageObject) instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) MessageObject.getMedia(messageObject); + msg = LocaleController.formatPluralString("NotificationChannelMessagePaidMedia", (int) paidMedia.stars_amount, chat.title); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto) { if (!shortMessage && Build.VERSION.SDK_INT >= 19 && !TextUtils.isEmpty(messageObject.messageOwner.message)) { msg = LocaleController.formatString("NotificationMessageText", R.string.NotificationMessageText, name, "\uD83D\uDDBC " + messageObject.messageOwner.message); @@ -2862,6 +2881,9 @@ private String getStringForMessage(MessageObject messageObject, boolean shortMes } else { msg = LocaleController.formatString("NotificationMessageGroupNoText", R.string.NotificationMessageGroupNoText, name, chat.title); } + } else if (messageObject.type == MessageObject.TYPE_PAID_MEDIA && MessageObject.getMedia(messageObject) instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) MessageObject.getMedia(messageObject); + msg = LocaleController.formatPluralString("NotificationChatMessagePaidMedia", (int) paidMedia.stars_amount, name, chat.title); } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPhoto) { if (!shortMessage && Build.VERSION.SDK_INT >= 19 && !TextUtils.isEmpty(messageObject.messageOwner.message)) { msg = LocaleController.formatString("NotificationMessageGroupText", R.string.NotificationMessageGroupText, name, chat.title, "\uD83D\uDDBC " + messageObject.messageOwner.message); @@ -2936,6 +2958,9 @@ private String getStringForMessage(MessageObject messageObject, boolean shortMes } if (ChatObject.isChannel(chat) && !chat.megagroup) { msg = LocaleController.formatString("ChannelMessageNoText", R.string.ChannelMessageNoText, name); + } else if (messageObject.type == MessageObject.TYPE_PAID_MEDIA && MessageObject.getMedia(messageObject) instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) MessageObject.getMedia(messageObject); + msg = LocaleController.formatPluralString("NotificationMessagePaidMedia", (int) paidMedia.stars_amount, name); } else { msg = LocaleController.formatString("NotificationMessageGroupNoText", R.string.NotificationMessageGroupNoText, name, chat.title); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java b/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java index 41be2f1ea0..6a07167482 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java @@ -215,6 +215,9 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon countDownLatch.countDown(); return; } + if (BuildVars.LOGS_ENABLED) { + FileLog.d(tag + " " + loc_key); + } switch (loc_key) { case "DC_UPDATE": { int dc = custom.getInt("dc"); @@ -495,6 +498,36 @@ public static void processRemoteMessage(@PushType int pushType, String data, lon message1 = args[1]; break; } + case "MESSAGE_PAID_MEDIA": { + int stars = Integer.parseInt(args[1]); + messageText = LocaleController.formatPluralString("NotificationMessagePaidMedia", stars, args[0]); + message1 = LocaleController.formatPluralString("NotificationPaidMedia", stars); + break; + } + case "CHANNEL_MESSAGE_PAID_MEDIA": { + int stars = Integer.parseInt(args[1]); + messageText = LocaleController.formatPluralString("NotificationChannelMessagePaidMedia", stars, args[0]); + message1 = LocaleController.formatPluralString("NotificationPaidMedia", stars); + break; + } + case "CHAT_MESSAGE_PAID_MEDIA": { + int stars = Integer.parseInt(args[2]); + messageText = LocaleController.formatPluralString("NotificationChatMessagePaidMedia", stars, args[0], args[1]); + message1 = LocaleController.formatPluralString("NotificationPaidMedia", stars); + break; + } + case "PINNED_PAID_MEDIA": { + int stars = Integer.parseInt(args[1]); + messageText = LocaleController.formatPluralString("NotificationPinnedPaidMedia", stars, args[0]); + message1 = LocaleController.formatPluralString("NotificationPinnedPaidMedia", stars, args[0]); + break; + } + case "CHAT_REACT_PAID_MEDIA": { + int stars = Integer.parseInt(args[1]); + messageText = LocaleController.formatPluralString("NotificationPinnedPaidMedia", stars, args[0]); + message1 = LocaleController.formatPluralString("NotificationPinnedPaidMedia", stars, args[0]); + break; + } case "MESSAGE_NOTEXT": { messageText = LocaleController.formatString("NotificationMessageNoText", R.string.NotificationMessageNoText, args[0]); message1 = LocaleController.getString("Message", R.string.Message); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java index 0800e5add2..36c543a244 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SendMessagesHelper.java @@ -34,6 +34,7 @@ import android.text.Spannable; import android.text.TextUtils; import android.util.Base64; +import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; import android.view.Gravity; @@ -89,6 +90,8 @@ import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -599,6 +602,7 @@ public static class SendingMediaInfo { public boolean updateStickersOrder; public boolean hasMediaSpoilers; public TLRPC.VideoSize emojiMarkup; + public long stars; } @SuppressLint("MissingPermission") @@ -752,7 +756,25 @@ protected class DelayedMessage { public VideoEditedInfo videoEditedInfo; public boolean performMediaUpload; - public boolean retriedToSend; + private boolean retriedToSend; + public boolean[] retriedToSendArray; + + public boolean getRetriedToSend(int index) { + if (index < 0 || retriedToSendArray == null || index >= retriedToSendArray.length) { + return retriedToSend; + } else { + return retriedToSendArray[index]; + } + } + + public void setRetriedToSend(int index, boolean value) { + if (index < 0) { + retriedToSend = value; + } else { + if (retriedToSendArray == null) retriedToSendArray = new boolean[messageObjects.size()]; + retriedToSendArray[index] = value; + } + } public int topMessageId; @@ -770,6 +792,7 @@ protected class DelayedMessage { public long groupId; public int finalGroupMessage; public boolean scheduled; + public boolean paidMedia; public Object parentObject; @@ -830,6 +853,8 @@ public void sendDelayedRequests() { getSecretChatHelper().performSendEncryptedRequest((TLRPC.TL_messages_sendEncryptedMultiMedia) request.request, this); } else if (request.request instanceof TLRPC.TL_messages_sendMultiMedia) { performSendMessageRequestMulti((TLRPC.TL_messages_sendMultiMedia) request.request, request.msgObjs, request.originalPaths, request.parentObjects, request.delayedMessage, request.scheduled); + } else if (request.request instanceof TLRPC.TL_messages_sendMedia && ((TLRPC.TL_messages_sendMedia) request.request).media instanceof TLRPC.TL_inputMediaPaidMedia) { + performSendMessageRequestMulti((TLRPC.TL_messages_sendMedia) request.request, request.msgObjs, request.originalPaths, request.parentObjects, request.delayedMessage, request.scheduled); } else { performSendMessageRequest(request.request, request.msgObj, request.originalPath, request.delayedMessage, request.parentObject, null, request.scheduled); } @@ -951,6 +976,13 @@ public void didReceivedNotification(int id, int account, final Object... args) { TLRPC.InputMedia media = null; if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia) { media = ((TLRPC.TL_messages_sendMedia) message.sendRequest).media; + if (media instanceof TLRPC.TL_inputMediaPaidMedia) { + if (message.extraHashMap == null) { + media = ((TLRPC.TL_inputMediaPaidMedia) media).extended_media.get(0); + } else { + media = (TLRPC.InputMedia) message.extraHashMap.get(location); + } + } } else if (message.sendRequest instanceof TLRPC.TL_messages_editMessage) { media = ((TLRPC.TL_messages_editMessage) message.sendRequest).media; } else if (message.sendRequest instanceof TLRPC.TL_messages_sendMultiMedia) { @@ -1133,7 +1165,10 @@ public void didReceivedNotification(int id, int account, final Object... args) { if (obj == messageObject) { message.obj.shouldRemoveVideoEditedInfo = true; obj.messageOwner.params.remove("ve"); - obj.messageOwner.media.document.size = finalSize; + TLRPC.Document document = message.obj.getDocument(); + if (document != null) { + document.size = finalSize; + } ArrayList messages = new ArrayList<>(); messages.add(obj.messageOwner); @@ -1145,6 +1180,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { } else if (obj.scheduled) { mode = ChatActivity.MODE_SCHEDULED; } + if (message.paidMedia && b != 0) break; getMessagesStorage().putMessages(messages, false, true, false, 0, mode, threadMessageId); break; } @@ -1152,7 +1188,10 @@ public void didReceivedNotification(int id, int account, final Object... args) { } else if (message.obj == messageObject) { message.obj.shouldRemoveVideoEditedInfo = true; message.obj.messageOwner.params.remove("ve"); - message.obj.messageOwner.media.document.size = finalSize; + TLRPC.Document document = message.obj.getDocument(); + if (document != null) { + document.size = finalSize; + } ArrayList messages = new ArrayList<>(); messages.add(message.obj.messageOwner); @@ -1357,6 +1396,31 @@ private void revertEditingMessageObject(MessageObject object) { public void cancelSendingMessage(MessageObject object) { ArrayList arrayList = new ArrayList<>(); arrayList.add(object); + if (object != null && object.type == MessageObject.TYPE_PAID_MEDIA) { + DelayedMessage msg = null; + for (HashMap.Entry> entry : delayedMessages.entrySet()) { + ArrayList messages = entry.getValue(); + for (int a = 0; a < messages.size(); a++) { + DelayedMessage message = messages.get(a); + if (message.type == 4) { + for (int b = 0; b < message.messageObjects.size(); b++) { + MessageObject messageObject = message.messageObjects.get(b); + if (messageObject.getId() == object.getId()) { + msg = message; + break; + } + } + } + if (msg != null) { + break; + } + } + } + if (msg != null) { + arrayList.clear(); + arrayList.addAll(msg.messageObjects); + } + } cancelSendingMessage(arrayList); } @@ -1405,9 +1469,12 @@ public void cancelSendingMessage(ArrayList objects) { if (!message.parentObjects.isEmpty()) { message.parentObjects.remove(index); } - if (message.sendRequest != null) { + if (message.sendRequest instanceof TLRPC.TL_messages_sendMultiMedia) { TLRPC.TL_messages_sendMultiMedia request = (TLRPC.TL_messages_sendMultiMedia) message.sendRequest; request.multi_media.remove(index); + } else if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia && ((TLRPC.TL_messages_sendMedia) message.sendRequest).media instanceof TLRPC.TL_inputMediaPaidMedia) { + TLRPC.TL_messages_sendMedia request = (TLRPC.TL_messages_sendMedia) message.sendRequest; + ((TLRPC.TL_inputMediaPaidMedia) request.media).extended_media.remove(index); } else { TLRPC.TL_messages_sendEncryptedMultiMedia request = (TLRPC.TL_messages_sendEncryptedMultiMedia) message.sendEncryptedRequest; request.messages.remove(index); @@ -2584,6 +2651,9 @@ public void editMessage(MessageObject messageObject, TLRPC.TL_photo photo, Video } else if (!TextUtils.equals(oldMessge, newMsg.message)) { newMsg.flags &=~ 128; } + if (messageObject.messageOwner != null && messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia) { + messageObject.generateCaption(); + } } else { if (messageObject.editingMessageEntities != null) { newMsg.entities = messageObject.editingMessageEntities; @@ -3175,7 +3245,7 @@ public void sendCallback(final boolean cache, final MessageObject messageObject, } else if (button instanceof TLRPC.TL_keyboardButtonBuy) { if (response instanceof TLRPC.TL_payments_paymentFormStars) { TLRPC.InputInvoice inputInvoice = ((TLRPC.TL_payments_getPaymentForm) request[0]).invoice; - StarsController.getInstance(currentAccount).openPaymentForm(inputInvoice, (TLRPC.TL_payments_paymentFormStars) response, () -> { + StarsController.getInstance(currentAccount).openPaymentForm(messageObject, inputInvoice, (TLRPC.TL_payments_paymentFormStars) response, () -> { waitingForCallback.remove(key); finalKeys.remove(key); }, status -> {}); @@ -3184,7 +3254,7 @@ public void sendCallback(final boolean cache, final MessageObject messageObject, getMessagesController().putUsers(form.users, false); parentFragment.presentFragment(new PaymentFormActivity(form, messageObject, parentFragment)); } else if (response instanceof TLRPC.TL_payments_paymentReceiptStars) { - StarsIntroActivity.showTransactionSheet(LaunchActivity.instance != null ? LaunchActivity.instance : ApplicationLoader.applicationContext, currentAccount, (TLRPC.TL_payments_paymentReceiptStars) response, null); + StarsIntroActivity.showTransactionSheet(LaunchActivity.instance != null ? LaunchActivity.instance : ApplicationLoader.applicationContext, false, currentAccount, (TLRPC.TL_payments_paymentReceiptStars) response, null); } else if (response instanceof TLRPC.PaymentReceipt) { parentFragment.presentFragment(new PaymentFormActivity((TLRPC.PaymentReceipt) response)); } @@ -3486,6 +3556,7 @@ public void sendMessage(SendMessageParams sendMessageParams) { boolean invert_media = sendMessageParams.invert_media; String quick_reply_shortcut = sendMessageParams.quick_reply_shortcut; int quick_reply_shortcut_id = sendMessageParams.quick_reply_shortcut_id; + long stars = sendMessageParams.stars; if (user != null && user.phone == null) { return; @@ -3711,6 +3782,11 @@ public void sendMessage(SendMessageParams sendMessageParams) { TLRPC.FileLocation location1 = photo.sizes.get(photo.sizes.size() - 1).location; newMsg.attachPath = FileLoader.getInstance(currentAccount).getPathToAttach(location1, true).toString(); } + if (caption != null) { + newMsg.message = caption; + } else if (newMsg.message == null) { + newMsg.message = ""; + } } else if (game != null) { newMsg = new TLRPC.TL_message(); newMsg.media = new TLRPC.TL_messageMediaGame(); @@ -4130,6 +4206,17 @@ public void sendMessage(SendMessageParams sendMessageParams) { isFinalGroupMedia = params.get("final") != null; } + if (stars > 0) { + TLRPC.MessageMedia media = newMsg.media; + TLRPC.TL_messageMediaPaidMedia paidMedia = new TLRPC.TL_messageMediaPaidMedia(); + paidMedia.stars_amount = stars; + TLRPC.TL_messageExtendedMedia extMedia = new TLRPC.TL_messageExtendedMedia(); + extMedia.attachPath = newMsg.attachPath; + extMedia.media = media; + paidMedia.extended_media.add(extMedia); + newMsg.media = paidMedia; + } + MessageObject reply = replyToMsg; if (replyToTopMsg != null && replyToTopMsg == reply && replyToTopMsg.getId() == 1 || destroyReply) { reply = null; @@ -4595,57 +4682,120 @@ public void sendMessage(SendMessageParams sendMessageParams) { inputMediaStory.peer = MessagesController.getInstance(currentAccount).getInputPeer(sendingStory.dialogId); inputMedia = inputMediaStory; } + if (groupId == 0 && stars > 0 && inputMedia != null) { + TLRPC.TL_inputMediaPaidMedia inputPaidMedia = new TLRPC.TL_inputMediaPaidMedia(); + inputPaidMedia.stars_amount = stars; + inputPaidMedia.extended_media.add(inputMedia); + inputMedia = inputPaidMedia; + } TLObject reqSend; if (groupId != 0) { - TLRPC.TL_messages_sendMultiMedia request; + TLObject request; if (delayedMessage.sendRequest != null) { - request = (TLRPC.TL_messages_sendMultiMedia) delayedMessage.sendRequest; + request = delayedMessage.sendRequest; + } else if (stars > 0) { + TLRPC.TL_messages_sendMedia req = new TLRPC.TL_messages_sendMedia(); + req.peer = sendToPeer; + req.silent = newMsg.silent; + req.message = caption; + if (entities != null && !entities.isEmpty()) { + req.entities = entities; + req.flags |= 8; + } + + if (newMsg.replyStory != null) { + req.flags |= 1; + req.reply_to = createReplyInput(replyToStoryItem); + } else if (newMsg.reply_to instanceof TLRPC.TL_messageReplyHeader) { + req.flags |= 1; + req.reply_to = createReplyInput((TLRPC.TL_messageReplyHeader) newMsg.reply_to); + } + if (scheduleDate != 0) { + req.schedule_date = scheduleDate; + req.flags |= 1024; + } + if (newMsg.quick_reply_shortcut != null) { + req.quick_reply_shortcut = newMsg.quick_reply_shortcut; + req.flags |= 131072; + } + if (newMsg.effect != 0) { + req.flags |= 262144; + req.effect = newMsg.effect; + } + req.invert_media = sendMessageParams.invert_media; + + TLRPC.TL_inputMediaPaidMedia media = new TLRPC.TL_inputMediaPaidMedia(); + media.stars_amount = stars; + req.random_id = newMsg.random_id; + req.media = media; + + request = req; + delayedMessage.paidMedia = true; + delayedMessage.sendRequest = request; } else { - request = new TLRPC.TL_messages_sendMultiMedia(); - request.peer = sendToPeer; - request.silent = newMsg.silent; + TLRPC.TL_messages_sendMultiMedia req = new TLRPC.TL_messages_sendMultiMedia(); + req.peer = sendToPeer; + req.silent = newMsg.silent; if (newMsg.replyStory != null) { - request.flags |= 1; - request.reply_to = createReplyInput(replyToStoryItem); + req.flags |= 1; + req.reply_to = createReplyInput(replyToStoryItem); } else if (newMsg.reply_to instanceof TLRPC.TL_messageReplyHeader) { - request.flags |= 1; - request.reply_to = createReplyInput((TLRPC.TL_messageReplyHeader) newMsg.reply_to); + req.flags |= 1; + req.reply_to = createReplyInput((TLRPC.TL_messageReplyHeader) newMsg.reply_to); } if (scheduleDate != 0) { - request.schedule_date = scheduleDate; - request.flags |= 1024; + req.schedule_date = scheduleDate; + req.flags |= 1024; } if (newMsg.quick_reply_shortcut != null) { - request.quick_reply_shortcut = newMsg.quick_reply_shortcut; - request.flags |= 131072; + req.quick_reply_shortcut = newMsg.quick_reply_shortcut; + req.flags |= 131072; } if (newMsg.effect != 0) { - request.flags |= 262144; - request.effect = newMsg.effect; + req.flags |= 262144; + req.effect = newMsg.effect; } - request.invert_media = sendMessageParams.invert_media; + req.invert_media = sendMessageParams.invert_media; + request = req; delayedMessage.sendRequest = request; } + if (delayedMessage.paidMedia && !delayedMessage.messages.isEmpty()) { + TLRPC.Message firstMessage = delayedMessage.messages.get(0); + TLRPC.MessageMedia media = newMsg.media; + if (media instanceof TLRPC.TL_messageMediaPaidMedia && !media.extended_media.isEmpty() && media.extended_media.get(0) instanceof TLRPC.TL_messageExtendedMedia) { + media = ((TLRPC.TL_messageExtendedMedia) media.extended_media.get(0)).media; + } + if (media != null && firstMessage.media instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageExtendedMedia extMedia = new TLRPC.TL_messageExtendedMedia(); + extMedia.attachPath = newMsg.attachPath; + extMedia.media = media; + firstMessage.media.extended_media.add(extMedia); + } + } delayedMessage.messageObjects.add(newMsgObj); + delayedMessage.messages.add(newMsg); delayedMessage.parentObjects.add(parentObject); delayedMessage.locations.add(delayedMessage.photoSize); delayedMessage.videoEditedInfos.add(delayedMessage.videoEditedInfo); delayedMessage.httpLocations.add(delayedMessage.httpLocation); delayedMessage.inputMedias.add(delayedMessage.inputUploadMedia); - delayedMessage.messages.add(newMsg); delayedMessage.originalPaths.add(originalPath); - TLRPC.TL_inputSingleMedia inputSingleMedia = new TLRPC.TL_inputSingleMedia(); - inputSingleMedia.random_id = newMsg.random_id; - inputSingleMedia.media = inputMedia; - inputSingleMedia.message = caption; - if (entities != null && !entities.isEmpty()) { - inputSingleMedia.entities = entities; - inputSingleMedia.flags |= 1; + if (request instanceof TLRPC.TL_messages_sendMultiMedia) { + TLRPC.TL_inputSingleMedia inputSingleMedia = new TLRPC.TL_inputSingleMedia(); + inputSingleMedia.random_id = newMsg.random_id; + inputSingleMedia.media = inputMedia; + inputSingleMedia.message = caption; + if (entities != null && !entities.isEmpty()) { + inputSingleMedia.entities = entities; + inputSingleMedia.flags |= 1; + } + ((TLRPC.TL_messages_sendMultiMedia) request).multi_media.add(inputSingleMedia); + } else if (request instanceof TLRPC.TL_messages_sendMedia && ((TLRPC.TL_messages_sendMedia) request).media instanceof TLRPC.TL_inputMediaPaidMedia) { + ((TLRPC.TL_inputMediaPaidMedia) ((TLRPC.TL_messages_sendMedia) request).media).extended_media.add(inputMedia); } - request.multi_media.add(inputSingleMedia); reqSend = request; } else { TLRPC.TL_messages_sendMedia request = new TLRPC.TL_messages_sendMedia(); @@ -5171,7 +5321,7 @@ private void performSendDelayedMessage(final DelayedMessage message, int index) } else if (message.type == 1) { if (message.videoEditedInfo != null && message.videoEditedInfo.needConvert()) { String location = message.obj.messageOwner.attachPath; - TLRPC.Document document = message.obj.getDocument(); + TLRPC.Document document = message.obj.getDocument(); // TODO: paid media if (location == null) { location = FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE) + "/" + document.id + "." + (message.videoEditedInfo.isSticker ? "webm" : "mp4"); } @@ -5208,6 +5358,9 @@ private void performSendDelayedMessage(final DelayedMessage message, int index) } else { media = ((TLRPC.TL_messages_editMessage) message.sendRequest).media; } + if (media instanceof TLRPC.TL_inputMediaPaidMedia && !((TLRPC.TL_inputMediaPaidMedia) media).extended_media.isEmpty()) { + media = ((TLRPC.TL_inputMediaPaidMedia) media).extended_media.get(0); + } if (media.file == null) { String location = message.obj.messageOwner.attachPath; TLRPC.Document document = message.obj.getDocument(); @@ -5339,10 +5492,16 @@ private void performSendDelayedMessage(final DelayedMessage message, int index) index = message.messageObjects.size() - 1; } MessageObject messageObject = message.messageObjects.get(index); - if (messageObject.getDocument() != null) { + TLRPC.Document document = messageObject.getDocument(); + if (document == null && MessageObject.getMedia(messageObject) instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) MessageObject.getMedia(messageObject); + TLRPC.MessageExtendedMedia emedia = index >= paidMedia.extended_media.size() ? null : paidMedia.extended_media.get(index); + TLRPC.MessageMedia media = emedia instanceof TLRPC.TL_messageExtendedMedia ? ((TLRPC.TL_messageExtendedMedia) emedia).media : null; + document = media == null ? null : media.document; + } + if (document != null) { if (message.videoEditedInfo != null) { String location = messageObject.messageOwner.attachPath; - TLRPC.Document document = messageObject.getDocument(); if (location == null) { location = FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE) + "/" + document.id + ".mp4"; } @@ -5358,15 +5517,22 @@ private void performSendDelayedMessage(final DelayedMessage message, int index) message.obj = messageObject; putToUploadingMessages(messageObject); } else { - TLRPC.Document document = messageObject.getDocument(); String documentLocation = messageObject.messageOwner.attachPath; if (documentLocation == null) { documentLocation = FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE) + "/" + document.id + ".mp4"; } if (message.sendRequest != null) { - TLRPC.TL_messages_sendMultiMedia request = (TLRPC.TL_messages_sendMultiMedia) message.sendRequest; - TLRPC.InputMedia media = request.multi_media.get(index).media; - if (media.file == null) { + TLRPC.InputMedia media = null; + if (message.sendRequest instanceof TLRPC.TL_messages_sendMultiMedia) { + TLRPC.TL_messages_sendMultiMedia request = (TLRPC.TL_messages_sendMultiMedia) message.sendRequest; + media = request.multi_media.get(index).media; + } else if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia) { + TLRPC.TL_messages_sendMedia request = (TLRPC.TL_messages_sendMedia) message.sendRequest; + if (request.media instanceof TLRPC.TL_inputMediaPaidMedia) { + media = ((TLRPC.TL_inputMediaPaidMedia) request.media).extended_media.get(index); + } + } + if (media != null && media.file == null) { putToDelayedMessages(documentLocation, message); message.extraHashMap.put(messageObject, documentLocation); message.extraHashMap.put(documentLocation, media); @@ -5417,9 +5583,11 @@ private void performSendDelayedMessage(final DelayedMessage message, int index) message.httpLocation = null; } else { TLObject inputMedia; - if (message.sendRequest != null) { + if (message.sendRequest instanceof TLRPC.TL_messages_sendMultiMedia) { TLRPC.TL_messages_sendMultiMedia request = (TLRPC.TL_messages_sendMultiMedia) message.sendRequest; inputMedia = request.multi_media.get(index).media; + } else if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia && ((TLRPC.TL_messages_sendMedia) message.sendRequest).media instanceof TLRPC.TL_inputMediaPaidMedia) { + inputMedia = ((TLRPC.TL_inputMediaPaidMedia) ((TLRPC.TL_messages_sendMedia) message.sendRequest).media).extended_media.get(index); } else { TLRPC.TL_messages_sendEncryptedMultiMedia request = (TLRPC.TL_messages_sendEncryptedMultiMedia) message.sendEncryptedRequest; inputMedia = request.files.get(index); @@ -5468,18 +5636,30 @@ private void performSendDelayedMessage(final DelayedMessage message, int index) private void uploadMultiMedia(final DelayedMessage message, final TLRPC.InputMedia inputMedia, final TLRPC.InputEncryptedFile inputEncryptedFile, String key) { if (inputMedia != null) { - TLRPC.TL_messages_sendMultiMedia multiMedia = (TLRPC.TL_messages_sendMultiMedia) message.sendRequest; - for (int a = 0; a < multiMedia.multi_media.size(); a++) { - if (multiMedia.multi_media.get(a).media == inputMedia) { - putToSendingMessages(message.messages.get(a), message.scheduled); - getNotificationCenter().postNotificationName(NotificationCenter.fileUploadProgressChanged, key, -1L, -1L, false); - break; - } - } - TLRPC.TL_messages_uploadMedia req = new TLRPC.TL_messages_uploadMedia(); req.media = inputMedia; - req.peer = ((TLRPC.TL_messages_sendMultiMedia) message.sendRequest).peer; + if (message.sendRequest instanceof TLRPC.TL_messages_sendMultiMedia) { + TLRPC.TL_messages_sendMultiMedia multiMedia = (TLRPC.TL_messages_sendMultiMedia) message.sendRequest; + req.peer = multiMedia.peer; + for (int a = 0; a < multiMedia.multi_media.size(); a++) { + if (multiMedia.multi_media.get(a).media == inputMedia) { + putToSendingMessages(message.messages.get(a), message.scheduled); + getNotificationCenter().postNotificationName(NotificationCenter.fileUploadProgressChanged, key, -1L, -1L, false); + break; + } + } + } else if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia && ((TLRPC.TL_messages_sendMedia) message.sendRequest).media instanceof TLRPC.TL_inputMediaPaidMedia) { + TLRPC.TL_messages_sendMedia sendMedia = (TLRPC.TL_messages_sendMedia) message.sendRequest; + req.peer = sendMedia.peer; + TLRPC.TL_inputMediaPaidMedia multiMedia = (TLRPC.TL_inputMediaPaidMedia) sendMedia.media; + for (int a = 0; a < multiMedia.extended_media.size(); a++) { + if (multiMedia.extended_media.get(a) == inputMedia) { + putToSendingMessages(message.messages.get(a), message.scheduled); + getNotificationCenter().postNotificationName(NotificationCenter.fileUploadProgressChanged, key, -1L, -1L, false); + break; + } + } + } getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { TLRPC.InputMedia newInputMedia = null; if (response != null) { @@ -5513,11 +5693,22 @@ private void uploadMultiMedia(final DelayedMessage message, final TLRPC.InputMed newInputMedia.ttl_seconds = inputMedia.ttl_seconds; newInputMedia.flags |= 1; } - TLRPC.TL_messages_sendMultiMedia req1 = (TLRPC.TL_messages_sendMultiMedia) message.sendRequest; - for (int a = 0; a < req1.multi_media.size(); a++) { - if (req1.multi_media.get(a).media == inputMedia) { - req1.multi_media.get(a).media = newInputMedia; - break; + if (message.sendRequest instanceof TLRPC.TL_messages_sendMultiMedia) { + TLRPC.TL_messages_sendMultiMedia req1 = (TLRPC.TL_messages_sendMultiMedia) message.sendRequest; + for (int a = 0; a < req1.multi_media.size(); a++) { + if (req1.multi_media.get(a).media == inputMedia) { + req1.multi_media.get(a).media = newInputMedia; + break; + } + } + } else if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia && ((TLRPC.TL_messages_sendMedia) message.sendRequest).media instanceof TLRPC.TL_inputMediaPaidMedia) { + TLRPC.TL_messages_sendMedia req1 = (TLRPC.TL_messages_sendMedia) message.sendRequest; + TLRPC.TL_inputMediaPaidMedia media = (TLRPC.TL_inputMediaPaidMedia) req1.media; + for (int a = 0; a < media.extended_media.size(); a++) { + if (media.extended_media.get(a) == inputMedia) { + media.extended_media.set(a, newInputMedia); + break; + } } } sendReadyToSendGroup(message, false, true); @@ -5569,8 +5760,18 @@ private void sendReadyToSendGroup(DelayedMessage message, boolean add, boolean c } else { mode = ChatActivity.MODE_DEFAULT; } - getMessagesStorage().putMessages(message.messages, false, true, false, 0, mode, 0); - getMessagesController().updateInterfaceWithMessages(message.peer, message.messageObjects, mode); + if (message.paidMedia) { + ArrayList firstMessageObject = new ArrayList<>(); + firstMessageObject.add(message.messageObjects.get(0)); + ArrayList firstMessage = new ArrayList<>(); + firstMessage.add(message.messages.get(0)); + + getMessagesStorage().putMessages(firstMessage, false, true, false, 0, mode, 0); + getMessagesController().updateInterfaceWithMessages(message.peer, firstMessageObject, mode); + } else { + getMessagesStorage().putMessages(message.messages, false, true, false, 0, mode, 0); + getMessagesController().updateInterfaceWithMessages(message.peer, message.messageObjects, mode); + } if (!message.scheduled) { getNotificationCenter().postNotificationName(NotificationCenter.dialogsNeedReload); } @@ -5590,6 +5791,32 @@ private void sendReadyToSendGroup(DelayedMessage message, boolean add, boolean c } } + if (check) { + DelayedMessage maxDelayedMessage = findMaxDelayedMessageForMessageId(message.finalGroupMessage, message.peer); + if (maxDelayedMessage != null) { + maxDelayedMessage.addDelayedRequest(message.sendRequest, message.messageObjects, message.originalPaths, message.parentObjects, message, message.scheduled); + if (message.requests != null) { + maxDelayedMessage.requests.addAll(message.requests); + } + if (BuildVars.DEBUG_VERSION) { + FileLog.d("has maxDelayedMessage, delay"); + } + return; + } + } + } else if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia && ((TLRPC.TL_messages_sendMedia) message.sendRequest).media instanceof TLRPC.TL_inputMediaPaidMedia) { + TLRPC.TL_messages_sendMedia request = (TLRPC.TL_messages_sendMedia) message.sendRequest; + TLRPC.TL_inputMediaPaidMedia media = (TLRPC.TL_inputMediaPaidMedia) request.media; + for (int a = 0; a < media.extended_media.size(); a++) { + TLRPC.InputMedia inputMedia = media.extended_media.get(a); + if (inputMedia instanceof TLRPC.TL_inputMediaUploadedPhoto || inputMedia instanceof TLRPC.TL_inputMediaUploadedDocument) { + if (BuildVars.DEBUG_VERSION) { + FileLog.d("multi media not ready"); + } + return; + } + } + if (check) { DelayedMessage maxDelayedMessage = findMaxDelayedMessageForMessageId(message.finalGroupMessage, message.peer); if (maxDelayedMessage != null) { @@ -5615,6 +5842,8 @@ private void sendReadyToSendGroup(DelayedMessage message, boolean add, boolean c if (message.sendRequest instanceof TLRPC.TL_messages_sendMultiMedia) { performSendMessageRequestMulti((TLRPC.TL_messages_sendMultiMedia) message.sendRequest, message.messageObjects, message.originalPaths, message.parentObjects, message, message.scheduled); + } else if (message.sendRequest instanceof TLRPC.TL_messages_sendMedia) { + performSendMessageRequestMulti((TLRPC.TL_messages_sendMedia) message.sendRequest, message.messageObjects, message.originalPaths, message.parentObjects, message, message.scheduled); } else { getSecretChatHelper().performSendEncryptedRequest((TLRPC.TL_messages_sendEncryptedMultiMedia) message.sendEncryptedRequest, message); } @@ -5742,39 +5971,71 @@ public boolean isUploadingMessageIdDialog(long did) { return uploadingMessagesIdDialogs.get(did, 0) > 0; } - protected void performSendMessageRequestMulti(final TLRPC.TL_messages_sendMultiMedia req, final ArrayList msgObjs, final ArrayList originalPaths, final ArrayList parentObjects, DelayedMessage delayedMessage, boolean scheduled) { + protected void performSendMessageRequestMulti(final TLObject request, final ArrayList msgObjs, final ArrayList originalPaths, final ArrayList parentObjects, DelayedMessage delayedMessage, boolean scheduled) { for (int a = 0, size = msgObjs.size(); a < size; a++) { putToSendingMessages(msgObjs.get(a).messageOwner, scheduled); } - getConnectionsManager().sendRequest(req, (response, error) -> { + getConnectionsManager().sendRequest(request, (response, error) -> { if (error != null && FileRefController.isFileRefError(error.text)) { + final int fileRefIndex = FileRefController.getFileRefErrorIndex(error.text); if (parentObjects != null) { ArrayList arrayList = new ArrayList<>(parentObjects); - getFileRefController().requestReference(arrayList, req, msgObjs, originalPaths, arrayList, delayedMessage, scheduled); + if (fileRefIndex >= 0) { + for (int i = 0; i < arrayList.size(); ++i) { + arrayList.set(i, fileRefIndex == i ? arrayList.get(i) : null); + } + } + getFileRefController().requestReference(arrayList, request, msgObjs, originalPaths, arrayList, delayedMessage, scheduled); return; - } else if (delayedMessage != null && !delayedMessage.retriedToSend) { - delayedMessage.retriedToSend = true; + } else if (delayedMessage != null && !delayedMessage.getRetriedToSend(fileRefIndex)) { + delayedMessage.setRetriedToSend(fileRefIndex, true); AndroidUtilities.runOnUIThread(() -> { boolean hasEmptyFile = false; - for (int a = 0, size = req.multi_media.size(); a < size; a++) { - if (delayedMessage.parentObjects.get(a) == null) { - continue; - } - removeFromSendingMessages(msgObjs.get(a).getId(), scheduled); - TLRPC.TL_inputSingleMedia request = req.multi_media.get(a); - if (request.media instanceof TLRPC.TL_inputMediaPhoto) { - request.media = delayedMessage.inputMedias.get(a); - } else if (request.media instanceof TLRPC.TL_inputMediaDocument) { - request.media = delayedMessage.inputMedias.get(a); + if (request instanceof TLRPC.TL_messages_sendMultiMedia) { + TLRPC.TL_messages_sendMultiMedia req = (TLRPC.TL_messages_sendMultiMedia) request; + for (int a = 0, size = req.multi_media.size(); a < size; a++) { + if (fileRefIndex >= 0 ? fileRefIndex != a : delayedMessage.parentObjects.get(a) == null) { + continue; + } + removeFromSendingMessages(msgObjs.get(a).getId(), scheduled); + TLRPC.TL_inputSingleMedia requestMedia = req.multi_media.get(a); + if (requestMedia.media instanceof TLRPC.TL_inputMediaPhoto) { + requestMedia.media = delayedMessage.inputMedias.get(a); + } else if (requestMedia.media instanceof TLRPC.TL_inputMediaDocument) { + requestMedia.media = delayedMessage.inputMedias.get(a); + } + delayedMessage.videoEditedInfo = delayedMessage.videoEditedInfos.get(a); + delayedMessage.httpLocation = delayedMessage.httpLocations.get(a); + delayedMessage.photoSize = delayedMessage.locations.get(a); + delayedMessage.performMediaUpload = true; + if (requestMedia.media.file == null || delayedMessage.photoSize != null) { + hasEmptyFile = true; + } + performSendDelayedMessage(delayedMessage, a); } - delayedMessage.videoEditedInfo = delayedMessage.videoEditedInfos.get(a); - delayedMessage.httpLocation = delayedMessage.httpLocations.get(a); - delayedMessage.photoSize = delayedMessage.locations.get(a); - delayedMessage.performMediaUpload = true; - if (request.media.file == null || delayedMessage.photoSize != null) { - hasEmptyFile = true; + } else if (request instanceof TLRPC.TL_messages_sendMedia) { + TLRPC.TL_messages_sendMedia req = (TLRPC.TL_messages_sendMedia) request; + TLRPC.TL_inputMediaPaidMedia paidMedia = (TLRPC.TL_inputMediaPaidMedia) req.media; + for (int a = 0, size = paidMedia.extended_media.size(); a < size; a++) { + if (fileRefIndex >= 0 ? fileRefIndex != a : delayedMessage.parentObjects.get(a) == null) { + continue; + } + removeFromSendingMessages(msgObjs.get(a).getId(), scheduled); + TLRPC.InputMedia requestMedia = paidMedia.extended_media.get(a); + if (requestMedia instanceof TLRPC.TL_inputMediaPhoto) { + paidMedia.extended_media.set(a, requestMedia = delayedMessage.inputMedias.get(a)); + } else if (requestMedia instanceof TLRPC.TL_inputMediaDocument) { + paidMedia.extended_media.set(a, requestMedia = delayedMessage.inputMedias.get(a)); + } + delayedMessage.videoEditedInfo = delayedMessage.videoEditedInfos.get(a); + delayedMessage.httpLocation = delayedMessage.httpLocations.get(a); + delayedMessage.photoSize = delayedMessage.locations.get(a); + delayedMessage.performMediaUpload = true; + if (requestMedia.file == null || delayedMessage.photoSize != null) { + hasEmptyFile = true; + } + performSendDelayedMessage(delayedMessage, a); } - performSendDelayedMessage(delayedMessage, a); } if (!hasEmptyFile) { for (int i = 0; i < msgObjs.size(); i++) { @@ -5886,7 +6147,11 @@ protected void performSendMessageRequestMulti(final TLRPC.TL_messages_sendMultiM msgObj.messageOwner.ttl_period = message.ttl_period; msgObj.messageOwner.flags |= 33554432; } - updateMediaPaths(msgObj, message, message.id, originalPath, false); + if (request instanceof TLRPC.TL_messages_sendMedia) { // paid media + updateMediaPaths(msgObjs.get(0), message, message.id, originalPaths, false, -1); + } else { + updateMediaPaths(msgObj, message, message.id, originalPath, false); + } existFlags = msgObj.getMediaExistanceFlags(); newMsgObj.id = message.id; newMsgObj.quick_reply_shortcut_id = message.quick_reply_shortcut_id; @@ -5936,7 +6201,7 @@ protected void performSendMessageRequestMulti(final TLRPC.TL_messages_sendMultiM } Utilities.stageQueue.postRunnable(() -> getMessagesController().processUpdates(updates, false)); } else { - AlertsCreator.processError(currentAccount, error, null, req); + AlertsCreator.processError(currentAccount, error, null, request); isSentError = true; } if (isSentError) { @@ -6299,8 +6564,19 @@ protected void performSendMessageRequest(final TLObject req, final MessageObject } private void updateMediaPaths(MessageObject newMsgObj, TLRPC.Message sentMessage, int newMsgId, String originalPath, boolean post) { + updateMediaPaths(newMsgObj, sentMessage, newMsgId, Collections.singletonList(originalPath), post, -1); + } + + private void updateMediaPaths(MessageObject newMsgObj, TLRPC.Message sentMessage, int newMsgId, List originalPaths, boolean post, int index) { TLRPC.Message newMsg = newMsgObj.messageOwner; + final String originalPath = originalPaths.isEmpty() || Math.max(0, index) >= originalPaths.size() ? null : originalPaths.get(Math.max(0, index)); + boolean singleAlbum = false; + TLRPC.MessageExtendedMedia sentEMedia = null; + TLRPC.MessageMedia sentMedia = sentMessage == null ? null : sentMessage.media; + TLRPC.MessageExtendedMedia newEMedia = null; + TLRPC.MessageMedia newMedia = newMsg == null ? null : newMsg.media; + TLRPC.PhotoSize strippedNew = null; if (newMsg.media != null) { TLRPC.PhotoSize strippedOld = null; @@ -6347,6 +6623,54 @@ private void updateMediaPaths(MessageObject newMsgObj, TLRPC.Message sentMessage } photoObject = newMsg.media.webpage.document; } + } else if (newMsg.media instanceof TLRPC.TL_messageMediaPaidMedia && sentMedia instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) newMsg.media; + TLRPC.TL_messageMediaPaidMedia sentPaidMedia = (TLRPC.TL_messageMediaPaidMedia) sentMedia; + if (paidMedia.extended_media.isEmpty() || sentPaidMedia.extended_media.isEmpty()) { + return; + } + if (index == -1) { + for (int i = 0; i < paidMedia.extended_media.size(); ++i) { + updateMediaPaths(newMsgObj, sentMessage, newMsgId, originalPaths, post, i); + }; + return; + } + singleAlbum = sentPaidMedia.extended_media.size() > 1; + if (index < 0 || index >= sentPaidMedia.extended_media.size()) { + return; + } + TLRPC.MessageExtendedMedia emedia2 = sentPaidMedia.extended_media.get(index); + if (emedia2 instanceof TLRPC.TL_messageExtendedMedia) { + TLRPC.TL_messageExtendedMedia eemedia2 = (TLRPC.TL_messageExtendedMedia) emedia2; + sentEMedia = eemedia2; + sentMedia = eemedia2.media; + } + + TLRPC.MessageExtendedMedia emedia = paidMedia.extended_media.get(index); + if (emedia instanceof TLRPC.TL_messageExtendedMedia) { + TLRPC.TL_messageExtendedMedia eemedia = (TLRPC.TL_messageExtendedMedia) emedia; + TLRPC.MessageMedia media = eemedia.media; + if (media.photo != null) { + strippedOld = FileLoader.getClosestPhotoSizeWithSize(media.photo.sizes, 40); + if (sentMedia != null && sentMedia.photo != null) { + strippedNew = FileLoader.getClosestPhotoSizeWithSize(sentMedia.photo.sizes, 40); + } else { + strippedNew = strippedOld; + } + photoObject = media.photo; + } else if (media.document != null) { + strippedOld = FileLoader.getClosestPhotoSizeWithSize(media.document.thumbs, 40); + if (sentMedia != null && sentMedia.document != null) { + strippedNew = FileLoader.getClosestPhotoSizeWithSize(sentMedia.document.thumbs, 40); + } else { + strippedNew = strippedOld; + } + photoObject = media.document; + } + + newEMedia = eemedia; + newMedia = media; + } } if (strippedNew instanceof TLRPC.TL_photoStrippedSize && strippedOld instanceof TLRPC.TL_photoStrippedSize) { String oldKey = "stripped" + FileRefController.getKeyForParentObject(newMsgObj); @@ -6362,22 +6686,22 @@ private void updateMediaPaths(MessageObject newMsgObj, TLRPC.Message sentMessage if (sentMessage == null) { return; } - if (sentMessage.media instanceof TLRPC.TL_messageMediaPhoto && sentMessage.media.photo != null && newMsg.media instanceof TLRPC.TL_messageMediaPhoto && newMsg.media.photo != null) { - if (sentMessage.media.ttl_seconds == 0 && !newMsgObj.scheduled) { - getMessagesStorage().putSentFile(originalPath, sentMessage.media.photo, 0, "sent_" + sentMessage.peer_id.channel_id + "_" + sentMessage.id + "_" + DialogObject.getPeerDialogId(sentMessage.peer_id) + "_" + MessageObject.TYPE_PHOTO + "_" + newMsgObj.getSize()); + if (sentMedia instanceof TLRPC.TL_messageMediaPhoto && sentMedia.photo != null && newMedia instanceof TLRPC.TL_messageMediaPhoto && newMedia.photo != null) { + if (sentMedia.ttl_seconds == 0 && !newMsgObj.scheduled) { + getMessagesStorage().putSentFile(originalPath, sentMedia.photo, 0, "sent_" + sentMessage.peer_id.channel_id + "_" + sentMessage.id + "_" + DialogObject.getPeerDialogId(sentMessage.peer_id) + "_" + MessageObject.TYPE_PHOTO + "_" + MessageObject.getMediaSize(newMedia)); } - if (newMsg.media.photo.sizes.size() == 1 && newMsg.media.photo.sizes.get(0).location instanceof TLRPC.TL_fileLocationUnavailable) { - newMsg.media.photo.sizes = sentMessage.media.photo.sizes; + if (newMedia.photo.sizes.size() == 1 && newMedia.photo.sizes.get(0).location instanceof TLRPC.TL_fileLocationUnavailable) { + newMedia.photo.sizes = sentMedia.photo.sizes; } else { - for (int b = 0; b < newMsg.media.photo.sizes.size(); b++) { - TLRPC.PhotoSize size2 = newMsg.media.photo.sizes.get(b); + for (int b = 0; b < newMedia.photo.sizes.size(); b++) { + TLRPC.PhotoSize size2 = newMedia.photo.sizes.get(b); if (size2 == null || size2.location == null || size2.type == null) { continue; } boolean found = false; - for (int a = 0; a < sentMessage.media.photo.sizes.size(); a++) { - TLRPC.PhotoSize size = sentMessage.media.photo.sizes.get(a); + for (int a = 0; a < sentMedia.photo.sizes.size(); a++) { + TLRPC.PhotoSize size = sentMedia.photo.sizes.get(a); if (size == null || size.location == null || size instanceof TLRPC.TL_photoSizeEmpty || size.type == null) { continue; } @@ -6390,13 +6714,13 @@ private void updateMediaPaths(MessageObject newMsgObj, TLRPC.Message sentMessage } File cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg"); File cacheFile2; - if (sentMessage.media.ttl_seconds == 0 && (sentMessage.media.photo.sizes.size() == 1 || size.w > 90 || size.h > 90)) { + if (sentMedia.ttl_seconds == 0 && (sentMedia.photo.sizes.size() == 1 || size.w > 90 || size.h > 90) && !singleAlbum) { cacheFile2 = FileLoader.getInstance(currentAccount).getPathToAttach(size); } else { cacheFile2 = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName2 + ".jpg"); } cacheFile.renameTo(cacheFile2); - ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, ImageLocation.getForPhoto(size, sentMessage.media.photo), post); + ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, ImageLocation.getForPhoto(size, sentMedia.photo), post); size2.location = size.location; size2.size = size.size; break; @@ -6407,37 +6731,43 @@ private void updateMediaPaths(MessageObject newMsgObj, TLRPC.Message sentMessage File cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg"); cacheFile.delete(); if ("s".equals(size2.type) && strippedNew != null) { - newMsg.media.photo.sizes.set(b, strippedNew); - ImageLocation location = ImageLocation.getForPhoto(strippedNew, sentMessage.media.photo); + newMedia.photo.sizes.set(b, strippedNew); + ImageLocation location = ImageLocation.getForPhoto(strippedNew, sentMedia.photo); ImageLoader.getInstance().replaceImageInCache(fileName, location.getKey(sentMessage, null, false), location, post); } } } } - newMsg.message = sentMessage.message; - sentMessage.attachPath = newMsg.attachPath; - newMsg.media.photo.id = sentMessage.media.photo.id; - newMsg.media.photo.dc_id = sentMessage.media.photo.dc_id; - newMsg.media.photo.access_hash = sentMessage.media.photo.access_hash; - } else if (sentMessage.media instanceof TLRPC.TL_messageMediaDocument && sentMessage.media.document != null && newMsg.media instanceof TLRPC.TL_messageMediaDocument && newMsg.media.document != null) { - if (sentMessage.media.ttl_seconds == 0 && (newMsgObj.videoEditedInfo == null || newMsgObj.videoEditedInfo.mediaEntities == null && TextUtils.isEmpty(newMsgObj.videoEditedInfo.paintPath) && newMsgObj.videoEditedInfo.cropState == null)) { + if (!singleAlbum) { + newMsg.message = sentMessage.message; + sentMessage.attachPath = newMsg.attachPath; + } else { + if (newEMedia != null && sentEMedia != null) { + newEMedia.attachPath = sentEMedia.attachPath; + } + } + newMedia.photo.id = sentMedia.photo.id; + newMedia.photo.dc_id = sentMedia.photo.dc_id; + newMedia.photo.access_hash = sentMedia.photo.access_hash; + } else if (sentMedia instanceof TLRPC.TL_messageMediaDocument && sentMedia.document != null && newMedia instanceof TLRPC.TL_messageMediaDocument && newMedia.document != null) { + if (sentMedia.ttl_seconds == 0 && (newMsgObj.videoEditedInfo == null || newMsgObj.videoEditedInfo.mediaEntities == null && TextUtils.isEmpty(newMsgObj.videoEditedInfo.paintPath) && newMsgObj.videoEditedInfo.cropState == null)) { boolean isVideo = MessageObject.isVideoMessage(sentMessage); - if ((isVideo || MessageObject.isGifMessage(sentMessage)) && MessageObject.isGifDocument(sentMessage.media.document) == MessageObject.isGifDocument(newMsg.media.document)) { + if ((isVideo || MessageObject.isGifMessage(sentMessage)) && MessageObject.isGifDocument(sentMedia.document) == MessageObject.isGifDocument(newMedia.document)) { if (!newMsgObj.scheduled) { MessageObject messageObject = new MessageObject(currentAccount, sentMessage, false, false); - getMessagesStorage().putSentFile(originalPath, sentMessage.media.document, 2, "sent_" + sentMessage.peer_id.channel_id + "_" + sentMessage.id + "_" + DialogObject.getPeerDialogId(sentMessage.peer_id) + "_" + messageObject.type + "_" + messageObject.getSize()); + getMessagesStorage().putSentFile(originalPath, sentMedia.document, 2, "sent_" + sentMessage.peer_id.channel_id + "_" + sentMessage.id + "_" + DialogObject.getPeerDialogId(sentMessage.peer_id) + "_" + messageObject.type + "_" + messageObject.getSize()); } if (isVideo) { sentMessage.attachPath = newMsg.attachPath; } } else if (!MessageObject.isVoiceMessage(sentMessage) && !MessageObject.isRoundVideoMessage(sentMessage) && !newMsgObj.scheduled) { MessageObject messageObject = new MessageObject(currentAccount, sentMessage, false, false); - getMessagesStorage().putSentFile(originalPath, sentMessage.media.document, 1, "sent_" + sentMessage.peer_id.channel_id + "_" + sentMessage.id + "_" + DialogObject.getPeerDialogId(sentMessage.peer_id) + "_" + messageObject.type + "_" + messageObject.getSize()); + getMessagesStorage().putSentFile(originalPath, sentMedia.document, 1, "sent_" + sentMessage.peer_id.channel_id + "_" + sentMessage.id + "_" + DialogObject.getPeerDialogId(sentMessage.peer_id) + "_" + messageObject.type + "_" + messageObject.getSize()); } } - TLRPC.PhotoSize size2 = FileLoader.getClosestPhotoSizeWithSize(newMsg.media.document.thumbs, 320); - TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(sentMessage.media.document.thumbs, 320); + TLRPC.PhotoSize size2 = FileLoader.getClosestPhotoSizeWithSize(newMedia.document.thumbs, 320); + TLRPC.PhotoSize size = FileLoader.getClosestPhotoSizeWithSize(sentMedia.document.thumbs, 320); if (size2 != null && size2.location != null && size2.location.volume_id == Integer.MIN_VALUE && size != null && size.location != null && !(size instanceof TLRPC.TL_photoSizeEmpty) && !(size2 instanceof TLRPC.TL_photoSizeEmpty)) { String fileName = size2.location.volume_id + "_" + size2.location.local_id; String fileName2 = size.location.volume_id + "_" + size.location.local_id; @@ -6445,66 +6775,80 @@ private void updateMediaPaths(MessageObject newMsgObj, TLRPC.Message sentMessage File cacheFile = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName + ".jpg"); File cacheFile2 = new File(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE), fileName2 + ".jpg"); cacheFile.renameTo(cacheFile2); - ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, ImageLocation.getForDocument(size, sentMessage.media.document), post); + ImageLoader.getInstance().replaceImageInCache(fileName, fileName2, ImageLocation.getForDocument(size, sentMedia.document), post); size2.location = size.location; size2.size = size.size; } } else if (size != null && size2 != null && MessageObject.isStickerMessage(sentMessage) && size2.location != null) { size.location = size2.location; } else if (size2 == null || size2 != null && size2.location instanceof TLRPC.TL_fileLocationUnavailable || size2 instanceof TLRPC.TL_photoSizeEmpty) { - newMsg.media.document.thumbs = sentMessage.media.document.thumbs; + newMedia.document.thumbs = sentMedia.document.thumbs; } - newMsg.media.document.dc_id = sentMessage.media.document.dc_id; - newMsg.media.document.id = sentMessage.media.document.id; - newMsg.media.document.access_hash = sentMessage.media.document.access_hash; + newMedia.document.dc_id = sentMedia.document.dc_id; + newMedia.document.id = sentMedia.document.id; + newMedia.document.access_hash = sentMedia.document.access_hash; byte[] oldWaveform = null; - for (int a = 0; a < newMsg.media.document.attributes.size(); a++) { - TLRPC.DocumentAttribute attribute = newMsg.media.document.attributes.get(a); + for (int a = 0; a < newMedia.document.attributes.size(); a++) { + TLRPC.DocumentAttribute attribute = newMedia.document.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio) { oldWaveform = attribute.waveform; break; } } - newMsg.media.document.attributes = sentMessage.media.document.attributes; + newMedia.document.attributes = sentMedia.document.attributes; if (oldWaveform != null) { - for (int a = 0; a < newMsg.media.document.attributes.size(); a++) { - TLRPC.DocumentAttribute attribute = newMsg.media.document.attributes.get(a); + for (int a = 0; a < newMedia.document.attributes.size(); a++) { + TLRPC.DocumentAttribute attribute = newMedia.document.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio) { attribute.waveform = oldWaveform; attribute.flags |= 4; } } } - newMsg.media.document.size = sentMessage.media.document.size; - newMsg.media.document.mime_type = sentMessage.media.document.mime_type; + newMedia.document.size = sentMedia.document.size; + newMedia.document.mime_type = sentMedia.document.mime_type; if ((sentMessage.flags & TLRPC.MESSAGE_FLAG_FWD) == 0 && (MessageObject.isOut(sentMessage) || sentMessage.dialog_id == getUserConfig().getClientUserId()) && !MessageObject.isQuickReply(sentMessage)) { - if (MessageObject.isNewGifDocument(sentMessage.media.document)) { + if (MessageObject.isNewGifDocument(sentMedia.document)) { boolean save; - if (MessageObject.isDocumentHasAttachedStickers(sentMessage.media.document)) { + if (MessageObject.isDocumentHasAttachedStickers(sentMedia.document)) { save = getMessagesController().saveGifsWithStickers; } else { save = true; } if (save) { - getMediaDataController().addRecentGif(sentMessage.media.document, sentMessage.date, true); + getMediaDataController().addRecentGif(sentMedia.document, sentMessage.date, true); } - } else if (MessageObject.isStickerDocument(sentMessage.media.document) || MessageObject.isAnimatedStickerDocument(sentMessage.media.document, true)) { - getMediaDataController().addRecentSticker(MediaDataController.TYPE_IMAGE, sentMessage, sentMessage.media.document, sentMessage.date, false); + } else if (MessageObject.isStickerDocument(sentMedia.document) || MessageObject.isAnimatedStickerDocument(sentMedia.document, true)) { + getMediaDataController().addRecentSticker(MediaDataController.TYPE_IMAGE, sentMessage, sentMedia.document, sentMessage.date, false); } } - if (newMsg.attachPath != null && newMsg.attachPath.startsWith(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE).getAbsolutePath()) && !MessageObject.isGifDocument(sentMessage.media.document)) { - File cacheFile = new File(newMsg.attachPath); - File cacheFile2 = FileLoader.getInstance(currentAccount).getPathToAttach(sentMessage.media.document, sentMessage.media.ttl_seconds != 0); + final String newAttachPath = newEMedia != null ? newEMedia.attachPath : newMsg.attachPath; + + if (newAttachPath != null && newAttachPath.startsWith(FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE).getAbsolutePath()) && !MessageObject.isGifDocument(sentMedia.document)) { + File cacheFile = new File(newAttachPath); + File cacheFile2 = FileLoader.getInstance(currentAccount).getPathToAttach(sentMedia.document, sentMedia.ttl_seconds != 0); if (!cacheFile.renameTo(cacheFile2)) { if (cacheFile.exists()) { - sentMessage.attachPath = newMsg.attachPath; + if (newEMedia != null) { + newEMedia.attachPath = newAttachPath; + } else { + sentMessage.attachPath = newAttachPath; + } } else { - newMsgObj.attachPathExists = false; + if (newEMedia != null) { + + } else { + newMsgObj.attachPathExists = false; + } + } + if (newEMedia != null) { + + } else { + newMsgObj.mediaExists = cacheFile2.exists(); } - newMsgObj.mediaExists = cacheFile2.exists(); sentMessage.message = newMsg.message; } else { if (MessageObject.isVideoMessage(sentMessage)) { @@ -6512,15 +6856,23 @@ private void updateMediaPaths(MessageObject newMsgObj, TLRPC.Message sentMessage } else { newMsgObj.mediaExists = newMsgObj.attachPathExists; newMsgObj.attachPathExists = false; - newMsg.attachPath = ""; + if (newEMedia != null) { + newEMedia.attachPath = ""; + } else { + newMsg.attachPath = ""; + } if (originalPath != null && originalPath.startsWith("http")) { - getMessagesStorage().addRecentLocalFile(originalPath, cacheFile2.toString(), newMsg.media.document); + getMessagesStorage().addRecentLocalFile(originalPath, cacheFile2.toString(), newMedia.document); } } } } else { - sentMessage.attachPath = newMsg.attachPath; - sentMessage.message = newMsg.message; + if (newEMedia != null) { + newEMedia.attachPath = newAttachPath; + } else { + sentMessage.attachPath = newAttachPath; + sentMessage.message = newMsg.message; + } } } else if (sentMessage.media instanceof TLRPC.TL_messageMediaContact && newMsg.media instanceof TLRPC.TL_messageMediaContact) { newMsg.media = sentMessage.media; @@ -7216,7 +7568,9 @@ 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 ? 1 : 0, 0); + if (!message.paidMedia) { + accountInstance.getMessagesStorage().putMessages(messagesRes, message.peer, -2, 0, false, scheduleDate != 0 ? 1 : 0, 0); + } instance.sendReadyToSendGroup(message, true, true); } }); @@ -8387,6 +8741,7 @@ public static void prepareSendingMedia(AccountInstance accountInstance, ArrayLis sendMessageParams.quick_reply_shortcut_id = quickReplyShortcutId; sendMessageParams.effect_id = effectId; sendMessageParams.invert_media = invertMedia; + sendMessageParams.stars = info.stars; accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); } }); @@ -8591,6 +8946,7 @@ public static void prepareSendingMedia(AccountInstance accountInstance, ArrayLis sendMessageParams.quick_reply_shortcut_id = quickReplyShortcutId; sendMessageParams.effect_id = effectId; sendMessageParams.invert_media = invertMedia; + sendMessageParams.stars = info.stars; accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); } }); @@ -9100,6 +9456,7 @@ public static class SendMessageParams { public String quick_reply_shortcut; public int quick_reply_shortcut_id; public long effect_id; + public long stars; public static SendMessageParams of(String string, long dialogId) { return of(string, null, null, null, null, null, null, null, null, null, dialogId, null, null, null, null, true, null, null, null, null, false, 0, 0, null, null, false); diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java index ede8a849c5..778dfb3ee6 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java @@ -313,6 +313,7 @@ private static boolean isWhitelisted(MediaCodecInfo codecInfo) { public static int emojiInteractionsHintCount; public static int dayNightThemeSwitchHintCount; public static int callEncryptionHintDisplayedCount; + public static boolean botTabs3DEffect; public static TLRPC.TL_help_appUpdate pendingAppUpdate; public static int pendingAppUpdateBuildVersion; @@ -646,12 +647,13 @@ public static void loadConfig() { dayNightWallpaperSwitchHint = preferences.getInt("dayNightWallpaperSwitchHint", 0); bigCameraForRound = preferences.getBoolean("bigCameraForRound", false); useNewBlur = preferences.getBoolean("useNewBlur", true); - useCamera2Force = !preferences.contains("useCamera2Force") ? null : preferences.getBoolean("useCamera2Force", false); + useCamera2Force = !preferences.contains("useCamera2Force_2") ? null : preferences.getBoolean("useCamera2Force_2", false); useSurfaceInStories = preferences.getBoolean("useSurfaceInStories", Build.VERSION.SDK_INT >= 30); payByInvoice = preferences.getBoolean("payByInvoice", false); photoViewerBlur = preferences.getBoolean("photoViewerBlur", true); multipleReactionsPromoShowed = preferences.getBoolean("multipleReactionsPromoShowed", false); callEncryptionHintDisplayedCount = preferences.getInt("callEncryptionHintDisplayedCount", 0); + botTabs3DEffect = preferences.getBoolean("botTabs3DEffect", true); loadDebugConfig(preferences); @@ -1081,6 +1083,13 @@ public static void incrementCallEncryptionHintDisplayed(int count) { editor.apply(); } + public static void setBotTabs3DEffect(boolean value) { + SharedPreferences preferences = MessagesController.getGlobalMainSettings(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putBoolean("botTabs3DEffect", botTabs3DEffect = value); + editor.apply(); + } + public static void toggleLoopStickers() { LiteMode.toggleFlag(LiteMode.FLAG_ANIMATED_STICKERS_CHAT); } @@ -1757,7 +1766,7 @@ public static boolean isUsingCamera2(int currentAccount) { public static void toggleUseCamera2(int currentAccount) { ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE) .edit() - .putBoolean("useCamera2", useCamera2Force = !isUsingCamera2(currentAccount)) + .putBoolean("useCamera2Force_2", useCamera2Force = !isUsingCamera2(currentAccount)) .apply(); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java b/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java index fa491caf36..99677d36c3 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/TranslateController.java @@ -4,6 +4,7 @@ import android.content.res.Resources; import android.icu.text.Collator; import android.text.TextUtils; +import android.util.Log; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; @@ -240,6 +241,7 @@ public void updateDialogFull(long dialogId) { synchronized (this) { if (hidden) { hideTranslateDialogs.add(dialogId); + translatingDialogs.remove(dialogId); } else { hideTranslateDialogs.remove(dialogId); } @@ -275,6 +277,7 @@ public void setHideTranslateDialog(long dialogId, boolean hide, boolean doNotNot synchronized (this) { if (hide) { hideTranslateDialogs.add(dialogId); + translatingDialogs.remove(dialogId); } else { hideTranslateDialogs.remove(dialogId); } @@ -498,6 +501,10 @@ private void checkTranslation(MessageObject messageObject, boolean onScreen, boo return; } + if (isTranslateDialogHidden(dialogId)) { + return; + } + final String language = getDialogTranslateTo(dialogId); MessageObject potentialReplyMessageObject; if (!keepReply && (messageObject.messageOwner.translatedText == null || !language.equals(messageObject.messageOwner.translatedToLanguage)) && (potentialReplyMessageObject = findReplyMessageObject(dialogId, messageObject.getId())) != null) { @@ -825,6 +832,9 @@ private void pushToTranslate( toggleTranslatingDialog(dialogId, false); NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.showBulletin, Bulletin.TYPE_ERROR, LocaleController.getString("TranslationFailedAlert2", R.string.TranslationFailedAlert2)); } else { + if (err != null && "QUOTA_EXCEEDED".equals(err.text)) { + NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.showBulletin, Bulletin.TYPE_ERROR, LocaleController.getString(R.string.TranslationFailedAlert1)); + } for (int i = 0; i < callbacks.size(); ++i) { callbacks.get(i).run(ids.get(i), null, pendingTranslation1.language); } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java b/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java index a1e39af6e2..30aeb03834 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/VideoEditedInfo.java @@ -11,7 +11,6 @@ import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Matrix; -import android.graphics.RectF; import android.text.TextUtils; import android.view.View; @@ -22,6 +21,7 @@ import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.Components.AnimatedFileDrawable; import org.telegram.ui.Components.Paint.PaintTypeface; +import org.telegram.ui.Components.Paint.Views.LinkPreview; import org.telegram.ui.Components.PhotoFilterView; import org.telegram.ui.Components.Point; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; @@ -126,6 +126,7 @@ public static class MediaEntity { public static final byte TYPE_REACTION = 4; public static final byte TYPE_ROUND = 5; public static final byte TYPE_MESSAGE = 6; + public static final byte TYPE_LINK = 7; public byte type; public byte subType; @@ -173,7 +174,7 @@ public static class MediaEntity { public boolean firstSeek; public TL_stories.MediaArea mediaArea; - public TLRPC.MessageMedia mediaGeo; + public TLRPC.MessageMedia media; public float density; public long roundOffset; @@ -184,6 +185,8 @@ public static class MediaEntity { public int W, H; public ReactionsLayoutInBubble.VisibleReaction visibleReaction; + public LinkPreview.WebPagePreview linkSettings; + public MediaEntity() { } @@ -229,27 +232,28 @@ public MediaEntity(AbstractSerializedData data, boolean full, boolean exception) if (type == TYPE_LOCATION) { density = data.readFloat(exception); mediaArea = TL_stories.MediaArea.TLdeserialize(data, data.readInt32(exception), exception); - mediaGeo = TLRPC.MessageMedia.TLdeserialize(data, data.readInt32(exception), exception); + media = TLRPC.MessageMedia.TLdeserialize(data, data.readInt32(exception), exception); if (data.remaining() > 0) { int magic = data.readInt32(exception); if (magic == 0xdeadbeef) { String emoji = data.readString(exception); - if (mediaGeo instanceof TLRPC.TL_messageMediaVenue) { - ((TLRPC.TL_messageMediaVenue) mediaGeo).emoji = emoji; + if (media instanceof TLRPC.TL_messageMediaVenue) { + ((TLRPC.TL_messageMediaVenue) media).emoji = emoji; } } } - } - if (type == TYPE_REACTION) { + } else if (type == TYPE_LINK) { + density = data.readFloat(exception); mediaArea = TL_stories.MediaArea.TLdeserialize(data, data.readInt32(exception), exception); - } - if (type == TYPE_ROUND) { + linkSettings = LinkPreview.WebPagePreview.TLdeserialize(data, data.readInt32(exception), exception); + } else if (type == TYPE_REACTION) { + mediaArea = TL_stories.MediaArea.TLdeserialize(data, data.readInt32(exception), exception); + } else if (type == TYPE_ROUND) { roundOffset = data.readInt64(exception); roundLeft = data.readInt64(exception); roundRight = data.readInt64(exception); roundDuration = data.readInt64(exception); - } - if (type == TYPE_PHOTO) { + } else if (type == TYPE_PHOTO) { segmentedPath = data.readString(exception); } } @@ -288,33 +292,34 @@ public void serializeTo(AbstractSerializedData data, boolean full) { if (type == TYPE_LOCATION) { data.writeFloat(density); mediaArea.serializeToStream(data); - if (mediaGeo.provider == null) { - mediaGeo.provider = ""; + if (media.provider == null) { + media.provider = ""; } - if (mediaGeo.venue_id == null) { - mediaGeo.venue_id = ""; + if (media.venue_id == null) { + media.venue_id = ""; } - if (mediaGeo.venue_type == null) { - mediaGeo.venue_type = ""; + if (media.venue_type == null) { + media.venue_type = ""; } - mediaGeo.serializeToStream(data); - if (mediaGeo instanceof TLRPC.TL_messageMediaVenue && ((TLRPC.TL_messageMediaVenue) mediaGeo).emoji != null) { + media.serializeToStream(data); + if (media instanceof TLRPC.TL_messageMediaVenue && ((TLRPC.TL_messageMediaVenue) media).emoji != null) { data.writeInt32(0xdeadbeef); - data.writeString(((TLRPC.TL_messageMediaVenue) mediaGeo).emoji); + data.writeString(((TLRPC.TL_messageMediaVenue) media).emoji); } else { data.writeInt32(TLRPC.TL_null.constructor); } - } - if (type == TYPE_REACTION) { + } else if (type == TYPE_LINK) { + data.writeFloat(density); mediaArea.serializeToStream(data); - } - if (type == TYPE_ROUND) { + linkSettings.serializeToStream(data); + } else if (type == TYPE_REACTION) { + mediaArea.serializeToStream(data); + } else if (type == TYPE_ROUND) { data.writeInt64(roundOffset); data.writeInt64(roundLeft); data.writeInt64(roundRight); data.writeInt64(roundDuration); - } - if (type == TYPE_PHOTO) { + } else if (type == TYPE_PHOTO) { data.writeString(segmentedPath); } } @@ -359,7 +364,7 @@ public MediaEntity copy() { entity.animatedFileDrawable = animatedFileDrawable; entity.roundRadiusCanvas = roundRadiusCanvas; entity.mediaArea = mediaArea; - entity.mediaGeo = mediaGeo; + entity.media = media; entity.density = density; entity.W = W; entity.H = H; @@ -368,6 +373,7 @@ public MediaEntity copy() { entity.roundDuration = roundDuration; entity.roundLeft = roundLeft; entity.roundRight = roundRight; + entity.linkSettings = linkSettings; return entity; } } diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/utils/BillingUtilities.java b/TMessagesProj/src/main/java/org/telegram/messenger/utils/BillingUtilities.java index 3c27452fb3..1c819bf6c4 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/utils/BillingUtilities.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/utils/BillingUtilities.java @@ -62,7 +62,6 @@ public static Pair createDeveloperPayload(TLRPC.InputStorePaymen serializedData.cleanup(); if ( paymentPurpose instanceof TLRPC.TL_inputStorePaymentPremiumGiftCode || - paymentPurpose instanceof TLRPC.TL_inputStorePaymentStars || paymentPurpose instanceof TLRPC.TL_inputStorePaymentPremiumGiveaway ) { remPaymentPurpose = paymentPurpose; @@ -107,7 +106,7 @@ public static Pair extractDevel purpose = TLRPC.InputStorePaymentPurpose.TLdeserialize(data, data.readInt32(true), true); data.cleanup(); } catch (Exception e) { - FileLog.e(e); + FileLog.e("Billing: Extract payload, no remPaymentPurpose; failed to get purpose", e); purpose = null; } } else { diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/video/TextureRenderer.java b/TMessagesProj/src/main/java/org/telegram/messenger/video/TextureRenderer.java index f27b855b5b..95ad248410 100644 --- a/TMessagesProj/src/main/java/org/telegram/messenger/video/TextureRenderer.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/video/TextureRenderer.java @@ -68,6 +68,7 @@ import org.telegram.ui.Components.EditTextEffects; import org.telegram.ui.Components.FilterShaders; import org.telegram.ui.Components.Paint.Views.EditTextOutline; +import org.telegram.ui.Components.Paint.Views.LinkPreview; import org.telegram.ui.Components.Paint.Views.LocationMarker; import org.telegram.ui.Components.Paint.Views.PaintTextOptionsView; import org.telegram.ui.Components.RLottieDrawable; @@ -1210,6 +1211,8 @@ public void surfaceCreated() { initTextEntity(entity); } else if (entity.type == VideoEditedInfo.MediaEntity.TYPE_LOCATION) { initLocationEntity(entity); + } else if (entity.type == VideoEditedInfo.MediaEntity.TYPE_LINK) { + initLinkEntity(entity); } } } catch (Throwable e) { @@ -1339,7 +1342,7 @@ public void draw(@NonNull Canvas canvas, CharSequence charSequence, int start, i } private void initLocationEntity(VideoEditedInfo.MediaEntity entity) { - LocationMarker marker = new LocationMarker(ApplicationLoader.applicationContext, entity.density); + LocationMarker marker = new LocationMarker(ApplicationLoader.applicationContext, entity.density, 0); marker.setText(entity.text); marker.setType(entity.subType, entity.color); marker.setMaxWidth(entity.viewWidth); @@ -1391,6 +1394,25 @@ private void initLocationEntity(VideoEditedInfo.MediaEntity entity) { } } + private void initLinkEntity(VideoEditedInfo.MediaEntity entity) { + LinkPreview marker = new LinkPreview(ApplicationLoader.applicationContext, entity.density); + marker.setVideoTexture(); + marker.set(UserConfig.selectedAccount, entity.linkSettings); + marker.setType(entity.subType, entity.color); + marker.setMaxWidth(entity.viewWidth + marker.padx + marker.padx); + marker.measure(View.MeasureSpec.makeMeasureSpec(entity.viewWidth, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(entity.viewHeight, View.MeasureSpec.EXACTLY)); + marker.layout(0, 0, entity.viewWidth, entity.viewHeight); + float scale = entity.width * transformedWidth / entity.viewWidth; + int w = (int) (entity.viewWidth * scale), h = (int) (entity.viewHeight * scale), pad = 8; + entity.bitmap = Bitmap.createBitmap(w + pad + pad, h + pad + pad, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(entity.bitmap); + canvas.translate(pad, pad); + canvas.scale(scale, scale); + marker.draw(canvas); + entity.additionalWidth = (2 * pad) * scale / transformedWidth; + entity.additionalHeight = (2 * pad) * scale / transformedHeight; + } + private void initStickerEntity(VideoEditedInfo.MediaEntity entity) { entity.W = (int) (entity.width * transformedWidth); entity.H = (int) (entity.height * transformedHeight); 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 7d7f941dc0..71f5ffb96a 100755 --- a/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java +++ b/TMessagesProj/src/main/java/org/telegram/messenger/voip/VoIPService.java @@ -780,6 +780,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { return START_NOT_STICKY; } sharedInstance = this; + FileLog.e("(4) set sharedInstance = this"); synchronized (sync) { if (setModeRunnable != null) { Utilities.globalQueue.cancelRunnable(setModeRunnable); @@ -831,6 +832,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { setAudioOutput(0); } callIShouldHavePutIntoIntent = null; + FileLog.e("(3) set VoIPService.callIShouldHavePutIntoIntent = null"); if (USE_CONNECTION_SERVICE) { acknowledgeCall(false); showNotification(); @@ -1000,6 +1002,7 @@ private void startOutgoingCall() { req1.peer.access_hash = privateCall.access_hash; req1.peer.id = privateCall.id; req1.reason = new TLRPC.TL_phoneCallDiscardReasonMissed(); + FileLog.e("discardCall " + req1.reason); ConnectionsManager.getInstance(currentAccount).sendRequest(req1, (response1, error1) -> { if (BuildVars.LOGS_ENABLED) { if (error1 != null) { @@ -1037,6 +1040,13 @@ private void startOutgoingCall() { } private void acknowledgeCall(final boolean startRinging) { + if (privateCall == null) { + if (BuildVars.LOGS_ENABLED) { + FileLog.w("Call is null, wtf"); + } + stopSelf(); + return; + } if (privateCall instanceof TLRPC.TL_phoneCallDiscarded) { if (BuildVars.LOGS_ENABLED) { FileLog.w("Call " + privateCall.id + " was discarded before the service started, stopping"); @@ -3120,6 +3130,7 @@ public void onDestroy() { } super.onDestroy(); sharedInstance = null; + FileLog.e("(5) set sharedInstance = null"); Arrays.fill(mySource, 0); cancelGroupCheckShortPoll(); AndroidUtilities.runOnUIThread(() -> NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.didEndCall)); @@ -3385,6 +3396,7 @@ public void declineIncomingCall(int reason, final Runnable onDone) { req.reason = new TLRPC.TL_phoneCallDiscardReasonHangup(); break; } + FileLog.e("discardCall " + req.reason); ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { if (error != null) { if (BuildVars.LOGS_ENABLED) { @@ -4244,6 +4256,7 @@ private void callFailed(String error) { req.duration = (int) (getCallDuration() / 1000); req.connection_id = tgVoip[CAPTURE_DEVICE_CAMERA] != null ? tgVoip[CAPTURE_DEVICE_CAMERA].getPreferredRelayId() : 0; req.reason = new TLRPC.TL_phoneCallDiscardReasonDisconnect(); + FileLog.e("discardCall " + req.reason); ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error1) -> { if (error1 != null) { if (BuildVars.LOGS_ENABLED) { diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java b/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java index ac6af9745c..795ca50a9d 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/ConnectionsManager.java @@ -1498,12 +1498,20 @@ public static void onPremiumFloodWait(final int currentAccount, final int reques }); } - public static void onIntegrityCheckClassic(final int currentAccount, final int requestToken, final String nonce) { + public static void onIntegrityCheckClassic(final int currentAccount, final int requestToken, final String project, final String nonce) { AndroidUtilities.runOnUIThread(() -> { long start = System.currentTimeMillis(); - FileLog.d("account"+currentAccount+": server requests integrity classic check with nonce = " + nonce); + FileLog.d("account"+currentAccount+": server requests integrity classic check with project = "+project+" nonce = " + nonce); IntegrityManager integrityManager = IntegrityManagerFactory.create(ApplicationLoader.applicationContext); - Task integrityTokenResponse = integrityManager.requestIntegrityToken(IntegrityTokenRequest.builder().setNonce(nonce).setCloudProjectNumber(760348033671L).build()); + final long project_id; + try { + project_id = Long.parseLong(project); + } catch (Exception e) { + FileLog.d("account"+currentAccount+": integrity check failes to parse project id"); + native_receivedIntegrityCheckClassic(currentAccount, requestToken, nonce, "PLAYINTEGRITY_FAILED_EXCEPTION_NOPROJECT"); + return; + } + Task integrityTokenResponse = integrityManager.requestIntegrityToken(IntegrityTokenRequest.builder().setNonce(nonce).setCloudProjectNumber(project_id).build()); integrityTokenResponse .addOnSuccessListener(r -> { final String token = r.token(); diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java index f0775a7742..8d03b90f5f 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/TLRPC.java @@ -26,6 +26,7 @@ import org.telegram.messenger.MessagesController; import org.telegram.messenger.SvgHelper; import org.telegram.messenger.Utilities; +import org.telegram.tgnet.tl.TL_stats; import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.Stories.MessageMediaStoryFull; import org.telegram.ui.Stories.MessageMediaStoryFull_old; @@ -77,7 +78,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 = 181; + public static final int LAYER = 183; public static abstract class EmailVerifyPurpose extends TLObject { @@ -413,6 +414,7 @@ public static abstract class DraftMessage extends TLObject { public ArrayList entities = new ArrayList<>(); public InputMedia media; public int date; + public long effect; public static DraftMessage TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { DraftMessage result = null; @@ -426,6 +428,9 @@ public static DraftMessage TLdeserialize(AbstractSerializedData stream, int cons case TL_draftMessage_layer165.constructor: result = new TL_draftMessage_layer165(); break; + case TL_draftMessage_layer181.constructor: + result = new TL_draftMessage_layer181(); + break; case TL_draftMessage.constructor: result = new TL_draftMessage(); break; @@ -470,6 +475,70 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_draftMessage extends DraftMessage { + public static final int constructor = 0x2d65321f; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + no_webpage = (flags & 2) != 0; + invert_media = (flags & 64) != 0; + if ((flags & 16) != 0) { + reply_to = InputReplyTo.TLdeserialize(stream, stream.readInt32(exception), exception); + } + message = stream.readString(exception); + if ((flags & 8) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + MessageEntity object = MessageEntity.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + entities.add(object); + } + } + if ((flags & 32) != 0) { + media = InputMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + } + date = stream.readInt32(exception); + if ((flags & 128) != 0) { + effect = stream.readInt64(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = no_webpage ? (flags | 2) : (flags &~ 2); + flags = invert_media ? (flags | 64) : (flags &~ 64); + stream.writeInt32(flags); + if ((flags & 16) != 0) { + reply_to.serializeToStream(stream); + } + stream.writeString(message); + if ((flags & 8) != 0) { + stream.writeInt32(0x1cb5c415); + int count = entities.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + entities.get(a).serializeToStream(stream); + } + } + if ((flags & 32) != 0) { + media.serializeToStream(stream); + } + stream.writeInt32(date); + if ((flags & 128) != 0) { + stream.writeInt64(effect); + } + } + } + + public static class TL_draftMessage_layer181 extends TL_draftMessage { public static final int constructor = 0x3fccf7ef; public void readParams(AbstractSerializedData stream, boolean exception) { @@ -6982,6 +7051,7 @@ public static abstract class auth_SentCodeType extends TLObject { public String email_pattern; public int next_phone_login_date; public byte[] nonce; + public long play_integrity_project_id; public byte[] play_integrity_nonce; public String receipt; public int push_timeout; @@ -7017,7 +7087,7 @@ public static auth_SentCodeType TLdeserialize(AbstractSerializedData stream, int case 0xd9565c39: result = new TL_auth_sentCodeTypeFragmentSms(); break; - case 0x13c90f17: + case 0x9fd736: result = new TL_auth_sentCodeTypeFirebaseSms(); break; case 0xa416ac81: @@ -7176,7 +7246,7 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_auth_sentCodeTypeFirebaseSms extends auth_SentCodeType { - public static final int constructor = 0x13c90f17; + public static final int constructor = 0x9fd736; public void readParams(AbstractSerializedData stream, boolean exception) { @@ -7185,6 +7255,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { nonce = stream.readByteArray(exception); } if ((flags & 4) != 0) { + play_integrity_project_id = stream.readInt64(exception); play_integrity_nonce = stream.readByteArray(exception); } if ((flags & 2) != 0) { @@ -8281,6 +8352,9 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class MessageExtendedMedia extends TLObject { + + public String attachPath; // custom + public float downloadProgress, uploadProgress; // custom public static MessageExtendedMedia TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { MessageExtendedMedia result = null; @@ -8382,7 +8456,8 @@ public void readParams(AbstractSerializedData stream, boolean exception) { total_amount = stream.readInt64(exception); start_param = stream.readString(exception); if ((flags & 16) != 0) { - extended_media = MessageExtendedMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + extended_media.clear(); + extended_media.add(MessageExtendedMedia.TLdeserialize(stream, stream.readInt32(exception), exception)); } } @@ -8403,7 +8478,7 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt64(total_amount); stream.writeString(start_param); if ((flags & 16) != 0) { - extended_media.serializeToStream(stream); + extended_media.get(0).serializeToStream(stream); } } } @@ -8474,6 +8549,7 @@ public static class TL_messageMediaVenue extends MessageMedia { public String emoji; //custom public long query_id; //custom public String result_id; //custom + public TL_stories.TL_geoPointAddress geoAddress; //custom public void readParams(AbstractSerializedData stream, boolean exception) { geo = GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); @@ -12977,6 +13053,7 @@ public static abstract class ChatFull extends TLObject { public int boosts_unrestrict; public StickerSet emojiset; public boolean can_view_revenue; + public boolean paid_media_allowed; public long inviterId; //custom public int invitesCount; //custom @@ -15610,6 +15687,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { view_forum_as_messages = (flags2 & 64) != 0; restricted_sponsored = (flags2 & 2048) != 0; can_view_revenue = (flags2 & 4096) != 0; + paid_media_allowed = (flags2 & 16384) != 0; id = stream.readInt64(exception); about = stream.readString(exception); if ((flags & 1) != 0) { @@ -15770,6 +15848,7 @@ public void serializeToStream(AbstractSerializedData stream) { flags2 = view_forum_as_messages ? (flags2 | 64) : (flags2 &~ 64); flags2 = restricted_sponsored ? (flags2 | 2048) : (flags2 &~ 2048); flags2 = can_view_revenue ? (flags2 | 4096) : (flags2 &~ 4096); + flags2 = paid_media_allowed ? (flags2 | 16384) : (flags2 &~ 16384); stream.writeInt32(flags2); stream.writeInt64(id); stream.writeString(about); @@ -33679,6 +33758,9 @@ public static InputMedia TLdeserialize(AbstractSerializedData stream, int constr case 0xc21b8849: result = new TL_inputMediaWebPage(); break; + case TL_inputMediaPaidMedia.constructor: + result = new TL_inputMediaPaidMedia(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in InputMedia", constructor)); @@ -34133,6 +34215,43 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_inputMediaPaidMedia extends InputMedia { + public static final int constructor = 0xaa661fc3; + + public long stars_amount; + public ArrayList extended_media = new ArrayList<>(); + + public void readParams(AbstractSerializedData stream, boolean exception) { + stars_amount = stream.readInt64(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++) { + InputMedia object = InputMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + extended_media.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(stars_amount); + stream.writeInt32(0x1cb5c415); + int count = extended_media.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + extended_media.get(a).serializeToStream(stream); + } + } + } + public static class TL_inputMediaPhoto extends InputMedia { public static final int constructor = 0xb3ba0635; @@ -35685,6 +35804,9 @@ public static Update TLdeserialize(AbstractSerializedData stream, int constructo case TL_updateStarsBalance.constructor: result = new TL_updateStarsBalance(); break; + case TL_updateStarsRevenueStatus.constructor: + result = new TL_updateStarsRevenueStatus(); + break; } if (result == null && ApplicationLoader.applicationLoaderInstance != null) { result = ApplicationLoader.applicationLoaderInstance.parseTLUpdate(constructor); @@ -35763,23 +35885,42 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_updateMessageExtendedMedia extends Update { - public static final int constructor = 0x5a73a98c; + public static final int constructor = 0xd5a41724; public Peer peer; public int msg_id; - public MessageExtendedMedia extended_media; + public ArrayList extended_media = new ArrayList<>(); public void readParams(AbstractSerializedData stream, boolean exception) { peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); msg_id = stream.readInt32(exception); - extended_media = MessageExtendedMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + MessageExtendedMedia object = MessageExtendedMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + extended_media.add(object); + } } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); peer.serializeToStream(stream); stream.writeInt32(msg_id); - extended_media.serializeToStream(stream); + stream.writeInt32(0x1cb5c415); + int count = extended_media.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + extended_media.get(a).serializeToStream(stream); + } } } @@ -62071,7 +62212,7 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_saveDraft extends TLObject { - public static final int constructor = 0x7ff3b806; + public static final int constructor = 0xd372c5ce; public int flags; public boolean no_webpage; @@ -62081,6 +62222,7 @@ public static class TL_messages_saveDraft extends TLObject { public String message; public ArrayList entities = new ArrayList<>(); public InputMedia media; + public long effect; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { return Bool.TLdeserialize(stream, constructor, exception); @@ -62107,6 +62249,9 @@ public void serializeToStream(AbstractSerializedData stream) { if ((flags & 32) != 0) { media.serializeToStream(stream); } + if ((flags & 128) != 0) { + stream.writeInt64(effect); + } } } @@ -66898,7 +67043,6 @@ public static abstract class MessageMedia extends TLObject { public int ttl_seconds; public int proximity_notification_radius; public boolean nopremium; - public MessageExtendedMedia extended_media; public boolean spoiler; public int id; public TL_stories.StoryItem storyItem; @@ -66911,6 +67055,8 @@ public static abstract class MessageMedia extends TLObject { public boolean video; public boolean round; public boolean voice; + public long stars_amount; + public ArrayList extended_media = new ArrayList<>(); public static MessageMedia TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { MessageMedia result = null; @@ -66993,6 +67139,9 @@ public static MessageMedia TLdeserialize(AbstractSerializedData stream, int cons case TL_messageMediaGiveaway_layer167.constructor: result = new TL_messageMediaGiveaway_layer167(); break; + case TL_messageMediaPaidMedia.constructor: + result = new TL_messageMediaPaidMedia(); + break; case 0xb5223b0f: result = new TL_messageMediaPhoto_layer74(); break; @@ -67490,6 +67639,7 @@ public static class Message extends TLObject { public int send_state = 0; //custom public int fwd_msg_id = 0; //custom public String attachPath = ""; //custom + public ArrayList attachPaths; //custom public HashMap params; //custom public long random_id; //custom public int local_id = 0; //custom @@ -67703,6 +67853,16 @@ public void readAttachPath(AbstractSerializedData stream, long currentUserId) { if ((flags & MESSAGE_FLAG_FWD) != 0 && id < 0) { fwd_msg_id = stream.readInt32(false); } +// if (media instanceof TLRPC.TL_messageMediaPaidMedia && stream.remaining() > 0) { +// TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) media; +// final int size = stream.readInt32(false); +// for (int i = 0; i < size; ++i) { +// final String attachPath = stream.readString(false); +// if (i < paidMedia.extended_media.size()) { +// paidMedia.extended_media.get(i).attachPath = attachPath; +// } +// } +// } } protected void writeAttachPath(AbstractSerializedData stream) { @@ -67734,6 +67894,15 @@ protected void writeAttachPath(AbstractSerializedData stream) { if ((flags & MESSAGE_FLAG_FWD) != 0 && id < 0) { stream.writeInt32(fwd_msg_id); } +// if (media instanceof TLRPC.TL_messageMediaPaidMedia) { +// TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) media; +// stream.writeInt32(paidMedia.extended_media.size()); +// for (int i = 0; i < paidMedia.extended_media.size(); ++i) { +// TLRPC.MessageExtendedMedia emedia = paidMedia.extended_media.get(i); +// String attachPath = emedia.attachPath == null ? "" : emedia.attachPath; +// stream.writeString(attachPath); +// } +// } } } } @@ -73372,8 +73541,10 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_webViewResultUrl extends TLObject { - public static final int constructor = 0xc14557c; + public static final int constructor = 0x4d22ff98; + public int flags; + public boolean fullsize; public long query_id; public String url; @@ -73391,13 +73562,21 @@ public static TL_webViewResultUrl TLdeserialize(AbstractSerializedData stream, i } public void readParams(AbstractSerializedData stream, boolean exception) { - query_id = stream.readInt64(exception); + flags = stream.readInt32(exception); + fullsize = (flags & 2) != 0; + if ((flags & 1) != 0) { + query_id = stream.readInt64(exception); + } url = stream.readString(exception); } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); - stream.writeInt64(query_id); + flags = fullsize ? flags | 2 : flags &~ 2; + stream.writeInt32(flags); + if ((flags & 1) != 0) { + stream.writeInt64(query_id); + } stream.writeString(url); } } @@ -73408,6 +73587,7 @@ public static class TL_messages_requestWebView extends TLObject { public int flags; public boolean from_bot_menu; public boolean silent; + public boolean compact; public InputPeer peer; public InputUser bot; public String url; @@ -73425,6 +73605,7 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = from_bot_menu ? (flags | 16) : (flags &~ 16); flags = silent ? (flags | 32) : (flags &~ 32); + flags = compact ? (flags | 128) : (flags &~ 128); stream.writeInt32(flags); peer.serializeToStream(stream); bot.serializeToStream(stream); @@ -73496,10 +73677,11 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_requestAppWebView extends TLObject { - public static final int constructor = 0x8c5a3b3c; + public static final int constructor = 0x53618bce; public int flags; public boolean write_allowed; + public boolean compact; public InputPeer peer; public InputBotApp app; public String start_param; @@ -73507,12 +73689,13 @@ public static class TL_messages_requestAppWebView extends TLObject { public String platform; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { - return TL_appWebViewResultUrl.TLdeserialize(stream, constructor, exception); + return TL_webViewResultUrl.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = write_allowed ? (flags | 1) : (flags &~ 1); + flags = compact ? (flags | 128) : (flags &~ 128); stream.writeInt32(flags); peer.serializeToStream(stream); app.serializeToStream(stream); @@ -73527,11 +73710,12 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class TL_messages_requestSimpleWebView extends TLObject { - public static final int constructor = 0x1a46500a; + public static final int constructor = 0x413a3e73; public int flags; public boolean from_switch_webview; public boolean from_side_menu; + public boolean compact; public InputUser bot; public String url; public String start_param; @@ -73539,13 +73723,14 @@ public static class TL_messages_requestSimpleWebView extends TLObject { public String platform; public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { - return TL_simpleWebViewResultUrl.TLdeserialize(stream, constructor, exception); + return TL_webViewResultUrl.TLdeserialize(stream, constructor, exception); } public void serializeToStream(AbstractSerializedData stream) { stream.writeInt32(constructor); flags = from_switch_webview ? (flags | 2) : (flags &~ 2); flags = from_side_menu ? (flags | 4) : (flags &~ 4); + flags = compact ? (flags | 128) : (flags &~ 128); stream.writeInt32(flags); bot.serializeToStream(stream); if ((flags & 8) != 0) { @@ -76371,6 +76556,24 @@ public void serializeToStream(AbstractSerializedData stream) { stream.writeInt64(balance); } } + + public static class TL_updateStarsRevenueStatus extends Update { + public static final int constructor = 0xa584b019; + + public Peer peer; + public TL_starsRevenueStatus status; + + public void readParams(AbstractSerializedData stream, boolean exception) { + peer = Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + status = TL_starsRevenueStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + status.serializeToStream(stream); + } + } public static class TL_updateSavedDialogPinned extends Update { public static final int constructor = 0xaeaf9e74; @@ -77356,6 +77559,38 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_messageMediaPaidMedia extends MessageMedia { + public static final int constructor = 0xa8852491; + + public void readParams(AbstractSerializedData stream, boolean exception) { + stars_amount = stream.readInt64(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++) { + MessageExtendedMedia object = MessageExtendedMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) continue; + extended_media.add(object); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt64(stars_amount); + stream.writeInt32(0x1cb5c415); + int count = extended_media.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + extended_media.get(a).serializeToStream(stream); + } + } + } + public static class TL_premiumGiftCodeOption extends TLObject { public static int constructor = 0x257e962b; @@ -81227,6 +81462,9 @@ public static StarsTransactionPeer TLdeserialize(AbstractSerializedData stream, case TL_starsTransactionPeerUnsupported.constructor: result = new TL_starsTransactionPeerUnsupported(); break; + case TL_starsTransactionPeerAds.constructor: + result = new TL_starsTransactionPeerAds(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in StarsTransactionPeer", constructor)); @@ -81301,11 +81539,22 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static class TL_starsTransaction extends TLObject { - public static final int constructor = 0xcc7079b2; + public static class TL_starsTransactionPeerAds extends StarsTransactionPeer { + public static final int constructor = 0x60682812; + + public void readParams(AbstractSerializedData stream, boolean exception) {} + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + } + } + + public static class StarsTransaction extends TLObject { public int flags; public boolean refund; + public boolean pending; + public boolean failed; public String id; public long stars; public int date; @@ -81313,20 +81562,39 @@ public static class TL_starsTransaction extends TLObject { public String title; public String description; public WebDocument photo; + public int transaction_date; + public String transaction_url; + public byte[] bot_payload; + public int msg_id; + public ArrayList extended_media = new ArrayList<>(); - public static TL_starsTransaction TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { - if (TL_starsTransaction.constructor != constructor) { - if (exception) { - throw new RuntimeException(String.format("can't parse magic %x in TL_starsTransaction", constructor)); - } else { - return null; - } + public static StarsTransaction TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + StarsTransaction result = null; + switch (constructor) { + case TL_starsTransaction_layer181.constructor: + result = new TL_starsTransaction_layer181(); + break; + case TL_starsTransaction_layer182.constructor: + result = new TL_starsTransaction_layer182(); + break; + case TL_starsTransaction.constructor: + result = new TL_starsTransaction(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in StarsTransaction", constructor)); + } + if (result != null) { + result.readParams(stream, exception); } - TL_starsTransaction result = new TL_starsTransaction(); - result.readParams(stream, exception); return result; } + } + + public static class TL_starsTransaction_layer181 extends StarsTransaction { + public static final int constructor = 0xcc7079b2; + public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); refund = (flags & 8) != 0; @@ -81364,12 +81632,153 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_starsTransaction_layer182 extends TL_starsTransaction { + public static final int constructor = 0xaa00c898; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + refund = (flags & 8) != 0; + pending = (flags & 16) != 0; + failed = (flags & 64) != 0; + id = stream.readString(exception); + stars = stream.readInt64(exception); + date = stream.readInt32(exception); + peer = StarsTransactionPeer.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 1) != 0) { + title = stream.readString(exception); + } + if ((flags & 2) != 0) { + description = stream.readString(exception); + } + if ((flags & 4) != 0) { + photo = WebDocument.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 32) != 0) { + transaction_date = stream.readInt32(exception); + transaction_url = stream.readString(exception); + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = refund ? flags | 8 : flags &~ 8; + flags = pending ? flags | 16 : flags &~ 16; + flags = failed ? flags | 64 : flags &~ 64; + stream.writeInt32(flags); + stream.writeInt64(stars); + stream.writeInt32(date); + peer.serializeToStream(stream); + if ((flags & 1) != 0) { + stream.writeString(title); + } + if ((flags & 2) != 0) { + stream.writeString(description); + } + if ((flags & 4) != 0) { + photo.serializeToStream(stream); + } + if ((flags & 32) != 0) { + stream.writeInt32(transaction_date); + stream.writeString(transaction_url); + } + } + } + + public static class TL_starsTransaction extends StarsTransaction { + public static final int constructor = 0x2db5418f; + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + refund = (flags & 8) != 0; + pending = (flags & 16) != 0; + failed = (flags & 64) != 0; + id = stream.readString(exception); + stars = stream.readInt64(exception); + date = stream.readInt32(exception); + peer = StarsTransactionPeer.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 1) != 0) { + title = stream.readString(exception); + } + if ((flags & 2) != 0) { + description = stream.readString(exception); + } + if ((flags & 4) != 0) { + photo = WebDocument.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 32) != 0) { + transaction_date = stream.readInt32(exception); + transaction_url = stream.readString(exception); + } + if ((flags & 128) != 0) { + bot_payload = stream.readByteArray(exception); + } + if ((flags & 256) != 0) { + msg_id = stream.readInt32(exception); + } + if ((flags & 512) != 0) { + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + MessageMedia object = MessageMedia.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + extended_media.add(object); + } + } + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = refund ? flags | 8 : flags &~ 8; + flags = pending ? flags | 16 : flags &~ 16; + flags = failed ? flags | 64 : flags &~ 64; + stream.writeInt32(flags); + stream.writeInt64(stars); + stream.writeInt32(date); + peer.serializeToStream(stream); + if ((flags & 1) != 0) { + stream.writeString(title); + } + if ((flags & 2) != 0) { + stream.writeString(description); + } + if ((flags & 4) != 0) { + photo.serializeToStream(stream); + } + if ((flags & 32) != 0) { + stream.writeInt32(transaction_date); + stream.writeString(transaction_url); + } + if ((flags & 128) != 0) { + stream.writeByteArray(bot_payload); + } + if ((flags & 256) != 0) { + stream.writeInt32(msg_id); + } + if ((flags & 512) != 0) { + stream.writeInt32(0x1cb5c415); + int count = extended_media.size(); + stream.writeInt32(count); + for (int i = 0; i < count; ++i) { + extended_media.get(i).serializeToStream(stream); + } + } + } + } + public static class TL_payments_starsStatus extends TLObject { public static final int constructor = 0x8cf4ee60; public int flags; public long balance; - public ArrayList history = new ArrayList<>(); + public ArrayList history = new ArrayList<>(); public String next_offset; public ArrayList chats = new ArrayList<>(); public ArrayList users = new ArrayList<>(); @@ -81399,7 +81808,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { } int count = stream.readInt32(exception); for (int a = 0; a < count; a++) { - TL_starsTransaction object = TL_starsTransaction.TLdeserialize(stream, stream.readInt32(exception), exception); + StarsTransaction object = StarsTransaction.TLdeserialize(stream, stream.readInt32(exception), exception); if (object == null) { return; } @@ -81657,6 +82066,195 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_starsRevenueStatus extends TLObject { + public static final int constructor = 0x79342946; + + public int flags; + public boolean withdrawal_enabled; + public long current_balance; + public long available_balance; + public long overall_revenue; + public int next_withdrawal_at; + + public static TL_starsRevenueStatus TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_starsRevenueStatus.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_starsRevenueStatus", constructor)); + } else { + return null; + } + } + TL_starsRevenueStatus result = new TL_starsRevenueStatus(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + withdrawal_enabled = (flags & 1) != 0; + current_balance = stream.readInt64(exception); + available_balance = stream.readInt64(exception); + overall_revenue = stream.readInt64(exception); + if ((flags & 2) != 0) { + next_withdrawal_at = stream.readInt32(exception); + } + } + + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = withdrawal_enabled ? flags | 1 : flags &~ 1; + stream.writeInt32(flags); + stream.writeInt64(current_balance); + stream.writeInt64(available_balance); + stream.writeInt64(overall_revenue); + if ((flags & 2) != 0) { + stream.writeInt32(next_withdrawal_at); + } + } + } + + public static class TL_payments_starsRevenueStats extends TLObject { + public static final int constructor = 0xc92bb73b; + + public TL_stats.StatsGraph revenue_graph; + public TL_starsRevenueStatus status; + public double usd_rate; + + public static TL_payments_starsRevenueStats TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_payments_starsRevenueStats.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_payments_starsRevenueStats", constructor)); + } else { + return null; + } + } + TL_payments_starsRevenueStats result = new TL_payments_starsRevenueStats(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + revenue_graph = TL_stats.StatsGraph.TLdeserialize(stream, stream.readInt32(exception), exception); + status = TL_starsRevenueStatus.TLdeserialize(stream, stream.readInt32(exception), exception); + usd_rate = stream.readDouble(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + revenue_graph.serializeToStream(stream); + status.serializeToStream(stream); + stream.writeDouble(usd_rate); + } + } + + public static class TL_payments_starsRevenueWithdrawalUrl extends TLObject { + public static final int constructor = 0x1dab80b7; + + public String url; + + public static TL_payments_starsRevenueWithdrawalUrl TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_payments_starsRevenueWithdrawalUrl.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_payments_starsRevenueWithdrawalUrl", constructor)); + } else { + return null; + } + } + TL_payments_starsRevenueWithdrawalUrl result = new TL_payments_starsRevenueWithdrawalUrl(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + url = stream.readString(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(url); + } + } + + public static class TL_payments_starsRevenueAdsAccountUrl extends TLObject { + public static final int constructor = 0x394e7f21; + + public String url; + + public static TL_payments_starsRevenueAdsAccountUrl TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_payments_starsRevenueAdsAccountUrl.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_payments_starsRevenueWithdrawalUrl", constructor)); + } else { + return null; + } + } + TL_payments_starsRevenueAdsAccountUrl result = new TL_payments_starsRevenueAdsAccountUrl(); + result.readParams(stream, exception); + return result; + } + + public void readParams(AbstractSerializedData stream, boolean exception) { + url = stream.readString(exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeString(url); + } + } + + public static class TL_payments_getStarsRevenueStats extends TLObject { + public static final int constructor = 0xd91ffad6; + + public int flags; + public boolean dark; + public InputPeer peer; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_payments_starsRevenueStats.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = dark ? flags | 1 : flags &~ 1; + stream.writeInt32(flags); + peer.serializeToStream(stream); + } + } + + public static class TL_payments_getStarsRevenueWithdrawalUrl extends TLObject { + public static final int constructor = 0x13bbe8b3; + + public InputPeer peer; + public long stars; + public InputCheckPasswordSRP password; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_payments_starsRevenueWithdrawalUrl.TLdeserialize(stream, constructor, exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + stream.writeInt64(stars); + password.serializeToStream(stream); + } + } + + public static class TL_payments_getStarsRevenueAdsAccountUrl extends TLObject { + public static final int constructor = 0xd1d7efc5; + + public InputPeer peer; + + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_payments_starsRevenueAdsAccountUrl.TLdeserialize(stream, constructor, exception); + } + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stories.java b/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stories.java index 988bf7e545..35670094d8 100644 --- a/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stories.java +++ b/TMessagesProj/src/main/java/org/telegram/tgnet/tl/TL_stories.java @@ -1,5 +1,7 @@ package org.telegram.tgnet.tl; +import androidx.annotation.NonNull; + import org.telegram.messenger.DialogObject; import org.telegram.tgnet.AbstractSerializedData; import org.telegram.tgnet.TLObject; @@ -1231,6 +1233,173 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_foundStory extends TLObject { + public static final int constructor = 0xe87acbc0; + + public TLRPC.Peer peer; + public StoryItem storyItem; + + public static TL_foundStory TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_foundStory.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_foundStory", constructor)); + } else { + return null; + } + } + TL_foundStory result = new TL_foundStory(); + result.readParams(stream, exception); + return result; + } + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + peer = TLRPC.Peer.TLdeserialize(stream, stream.readInt32(exception), exception); + storyItem = StoryItem.TLdeserialize(stream, stream.readInt32(exception), exception); + } + + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + peer.serializeToStream(stream); + storyItem.serializeToStream(stream); + } + } + + public static class TL_foundStories extends TLObject { + public static final int constructor = 0xe2de7737; + + public int flags; + public int count; + public ArrayList stories = new ArrayList<>(); + public String next_offset; + public ArrayList chats = new ArrayList<>(); + public ArrayList users = new ArrayList<>(); + + public static TL_foundStories TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_foundStories.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_foundStories", constructor)); + } else { + return null; + } + } + TL_foundStories result = new TL_foundStories(); + result.readParams(stream, exception); + return result; + } + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + this.count = stream.readInt32(exception); + int magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + int count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TL_foundStory object = TL_foundStory.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + stories.add(object); + } + if ((flags & 1) != 0) { + next_offset = stream.readString(exception); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TLRPC.Chat object = TLRPC.Chat.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + chats.add(object); + } + magic = stream.readInt32(exception); + if (magic != 0x1cb5c415) { + if (exception) { + throw new RuntimeException(String.format("wrong Vector magic, got %x", magic)); + } + return; + } + count = stream.readInt32(exception); + for (int a = 0; a < count; a++) { + TLRPC.User object = TLRPC.User.TLdeserialize(stream, stream.readInt32(exception), exception); + if (object == null) { + return; + } + users.add(object); + } + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeInt32(this.count); + stream.writeInt32(0x1cb5c415); + int count = stories.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + stories.get(a).serializeToStream(stream); + } + if ((flags & 1) != 0) { + stream.writeString(next_offset); + } + stream.writeInt32(0x1cb5c415); + count = chats.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + chats.get(a).serializeToStream(stream); + } + stream.writeInt32(0x1cb5c415); + count = users.size(); + stream.writeInt32(count); + for (int a = 0; a < count; a++) { + users.get(a).serializeToStream(stream); + } + } + } + + public static class TL_stories_searchPosts extends TLObject { + public static final int constructor = 0x6cea116a; + + public int flags; + public String hashtag; + public MediaArea area; + public String offset; + public int limit; + + @Override + public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) { + return TL_foundStories.TLdeserialize(stream, constructor, exception); + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + if ((flags & 1) != 0) { + stream.writeString(hashtag); + } + if ((flags & 2) != 0) { + area.serializeToStream(stream); + } + stream.writeString(offset); + stream.writeInt32(limit); + } + } + public static class TL_stories_getStoriesViews extends TLObject { public static final int constructor = 0x28e16cc8; @@ -2839,27 +3008,69 @@ public void serializeToStream(AbstractSerializedData stream) { } } - public static class TL_mediaAreaCoordinates extends TLObject { - public static final int constructor = 0x3d1ea4e; + public static class MediaAreaCoordinates extends TLObject { + public int flags; public double x; public double y; public double w; public double h; public double rotation; + public double radius; - public static TL_mediaAreaCoordinates TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { - if (TL_mediaAreaCoordinates.constructor != constructor) { - if (exception) { - throw new RuntimeException(String.format("can't parse magic %x in TL_mediaAreaCoordinates", constructor)); - } else { - return null; - } + public static MediaAreaCoordinates TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + MediaAreaCoordinates result = null; + switch (constructor) { + case TL_mediaAreaCoordinates.constructor: + result = new TL_mediaAreaCoordinates(); + break; + case TL_mediaAreaCoordinates_layer181.constructor: + result = new TL_mediaAreaCoordinates_layer181(); + break; + } + if (result == null && exception) { + throw new RuntimeException(String.format("can't parse magic %x in MediaAreaCoordinates", constructor)); + } + if (result != null) { + result.readParams(stream, exception); } - TL_mediaAreaCoordinates result = new TL_mediaAreaCoordinates(); - result.readParams(stream, exception); return result; } + } + + public static class TL_mediaAreaCoordinates extends MediaAreaCoordinates { + public static final int constructor = 0xcfc9e002; + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + x = stream.readDouble(exception); + y = stream.readDouble(exception); + w = stream.readDouble(exception); + h = stream.readDouble(exception); + rotation = stream.readDouble(exception); + if ((flags & 1) != 0) { + radius = stream.readDouble(exception); + } + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeDouble(x); + stream.writeDouble(y); + stream.writeDouble(w); + stream.writeDouble(h); + stream.writeDouble(rotation); + if ((flags & 1) != 0) { + stream.writeDouble(radius); + } + } + } + + public static class TL_mediaAreaCoordinates_layer181 extends MediaAreaCoordinates { + public static final int constructor = 0x3d1ea4e; @Override public void readParams(AbstractSerializedData stream, boolean exception) { @@ -2882,7 +3093,7 @@ public void serializeToStream(AbstractSerializedData stream) { } public static class MediaArea extends TLObject { - public TL_mediaAreaCoordinates coordinates; + public MediaAreaCoordinates coordinates; public TLRPC.Reaction reaction; public int flags; public boolean dark; @@ -2891,17 +3102,17 @@ public static class MediaArea extends TLObject { public static MediaArea TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { MediaArea result = null; switch (constructor) { + case TL_mediaAreaUrl.constructor: + result = new TL_mediaAreaUrl(); + break; case TL_mediaAreaVenue.constructor: result = new TL_mediaAreaVenue(); break; case TL_mediaAreaGeoPoint.constructor: result = new TL_mediaAreaGeoPoint(); break; - case TL_inputMediaAreaVenue.constructor: - result = new TL_inputMediaAreaVenue(); - break; - case TL_inputMediaAreaChannelPost.constructor: - result = new TL_inputMediaAreaChannelPost(); + case TL_mediaAreaGeoPoint_layer181.constructor: + result = new TL_mediaAreaGeoPoint_layer181(); break; case TL_mediaAreaSuggestedReaction.constructor: result = new TL_mediaAreaSuggestedReaction(); @@ -2909,6 +3120,13 @@ public static MediaArea TLdeserialize(AbstractSerializedData stream, int constru case TL_mediaAreaChannelPost.constructor: result = new TL_mediaAreaChannelPost(); break; + + case TL_inputMediaAreaVenue.constructor: + result = new TL_inputMediaAreaVenue(); + break; + case TL_inputMediaAreaChannelPost.constructor: + result = new TL_inputMediaAreaChannelPost(); + break; } if (result == null && exception) { throw new RuntimeException(String.format("can't parse magic %x in MediaArea", constructor)); @@ -2927,7 +3145,7 @@ public void readParams(AbstractSerializedData stream, boolean exception) { flags = stream.readInt32(exception); dark = (flags & 1) != 0; flipped = (flags & 2) != 0; - coordinates = TL_mediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); + coordinates = MediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); reaction = TLRPC.Reaction.TLdeserialize(stream, stream.readInt32(exception), exception); } @@ -2948,7 +3166,7 @@ public static class TL_mediaAreaChannelPost extends MediaArea { public int msg_id; public void readParams(AbstractSerializedData stream, boolean exception) { - coordinates = TL_mediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); + coordinates = MediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); channel_id = stream.readInt64(exception); msg_id = stream.readInt32(exception); } @@ -2973,7 +3191,7 @@ public static class TL_mediaAreaVenue extends MediaArea { @Override public void readParams(AbstractSerializedData stream, boolean exception) { - coordinates = TL_mediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); + coordinates = MediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); geo = TLRPC.GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); title = stream.readString(exception); address = stream.readString(exception); @@ -2995,6 +3213,26 @@ public void serializeToStream(AbstractSerializedData stream) { } } + + public static class TL_mediaAreaUrl extends MediaArea { + public static final int constructor = 0x37381085; + + public String url; + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + coordinates = MediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); + url = stream.readString(exception); + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + coordinates.serializeToStream(stream); + stream.writeString(url); + } + } + public static class TL_inputMediaAreaVenue extends MediaArea { public static final int constructor = 0xb282217f; @@ -3003,7 +3241,7 @@ public static class TL_inputMediaAreaVenue extends MediaArea { @Override public void readParams(AbstractSerializedData stream, boolean exception) { - coordinates = TL_mediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); + coordinates = MediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); query_id = stream.readInt64(exception); result_id = stream.readString(exception); } @@ -3025,7 +3263,7 @@ public static class TL_inputMediaAreaChannelPost extends MediaArea { @Override public void readParams(AbstractSerializedData stream, boolean exception) { - coordinates = TL_mediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); + coordinates = MediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); channel = TLRPC.InputChannel.TLdeserialize(stream, stream.readInt32(exception), exception); msg_id = stream.readInt32(exception); } @@ -3039,14 +3277,100 @@ public void serializeToStream(AbstractSerializedData stream) { } } + public static class TL_geoPointAddress extends TLObject { + public static final int constructor = 0xde4c5d93; + + public int flags; + public String country_iso2; + public String state; + public String city; + public String street; + + @NonNull + @Override + public String toString() { + return "geo{country=" + country_iso2 + ", " + (state != null ? "state=" + state + ", " : "") + (city != null ? "city=" + city + ", " : "") + (street != null ? "street=" + street : "") + "}"; + } + + public static TL_geoPointAddress TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (TL_geoPointAddress.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in TL_geoPointAddress", constructor)); + } else { + return null; + } + } + TL_geoPointAddress result = new TL_geoPointAddress(); + result.readParams(stream, exception); + return result; + } + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + country_iso2 = stream.readString(exception); + if ((flags & 1) != 0) { + state = stream.readString(exception); + } + if ((flags & 2) != 0) { + city = stream.readString(exception); + } + if ((flags & 4) != 0) { + street = stream.readString(exception); + } + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + stream.writeString(country_iso2); + if ((flags & 1) != 0) { + stream.writeString(state); + } + if ((flags & 2) != 0) { + stream.writeString(city); + } + if ((flags & 4) != 0) { + stream.writeString(street); + } + } + } + public static class TL_mediaAreaGeoPoint extends MediaArea { - public static final int constructor = 0xdf8b3b22; + public static final int constructor = 0xcad5452d; public TLRPC.GeoPoint geo; + public TL_geoPointAddress address; + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + coordinates = MediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); + geo = TLRPC.GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); + if ((flags & 1) != 0) { + address = TL_geoPointAddress.TLdeserialize(stream, stream.readInt32(exception), exception); + } + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + stream.writeInt32(flags); + coordinates.serializeToStream(stream); + geo.serializeToStream(stream); + if ((flags & 1) != 0) { + address.serializeToStream(stream); + } + } + } + + public static class TL_mediaAreaGeoPoint_layer181 extends TL_mediaAreaGeoPoint { + public static final int constructor = 0xdf8b3b22; @Override public void readParams(AbstractSerializedData stream, boolean exception) { - coordinates = TL_mediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); + coordinates = MediaAreaCoordinates.TLdeserialize(stream, stream.readInt32(exception), exception); geo = TLRPC.GeoPoint.TLdeserialize(stream, stream.readInt32(exception), exception); } 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 454cc6361a..893d3c99e5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBar.java @@ -1628,6 +1628,10 @@ public void setItemsColor(int color, boolean isActionMode) { } public void setCastShadows(boolean value) { + if (castShadows != value && getParent() instanceof View) { + ((View) getParent()).invalidate(); + invalidate(); + } castShadows = value; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java index 2a4f22557b..3fb73ffea7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/ActionBarLayout.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; @@ -22,11 +24,14 @@ import android.graphics.Color; import android.graphics.Outline; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; import android.text.TextUtils; +import android.util.Log; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; @@ -113,7 +118,7 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (!fragmentsStack.isEmpty()) { lastFragment = fragmentsStack.get(fragmentsStack.size() - 1); } - if (storyViewerAttached() && lastFragment != null && lastFragment.getLastStoryViewer() != null && lastFragment.getLastStoryViewer().isFullyVisible() && lastFragment.getLastStoryViewer().windowView != child) { + if (storyViewerAttached() && lastFragment != null && lastFragment.getLastSheet() != null && lastFragment.getLastSheet().isFullyVisible() && lastFragment.getLastSheet().getWindowView() != child) { return true; } if (child instanceof ActionBar) { @@ -164,6 +169,14 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); int actionBarHeight = 0; + + View rootView = getRootView(); + getWindowVisibleDisplayFrame(rect); + int usableViewHeight = rootView.getHeight() - (rect.top != 0 ? AndroidUtilities.statusBarHeight : 0) - AndroidUtilities.getViewInset(rootView); + boolean isKeyboardVisible = usableViewHeight - (rect.bottom - rect.top) > 0; + + int bottomTabsHeight = isKeyboardVisible ? 0 : getBottomTabsHeight(false); + for (int a = 0; a < count; a++) { View child = getChildAt(a); if (child instanceof ActionBar) { @@ -176,9 +189,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { View child = getChildAt(a); if (!(child instanceof ActionBar)) { if (child.getFitsSystemWindows()) { - measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); + measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, bottomTabsHeight); } else { - measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, actionBarHeight); + measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, actionBarHeight + bottomTabsHeight); } } } @@ -202,9 +215,19 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { if (!(child instanceof ActionBar)) { FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) child.getLayoutParams(); if (child.getFitsSystemWindows()) { - child.layout(layoutParams.leftMargin, layoutParams.topMargin, layoutParams.leftMargin + child.getMeasuredWidth(), layoutParams.topMargin + child.getMeasuredHeight()); + child.layout( + layoutParams.leftMargin, + layoutParams.topMargin, + layoutParams.leftMargin + child.getMeasuredWidth(), + layoutParams.topMargin + child.getMeasuredHeight() + ); } else { - child.layout(layoutParams.leftMargin, layoutParams.topMargin + actionBarHeight, layoutParams.leftMargin + child.getMeasuredWidth(), layoutParams.topMargin + actionBarHeight + child.getMeasuredHeight()); + child.layout( + layoutParams.leftMargin, + layoutParams.topMargin + actionBarHeight, + layoutParams.leftMargin + child.getMeasuredWidth(), + layoutParams.topMargin + actionBarHeight + child.getMeasuredHeight() + ); } } } @@ -222,6 +245,12 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { @Override public boolean dispatchTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + final int bottomSheetHeight = isKeyboardVisible ? 0 : getBottomTabsHeight(true); + if (ev.getY() > getHeight() - bottomSheetHeight) { + return false; + } + } // processMenuButtonsTouch(ev); boolean passivePreview = inPreviewMode && previewMenu == null; if ((passivePreview || transitionAnimationPreviewMode) && (ev.getActionMasked() == MotionEvent.ACTION_DOWN || ev.getActionMasked() == MotionEvent.ACTION_POINTER_DOWN)) { @@ -280,7 +309,7 @@ public void processMenuButtonsTouch(MotionEvent event) { } else if (event.getAction() == MotionEvent.ACTION_MOVE || event.getAction() == MotionEvent.ACTION_UP) { if (previewMenu != null && highlightActionButtons) { // movePreviewFragment(Math.min(pressY, AndroidUtilities.displaySize.y * .4f) - event.getY()); - if (!allowToPressByHover && Math.sqrt(Math.pow(pressX - event.getX(), 2) + Math.pow(pressY - event.getY(), 2)) > AndroidUtilities.dp(30)) { + if (!allowToPressByHover && Math.sqrt(Math.pow(pressX - event.getX(), 2) + Math.pow(pressY - event.getY(), 2)) > dp(30)) { allowToPressByHover = true; } if (allowToPressByHover && (previewMenu.getSwipeBack() == null || !previewMenu.getSwipeBack().isForegroundOpen())) { @@ -351,10 +380,11 @@ public void processMenuButtonsTouch(MotionEvent event) { private boolean previewOpenAnimationInProgress; private ColorDrawable previewBackgroundDrawable; - public LayoutContainer containerView; /* Contest: private -> public temp hack bc I don't know how to blur action bar otherwise */ + public LayoutContainer containerView; private LayoutContainer containerViewBack; private DrawerLayoutContainer drawerLayoutContainer; private ActionBar currentActionBar; + private BottomSheetTabs bottomSheetTabs; private BaseFragment newFragment; private BaseFragment oldFragment; @@ -420,6 +450,7 @@ public void processMenuButtonsTouch(MotionEvent event) { private INavigationLayoutDelegate delegate; protected Activity parentActivity; + private final boolean main; private List fragmentsStack; private List pulledDialogs; @@ -429,9 +460,10 @@ public void processMenuButtonsTouch(MotionEvent event) { private int overrideWidthOffset = -1; - public ActionBarLayout(Context context) { + public ActionBarLayout(Context context, boolean main) { super(context); parentActivity = (Activity) context; + this.main = main; if (layerShadowDrawable == null) { layerShadowDrawable = getResources().getDrawable(R.drawable.layer_shadow); @@ -443,9 +475,27 @@ public ActionBarLayout(Context context) { @Override public void setFragmentStack(List stack) { this.fragmentsStack = stack; + + FrameLayout.LayoutParams layoutParams; + if (main) { + if (bottomSheetTabs != null) { + AndroidUtilities.removeFromParent(bottomSheetTabs); + bottomSheetTabs = null; + } + + bottomSheetTabs = new BottomSheetTabs(parentActivity, this); + layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp(68 + 8)); + layoutParams.gravity = Gravity.BOTTOM | Gravity.FILL_HORIZONTAL; + addView(bottomSheetTabs, layoutParams); + + if (LaunchActivity.instance.getBottomSheetTabsOverlay() != null) { + LaunchActivity.instance.getBottomSheetTabsOverlay().setTabsView(bottomSheetTabs); + } + } + this.containerViewBack = new LayoutContainer(parentActivity); addView(containerViewBack); - FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) containerViewBack.getLayoutParams(); + layoutParams = (FrameLayout.LayoutParams) containerViewBack.getLayoutParams(); layoutParams.width = LayoutHelper.MATCH_PARENT; layoutParams.height = LayoutHelper.MATCH_PARENT; layoutParams.gravity = Gravity.TOP | Gravity.LEFT; @@ -496,6 +546,7 @@ public void onConfigurationChanged(android.content.res.Configuration newConfig) } } + public boolean isKeyboardVisible; private int[] measureSpec = new int[2]; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @@ -517,9 +568,83 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { widthMeasureSpec = measureSpec[0]; heightMeasureSpec = measureSpec[1]; } + isKeyboardVisible = measureKeyboardHeight() > dp(20); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } + private int savedBottomSheetTabsTop; + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + final int count = getChildCount(); + + final int parentLeft = getPaddingLeft(); + final int parentRight = right - left - getPaddingRight(); + + final int parentTop = getPaddingTop(); + final int parentBottom = bottom - top - getPaddingBottom(); + + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() != GONE) { + if (child == bottomSheetTabs) { + bottomSheetTabs.updateCurrentAccount(); + } + + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + + final int width = child.getMeasuredWidth(); + final int height = child.getMeasuredHeight(); + + int childLeft; + int childTop; + + int gravity = lp.gravity; + if (gravity == -1) { + gravity = Gravity.TOP | Gravity.START; + } + + final int layoutDirection = getLayoutDirection(); + final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); + final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; + + switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + case Gravity.CENTER_HORIZONTAL: + childLeft = parentLeft + (parentRight - parentLeft - width) / 2 + + lp.leftMargin - lp.rightMargin; + break; + case Gravity.RIGHT: + childLeft = parentRight - width - lp.rightMargin; + break; + case Gravity.LEFT: + default: + childLeft = parentLeft + lp.leftMargin; + } + + switch (verticalGravity) { + case Gravity.TOP: + childTop = parentTop + lp.topMargin; + break; + case Gravity.CENTER_VERTICAL: + childTop = parentTop + (parentBottom - parentTop - height) / 2 + + lp.topMargin - lp.bottomMargin; + break; + case Gravity.BOTTOM: + childTop = parentBottom - height - lp.bottomMargin; + break; + default: + childTop = parentTop + lp.topMargin; + } + + if (child == bottomSheetTabs && savedBottomSheetTabsTop != 0 && (isKeyboardVisible || getParent() instanceof View && ((View) getParent()).getHeight() > getHeight())) { + childTop = savedBottomSheetTabsTop; + } else if (child == bottomSheetTabs) { + savedBottomSheetTabsTop = childTop; + } + child.layout(childLeft, childTop, childLeft + width, childTop + height); + } + } + } + @Override public void setInBubbleMode(boolean value) { inBubbleMode = value; @@ -640,6 +765,14 @@ public boolean dispatchKeyEventPreIme(KeyEvent event) { return super.dispatchKeyEventPreIme(event); } + private boolean withShadow; + + @Override + protected void dispatchDraw(Canvas canvas) { + withShadow = true; + super.dispatchDraw(canvas); + } + @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (drawerLayoutContainer != null && drawerLayoutContainer.isDrawCurrentPreviewFragmentAbove()) { @@ -657,11 +790,17 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { int clipRight = width + getPaddingLeft(); if (child == containerViewBack) { - clipRight = translationX + AndroidUtilities.dp(1); + clipRight = translationX + dp(1); } else if (child == containerView) { clipLeft = translationX; } + final int restoreCount2 = canvas.save(); + if (child != bottomSheetTabs) { + clipBottomSheetTabs(canvas, withShadow); + withShadow = false; + } + final int restoreCount = canvas.save(); if (!isTransitionAnimationInProgress() && !inPreviewMode) { canvas.clipRect(clipLeft, 0, clipRight, getHeight()); @@ -675,24 +814,62 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (translationX != 0 || overrideWidthOffset != -1) { int widthOffset = overrideWidthOffset != -1 ? overrideWidthOffset : width - translationX; if (child == containerView) { - float alpha = MathUtils.clamp(widthOffset / (float) AndroidUtilities.dp(20), 0, 1f); - layerShadowDrawable.setBounds(translationX - layerShadowDrawable.getIntrinsicWidth(), child.getTop(), translationX, child.getBottom()); + float alpha = MathUtils.clamp(widthOffset / (float) dp(20), 0, 1f); + layerShadowDrawable.setBounds(translationX - layerShadowDrawable.getIntrinsicWidth(), child.getTop(), translationX, child.getBottom() - getBottomTabsHeight(true)); layerShadowDrawable.setAlpha((int) (0xff * alpha)); layerShadowDrawable.draw(canvas); } else if (child == containerViewBack) { float opacity = MathUtils.clamp(widthOffset / (float) width, 0, 0.8f); scrimPaint.setColor(Color.argb((int)(0x99 * opacity), 0x00, 0x00, 0x00)); if (overrideWidthOffset != -1) { - canvas.drawRect(0, 0, getWidth(), getHeight(), scrimPaint); + canvas.drawRect(0, 0, getWidth(), getHeight() - getBottomTabsHeight(true), scrimPaint); } else { - canvas.drawRect(clipLeft, 0, clipRight, getHeight(), scrimPaint); + canvas.drawRect(clipLeft, 0, clipRight, getHeight() - getBottomTabsHeight(true), scrimPaint); } } } + canvas.restoreToCount(restoreCount2); return result; } + public void parentDraw(View parent, Canvas canvas) { + if (bottomSheetTabs != null && getHeight() < parent.getHeight()) { + canvas.save(); + canvas.translate(getX() + bottomSheetTabs.getX(), getY() + bottomSheetTabs.getY()); + bottomSheetTabs.draw(canvas); + canvas.restore(); + } + } + + private final RectF clipRect = new RectF(); + private final float[] clipRadius = new float[8]; + private final Path clipPath = new Path(); + private final Paint clipShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public void clipBottomSheetTabs(Canvas canvas, boolean withShadow) { + if (bottomSheetTabs == null) + return; + final int bottomSheetHeight = isKeyboardVisible ? 0 : getBottomTabsHeight(true); + final int bottomRadius = Math.min(1, bottomSheetHeight / dp(60)) * dp(10); + if (bottomSheetHeight <= 0) + return; + + clipRadius[0] = clipRadius[1] = clipRadius[2] = clipRadius[3] = 0; // top + clipRadius[4] = clipRadius[5] = clipRadius[6] = clipRadius[7] = bottomRadius; // bottom + + clipPath.rewind(); + clipRect.set(0, 0, getWidth(), bottomSheetTabs.getY() + bottomSheetTabs.getHeight() - bottomSheetHeight); + clipPath.addRoundRect(clipRect, clipRadius, Path.Direction.CW); + + clipShadowPaint.setAlpha(0); + if (withShadow) { + clipShadowPaint.setShadowLayer(dp(2), 0, dp(1), 0x10000000); + canvas.drawPath(clipPath, clipShadowPaint); + } + canvas.clipPath(clipPath); + } + public void setOverrideWidthOffset(int overrideWidthOffset) { this.overrideWidthOffset = overrideWidthOffset; invalidate(); @@ -742,9 +919,9 @@ private void drawPreviewDrawables(Canvas canvas, ViewGroup containerView) { previewBackgroundDrawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); previewBackgroundDrawable.draw(canvas); if (previewMenu == null) { - int width = AndroidUtilities.dp(32), height = width / 2; + int width = dp(32), height = width / 2; int x = (getMeasuredWidth() - width) / 2; - int y = (int) (view.getTop() + containerView.getTranslationY() - AndroidUtilities.dp(12 + (Build.VERSION.SDK_INT < 21 ? 20 : 0))); + int y = (int) (view.getTop() + containerView.getTranslationY() - dp(12 + (Build.VERSION.SDK_INT < 21 ? 20 : 0))); Theme.moveUpDrawable.setBounds(x, y, x + width, y + height); Theme.moveUpDrawable.draw(canvas); } @@ -803,7 +980,7 @@ private void onSlideAnimationEnd(final boolean backAnimation) { parent.removeViewInLayout(lastFragment.actionBar); } } - lastFragment.detachStoryViewer(); + lastFragment.detachSheets(); } layoutToIgnore = null; } @@ -847,7 +1024,7 @@ private void prepareForMoving(MotionEvent ev) { containerViewBack.addView(lastFragment.actionBar); lastFragment.actionBar.setTitleOverlayText(titleOverlayText, titleOverlayTextId, overlayAction); } - lastFragment.attachStoryViewer(containerViewBack); + lastFragment.attachSheets(containerViewBack); if (!lastFragment.hasOwnBackground && fragmentView.getBackground() == null) { fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); } @@ -944,12 +1121,13 @@ public boolean onTouchEvent(MotionEvent ev) { } } else { distToMove = x; - int duration = Math.max((int) (200.0f / containerView.getMeasuredWidth() * distToMove), 50); + int duration = Math.max((int) (320.0f / containerView.getMeasuredWidth() * distToMove), 120); if (!overrideTransition) { animatorSet.playTogether( ObjectAnimator.ofFloat(containerView, View.TRANSLATION_X, 0).setDuration(duration), ObjectAnimator.ofFloat(this, "innerTranslationX", 0.0f).setDuration(duration) ); + animatorSet.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); } } @@ -1064,7 +1242,7 @@ public boolean checkTransitionAnimation() { if (transitionAnimationPreviewMode) { return false; } - if (transitionAnimationInProgress && transitionAnimationStartTime < System.currentTimeMillis() - 1500) { + if (transitionAnimationInProgress && (transitionAnimationStartTime < System.currentTimeMillis() - 1500 || inPreviewMode)) { onAnimationEndCheck(true); } return transitionAnimationInProgress; @@ -1119,7 +1297,7 @@ private void presentFragmentInternalRemoveOld(boolean removeLast, final BaseFrag parent.removeViewInLayout(fragment.actionBar); } } - fragment.detachStoryViewer(); + fragment.detachSheets(); } containerViewBack.setVisibility(View.INVISIBLE); } @@ -1181,8 +1359,8 @@ public void run() { containerView.setScaleX(0.7f + 0.3f * interpolated); containerView.setScaleY(0.7f + 0.3f * interpolated); if (previewMenu != null) { - containerView.setTranslationY(AndroidUtilities.dp(40) * (1f - interpolated)); - previewMenu.setTranslationY(-AndroidUtilities.dp(40 + 30) * (1f - interpolated)); + containerView.setTranslationY(dp(40) * (1f - interpolated)); + previewMenu.setTranslationY(-dp(40 + 30) * (1f - interpolated)); previewMenu.setScaleX(0.95f + 0.05f * interpolated); previewMenu.setScaleY(0.95f + 0.05f * interpolated); } @@ -1191,7 +1369,7 @@ public void run() { containerView.invalidate(); invalidate(); } else { - containerView.setTranslationX(AndroidUtilities.dp(48) * (1.0f - interpolated)); + containerView.setTranslationX(dp(48) * (1.0f - interpolated)); } } else { float clampedReverseInterpolated = MathUtils.clamp(1f - interpolated, 0, 1); @@ -1206,7 +1384,7 @@ public void run() { containerView.invalidate(); invalidate(); } else { - containerViewBack.setTranslationX(AndroidUtilities.dp(48) * interpolated); + containerViewBack.setTranslationX(dp(48) * interpolated); } } if (animationProgress < 1) { @@ -1311,11 +1489,11 @@ public boolean presentFragment(NavigationParams params) { if (menu != null) { containerViewBack.addView(menu); menu.measure(MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.AT_MOST)); - menuHeight = menu.getMeasuredHeight() + AndroidUtilities.dp(24); + menuHeight = menu.getMeasuredHeight() + dp(24); FrameLayout.LayoutParams menuParams = (FrameLayout.LayoutParams) menu.getLayoutParams(); menuParams.width = LayoutHelper.WRAP_CONTENT; menuParams.height = LayoutHelper.WRAP_CONTENT; - menuParams.topMargin = getMeasuredHeight() - menuHeight - AndroidUtilities.dp(6); + menuParams.topMargin = getMeasuredHeight() - menuHeight - dp(6); menu.setLayoutParams(menuParams); } FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) wrappedView.getLayoutParams(); @@ -1328,14 +1506,14 @@ public boolean presentFragment(NavigationParams params) { layoutParams.height = height; layoutParams.topMargin = statusBarHeight + (getMeasuredHeight() - statusBarHeight - height) / 2; } else { - layoutParams.topMargin = layoutParams.bottomMargin = AndroidUtilities.dp(menu != null ? 0 : 24); + layoutParams.topMargin = layoutParams.bottomMargin = dp(menu != null ? 0 : 24); layoutParams.topMargin += AndroidUtilities.statusBarHeight; } if (menu != null) { - layoutParams.bottomMargin += menuHeight + AndroidUtilities.dp(8); + layoutParams.bottomMargin += menuHeight + dp(8); // layoutParams.topMargin += AndroidUtilities.dp(32); } - layoutParams.rightMargin = layoutParams.leftMargin = AndroidUtilities.dp(8); + layoutParams.rightMargin = layoutParams.leftMargin = dp(8); } else { layoutParams.topMargin = layoutParams.bottomMargin = layoutParams.rightMargin = layoutParams.leftMargin = 0; } @@ -1348,7 +1526,7 @@ public boolean presentFragment(NavigationParams params) { containerViewBack.addView(fragment.actionBar); fragment.actionBar.setTitleOverlayText(titleOverlayText, titleOverlayTextId, overlayAction); } - fragment.attachStoryViewer(containerViewBack); + fragment.attachSheets(containerViewBack); fragmentsStack.add(fragment); onFragmentStackChanged("presentFragment"); @@ -1372,11 +1550,11 @@ public boolean presentFragment(NavigationParams params) { @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public void getOutline(View view, Outline outline) { - outline.setRoundRect(0, AndroidUtilities.statusBarHeight, view.getMeasuredWidth(), view.getMeasuredHeight(), AndroidUtilities.dp(6)); + outline.setRoundRect(0, AndroidUtilities.statusBarHeight, view.getMeasuredWidth(), view.getMeasuredHeight(), dp(6)); } }); fragmentView.setClipToOutline(true); - fragmentView.setElevation(AndroidUtilities.dp(4)); + fragmentView.setElevation(dp(4)); } if (previewBackgroundDrawable == null) { previewBackgroundDrawable = new ColorDrawable(0x2e000000); @@ -1611,7 +1789,7 @@ public boolean addFragmentToStack(BaseFragment fragment, int position) { parent.removeView(previousFragment.fragmentView); } } - previousFragment.detachStoryViewer(); + previousFragment.detachSheets(); } fragmentsStack.add(fragment); if (position != INavigationLayout.FORCE_NOT_ATTACH_VIEW) { @@ -1665,7 +1843,7 @@ private void attachView(BaseFragment fragment) { containerView.addView(fragment.actionBar); fragment.actionBar.setTitleOverlayText(titleOverlayText, titleOverlayTextId, overlayAction); } - fragment.attachStoryViewer(containerView); + fragment.attachSheets(containerView); } private void attachViewTo(BaseFragment fragment, int position) { @@ -1694,7 +1872,7 @@ private void attachViewTo(BaseFragment fragment, int position) { containerView.addView(fragment.actionBar); fragment.actionBar.setTitleOverlayText(titleOverlayText, titleOverlayTextId, overlayAction); } - fragment.attachStoryViewer(containerView); + fragment.attachSheets(containerView); } private void closeLastFragmentInternalRemoveOld(BaseFragment fragment) { @@ -1718,7 +1896,7 @@ public void movePreviewFragment(float dy) { float nextTranslation = -dy; if (nextTranslation > 0) { nextTranslation = 0; - } else if (nextTranslation < -AndroidUtilities.dp(60)) { + } else if (nextTranslation < -dp(60)) { nextTranslation = 0; expandPreviewFragment(); } @@ -1843,7 +2021,7 @@ public void closeLastFragment(boolean animated, boolean forceNoAnimation) { containerView.addView(previousFragment.actionBar); previousFragment.actionBar.setTitleOverlayText(titleOverlayText, titleOverlayTextId, overlayAction); } - previousFragment.attachStoryViewer(containerView); + previousFragment.attachSheets(containerView); } newFragment = previousFragment; @@ -2017,7 +2195,7 @@ public void bringToFront(int i) { containerView.addView(previousFragment.actionBar); previousFragment.actionBar.setTitleOverlayText(titleOverlayText, titleOverlayTextId, overlayAction); } - previousFragment.attachStoryViewer(containerView); + previousFragment.attachSheets(containerView); previousFragment.onResume(); currentActionBar = previousFragment.actionBar; if (!previousFragment.hasOwnBackground && fragmentView.getBackground() == null) { @@ -2635,10 +2813,27 @@ public int measureKeyboardHeight() { return Math.max(0, usableViewHeight - (rect.bottom - rect.top)); } + private boolean tabsEvents; @Override public boolean dispatchTouchEvent(MotionEvent ev) { - if (getLastFragment() != null && getLastFragment().getLastStoryViewer() != null && getLastFragment().getLastStoryViewer().attachedToParent()) { - return getLastFragment().getLastStoryViewer().windowView.dispatchTouchEvent(ev); + final boolean tabs = ev.getY() > getHeight() - getBottomTabsHeight(true); + if ( + getLastFragment() != null && + getLastFragment().getLastSheet() != null && + getLastFragment().getLastSheet().attachedToParent() + ) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + tabsEvents = tabs; + } + if (!tabsEvents) { + if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + tabsEvents = false; + } + return getLastFragment().getLastSheet().getWindowView().dispatchTouchEvent(ev); + } + } + if (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL) { + tabsEvents = false; } return super.dispatchTouchEvent(ev); } @@ -2658,4 +2853,69 @@ public Window getWindow() { } return null; } + + @Override + public BottomSheetTabs getBottomSheetTabs() { + return bottomSheetTabs; + } + + @Override + public void setNavigationBarColor(int color) { + if (bottomSheetTabs != null) { + bottomSheetTabs.setNavigationBarColor(color, !(startedTracking || animationInProgress)); + } + } + + private ValueAnimator bottomTabsAnimator; + private float bottomTabsProgress; + private int bottomTabsHeight; + + public void updateBottomTabsVisibility(boolean animated) { + if (bottomSheetTabs == null) { + return; + } + if (bottomTabsAnimator != null) { + ValueAnimator prev = bottomTabsAnimator; + bottomTabsAnimator = null; + prev.cancel(); + } + if (bottomTabsHeight == bottomSheetTabs.getExpandedHeight()) + return; + bottomTabsHeight = bottomSheetTabs.getExpandedHeight(); + requestLayout(); + containerView.requestLayout(); + containerViewBack.requestLayout(); + if (animated) { + bottomTabsAnimator = ValueAnimator.ofFloat(bottomTabsProgress, bottomTabsHeight); + bottomTabsAnimator.addUpdateListener(anm -> { + bottomTabsProgress = (float) anm.getAnimatedValue(); + invalidate(); + }); + bottomTabsAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (bottomTabsAnimator == animation) { + bottomTabsProgress = bottomTabsHeight; + invalidate(); + } + } + }); + bottomTabsAnimator.setDuration(AdjustPanLayoutHelper.keyboardDuration); + bottomTabsAnimator.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator); + bottomTabsAnimator.start(); + } else { + bottomTabsProgress = bottomTabsHeight; + } + } + + @Override + public int getBottomTabsHeight(boolean animated) { + if (!main) return 0; + if (animated) { + return (int) bottomTabsProgress; + } else { + return bottomTabsHeight; + } + } + } \ No newline at end of file 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 92c46ceec7..7dd1f25d6a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BaseFragment.java @@ -32,6 +32,7 @@ import android.widget.FrameLayout; import androidx.annotation.CallSuper; +import androidx.annotation.Nullable; import androidx.core.graphics.ColorUtils; import org.telegram.messenger.AccountInstance; @@ -55,6 +56,7 @@ import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.LaunchActivity; import org.telegram.ui.Stories.StoryViewer; +import org.telegram.ui.bots.BotWebViewAttachedSheet; import java.util.ArrayList; @@ -81,30 +83,69 @@ public abstract class BaseFragment { private boolean removingFromStack; private PreviewDelegate previewDelegate; protected Theme.ResourcesProvider resourceProvider; - public ArrayList storyViewerStack; +// public ArrayList storyViewerStack; +// public ArrayList botsStack; +// + public ArrayList sheetsStack; + public static interface AttachedSheet { + public View getWindowView(); + public boolean isShown(); + public void dismiss(); + public void release(); + + public boolean isFullyVisible(); + + public boolean attachedToParent(); + + public boolean onBackPressed(); + public boolean showDialog(Dialog dialog); + + public void setKeyboardHeightFromParent(int keyboardHeight); + + public int getNavigationBarColor(int color); + + public void setOnDismissListener(Runnable onDismiss); + } + + @Nullable public StoryViewer getLastStoryViewer() { - if (storyViewerStack == null || storyViewerStack.isEmpty()) + if (sheetsStack == null || sheetsStack.isEmpty()) return null; - for (int i = storyViewerStack.size() - 1; i >= 0; --i) { - if (storyViewerStack.get(i).isShown()) { - return storyViewerStack.get(i); + for (int i = sheetsStack.size() - 1; i >= 0; --i) { + if (sheetsStack.get(i) instanceof StoryViewer && sheetsStack.get(i).isShown()) { + return (StoryViewer) sheetsStack.get(i); + } + } + return null; + } + + public AttachedSheet getLastSheet() { + if (sheetsStack == null || sheetsStack.isEmpty()) + return null; + for (int i = sheetsStack.size() - 1; i >= 0; --i) { + if (sheetsStack.get(i).isShown()) { + return sheetsStack.get(i); } } return null; } public boolean hasStoryViewer() { - return storyViewerStack != null && !storyViewerStack.isEmpty(); + return getLastStoryViewer() != null; + } + + public boolean hasSheet() { + return sheetsStack != null && !sheetsStack.isEmpty(); } - public void clearStoryViewers() { - if (storyViewerStack == null || storyViewerStack.isEmpty()) + public void clearSheets() { + if (sheetsStack == null || sheetsStack.isEmpty()) return; - for (int i = storyViewerStack.size() - 1; i >= 0; --i) { - storyViewerStack.get(i).release(); + for (int i = sheetsStack.size() - 1; i >= 0; --i) { + sheetsStack.get(i).release(); } - storyViewerStack.clear(); + sheetsStack.clear(); } public BaseFragment() { @@ -237,7 +278,7 @@ public void clearViews() { } actionBar = null; } - clearStoryViewers(); + clearSheets(); parentLayout = null; } @@ -266,7 +307,7 @@ public void setParentLayout(INavigationLayout layout) { } if (parentLayout != null && parentLayout.getView().getContext() != fragmentView.getContext()) { fragmentView = null; - clearStoryViewers(); + clearSheets(); } } if (actionBar != null) { @@ -455,17 +496,17 @@ public void onConfigurationChanged(android.content.res.Configuration newConfig) } public boolean onBackPressed() { - if (closeStoryViewer()) { + if (closeSheet()) { return false; } return true; } - public boolean closeStoryViewer() { - if (storyViewerStack != null) { - for (int i = storyViewerStack.size() - 1; i >= 0; --i) { - if (storyViewerStack.get(i).isShown()) { - return storyViewerStack.get(i).onBackPressed(); + public boolean closeSheet() { + if (sheetsStack != null) { + for (int i = sheetsStack.size() - 1; i >= 0; --i) { + if (sheetsStack.get(i).isShown()) { + return sheetsStack.get(i).onBackPressed(); } } } @@ -652,11 +693,12 @@ public Dialog showDialog(Dialog dialog, boolean allowInTransition, final Dialog. if (dialog == null || parentLayout == null || parentLayout.isTransitionAnimationInProgress() || parentLayout.isSwipeInProgress() || !allowInTransition && parentLayout.checkTransitionAnimation()) { return null; } - if (storyViewerStack != null) { - for (int i = storyViewerStack.size() - 1; i >= 0; --i) { - if (storyViewerStack.get(i).isShown()) { - storyViewerStack.get(i).showDialog(dialog); - return dialog; + if (sheetsStack != null) { + for (int i = sheetsStack.size() - 1; i >= 0; --i) { + if (sheetsStack.get(i).isShown()) { + if (sheetsStack.get(i).showDialog(dialog)) { + return dialog; + } } } } @@ -819,7 +861,7 @@ public INavigationLayout[] showAsSheet(BaseFragment fragment, BottomSheetParams return null; } BottomSheet[] bottomSheet = new BottomSheet[1]; - INavigationLayout[] actionBarLayout = new INavigationLayout[]{INavigationLayout.newLayout(getParentActivity(), () -> bottomSheet[0])}; + INavigationLayout[] actionBarLayout = new INavigationLayout[]{INavigationLayout.newLayout(getParentActivity(), false, () -> bottomSheet[0])}; actionBarLayout[0].setIsSheet(true); LaunchActivity.instance.sheetFragmentsStack.add(actionBarLayout[0]); fragment.onTransitionAnimationStart(true, false); @@ -948,11 +990,11 @@ public boolean hasForceLightStatusBar() { public int getNavigationBarColor() { int color = Theme.getColor(Theme.key_windowBackgroundGray, getResourceProvider()); - if (storyViewerStack != null) { - for (int i = storyViewerStack.size() - 1; i >= 0; --i) { - StoryViewer storyViewer = storyViewerStack.get(i); - if (storyViewer.attachedToParent()) { - color = storyViewer.getNavigationBarColor(color); + if (sheetsStack != null) { + for (int i = 0; i < sheetsStack.size(); ++i) { + AttachedSheet sheet = sheetsStack.get(i); + if (sheet.attachedToParent()) { + color = sheet.getNavigationBarColor(color); } } } @@ -974,6 +1016,9 @@ public void setNavigationBarColor(int color) { } } } + if (parentLayout != null) { + parentLayout.setNavigationBarColor(color); + } } public boolean isBeginToShow() { @@ -1057,34 +1102,46 @@ public void onFragmentClosed() { } - public void attachStoryViewer(ActionBarLayout.LayoutContainer parentLayout) { - if (storyViewerStack != null) { - for (int i = 0; i < storyViewerStack.size(); ++i) { - StoryViewer storyViewer = storyViewerStack.get(i); - if (storyViewer != null && storyViewer.attachedToParent()) { - AndroidUtilities.removeFromParent(storyViewer.windowView); - parentLayout.addView(storyViewer.windowView); + public void attachSheets(ActionBarLayout.LayoutContainer parentLayout) { + if (sheetsStack != null) { + for (int i = 0; i < sheetsStack.size(); ++i) { + AttachedSheet sheet = sheetsStack.get(i); + if (sheet != null && sheet.attachedToParent()) { + AndroidUtilities.removeFromParent(sheet.getWindowView()); + parentLayout.addView(sheet.getWindowView()); } } } } - public void detachStoryViewer() { - if (storyViewerStack != null) { - for (int i = 0; i < storyViewerStack.size(); ++i) { - StoryViewer storyViewer = storyViewerStack.get(i); - if (storyViewer != null && storyViewer.attachedToParent()) { - AndroidUtilities.removeFromParent(storyViewer.windowView); + public void detachSheets() { + if (sheetsStack != null) { + for (int i = 0; i < sheetsStack.size(); ++i) { + AttachedSheet sheet = sheetsStack.get(i); + if (sheet != null && sheet.attachedToParent()) { + AndroidUtilities.removeFromParent(sheet.getWindowView()); } } } } public boolean isStoryViewer(View child) { - if (storyViewerStack != null) { - for (int i = 0; i < storyViewerStack.size(); ++i) { - StoryViewer storyViewer = storyViewerStack.get(i); - if (storyViewer != null && child == storyViewer.windowView) { + if (sheetsStack != null) { + for (int i = 0; i < sheetsStack.size(); ++i) { + AttachedSheet sheet = sheetsStack.get(i); + if (sheet instanceof StoryViewer && child == sheet.getWindowView()) { + return true; + } + } + } + return false; + } + + public boolean isBotView(View child) { + if (sheetsStack != null) { + for (int i = 0; i < sheetsStack.size(); ++i) { + AttachedSheet sheet = sheetsStack.get(i); + if (sheet instanceof BotWebViewAttachedSheet && child == sheet.getWindowView()) { return true; } } @@ -1093,9 +1150,9 @@ public boolean isStoryViewer(View child) { } public void setKeyboardHeightFromParent(int keyboardHeight) { - if (storyViewerStack != null) { - for (int i = 0; i < storyViewerStack.size(); ++i) { - StoryViewer storyViewer = storyViewerStack.get(i); + if (sheetsStack != null) { + for (int i = 0; i < sheetsStack.size(); ++i) { + AttachedSheet storyViewer = sheetsStack.get(i); if (storyViewer != null) { storyViewer.setKeyboardHeightFromParent(keyboardHeight); } @@ -1108,31 +1165,48 @@ public interface PreviewDelegate { } public StoryViewer getOrCreateStoryViewer() { - if (storyViewerStack == null) { - storyViewerStack = new ArrayList<>(); + if (sheetsStack == null) { + sheetsStack = new ArrayList<>(); } - if (storyViewerStack.isEmpty()) { - StoryViewer storyViewer = new StoryViewer(this); + StoryViewer storyViewer = null; + if (!sheetsStack.isEmpty() && sheetsStack.get(sheetsStack.size() - 1) instanceof StoryViewer) { + storyViewer = (StoryViewer) sheetsStack.get(sheetsStack.size() - 1); + } + if (storyViewer == null) { + storyViewer = new StoryViewer(this); if (parentLayout != null && parentLayout.isSheet()) { storyViewer.fromBottomSheet = true; } - storyViewerStack.add(storyViewer); + sheetsStack.add(storyViewer); } - return storyViewerStack.get(0); + return storyViewer; } public StoryViewer createOverlayStoryViewer() { - if (storyViewerStack == null) { - storyViewerStack = new ArrayList<>(); + if (sheetsStack == null) { + sheetsStack = new ArrayList<>(); } StoryViewer storyViewer = new StoryViewer(this); if (parentLayout != null && parentLayout.isSheet()) { storyViewer.fromBottomSheet = true; } - storyViewerStack.add(storyViewer); + sheetsStack.add(storyViewer); return storyViewer; } + public BotWebViewAttachedSheet createBotViewer() { + if (sheetsStack == null) { + sheetsStack = new ArrayList<>(); + } + BotWebViewAttachedSheet botViewer = new BotWebViewAttachedSheet(this); + StoryViewer storyViewer = getLastStoryViewer(); + if (storyViewer != null) { + storyViewer.listenToAttachedSheet(botViewer); + } + sheetsStack.add(botViewer); + return botViewer; + } + public void onBottomSheetCreated() { } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java index 341049e5c5..442d531793 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheet.java @@ -191,6 +191,7 @@ public void setDisableScroll(boolean b) { private ValueAnimator keyboardContentAnimator; public boolean smoothKeyboardAnimationEnabled; + public boolean smoothKeyboardByBottom; private boolean openNoDelay; private float hideSystemVerticalInsetsProgress; @@ -587,8 +588,8 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto if (lastInsets != null && Build.VERSION.SDK_INT >= 21) { l += getLeftInset(); } - if (smoothKeyboardAnimationEnabled && startAnimationRunnable == null && keyboardChanged && !dismissed && containerView.getTop() != t || smoothContainerViewLayoutUntil > 0 && System.currentTimeMillis() < smoothContainerViewLayoutUntil) { - containerView.setTranslationY(containerView.getTop() - t); + if (smoothKeyboardAnimationEnabled && startAnimationRunnable == null && keyboardChanged && !dismissed && (smoothKeyboardByBottom ? containerView.getBottom() != t + containerView.getMeasuredHeight() : containerView.getTop() != t) || smoothContainerViewLayoutUntil > 0 && System.currentTimeMillis() < smoothContainerViewLayoutUntil) { + containerView.setTranslationY(smoothKeyboardByBottom ? containerView.getBottom() - (t + containerView.getMeasuredHeight()) : containerView.getTop() - t); onSmoothContainerViewLayout(containerView.getTranslationY()); if (keyboardContentAnimator != null) { keyboardContentAnimator.cancel(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabs.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabs.java new file mode 100644 index 0000000000..99ab9c182f --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabs.java @@ -0,0 +1,692 @@ +package org.telegram.ui.ActionBar; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.lerp; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; +import android.webkit.WebView; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.graphics.ColorUtils; + +import org.checkerframework.checker.units.qual.A; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ContactsController; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ChatActivity; +import org.telegram.ui.Components.AnimatedColor; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.Text; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.bots.BotWebViewAttachedSheet; +import org.telegram.ui.bots.BotWebViewContainer; +import org.telegram.ui.bots.BotWebViewSheet; + +import java.io.CharArrayReader; +import java.util.ArrayList; +import java.util.HashMap; + +public class BottomSheetTabs extends FrameLayout { + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + public boolean drawTabs = true; + + private final ActionBarLayout actionBarLayout; + + public BottomSheetTabs(Context context, ActionBarLayout actionBarLayout) { + super(context); + this.actionBarLayout = actionBarLayout; + + setNavigationBarColor(Theme.getColor(Theme.key_windowBackgroundGray)); + + setOnClickListener(v -> { + final ArrayList tabs = getTabs(); + + final int count = tabs.size(); + if (count == 0) return; + WebTabData lastTab = tabs.get(tabs.size() - 1); + BottomSheetTabsOverlay overlay = LaunchActivity.instance.getBottomSheetTabsOverlay(); + + if (count == 1 || overlay == null) { + openTab(lastTab); + } else { + overlay.openTabsView(); + } + }); + + updateMultipleTitle(); + } + + public void openTab(WebTabData tab) { + BaseFragment lastFragment = LaunchActivity.getLastFragment(); + if (lastFragment == null || lastFragment.getParentActivity() == null) return; + if (lastFragment instanceof ChatActivity) { + if (((ChatActivity) lastFragment).getChatActivityEnterView() != null) { + ((ChatActivity) lastFragment).getChatActivityEnterView().closeKeyboard(); + ((ChatActivity) lastFragment).getChatActivityEnterView().hidePopup(true, false); + } + } + boolean closed = closeAttachedSheets(); + Utilities.Callback open = fragment -> { + if (fragment instanceof ChatActivity) { + if (((ChatActivity) fragment).getChatActivityEnterView() != null) { + ((ChatActivity) fragment).getChatActivityEnterView().closeKeyboard(); + ((ChatActivity) fragment).getChatActivityEnterView().hidePopup(true, false); + } + } + if (AndroidUtilities.isTablet()) { + BotWebViewSheet sheet = new BotWebViewSheet(fragment.getContext(), fragment.getResourceProvider()); + sheet.setParentActivity(fragment.getParentActivity()); + if (sheet.restoreState(fragment, tab)) { + removeTab(tab, false); + sheet.show(); + } + } else { + BotWebViewAttachedSheet webViewSheet = fragment.createBotViewer(); + webViewSheet.setParentActivity(fragment.getParentActivity()); + if (webViewSheet.restoreState(fragment, tab)) { + removeTab(tab, false); + webViewSheet.show(closed); + } + } + }; + if (tab.needsContext && (!(lastFragment instanceof ChatActivity) || ((ChatActivity) lastFragment).getDialogId() != tab.props.botId)) { + BaseFragment chatActivity = ChatActivity.of(tab.props.botId); + lastFragment.presentFragment(chatActivity); + AndroidUtilities.runOnUIThread(() -> { + open.run(chatActivity); + }, 200); + } else { + open.run(lastFragment); + } + } + + public WebTabData tryReopenTab(BotWebViewAttachedSheet.WebViewRequestProps props) { + ArrayList tabs = this.tabs.get(currentAccount); + if (tabs == null) this.tabs.put(currentAccount, tabs = new ArrayList<>()); + + if (props == null) return null; + for (int i = 0; i < tabs.size(); ++i) { + WebTabData tab = tabs.get(i); + if (props.equals(tab.props)) { + openTab(tab); + return tab; + } + } + return null; + } + + public boolean closeAttachedSheets() { + boolean had = false; + BottomSheetTabsOverlay overlay = LaunchActivity.instance.getBottomSheetTabsOverlay(); + BaseFragment fragment = LaunchActivity.getSafeLastFragment(); + if (fragment != null) { + for (int i = 0; fragment.sheetsStack != null && i < fragment.sheetsStack.size(); ++i) { + BaseFragment.AttachedSheet sheet = fragment.sheetsStack.get(i); + if (sheet instanceof BotWebViewAttachedSheet) { + if (overlay != null) { + overlay.setSlowerDismiss(true); + } + ((BotWebViewAttachedSheet) sheet).dismiss(true, null); + had = true; + } + } + } + return had; + } + + private int backgroundColor; + private AnimatedColor backgroundColorAnimated = new AnimatedColor(this, 0, 200, CubicBezierInterpolator.EASE_OUT_QUINT); + private int tabColor; + private AnimatedColor tabColorAnimated = new AnimatedColor(this, 0, 200, CubicBezierInterpolator.EASE_OUT_QUINT); + private boolean tabIsDark; + private AnimatedFloat tabDarkAnimated = new AnimatedFloat(this, 0, 200, CubicBezierInterpolator.EASE_OUT_QUINT); + + public void setNavigationBarColor(int color) { + setNavigationBarColor(color, true); + } + + public void setNavigationBarColor(int color, boolean animated) { + if (color != backgroundColor) { + if (!actionBarLayout.startedTracking || actionBarLayout.animationInProgress) { + animated = false; + } + backgroundColor = color; + final boolean isDark = AndroidUtilities.computePerceivedBrightness(color) < .721f; + tabColor = Theme.blendOver(color, Theme.multAlpha(0xFFFFFFFF, isDark ? .08f : .75f)); + tabIsDark = AndroidUtilities.computePerceivedBrightness(tabColor) < .721f; + if (!animated) { + backgroundColorAnimated.set(backgroundColor, true); + tabColorAnimated.set(tabColor, true); + tabDarkAnimated.set(tabIsDark, true); + } + invalidate(); + } + } + + public int currentAccount = UserConfig.selectedAccount; + public final HashMap> tabs = new HashMap<>(); + public final HashMap> tabDrawables = new HashMap<>(); + + public void updateCurrentAccount() { + setCurrentAccount(UserConfig.selectedAccount); + } + + public void setCurrentAccount(int account) { + if (currentAccount != account) { + currentAccount = account; + + actionBarLayout.updateBottomTabsVisibility(false); + invalidate(); + } + } + + public boolean isExpanded() { + return !getTabs().isEmpty(); + } + + public int getExpandedHeight() { + final int count = getTabs().size(); + if (count == 0) { + return 0; + } else if (count == 1) { + return dp(60); + } else { + return dp(68); + } + } + + public ArrayList getTabs() { + ArrayList tabs = this.tabs.get(currentAccount); + if (tabs == null) this.tabs.put(currentAccount, tabs = new ArrayList<>()); + return tabs; + } + + public ArrayList getTabDrawables() { + ArrayList tabDrawables = this.tabDrawables.get(currentAccount); + if (tabDrawables == null) this.tabDrawables.put(currentAccount, tabDrawables = new ArrayList<>()); + return tabDrawables; + } + + public TabDrawable findTabDrawable(WebTabData tab) { + final ArrayList tabDrawables = getTabDrawables(); + + for (int i = 0; i < tabDrawables.size(); ++i) { + if (tabDrawables.get(i).tab == tab) { + return tabDrawables.get(i); + } + } + return null; + } + + public TabDrawable pushTab(WebTabData tab) { + final ArrayList tabs = getTabs(); + final ArrayList tabDrawables = getTabDrawables(); + + TabDrawable tabDrawable = new TabDrawable(this, tab); + tabDrawable.animatedPosition.set(-1, true); + tabDrawable.animatedAlpha.set(0, true); + tabDrawables.add(tabDrawable); + + tabs.add(0, tab); + for (int i = 0; i < tabDrawables.size(); ++i) { + TabDrawable drawable = tabDrawables.get(i); + final int index = tabs.indexOf(drawable.tab); + drawable.index = index; + if (index >= 0) { + drawable.position = index; + } + } + updateMultipleTitle(); + + actionBarLayout.updateBottomTabsVisibility(true); + + invalidate(); + return tabDrawable; + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return super.verifyDrawable(who) || true; + } + + private void updateMultipleTitle() { + final ArrayList tabs = getTabs(); + final ArrayList tabDrawables = getTabDrawables(); + + String title = null; + for (int i = 0; i < tabDrawables.size(); ++i) { + TabDrawable drawable = tabDrawables.get(i); + + if (tabs.size() > 1 && drawable.position == 0) { + TLRPC.User user = MessagesController.getInstance(drawable.tab.props.currentAccount).getUser(drawable.tab.props.botId); + title = LocaleController.formatPluralString("BotMoreTabs", tabs.size() - 1, UserObject.getUserName(user)); + drawable.setOverrideTitle(title); + } else { + TLRPC.User user = MessagesController.getInstance(drawable.tab.props.currentAccount).getUser(drawable.tab.props.botId); + title = UserObject.getUserName(user); + drawable.setOverrideTitle(null); + } + } + + if (tabs.isEmpty()) { + setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO); + setContentDescription(LocaleController.formatString(R.string.AccDescrTabs, "")); + } else { + setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); + setContentDescription(LocaleController.formatString(R.string.AccDescrTabs, title == null ? "" : title)); + } + } + + public boolean removeAll() { + final ArrayList tabs = getTabs(); + final ArrayList tabDrawables = getTabDrawables(); + + for (int i = 0; i < tabs.size(); ++i) { + tabs.get(i).destroy(); + } + tabs.clear(); + for (int i = 0; i < tabDrawables.size(); ++i) { + TabDrawable drawable = tabDrawables.get(i); + drawable.index = -1; + } + updateMultipleTitle(); + actionBarLayout.updateBottomTabsVisibility(true); + invalidate(); + return tabs.isEmpty(); + } + + public void removeTab(WebTabData tab, Utilities.Callback callback) { + if (tab == null) { + callback.run(true); + return; + } + if (!tab.confirmDismiss) { + removeTab(tab, true); + callback.run(true); + return; + } + + String botName = null; + TLRPC.User user = MessagesController.getInstance(tab.props.currentAccount).getUser(tab.props.botId); + if (user != null) { + botName = ContactsController.formatName(user.first_name, user.last_name); + } + + final boolean[] clicked = new boolean[] { false }; + final AlertDialog[] dialog = new AlertDialog[1]; + dialog[0] = new AlertDialog.Builder(getContext()) + .setTitle(botName) + .setMessage(LocaleController.getString(R.string.BotWebViewChangesMayNotBeSaved)) + .setPositiveButton(LocaleController.getString(R.string.BotWebViewCloseAnyway), (d, w) -> { + clicked[0] = true; + removeTab(tab, true); + callback.run(true); + dialog[0].dismiss(); + }) + .setNegativeButton(LocaleController.getString(R.string.Cancel), (d, w) -> { + clicked[0] = true; + callback.run(false); + dialog[0].dismiss(); + }) + .create(); + dialog[0].setOnDismissListener(v -> { + if (!clicked[0]) { + callback.run(false); + clicked[0] = true; + } + }); + dialog[0].show(); + TextView textView = (TextView) dialog[0].getButton(AlertDialog.BUTTON_POSITIVE); + textView.setTextColor(Theme.getColor(Theme.key_text_RedBold)); + } + + public boolean removeTab(WebTabData tab, boolean destroy) { + final ArrayList tabs = getTabs(); + final ArrayList tabDrawables = getTabDrawables(); + + tabs.remove(tab); + if (destroy) { + tab.destroy(); + } + for (int i = 0; i < tabDrawables.size(); ++i) { + TabDrawable drawable = tabDrawables.get(i); + final int index = tabs.indexOf(drawable.tab); + drawable.index = index; + if (index >= 0) { + drawable.position = index; + } + } + updateMultipleTitle(); + final ArrayList finalTabDrawables = tabDrawables; + AndroidUtilities.runOnUIThread(() -> { + for (int i = 0; i < finalTabDrawables.size(); ++i) { + TabDrawable drawable = finalTabDrawables.get(i); + if (drawable.tab == tab) { + finalTabDrawables.remove(i); + i--; + } + } + invalidate(); + }, 320); + actionBarLayout.updateBottomTabsVisibility(true); + invalidate(); + return tabs.isEmpty(); + } + + private boolean closeRippleHit; + @Override + public boolean onTouchEvent(MotionEvent event) { + final ArrayList tabs = getTabs(); + final ArrayList tabDrawables = getTabDrawables(); + + if (drawTabs) { + + WebTabData lastTab = tabs.isEmpty() ? null : tabs.get(0); + TabDrawable drawable = findTabDrawable(lastTab); + + if (drawable != null) { + getTabBounds(rect, drawable.getPosition()); + final boolean closeHit = drawable.closeRipple.getBounds().contains((int) (event.getX() - rect.left), (int) (event.getY() - rect.centerY())); + if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) { + closeRippleHit = closeHit; + drawable.closeRipple.setState(closeHit ? new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled} : new int[] {}); + } else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { + if (closeRippleHit && event.getAction() == MotionEvent.ACTION_UP) { + removeTab(lastTab, success -> {}); + } + closeRippleHit = false; + drawable.closeRipple.setState(new int[] {}); + } + for (int i = 0; i < tabDrawables.size(); ++i) { + if (tabDrawables.get(i) != drawable) { + tabDrawables.get(i).closeRipple.setState(new int[] {}); + } + } + } else { + closeRippleHit = false; + } + } + if (closeRippleHit) return true; + return super.onTouchEvent(event); + } + + private final RectF rect = new RectF(); + + @Override + protected void dispatchDraw(Canvas canvas) { + final ArrayList tabs = getTabs(); + final ArrayList tabDrawables = getTabDrawables(); + + backgroundPaint.setColor(backgroundColorAnimated.set(backgroundColor)); + canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint); + super.dispatchDraw(canvas); + + final int tabColor = tabColorAnimated.set(this.tabColor); + final float tabIsDark = tabDarkAnimated.set(this.tabIsDark); + if (drawTabs) { + for (int i = 0; i < tabDrawables.size(); ++i) { + final TabDrawable drawable = tabDrawables.get(i); + float position = drawable.getPosition(); + float alpha = drawable.getAlpha(); + + if (alpha <= 0) continue; + if (position > 1.99f) continue; + + getTabBounds(rect, position); + drawable.setExpandProgress(0f); + drawable.setBackgroundColor(tabColor, tabIsDark > .5f); + drawable.draw(canvas, rect, dp(10), alpha); + } + } + } + + public void setupTab(TabDrawable drawable) { + final int tabColor = tabColorAnimated.set(this.tabColor); + final float tabIsDark = tabDarkAnimated.set(this.tabIsDark); + drawable.setExpandProgress(0f); + drawable.setBackgroundColor(tabColor, tabIsDark > .5f); + } + + public void getTabBounds(RectF rect, float position) { + rect.set(dp(4), getHeight() - dp(4) - dp(50), getWidth() - dp(4), getHeight() - dp(4)); + rect.offset(0, position * -dp(8)); + final float s = lerp(1f, .95f, Math.abs(position)); + final float cx = rect.centerX(), cy = rect.centerY(), w = rect.width(), h = rect.height(); + rect.left = cx - w / 2f * s; + rect.right = cx + w / 2f * s; + rect.top = cy - h / 2f * s; + rect.bottom = cy + h / 2f * s; + } + + + public static class TabDrawable { + + public final WebTabData tab; + public final View parentView; + + private int position; + public int index; + public final AnimatedFloat animatedPosition; + public final AnimatedFloat animatedAlpha; + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Paint iconPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public int closeRippleColor; + public final Drawable closeRipple = Theme.createSelectorDrawable(0x30ffffff, Theme.RIPPLE_MASK_CIRCLE_20DP); + + private boolean tabColorOverride; + private int backgroundColor, tabColor; + private boolean backgroundIsDark, tabIsDark; + + private Text title; + private Text overrideTitle; + + public TabDrawable(View view, WebTabData tab) { + parentView = view; + this.tab = tab; + closeRipple.setCallback(view); + + iconPaint.setStyle(Paint.Style.STROKE); + iconPaint.setStrokeJoin(Paint.Join.ROUND); + iconPaint.setStrokeCap(Paint.Cap.ROUND); + + animatedPosition = new AnimatedFloat(view, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + animatedAlpha = new AnimatedFloat(view, 320, CubicBezierInterpolator.EASE_OUT_QUINT); + + TLRPC.User user = MessagesController.getInstance(tab.props.currentAccount).getUser(tab.getBotId()); + this.title = new Text(UserObject.getUserName(user), 17, AndroidUtilities.bold()); + this.tabColor = tab.actionBarColor; + this.tabIsDark = AndroidUtilities.computePerceivedBrightness(tabColor) < .721f; + + closePath.rewind(); + closePath.moveTo(0, 0); + closePath.lineTo(dp(12), dp(12)); + closePath.moveTo(dp(12), 0); + closePath.lineTo(0, dp(12)); + + expandPath.rewind(); + expandPath.moveTo(0, dp(6.33f) / 2f); + expandPath.lineTo(dp(12.66f) / 2f, -dp(6.33f) / 2f); + expandPath.lineTo(dp(12.66f), dp(6.33f) / 2f); + } + + public void setOverrideTitle(String title) { + if (title == null) { + overrideTitle = null; + } else { + overrideTitle = new Text(title, 17, AndroidUtilities.bold()); + } + } + + public float getPosition() { + return index < 0 ? position : animatedPosition.set(position); + } + + public float getAlpha() { + final float position = getPosition(); + float positionalpha; + if (position < 0) + positionalpha = 1f + position; + else if (position >= 0 && position < 1) + positionalpha = lerp(1f, .87f, position); + else + positionalpha = .87f * (1f - Math.min(1, position - 1)); + return positionalpha * animatedAlpha.set(index >= 0); + } + + public void setBackgroundColor(int color, boolean isDark) { + backgroundColor = color; + backgroundIsDark = isDark; + } + + public void setOnCloseClick(Runnable listener) { + + } + + public void setOnExpandClick(Runnable listener) { + + } + + private float expandProgress; + public void setExpandProgress(float expandProgress) { + this.expandProgress = expandProgress; + } + + private final float[] radii = new float[8]; + private final Path rectPath = new Path(); + private final Path closePath = new Path(); + private final Path expandPath = new Path(); + + public void draw(Canvas canvas, RectF bounds, float r, float alpha) { + final int backgroundColor = ColorUtils.blendARGB(this.backgroundColor, this.tabColor, expandProgress); + backgroundPaint.setColor(backgroundColor); + backgroundPaint.setAlpha((int) (0xFF * alpha)); + backgroundPaint.setShadowLayer(dp(2.33f), 0, dp(1), Theme.multAlpha(0x10000000, alpha)); + + radii[0] = radii[1] = radii[2] = radii[3] = r; + radii[4] = radii[5] = radii[6] = radii[7] = lerp(r, 0, expandProgress); + rectPath.rewind(); + rectPath.addRoundRect(bounds, radii, Path.Direction.CW); + canvas.drawPath(rectPath, backgroundPaint); + + final float isDark = lerp(backgroundIsDark ? 1f : 0f, tabIsDark ? 1f : 0f, expandProgress); + final int iconColor = ColorUtils.blendARGB(0xFF000000, 0xFFFFFFFF, isDark); + + iconPaint.setColor(iconColor); + iconPaint.setStrokeWidth(dp(2)); + + canvas.save(); + canvas.translate(bounds.left, bounds.centerY()); + int rippleColor = ColorUtils.blendARGB(0x20FFFFFF, 0x20FFFFFF, isDark); + closeRipple.setBounds( + (int) (dp(25) + -dp(25)), + (int) ( + -dp(25)), + (int) (dp(25) + dp(25)), + (int) ( + dp(25)) + ); + if (closeRippleColor != rippleColor) { + Theme.setSelectorDrawableColor(closeRipple, closeRippleColor = rippleColor, false); + } + closeRipple.draw(canvas); + canvas.restore(); + + canvas.save(); + canvas.translate(bounds.left + dp(22 - 4), bounds.centerY() - dp(6)); + iconPaint.setAlpha((int) (0xFF * alpha)); + canvas.drawPath(closePath, iconPaint); + canvas.restore(); + + canvas.save(); + canvas.translate(bounds.right - dp(22 - 4 + 12.66f), bounds.centerY()); + iconPaint.setAlpha((int) (0xFF * alpha * (1f - expandProgress))); + canvas.drawPath(expandPath, iconPaint); + canvas.restore(); + + if (overrideTitle != null) { + overrideTitle + .ellipsize((int) (bounds.width() - dp(100))) + .draw(canvas, bounds.left + dp(60), bounds.centerY(), iconColor, (1f - expandProgress) * alpha); + } + title + .ellipsize((int) (bounds.width() - dp(100))) + .draw(canvas, bounds.left + dp(60), bounds.centerY(), iconColor, (overrideTitle == null ? 1f : expandProgress) * alpha); + } + + } + + public static class WebTabData { + + public BotWebViewAttachedSheet.WebViewRequestProps props; + public Bundle webViewState; + public BotWebViewContainer.MyWebView webView; + public BotWebViewContainer.WebViewProxy webViewProxy; + public int webViewWidth, webViewHeight; + public int webViewScroll; + public boolean expanded; + public float expandedOffset = Float.MAX_VALUE; + + public Bitmap previewBitmap; + public Object previewNode; + + public boolean overrideActionBarColor; + public int actionBarColorKey; + public int actionBarColor; + public int backgroundColor; + + public boolean ready; + public boolean backButton; + public boolean settings; + public BotWebViewAttachedSheet.MainButtonSettings main; + public String lastUrl; + public boolean confirmDismiss; + + public boolean fullsize; + public boolean needsContext; + + public boolean themeIsDark; + + public long getBotId() { + if (props == null) return 0; + return props.botId; + } + + public void destroy() { + try { + if (webView != null) { + webView.destroy(); + webView = null; + } + } catch (Exception e) { + FileLog.e(e); + } + } + + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabsOverlay.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabsOverlay.java new file mode 100644 index 0000000000..94df343e25 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/BottomSheetTabsOverlay.java @@ -0,0 +1,1214 @@ +package org.telegram.ui.ActionBar; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.lerp; +import static org.telegram.messenger.LocaleController.getString; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Camera; +import android.graphics.Canvas; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RadialGradient; +import android.graphics.RectF; +import android.graphics.RenderEffect; +import android.graphics.RenderNode; +import android.graphics.Shader; +import android.graphics.SurfaceTexture; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Handler; +import android.util.Log; +import android.view.MotionEvent; +import android.view.PixelCopy; +import android.view.Surface; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewConfiguration; +import android.view.ViewGroup; +import android.webkit.WebView; +import android.widget.FrameLayout; +import android.widget.OverScroller; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import com.google.firebase.encoders.ValueEncoder; +import com.google.zxing.common.detector.MathUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.R; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.Utilities; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Text; +import org.telegram.ui.GradientClip; +import org.telegram.ui.bots.BotWebViewAttachedSheet; +import org.telegram.ui.bots.BotWebViewMenuContainer; +import org.telegram.ui.bots.BotWebViewSheet; + +import java.util.ArrayList; + +public class BottomSheetTabsOverlay extends FrameLayout { + + private BottomSheetTabs tabsView; + + private BotWebViewAttachedSheet dismissingSheet; + private BotWebViewSheet dismissingSheet2; + private BotWebViewMenuContainer dismissingMenuContainer; + private BottomSheetTabs.TabDrawable dismissingTab; + private ValueAnimator dismissingAnimator; + private float dismissProgress; + + private final AnimatedFloat animatedCount = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + private final OverScroller scroller; + private final int maximumVelocity, minimumVelocity; + + public BottomSheetTabsOverlay(Context context) { + super(context); + + setWillNotDraw(false); + + scroller = new OverScroller(context); + ViewConfiguration configuration = ViewConfiguration.get(context); + maximumVelocity = configuration.getScaledMaximumFlingVelocity(); + minimumVelocity = configuration.getScaledMinimumFlingVelocity(); + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + return false; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) + AndroidUtilities.navigationBarHeight, MeasureSpec.EXACTLY)); + } + + public void setTabsView(BottomSheetTabs tabsView) { + this.tabsView = tabsView; + } + + private TabPreview pressTab; + private boolean pressTabClose; + private float startY, startX; + private long startTime; + + private boolean verticallyScrolling; + private boolean horizontallySwiping; + private VelocityTracker velocityTracker; + + private float lastY; + + private boolean hitCloseAllButton; + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + if (AndroidUtilities.isTablet() && event.getAction() == MotionEvent.ACTION_DOWN && !tabsViewBounds.contains(event.getX(), event.getY())) { + return false; + } + boolean r = false; + if (openProgress > 0) { + if (velocityTracker == null) { + velocityTracker = VelocityTracker.obtain(); + } + velocityTracker.addMovement(event); + if (event.getAction() == MotionEvent.ACTION_DOWN) { + startTime = System.currentTimeMillis(); + startX = event.getX(); + startY = event.getY(); + pressTab = getTabAt(event.getX(), event.getY()); + hitCloseAllButton = closeAllButtonBackground != null && closeAllButtonBackground.getBounds().contains((int) event.getX(), (int) event.getY()); + if (hitCloseAllButton) pressTab = null; + if (closeAllButtonBackground != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + closeAllButtonBackground.setHotspot(event.getX(), event.getY()); + } + closeAllButtonBackground.setState(hitCloseAllButton ? new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled} : new int[] {}); + } + verticallyScrolling = false; + horizontallySwiping = false; + pressTabClose = false; + if (pressTab != null) { + pressTab.cancelDismissAnimator(); + pressTabClose = pressTab.tabDrawable.closeRipple.getBounds().contains((int) (event.getX() - pressTab.clickBounds.left), (int) (event.getY() - pressTab.clickBounds.top - dp(24))); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && pressTabClose) { + pressTab.tabDrawable.closeRipple.setHotspot((int) (event.getX() - rect.left), (int) (event.getY() - rect.centerY())); + } + pressTab.setPressed(!pressTabClose); + pressTab.tabDrawable.closeRipple.setState(pressTabClose ? new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled} : new int[]{}); + } + lastY = event.getY(); + if (!scroller.isFinished()) { + scroller.abortAnimation(); + } + if (scrollAnimator != null) { + scrollAnimator.cancel(); + scrollAnimator = null; + } + } else if (event.getAction() == MotionEvent.ACTION_MOVE) { + if (pressTab != null) { + if (pressTab.isPressed()) { + if (!horizontallySwiping && !verticallyScrolling && MathUtils.distance(startX, event.getY(), event.getX(), event.getY()) > AndroidUtilities.touchSlop) { + horizontallySwiping = true; + } + if (!verticallyScrolling && !horizontallySwiping && MathUtils.distance(event.getX(), startY, event.getX(), event.getY()) > AndroidUtilities.touchSlop) { + if (!scroller.isFinished()) { + scroller.abortAnimation(); + } + if (scrollAnimator != null) { + scrollAnimator.cancel(); + scrollAnimator = null; + } + verticallyScrolling = true; + } + if (tabsView != null && (verticallyScrolling || horizontallySwiping)) { + pressTab.setPressed(false); + pressTab.cancelDismissAnimator(); + } + } else { + if (!pressTabClose && !horizontallySwiping && !verticallyScrolling && MathUtils.distance(startX, event.getY(), event.getX(), event.getY()) > AndroidUtilities.touchSlop) { + horizontallySwiping = true; + } + if (!pressTabClose && !verticallyScrolling && !horizontallySwiping && MathUtils.distance(event.getX(), startY, event.getX(), event.getY()) > AndroidUtilities.touchSlop) { + if (!scroller.isFinished()) { + scroller.abortAnimation(); + } + if (scrollAnimator != null) { + scrollAnimator.cancel(); + scrollAnimator = null; + } + verticallyScrolling = true; + } + if (pressTabClose) { + pressTabClose = pressTab.tabDrawable.closeRipple.getBounds().contains((int) (event.getX() - pressTab.clickBounds.left), (int) (event.getY() - pressTab.clickBounds.top - dp(24))); + if (!pressTabClose) { + pressTab.tabDrawable.closeRipple.setState(new int[]{}); + } + } + } + if (!pressTab.isPressed()) { + if (horizontallySwiping) { + pressTab.dismissProgress = (event.getX() - startX) / dp(300); + } else if (verticallyScrolling) { + float deltaY = event.getY() - lastY; + if (offset < getScrollMin()) { + deltaY *= (1f - .5f * Utilities.clamp((getScrollMin() - offset) / getScrollStep(), 1f, 0f)); + } + setScrollOffset( + Utilities.clamp((getScrollOffset() * getScrollStep() - deltaY) / getScrollStep(), getScrollMax(), getScrollMin() - 1.4f * getScrollStep()) + ); + invalidate(); + } + } + invalidate(); + } + if (closeAllButtonBackground != null && hitCloseAllButton) { + hitCloseAllButton = pressTab == null && closeAllButtonBackground != null && closeAllButtonBackground.getBounds().contains((int) event.getX(), (int) event.getY()); + if (!hitCloseAllButton) { + closeAllButtonBackground.setState(new int[] {}); + } + } + lastY = event.getY(); + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (pressTab != null) { + if (tabsView != null && Math.abs(pressTab.dismissProgress) > .4f) { + TabPreview tab = pressTab; + tabsView.removeTab(pressTab.tabData, success -> { + if (success) { + tab.animateDismiss(tab.dismissProgress < 0 ? -1f : 1f); + scrollTo(Utilities.clamp(offset, getScrollMax(false), getScrollMin(false))); + if (tabsView.getTabs().isEmpty()) { + closeTabsView(); + } + } else { + tab.animateDismiss(0); + } + }); + } else { + pressTab.animateDismiss(0); + if (tabsView != null && pressTab.isPressed()) { + closeTabsView(); + pressTab.webView = null; + tabsView.openTab(pressTab.tabData); + } else if (verticallyScrolling) { + if (offset < getScrollMin() - getScrollWindow() * .15f) { + closeTabsView(); + } else if (offset < getScrollMin()) { + scrollTo(getScrollMin()); + } else { + velocityTracker.computeCurrentVelocity(1000, maximumVelocity); + float velocityY = velocityTracker.getYVelocity(); + if (Math.abs(velocityY) > minimumVelocity) { + scroller.fling(0, (int) (getScrollOffset() * getScrollStep()), 0, (int) -velocityY, 0, 0, (int) (getScrollMin() * getScrollStep()), (int) (getScrollMax() * getScrollStep()), 0, (int) (.1f * getScrollStep())); + } else { + scroller.startScroll(0, (int) (getScrollOffset() * getScrollStep()), 0, 0, 0); + } + } + velocityTracker.recycle(); + velocityTracker = null; + postInvalidateOnAnimation(); + } + } + pressTab.setPressed(false); + if (pressTabClose) { + pressTabClose = pressTab.tabDrawable.closeRipple.getBounds().contains((int) (event.getX() - pressTab.clickBounds.left), (int) (event.getY() - pressTab.clickBounds.top - dp(24))); + } + if (pressTabClose) { + TabPreview tab = pressTab; + tabsView.removeTab(pressTab.tabData, success -> { + if (success) { + tab.animateDismiss(1f); + scrollTo(Utilities.clamp(offset, getScrollMax(false), getScrollMin(false))); + if (tabsView.getTabs().isEmpty()) { + closeTabsView(); + } + } else { + tab.animateDismiss(0); + } + }); + } + pressTab.tabDrawable.closeRipple.setState(new int[]{}); + } else if (hitCloseAllButton) { + tabsView.removeAll(); + closeTabsView(); + } else if (MathUtils.distance(startX, startY, event.getX(), event.getY()) <= AndroidUtilities.touchSlop && !verticallyScrolling && !horizontallySwiping && System.currentTimeMillis() - startTime <= ViewConfiguration.getTapTimeout() * 1.2f) { + closeTabsView(); + } + pressTab = null; + pressTabClose = false; + if (velocityTracker != null) { + velocityTracker.recycle(); + velocityTracker = null; + } + hitCloseAllButton = false; + if (closeAllButtonBackground != null) { + closeAllButtonBackground.setState(new int[] {}); + } + } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { + if (pressTab != null) { + pressTab.animateDismiss(0); + pressTab.setPressed(false); + pressTab.tabDrawable.closeRipple.setState(new int[]{}); + } + pressTab = null; + pressTabClose = false; + if (velocityTracker != null) { + velocityTracker.recycle(); + velocityTracker = null; + } + hitCloseAllButton = false; + if (closeAllButtonBackground != null) { + closeAllButtonBackground.setState(new int[] {}); + } + } + r = true; + } + return r; + } + + @Override + public void computeScroll() { + if (scroller.computeScrollOffset()) { + setScrollOffset(scroller.getCurrY() / getScrollStep()); + postInvalidateOnAnimation(); + } + } + + public float offset; + public float getScrollOffset() { + return this.offset; // Utilities.clamp(this.offset, getScrollMax(), getScrollMin()); + } + + public void setScrollOffset(float offset) { + this.offset = offset; // Utilities.clamp(offset, getScrollMax(), getScrollMin()); + } + + private float getScrollStep() { + return dp(200); + } + + public float getScrollRange() { + return getScrollRange(true); + } + + public float getScrollRange(boolean animated) { + float tabCount = 0; + for (int i = 0; i < tabs.size(); ++i) { + final TabPreview tab = tabs.get(i); + tabCount += tab.tabDrawable.index >= 0 ? 1 : 0; + } + return animated ? animatedCount.set(tabCount) : tabCount; + } + + public float getScrollWindow() { + return Math.min(SharedConfig.botTabs3DEffect ? 3 : 6, getScrollRange()); + } + + public float getScrollMin() { + return getScrollMin(true); + } + + public float getScrollMin(boolean animated) { + return -getScrollWindow() / 3f * Utilities.clamp(getScrollRange(animated), 1f, 0); + } + + public float getScrollMax() { + return getScrollMax(true); + } + + public float getScrollMax(boolean animated) { + return getScrollRange(animated) - getScrollWindow() - getScrollWindow() / 3f * Utilities.clamp(4f - getScrollRange(animated), .5f, 0); + } + + public boolean canScroll() { + return canScroll(false); + } + + public boolean canScroll(boolean animated) { + return getScrollMax(animated) - getScrollMin(animated) > .5f; + } + + private TabPreview getTabAt(float x, float y) { + if (openProgress < 1) + return null; + for (int i = tabs.size() - 1; i >= 0; --i) { + TabPreview tab = tabs.get(i); + if (Math.abs(tab.dismissProgress) < .4f && tab.clickBounds.contains(x, y)) + return tab; + } + return null; + } + + private boolean slowerDismiss; + public void setSlowerDismiss(boolean slowerDismiss) { + this.slowerDismiss = slowerDismiss; + } + + public boolean dismissSheet(BotWebViewAttachedSheet sheet) { + if (sheet == null) return false; + if (tabsView == null) return false; + + if (dismissingSheet != null) { + if (dismissingAnimator != null) { + dismissingAnimator.end(); + dismissingAnimator = null; + } + } + + dismissingSheet = sheet; + sheet.getWindowView().setDrawingFromOverlay(true); + invalidate(); + + if (dismissingAnimator != null) { + dismissingAnimator.cancel(); + } + + BottomSheetTabs.WebTabData tab = sheet.saveState(); + dismissingTab = tabsView.pushTab(tab); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + renderHardwareViewToBitmap(tab.webView, -tab.webViewScroll, b -> tab.previewBitmap = b); + } + + dismissProgress = 0; + dismissingAnimator = ValueAnimator.ofFloat(0, 1); + dismissingAnimator.addUpdateListener(anm -> { + dismissProgress = (float) anm.getAnimatedValue(); + invalidate(); + }); + dismissingAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (tab.webView != null && tab.previewBitmap == null && tab.webViewWidth > 0 && tab.webViewHeight > 0) { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) { + tab.previewBitmap = Bitmap.createBitmap(tab.webViewWidth, tab.webViewHeight, Bitmap.Config.RGB_565); + Canvas canvas = new Canvas(tab.previewBitmap); + canvas.translate(0, -tab.webViewScroll); + tab.webView.draw(canvas); + } + } + sheet.release(); + dismissingSheet = null; + invalidate(); + } + }); + if (slowerDismiss || sheet.getFullSize()) { + AndroidUtilities.applySpring(dismissingAnimator, 260, 30, 1); + } else { + AndroidUtilities.applySpring(dismissingAnimator, 350, 30, 1); + } + dismissingAnimator.start(); + + slowerDismiss = false; + + return true; + } + + public boolean dismissSheet(BotWebViewSheet sheet) { + if (sheet == null) return false; + if (tabsView == null) return false; + + if (dismissingSheet2 != null) { + if (dismissingAnimator != null) { + dismissingAnimator.end(); + dismissingAnimator = null; + } + } + + dismissingSheet2 = sheet; + sheet.getWindowView().setDrawingFromOverlay(true); + invalidate(); + + if (dismissingAnimator != null) { + dismissingAnimator.cancel(); + } + + BottomSheetTabs.WebTabData tab = sheet.saveState(); + dismissingTab = tabsView.pushTab(tab); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + renderHardwareViewToBitmap(tab.webView, -tab.webViewScroll, b -> tab.previewBitmap = b); + } + + dismissProgress = 0; + dismissingAnimator = ValueAnimator.ofFloat(0, 1); + dismissingAnimator.addUpdateListener(anm -> { + dismissProgress = (float) anm.getAnimatedValue(); + invalidate(); + }); + dismissingAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (tab.webView != null && tab.previewBitmap == null && tab.webViewWidth > 0 && tab.webViewHeight > 0) { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) { + tab.previewBitmap = Bitmap.createBitmap(tab.webViewWidth, tab.webViewHeight, Bitmap.Config.RGB_565); + Canvas canvas = new Canvas(tab.previewBitmap); + canvas.translate(0, -tab.webViewScroll); + tab.webView.draw(canvas); + } + } + sheet.release(); + dismissingSheet2 = null; + invalidate(); + } + }); + AndroidUtilities.applySpring(dismissingAnimator, 350, 30, 1); + dismissingAnimator.setDuration(dismissingAnimator.getDuration() * 2); + dismissingAnimator.start(); + + slowerDismiss = false; + + return true; + } + + public boolean dismissSheet(BotWebViewMenuContainer menuContainer) { + if (menuContainer == null) return false; + if (tabsView == null) return false; + + dismissingMenuContainer = menuContainer; + menuContainer.setDrawingFromOverlay(true); + invalidate(); + + if (dismissingAnimator != null) { + dismissingAnimator.cancel(); + } + + BottomSheetTabs.WebTabData tab = menuContainer.saveState(); + dismissingTab = tabsView.pushTab(tab); + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + renderHardwareViewToBitmap(tab.webView, -tab.webViewScroll, b -> tab.previewBitmap = b); + } + + dismissProgress = 0; + dismissingAnimator = ValueAnimator.ofFloat(0, 1); + dismissingAnimator.addUpdateListener(anm -> { + dismissProgress = (float) anm.getAnimatedValue(); + invalidate(); + }); + dismissingAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (tab.webView != null && tab.previewBitmap == null && tab.webViewWidth > 0 && tab.webViewHeight > 0) { + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) { + tab.previewBitmap = Bitmap.createBitmap(tab.webViewWidth, tab.webViewHeight, Bitmap.Config.RGB_565); + Canvas canvas = new Canvas(tab.previewBitmap); + canvas.translate(0, -tab.webViewScroll); + tab.webView.draw(canvas); + } + } + menuContainer.onDismiss(); + menuContainer.setDrawingFromOverlay(false); + dismissingMenuContainer = null; + invalidate(); + } + }); + AndroidUtilities.applySpring(dismissingAnimator, 350, 30, 1); + dismissingAnimator.setDuration(dismissingAnimator.getDuration()); + dismissingAnimator.start(); + + return true; + } + + public boolean onBackPressed() { + if (isOpen) { + closeTabsView(); + return true; + } + return false; + } + + private Bitmap blurBitmap; + private BitmapShader blurBitmapShader; + private Paint blurBitmapPaint; + private Matrix blurMatrix; + private void prepareBlur(View view) { + blurBitmap = AndroidUtilities.makeBlurBitmap(view, 14, 14); + + blurBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + blurBitmapPaint.setShader(blurBitmapShader = new BitmapShader(blurBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); + ColorMatrix colorMatrix = new ColorMatrix(); + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, Theme.isCurrentThemeDark() ? .08f : +.25f); + blurBitmapPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); + blurMatrix = new Matrix(); + } + + private final RectF tabsViewBounds = new RectF(); + private final ArrayList tabs = new ArrayList<>(); + + public void openTabsView() { + if (tabsView == null || !(tabsView.getParent() instanceof View)) return; + View actionBarLayout = (View) tabsView.getParent(); + + actionBarLayout.getLocationOnScreen(pos); + getLocationOnScreen(pos2); + tabsViewBounds.set(pos[0] - pos2[0], pos[1] - pos2[1], pos[0] - pos2[0] + actionBarLayout.getWidth(), pos[1] - pos2[1] + actionBarLayout.getHeight()); + + prepareBlur(actionBarLayout); + clearTabs(); + prepareTabs(); + animateOpen(true); + } + + private void clearTabs() { +// for (int i = 0; i < tabs.size(); ++i) { +// TabPreview tab = tabs.get(i); +// if (tab.webView != null) { +// tab.webView.onPause(); +// AndroidUtilities.removeFromParent(tab.webView); +// } +// } + tabs.clear(); + } + + private void prepareTabs() { + ArrayList tabs = tabsView.getTabs(); + ArrayList tabDrawables = tabsView.getTabDrawables(); + + for (int i = tabs.size() - 1; i >= 0; --i) { + BottomSheetTabs.WebTabData tabData = tabs.get(i); +// if (tabData.webView != null) { +// AndroidUtilities.removeFromParent(tabData.webView); +// tabData.webView.onResume(); +// tabData.webView.post(tabData.webView::onPause); +// addView(tabData.webView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); +// } + BottomSheetTabs.TabDrawable tabDrawable = null; + for (int j = 0; j < tabDrawables.size(); ++j) { + BottomSheetTabs.TabDrawable d = tabDrawables.get(j); + if (d.tab == tabData) { + tabDrawable = d; + break; + } + } + if (tabDrawable == null) continue; + this.tabs.add(new TabPreview(this, tabData, tabDrawable)); + } + animatedCount.set(this.tabs.size(), true); + setScrollOffset(getScrollMax()); + } + + public void closeTabsView() { + animateOpen(false); + } + + private ValueAnimator scrollAnimator; + private void scrollTo(float offset) { + if (scrollAnimator != null) { + scrollAnimator.cancel(); + scrollAnimator = null; + } + scrollAnimator = ValueAnimator.ofFloat(this.offset, offset); + scrollAnimator.addUpdateListener(anm -> { + this.offset = (float) anm.getAnimatedValue(); + }); + scrollAnimator.setDuration(250); + scrollAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + scrollAnimator.start(); + } + + private boolean isOpen; + private float openProgress; + private ValueAnimator openAnimator; + private void animateOpen(boolean open) { + if (isOpen == open) return; + if (openAnimator != null) { + openAnimator.cancel(); + } + + isOpen = open; + if (tabsView != null) { + tabsView.drawTabs = false; + tabsView.invalidate(); + } + invalidate(); + openAnimator = ValueAnimator.ofFloat(openProgress, open ? 1f : 0f); + openAnimator.addUpdateListener(anm -> { + openProgress = (float) anm.getAnimatedValue(); + invalidate(); + }); + openAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (tabsView != null) { + tabsView.drawTabs = true; + tabsView.invalidate(); + } + openProgress = isOpen ? 1f : 0f; + invalidate(); + if (!isOpen) { + clearTabs(); + } + } + }); + openAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + openAnimator.setDuration(320); + openAnimator.start(); + } + + private final int[] pos = new int[2]; + private final int[] pos2 = new int[2]; + private final int[] pos3 = new int[2]; + private final RectF rect = new RectF(); + private final RectF rect2 = new RectF(); + private final RectF clipRect = new RectF(); + private final Path clipPath = new Path(); + + private void drawDismissingTab(Canvas canvas) { + if (dismissingSheet != null) { + getLocationOnScreen(pos2); + tabsView.getLocationOnScreen(pos); + tabsView.getTabBounds(rect, 0); + rect.offset(pos[0] - pos2[0], pos[1] - pos2[1]); + float radius = dismissingSheet.getWindowView().drawInto(canvas, rect, dismissProgress, clipRect); + + if (dismissingTab != null) { + clipPath.rewind(); + clipPath.addRoundRect(clipRect, radius, radius, Path.Direction.CW); + canvas.save(); + canvas.clipPath(clipPath); + final float y = clipRect.top - dp(50) * (1f - dismissProgress); + rect.set(clipRect.left, y, clipRect.right, y + dp(50)); + tabsView.setupTab(dismissingTab); + dismissingTab.draw(canvas, rect, radius, dismissProgress); + canvas.restore(); + } + } + + if (dismissingSheet2 != null) { + BotWebViewSheet.WindowView windowView = dismissingSheet2.getWindowView(); + getLocationOnScreen(pos2); + tabsView.getLocationOnScreen(pos); + tabsView.getTabBounds(rect, 0); + rect.offset(pos[0] - pos2[0], pos[1] - pos2[1]); + float radius = windowView.drawInto(canvas, rect, dismissProgress, clipRect); + + if (dismissingTab != null) { + clipPath.rewind(); + clipPath.addRoundRect(clipRect, radius, radius, Path.Direction.CW); + canvas.save(); + canvas.clipPath(clipPath); + final float y = clipRect.top - dp(50) * (1f - dismissProgress); + rect.set(clipRect.left, y, clipRect.right, y + dp(50)); + tabsView.setupTab(dismissingTab); + dismissingTab.draw(canvas, rect, radius, dismissProgress); + canvas.restore(); + } + } + + if (dismissingMenuContainer != null) { + getLocationOnScreen(pos2); + dismissingMenuContainer.getLocationOnScreen(pos3); + tabsView.getLocationOnScreen(pos); + tabsView.getTabBounds(rect, 0); + rect.offset(pos[0] - pos2[0], pos[1] - pos2[1]); + float radius = dismissingMenuContainer.drawInto(canvas, rect, dismissProgress, clipRect); + + if (dismissingTab != null) { + clipPath.rewind(); + clipPath.addRoundRect(clipRect, radius, radius, Path.Direction.CW); + canvas.save(); + canvas.clipPath(clipPath); + final float y = clipRect.top - dp(50) * (1f - dismissProgress); + rect.set(clipRect.left, y, clipRect.right, y + dp(50)); + tabsView.setupTab(dismissingTab); + dismissingTab.draw(canvas, rect, radius, dismissProgress); + canvas.restore(); + } + } + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == closeAllButtonBackground || super.verifyDrawable(who); + } + + private Text closeAllButtonText; + private boolean closeAllButtonBackgroundDark; + private Drawable closeAllButtonBackground; + + private GradientClip gradientClip; + + private void drawTabsPreview(Canvas canvas) { + if (openProgress <= 0) return; + + canvas.save(); + canvas.clipRect(tabsViewBounds); + canvas.translate(tabsViewBounds.left, tabsViewBounds.top); + + final float thisWidth = tabsViewBounds.width(), thisHeight = tabsViewBounds.height(); + + if (blurBitmap != null) { + blurMatrix.reset(); + final float s = (float) tabsViewBounds.width() / blurBitmap.getWidth(); + blurMatrix.postScale(s, s); + blurBitmapShader.setLocalMatrix(blurMatrix); + + blurBitmapPaint.setAlpha((int) (0xFF * openProgress)); + canvas.drawRect(0, 0, thisWidth, thisHeight, blurBitmapPaint); + } + + canvas.saveLayerAlpha(0, 0, thisWidth, thisHeight, 0xFF, Canvas.ALL_SAVE_FLAG); + + final float paddingTop = AndroidUtilities.statusBarHeight + dp(40) + dp(55); + final float paddingBottom = dp(68); + + final int width = (int) Math.min(dp(340), thisWidth * .95f); + final int height = (int) (AndroidUtilities.isTablet() ? Math.min(thisWidth, thisHeight) * .75f : thisHeight * .75f); + final float cx = thisWidth / 2f; + float tabCount = 0; + for (int i = 0; i < tabs.size(); ++i) { + final TabPreview tab = tabs.get(i); + tabCount += tab.tabDrawable.index >= 0 ? 1 : 0; + } + final float count = animatedCount.set(tabCount); + boolean reverse = true; + for (int i = (reverse ? 0 : tabs.size() - 1); (reverse ? i < tabs.size() : i >= 0); i = (reverse ? i + 1 : i - 1)) { + final TabPreview tab = tabs.get(i); + + final float position = count - 1 - tab.tabDrawable.getPosition(); + final float scroll = (position - getScrollOffset()) / getScrollWindow(); + final float scrollT = Math.max(scroll, 0f); + final float oscrollT = Math.max(Math.min(scroll, 1f), -4); + + float alpha = 1f; + float top, bottom, y; + if (SharedConfig.botTabs3DEffect) { + top = paddingTop + dp(6) * Math.min(5, position); + bottom = thisHeight - paddingBottom - height * .26f;// - dp(6) * Math.min(5, count - position); + y = top + (bottom - top) * scroll; + alpha = 1f; // Utilities.clamp(oscrollT * 4f + 1f, 1f, 0f); + } else { + top = paddingTop + dp(20) * ((float) Math.pow(1.1f, position) - 1f); + bottom = thisHeight - paddingBottom - height * .26f; + y = top + (bottom - top) * (float) (Math.pow(scrollT, 2)); + y = Math.min(y, thisHeight); + } + + if (alpha <= 0) continue; + + rect2.set(cx - width / 2f, y, cx + width / 2f, y + height); + tabsView.getTabBounds(rect, Utilities.clamp(tab.tabDrawable.getPosition(), 1, 0)); + rect.offset(tabsView.getX(), tabsView.getY()); + AndroidUtilities.lerpCentered(rect, rect2, openProgress, rect2); + + if (tabsView != null) { + tabsView.setupTab(tab.tabDrawable); + } + + canvas.save(); + tab.clickBounds.set(rect2); + if (SharedConfig.botTabs3DEffect) { +// final float scale = lerp(1f, .5f, openProgress); +// canvas.scale(scale, scale, rect2.centerX(), rect2.centerY()); +// scale(tab.clickBounds, scale, rect.centerX(), rect2.centerY()); + + Canvas tabCanvas = canvas; +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && alpha < 1 && false) { +// if (tab.node == null) { +// tab.node = new RenderNode("a"); +// } +// tab.node.setRenderEffect(RenderEffect.createBlurEffect((1f - alpha) * 300, (1f - alpha) * 300, Shader.TileMode.CLAMP)); +// tab.node.setPosition(0, 0, (int) thisWidth, (int) thisHeight); +// tabCanvas = tab.node.beginRecording(); +// } + + tab.matrix.reset(); + + final int p = 0; + final float Sh = 1f; + tab.src[0] = rect2.left; + tab.src[1] = rect2.top; + tab.src[2] = rect2.right; + tab.src[3] = rect2.top; + tab.src[4] = rect2.right; + tab.src[5] = rect2.top + rect2.height() * Sh; + tab.src[6] = rect2.left; + tab.src[7] = rect2.top + rect2.height() * Sh; + +// final float ws = .75f; +// final float wss = 1.2f; +// final float hs = 1f; +// final float wstop = 1f; +// final float wsbottom = 1.2f; +// tab.dst[0] = rect2.centerX() - rect2.width() / 2f * ws * wstop; +// tab.dst[1] = rect2.centerY() - rect2.height() / 2f * hs; +// tab.dst[2] = rect2.centerX() + rect2.width() / 2f * ws * wstop; +// tab.dst[3] = rect2.centerY() - rect2.height() / 2f * hs; +// tab.dst[4] = rect2.centerX() + rect2.width() / 2f * ws * (2f - wsbottom); +// tab.dst[5] = rect2.centerY() + rect2.height() / 2f * hs; +// tab.dst[6] = rect2.centerX() - rect2.width() / 2f * ws * (2f - wsbottom); +// tab.dst[7] = rect2.centerY() + rect2.height() / 2f * hs; + + tab.dst[0] = rect2.left; + tab.dst[1] = rect2.top - dp(p); + tab.dst[2] = rect2.right; + tab.dst[3] = rect2.top - dp(p); + final float s1 = .83f, s2 = .6f; + tab.dst[4] = rect2.centerX() + rect2.width() / 2f * lerp(1f, s1, openProgress); + tab.dst[5] = rect2.top - dp(p) + (rect2.height() * Sh + dp(p + p)) * lerp(1f, s2, openProgress); + tab.dst[6] = rect2.centerX() - rect2.width() / 2f * lerp(1f, s1, openProgress); + tab.dst[7] = rect2.top - dp(p) + (rect2.height() * Sh + dp(p + p)) * lerp(1f, s2, openProgress); + + tab.matrix.setPolyToPoly(tab.src, 0, tab.dst, 0, 4); + tabCanvas.concat(tab.matrix); + + tab.draw(tabCanvas, rect2, lerp(tab.tabDrawable.getAlpha(), alpha, openProgress), openProgress); + + canvas.restore(); +// +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && alpha < 1 && false) { +// tab.node.endRecording(); +// canvas.drawRenderNode(tab.node); +// } + } else { + final float s = lerp( + 1f, + lerp( + lerp(1f, 1f - Utilities.clamp(count * .1f, .5f, .25f), 1f - scrollT), + Math.min(1, (float) Math.pow(0.7f, 1f - oscrollT)), + Utilities.clamp(count - 3, 1, 0) + ), + openProgress + ); + canvas.scale(s, s, rect2.centerX(), rect2.top); + scale(tab.clickBounds, s, rect.centerX(), rect2.top); + + tab.draw(canvas, rect2, lerp(tab.tabDrawable.getAlpha(), 1f, openProgress), openProgress); + canvas.restore(); + } + } + canvas.save(); + if (gradientClip == null) { + gradientClip = new GradientClip(); + } + AndroidUtilities.rectTmp.set(0, 0, thisWidth, paddingTop); + gradientClip.draw(canvas, AndroidUtilities.rectTmp, true, openProgress); + canvas.restore(); + canvas.restore(); + + if (closeAllButtonText == null) { + closeAllButtonText = new Text(getString(R.string.BotCloseAllTabs), 14, AndroidUtilities.bold()); + } + if (closeAllButtonBackground == null || closeAllButtonBackgroundDark != Theme.isCurrentThemeDark()) { + closeAllButtonBackgroundDark = Theme.isCurrentThemeDark(); + if (closeAllButtonBackgroundDark) { + closeAllButtonBackground = Theme.createSimpleSelectorRoundRectDrawable(64, 0x20ffffff, 0x33ffffff); + } else { + closeAllButtonBackground = Theme.createSimpleSelectorRoundRectDrawable(64, 0x2e000000, 0x44000000); + } + closeAllButtonBackground.setCallback(this); + } + final float buttonWidth = closeAllButtonText.getCurrentWidth() + dp(24); + closeAllButtonBackground.setBounds( + (int) ((thisWidth - buttonWidth) / 2f), + (int) (paddingTop - dp(75 + 20) / 2f - dp(14)), + (int) ((thisWidth + buttonWidth) / 2f), + (int) (paddingTop - dp(75 + 20) / 2f + dp(14)) + ); + closeAllButtonBackground.setAlpha((int) (0xFF * openProgress)); + closeAllButtonBackground.draw(canvas); + closeAllButtonText.draw(canvas, (thisWidth - buttonWidth) / 2f + dp(12), paddingTop - dp(75 + 20) / 2f, 0xFFFFFFFF, openProgress); + + canvas.restore(); + } + + private void scale(RectF rect, float s, float px, float py) { + final float wl = px - rect.left, wr = rect.right - px; + final float ht = py - rect.top, hb = rect.bottom - py; + rect.set( + px - wl * s, + py - ht * s, + px + wr * s, + py + hb * s + ); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + + drawDismissingTab(canvas); + drawTabsPreview(canvas); + } + + private static class TabPreview { + + public final RectF clickBounds = new RectF(); + + public final View parentView; + public final BottomSheetTabs.WebTabData tabData; + public final BottomSheetTabs.TabDrawable tabDrawable; + public final Bitmap previewBitmap; + public WebView webView; + public final Object previewNode; + + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + +// private RenderNode node; + private final Matrix matrix = new Matrix(); + private final float[] src = new float[8]; + private final float[] dst = new float[8]; + + public float dismissProgress = 0f; + private ValueAnimator dismissAnimator; + public void cancelDismissAnimator() { + if (dismissAnimator != null) { + dismissAnimator.cancel(); + } + } + public void animateDismiss(float dismissTo) { + cancelDismissAnimator(); + dismissAnimator = ValueAnimator.ofFloat(dismissProgress, dismissTo); + dismissAnimator.addUpdateListener(anm -> { + dismissProgress = (float) anm.getAnimatedValue(); + if (parentView != null) { + parentView.invalidate(); + } + }); + dismissAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + dismissProgress = dismissTo; + if (parentView != null) { + parentView.invalidate(); + } + } + }); + if (Math.abs(dismissTo) < .1f) { + AndroidUtilities.applySpring(dismissAnimator, 285, 20); + } else { + dismissAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + } + dismissAnimator.start(); + } + + public final ButtonBounce bounce; + public boolean isPressed() { + return bounce.isPressed(); + } + public void setPressed(boolean pressed) { + bounce.setPressed(pressed); + } + + public TabPreview( + View parentView, + BottomSheetTabs.WebTabData tabData, + BottomSheetTabs.TabDrawable tabDrawable + ) { + this.parentView = parentView; + this.tabData = tabData; + this.tabDrawable = tabDrawable; + this.previewBitmap = tabData.previewBitmap; + this.webView = null;// tabData.webView; + this.previewNode = tabData.previewNode; + this.bounce = new ButtonBounce(parentView); + + backgroundPaint.setColor(tabData.backgroundColor); + } + + private final Paint shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final RectF tabBounds = new RectF(); + private final Path clipPath = new Path(); + private final Paint bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + + private final RadialGradient gradient = new RadialGradient(0, 0, 255, new int[] { 0x00000000, 0x30000000 }, new float[] { .5f, 1 }, Shader.TileMode.CLAMP); + private final Matrix gradientMatrix = new Matrix(); + private final Paint gradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public void draw(Canvas canvas, RectF bounds, float alpha, float expandProgress) { + alpha *= Utilities.clamp(1f - ((Math.abs(dismissProgress) - .3f) / .7f), 1f, 0f); + if (alpha <= 0) + return; + + float tabScaleY = 1f; + if (SharedConfig.botTabs3DEffect) { + tabScaleY = lerp(1f, 1.3f, expandProgress); + } + + canvas.save(); + canvas.rotate(dismissProgress * 20, bounds.centerX() + dp(50) * dismissProgress, bounds.bottom + dp(350)); + final float s = bounce.getScale(.01f); + canvas.scale(s, s, bounds.centerX(), bounds.centerY()); + + final float r = lerp(dp(10), dp(8), expandProgress); + clipPath.rewind(); + clipPath.addRoundRect(bounds, r, r, Path.Direction.CW); + canvas.save(); + shadowPaint.setColor(0); + shadowPaint.setShadowLayer(dp(30), 0, dp(10), Theme.multAlpha(0x20000000, alpha * (expandProgress > .7f ? expandProgress : 0))); + canvas.drawPath(clipPath, shadowPaint); + canvas.clipPath(clipPath); + + backgroundPaint.setAlpha((int) (0xFF * alpha)); + canvas.drawRoundRect(bounds, r, r, backgroundPaint); + + canvas.save(); + canvas.translate(bounds.left, bounds.top + dp(50) * tabScaleY); + canvas.scale(1f, lerp(1f, 1.25f, expandProgress)); + if (previewNode != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && ((RenderNode) previewNode).hasDisplayList()) { + RenderNode node = (RenderNode) previewNode; + final float s2 = bounds.width() / node.getWidth(); + canvas.scale(s2, s2); + node.setAlpha(alpha * expandProgress); + canvas.drawRenderNode(node); + } else if (previewBitmap != null) { + final float s2 = bounds.width() / previewBitmap.getWidth(); + canvas.scale(s2, s2); + bitmapPaint.setAlpha((int) (0xFF * alpha * expandProgress)); + canvas.drawBitmap(previewBitmap, 0, 0, bitmapPaint); + } else if (webView != null) { + final float s2 = bounds.width() / webView.getWidth(); + canvas.scale(s2, s2); + canvas.saveLayerAlpha(0, 0, webView.getWidth(), webView.getHeight(), (int) (0xFF * alpha * expandProgress), Canvas.ALL_SAVE_FLAG); + webView.draw(canvas); + canvas.restore(); + } + canvas.restore(); + + canvas.save(); + gradientPaint.setAlpha((int) (0xFF * alpha * expandProgress)); + gradientMatrix.reset(); + final float gradientScale = bounds.height() / 255f; + gradientMatrix.postScale(gradientScale, gradientScale); + gradientMatrix.postTranslate(bounds.centerX(), bounds.top); + gradient.setLocalMatrix(gradientMatrix); + gradientPaint.setShader(gradient); + canvas.drawRect(bounds, gradientPaint); + canvas.restore(); + + tabBounds.set(bounds); + tabBounds.bottom = tabBounds.top + Math.min(bounds.height(), dp(50)); + tabDrawable.setExpandProgress(expandProgress); + canvas.scale(1f, tabScaleY, tabBounds.centerX(), tabBounds.top); + tabDrawable.draw(canvas, tabBounds, r, alpha * alpha); + + canvas.restore(); + + canvas.restore(); + } + + } + + @RequiresApi(api = Build.VERSION_CODES.Q) + public static void renderNodeToBitmap(RenderNode renderNode, Utilities.Callback whenBitmapDone) { + if (renderNode == null || whenBitmapDone == null || renderNode.getWidth() <= 0 || renderNode.getHeight() <= 0) { + if (whenBitmapDone != null) { + whenBitmapDone.run(null); + } + return; + } + + final SurfaceTexture surfaceTexture = new SurfaceTexture(false); + surfaceTexture.setDefaultBufferSize(renderNode.getWidth(), renderNode.getHeight()); + final Surface surface = new Surface(surfaceTexture); + + final Bitmap bitmap = Bitmap.createBitmap(renderNode.getWidth(), renderNode.getHeight(), Bitmap.Config.ARGB_8888); + final Canvas hwCanvas = surface.lockHardwareCanvas(); + hwCanvas.drawRenderNode(renderNode); + surface.unlockCanvasAndPost(hwCanvas); + + PixelCopy.request(surface, bitmap, new PixelCopy.OnPixelCopyFinishedListener() { + @Override + public void onPixelCopyFinished(int copyResult) { + if (copyResult == PixelCopy.SUCCESS) { + whenBitmapDone.run(bitmap); + } else { + bitmap.recycle(); + whenBitmapDone.run(null); + } + surface.release(); + surfaceTexture.release(); + } + }, new Handler()); + } + + @RequiresApi(api = Build.VERSION_CODES.O) + public static void renderHardwareViewToBitmap(View view, float offsetY, Utilities.Callback whenBitmapDone) { + if (view == null || whenBitmapDone == null || view.getWidth() <= 0 || view.getHeight() <= 0) { + if (whenBitmapDone != null) { + whenBitmapDone.run(null); + } + return; + } + + final SurfaceTexture surfaceTexture = new SurfaceTexture(false); + surfaceTexture.setDefaultBufferSize(view.getWidth(), view.getHeight()); + final Surface surface = new Surface(surfaceTexture); + + final Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.ARGB_8888); + final Canvas hwCanvas = surface.lockHardwareCanvas(); + hwCanvas.translate(0, offsetY); + view.draw(hwCanvas); + surface.unlockCanvasAndPost(hwCanvas); + + PixelCopy.request(surface, bitmap, new PixelCopy.OnPixelCopyFinishedListener() { + @Override + public void onPixelCopyFinished(int copyResult) { + if (copyResult == PixelCopy.SUCCESS) { + whenBitmapDone.run(bitmap); + } else { + bitmap.recycle(); + whenBitmapDone.run(null); + } + surface.release(); + surfaceTexture.release(); + } + }, new Handler()); + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java index f3f17788f5..bf797933c1 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/DrawerLayoutContainer.java @@ -449,7 +449,7 @@ public boolean onTouchEvent(MotionEvent ev) { return true; } - if ((allowOpenDrawerBySwipe || drawerOpened) && allowOpenDrawer && parentActionBarLayout.getFragmentStack().size() == 1 && (parentActionBarLayout.getLastFragment().getLastStoryViewer() == null || !parentActionBarLayout.getLastFragment().getLastStoryViewer().attachedToParent())) { + if ((allowOpenDrawerBySwipe || drawerOpened) && allowOpenDrawer && parentActionBarLayout.getFragmentStack().size() == 1 && (parentActionBarLayout.getLastFragment().getLastSheet() == null || !parentActionBarLayout.getLastFragment().getLastSheet().attachedToParent())) { if (ev != null && (ev.getAction() == MotionEvent.ACTION_DOWN || ev.getAction() == MotionEvent.ACTION_MOVE) && !startedTracking && !maybeStartTracking) { View scrollingChild = findScrollingChild(this, ev.getX(),ev.getY()); if (scrollingChild != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/INavigationLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/INavigationLayout.java index 0180bbe213..234622a673 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/INavigationLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/INavigationLayout.java @@ -79,12 +79,12 @@ public interface INavigationLayout { List getPulledDialogs(); void setPulledDialogs(List pulledDialogs); - static INavigationLayout newLayout(Context context) { - return new ActionBarLayout(context); + static INavigationLayout newLayout(Context context, boolean main) { + return new ActionBarLayout(context, main); } - static INavigationLayout newLayout(Context context, Supplier supplier) { - return new ActionBarLayout(context) { + static INavigationLayout newLayout(Context context, boolean main, Supplier supplier) { + return new ActionBarLayout(context, main) { @Override public BottomSheet getBottomSheet() { return supplier.get(); @@ -433,6 +433,15 @@ public void saveColors(Theme.ResourcesProvider fragmentResourceProvider) { } } + void setNavigationBarColor(int color); + + default int getBottomTabsHeight(boolean animated) { + return 0; + } + + default BottomSheetTabs getBottomSheetTabs() { + return null; + } enum BackButtonState { BACK, MENU diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java index 912a5afb92..caaec78e39 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/BaseLocationAdapter.java @@ -27,6 +27,7 @@ import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; +import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.Components.RecyclerListView; import java.util.ArrayList; @@ -203,6 +204,7 @@ public void searchPlacesWithQuery(final String query, final Location coordinate, if (!TextUtils.isEmpty(query) && (stories || biz)) { searchingLocations = true; final Locale locale = LocaleController.getInstance().getCurrentLocale(); + final Locale englishLocale = stories ? locale.getLanguage().contains("en") ? locale : Locale.US : null; final String finalQuery = query; Utilities.globalQueue.postRunnable(() -> { final ArrayList locations = new ArrayList<>(); @@ -210,11 +212,17 @@ public void searchPlacesWithQuery(final String query, final Location coordinate, final int maxCount = biz ? 10 : 5; Geocoder geocoder = new Geocoder(ApplicationLoader.applicationContext, locale); List
addresses = geocoder.getFromLocationName(finalQuery, 5); + List
engAddresses = null; + if (stories) { + geocoder = new Geocoder(ApplicationLoader.applicationContext, englishLocale); + engAddresses = geocoder.getFromLocationName(finalQuery, 5); + } HashSet countries = new HashSet<>(); HashSet cities = new HashSet<>(); String arg, lc; for (int i = 0; i < addresses.size(); ++i) { Address address = addresses.get(i); + Address engAddress = engAddresses != null && i < engAddresses.size() ? engAddresses.get(i) : null; if (!address.hasLatitude() || !address.hasLongitude()) continue; double lat = address.getLatitude(); @@ -230,6 +238,13 @@ public void searchPlacesWithQuery(final String query, final Location coordinate, if (TextUtils.isEmpty(locality)) { locality = address.getAdminArea(); } + String engLocality = null; + if (engAddress != null) { + engLocality = engAddress.getLocality(); + if (TextUtils.isEmpty(engLocality)) { + engLocality = engAddress.getAdminArea(); + } + } arg = address.getThoroughfare(); if (!TextUtils.isEmpty(arg) && !TextUtils.equals(arg, address.getAdminArea())) { if (streetBuilder.length() > 0) { @@ -322,9 +337,73 @@ public void searchPlacesWithQuery(final String query, final Location coordinate, streetLocation.title = streetBuilder.toString(); streetLocation.icon = "pin"; streetLocation.address = onlyCity ? LocaleController.getString("PassportCity", R.string.PassportCity) : LocaleController.getString("PassportStreet1", R.string.PassportStreet1); - locations.add(streetLocation); - if (locations.size() >= maxCount) { - break; + boolean isUnnamed = false; + if (engAddress != null) { + streetLocation.geoAddress = new TL_stories.TL_geoPointAddress(); + streetLocation.geoAddress.country_iso2 = engAddress.getCountryCode(); + + String engCity = null, engState = null; + if (TextUtils.isEmpty(engCity)) { + engCity = engAddress.getLocality(); + } + if (TextUtils.isEmpty(engCity)) { + engCity = engAddress.getAdminArea(); + } + if (TextUtils.isEmpty(engCity)) { + engCity = engAddress.getSubAdminArea(); + } + engState = engAddress.getAdminArea(); + StringBuilder engStreet = new StringBuilder(); + + if (!TextUtils.isEmpty(engState)) { + streetLocation.geoAddress.state = engState; + streetLocation.geoAddress.flags |= 1; + } + if (!TextUtils.isEmpty(engCity)) { + streetLocation.geoAddress.city = engCity; + streetLocation.geoAddress.flags |= 2; + } + + if (!onlyCity) { + String engFeature = null; + if (TextUtils.isEmpty(engFeature) && !TextUtils.equals(engAddress.getThoroughfare(), locality) && !TextUtils.equals(engAddress.getThoroughfare(), engAddress.getCountryName())) { + engFeature = engAddress.getThoroughfare(); + } + if (TextUtils.isEmpty(engFeature) && !TextUtils.equals(engAddress.getSubLocality(), locality) && !TextUtils.equals(engAddress.getSubLocality(), engAddress.getCountryName())) { + engFeature = engAddress.getSubLocality(); + } + if (TextUtils.isEmpty(engFeature) && !TextUtils.equals(engAddress.getLocality(), locality) && !TextUtils.equals(engAddress.getLocality(), engAddress.getCountryName())) { + engFeature = engAddress.getLocality(); + } + if (!TextUtils.isEmpty(engFeature) && !TextUtils.equals(engFeature, engState) && !TextUtils.equals(engFeature, engAddress.getCountryName())) { + if (engStreet.length() > 0) { + engStreet.append(", "); + } + engStreet.append(engFeature); + } else { + engStreet = null; + } + + if (!TextUtils.isEmpty(engStreet)) { + for (int j = 0; j < LocationController.unnamedRoads.length; ++j) { + if (LocationController.unnamedRoads[j].equalsIgnoreCase(engStreet.toString())) { + isUnnamed = true; + break; + } + } + } + + if (!TextUtils.isEmpty(engStreet)) { + streetLocation.geoAddress.flags |= 4; + streetLocation.geoAddress.street = engStreet.toString(); + } + } + } + if (!isUnnamed) { + locations.add(streetLocation); + if (locations.size() >= maxCount) { + break; + } } } @@ -339,6 +418,31 @@ public void searchPlacesWithQuery(final String query, final Location coordinate, cityLocation.emoji = LocationController.countryCodeToEmoji(address.getCountryCode()); cities.add(cityLocation.title); cityLocation.address = LocaleController.getString("PassportCity", R.string.PassportCity); + if (engAddress != null) { + cityLocation.geoAddress = new TL_stories.TL_geoPointAddress(); + cityLocation.geoAddress.country_iso2 = engAddress.getCountryCode(); + + String engCity = null, engState = null; + if (TextUtils.isEmpty(engCity)) { + engCity = engAddress.getLocality(); + } + if (TextUtils.isEmpty(engCity)) { + engCity = engAddress.getAdminArea(); + } + if (TextUtils.isEmpty(engCity)) { + engCity = engAddress.getSubAdminArea(); + } + engState = engAddress.getAdminArea(); + + if (!TextUtils.isEmpty(engState)) { + cityLocation.geoAddress.state = engState; + cityLocation.geoAddress.flags |= 1; + } + if (!TextUtils.isEmpty(engCity)) { + cityLocation.geoAddress.city = engCity; + cityLocation.geoAddress.flags |= 2; + } + } locations.add(cityLocation); if (locations.size() >= maxCount) { break; @@ -356,6 +460,10 @@ public void searchPlacesWithQuery(final String query, final Location coordinate, countryLocation.emoji = LocationController.countryCodeToEmoji(address.getCountryCode()); countries.add(countryLocation.title); countryLocation.address = LocaleController.getString("Country", R.string.Country); + if (engAddress != null) { + countryLocation.geoAddress = new TL_stories.TL_geoPointAddress(); + countryLocation.geoAddress.country_iso2 = engAddress.getCountryCode(); + } locations.add(countryLocation); if (locations.size() >= maxCount) { break; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java index f950cf40f0..d59e869a2c 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/LocationActivityAdapter.java @@ -37,6 +37,7 @@ import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.FlickerLoadingView; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.SharedMediaLayout; import org.telegram.ui.LocationActivity; import java.util.ArrayList; @@ -75,9 +76,11 @@ public void setAddressNameOverride(String address) { updateCell(); } - public LocationActivityAdapter(Context context, int type, long did, boolean emptyView, Theme.ResourcesProvider resourcesProvider, boolean stories, boolean biz) { - super(stories, biz); + private boolean fromStories; + public LocationActivityAdapter(Context context, int type, long did, boolean emptyView, Theme.ResourcesProvider resourcesProvider, boolean stories, boolean fromStories, boolean biz) { + super(stories, biz); + this.fromStories = fromStories; mContext = context; locationType = type; dialogId = did; @@ -85,6 +88,21 @@ public LocationActivityAdapter(Context context, int type, long did, boolean empt this.resourcesProvider = resourcesProvider; } + private SharedMediaLayout sharedMediaLayout; + private boolean sharedMediaLayoutVisible; + public void setSharedMediaLayout(SharedMediaLayout layout) { + this.sharedMediaLayout = layout; + } + + public boolean setSharedMediaLayoutVisible(boolean sharedMediaLayoutVisible) { + if (this.sharedMediaLayoutVisible != sharedMediaLayoutVisible) { + this.sharedMediaLayoutVisible = sharedMediaLayoutVisible; + notifyDataSetChanged(); + return true; + } + return false; + } + private boolean myLocationDenied = false; private boolean askingForMyLocation = false; public void setMyLocationDenied(boolean myLocationDenied, boolean askingForLocation) { @@ -305,43 +323,49 @@ public void fetchLocationAddress() { } fetchingLocation = true; updateCell(); - LocationController.fetchLocationAddress(location, this); + LocationController.fetchLocationAddress(location, stories ? LocationController.TYPE_STORY : 0, this); } } @Override public int getItemCount() { + int count; if (locationType == LocationActivity.LOCATION_TYPE_LIVE_VIEW) { - return 2; + count = 2; } else if (locationType == LocationActivity.LOCATION_TYPE_GROUP_VIEW) { - return 2; + count = 2; } else if (locationType == LocationActivity.LOCATION_TYPE_GROUP) { - return 2; + count = 2; } else if (biz) { - return 2; + count = 2; } else if (currentMessageObject != null) { - return 2 + (currentLiveLocations.isEmpty() ? 1 : currentLiveLocations.size() + 3); + count = 2 + (currentLiveLocations.isEmpty() ? (fromStories ? 0 : 1) : currentLiveLocations.size() + 3); } else if (locationType == LocationActivity.LOCATION_TYPE_LIVE) { LocationController.SharingLocationInfo currentInfo = LocationController.getInstance(currentAccount).getSharingLocationInfo(dialogId); - return 2 + currentLiveLocations.size() + (currentInfo != null && currentInfo.period != 0x7FFFFFFF ? 1 : 0); + count = 2 + currentLiveLocations.size() + (currentInfo != null && currentInfo.period != 0x7FFFFFFF ? 1 : 0); } else { if (searching || !searched || places.isEmpty()) { - int count = 6; + count = 6; if (locationType == LocationActivity.LOCATION_TYPE_SEND) { count = 5; } else if (locationType == ChatAttachAlertLocationLayout.LOCATION_TYPE_STORY) { count = 5 + (this.street != null ? 1 : 0); } - return count + (!myLocationDenied && (searching || !searched) ? 2 : 0) + (needEmptyView ? 1 : 0) - (myLocationDenied ? 2 : 0); - } - int count = 5; - if (locationType == LocationActivity.LOCATION_TYPE_SEND_WITH_LIVE) { - count = 6; - } else if (locationType == ChatAttachAlertLocationLayout.LOCATION_TYPE_STORY) { - count = 5;// + (this.street != null ? 1 : 0); + count += (!myLocationDenied && (searching || !searched) ? 2 : 0) + (needEmptyView ? 1 : 0) - (myLocationDenied ? 2 : 0); + } else { + count = 5; + if (locationType == LocationActivity.LOCATION_TYPE_SEND_WITH_LIVE) { + count = 6; + } else if (locationType == ChatAttachAlertLocationLayout.LOCATION_TYPE_STORY) { + count = 5;// + (this.street != null ? 1 : 0); + } + count += locations.size() + places.size() + (needEmptyView ? 1 : 0); } - return count + locations.size() + places.size() + (needEmptyView ? 1 : 0); } + if (sharedMediaLayout != null && sharedMediaLayoutVisible) { + count++; + } + return count; } private FrameLayout emptyCell; @@ -419,6 +443,9 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType view = locationCell2; break; } + case VIEW_TYPE_SHARED_STORIES: + view = sharedMediaLayout; + break; case VIEW_TYPE_EMPTY: default: { view = new View(mContext); @@ -441,6 +468,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType public static final int VIEW_TYPE_SHADOW = 10; public static final int VIEW_TYPE_EMPTY = 11; public static final int VIEW_TYPE_STORY_LOCATION = 12; + public static final int VIEW_TYPE_SHARED_STORIES = 13; @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { @@ -603,6 +631,9 @@ public Object getItem(int i) { @Override public int getItemViewType(int position) { + if (position == getItemCount() - 1 && sharedMediaLayout != null && sharedMediaLayoutVisible) { + return VIEW_TYPE_SHARED_STORIES; + } if (position == 0) { return VIEW_TYPE_PADDING; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MessagesSearchAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MessagesSearchAdapter.java index bfb25c2896..1665b5c8a8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MessagesSearchAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Adapters/MessagesSearchAdapter.java @@ -8,30 +8,53 @@ package org.telegram.ui.Adapters; +import static org.telegram.messenger.AndroidUtilities.checkAndroidTheme; +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.statusBarHeight; + import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; import android.view.View; import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; +import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.HashtagSearchController; +import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.DialogCell; +import org.telegram.ui.Components.AvatarsDrawable; import org.telegram.ui.Components.FlickerLoadingView; +import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Stories.StoriesController; import java.util.ArrayList; import java.util.HashSet; -public class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter { +public class MessagesSearchAdapter extends RecyclerListView.SelectionAdapter implements NotificationCenter.NotificationCenterDelegate { private Context mContext; private HashSet messageIds = new HashSet<>(); private ArrayList searchResultMessages = new ArrayList<>(); + + public boolean containsStories; + public int loadedCount; public int flickerCount; @@ -48,10 +71,64 @@ public MessagesSearchAdapter(Context context, Theme.ResourcesProvider resourcesP this.isSavedMessages = isSavedMessages; } + public StoriesController.SearchStoriesList storiesList; + public void setStoriesList(StoriesController.SearchStoriesList storiesList) { + this.storiesList = storiesList; + if (storiesList != null) { + storiesList.load(true, 3); + } + } + + private Runnable loadStories = () -> { + if (storiesList != null) { + storiesList.load(true, 3); + } + }; + + public void searchStories(String hashtag, boolean instant) { + if (hashtag.startsWith("$")) hashtag = ""; + if (hashtag.startsWith("#")) hashtag = hashtag.substring(1); + + final String currentHashtag = storiesList == null ? "" : storiesList.query; + if (TextUtils.equals(currentHashtag, hashtag)) return; + + final boolean wereContainingStories = containsStories; + + AndroidUtilities.cancelRunOnUIThread(loadStories); + if (storiesList != null) { + storiesList.cancel(); + } + + if (!TextUtils.isEmpty(hashtag)) { + storiesList = new StoriesController.SearchStoriesList(currentAccount, hashtag); + if (instant) { + loadStories.run(); + } else { + AndroidUtilities.runOnUIThread(loadStories, 1000); + } + } + + final boolean nowContainingStories = storiesList != null && storiesList.getCount() > 0; + if (nowContainingStories != wereContainingStories) { + notifyDataSetChanged(); + } + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.storiesListUpdated) { + if (args[0] == storiesList) { + notifyDataSetChanged(); + } + } + } + @Override public void notifyDataSetChanged() { final int oldItemsCount = getItemCount(); + containsStories = storiesList != null && storiesList.getCount() > 0; + searchResultMessages.clear(); messageIds.clear(); ArrayList searchResults = searchType == 0 ? MediaDataController.getInstance(currentAccount).getFoundMessageObjects() : HashtagSearchController.getInstance(currentAccount).getMessages(searchType); @@ -87,10 +164,13 @@ public void notifyDataSetChanged() { @Override public int getItemCount() { - return searchResultMessages.size() + flickerCount; + return (containsStories ? 1 : 0) + searchResultMessages.size() + flickerCount; } public Object getItem(int i) { + if (containsStories) { + i--; + } if (i < 0 || i >= searchResultMessages.size()) { return null; } @@ -99,7 +179,7 @@ public Object getItem(int i) { @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { - return holder.getItemViewType() == 0; + return holder.getItemViewType() == 0 || holder.getItemViewType() == 2; } @Override @@ -115,6 +195,9 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType flickerLoadingView.setViewType(FlickerLoadingView.DIALOG_CELL_TYPE); view = flickerLoadingView; break; + case 2: + view = new StoriesView(mContext, resourcesProvider); + break; } view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); return new RecyclerListView.Holder(view); @@ -149,14 +232,106 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { useMe = true; } cell.setDialog(did, messageObject, date, useMe, false); + } else if (holder.getItemViewType() == 2) { + ((StoriesView) holder.itemView).set(storiesList); } } @Override public int getItemViewType(int i) { + if (containsStories) { + i--; + if (i == -1) + return 2; + } if (i < searchResultMessages.size()) { return 0; } return 1; } + + public void attach() { + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.storiesListUpdated); + } + + public void detach() { + AndroidUtilities.cancelRunOnUIThread(loadStories); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.storiesListUpdated); + } + + public static class StoriesView extends FrameLayout { + + private final Theme.ResourcesProvider resourcesProvider; + private final AvatarsDrawable avatarsDrawable; + private final TextView titleTextView; + private final TextView subtitleTextView; + + public StoriesView(Context context, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.resourcesProvider = resourcesProvider; + setWillNotDraw(false); + + avatarsDrawable = new AvatarsDrawable(this, false); + avatarsDrawable.setCentered(true); + avatarsDrawable.width = dp(75); + avatarsDrawable.height = dp(48); + avatarsDrawable.drawStoriesCircle = true; + avatarsDrawable.setSize(dp(22)); + + titleTextView = new TextView(context); + titleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + titleTextView.setTypeface(AndroidUtilities.bold()); + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 76, 7, 12, 0)); + + subtitleTextView = new TextView(context); + subtitleTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); + subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + addView(subtitleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 76, 26.33f, 12, 0)); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(dp(48), MeasureSpec.EXACTLY)); + } + + public void set(StoriesController.SearchStoriesList list) { + int actualCount = 0; + for (int i = 0; i < list.messageObjects.size() && actualCount < 3; ++i) { + MessageObject msg = list.messageObjects.get(i); + final long dialogId = msg.storyItem.dialogId; + if (dialogId >= 0) { + TLRPC.User user = MessagesController.getInstance(list.currentAccount).getUser(dialogId); + if (user != null) { + avatarsDrawable.setObject(actualCount, list.currentAccount, user); + actualCount++; + } + } else { + TLRPC.Chat chat = MessagesController.getInstance(list.currentAccount).getChat(-dialogId); + if (chat != null) { + avatarsDrawable.setObject(actualCount, list.currentAccount, chat); + actualCount++; + } + } + } + avatarsDrawable.setCount(actualCount); + avatarsDrawable.commitTransition(false); + + titleTextView.setText(LocaleController.formatPluralString("HashtagStoriesFound", list.getCount())); + subtitleTextView.setText(LocaleController.formatString(R.string.HashtagStoriesFoundSubtitle, list.query)); + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.save(); + canvas.translate(0, 0); + avatarsDrawable.onDraw(canvas); + canvas.restore(); + + super.onDraw(canvas); + Paint dividerPaint = Theme.getThemePaint(Theme.key_paint_divider, resourcesProvider); + if (dividerPaint == null) dividerPaint = Theme.dividerPaint; + canvas.drawRect(0, getHeight() - 1, getWidth(), getHeight(), dividerPaint); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java index 711662175a..3e34c6c8be 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ArticleViewer.java @@ -170,6 +170,7 @@ import org.telegram.ui.Components.SeekBar; import org.telegram.ui.Components.SeekBarView; import org.telegram.ui.Components.ShareAlert; +import org.telegram.ui.Components.SizeNotifierFrameLayout; import org.telegram.ui.Components.StaticLayoutEx; import org.telegram.ui.Components.TableLayout; import org.telegram.ui.Components.TextPaintImageReceiverSpan; @@ -192,6 +193,14 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDelegate { + public final boolean isSheet; + public final ArticleViewer.Sheet sheet; + + public ArticleViewer(boolean sheet, Context context) { + this.isSheet = sheet; + this.sheet = sheet ? new ArticleViewer.Sheet(context) : null; + } + private Activity parentActivity; private BaseFragment parentFragment; private ArrayList createdWebViews = new ArrayList<>(); @@ -327,13 +336,17 @@ public static ArticleViewer getInstance() { synchronized (ArticleViewer.class) { localInstance = Instance; if (localInstance == null) { - Instance = localInstance = new ArticleViewer(); + Instance = localInstance = new ArticleViewer(false, null); } } } return localInstance; } + public static ArticleViewer makeSheet(Context context) { + return new ArticleViewer(true, context); + } + public static boolean hasInstance() { return Instance != null; } @@ -359,7 +372,7 @@ private static class TL_pageBlockDetailsBottom extends TLRPC.PageBlock { private TLRPC.TL_pageBlockDetails parent; } - private class TL_pageBlockListParent extends TLRPC.PageBlock { + private static class TL_pageBlockListParent extends TLRPC.PageBlock { private TLRPC.TL_pageBlockList pageBlockList; private ArrayList items = new ArrayList<>(); private int maxNumWidth; @@ -368,7 +381,7 @@ private class TL_pageBlockListParent extends TLRPC.PageBlock { private int level; } - private class TL_pageBlockListItem extends TLRPC.PageBlock { + private static class TL_pageBlockListItem extends TLRPC.PageBlock { private TL_pageBlockListParent parent; private TLRPC.PageBlock blockItem; private TLRPC.RichText textItem; @@ -377,7 +390,7 @@ private class TL_pageBlockListItem extends TLRPC.PageBlock { private int index = Integer.MAX_VALUE; } - private class TL_pageBlockOrderedListParent extends TLRPC.PageBlock { + private static class TL_pageBlockOrderedListParent extends TLRPC.PageBlock { private TLRPC.TL_pageBlockOrderedList pageBlockOrderedList; private ArrayList items = new ArrayList<>(); private int maxNumWidth; @@ -386,7 +399,7 @@ private class TL_pageBlockOrderedListParent extends TLRPC.PageBlock { private int level; } - private class TL_pageBlockOrderedListItem extends TLRPC.PageBlock { + private static class TL_pageBlockOrderedListItem extends TLRPC.PageBlock { private TL_pageBlockOrderedListParent parent; private TLRPC.PageBlock blockItem; private TLRPC.RichText textItem; @@ -3038,7 +3051,7 @@ private void setMapColors(SparseArray map) { public void setParentActivity(Activity activity, BaseFragment fragment) { parentFragment = fragment; - currentAccount = UserConfig.selectedAccount; + currentAccount = fragment != null ? fragment.getCurrentAccount() : UserConfig.selectedAccount; NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingProgressDidChanged); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidReset); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingPlayStateChanged); @@ -4313,7 +4326,10 @@ private boolean open(final MessageObject messageObject, TLRPC.WebPage webpage, S } lastInsets = null; - if (!isVisible) { + if (sheet != null) { + AndroidUtilities.removeFromParent(windowView); + sheet.windowView.addView(windowView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + } else if (!isVisible) { WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); if (attachedToWindow) { try { @@ -11828,4 +11844,79 @@ private BlockVideoCell getViewFromListView(ViewGroup listView, TLRPC.PageBlock p return null; } } + + public class Sheet implements BaseFragment.AttachedSheet { + + public WindowView windowView; + + public Sheet(Context context) { + + windowView = new WindowView(context); + + } + + public WindowView getWindowView() { + return windowView; + } + + @Override + public boolean isShown() { + return false; + } + + @Override + public void dismiss() { + + } + + @Override + public void release() { + + } + + @Override + public boolean isFullyVisible() { + return false; + } + + @Override + public boolean attachedToParent() { + return false; + } + + @Override + public boolean onBackPressed() { + return false; + } + + @Override + public boolean showDialog(Dialog dialog) { + return false; + } + + @Override + public void setKeyboardHeightFromParent(int keyboardHeight) { + + } + + @Override + public int getNavigationBarColor(int color) { + return 0; + } + + @Override + public void setOnDismissListener(Runnable onDismiss) { + + } + + public class WindowView extends SizeNotifierFrameLayout { + + public WindowView(Context context) { + super(context); + } + + } + + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/AvatarSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/AvatarSpan.java index 88c4ad6dbe..bf7b795b32 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/AvatarSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/AvatarSpan.java @@ -7,6 +7,7 @@ import android.graphics.Paint; import android.text.Spannable; import android.text.style.ReplacementSpan; +import android.util.Log; import android.view.View; import androidx.annotation.NonNull; @@ -15,6 +16,7 @@ import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.MessagesController; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.AvatarDrawable; public class AvatarSpan extends ReplacementSpan { @@ -87,6 +89,14 @@ public void onViewDetachedFromWindow(@NonNull View v) { } }; + public void setDialogId(long dialogId) { + if (dialogId >= 0) { + setUser(MessagesController.getInstance(currentAccount).getUser(dialogId)); + } else { + setChat(MessagesController.getInstance(currentAccount).getChat(-dialogId)); + } + } + public void setChat(TLRPC.Chat chat) { avatarDrawable.setInfo(currentAccount, chat); imageReceiver.setForUserOrChat(chat, avatarDrawable); @@ -108,11 +118,17 @@ public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, } private float translateX, translateY; + private int shadowPaintAlpha = 0xFF; @Override public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + if (shadowPaintAlpha != paint.getAlpha()) { + shadowPaint.setAlpha(shadowPaintAlpha = paint.getAlpha()); + shadowPaint.setShadowLayer(dp(1), 0, dp(.66f), Theme.multAlpha(0x33000000, shadowPaintAlpha / 255f)); + } canvas.drawCircle(translateX + x + dp(sz) / 2f, translateY + (top + bottom) / 2f, dp(sz) / 2f, shadowPaint); imageReceiver.setImageCoords(translateX + x, translateY + (top + bottom) / 2f - dp(sz) / 2f, dp(sz), dp(sz)); + imageReceiver.setAlpha(paint.getAlpha() / 255f); imageReceiver.draw(canvas); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/BoostsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/BoostsActivity.java index 38d56e4721..65cf85310f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/BoostsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/BoostsActivity.java @@ -270,7 +270,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi overviewCell.setData(0, Integer.toString(boostsStatus.level), null, LocaleController.getString("BoostsLevel2", R.string.BoostsLevel2)); if (boostsStatus.premium_audience != null && boostsStatus.premium_audience.total != 0) { float percent = (((float) boostsStatus.premium_audience.part / (float) boostsStatus.premium_audience.total) * 100f); - overviewCell.setData(1, "~" + (int) boostsStatus.premium_audience.part, String.format(Locale.US, "%.1f", percent) + "%", LocaleController.getString(isChannel() ? R.string.PremiumSubscribers : R.string.PremiumMembers)); + overviewCell.setData(1, "≈" + (int) boostsStatus.premium_audience.part, String.format(Locale.US, "%.1f", percent) + "%", LocaleController.getString(isChannel() ? R.string.PremiumSubscribers : R.string.PremiumMembers)); } else { overviewCell.setData(1, "~0", "0%", LocaleController.getString(isChannel() ? R.string.PremiumSubscribers : R.string.PremiumMembers)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/BubbleActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/BubbleActivity.java index 264f325730..a475603a27 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/BubbleActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/BubbleActivity.java @@ -80,7 +80,7 @@ protected void onCreate(Bundle savedInstanceState) { Theme.createDialogsResources(this); Theme.createChatResources(this, false); - actionBarLayout = INavigationLayout.newLayout(this); + actionBarLayout = INavigationLayout.newLayout(this, false); actionBarLayout.setInBubbleMode(true); actionBarLayout.setRemoveActionBarExtraHeight(true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Business/BusinessIntroActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/Business/BusinessIntroActivity.java index fc4c9dd8d5..c895b06c45 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Business/BusinessIntroActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Business/BusinessIntroActivity.java @@ -174,7 +174,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { previewContainer.addView(previewBackground, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); previewContainer.addView(greetingsView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 42, 18, 42, 18)); - titleEdit = new EditTextCell(context, getString(R.string.BusinessIntroTitleHint), false, getMessagesController().introTitleLengthLimit) { + titleEdit = new EditTextCell(context, getString(R.string.BusinessIntroTitleHint), false, getMessagesController().introTitleLengthLimit, resourceProvider) { @Override protected void onTextChanged(CharSequence newText) { greetingsView.setPreview(titleEdit.getText().toString(), messageEdit.getText().toString()); @@ -193,7 +193,7 @@ protected void onFocusChanged(boolean focused) { titleEdit.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); titleEdit.setDivider(true); titleEdit.hideKeyboardOnEnter(); - messageEdit = new EditTextCell(context, getString(R.string.BusinessIntroMessageHint), true, getMessagesController().introDescriptionLengthLimit) { + messageEdit = new EditTextCell(context, getString(R.string.BusinessIntroMessageHint), true, getMessagesController().introDescriptionLengthLimit, resourceProvider) { @Override protected void onTextChanged(CharSequence newText) { greetingsView.setPreview(titleEdit.getText().toString(), messageEdit.getText().toString()); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/CameraScanActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/CameraScanActivity.java index f2a49168c5..5ad81ed5a0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/CameraScanActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/CameraScanActivity.java @@ -188,7 +188,7 @@ public static BottomSheet showAsSheet(Activity parentActivity, boolean gallery, if (parentActivity == null) { return null; } - INavigationLayout[] actionBarLayout = new INavigationLayout[]{INavigationLayout.newLayout(parentActivity)}; + INavigationLayout[] actionBarLayout = new INavigationLayout[]{INavigationLayout.newLayout(parentActivity, false)}; BottomSheet bottomSheet = new BottomSheet(parentActivity, false) { CameraScanActivity fragment; { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java index 8ae4147941..70aa9ee8d8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/BaseCell.java @@ -16,6 +16,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.os.Build; +import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import android.view.ViewConfiguration; 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 8a0e7c062c..99fc138037 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ChatMessageCell.java @@ -385,12 +385,20 @@ public void setScrimReaction(Integer scrimViewReaction) { reactionsLayoutInBubble.setScrimReaction(scrimViewReaction); } - public void drawScrimReaction(Canvas canvas, Integer scrimViewReaction) { + public void drawScrimReaction(Canvas canvas, Integer scrimViewReaction, float progress) { if ((currentPosition == null || ((currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) != 0 && (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0)) && !reactionsLayoutInBubble.isSmall) { + reactionsLayoutInBubble.setScrimProgress(progress); reactionsLayoutInBubble.draw(canvas, transitionParams.animateChangeProgress, scrimViewReaction); } } + public void drawScrimReactionPreview(View view, Canvas canvas, int offset, Integer scrimViewReaction, float progress) { + if ((currentPosition == null || ((currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) != 0 && (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0)) && !reactionsLayoutInBubble.isSmall) { + reactionsLayoutInBubble.setScrimProgress(progress); + reactionsLayoutInBubble.drawPreview(view, canvas, offset, scrimViewReaction); + } + } + public boolean checkUnreadReactions(float clipTop, int clipBottom) { if (!reactionsLayoutInBubble.hasUnreadReactions) { return false; @@ -530,6 +538,9 @@ default void didPressWebPage(ChatMessageCell cell, TLRPC.WebPage webpage, String default void didPressImage(ChatMessageCell cell, float x, float y) { } + default void didPressGroupImage(ChatMessageCell cell, ImageReceiver imageReceiver, TLRPC.MessageExtendedMedia media, float x, float y) { + } + default void didPressSideButton(ChatMessageCell cell) { } @@ -1203,6 +1214,8 @@ class LoadingDrawableLocation { private boolean topNearToSet; private boolean bottomNearToSet; + public GroupMedia groupMedia; + private AnimatorSet shakeAnimation; // @@ -1447,7 +1460,7 @@ class LoadingDrawableLocation { private MessageBackgroundDrawable backgroundDrawable; - private int namesOffset; + public int namesOffset; private int lastSendState; private int lastDeleteDate; @@ -3952,6 +3965,9 @@ public boolean onTouchEvent(MotionEvent event) { if (!result) { result = checkFactCheckMotionEvent(event); } + if (!result && groupMedia != null) { + result = groupMedia.onTouchEvent(event); + } if (event.getAction() == MotionEvent.ACTION_CANCEL) { spoilerPressed = null; @@ -4357,8 +4373,9 @@ private boolean checkPinchToZoom(MotionEvent ev) { } private boolean checkTextSelection(MotionEvent event) { + if (delegate == null) return false; TextSelectionHelper.ChatListTextSelectionHelper textSelectionHelper = delegate.getTextSelectionHelper(); - if (textSelectionHelper == null/* || MessagesController.getInstance(currentAccount).isChatNoForwards(currentMessageObject.getChatId()) || (currentMessageObject.messageOwner != null && currentMessageObject.messageOwner.noforwards)*/) { + if (textSelectionHelper == null || textSelectionHelper.isMenuEmpty()) { return false; } boolean hasTextBlocks = currentMessageObject.textLayoutBlocks != null && !currentMessageObject.textLayoutBlocks.isEmpty(); @@ -4784,7 +4801,7 @@ private void didClickedImage() { } if (currentMessageObject.type == MessageObject.TYPE_EXTENDED_MEDIA_PREVIEW) { if (currentMessageObject.messageOwner != null && currentMessageObject.messageOwner.media != null && - currentMessageObject.messageOwner.media.extended_media != null && currentMessageObject.messageOwner.reply_markup != null) { + !currentMessageObject.messageOwner.media.extended_media.isEmpty() && currentMessageObject.messageOwner.reply_markup != null) { for (TLRPC.TL_keyboardButtonRow row : currentMessageObject.messageOwner.reply_markup.rows) { for (TLRPC.KeyboardButton button : row.buttons) { delegate.didPressExtendedMediaPreview(this, button); @@ -5056,6 +5073,13 @@ public ImageReceiver getPhotoImage() { return photoImage; } + public ImageReceiver getPhotoImage(int index) { + if (groupMedia != null) { + return groupMedia.getPhotoImage(index); + } + return photoImage; + } + public ImageReceiver getBlurredPhotoImage() { return blurredPhotoImage; } @@ -5119,6 +5143,9 @@ protected void onDetachedFromWindow() { } } attachedToWindow = false; + if (groupMedia != null) { + groupMedia.onDetachedFromWindow(); + } avatarImage.onDetachedFromWindow(); checkImageReceiversAttachState(); if (addedForTest && currentUrl != null && currentWebFile != null) { @@ -5230,6 +5257,9 @@ protected void onAttachedToWindow() { if (flagSecure != null) { flagSecure.attach(); } + if (groupMedia != null) { + groupMedia.onAttachedToWindow(); + } updateFlagSecure(); if (currentMessageObject != null && currentMessageObject.type == MessageObject.TYPE_EXTENDED_MEDIA_PREVIEW && unlockLayout != null) { @@ -5243,7 +5273,7 @@ protected void onAttachedToWindow() { } if (mediaSpoilerEffect2 != null) { if (mediaSpoilerEffect2.destroyed) { - mediaSpoilerEffect2 = SpoilerEffect2.getInstance(this); + mediaSpoilerEffect2 = makeSpoilerEffect(); if (mediaSpoilerEffect2Index != null) { mediaSpoilerEffect2.reassignAttach(this, mediaSpoilerEffect2Index); } @@ -5259,6 +5289,10 @@ protected void onAttachedToWindow() { } } + protected SpoilerEffect2 makeSpoilerEffect() { + return SpoilerEffect2.getInstance(this); + } + public void copySpoilerEffect2AttachIndexFrom(ChatMessageCell cell) { if (cell != null && cell.mediaSpoilerEffect2 != null) { mediaSpoilerEffect2Index = cell.mediaSpoilerEffect2.getAttachIndex(cell); @@ -5634,6 +5668,10 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe hasInvoicePrice = false; instantPressed = commentButtonPressed = false; gradientDrawable = null; + if (groupMedia != null && messageObject.type != MessageObject.TYPE_PAID_MEDIA) { + groupMedia.onDetachedFromWindow(); + groupMedia = null; + } setInstantButtonPressed(false); if (!pollChanged && Build.VERSION.SDK_INT >= 21) { for (int a = 0; a < selectorDrawable.length; a++) { @@ -7839,6 +7877,149 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } } } + } else if (messageObject.type == MessageObject.TYPE_PAID_MEDIA) { + 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); + drawForwardedName = !isRepliesChat; + + if (groupMedia == null) { + groupMedia = new GroupMedia(this); + } + groupMedia.setOverrideWidth(-1); + groupMedia.setMessageObject(messageObject, pinnedBottom, pinnedTop); + backgroundWidth = groupMedia.width + dp(8 + 9); + measureTime(messageObject); + if (drawCommentButton && totalCommentWidth + AndroidUtilities.dp(20) > backgroundWidth) { + backgroundWidth = totalCommentWidth + AndroidUtilities.dp(20); + groupMedia.setOverrideWidth(backgroundWidth - dp(8 + 9)); + } + if (timeWidth + dp(25) > backgroundWidth) { + backgroundWidth = timeWidth + dp(25); + groupMedia.setOverrideWidth(backgroundWidth - dp(8 + 9)); + } + mediaBackground = false; + + int timeWidthTotal = timeWidth + AndroidUtilities.dp((SharedConfig.bubbleRadius >= 10 ? 22 : 18) + (messageObject.isOutOwner() ? 20 : 0)); + if (backgroundWidth < timeWidthTotal) { + backgroundWidth = timeWidthTotal; + } + int widthForCaption = backgroundWidth - dp(20); + int additionHeight = 0; + + currentCaption = messageObject.caption; + if (currentCaption != null) { + try { + captionFullWidth = widthForCaption; + widthForCaption -= getExtraTextX() * 2; + captionLayout = new MessageObject.TextLayoutBlocks(getPrimaryMessageObject(), currentCaption, Theme.chat_msgTextPaint, widthForCaption); + captionLayout.bounceFrom(prevCaptionLayout); + captionWidth = captionLayout.textWidth; + captionHeight = captionLayout.textHeight(); + addedCaptionHeight = captionHeight + AndroidUtilities.dp(9); + if (captionWidth > widthForCaption) { + groupMedia.setOverrideWidth(captionWidth + getExtraTextX() * 2 + dp(20) - dp(8 + 9) + dp(14)); + } + if (captionLayout.hasCodeAtBottom || captionLayout.hasQuoteAtBottom) { + captionHeight += AndroidUtilities.dp(14); + addedCaptionHeight += AndroidUtilities.dp(14); + } + if (currentPosition == null || (currentPosition.flags & captionFlag()) != 0) { + additionHeight += addedCaptionHeight; + int widthToCheck = Math.max(captionWidth, backgroundWidth - AndroidUtilities.dp(20)); + if ((reactionsLayoutInBubble.isEmpty || reactionsLayoutInBubble.isSmall) && !shouldDrawTimeOnMedia() && widthToCheck + AndroidUtilities.dp(2) - captionLayout.lastLineWidth < timeWidthTotal + getExtraTimeX() && !hasFactCheck) { + additionHeight += AndroidUtilities.dp(14); + addedCaptionHeight += AndroidUtilities.dp(14); + captionNewLine = 1; + } + } else { + captionLayout = null; + } + } catch (Exception e) { + FileLog.e(e); + } + } + + int additionalTop = 0; + if (captionLayout != null && captionAbove) { + additionalTop = captionLayout.textHeight() + dp(8); + } + + setMessageObjectInternal(messageObject); + if (backgroundWidth > groupMedia.width + dp(8 + 9)) { + groupMedia.setOverrideWidth(backgroundWidth - dp(8 + 9)); + groupMedia.updateHolders(messageObject); + } + groupMedia.updateHolders(messageObject); + backgroundWidth = groupMedia.width + dp(8 + 9); + if (backgroundWidth < timeWidthTotal) { + backgroundWidth = timeWidthTotal; + } + setMessageObjectInternal(messageObject); + + totalHeight = groupMedia.height + AndroidUtilities.dp(14) + namesOffset + additionHeight; + + if (currentPosition != null && currentMessagesGroup != null && currentMessagesGroup.messages.size() > 1) { + if ((currentPosition.flags & MessageObject.POSITION_FLAG_TOP) == 0) { + totalHeight -= AndroidUtilities.dp(6); + mediaOffsetY -= AndroidUtilities.dp(6); + } + if ((currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) == 0) { + totalHeight -= AndroidUtilities.dp(6); + } + } + if (drawPinnedTop) { + namesOffset -= AndroidUtilities.dp(1); + } + if (!reactionsLayoutInBubble.isSmall) { + reactionsLayoutInBubble.measure(backgroundWidth - AndroidUtilities.dp(24), Gravity.LEFT); + if (!reactionsLayoutInBubble.isEmpty) { + reactionsLayoutInBubble.totalHeight = reactionsLayoutInBubble.height + AndroidUtilities.dp(12); + measureTime(messageObject); + if (reactionsLayoutInBubble.width > backgroundWidth) { + backgroundWidth = reactionsLayoutInBubble.width; + } + if (reactionsLayoutInBubble.lastLineX + timeWidth + AndroidUtilities.dp(24) > backgroundWidth) { + reactionsLayoutInBubble.totalHeight += AndroidUtilities.dp(12); + reactionsLayoutInBubble.positionOffsetY -= AndroidUtilities.dp(12); + } + if (!messageObject.isRestrictedMessage && messageObject.caption != null) { + reactionsLayoutInBubble.positionOffsetY += AndroidUtilities.dp(14); + } + totalHeight += reactionsLayoutInBubble.totalHeight; + } + } + + int y = 0; + if (drawPinnedTop) { + namesOffset -= AndroidUtilities.dp(documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT ? 2 : 1); + } + if (drawPinnedTop && !messageObject.isOutOwner()) { + totalHeight += AndroidUtilities.dp(documentAttachType == DOCUMENT_ATTACH_TYPE_DOCUMENT ? 2 : 0); + } + if (namesOffset > 0) { + y = AndroidUtilities.dp(9 + 4); + totalHeight += dp(4); + } else { + y = AndroidUtilities.dp(5); + totalHeight -= AndroidUtilities.dp(4); + } + + int x; + if (currentMessageObject.isOutOwner()) { + if (mediaBackground) { + x = layoutWidth - backgroundWidth - AndroidUtilities.dp(3); + } else { + x = layoutWidth - backgroundWidth + AndroidUtilities.dp(6); + } + } else { + if ((isChat || currentMessageObject.isRepostPreview) && isAvatarVisible && !isPlayingRound) { + x = AndroidUtilities.dp(63); + } else { + x = AndroidUtilities.dp(15); + } + } + x -= AndroidUtilities.dp(2); + groupMedia.x = x; + groupMedia.y = y + namesOffset + additionalTop; } 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) { @@ -7917,15 +8098,15 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } } if (captionLayout != null) { - maxTextWidth = Math.max(captionLayout.textWidth, maxTextWidth); + maxTextWidth = Math.max(captionLayout.textWidth + getExtraTextX() * 2 + dp(31), maxTextWidth); } if (!reactionsLayoutInBubble.isSmall) { reactionsLayoutInBubble.measure(captionFullWidth, Gravity.LEFT); - if (!reactionsLayoutInBubble.isEmpty && reactionsLayoutInBubble.width + AndroidUtilities.dp(31) > maxTextWidth) { - maxTextWidth = reactionsLayoutInBubble.width + AndroidUtilities.dp(31); + if (!reactionsLayoutInBubble.isEmpty) { + maxTextWidth = Math.max(maxTextWidth, reactionsLayoutInBubble.width + AndroidUtilities.dp(31)); } } - if (maxTextWidth > 0 && currentPosition == null) { + if (maxTextWidth > backgroundWidth && currentPosition == null) { backgroundWidth = maxTextWidth; maxWidth = maxTextWidth - AndroidUtilities.dp(31); } @@ -7970,6 +8151,9 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe reactionsLayoutInBubble.totalHeight += dp(captionLayout == null ? 15 : 12); reactionsLayoutInBubble.positionOffsetY -= dp(15); } + if (currentPosition == null && captionLayout != null) { + additionHeight += dp(12); + } additionHeight += reactionsLayoutInBubble.totalHeight; } } else if (messageObject.type == MessageObject.TYPE_GEO) { @@ -8413,7 +8597,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe int imageW = 0; int imageH = 0; if (messageObject.hasExtendedMediaPreview()) { - TLRPC.TL_messageExtendedMediaPreview preview = (TLRPC.TL_messageExtendedMediaPreview) messageObject.messageOwner.media.extended_media; + TLRPC.TL_messageExtendedMediaPreview preview = (TLRPC.TL_messageExtendedMediaPreview) messageObject.messageOwner.media.extended_media.get(0); if (preview.w != 0 && preview.h != 0) { imageW = preview.w; imageH = preview.h; @@ -8847,7 +9031,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe currentPhotoObjectThumb.size = -1; } - if (SharedConfig.isAutoplayVideo() && !currentMessageObject.isRepostPreview && (!currentMessageObject.hasMediaSpoilers() || currentMessageObject.isMediaSpoilersRevealed || currentMessageObject.revealingMediaSpoilers) && messageObject.type == MessageObject.TYPE_VIDEO && !messageObject.needDrawBluredPreview() && + if (SharedConfig.isAutoplayVideo() && !currentMessageObject.isRepostPreview && (!currentMessageObject.hasMediaSpoilers() || currentMessageObject.isMediaSpoilersRevealed || currentMessageObject.revealingMediaSpoilers) && (messageObject.type == MessageObject.TYPE_VIDEO /*|| messageObject.type == MessageObject.TYPE_STORY && messageObject.getDocument() != null*/) && !messageObject.needDrawBluredPreview() && ((currentMessageObject.mediaExists || currentMessageObject.attachPathExists) || messageObject.canStreamVideo() && DownloadController.getInstance(currentAccount).canDownloadMedia(currentMessageObject)) ) { if (currentPosition != null) { @@ -8856,6 +9040,13 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe autoPlayingMedia = true; } } +// if (autoPlayingMedia && messageObject.type == MessageObject.TYPE_STORY && MessageObject.getMedia(messageObject) instanceof TLRPC.TL_messageMediaStory) { +// TLRPC.TL_messageMediaStory mediaStory = (TLRPC.TL_messageMediaStory) MessageObject.getMedia(messageObject); +// TL_stories.StoryItem storyItem = mediaStory.storyItem; +// if (storyItem == null || !storyItem.isPublic) { +// autoPlayingMedia = false; +// } +// } final int cacheType = currentMessageObject.shouldEncryptPhotoOrVideo() ? ImageLoader.CACHE_TYPE_ENCRYPTED : ImageLoader.CACHE_TYPE_NONE; if (currentMessageObject.sendPreviewEntry != null) { @@ -8869,6 +9060,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe photoImage.setOrientation(currentMessageObject.sendPreviewEntry.orientation, currentMessageObject.sendPreviewEntry.invert, true); photoImage.setImage(ImageLocation.getForPath("thumb://" + currentMessageObject.sendPreviewEntry.imageId + ":" + currentMessageObject.sendPreviewEntry.path), null, null, null, null, null, currentMessageObject.sendPreviewEntry.thumb, 0, null, messageObject, 0); } + } } else if (autoPlayingMedia) { photoImage.setAllowStartAnimation(true); @@ -9072,7 +9264,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe } if (messageObject.hasMediaSpoilers() && SpoilerEffect2.supports()) { if (mediaSpoilerEffect2 == null) { - mediaSpoilerEffect2 = SpoilerEffect2.getInstance(this); + mediaSpoilerEffect2 = makeSpoilerEffect(); if (mediaSpoilerEffect2Index != null) { mediaSpoilerEffect2.reassignAttach(this, mediaSpoilerEffect2Index); } @@ -9086,7 +9278,7 @@ private void setMessageContent(MessageObject messageObject, MessageObject.Groupe invalidate(); } - if ((currentPosition == null || currentMessageObject.isMusic() || currentMessageObject.isDocument()) && !messageObject.isAnyKindOfSticker() && addedCaptionHeight == 0 && !messageObject.isExpiredStory() && !messageObject.isUnsupported()) { + if ((currentPosition == null || currentMessageObject.isMusic() || currentMessageObject.type == MessageObject.TYPE_PAID_MEDIA || currentMessageObject.isDocument()) && !messageObject.isAnyKindOfSticker() && addedCaptionHeight == 0 && !messageObject.isExpiredStory() && !messageObject.isUnsupported()) { int addCaptionLayoutWidth = 0; int width = backgroundWidth; if ((currentMessageObject.type == MessageObject.TYPE_VOICE || isRoundVideo) && messageObject.isVoiceTranscriptionOpen() && messageObject.getFactCheck() == null) { @@ -10664,7 +10856,7 @@ public boolean setHighlightedSpan(CharacterStyle span) { @Override protected boolean verifyDrawable(Drawable who) { - return super.verifyDrawable(who) || who == selectorDrawable[0] || who == selectorDrawable[1] || who == linkPreviewSelector || who == nameLayoutSelector || who == replySelector || reactionsLayoutInBubble != null && reactionsLayoutInBubble.verifyDrawable(who); + return super.verifyDrawable(who) || who == selectorDrawable[0] || who == selectorDrawable[1] || who == linkPreviewSelector || who == nameLayoutSelector || who == replySelector || reactionsLayoutInBubble != null && reactionsLayoutInBubble.verifyDrawable(who) || who instanceof LoadingDrawable; } @Override @@ -11141,7 +11333,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } if ((currentMessageObject.messageOwner.flags & TLRPC.MESSAGE_FLAG_HAS_VIEWS) != 0) { - viewsLayout = new StaticLayout(currentViewsString, Theme.chat_timePaint, viewsTextWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); + viewsLayout = new StaticLayout(currentViewsString == null ? "" : currentViewsString, Theme.chat_timePaint, viewsTextWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } else { viewsLayout = null; } @@ -11159,7 +11351,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto if (currentMessageObject.type == MessageObject.TYPE_EXTENDED_MEDIA_PREVIEW && currentUnlockString != null) { unlockLayout = new StaticLayout(currentUnlockString, Theme.chat_unlockExtendedMediaTextPaint, unlockTextWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); - TLRPC.TL_messageExtendedMediaPreview preview = (TLRPC.TL_messageExtendedMediaPreview) currentMessageObject.messageOwner.media.extended_media; + TLRPC.TL_messageExtendedMediaPreview preview = (TLRPC.TL_messageExtendedMediaPreview) currentMessageObject.messageOwner.media.extended_media.get(0); if (preview.video_duration != 0) { String str = AndroidUtilities.formatDuration(preview.video_duration, false); durationWidth = (int) Math.ceil(Theme.chat_durationPaint.measureText(str)); @@ -11190,6 +11382,24 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } factCheckY = linkPreviewAbove ? textY + currentMessageObject.textHeight(transitionParams) + dp(10) : linkPreviewY + linkPreviewHeight + dp(drawInstantView ? 46 : 0) + dp(linkPreviewHeight > 0 ? 4 : -8); } + if (groupMedia != null) { + int x; + if (currentMessageObject.isOutOwner()) { + if (mediaBackground) { + x = layoutWidth - backgroundWidth - AndroidUtilities.dp(3); + } else { + x = layoutWidth - backgroundWidth + AndroidUtilities.dp(6); + } + } else { + if ((isChat || currentMessageObject.isRepostPreview) && isAvatarVisible && !isPlayingRound) { + x = AndroidUtilities.dp(63); + } else { + x = AndroidUtilities.dp(15); + } + } + x -= AndroidUtilities.dp(2); + groupMedia.x = x; + } if (isRoundVideo) { updatePlayingMessageProgress(); } @@ -11591,7 +11801,7 @@ public void drawContent(Canvas canvas, boolean preview) { photoImage.setPressed((isHighlightedAnimated || isHighlighted) && currentPosition != null ? 2 : 0); photoImage.setVisible(!PhotoViewer.isShowingImage(currentMessageObject) && !SecretMediaViewer.getInstance().isShowingImage(currentMessageObject) && !StoryViewer.isShowingImage(currentMessageObject), false); blurredPhotoImage.setVisible(fitPhotoImage || !PhotoViewer.isShowingImage(currentMessageObject) && !SecretMediaViewer.getInstance().isShowingImage(currentMessageObject) && !StoryViewer.isShowingImage(currentMessageObject), false); - if (!photoImage.getVisible()) { + if (groupMedia != null ? !groupMedia.allVisible() : !photoImage.getVisible()) { mediaWasInvisible = true; timeWasInvisible = true; if (animatingNoSound == 1) { @@ -11620,7 +11830,10 @@ public void drawContent(Canvas canvas, boolean preview) { imageDrawn = false; radialProgress.setCircleCrossfadeColor(-1, 0.0f, 1.0f); - if (currentMessageObject.type == MessageObject.TYPE_TEXT || currentMessageObject.type == MessageObject.TYPE_STORY_MENTION || currentMessageObject.type == MessageObject.TYPE_EMOJIS || currentMessageObject.isGiveawayOrGiveawayResults()) { + if (groupMedia != null) { + drawTime = groupMedia.allVisible(); + groupMedia.draw(canvas); + } else if (currentMessageObject.type == MessageObject.TYPE_TEXT || currentMessageObject.type == MessageObject.TYPE_STORY_MENTION || currentMessageObject.type == MessageObject.TYPE_EMOJIS || currentMessageObject.isGiveawayOrGiveawayResults()) { layoutTextXY(false); if (!enterTransitionInProgress && currentMessageObject != null && !currentMessageObject.preview) { if (!transitionParams.animateExpandedQuotes || delegate == null || delegate.canDrawOutboundsContent()) { @@ -12417,6 +12630,7 @@ 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); + invalidate(); } else { int sColor = Color.WHITE; mediaSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f * photoImage.getAlpha()))); @@ -12488,12 +12702,20 @@ private void updateReactionLayoutPosition() { timeYOffset = -(drawCommentButton ? AndroidUtilities.dp(43) : 0); } } - reactionsLayoutInBubble.y = (int) (shouldDrawTimeOnMedia() ? photoImage.getImageY2() + additionalTimeOffsetY - AndroidUtilities.dp(7.3f) - timeLayout.getHeight() : layoutHeight - AndroidUtilities.dp(pinnedBottom || pinnedTop ? 7.5f : 6.5f) - timeLayout.getHeight() + timeYOffset); + reactionsLayoutInBubble.y = (int) (shouldDrawTimeOnMedia() ? getPhotoBottom() + additionalTimeOffsetY - AndroidUtilities.dp(7.3f) - timeLayout.getHeight() : layoutHeight - AndroidUtilities.dp(pinnedBottom || pinnedTop ? 7.5f : 6.5f) - timeLayout.getHeight() + timeYOffset); reactionsLayoutInBubble.y += timeLayout.getHeight() / 2f - AndroidUtilities.dp(7); reactionsLayoutInBubble.x = (int) timeX; } } + public float getPhotoBottom() { + if (groupMedia != null) { + return groupMedia.y + groupMedia.height; + } else { + return photoImage.getImageY2(); + } + } + public void drawVoiceOnce(Canvas canvas, float progress, Runnable drawRadialProgress) { if (currentMessageObject != null && currentMessageObject.isVoiceOnce()) { final float scale = progress; @@ -13681,7 +13903,9 @@ private void drawBotButtons(Canvas canvas, ArrayList botButtons, int drawable.draw(canvas); } else if (button.button instanceof TLRPC.TL_keyboardButtonUrl) { Drawable drawable; - if (button.isInviteButton) { + if (AndroidUtilities.isWebAppLink(button.button.url)) { + drawable = getThemedDrawable(Theme.key_drawable_botWebView); + } else if (button.isInviteButton) { drawable = getThemedDrawable(Theme.key_drawable_botInvite); } else { drawable = getThemedDrawable(Theme.key_drawable_botLink); @@ -13993,7 +14217,8 @@ public void drawMessageText(float textX, float textY, Canvas canvas, ArrayList 0) { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(fromId); if (user != null) { - name = UserObject.getUserName(user); + name = AndroidUtilities.removeDiacritics(UserObject.getUserName(user)); } } else if (fromId < 0) { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-fromId); if (chat != null) { - name = chat.title; + name = AndroidUtilities.removeDiacritics(chat.title); } } else if (messageObject.replyMessageObject.messageOwner != null && messageObject.replyMessageObject.messageOwner.peer_id != null) { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(messageObject.replyMessageObject.messageOwner.peer_id.channel_id); if (chat != null) { - name = chat.title; + name = AndroidUtilities.removeDiacritics(chat.title); } } } @@ -16245,7 +16470,7 @@ protected void onClick() { mess = mess.substring(0, 150); } mess = mess.replace('\n', ' '); - stringFinalText = Emoji.replaceEmoji(mess, textPaint.getFontMetricsInt(), AndroidUtilities.dp(14), false); + stringFinalText = Emoji.replaceEmoji(mess, textPaint.getFontMetricsInt(), AndroidUtilities.dp(14), true); if (messageObject.replyMessageObject.messageOwner != null) { stringFinalText = MessageObject.replaceAnimatedEmoji(stringFinalText, messageObject.replyMessageObject.messageOwner.entities, textPaint.getFontMetricsInt(), true); } @@ -16255,12 +16480,12 @@ protected void onClick() { 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(); + CharSequence mess = messageObject.replyMessageObject.messageText; if (mess.length() > 150) { - mess = mess.substring(0, 150); + mess = mess.subSequence(0, 150); } - mess = mess.replace('\n', ' '); - stringFinalText = Emoji.replaceEmoji(mess, textPaint.getFontMetricsInt(), AndroidUtilities.dp(14), false); + mess = AndroidUtilities.replaceNewLines(mess); + stringFinalText = Emoji.replaceEmoji(mess, textPaint.getFontMetricsInt(), AndroidUtilities.dp(14), true); if (messageObject.replyMessageObject.messageOwner != null) { stringFinalText = MessageObject.replaceAnimatedEmoji(stringFinalText, messageObject.replyMessageObject.messageOwner.entities, textPaint.getFontMetricsInt(), true); } @@ -18724,6 +18949,7 @@ public void drawNamesLayout(Canvas canvas, float alpha) { oldAlpha = Theme.chat_replyNamePaint.getAlpha(); Theme.chat_replyNamePaint.setAlpha((int) (oldAlpha * timeAlpha * replyForwardAlpha)); Theme.chat_replyTextPaint.setColor(getThemedColor(Theme.key_chat_stickerReplyMessageText)); + Theme.chat_replyTextPaint.linkColor = getThemedColor(Theme.key_chat_stickerReplyMessageText); oldAlpha = Theme.chat_replyTextPaint.getAlpha(); Theme.chat_replyTextPaint.setAlpha((int) (oldAlpha * timeAlpha * replyForwardAlpha)); if (needDrawReplyBackground) { @@ -18752,6 +18978,7 @@ public void drawNamesLayout(Canvas canvas, float alpha) { if (currentMessageObject.isOutOwner()) { if (currentMessageObject.isReplyToStory()) { Theme.chat_replyTextPaint.setColor(Theme.chat_replyNamePaint.getColor()); + Theme.chat_replyTextPaint.linkColor = Theme.chat_replyTextPaint.getColor(); } else { float blendPressed = replyPressedT; int color = getThemedColor(Theme.key_chat_outReplyMessageText); @@ -18760,10 +18987,12 @@ public void drawNamesLayout(Canvas canvas, float alpha) { blendPressed = .6f + (blendPressed * .4f); } Theme.chat_replyTextPaint.setColor(ColorUtils.blendARGB(color, Theme.adaptHue(color, Theme.chat_replyNamePaint.getColor()), blendPressed)); + Theme.chat_replyTextPaint.linkColor = ColorUtils.blendARGB(color, Theme.adaptHue(color, Theme.chat_replyNamePaint.getColor()), Utilities.clamp(blendPressed * 2, 1f, 0f)); } } else { if (currentMessageObject.isReplyToStory()) { Theme.chat_replyTextPaint.setColor(Theme.chat_replyNamePaint.getColor()); + Theme.chat_replyTextPaint.linkColor = Theme.chat_replyTextPaint.getColor(); } else { float blendPressed = replyPressedT; int color = getThemedColor(Theme.key_chat_inReplyMessageText); @@ -18772,10 +19001,12 @@ public void drawNamesLayout(Canvas canvas, float alpha) { blendPressed = .6f + (blendPressed * .4f); } Theme.chat_replyTextPaint.setColor(ColorUtils.blendARGB(color, Theme.adaptHue(color, Theme.chat_replyNamePaint.getColor()), blendPressed)); + Theme.chat_replyTextPaint.linkColor = ColorUtils.blendARGB(color, Theme.adaptHue(color, Theme.chat_replyNamePaint.getColor()), Utilities.clamp(blendPressed * 2, 1f, 0f)); } } } Theme.chat_quoteTextPaint.setColor(Theme.chat_replyTextPaint.getColor()); + Theme.chat_quoteTextPaint.linkColor = Theme.chat_replyTextPaint.linkColor; int offset = AndroidUtilities.dp(10); forwardNameX = replyStartX - replyTextOffset + offset + (needReplyImage ? offset + AndroidUtilities.dp(25) : 0); if ((currentPosition == null || currentPosition.minY == 0 && currentPosition.minX == 0) && !(enterTransitionInProgress && !currentMessageObject.isVoice())) { @@ -19709,7 +19940,7 @@ public boolean shouldDrawTimeOnMedia() { if (overideShouldDrawTimeOnMedia != 0) { return overideShouldDrawTimeOnMedia == 1; } - return mediaBackground && (captionLayout == null || captionAbove) && (reactionsLayoutInBubble.isEmpty || reactionsLayoutInBubble.isSmall || currentMessageObject != null && (currentMessageObject.isAnyKindOfSticker() || currentMessageObject.isRoundVideo()))/* || isMedia && drawCommentButton && !isRepliesChat*/; + return mediaBackground && (captionLayout == null || captionAbove) && (reactionsLayoutInBubble.isEmpty || reactionsLayoutInBubble.isSmall || currentMessageObject != null && (currentMessageObject.isAnyKindOfSticker() || currentMessageObject.isRoundVideo())) || currentMessageObject != null && currentMessageObject.type == MessageObject.TYPE_PAID_MEDIA/* || isMedia && drawCommentButton && !isRepliesChat*/; } public void drawTime(Canvas canvas, float alpha, boolean fromParent) { @@ -19776,6 +20007,9 @@ public void drawTime(Canvas canvas, float alpha, boolean fromParent) { if (shouldDrawTimeOnMedia() && currentMessageObject.sendPreview) { x -= dp(1); } + if (currentMessageObject != null && (currentMessageObject.type == MessageObject.TYPE_ANIMATED_STICKER || currentMessageObject.isAnyKindOfSticker())) { + x -= dp(6); + } int sz = dp(14); int cx = x + sz / 2, cy = y + sz / 2; effectDrawable.setBounds((int) (cx - sz / 2 * s), (int) (cy - sz / 2 * s), (int) (cx + sz / 2 * s), (int) (cy + sz / 2 * s)); @@ -19900,13 +20134,15 @@ private void drawTimeInternal(Canvas canvas, float alpha, boolean fromParent, fl 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; + timeY = getPhotoBottom() + additionalTimeOffsetY; } float y1 = timeY - AndroidUtilities.dp(23); float timeHeight = Math.max(AndroidUtilities.dp(17), Theme.chat_timePaint.getTextSize() + AndroidUtilities.dp(5)); rect.set(x1 - offsetX, y1, offsetX + x1 + timeWidth + AndroidUtilities.dp((bigRadius ? 12 : 8) + (currentMessageObject.isOutOwner() ? 20 + (currentMessageObject.type == MessageObject.TYPE_EMOJIS ? 4 : 0) : 0)), y1 + timeHeight); - if (!currentMessageObject.isQuickReply()) { + if (groupMedia != null) { + groupMedia.drawBlurRect(canvas, rect, r, .5f * timeAlpha * alpha); + } else if (!currentMessageObject.isQuickReply()) { if (currentMessageObject.hasMediaSpoilers() && currentMessageObject.type != MessageObject.TYPE_ROUND_VIDEO) { rectPath.rewind(); rectPath.addRoundRect(rect, r, r, Path.Direction.CW); @@ -20158,9 +20394,11 @@ private void drawTimeInternal(Canvas canvas, float alpha, boolean fromParent, fl canvas.clipPath(unlockSpoilerPath, Region.Op.DIFFERENCE); int sColor = Color.WHITE; - unlockSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f))); - unlockSpoilerEffect.setBounds((int) photoImage.getImageX(), (int) photoImage.getImageY(), (int) photoImage.getImageX2(), (int) photoImage.getImageY2()); - unlockSpoilerEffect.draw(canvas); + if (mediaSpoilerEffect2 == null) { + unlockSpoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f))); + unlockSpoilerEffect.setBounds((int) photoImage.getImageX(), (int) photoImage.getImageY(), (int) photoImage.getImageX2(), (int) photoImage.getImageY2()); + unlockSpoilerEffect.draw(canvas); + } invalidate(); canvas.restore(); @@ -20261,7 +20499,7 @@ private void drawClockOrErrorLayout(Canvas canvas, boolean drawTime, boolean dra clockDrawable.setColor(clockColor); float timeY; if (shouldDrawTimeOnMedia()) { - timeY = photoImage.getImageY2() + additionalTimeOffsetY - AndroidUtilities.dp(9.0f); + timeY = getPhotoBottom() + additionalTimeOffsetY - AndroidUtilities.dp(9.0f); } else { timeY = layoutHeight - AndroidUtilities.dp(pinnedBottom || pinnedTop ? 9.5f : 8.5f) + timeYOffset; if (isRoundVideo) { @@ -20286,7 +20524,7 @@ private void drawClockOrErrorLayout(Canvas canvas, boolean drawTime, boolean dra float x = timeX + (currentMessageObject.scheduled ? 0 : AndroidUtilities.dp(11)); float y; if (shouldDrawTimeOnMedia()) { - y = photoImage.getImageY2() + additionalTimeOffsetY - AndroidUtilities.dp(21.5f); + y = getPhotoBottom() + additionalTimeOffsetY - AndroidUtilities.dp(21.5f); } else { y = layoutHeight - AndroidUtilities.dp(pinnedBottom || pinnedTop ? 21.5f : 20.5f) + timeYOffset; if (isRoundVideo) { @@ -20337,7 +20575,7 @@ public float getTimeY() { public float getTimeY(float timeYOffset) { if (shouldDrawTimeOnMedia() && documentAttachType != DOCUMENT_ATTACH_TYPE_ROUND) { - return photoImage.getImageY2() + additionalTimeOffsetY - AndroidUtilities.dp(7.3f) - timeLayout.getHeight(); + return getPhotoBottom() + additionalTimeOffsetY - AndroidUtilities.dp(7.3f) - timeLayout.getHeight(); } float timeY = layoutHeight - AndroidUtilities.dp(pinnedBottom || pinnedTop ? 7.5f : 6.5f) - timeLayout.getHeight() + timeYOffset; if (isRoundVideo) { @@ -20584,7 +20822,7 @@ private void drawStatusDrawable(Canvas canvas, boolean drawCheck1, boolean drawC 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; + timeY = getPhotoBottom() + additionalTimeOffsetY; } timeY -= AndroidUtilities.dp(8.5f); @@ -24036,6 +24274,8 @@ public boolean areTags() { } public String getFilename() { + if (currentMessageObject == null) + return null; if (currentMessageObject.type == MessageObject.TYPE_PHOTO) { if (currentPhotoObject == null) { return null; 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 a5103bddaa..7a42746075 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java @@ -113,6 +113,7 @@ import org.telegram.ui.Components.spoilers.SpoilerEffect; import org.telegram.ui.DialogsActivity; import org.telegram.ui.RightSlidingDialogContainer; +import org.telegram.ui.Stars.StarsIntroActivity; import org.telegram.ui.Stories.StoriesListPlaceProvider; import org.telegram.ui.Stories.StoriesUtilities; import org.telegram.ui.Stories.StoryViewer; @@ -936,7 +937,7 @@ private CharSequence formatArchivedDialogNames() { if (UserObject.isDeleted(currentUser)) { title = LocaleController.getString("HiddenName", R.string.HiddenName); } else { - title = ContactsController.formatName(currentUser.first_name, currentUser.last_name).replace('\n', ' '); + title = AndroidUtilities.removeDiacritics(ContactsController.formatName(currentUser.first_name, currentUser.last_name).replace('\n', ' ')); } } else { continue; @@ -1289,7 +1290,7 @@ public void buildLayout() { draftVoice = false; needEmoji = true; updateMessageThumbs(); - messageNameString = getMessageNameString(); + messageNameString = AndroidUtilities.removeDiacritics(getMessageNameString()); messageString = formatTopicsNames(); String restrictionReason = message != null ? MessagesController.getRestrictionReason(message.messageOwner.restriction_reason) : null; buttonString = message != null ? getMessageStringFormatted(messageFormatType, restrictionReason, messageNameString, true) : ""; @@ -1485,10 +1486,10 @@ public void buildLayout() { updateMessageThumbs(); String triedMessageName = null; if (!isSavedDialog && user != null && user.self && !message.isOutOwner()) { - triedMessageName = getMessageNameString(); + triedMessageName = AndroidUtilities.removeDiacritics(getMessageNameString()); } if (isSavedDialog && user != null && !user.self && message != null && message.isOutOwner() || triedMessageName != null || chat != null && chat.id > 0 && fromChat == null && (!ChatObject.isChannel(chat) || ChatObject.isMegagroup(chat)) && !ForumUtilities.isTopicCreateMessage(message)) { - messageNameString = triedMessageName != null ? triedMessageName : getMessageNameString(); + messageNameString = AndroidUtilities.removeDiacritics(triedMessageName != null ? triedMessageName : getMessageNameString()); if (chat != null && chat.forum && !isTopic && !useFromUserAsAvatar) { CharSequence topicName = MessagesController.getInstance(currentAccount).getTopicsController().getTopicIconName(chat, message, currentMessagePaint); if (!TextUtils.isEmpty(topicName)) { @@ -1599,6 +1600,16 @@ public void buildLayout() { } messageString = new SpannableStringBuilder(emoji).append(msgBuilder); } + } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) message.messageOwner.media; + final int count = paidMedia.extended_media.size(); + if (hasVideoThumb) { + messageString = count > 1 ? LocaleController.formatPluralString("Media", count) : LocaleController.getString(R.string.AttachVideo); + } else { + messageString = count > 1 ? LocaleController.formatPluralString("Photos", count) : LocaleController.getString(R.string.AttachPhoto); + } + messageString = StarsIntroActivity.replaceStars(LocaleController.formatString(R.string.AttachPaidMedia, messageString)); + currentMessagePaint = Theme.dialogs_messagePrintingPaint[paintIndex]; } else if (thumbsCount > 1) { if (hasVideoThumb) { messageString = LocaleController.formatPluralString("Media", groupMessages == null ? 0 : groupMessages.size()); @@ -1856,9 +1867,9 @@ public void buildLayout() { topicIconInName = new Drawable[1]; } topicIconInName[0] = null; - nameString = showTopicIconInName ? ForumUtilities.getTopicSpannedName(forumTopic, Theme.dialogs_namePaint[paintIndex], topicIconInName, false) : forumTopic.title; + nameString = showTopicIconInName ? ForumUtilities.getTopicSpannedName(forumTopic, Theme.dialogs_namePaint[paintIndex], topicIconInName, false) : AndroidUtilities.removeDiacritics(forumTopic.title); } else { - nameString = chat.title; + nameString = AndroidUtilities.removeDiacritics(chat.title); } } else if (user != null) { if (UserObject.isReplyUser(user)) { @@ -1877,7 +1888,7 @@ public void buildLayout() { nameString = LocaleController.getString("SavedMessages", R.string.SavedMessages); } } else { - nameString = UserObject.getUserName(user); + nameString = AndroidUtilities.removeDiacritics(UserObject.getUserName(user)); } } if (nameString != null && nameString.length() == 0) { @@ -4856,7 +4867,20 @@ public void updateMessageThumbs() { return; } String restrictionReason = MessagesController.getRestrictionReason(message.messageOwner.restriction_reason); - if (groupMessages != null && groupMessages.size() > 1 && TextUtils.isEmpty(restrictionReason) && currentDialogFolderId == 0 && encryptedChat == null) { + if (message != null && message.messageOwner != null && message.messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia) { + thumbsCount = 0; + hasVideoThumb = false; + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) message.messageOwner.media; + int index = 0; + for (int i = 0; i < paidMedia.extended_media.size() && thumbsCount < 3; ++i) { + TLRPC.MessageExtendedMedia emedia = paidMedia.extended_media.get(i); + if (emedia instanceof TLRPC.TL_messageExtendedMediaPreview) { + setThumb(index++, ((TLRPC.TL_messageExtendedMediaPreview) emedia).thumb); + } else if (emedia instanceof TLRPC.TL_messageExtendedMedia) { + setThumb(index++, ((TLRPC.TL_messageExtendedMedia) emedia).media); + } + } + } else if (groupMessages != null && groupMessages.size() > 1 && TextUtils.isEmpty(restrictionReason) && currentDialogFolderId == 0 && encryptedChat == null) { thumbsCount = 0; hasVideoThumb = false; Collections.sort(groupMessages, Comparator.comparingInt(MessageObject::getId)); @@ -4924,6 +4948,60 @@ private void setThumb(int index, MessageObject message) { } } + private void setThumb(int index, TLRPC.MessageMedia media) { + TLObject object = null; + ArrayList photoThumbs = null; + boolean isVideo = false; + if (media instanceof TLRPC.TL_messageMediaPhoto) { + object = media.photo; + photoThumbs = media.photo.sizes; + } else if (media instanceof TLRPC.TL_messageMediaDocument) { + isVideo = MessageObject.isVideoDocument(media.document); + object = media.document; + photoThumbs = media.document.thumbs; + } + + TLRPC.PhotoSize smallThumb = FileLoader.getClosestPhotoSizeWithSize(photoThumbs, 40); + TLRPC.PhotoSize bigThumb = FileLoader.getClosestPhotoSizeWithSize(photoThumbs, AndroidUtilities.getPhotoSize(), false, null, true); + if (smallThumb == bigThumb) { + bigThumb = null; + } + TLRPC.PhotoSize selectedThumb = bigThumb; + if (selectedThumb == null || DownloadController.getInstance(currentAccount).canDownloadMedia(message.messageOwner, media) == 0) { + selectedThumb = smallThumb; + } + + if (smallThumb != null) { + hasVideoThumb = hasVideoThumb || isVideo; + if (thumbsCount < 3) { + thumbsCount++; + drawPlay[index] = isVideo; + drawSpoiler[index] = false; + int size = !isVideo && selectedThumb != null ? selectedThumb.size : 0; + String filter = "20_20"; + thumbImage[index].setImage(ImageLocation.getForObject(selectedThumb, object), filter, ImageLocation.getForObject(smallThumb, object), filter, size, null, message, 0); + thumbImage[index].setRoundRadius(dp(2)); + needEmoji = false; + } + } + } + + private void setThumb(int index, TLRPC.PhotoSize thumb) { + if (thumb != null) { + hasVideoThumb = false; + if (thumbsCount < 3) { + thumbsCount++; + drawPlay[index] = false; + drawSpoiler[index] = true; + int size = 0; + String filter = "2_2_b"; + thumbImage[index].setImage(ImageLocation.getForObject(thumb, message.messageOwner), filter, null, null, size, null, message, 0); + thumbImage[index].setRoundRadius(dp(2)); + needEmoji = false; + } + } + } + public String getMessageNameString() { if (message == null) { return null; @@ -4964,29 +5042,29 @@ public String getMessageNameString() { if (currentDialogId == selfId) { if (fromUser != null) { - return UserObject.getFirstName(fromUser).replace("\n", ""); + return AndroidUtilities.removeDiacritics(UserObject.getFirstName(fromUser).replace("\n", "")); } else if (fromChat != null) { - return fromChat.title.replace("\n", ""); + return AndroidUtilities.removeDiacritics(fromChat.title.replace("\n", "")); } return null; } else if (message.isOutOwner()) { return LocaleController.getString("FromYou", R.string.FromYou); } 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", ""); + return AndroidUtilities.removeDiacritics(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; + return AndroidUtilities.removeDiacritics(message.messageOwner.fwd_from.from_name); } else if (fromUser != null) { if (useForceThreeLines || SharedConfig.useThreeLinesLayout) { if (UserObject.isDeleted(fromUser)) { return LocaleController.getString("HiddenName", R.string.HiddenName); } else { - return ContactsController.formatName(fromUser.first_name, fromUser.last_name).replace("\n", ""); + return AndroidUtilities.removeDiacritics(ContactsController.formatName(fromUser.first_name, fromUser.last_name).replace("\n", "")); } } else { - return UserObject.getFirstName(fromUser).replace("\n", ""); + return AndroidUtilities.removeDiacritics(UserObject.getFirstName(fromUser).replace("\n", "")); } } else if (fromChat != null && fromChat.title != null) { - return fromChat.title.replace("\n", ""); + return AndroidUtilities.removeDiacritics(fromChat.title.replace("\n", "")); } else { return "DELETED"; } @@ -5108,6 +5186,16 @@ public SpannableStringBuilder getMessageStringFormatted(int messageFormatType, S } else { innerMessage = String.format("\uD83C\uDFA7 %s - %s", message.getMusicAuthor(), message.getMusicTitle()); } + } else if (message.messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia) { + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) message.messageOwner.media; + final int count = paidMedia.extended_media.size(); + if (hasVideoThumb) { + innerMessage = count > 1 ? LocaleController.formatPluralString("Media", count) : LocaleController.getString(R.string.AttachVideo); + } else { + innerMessage = count > 1 ? LocaleController.formatPluralString("Photos", count) : LocaleController.getString(R.string.AttachPhoto); + } + innerMessage = StarsIntroActivity.replaceStars(LocaleController.formatString(R.string.AttachPaidMedia, innerMessage)); + colorKey = Theme.key_chats_actionMessage; } else if (thumbsCount > 1) { if (hasVideoThumb) { innerMessage = LocaleController.formatPluralString("Media", groupMessages == null ? 0 : groupMessages.size()); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsHintCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsHintCell.java index d94007e1da..e8fd6b9a34 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsHintCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogsHintCell.java @@ -167,9 +167,9 @@ public boolean onTouchEvent(MotionEvent event) { } @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - canvas.drawLine(0, getHeight() - 1, getWidth(), getHeight() - 1, Theme.dividerPaint); + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + canvas.drawRect(0, getHeight() - 1, getWidth(), getHeight(), Theme.dividerPaint); } private int height; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/EditTextCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/EditTextCell.java index b7e84bf920..d6acbcc23a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/EditTextCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/EditTextCell.java @@ -92,14 +92,15 @@ public void setShowLimitOnFocus(boolean show) { } public EditTextCell(Context context, String hint, boolean multiline) { - this(context, hint, multiline, -1); + this(context, hint, multiline, -1, null); } public EditTextCell( Context context, String hint, boolean multiline, - int maxLength + int maxLength, + Theme.ResourcesProvider resourceProvider ) { super(context); this.maxLength = maxLength; @@ -122,7 +123,7 @@ protected void onTextChanged(CharSequence text, int start, int lengthBefore, int @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); - limit.setTextColor(limitColor.set(Theme.getColor(limitCount <= 0 ? Theme.key_text_RedRegular : Theme.key_dialogSearchHint, getResourcesProvider()))); + limit.setTextColor(limitColor.set(Theme.getColor(limitCount <= 0 ? Theme.key_text_RedRegular : Theme.key_dialogSearchHint, resourceProvider))); limit.setBounds(getScrollX(), 0, getScrollX() + getWidth() - getPaddingRight() + dp(42), getHeight()); limit.draw(canvas); } @@ -137,8 +138,8 @@ protected void onDraw(Canvas canvas) { }; limit.setCallback(editText); editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 17); - editText.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); - editText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + editText.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText, resourceProvider)); + editText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourceProvider)); editText.setBackground(null); if (multiline) { editText.setMaxLines(5); @@ -152,7 +153,7 @@ protected void onDraw(Canvas canvas) { editText.setInputType(InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_CLASS_TEXT | (multiline ? InputType.TYPE_TEXT_FLAG_MULTI_LINE : 0) | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); editText.setRawInputType(InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); editText.setHint(hint); - editText.setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); + editText.setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourceProvider)); editText.setCursorSize(dp(19)); editText.setCursorWidth(1.5f); editText.addTextChangedListener(new TextWatcher() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupMedia.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupMedia.java new file mode 100644 index 0000000000..7d5ab04e13 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/GroupMedia.java @@ -0,0 +1,1493 @@ +package org.telegram.ui.Cells; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.MessageObject.POSITION_FLAG_BOTTOM; +import static org.telegram.messenger.MessageObject.POSITION_FLAG_LEFT; +import static org.telegram.messenger.MessageObject.POSITION_FLAG_RIGHT; +import static org.telegram.messenger.MessageObject.POSITION_FLAG_TOP; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.LinearGradient; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.Log; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.collection.LongSparseArray; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.DownloadController; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessageObject.GroupedMessagePosition; +import org.telegram.messenger.SendMessagesHelper; +import org.telegram.messenger.SharedConfig; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChatActivity; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.BlurBehindDrawable; +import org.telegram.ui.Components.ButtonBounce; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LoadingDrawable; +import org.telegram.ui.Components.MediaActionDrawable; +import org.telegram.ui.Components.Point; +import org.telegram.ui.Components.RadialProgress2; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.Text; +import org.telegram.ui.Components.spoilers.SpoilerEffect2; +import org.telegram.ui.Stars.StarsIntroActivity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +public class GroupMedia { + + @NonNull + public final ChatMessageCell cell; + + private GroupedMessages layout; + public final ArrayList holders = new ArrayList<>(); + + public int x, y; + public int maxWidth; + public int width; + public int height; + + public boolean hidden; + private final AnimatedFloat animatedHidden; + private LoadingDrawable loadingDrawable; + + SpoilerEffect2 spoilerEffect; + + public GroupMedia(@NonNull ChatMessageCell cell) { + this.cell = cell; + this.spoilerEffect = SpoilerEffect2.getInstance(cell); + this.animatedHidden = new AnimatedFloat(cell, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + this.bounce = new ButtonBounce(cell); + } + + private int overrideWidth; + public void setOverrideWidth(int overrideWidth) { + this.overrideWidth = overrideWidth; + } + + public void setMessageObject(MessageObject messageObject, boolean pinnedBottom, boolean pinnedTop) { + if (messageObject == null) return; + if (messageObject.messageOwner == null) return; + if (!(messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia)) return; + + final TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) messageObject.messageOwner.media; + + if (layout == null) { + layout = new GroupedMessages(); + } + layout.medias.clear(); + layout.medias.addAll(paidMedia.extended_media); + layout.calculate(); + + if (overrideWidth > 0) { + maxWidth = overrideWidth; + } else { + if (AndroidUtilities.isTablet()) { + maxWidth = AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(122); + } else { + maxWidth = Math.min(cell.getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(64 + (cell.checkNeedDrawShareButton(messageObject) ? 10 : 0)); + } + if (cell.needDrawAvatar()) { + maxWidth -= dp(52); + } + } + + for (int i = 0; i < paidMedia.extended_media.size(); ++i) { + final TLRPC.MessageExtendedMedia media = paidMedia.extended_media.get(i); + MediaHolder holder = i >= holders.size() ? null : holders.get(i); + if (holder == null) { + final GroupedMessagePosition pos = layout.getPosition(media); + final int w = (int) (pos.pw / 1000f * maxWidth); + final int h = (int) (pos.ph * layout.maxSizeHeight); + holder = new MediaHolder(cell, messageObject, media, paidMedia.extended_media.size() != 1, w, h); + if (media.attachPath != null) { + holder.attachPath = media.attachPath; + } else if (paidMedia.extended_media.size() == 1) { + holder.attachPath = messageObject.messageOwner != null ? messageObject.messageOwner.attachPath : null; + } + if (!TextUtils.isEmpty(holder.attachPath)) { + DownloadController.getInstance(cell.currentAccount).addLoadingFileObserver(holder.attachPath, messageObject, holder); + if (messageObject.isSending()) { + holder.radialProgress.setProgress(media.uploadProgress, false); + } + } + if (cell.isCellAttachedToWindow()) { + holder.attach(); + } + holders.add(holder); + } else { + holder.updateMedia(media, messageObject); + } + } + + for (int i = paidMedia.extended_media.size(); i < holders.size(); ++i) { + MediaHolder holder = i >= holders.size() ? null : holders.get(i); + if (holder != null) { + holder.detach(); + holders.remove(i); + i--; + } + } + +// for (int i = 0; i < holders.size(); ++i) { +// final MediaHolder holder = holders.get(i); +// if (!paidMedia.extended_media.contains(holder.media)) { +// holder.detach(); +// holders.remove(i); +// i--; +// } +// } +// for (int i = 0; i < paidMedia.extended_media.size(); ++i) { +// final TLRPC.MessageExtendedMedia media = paidMedia.extended_media.get(i); +// final GroupedMessagePosition pos = layout.getPosition(media); +// boolean found = false; +// for (int j = 0; j < holders.size(); ++j) { +// if (holders.get(j).media == media) { +// found = true; +// break; +// } +// } +// if (!found) { +// final int w = (int) (pos.pw / 1000f * maxWidth); +// final int h = (int) (pos.ph * layout.maxSizeHeight); +// final MediaHolder holder = new MediaHolder(cell, messageObject, media, paidMedia.extended_media.size() != 1, w, h); +// if (media.attachPath != null) { +// holder.attachPath = media.attachPath; +// } else if (paidMedia.extended_media.size() == 1) { +// holder.attachPath = messageObject.messageOwner != null ? messageObject.messageOwner.attachPath : null; +// } +// if (!TextUtils.isEmpty(holder.attachPath)) { +// DownloadController.getInstance(cell.currentAccount).addLoadingFileObserver(holder.attachPath, messageObject, holder); +// if (messageObject.isSending()) { +// holder.radialProgress.setProgress(media.uploadProgress, false); +// } +// } +// if (cell.isCellAttachedToWindow()) { +// holder.attach(); +// } +// holders.add(holder); +// } +// } + + updateHolders(messageObject); + + width = (int) (layout.width / 1000f * maxWidth); + height = (int) (layout.height * layout.maxSizeHeight); + + if (hidden) { + buttonText = new Text(StarsIntroActivity.replaceStarsWithPlain(LocaleController.formatPluralStringComma("UnlockPaidContent", (int) (buttonTextPrice = paidMedia.stars_amount)), .7f), 14, AndroidUtilities.bold()); + if (buttonText.getCurrentWidth() > width - dp(30)) { + buttonText = new Text(StarsIntroActivity.replaceStarsWithPlain(LocaleController.formatPluralStringComma("UnlockPaidContentShort", (int) (buttonTextPrice = paidMedia.stars_amount)), .7f), 14, AndroidUtilities.bold()); + } + } + if (priceText == null || priceTextPrice != paidMedia.stars_amount) { + priceText = new Text(StarsIntroActivity.replaceStars(LocaleController.formatPluralStringComma("PaidMediaPrice", (int) (priceTextPrice = paidMedia.stars_amount)), .9f), 12, AndroidUtilities.bold()); + } + } + + public void updateHolders(MessageObject messageObject) { + final boolean top = cell.namesOffset > 0 || cell.captionAbove && !TextUtils.isEmpty(messageObject.caption); + final boolean bottom = !cell.captionAbove && !TextUtils.isEmpty(messageObject.caption) || !cell.reactionsLayoutInBubble.isEmpty || cell.hasCommentLayout(); + + float f = 1f; + if (overrideWidth > 0) { + f = 1000f / layout.width; + maxWidth = overrideWidth; + } else { + if (AndroidUtilities.isTablet()) { + maxWidth = AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(122); + } else { + maxWidth = Math.min(cell.getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(64 + (cell.checkNeedDrawShareButton(messageObject) ? 10 : 0)); + } + if (cell.needDrawAvatar()) { + maxWidth -= dp(52); + } + } + + width = (int) (layout.width / 1000f * f * maxWidth); + height = (int) (layout.height * layout.maxSizeHeight); + + hidden = false; + final int p = dp(1); + final int minRad = dp(4); + final int rad = dp(SharedConfig.bubbleRadius - (SharedConfig.bubbleRadius > 2 ? 2 : 0)); + final int nearRad = Math.min(dp(3), rad); + for (int i = 0; i < holders.size(); ++i) { + final MediaHolder holder = holders.get(i); + final GroupedMessagePosition pos = layout.getPosition(holder.media); + + if (pos == null) continue; + + int l = (int) (pos.left / 1000f * f * maxWidth); + int t = (int) (pos.top * layout.maxSizeHeight); + int w = (int) (pos.pw / 1000f * f * maxWidth); + int h = (int) (pos.ph * layout.maxSizeHeight); + + if ((pos.flags & POSITION_FLAG_LEFT) == 0) { l += p; w -= p; } + if ((pos.flags & POSITION_FLAG_TOP) == 0) { t += p; h -= p; } + if ((pos.flags & POSITION_FLAG_RIGHT) == 0) { w -= p; } + if ((pos.flags & POSITION_FLAG_BOTTOM) == 0) { h -= p; } + + holder.l = l; + holder.t = t; + holder.r = l + w; + holder.b = t + h; + holder.imageReceiver.setImageCoords(l, t, w, h); + + int tl, tr, bl, br; + + tl = (pos.flags & POSITION_FLAG_TOP) != 0 && (pos.flags & POSITION_FLAG_LEFT) != 0 && !top ? rad : minRad; + tr = (pos.flags & POSITION_FLAG_TOP) != 0 && (pos.flags & POSITION_FLAG_RIGHT) != 0 && !top ? rad : minRad; + bl = (pos.flags & POSITION_FLAG_BOTTOM) != 0 && (pos.flags & POSITION_FLAG_LEFT) != 0 && !bottom ? rad : minRad; + br = (pos.flags & POSITION_FLAG_BOTTOM) != 0 && (pos.flags & POSITION_FLAG_RIGHT) != 0 && !bottom ? rad : minRad; + + if (!bottom) { + if (messageObject.isOutOwner()) { + br = minRad; + } else { + bl = minRad; + } + } + if (!top && cell.pinnedTop) { + if (messageObject.isOutOwner()) { + tr = nearRad; + } else { + tl = nearRad; + } + } + + holder.imageReceiver.setRoundRadius(tl, tr, br, bl); + + holder.radii[0] = holder.radii[1] = tl; + holder.radii[2] = holder.radii[3] = tr; + holder.radii[4] = holder.radii[5] = br; + holder.radii[6] = holder.radii[7] = bl; + + if (messageObject != null) { + if (messageObject.isSending()) { + holder.setIcon(MediaActionDrawable.ICON_CANCEL); + } + } + + hidden = hidden || holder.hidden; + } + + if (hidden) { + final TLRPC.TL_messageMediaPaidMedia paidMedia = messageObject == null ? null : (TLRPC.TL_messageMediaPaidMedia) messageObject.messageOwner.media; + if (paidMedia != null) { + buttonText = new Text(StarsIntroActivity.replaceStarsWithPlain(LocaleController.formatPluralStringComma("UnlockPaidContent", (int) (buttonTextPrice = paidMedia.stars_amount)), .7f), 14, AndroidUtilities.bold()); + if (buttonText.getCurrentWidth() > width - dp(30)) { + buttonText = new Text(StarsIntroActivity.replaceStarsWithPlain(LocaleController.formatPluralStringComma("UnlockPaidContentShort", (int) (buttonTextPrice = paidMedia.stars_amount)), .7f), 14, AndroidUtilities.bold()); + } + } + } + } + + private final ButtonBounce bounce; + private MediaHolder pressHolder; + private boolean pressButton; + public boolean onTouchEvent(MotionEvent e) { + final float ex = e.getX(); + final float ey = e.getY(); + if (e.getAction() == MotionEvent.ACTION_DOWN) { + pressHolder = getHolderAt(ex, ey); + pressButton = pressHolder != null && pressHolder.radialProgress.getIcon() != MediaActionDrawable.ICON_NONE && pressHolder.radialProgress.getProgressRect().contains(ex, ey); + } else if (e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_CANCEL) { + MediaHolder holder = getHolderAt(ex, ey); + boolean pressedButton = holder != null && holder.radialProgress.getIcon() != MediaActionDrawable.ICON_NONE && holder.radialProgress.getProgressRect().contains(ex, ey); + if (pressHolder != null && pressHolder == holder && cell.getDelegate() != null && e.getAction() == MotionEvent.ACTION_UP) { + MessageObject messageObject = cell.getMessageObject(); + if (pressButton && pressedButton && holder.radialProgress.getIcon() == MediaActionDrawable.ICON_CANCEL && messageObject != null) { + if (messageObject.isSending()) { + SendMessagesHelper.getInstance(messageObject.currentAccount).cancelSendingMessage(messageObject); + } else { + + } + } else { + cell.getDelegate().didPressGroupImage(cell, pressHolder.imageReceiver, pressHolder.media, e.getX(), e.getY()); + } + } + pressButton = false; + pressHolder = null; + } + bounce.setPressed(pressHolder != null); + return pressHolder != null; + } + + public MediaHolder getHolderAt(float x, float y) { + for (int i = 0; i < holders.size(); ++i) { + if (holders.get(i).imageReceiver.isInsideImage(x, y)) { + return holders.get(i); + } + } + return null; + } + + public ImageReceiver getPhotoImage(int index) { + if (layout == null) return null; + if (index < 0 || index >= layout.medias.size()) return null; + TLRPC.MessageExtendedMedia media = layout.medias.get(index); + for (int i = 0; i < holders.size(); ++i) { + if (holders.get(i).media == media) + return holders.get(i).imageReceiver; + } + return null; + } + + public ImageReceiver getPhotoImage(TLRPC.FileLocation location) { + if (layout == null) return null; + for (int i = 0; i < holders.size(); ++i) { + final MediaHolder holder = holders.get(i); + final TLRPC.MessageExtendedMedia extendedMedia = holder.media; + if (extendedMedia instanceof TLRPC.TL_messageExtendedMedia) { + TLRPC.MessageMedia media = ((TLRPC.TL_messageExtendedMedia) extendedMedia).media; + if (media.photo != null) { + for (int j = 0; j < media.photo.sizes.size(); ++j) { + if (media.photo.sizes.get(j).location == location) + return holder.imageReceiver; + } + for (int j = 0; j < media.photo.video_sizes.size(); ++j) { + if (media.photo.video_sizes.get(j).location == location) + return holder.imageReceiver; + } + } + if (media.document != null) { + for (int j = 0; j < media.document.thumbs.size(); ++j) { + if (media.document.thumbs.get(j).location == location) + return holder.imageReceiver; + } + for (int j = 0; j < media.document.video_thumbs.size(); ++j) { + if (media.document.video_thumbs.get(j).location == location) + return holder.imageReceiver; + } + } + } + } + return null; + } + + public boolean allVisible() { + for (MediaHolder holder : holders) { + if (!holder.imageReceiver.getVisible()) { + return false; + } + } + return true; + } + + private Text buttonText; + private long buttonTextPrice; + private Text priceText; + private long priceTextPrice; + private Path clipPath = new Path(); + private Path clipPath2 = new Path(); + private RectF clipRect = new RectF(); + + public void draw(Canvas canvas) { + if (layout == null) return; + float hiddenAlpha = animatedHidden.set(hidden); + +// Theme.MessageDrawable backgroundDrawable = cell.currentBackgroundDrawable; +// if (backgroundDrawable != null && hiddenAlpha > 0) { +// canvas.save(); +// canvas.clipRect(x - dp(20), y - dp(2), x + width + dp(20), y + height + dp(3)); +// canvas.clipPath(backgroundDrawable.makePath()); +// drawBlurred(canvas, hiddenAlpha); +// canvas.drawColor(Theme.multAlpha(Theme.isCurrentThemeDark() ? 0x80000000 : 0x90FFFFFF, hiddenAlpha)); +// canvas.restore(); +// } + + drawImages(canvas, true); + if (buttonText != null) { + if (hiddenAlpha > 0) { + final float s = bounce.getScale(.05f); + final float buttonWidth = dp(14 + 14) + buttonText.getCurrentWidth(); + final float buttonHeight = dp(32); + clipRect.set( + x + (width - buttonWidth) / 2f, + y + (height - buttonHeight) / 2f, + x + (width + buttonWidth) / 2f, + y + (height + buttonHeight) / 2f + ); + clipPath.rewind(); + clipPath.addRoundRect(clipRect, buttonHeight / 2f, buttonHeight / 2f, Path.Direction.CW); + canvas.save(); + canvas.scale(s, s, x + width / 2f, y + height / 2f); + + canvas.save(); + canvas.clipPath(clipPath); + drawBlurred(canvas, hiddenAlpha); + canvas.drawColor(Theme.multAlpha(0x30000000, hiddenAlpha)); + buttonText.draw(canvas, x + width / 2f - buttonWidth / 2f + dp(14), y + height / 2f, 0xFFFFFFFF, hiddenAlpha); + canvas.restore(); + + final boolean loading = isLoading(); + if (loading) { + if (loadingDrawable == null) { + loadingDrawable = new LoadingDrawable(); + loadingDrawable.setCallback(cell); + loadingDrawable.setColors( + Theme.multAlpha(Color.WHITE, .1f), + Theme.multAlpha(Color.WHITE, .3f), + Theme.multAlpha(Color.WHITE, .35f), + Theme.multAlpha(Color.WHITE, .8f) + ); + loadingDrawable.setAppearByGradient(true); + loadingDrawable.strokePaint.setStrokeWidth(AndroidUtilities.dpf2(1.25f)); + } else if (loadingDrawable.isDisappeared() || loadingDrawable.isDisappearing()) { + loadingDrawable.reset(); + loadingDrawable.resetDisappear(); + } + } else if (loadingDrawable != null && !loadingDrawable.isDisappearing() && !loadingDrawable.isDisappeared()) { + loadingDrawable.disappear(); + } + + if (loadingDrawable != null) { + loadingDrawable.setBounds(clipRect); + loadingDrawable.setRadiiDp(buttonHeight / 2f); + loadingDrawable.setAlpha((int) (hiddenAlpha * 0xFF)); + loadingDrawable.draw(canvas); + } + + canvas.restore(); + } + } + if (priceText != null && hiddenAlpha < 1 && allVisible()) { + float alpha = (1f - hiddenAlpha) * cell.getTimeAlpha(); + final float w = dp(2 * 5.66f) + priceText.getCurrentWidth(), h = dp(17); + final float p = dp(5); + clipRect.set(x + width - w - p, y + p, x + width - p, y + p + h); + clipPath.rewind(); + clipPath.addRoundRect(clipRect, h / 2f, h / 2f, Path.Direction.CW); + canvas.save(); + canvas.clipPath(clipPath); +// drawBlurred(canvas, alpha); + canvas.drawColor(Theme.multAlpha(0x40000000, alpha)); + priceText.draw(canvas, x + width - w - p + dp(5.66f), y + p + h / 2f, 0xFFFFFFFF, alpha); + canvas.restore(); + } + } + + public boolean isLoading() { + return cell.getDelegate() != null && cell.getDelegate().isProgressLoading(cell, ChatActivity.PROGRESS_PAID_MEDIA); + } + + public void drawBlurRect(Canvas canvas, RectF rect, float r, float alpha) { + canvas.save(); + clipPath.rewind(); + clipPath.addRoundRect(rect, r, r, Path.Direction.CW); + canvas.clipPath(clipPath); +// drawBlurred(canvas, alpha); + canvas.drawColor(0x40000000); + canvas.restore(); + } + + private Bitmap blurBitmap; + private Paint blurBitmapPaint; + private int blurBitmapState; + private int blurBitmapMessageId; + private int blurBitmapWidth, blurBitmapHeight; + + public void checkBlurBitmap() { + final int maxSize = 100; + final int msg_id = cell.getMessageObject() != null ? cell.getMessageObject().getId() : 0; + final int width = (int) Math.max(1, this.width > this.height ? maxSize : (float) this.width / this.height * maxSize); + final int height = (int) Math.max(1, this.height > this.width ? maxSize : (float) this.height / this.width * maxSize); + int state = 0; + for (int i = 0; i < holders.size(); ++i) { + final MediaHolder h = holders.get(i); + if (h.imageReceiver.hasImageSet() && h.imageReceiver.getBitmap() != null) state |= 1 << i; + } + if (blurBitmap == null || blurBitmapMessageId != msg_id || blurBitmapState != state || blurBitmapWidth != width || blurBitmapHeight != height) { + blurBitmapState = state; + blurBitmapMessageId = msg_id; + blurBitmapWidth = width; + blurBitmapHeight = height; + + if (blurBitmap != null) { + blurBitmap.recycle(); + } + blurBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(blurBitmap); + canvas.scale((float) width / this.width, (float) width / this.width); + for (int i = 0; i < holders.size(); ++i) { + final MediaHolder h = holders.get(i); + h.imageReceiver.setImageCoords(h.l, h.t, h.r - h.l, h.b - h.t); + h.imageReceiver.draw(canvas); + } + Utilities.stackBlurBitmap(blurBitmap, 12); + + if (blurBitmapPaint == null) { + blurBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + ColorMatrix colorMatrix = new ColorMatrix(); + colorMatrix.setSaturation(1.5f); + blurBitmapPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); + } + } + } + + public void drawBlurred(Canvas canvas, float alpha) { + if (layout == null) return; + checkBlurBitmap(); + if (blurBitmap != null) { + canvas.save(); + canvas.translate(x, y); + canvas.scale((float) this.width / blurBitmap.getWidth(), (float) this.width / blurBitmap.getWidth()); + blurBitmapPaint.setAlpha((int) (0xFF * alpha)); + canvas.drawBitmap(blurBitmap, 0, 0, blurBitmapPaint); + canvas.restore(); + } + } + + public void drawImages(Canvas canvas, boolean withSpoilers) { + float hiddenAlpha = animatedHidden.set(hidden); + final MessageObject messageObject = cell.getMessageObject(); + float l = Float.MAX_VALUE, t = Float.MAX_VALUE, b = Float.MIN_VALUE, r = Float.MIN_VALUE; + clipPath2.rewind(); + for (int i = 0; i < holders.size(); ++i) { + final MediaHolder h = holders.get(i); + + h.imageReceiver.setImageCoords(x + h.l, y + h.t, h.r - h.l, h.b - h.t); + h.imageReceiver.draw(canvas); + if (h.imageReceiver.getAnimation() != null) { + int sec = Math.round(h.imageReceiver.getAnimation().currentTime / 1000f); + h.setTime(sec); + } + if (hiddenAlpha > 0) { + l = Math.min(x + h.l, l); + t = Math.min(y + h.t, t); + r = Math.max(x + h.r, r); + b = Math.max(y + h.b, b); + AndroidUtilities.rectTmp.set(x + h.l, y + h.t, x + h.r, y + h.b); + clipPath2.addRoundRect(AndroidUtilities.rectTmp, h.radii, Path.Direction.CW); + } + + h.radialProgress.setColorKeys(Theme.key_chat_mediaLoaderPhoto, Theme.key_chat_mediaLoaderPhotoSelected, Theme.key_chat_mediaLoaderPhotoIcon, Theme.key_chat_mediaLoaderPhotoIconSelected); + h.radialProgress.setProgressRect( + h.imageReceiver.getImageX() + (h.imageReceiver.getImageWidth() / 2f - h.radialProgress.getRadius()), + h.imageReceiver.getImageY() + (h.imageReceiver.getImageHeight() / 2f - h.radialProgress.getRadius()), + h.imageReceiver.getImageX() + (h.imageReceiver.getImageWidth() / 2f + h.radialProgress.getRadius()), + h.imageReceiver.getImageY() + (h.imageReceiver.getImageHeight() / 2f + h.radialProgress.getRadius()) + ); + if (messageObject.isSending()) { + SendMessagesHelper sendMessagesHelper = SendMessagesHelper.getInstance(messageObject.currentAccount); + long[] progress = ImageLoader.getInstance().getFileProgressSizes(h.attachPath); + float loadingProgress = 0; + boolean sending = sendMessagesHelper.isSendingMessage(messageObject.getId()); + if (progress == null && sending) { + loadingProgress = 1.0f; + } else if (progress != null) { + loadingProgress = DownloadController.getProgress(progress); + } + h.radialProgress.setProgress(loadingProgress, false); + h.setIcon(sending && loadingProgress < 1.0f ? MediaActionDrawable.ICON_CANCEL : (h.album ? MediaActionDrawable.ICON_CHECK : h.getDefaultIcon())); + } else if (FileLoader.getInstance(messageObject.currentAccount).isLoadingFile(h.filename)) { + h.setIcon(MediaActionDrawable.ICON_CANCEL); + } else { + h.setIcon(h.getDefaultIcon()); + } + canvas.saveLayerAlpha(h.radialProgress.getProgressRect(), (int) (0xFF * (1f - hiddenAlpha)), Canvas.ALL_SAVE_FLAG); + h.radialProgress.draw(canvas); + canvas.restore(); + } + if (hiddenAlpha > 0 && withSpoilers) { + canvas.save(); + canvas.clipPath(clipPath2); + canvas.translate(l, t); + canvas.saveLayerAlpha(0, 0, (int) (r - l), (int) (b - t), (int) (0xFF * hiddenAlpha), Canvas.ALL_SAVE_FLAG); + spoilerEffect.draw(canvas, cell, (int) (r - l), (int) (b - t), 1f); + canvas.restore(); + canvas.restore(); + cell.invalidate(); + } + for (int i = 0; i < holders.size(); ++i) { + final MediaHolder h = holders.get(i); + + if (h.durationText != null) { + float alpha = 1f;//cell.getTimeAlpha(); + final float bw = dp(2 * 5.7f) + h.durationText.getCurrentWidth(), bh = dp(17); + final float p = dp(5); + clipRect.set(x + h.l + p, y + h.t + p, x + h.l + p + bw, y + h.t + p + bh); + if (priceText != null && clipRect.right > x + width - (dp(2 * 5.66f) + priceText.getCurrentWidth()) - p && clipRect.top <= y + p) { + continue; + } + clipPath.rewind(); + clipPath.addRoundRect(clipRect, bh / 2f, bh / 2f, Path.Direction.CW); + canvas.save(); + canvas.clipPath(clipPath); + drawBlurred(canvas, hiddenAlpha); + canvas.drawColor(Theme.multAlpha(0x40000000, alpha)); + h.durationText.draw(canvas, x + h.l + p + dp(5.66f), y + h.t + p + bh / 2f, 0xFFFFFFFF, alpha); + canvas.restore(); + } + } + } + + public static class MediaHolder implements DownloadController.FileDownloadProgressListener { + + public int l, t, r, b; + + public final ChatMessageCell cell; + public final ImageReceiver imageReceiver; + public boolean hidden; + private final int w, h; + + public final float[] radii = new float[8]; + public final RectF clipRect = new RectF(); + public final Path clipPath = new Path(); + + public String filename; + + public boolean album, video, autoplay; + public TLRPC.MessageExtendedMedia media; + public String attachPath; + + public final RadialProgress2 radialProgress; + + public int icon = MediaActionDrawable.ICON_NONE; + + private final int TAG; + + private int duration = 0; + private int durationValue = 0; + private Text durationText; + + public void setIcon(int icon) { + if (icon != this.icon) { + radialProgress.setIcon(this.icon = icon, true, true); + } + } + + public void setTime(int time) { + if (video) return; + final int newDurationValue = Math.max(0, duration - time); + if (durationValue != newDurationValue) { + durationText = new Text(AndroidUtilities.formatLongDuration(durationValue = newDurationValue), 12); + } + } + + public MediaHolder( + ChatMessageCell cell, + MessageObject msg, + TLRPC.MessageExtendedMedia media, + boolean album, + int w, int h + ) { + this.cell = cell; + this.album = album; + this.video = false; + if (media instanceof TLRPC.TL_messageExtendedMedia) { + TLRPC.MessageMedia messageMedia = ((TLRPC.TL_messageExtendedMedia) media).media; + video = messageMedia instanceof TLRPC.TL_messageMediaDocument && MessageObject.isVideoDocument(messageMedia.document); + duration = (int) Math.max(1, Math.round(MessageObject.getDocumentDuration(messageMedia.document))); + } else if (media instanceof TLRPC.TL_messageExtendedMediaPreview) { + TLRPC.TL_messageExtendedMediaPreview p = (TLRPC.TL_messageExtendedMediaPreview) media; + video = (p.flags & 4) != 0; + duration = p.video_duration; + } + if (video) { + durationText = new Text(AndroidUtilities.formatLongDuration(durationValue = duration), 12); + } + this.imageReceiver = new ImageReceiver(cell); + this.imageReceiver.setColorFilter(null); + this.w = w; + this.h = h; + + this.TAG = DownloadController.getInstance(cell.currentAccount).generateObserverTag(); + +// this.spoilerEffect = SpoilerEffect2.getInstance(cell); + + updateMedia(media, msg); + + this.radialProgress = new RadialProgress2(cell, cell.getResourcesProvider()); + this.radialProgress.setIcon(icon = getDefaultIcon(), false, false); + } + + public void updateMedia(TLRPC.MessageExtendedMedia media, MessageObject msg) { + if (this.media == media) + return; + this.media = media; + + autoplay = false; + final String filter = w + "_" + h; + if (media instanceof TLRPC.TL_messageExtendedMediaPreview) { + hidden = true; + filename = null; + + TLRPC.TL_messageExtendedMediaPreview mediaPreview = (TLRPC.TL_messageExtendedMediaPreview) media; + imageReceiver.setImage(ImageLocation.getForObject(mediaPreview.thumb, msg.messageOwner), filter + "_b2", null, null, msg, 0); + + ColorMatrix colorMatrix = new ColorMatrix(); + colorMatrix.setSaturation(1.4f); + AndroidUtilities.adjustBrightnessColorMatrix(colorMatrix, -.1f); + this.imageReceiver.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); + } else if (media instanceof TLRPC.TL_messageExtendedMedia) { + hidden = false; + + this.imageReceiver.setColorFilter(null); + + TLRPC.TL_messageExtendedMedia extMedia = (TLRPC.TL_messageExtendedMedia) media; + TLRPC.MessageMedia messageMedia = extMedia.media; + filename = MessageObject.getFileName(messageMedia); + if (messageMedia instanceof TLRPC.TL_messageMediaPhoto) { + TLRPC.TL_messageMediaPhoto mediaPhoto = (TLRPC.TL_messageMediaPhoto) messageMedia; + TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(mediaPhoto.photo.sizes, AndroidUtilities.getPhotoSize(), true, null, true); + TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(mediaPhoto.photo.sizes, Math.min(w, h) / 100, false, photoSize, false); + ImageLocation photoLocation = ImageLocation.getForPhoto(photoSize, mediaPhoto.photo); + ImageLocation thumbLocation = ImageLocation.getForPhoto(thumbSize, mediaPhoto.photo); +// if (msg.isSending()) { +// sendingPhotoLocation = photoLocation; +// sendingPhotoLocationDialogId = msg.getDialogId(); +// sendingPhotoLocationMessageId = msg.getId(); +// } +// if (sendingPhotoLocation != null && sendingPhotoLocationDialogId == msg.getDialogId() && sendingPhotoLocationMessageId == msg.getId()) { +// thumbLocation = sendingPhotoLocation; +// } + imageReceiver.setImage( + photoLocation, filter, + thumbLocation, filter, + 0, null, + msg, 0 + ); + } else if (messageMedia instanceof TLRPC.TL_messageMediaDocument) { + TLRPC.TL_messageMediaDocument mediaDocument = (TLRPC.TL_messageMediaDocument) messageMedia; + autoplay = !album && video && SharedConfig.isAutoplayVideo(); +// if (!TextUtils.isEmpty(extMedia.attachPath)) { +// imageReceiver.setImage(ImageLocation.getForPath(extMedia.attachPath), filter + (autoplay ? "_g" : ""), null, null, msg, 0); +// return; +// } + if (!album && video) { + if (mediaDocument.document != null) { + TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(mediaDocument.document.thumbs, AndroidUtilities.getPhotoSize(), true, null, true); + TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(mediaDocument.document.thumbs, Math.min(w, h), false, photoSize, false); + ImageLocation mediaLocation = ImageLocation.getForDocument(mediaDocument.document); + ImageLocation photoLocation = ImageLocation.getForDocument(photoSize, mediaDocument.document); + ImageLocation thumbLocation = ImageLocation.getForDocument(thumbSize, mediaDocument.document); +// if (msg.isSending()) { +// sendingPhotoLocation = photoLocation; +// sendingPhotoLocationDialogId = msg.getDialogId(); +// sendingPhotoLocationMessageId = msg.getId(); +// } +// if (sendingPhotoLocation != null && sendingPhotoLocationDialogId == msg.getDialogId() && sendingPhotoLocationMessageId == msg.getId()) { +// thumbLocation = sendingPhotoLocation; +// } + imageReceiver.setImage( + mediaLocation, filter + (autoplay ? "_g" : ""), + photoLocation, filter, + thumbLocation, filter, null, + 0, null, + msg, 0 + ); + return; + } + } + if (mediaDocument.document != null) { + TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(mediaDocument.document.thumbs, AndroidUtilities.getPhotoSize(), true, null, true); + TLRPC.PhotoSize thumbSize = FileLoader.getClosestPhotoSizeWithSize(mediaDocument.document.thumbs, Math.min(w, h), false, photoSize, false); + ImageLocation photoLocation = ImageLocation.getForDocument(photoSize, mediaDocument.document); + ImageLocation thumbLocation = ImageLocation.getForDocument(thumbSize, mediaDocument.document); +// if (msg.isSending()) { +// sendingPhotoLocation = photoLocation; +// sendingPhotoLocationDialogId = msg.getDialogId(); +// sendingPhotoLocationMessageId = msg.getId(); +// } +// if (sendingPhotoLocation != null && sendingPhotoLocationDialogId == msg.getDialogId() && sendingPhotoLocationMessageId == msg.getId()) { +// thumbLocation = sendingPhotoLocation; +// } + imageReceiver.setImage( + photoLocation, filter, + thumbLocation, filter, + 0, null, + msg, 0 + ); + } + } + } + } + + public boolean attached; + + public void attach() { + if (attached) return; + attached = true; + imageReceiver.onAttachedToWindow(); +// if (spoilerEffect.destroyed) { +// spoilerEffect = SpoilerEffect2.getInstance(cell); +// } else { +// spoilerEffect.attach(cell); +// } + } + + public void detach() { + if (!attached) return; + attached = false; + imageReceiver.onDetachedFromWindow(); +// this.spoilerEffect.detach(cell); + } + + @Override + public void onFailedDownload(String fileName, boolean canceled) { + + } + + @Override + public void onSuccessDownload(String fileName) { + + } + + @Override + public void onProgressDownload(String fileName, long downloadSize, long totalSize) { + float progress = totalSize == 0 ? 0 : Math.min(1f, downloadSize / (float) totalSize); + radialProgress.setProgress(media.downloadProgress = progress, true); + setIcon(progress < 1f ? MediaActionDrawable.ICON_CANCEL : getDefaultIcon()); + cell.invalidate(); + } + + @Override + public void onProgressUpload(String fileName, long uploadedSize, long totalSize, boolean isEncrypted) { + float progress = totalSize == 0 ? 0 : Math.min(1f, uploadedSize / (float) totalSize); + radialProgress.setProgress(media.uploadProgress = progress, true); + setIcon(progress < 1f ? MediaActionDrawable.ICON_CANCEL : (album ? MediaActionDrawable.ICON_CHECK : getDefaultIcon())); + cell.invalidate(); + } + + private int getDefaultIcon() { + return video && !autoplay ? MediaActionDrawable.ICON_PLAY : MediaActionDrawable.ICON_NONE; + } + + @Override + public int getObserverTag() { + return TAG; + } + } + + public boolean attached; + + public void onAttachedToWindow() { + if (attached) return; + attached = true; + if (spoilerEffect != null) { + spoilerEffect.detach(cell); + } + for (int i = 0; i < holders.size(); ++i) { + holders.get(i).attach(); + } + } + + public void onDetachedFromWindow() { + if (!attached) return; + attached = false; + if (spoilerEffect != null) { + spoilerEffect.attach(cell); + } + for (int i = 0; i < holders.size(); ++i) { + holders.get(i).detach(); + } + } + + public static class GroupedMessages { + + public long groupId; + public boolean hasSibling; + public ArrayList medias = new ArrayList<>(); + public ArrayList posArray = new ArrayList<>(); + public HashMap positions = new HashMap<>(); + + int width, maxX, maxY; + float height; + + public GroupedMessagePosition getPosition(TLRPC.MessageExtendedMedia media) { + if (media == null) { + return null; + } + return positions.get(media); + } + + public int maxSizeWidth = 800; + public float maxSizeHeight = 814; + + public final GroupedMessages.TransitionParams transitionParams = new GroupedMessages.TransitionParams(); + + private static class MessageGroupedLayoutAttempt { + + public int[] lineCounts; + public float[] heights; + + public MessageGroupedLayoutAttempt(int i1, int i2, float f1, float f2) { + lineCounts = new int[]{i1, i2}; + heights = new float[]{f1, f2}; + } + + public MessageGroupedLayoutAttempt(int i1, int i2, int i3, float f1, float f2, float f3) { + lineCounts = new int[]{i1, i2, i3}; + heights = new float[]{f1, f2, f3}; + } + + public MessageGroupedLayoutAttempt(int i1, int i2, int i3, int i4, float f1, float f2, float f3, float f4) { + lineCounts = new int[]{i1, i2, i3, i4}; + heights = new float[]{f1, f2, f3, f4}; + } + } + + private float multiHeight(float[] array, int start, int end) { + float sum = 0; + for (int a = start; a < end; a++) { + sum += array[a]; + } + return maxSizeWidth / sum; + } + + public void calculate() { + posArray.clear(); + positions.clear(); + + maxX = 0; + final int count = medias.size(); + if (count == 0) { + width = 0; + height = 0; + maxY = 0; + return; + } + maxSizeWidth = 800; + int firstSpanAdditionalSize = 200; + + StringBuilder proportions = new StringBuilder(); + float averageAspectRatio = 1.0f; + boolean isOut = false; + boolean forceCalc = false; + boolean needShare = false; + hasSibling = false; + + for (int a = 0; a < count;) { + final TLRPC.MessageExtendedMedia media = medias.get(a); + + final GroupedMessagePosition position = new GroupedMessagePosition(); + position.last = a == count - 1; + if (media instanceof TLRPC.TL_messageExtendedMediaPreview) { + TLRPC.TL_messageExtendedMediaPreview m = (TLRPC.TL_messageExtendedMediaPreview) media; + position.photoWidth = m.w; + position.photoHeight = m.h; + } else if (media instanceof TLRPC.TL_messageExtendedMedia) { + TLRPC.PhotoSize photoSize; + TLRPC.TL_messageExtendedMedia m = (TLRPC.TL_messageExtendedMedia) media; + if (m.media instanceof TLRPC.TL_messageMediaPhoto) { + TLRPC.TL_messageMediaPhoto photo = (TLRPC.TL_messageMediaPhoto) m.media; + photoSize = photo.photo == null ? null : FileLoader.getClosestPhotoSizeWithSize(photo.photo.sizes, AndroidUtilities.getPhotoSize()); + } else if (m.media instanceof TLRPC.TL_messageMediaDocument) { + TLRPC.TL_messageMediaDocument doc = (TLRPC.TL_messageMediaDocument) m.media; + photoSize = doc.document == null ? null : FileLoader.getClosestPhotoSizeWithSize(doc.document.thumbs, AndroidUtilities.getPhotoSize()); + } else { + photoSize = null; + } + position.photoWidth = photoSize == null ? 100 : photoSize.w; + position.photoHeight = photoSize == null ? 100 : photoSize.h; + } else { + position.photoWidth = 100; + position.photoHeight = 100; + } + position.aspectRatio = position.photoWidth / (float) position.photoHeight; + + if (position.aspectRatio > 1.2f) { + proportions.append("w"); + } else if (position.aspectRatio < 0.8f) { + proportions.append("n"); + } else { + proportions.append("q"); + } + + averageAspectRatio += position.aspectRatio; + + if (position.aspectRatio > 2.0f) { + forceCalc = true; + } + + positions.put(media, position); + posArray.add(position); + + a++; + } + + if (needShare) { + maxSizeWidth -= 50; + firstSpanAdditionalSize += 50; + } + + int minHeight = dp(120); + int minWidth = (int) (dp(120) / (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) / (float) maxSizeWidth)); + int paddingsWidth = (int) (dp(40) / (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) / (float) maxSizeWidth)); + + float maxAspectRatio = maxSizeWidth / maxSizeHeight; + averageAspectRatio = averageAspectRatio / count; + + float minH = dp(100) / maxSizeHeight; + + if (count == 1) { + GroupedMessagePosition position1 = posArray.get(0); + float w, h; + if (position1.aspectRatio >= 1) { + w = maxSizeWidth; + h = w / position1.aspectRatio / maxSizeWidth * maxSizeHeight; + } else { + h = maxSizeHeight; + w = h * position1.aspectRatio / maxSizeHeight * maxSizeWidth; + } + position1.set(0, 0, 0, 0, (int) w, h / maxSizeHeight, POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT | POSITION_FLAG_TOP | POSITION_FLAG_BOTTOM); + } else if (!forceCalc && (count == 2 || count == 3 || count == 4)) { + if (count == 2) { + GroupedMessagePosition position1 = posArray.get(0); + GroupedMessagePosition position2 = posArray.get(1); + String pString = proportions.toString(); + if (pString.equals("ww") && averageAspectRatio > 1.4 * maxAspectRatio && position1.aspectRatio - position2.aspectRatio < 0.2) { + float height = Math.round(Math.min(maxSizeWidth / position1.aspectRatio, Math.min(maxSizeWidth / position2.aspectRatio, maxSizeHeight / 2.0f))) / maxSizeHeight; + position1.set(0, 0, 0, 0, maxSizeWidth, height, POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT | POSITION_FLAG_TOP); + position2.set(0, 0, 1, 1, maxSizeWidth, height, POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); + } else if (pString.equals("ww") || pString.equals("qq")) { + int width = maxSizeWidth / 2; + float height = Math.round(Math.min(width / position1.aspectRatio, Math.min(width / position2.aspectRatio, maxSizeHeight))) / maxSizeHeight; + position1.set(0, 0, 0, 0, width, height, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM | POSITION_FLAG_TOP); + position2.set(1, 1, 0, 0, width, height, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM | POSITION_FLAG_TOP); + maxX = 1; + } else { + int secondWidth = (int) Math.max(0.4f * maxSizeWidth, Math.round((maxSizeWidth / position1.aspectRatio / (1.0f / position1.aspectRatio + 1.0f / position2.aspectRatio)))); + int firstWidth = maxSizeWidth - secondWidth; + if (firstWidth < minWidth) { + int diff = minWidth - firstWidth; + firstWidth = minWidth; + secondWidth -= diff; + } + + float height = Math.min(maxSizeHeight, Math.round(Math.min(firstWidth / position1.aspectRatio, secondWidth / position2.aspectRatio))) / maxSizeHeight; + position1.set(0, 0, 0, 0, firstWidth, height, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM | POSITION_FLAG_TOP); + position2.set(1, 1, 0, 0, secondWidth, height, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM | POSITION_FLAG_TOP); + maxX = 1; + } + } else if (count == 3) { + GroupedMessagePosition position1 = posArray.get(0); + GroupedMessagePosition position2 = posArray.get(1); + GroupedMessagePosition position3 = posArray.get(2); + if (proportions.charAt(0) == 'n') { + float thirdHeight = Math.min(maxSizeHeight * 0.5f, Math.round(position2.aspectRatio * maxSizeWidth / (position3.aspectRatio + position2.aspectRatio))); + float secondHeight = maxSizeHeight - thirdHeight; + int rightWidth = (int) Math.max(minWidth, Math.min(maxSizeWidth * 0.5f, Math.round(Math.min(thirdHeight * position3.aspectRatio, secondHeight * position2.aspectRatio)))); + + int leftWidth = Math.round(Math.min(maxSizeHeight * position1.aspectRatio + paddingsWidth, maxSizeWidth - rightWidth)); + position1.set(0, 0, 0, 1, leftWidth, 1.0f, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM | POSITION_FLAG_TOP); + + position2.set(1, 1, 0, 0, rightWidth, secondHeight / maxSizeHeight, POSITION_FLAG_RIGHT | POSITION_FLAG_TOP); + + position3.set(1, 1, 1, 1, rightWidth, thirdHeight / maxSizeHeight, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); + position3.spanSize = maxSizeWidth; + + position1.siblingHeights = new float[]{thirdHeight / maxSizeHeight, secondHeight / maxSizeHeight}; + + if (isOut) { + position1.spanSize = maxSizeWidth - rightWidth; + } else { + position2.spanSize = maxSizeWidth - leftWidth; + position3.leftSpanOffset = leftWidth; + } + hasSibling = true; + maxX = 1; + } else { + float firstHeight = Math.round(Math.min(maxSizeWidth / position1.aspectRatio, (maxSizeHeight) * 0.66f)) / maxSizeHeight; + position1.set(0, 1, 0, 0, maxSizeWidth, firstHeight, POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT | POSITION_FLAG_TOP); + + int width = maxSizeWidth / 2; + float secondHeight = Math.min(maxSizeHeight - firstHeight, Math.round(Math.min(width / position2.aspectRatio, width / position3.aspectRatio))) / maxSizeHeight; + if (secondHeight < minH) { + secondHeight = minH; + } + position2.set(0, 0, 1, 1, width, secondHeight, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM); + position3.set(1, 1, 1, 1, width, secondHeight, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); + maxX = 1; + } + } else { + GroupedMessagePosition position1 = posArray.get(0); + GroupedMessagePosition position2 = posArray.get(1); + GroupedMessagePosition position3 = posArray.get(2); + GroupedMessagePosition position4 = posArray.get(3); + if (proportions.charAt(0) == 'w') { + float h0 = Math.round(Math.min(maxSizeWidth / position1.aspectRatio, maxSizeHeight * 0.66f)) / maxSizeHeight; + position1.set(0, 2, 0, 0, maxSizeWidth, h0, POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT | POSITION_FLAG_TOP); + + float h = Math.round(maxSizeWidth / (position2.aspectRatio + position3.aspectRatio + position4.aspectRatio)); + int w0 = (int) Math.max(minWidth, Math.min(maxSizeWidth * 0.4f, h * position2.aspectRatio)); + int w2 = (int) Math.max(Math.max(minWidth, maxSizeWidth * 0.33f), h * position4.aspectRatio); + int w1 = maxSizeWidth - w0 - w2; + if (w1 < dp(58)) { + int diff = dp(58) - w1; + w1 = dp(58); + w0 -= diff / 2; + w2 -= (diff - diff / 2); + } + h = Math.min(maxSizeHeight - h0, h); + h /= maxSizeHeight; + if (h < minH) { + h = minH; + } + position2.set(0, 0, 1, 1, w0, h, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM); + position3.set(1, 1, 1, 1, w1, h, POSITION_FLAG_BOTTOM); + position4.set(2, 2, 1, 1, w2, h, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); + maxX = 2; + } else { + int w = Math.max(minWidth, Math.round(maxSizeHeight / (1.0f / position2.aspectRatio + 1.0f / position3.aspectRatio + 1.0f / position4.aspectRatio))); + float h0 = Math.min(0.33f, Math.max(minHeight, w / position2.aspectRatio) / maxSizeHeight); + float h1 = Math.min(0.33f, Math.max(minHeight, w / position3.aspectRatio) / maxSizeHeight); + float h2 = 1.0f - h0 - h1; + int w0 = Math.round(Math.min(maxSizeHeight * position1.aspectRatio + paddingsWidth, maxSizeWidth - w)); + + position1.set(0, 0, 0, 2, w0, h0 + h1 + h2, POSITION_FLAG_LEFT | POSITION_FLAG_TOP | POSITION_FLAG_BOTTOM); + + position2.set(1, 1, 0, 0, w, h0, POSITION_FLAG_RIGHT | POSITION_FLAG_TOP); + + position3.set(1, 1, 1, 1, w, h1, POSITION_FLAG_RIGHT); + position3.spanSize = maxSizeWidth; + + position4.set(1, 1, 2, 2, w, h2, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); + position4.spanSize = maxSizeWidth; + + if (isOut) { + position1.spanSize = maxSizeWidth - w; + } else { + position2.spanSize = maxSizeWidth - w0; + position3.leftSpanOffset = w0; + position4.leftSpanOffset = w0; + } + position1.siblingHeights = new float[]{h0, h1, h2}; + hasSibling = true; + maxX = 1; + } + } + } else { + float[] croppedRatios = new float[posArray.size()]; + for (int a = 0; a < count; a++) { + if (averageAspectRatio > 1.1f) { + croppedRatios[a] = Math.max(1.0f, posArray.get(a).aspectRatio); + } else { + croppedRatios[a] = Math.min(1.0f, posArray.get(a).aspectRatio); + } + croppedRatios[a] = Math.max(0.66667f, Math.min(1.7f, croppedRatios[a])); + } + + int firstLine; + int secondLine; + int thirdLine; + int fourthLine; + ArrayList attempts = new ArrayList<>(); + for (firstLine = 1; firstLine < croppedRatios.length; firstLine++) { + secondLine = croppedRatios.length - firstLine; + if (firstLine > 3 || secondLine > 3) { + continue; + } + attempts.add(new GroupedMessages.MessageGroupedLayoutAttempt(firstLine, secondLine, multiHeight(croppedRatios, 0, firstLine), multiHeight(croppedRatios, firstLine, croppedRatios.length))); + } + + for (firstLine = 1; firstLine < croppedRatios.length - 1; firstLine++) { + for (secondLine = 1; secondLine < croppedRatios.length - firstLine; secondLine++) { + thirdLine = croppedRatios.length - firstLine - secondLine; + if (firstLine > 3 || secondLine > (averageAspectRatio < 0.85f ? 4 : 3) || thirdLine > 3) { + continue; + } + attempts.add(new GroupedMessages.MessageGroupedLayoutAttempt(firstLine, secondLine, thirdLine, multiHeight(croppedRatios, 0, firstLine), multiHeight(croppedRatios, firstLine, firstLine + secondLine), multiHeight(croppedRatios, firstLine + secondLine, croppedRatios.length))); + } + } + + for (firstLine = 1; firstLine < croppedRatios.length - 2; firstLine++) { + for (secondLine = 1; secondLine < croppedRatios.length - firstLine; secondLine++) { + for (thirdLine = 1; thirdLine < croppedRatios.length - firstLine - secondLine; thirdLine++) { + fourthLine = croppedRatios.length - firstLine - secondLine - thirdLine; + if (firstLine > 3 || secondLine > 3 || thirdLine > 3 || fourthLine > 3) { + continue; + } + attempts.add(new GroupedMessages.MessageGroupedLayoutAttempt(firstLine, secondLine, thirdLine, fourthLine, multiHeight(croppedRatios, 0, firstLine), multiHeight(croppedRatios, firstLine, firstLine + secondLine), multiHeight(croppedRatios, firstLine + secondLine, firstLine + secondLine + thirdLine), multiHeight(croppedRatios, firstLine + secondLine + thirdLine, croppedRatios.length))); + } + } + } + + GroupedMessages.MessageGroupedLayoutAttempt optimal = null; + float optimalDiff = 0.0f; + float maxHeight = maxSizeWidth / 3 * 4; + for (int a = 0; a < attempts.size(); a++) { + GroupedMessages.MessageGroupedLayoutAttempt attempt = attempts.get(a); + float height = 0; + float minLineHeight = Float.MAX_VALUE; + for (int b = 0; b < attempt.heights.length; b++) { + height += attempt.heights[b]; + if (attempt.heights[b] < minLineHeight) { + minLineHeight = attempt.heights[b]; + } + } + + float diff = Math.abs(height - maxHeight); + if (attempt.lineCounts.length > 1) { + if (attempt.lineCounts[0] > attempt.lineCounts[1] || (attempt.lineCounts.length > 2 && attempt.lineCounts[1] > attempt.lineCounts[2]) || (attempt.lineCounts.length > 3 && attempt.lineCounts[2] > attempt.lineCounts[3])) { + diff *= 1.2f; + } + } + + if (minLineHeight < minWidth) { + diff *= 1.5f; + } + + if (optimal == null || diff < optimalDiff) { + optimal = attempt; + optimalDiff = diff; + } + } + if (optimal == null) { + return; + } + + int index = 0; + float y = 0.0f; + + for (int i = 0; i < optimal.lineCounts.length; i++) { + int c = optimal.lineCounts[i]; + float lineHeight = optimal.heights[i]; + int spanLeft = maxSizeWidth; + GroupedMessagePosition posToFix = null; + maxX = Math.max(maxX, c - 1); + for (int k = 0; k < c; k++) { + float ratio = croppedRatios[index]; + int width = (int) (ratio * lineHeight); + spanLeft -= width; + GroupedMessagePosition pos = posArray.get(index); + int flags = 0; + if (i == 0) { + flags |= POSITION_FLAG_TOP; + } + if (i == optimal.lineCounts.length - 1) { + flags |= POSITION_FLAG_BOTTOM; + } + if (k == 0) { + flags |= POSITION_FLAG_LEFT; + if (isOut) { + posToFix = pos; + } + } + if (k == c - 1) { + flags |= POSITION_FLAG_RIGHT; + if (!isOut) { + posToFix = pos; + } + } + pos.set(k, k, i, i, width, Math.max(minH, lineHeight / maxSizeHeight), flags); + index++; + } + posToFix.pw += spanLeft; + posToFix.spanSize += spanLeft; + y += lineHeight; + } + } + int avatarOffset = 108; + for (int a = 0; a < count; a++) { + GroupedMessagePosition pos = posArray.get(a); + if (isOut) { + if (pos.minX == 0) { + pos.spanSize += firstSpanAdditionalSize; + } + if ((pos.flags & POSITION_FLAG_RIGHT) != 0) { + pos.edge = true; + } + } else { + if (pos.maxX == maxX || (pos.flags & POSITION_FLAG_RIGHT) != 0) { + pos.spanSize += firstSpanAdditionalSize; + } + if ((pos.flags & POSITION_FLAG_LEFT) != 0) { + pos.edge = true; + } + } + TLRPC.MessageExtendedMedia media = medias.get(a); + if (!isOut && true /* media.needDrawAvatarInternal()*/) { + if (pos.edge) { + if (pos.spanSize != 1000) { + pos.spanSize += avatarOffset; + } + pos.pw += avatarOffset; + } else if ((pos.flags & POSITION_FLAG_RIGHT) != 0) { + if (pos.spanSize != 1000) { + pos.spanSize -= avatarOffset; + } else if (pos.leftSpanOffset != 0) { + pos.leftSpanOffset += avatarOffset; + } + } + } + } + for (int a = 0; a < count; a++) { + MessageObject.GroupedMessagePosition pos = posArray.get(a); + if (pos.minX == 0) { + pos.spanSize += firstSpanAdditionalSize; + } + if ((pos.flags & POSITION_FLAG_RIGHT) != 0) { + pos.edge = true; + } + maxX = Math.max(maxX, pos.maxX); + maxY = Math.max(maxY, pos.maxY); + pos.left = getLeft(pos, pos.minY, pos.maxY, pos.minX); + } + for (int a = 0; a < count; ++a) { + MessageObject.GroupedMessagePosition pos = posArray.get(a); + pos.top = getTop(pos, pos.minY); + } + + width = getWidth(); + height = getHeight(); + } + + public int getWidth() { + int[] lineWidths = new int[10]; + Arrays.fill(lineWidths, 0); + final int count = posArray.size(); + for (int i = 0; i < count; ++i) { + MessageObject.GroupedMessagePosition pos = posArray.get(i); + int width = pos.pw; + for (int y = pos.minY; y <= pos.maxY; ++y) { + lineWidths[y] += width; + } + } + int width = lineWidths[0]; + for (int y = 1; y < lineWidths.length; ++y) { + if (width < lineWidths[y]) { + width = lineWidths[y]; + } + } + return width; + } + + public float getHeight() { + float[] lineHeights = new float[10]; + Arrays.fill(lineHeights, 0f); + final int count = posArray.size(); + for (int i = 0; i < count; ++i) { + MessageObject.GroupedMessagePosition pos = posArray.get(i); + float height = pos.ph; + for (int x = pos.minX; x <= pos.maxX; ++x) { + lineHeights[x] += height; + } + } + float height = lineHeights[0]; + for (int y = 1; y < lineHeights.length; ++y) { + if (height < lineHeights[y]) { + height = lineHeights[y]; + } + } + return height; + } + + private float getLeft(MessageObject.GroupedMessagePosition except, int minY, int maxY, int minX) { + float[] sums = new float[maxY - minY + 1]; + Arrays.fill(sums, 0f); + final int count = posArray.size(); + for (int i = 0; i < count; ++i) { + MessageObject.GroupedMessagePosition pos = posArray.get(i); + if (pos != except && pos.maxX < minX) { + final int end = Math.min(pos.maxY, maxY) - minY; + for (int y = Math.max(pos.minY - minY, 0); y <= end; ++y) { + sums[y] += pos.pw; + } + } + } + float max = 0; + for (int i = 0; i < sums.length; ++i) { + if (max < sums[i]) { + max = sums[i]; + } + } + return max; + } + + private float getTop(MessageObject.GroupedMessagePosition except, int minY) { + float[] sums = new float[maxX + 1]; + Arrays.fill(sums, 0f); + final int count = posArray.size(); + for (int i = 0; i < count; ++i) { + MessageObject.GroupedMessagePosition pos = posArray.get(i); + if (pos != except && pos.maxY < minY) { + for (int x = pos.minX; x <= pos.maxX; ++x) { + sums[x] += pos.ph; + } + } + } + float max = 0; + for (int i = 0; i < sums.length; ++i) { + if (max < sums[i]) { + max = sums[i]; + } + } + return max; + } + + public TLRPC.MessageExtendedMedia findMediaWithFlags(int flags) { + if (!medias.isEmpty() && positions.isEmpty()) { + calculate(); + } + for (int i = 0; i < medias.size(); i++) { + TLRPC.MessageExtendedMedia media = medias.get(i); + GroupedMessagePosition position = positions.get(media); + if (position != null && (position.flags & (flags)) == flags) { + return media; + } + } + return null; + } + + public static class TransitionParams { + public int left; + public int top; + public int right; + public int bottom; + + public float offsetLeft; + public float offsetTop; + public float offsetRight; + public float offsetBottom; + + public boolean drawBackgroundForDeletedItems; + public boolean backgroundChangeBounds; + + public boolean pinnedTop; + public boolean pinnedBotton; + + public ChatMessageCell cell; + public float captionEnterProgress = 1f; + public boolean drawCaptionLayout; + public boolean isNewGroup; + + public void reset() { + captionEnterProgress = 1f; + offsetBottom = 0; + offsetTop = 0; + offsetRight = 0; + offsetLeft = 0; + backgroundChangeBounds = false; + } + } + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java index b4f7a6d638..d53d26447d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/LocationCell.java @@ -57,7 +57,8 @@ public LocationCell(Context context, boolean wrap, Theme.ResourcesProvider resou addView(imageView, LayoutHelper.createFrame(42, 42, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 15, 11, LocaleController.isRTL ? 15 : 0, 0)); nameTextView = new AnimatedTextView(context, true, true, true); - nameTextView.setAnimationProperties(0.4f, 0, 240, CubicBezierInterpolator.EASE_OUT_QUINT); + nameTextView.setAnimationProperties(0.4f, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + nameTextView.setScaleProperty(.6f); nameTextView.setTextSize(AndroidUtilities.dp(16)); nameTextView.setEllipsizeByGradient(true); nameTextView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); @@ -68,7 +69,8 @@ public LocationCell(Context context, boolean wrap, Theme.ResourcesProvider resou addView(nameTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 22, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), (LocaleController.isRTL ? 16 : 73), 10, (LocaleController.isRTL ? 73 : 16), 0)); addressTextView = new AnimatedTextView(context, true, true, true); - addressTextView.setAnimationProperties(0.4f, 0, 240, CubicBezierInterpolator.EASE_OUT_QUINT); + addressTextView.setScaleProperty(.6f); + addressTextView.setAnimationProperties(0.4f, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); addressTextView.setTextSize(AndroidUtilities.dp(14)); addressTextView.setEllipsizeByGradient(true); addressTextView.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteGrayText3)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java index 9a97d25a37..b85e84fa8e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/PhotoAttachPhotoCell.java @@ -26,6 +26,9 @@ import android.os.Build; import android.os.Bundle; import android.os.SystemClock; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; @@ -54,11 +57,13 @@ import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.CheckBox2; +import org.telegram.ui.Components.ColoredImageSpan; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.spoilers.SpoilerEffect; import org.telegram.ui.Components.spoilers.SpoilerEffect2; import org.telegram.ui.PhotoViewer; +import org.telegram.ui.Stars.StarsIntroActivity; public class PhotoAttachPhotoCell extends FrameLayout { @@ -90,6 +95,9 @@ public class PhotoAttachPhotoCell extends FrameLayout { private SpoilerEffect2 spoilerEffect2; private boolean hasSpoiler; + private long stars; + private boolean starsSelectedMultiple; + private Path path = new Path(); private float spoilerRevealX; private float spoilerRevealY; @@ -122,9 +130,12 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { path.addCircle(spoilerRevealX, spoilerRevealY, spoilerMaxRadius * spoilerRevealProgress, Path.Direction.CW); canvas.clipPath(path, Region.Op.DIFFERENCE); } - float alphaProgress = CubicBezierInterpolator.DEFAULT.getInterpolation(1f - imageViewCrossfadeProgress); - float alpha = hasSpoiler ? alphaProgress : 1f - alphaProgress; +// float alphaProgress = CubicBezierInterpolator.DEFAULT.getInterpolation(1f - imageViewCrossfadeProgress); +// float alpha = hasSpoiler ? alphaProgress : 1f - alphaProgress; spoilerEffect2.draw(canvas, container, imageView.getMeasuredWidth(), imageView.getMeasuredHeight()); + if (photoEntry != null && photoEntry.starsAmount > 0) { + imageView.drawBlurredText(canvas, 1f); + } if (spoilerRevealProgress != 0f) { canvas.restore(); } @@ -300,6 +311,40 @@ public void setHasSpoiler(boolean hasSpoiler, Float crossfadeDuration) { } } + private SpannableString star, lock; + public void setStarsPrice(long stars, boolean multiple) { + if (multiple != starsSelectedMultiple || stars != this.stars) { + this.stars = stars; + this.starsSelectedMultiple = multiple; + + SpannableStringBuilder s = null; + if (stars > 0) { + s = new SpannableStringBuilder(); + if (star == null) { + star = new SpannableString("⭐"); + ColoredImageSpan span = new ColoredImageSpan(R.drawable.star_small_inner); + span.setScale(.7f, .7f); + star.setSpan(span, 0, star.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + s.append(star); + s.append(" "); + if (multiple) { + if (lock == null) { + lock = new SpannableString("l"); + ColoredImageSpan span = new ColoredImageSpan(R.drawable.msg_mini_lock2); + lock.setSpan(span, 0, lock.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + s.append(lock); + } else { + s.append(Long.toString(stars)); + } + } + imageView.setBlurredText(s); + imageView.invalidate(); + container.invalidate(); + } + } + private void updateSpoilers2(boolean hasSpoiler) { if (container == null || imageView == null || imageView.getMeasuredHeight() <= 0 || imageView.getMeasuredWidth() <= 0) { return; @@ -396,7 +441,7 @@ public View getVideoInfoContainer() { return videoInfoContainer; } - public void setPhotoEntry(MediaController.PhotoEntry entry, boolean needCheckShow, boolean last) { + public void setPhotoEntry(MediaController.PhotoEntry entry, boolean selectedMultiple, boolean needCheckShow, boolean last) { pressed = false; photoEntry = entry; isLast = last; @@ -425,6 +470,7 @@ public void setPhotoEntry(MediaController.PhotoEntry entry, boolean needCheckSho videoInfoContainer.setAlpha(showing ? 0.0f : 1.0f); requestLayout(); setHasSpoiler(entry.hasSpoiler); + setStarsPrice(entry.starsAmount, selectedMultiple); } public void setPhotoEntry(MediaController.SearchImage searchImage, boolean needCheckShow, boolean last) { @@ -464,6 +510,7 @@ public void setPhotoEntry(MediaController.SearchImage searchImage, boolean needC videoInfoContainer.setAlpha(showing ? 0.0f : 1.0f); requestLayout(); setHasSpoiler(false); + setStarsPrice(0, false); } public boolean isChecked() { @@ -513,8 +560,8 @@ public void setNum(int num) { checkBox.setNum(num); } - public void setOnCheckClickLisnener(OnClickListener onCheckClickLisnener) { - checkFrame.setOnClickListener(onCheckClickLisnener); + public void setOnCheckClickListener(OnClickListener onCheckClickListener) { + checkFrame.setOnClickListener(onCheckClickListener); } public void setDelegate(PhotoAttachPhotoCellDelegate delegate) { 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 f53de096d4..a378cf5bd4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ProfileSearchCell.java @@ -390,9 +390,9 @@ public void buildLayout() { } else { String nameString2 = ""; if (chat != null) { - nameString2 = chat.title; + nameString2 = AndroidUtilities.removeDiacritics(chat.title); } else if (user != null) { - nameString2 = UserObject.getUserName(user); + nameString2 = AndroidUtilities.removeDiacritics(UserObject.getUserName(user)); } nameString = nameString2.replace('\n', ' '); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ReactedUserHolderView.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ReactedUserHolderView.java index 1e688dae59..5190ca948a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/ReactedUserHolderView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/ReactedUserHolderView.java @@ -85,10 +85,10 @@ public void openStory(long dialogId, Runnable onDone) { public static final MessageSeenCheckDrawable forwardDrawable = new MessageSeenCheckDrawable(R.drawable.mini_forward_story, Theme.key_stories_circle1); public ReactedUserHolderView(int style, int currentAccount, @NonNull Context context, Theme.ResourcesProvider resourcesProvider) { - this(style, currentAccount, context, resourcesProvider, true); + this(style, currentAccount, context, resourcesProvider, true, true); } - public ReactedUserHolderView(int style, int currentAccount, @NonNull Context context, Theme.ResourcesProvider resourcesProvider, boolean useOverlaySelector) { + public ReactedUserHolderView(int style, int currentAccount, @NonNull Context context, Theme.ResourcesProvider resourcesProvider, boolean useOverlaySelector, boolean showReactionPreview) { super(context); this.style = style; this.currentAccount = currentAccount; @@ -155,11 +155,13 @@ public boolean setText(CharSequence value) { topMargin = style == STYLE_STORY ? 24f : 19f; addView(subtitleView, LayoutHelper.createFrameRelatively(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, leftMargin, topMargin , 20, 0)); - reactView = new BackupImageView(context); - addView(reactView, LayoutHelper.createFrameRelatively(24, 24, Gravity.END | Gravity.CENTER_VERTICAL, 0, 0, 12, 0)); + if (showReactionPreview) { + reactView = new BackupImageView(context); + addView(reactView, LayoutHelper.createFrameRelatively(24, 24, Gravity.END | Gravity.CENTER_VERTICAL, 0, 0, 12, 0)); - storyPreviewView = new BackupImageView(context); - addView(storyPreviewView, LayoutHelper.createFrameRelatively(22, 35, Gravity.END | Gravity.CENTER_VERTICAL, 0, 0, 12, 0)); + storyPreviewView = new BackupImageView(context); + addView(storyPreviewView, LayoutHelper.createFrameRelatively(22, 35, Gravity.END | Gravity.CENTER_VERTICAL, 0, 0, 12, 0)); + } if (useOverlaySelector) { overlaySelectorView = new View(context); @@ -201,10 +203,12 @@ public void setUserReaction(TLRPC.User user, TLRPC.Chat chat, TLRPC.Reaction rea } avatarView.setImage(ImageLocation.getForUserOrChat(u, ImageLocation.TYPE_SMALL), "50_50", thumb, u); - String contentDescription; + String contentDescription = ""; boolean hasReactImage = false; if (like) { - reactView.setAnimatedEmojiDrawable(null); + if (reactView != null) { + reactView.setAnimatedEmojiDrawable(null); + } hasReactImage = true; Drawable likeDrawableFilled = ContextCompat.getDrawable(getContext(), R.drawable.media_like_active).mutate(); reactView.setColorFilter(new PorterDuffColorFilter(0xFFFF2E38, PorterDuff.Mode.MULTIPLY)); @@ -213,45 +217,57 @@ public void setUserReaction(TLRPC.User user, TLRPC.Chat chat, TLRPC.Reaction rea } else if (reaction != null) { ReactionsLayoutInBubble.VisibleReaction visibleReaction = ReactionsLayoutInBubble.VisibleReaction.fromTL(reaction); if (visibleReaction.emojicon != null) { - reactView.setAnimatedEmojiDrawable(null); + if (reactView != null) { + reactView.setAnimatedEmojiDrawable(null); + } TLRPC.TL_availableReaction r = MediaDataController.getInstance(currentAccount).getReactionsMap().get(visibleReaction.emojicon); - if (r != null) { - SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(r.static_icon.thumbs, Theme.key_windowBackgroundGray, 1.0f); - reactView.setImage(ImageLocation.getForDocument(r.center_icon), "40_40_lastreactframe", "webp", svgThumb, r); - hasReactImage = true; - } else { - reactView.setImageDrawable(null); + if (reactView != null) { + if (r != null) { + SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(r.static_icon.thumbs, Theme.key_windowBackgroundGray, 1.0f); + reactView.setImage(ImageLocation.getForDocument(r.center_icon), "40_40_lastreactframe", "webp", svgThumb, r); + hasReactImage = true; + } else { + reactView.setImageDrawable(null); + } + reactView.setColorFilter(null); } - reactView.setColorFilter(null); } else { AnimatedEmojiDrawable drawable = new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES, currentAccount, visibleReaction.documentId); drawable.setColorFilter(Theme.getAnimatedEmojiColorFilter(resourcesProvider)); - reactView.setAnimatedEmojiDrawable(drawable); + if (reactView != null) { + reactView.setAnimatedEmojiDrawable(drawable); + } hasReactImage = true; } contentDescription = LocaleController.formatString("AccDescrReactedWith", R.string.AccDescrReactedWith, titleView.getText(), visibleReaction.emojicon != null ? visibleReaction.emojicon : reaction); } else { - reactView.setAnimatedEmojiDrawable(null); - reactView.setImageDrawable(null); + if (reactView != null) { + reactView.setAnimatedEmojiDrawable(null); + reactView.setImageDrawable(null); + } contentDescription = LocaleController.formatString("AccDescrPersonHasSeen", R.string.AccDescrPersonHasSeen, titleView.getText()); } if (storyItem != null) { storyId = storyItem.id; - if (storyItem.media != null && storyItem.media.photo != null) { - final TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(storyItem.media.photo.sizes, 35, false, null, true); - storyPreviewView.setImage(ImageLocation.getForPhoto(photoSize, storyItem.media.photo), "22_35", null, null, -1, storyItem); - } else if (storyItem.media != null && storyItem.media.document != null) { - final TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(storyItem.media.document.thumbs, 35, false, null, true); - storyPreviewView.setImage(ImageLocation.getForDocument(photoSize, storyItem.media.document), "22_35", null, null, -1, storyItem); + if (storyPreviewView != null) { + if (storyItem.media != null && storyItem.media.photo != null) { + final TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(storyItem.media.photo.sizes, 35, false, null, true); + storyPreviewView.setImage(ImageLocation.getForPhoto(photoSize, storyItem.media.photo), "22_35", null, null, -1, storyItem); + } else if (storyItem.media != null && storyItem.media.document != null) { + final TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(storyItem.media.document.thumbs, 35, false, null, true); + storyPreviewView.setImage(ImageLocation.getForDocument(photoSize, storyItem.media.document), "22_35", null, null, -1, storyItem); + } + storyPreviewView.setRoundRadius(AndroidUtilities.dp(3.33f)); } - storyPreviewView.setRoundRadius(AndroidUtilities.dp(3.33f)); if (date <= 0) { date = storyItem.date; } } else { storyId = -1; - storyPreviewView.setImageDrawable(null); + if (storyPreviewView != null) { + storyPreviewView.setImageDrawable(null); + } } if (date != 0) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java index 2cf6f7fddc..d347e21924 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SessionCell.java @@ -350,6 +350,10 @@ public static Drawable createDrawable(int sz, TLRPC.TL_authorization session) { iconId = R.drawable.filled_star_plus; colorKey = Theme.key_color_yellow; colorKey2 = Theme.key_color_orange; + } else if (platform.contains("ads")) { + iconId = R.drawable.msg_channel; + colorKey = Theme.key_avatar_backgroundPink; + colorKey2 = Theme.key_avatar_background2Pink; } else if (platform.equals("?")) { iconId = R.drawable.msg_emoji_question; colorKey = -1; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell2.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell2.java index f7f324c125..5174d718f9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/SharedPhotoVideoCell2.java @@ -21,6 +21,8 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.text.Layout; +import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; @@ -45,6 +47,7 @@ import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.AvatarSpan; import org.telegram.ui.Components.AnimatedFloat; import org.telegram.ui.Components.AnimatedTextView; import org.telegram.ui.Components.CanvasButton; @@ -52,6 +55,7 @@ import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.FlickerLoadingView; +import org.telegram.ui.Components.Text; import org.telegram.ui.Components.spoilers.SpoilerEffect; import org.telegram.ui.Components.spoilers.SpoilerEffect2; import org.telegram.ui.PhotoViewer; @@ -67,6 +71,7 @@ public class SharedPhotoVideoCell2 extends FrameLayout { public ImageReceiver blurImageReceiver = new ImageReceiver(); public int storyId; int currentAccount; + public boolean isSearchingHashtag; MessageObject currentMessageObject; int currentParentColumnsCount; FlickerLoadingView globalGradientView; @@ -87,6 +92,8 @@ public class SharedPhotoVideoCell2 extends FrameLayout { AnimatedFloat viewsAlpha = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); AnimatedTextView.AnimatedTextDrawable viewsText = new AnimatedTextView.AnimatedTextDrawable(false, true, true); + private Text authorText; + CheckBoxBase checkBoxBase; SharedResources sharedResources; private boolean attached; @@ -171,12 +178,12 @@ public void onCheckBoxPressed() { } public void setMessageObject(MessageObject messageObject, int parentColumnsCount) { - int oldParentColumsCount = currentParentColumnsCount; + int oldParentColumnsCount = currentParentColumnsCount; currentParentColumnsCount = parentColumnsCount; if (currentMessageObject == null && messageObject == null) { return; } - if (currentMessageObject != null && messageObject != null && currentMessageObject.getId() == messageObject.getId() && oldParentColumsCount == parentColumnsCount && (privacyType == 100) == isStoryPinned) { + if (currentMessageObject != null && messageObject != null && currentMessageObject.getId() == messageObject.getId() && oldParentColumnsCount == parentColumnsCount && (privacyType == 100) == isStoryPinned) { return; } currentMessageObject = messageObject; @@ -195,6 +202,7 @@ public void setMessageObject(MessageObject messageObject, int parentColumnsCount gradientDrawable = null; privacyType = -1; privacyBitmap = null; + authorText = null; return; } else { if (attached) { @@ -331,6 +339,16 @@ public void setMessageObject(MessageObject messageObject, int parentColumnsCount setPrivacyType(-1, 0); } + if (isSearchingHashtag) { + final long did = messageObject.getDialogId(); + SpannableStringBuilder sb = new SpannableStringBuilder("x "); + sb.append(MessagesController.getInstance(currentAccount).getPeerName(did)); + AvatarSpan avatar = new AvatarSpan(this, currentAccount, parentColumnsCount == 2 ? 16f : 13.66f); + avatar.setDialogId(did); + sb.setSpan(avatar, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + authorText = new Text(sb, parentColumnsCount == 2 ? 14f : 10.1666f, AndroidUtilities.bold()); + } + invalidate(); } @@ -412,7 +430,7 @@ protected void onDraw(Canvas canvas) { canvas.save(); } - if ((checkBoxBase != null && checkBoxBase.isChecked()) || PhotoViewer.isShowingImage(currentMessageObject)) { + if (((checkBoxBase != null && checkBoxBase.isChecked()) || PhotoViewer.isShowingImage(currentMessageObject))) { canvas.drawRect(leftpadding, padding, leftpadding + imageWidth - rightpadding, imageHeight, sharedResources.backgroundPaint); } @@ -475,7 +493,7 @@ protected void onDraw(Canvas canvas) { invalidate(); } - if (highlightProgress > 0) { + if (!isSearchingHashtag && highlightProgress > 0) { sharedResources.highlightPaint.setColor(ColorUtils.setAlphaComponent(Color.BLACK, (int) (0.5f * highlightProgress * 255))); canvas.drawRect(imageReceiver.getDrawRegion(), sharedResources.highlightPaint); } @@ -484,7 +502,11 @@ protected void onDraw(Canvas canvas) { bounds.set(imageReceiver.getImageX(), imageReceiver.getImageY(), imageReceiver.getImageX2(), imageReceiver.getImageY2()); drawDuration(canvas, bounds, 1f); drawViews(canvas, bounds, 1f); - drawPrivacy(canvas, bounds, 1f); + if (!isSearchingHashtag) { + drawPrivacy(canvas, bounds, 1f); + } else { + drawAuthor(canvas, bounds, 1f); + } if (checkBoxBase != null && (style == STYLE_CACHE || checkBoxBase.getProgress() != 0)) { canvas.save(); @@ -596,6 +618,17 @@ public void drawPrivacy(Canvas canvas, RectF bounds, float alpha) { canvas.restore(); } + public void drawAuthor(Canvas canvas, RectF bounds, float alpha) { + if (!isStory || imageReceiver != null && !imageReceiver.getVisible() || !isSearchingHashtag || authorText == null) return; + + final float p = dp(5.33f); + authorText + .ellipsize((int) (bounds.width() - p * 2)) + .setVerticalClipPadding(dp(14)) + .setShadow(.4f * alpha) + .draw(canvas, bounds.left + p, bounds.top + dp(currentParentColumnsCount <= 2 ? 15 : 11.33f), Theme.multAlpha(0xFFFFFFFF, alpha), 1f); + } + public void drawViews(Canvas canvas, RectF bounds, float alpha) { if (!isStory || imageReceiver != null && !imageReceiver.getVisible() || currentParentColumnsCount >= 5) { return; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java index 17379574bc..3ddaa4d840 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TextSelectionHelper.java @@ -2204,7 +2204,7 @@ private void fillLayoutForCoords(int x, int y, ChatMessageCell cell, LayoutBlock MessageObject.TextLayoutBlocks captionLayout = cell.getCaptionLayout(); for (int i = 0; i < captionLayout.textLayoutBlocks.size(); i++) { MessageObject.TextLayoutBlock block = captionLayout.textLayoutBlocks.get(i); - if (y >= block.textYOffset(captionLayout.textLayoutBlocks) && y <= block.textYOffset(captionLayout.textLayoutBlocks) + block.padTop + block.height) { + if (y >= block.textYOffset(captionLayout.textLayoutBlocks) && y <= block.textYOffset(captionLayout.textLayoutBlocks) + block.padTop + block.height(cell.transitionParams)) { layoutBlock.layout = block.textLayout; layoutBlock.yOffset = block.textYOffset(captionLayout.textLayoutBlocks) + block.padTop; int offsetX = 0; @@ -2224,7 +2224,7 @@ private void fillLayoutForCoords(int x, int y, ChatMessageCell cell, LayoutBlock for (int i = 0; i < messageObject.textLayoutBlocks.size(); i++) { MessageObject.TextLayoutBlock block = messageObject.textLayoutBlocks.get(i); - if (y >= block.textYOffset(messageObject.textLayoutBlocks) && y <= block.textYOffset(messageObject.textLayoutBlocks) + block.padTop + block.height) { + if (y >= block.textYOffset(messageObject.textLayoutBlocks) && y <= block.textYOffset(messageObject.textLayoutBlocks) + block.padTop + block.height(cell.transitionParams)) { layoutBlock.layout = block.textLayout; layoutBlock.yOffset = block.textYOffset(messageObject.textLayoutBlocks) + block.padTop; int offsetX = 0; @@ -2498,6 +2498,10 @@ public void checkDataChanged(MessageObject messageObject) { clear(true); } } + + public boolean isMenuEmpty() { + return !canCopy() && !canShowQuote(); + } } public static class ArticleTextSelectionHelper extends TextSelectionHelper { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TopicSearchCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TopicSearchCell.java index 82e76f7777..7f8eef9bb9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/TopicSearchCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/TopicSearchCell.java @@ -52,9 +52,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { public void setTopic(TLRPC.TL_forumTopic topic) { this.topic = topic; if (TextUtils.isEmpty(topic.searchQuery)) { - textView.setText(topic.title); + textView.setText(AndroidUtilities.removeDiacritics(topic.title)); } else { - textView.setText(AndroidUtilities.highlightText(topic.title, topic.searchQuery, null)); + textView.setText(AndroidUtilities.highlightText(AndroidUtilities.removeDiacritics(topic.title), topic.searchQuery, null)); } ForumUtilities.setTopicIcon(backupImageView, topic); if (backupImageView != null && backupImageView.getImageReceiver() != null && backupImageView.getImageReceiver().getDrawable() instanceof ForumUtilities.GeneralTopicDrawable) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java index ce0292420d..a48abd0e27 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/UserCell.java @@ -466,9 +466,9 @@ public void update(int mask) { } if (!continueUpdate && currentName == null && lastName != null && (mask & MessagesController.UPDATE_MASK_NAME) != 0) { if (currentUser != null) { - newName = UserObject.getUserName(currentUser); + newName = AndroidUtilities.removeDiacritics(UserObject.getUserName(currentUser)); } else { - newName = currentChat.title; + newName = AndroidUtilities.removeDiacritics(currentChat == null ? "" : currentChat.title); } if (!newName.equals(lastName)) { continueUpdate = true; @@ -547,9 +547,9 @@ public void update(int mask) { nameTextView.setText(currentName); } else { if (currentUser != null) { - lastName = newName == null ? UserObject.getUserName(currentUser) : newName; + lastName = AndroidUtilities.removeDiacritics(newName == null ? UserObject.getUserName(currentUser) : newName); } else if (currentChat != null) { - lastName = newName == null ? currentChat.title : newName; + lastName = AndroidUtilities.removeDiacritics(newName == null ? currentChat.title : newName); } else { lastName = ""; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChannelBoostLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ChannelBoostLayout.java index dccc0ec876..8ae5ba4b1a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelBoostLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelBoostLayout.java @@ -254,9 +254,9 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi overviewCell.setData(0, Integer.toString(boostsStatus.level), null, LocaleController.getString("BoostsLevel2", R.string.BoostsLevel2)); if (boostsStatus.premium_audience != null && boostsStatus.premium_audience.total != 0) { float percent = (((float) boostsStatus.premium_audience.part / (float) boostsStatus.premium_audience.total) * 100f); - overviewCell.setData(1, "~" + (int) boostsStatus.premium_audience.part, String.format(Locale.US, "%.1f", percent) + "%", LocaleController.getString(isChannel() ? R.string.PremiumSubscribers : R.string.PremiumMembers)); + overviewCell.setData(1, "≈" + (int) boostsStatus.premium_audience.part, String.format(Locale.US, "%.1f", percent) + "%", LocaleController.getString(isChannel() ? R.string.PremiumSubscribers : R.string.PremiumMembers)); } else { - overviewCell.setData(1, "~0", "0%", LocaleController.getString(isChannel() ? R.string.PremiumSubscribers : R.string.PremiumMembers)); + overviewCell.setData(1, "≈0", "0%", LocaleController.getString(isChannel() ? R.string.PremiumSubscribers : R.string.PremiumMembers)); } overviewCell.setData(2, String.valueOf(boostsStatus.boosts), null, LocaleController.getString("BoostsExisting", R.string.BoostsExisting)); overviewCell.setData(3, String.valueOf(Math.max(0, boostsStatus.next_level_boosts - boostsStatus.boosts)), null, LocaleController.getString("BoostsToLevel", R.string.BoostsToLevel)); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChannelMonetizationLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/ChannelMonetizationLayout.java index c1c9099c5a..33367fd79f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChannelMonetizationLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChannelMonetizationLayout.java @@ -2,7 +2,6 @@ import static org.telegram.messenger.AndroidUtilities.REPLACING_TAG_TYPE_LINK_NBSP; import static org.telegram.messenger.AndroidUtilities.dp; -import static org.telegram.messenger.AndroidUtilities.dpf2; import static org.telegram.messenger.AndroidUtilities.makeBlurBitmap; import static org.telegram.messenger.LocaleController.formatPluralString; import static org.telegram.messenger.LocaleController.formatString; @@ -15,23 +14,36 @@ import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.text.Editable; +import android.text.InputType; import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; import android.text.TextUtils; +import android.text.TextWatcher; import android.text.style.RelativeSizeSpan; import android.util.Base64; +import android.util.Log; import android.util.TypedValue; import android.view.Gravity; +import android.view.MotionEvent; import android.view.View; +import android.view.inputmethod.EditorInfo; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.Space; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.core.view.NestedScrollingParent3; +import androidx.core.view.NestedScrollingParentHelper; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BillingController; @@ -40,6 +52,7 @@ import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; @@ -49,6 +62,7 @@ import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.tl.TL_stats; import org.telegram.tgnet.tl.TL_stories; +import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; @@ -57,17 +71,27 @@ import org.telegram.ui.Components.AnimatedTextView; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BackupImageView; +import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.BulletinFactory; -import org.telegram.ui.Components.CircularProgressDrawable; import org.telegram.ui.Components.ColoredImageSpan; import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EditTextBoldCursor; +import org.telegram.ui.Components.FlickerLoadingView; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LinkSpanDrawable; +import org.telegram.ui.Components.OutlineTextContainerView; import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; import org.telegram.ui.Components.RLottieImageView; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.SizeNotifierFrameLayout; import org.telegram.ui.Components.UItem; import org.telegram.ui.Components.UniversalAdapter; import org.telegram.ui.Components.UniversalRecyclerView; +import org.telegram.ui.Components.ViewPagerFixed; +import org.telegram.ui.Stars.BotStarsActivity; +import org.telegram.ui.Stars.BotStarsController; +import org.telegram.ui.Stars.StarsController; +import org.telegram.ui.Stars.StarsIntroActivity; import org.telegram.ui.Stories.recorder.ButtonWithCounterView; import java.text.DecimalFormat; @@ -77,7 +101,7 @@ import java.util.HashMap; import java.util.Locale; -public class ChannelMonetizationLayout extends FrameLayout { +public class ChannelMonetizationLayout extends SizeNotifierFrameLayout implements NestedScrollingParent3 { public static ChannelMonetizationLayout instance; @@ -90,6 +114,7 @@ public class ChannelMonetizationLayout extends FrameLayout { private final CharSequence titleInfo; private final CharSequence balanceInfo; + private final CharSequence starsBalanceInfo; private final LinearLayout balanceLayout; private final RelativeSizeSpan balanceTitleSizeSpan; @@ -98,6 +123,23 @@ public class ChannelMonetizationLayout extends FrameLayout { private final ButtonWithCounterView balanceButton; private int shakeDp = 4; + private int starsBalanceBlockedUntil; + private final LinearLayout starsBalanceLayout; + private final RelativeSizeSpan starsBalanceTitleSizeSpan; + private long starsBalance; + private final AnimatedTextView starsBalanceTitle; + private final AnimatedTextView starsBalanceSubtitle; + private final ButtonWithCounterView starsBalanceButton; + private ColoredImageSpan[] starRef = new ColoredImageSpan[1]; + private final ButtonWithCounterView starsAdsButton; + private OutlineTextContainerView starsBalanceEditTextContainer; + private boolean starsBalanceEditTextIgnore = false; + private boolean starsBalanceEditTextAll = true; + private long starsBalanceEditTextValue; + private EditTextBoldCursor starsBalanceEditText; + + private Bulletin withdrawalBulletin; + private boolean transfering; private final UniversalRecyclerView listView; @@ -105,6 +147,13 @@ public class ChannelMonetizationLayout extends FrameLayout { private DecimalFormat formatter; + private final ChannelTransactionsView transactionsLayout; + public void updateList() { + if (listView != null) { + listView.adapter.update(true); + } + } + public ChannelMonetizationLayout( Context context, BaseFragment fragment, @@ -127,7 +176,6 @@ public ChannelMonetizationLayout( this.currentAccount = currentAccount; this.dialogId = dialogId; initLevel(); - loadTransactions(); titleInfo = AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(formatString(R.string.MonetizationInfo, 50), -1, REPLACING_TAG_TYPE_LINK_NBSP, () -> { showLearnSheet(); @@ -135,9 +183,14 @@ public ChannelMonetizationLayout( balanceInfo = AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(getString(MessagesController.getInstance(currentAccount).channelRevenueWithdrawalEnabled ? R.string.MonetizationBalanceInfo : R.string.MonetizationBalanceInfoNotAvailable), -1, REPLACING_TAG_TYPE_LINK_NBSP, () -> { Browser.openUrl(getContext(), getString(R.string.MonetizationBalanceInfoLink)); }), true); + starsBalanceInfo = AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(getString(R.string.MonetizationStarsInfo), () -> { + Browser.openUrl(getContext(), getString(R.string.MonetizationStarsInfoLink)); + }), true); setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray, resourcesProvider)); + transactionsLayout = new ChannelTransactionsView(context, currentAccount, dialogId, fragment.getClassGuid(), this::updateList, resourcesProvider); + balanceLayout = new LinearLayout(context) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @@ -165,37 +218,245 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { balanceSubtitle.setTextSize(dp(14)); balanceLayout.addView(balanceSubtitle, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 17, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 22, 4, 22, 0)); - final CircularProgressDrawable circularProgressDrawable = new CircularProgressDrawable(dp(15), dpf2(2), Theme.getColor(Theme.key_featuredStickers_buttonText, resourcesProvider)) { + balanceButton = new ButtonWithCounterView(context, resourcesProvider); + balanceButton.setEnabled(MessagesController.getInstance(currentAccount).channelRevenueWithdrawalEnabled); + balanceButton.setText(getString(R.string.MonetizationWithdraw), false); + balanceButton.setVisibility(View.GONE); + balanceButton.setOnClickListener(v -> { + if (!v.isEnabled() || balanceButton.isLoading() || ChannelMonetizationLayout.this.starsBalanceButton != null && ChannelMonetizationLayout.this.starsBalanceButton.isLoading()) { + return; + } + TwoStepVerificationActivity passwordFragment = new TwoStepVerificationActivity(); + passwordFragment.setDelegate(1, password -> initWithdraw(false, password, passwordFragment)); + balanceButton.setLoading(true); + passwordFragment.preload(() -> { + balanceButton.setLoading(false); + fragment.presentFragment(passwordFragment);; + }); + }); + balanceLayout.addView(balanceButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.TOP | Gravity.FILL_HORIZONTAL, 18, 13, 18, 0)); + + + starsBalanceLayout = new LinearLayout(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + heightMeasureSpec + ); + } + }; + starsBalanceLayout.setOrientation(LinearLayout.VERTICAL); + starsBalanceLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + starsBalanceLayout.setPadding(0, 0, 0, dp(17)); + + starsBalanceTitle = new AnimatedTextView(context, false, true, true); + starsBalanceTitle.setTypeface(AndroidUtilities.bold()); + starsBalanceTitle.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + starsBalanceTitle.setTextSize(dp(32)); + starsBalanceTitle.setGravity(Gravity.CENTER); + starsBalanceTitleSizeSpan = new RelativeSizeSpan(65f / 96f); + starsBalanceLayout.addView(starsBalanceTitle, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 38, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 22, 15, 22, 0)); + + starsBalanceSubtitle = new AnimatedTextView(context, true, true, true); + starsBalanceSubtitle.setGravity(Gravity.CENTER); + starsBalanceSubtitle.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText, resourcesProvider)); + starsBalanceSubtitle.setTextSize(dp(14)); + starsBalanceLayout.addView(starsBalanceSubtitle, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 17, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 22, 4, 22, 0)); + + starsBalanceEditTextContainer = new OutlineTextContainerView(context) { @Override - public int getIntrinsicWidth() { - return dp(24); + public boolean dispatchTouchEvent(MotionEvent event) { + if (starsBalanceEditText != null && !starsBalanceEditText.isFocusable()) { + starsBalanceEditText.setFocusable(true); + starsBalanceEditText.setFocusableInTouchMode(true); + int position = listView.findPositionByItemId(STARS_BALANCE); + if (position >= 0 && position < listView.adapter.getItemCount()) { + listView.stopScroll(); + listView.smoothScrollToPosition(position); + } + starsBalanceEditText.requestFocus(); + } + return super.dispatchTouchEvent(event); } + }; + starsBalanceEditTextContainer.setText(getString(R.string.BotStarsWithdrawPlaceholder)); + starsBalanceEditTextContainer.setLeftPadding(dp(14 + 22)); + starsBalanceEditText = new EditTextBoldCursor(context) { @Override - public int getIntrinsicHeight() { - return dp(24); + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + AndroidUtilities.hideKeyboard(this); } }; - circularProgressDrawable.setBounds(0, 0, dp(24), dp(24)); - - balanceButton = new ButtonWithCounterView(context, resourcesProvider) { + starsBalanceEditText.setFocusable(false); + starsBalanceEditText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + starsBalanceEditText.setCursorSize(AndroidUtilities.dp(20)); + starsBalanceEditText.setCursorWidth(1.5f); + starsBalanceEditText.setBackground(null); + starsBalanceEditText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + starsBalanceEditText.setMaxLines(1); + int padding = AndroidUtilities.dp(16); + starsBalanceEditText.setPadding(dp(6), padding, padding, padding); + starsBalanceEditText.setInputType(InputType.TYPE_CLASS_NUMBER); + starsBalanceEditText.setTypeface(Typeface.DEFAULT); + starsBalanceEditText.setHighlightColor(Theme.getColor(Theme.key_chat_inTextSelectionHighlight, resourcesProvider)); + starsBalanceEditText.setHandlesColor(Theme.getColor(Theme.key_chat_TextSelectionCursor, resourcesProvider)); + starsBalanceEditText.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + starsBalanceEditText.setOnFocusChangeListener((v, hasFocus) -> starsBalanceEditTextContainer.animateSelection(hasFocus ? 1f : 0f)); + starsBalanceEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override - protected boolean verifyDrawable(@NonNull Drawable who) { - return who == circularProgressDrawable || super.verifyDrawable(who); + public void onTextChanged(CharSequence s, int start, int before, int count) {} + @Override + public void afterTextChanged(Editable s) { + if (starsBalanceEditTextIgnore) return; + long balance = starsBalance; + starsBalanceEditTextValue = TextUtils.isEmpty(s) ? 0 : Long.parseLong(s.toString()); + if (starsBalanceEditTextValue > balance) { + starsBalanceEditTextValue = balance; + starsBalanceEditTextIgnore = true; + starsBalanceEditText.setText(Long.toString(starsBalanceEditTextValue)); + starsBalanceEditText.setSelection(starsBalanceEditText.getText().length()); + starsBalanceEditTextIgnore = false; + } + starsBalanceEditTextAll = starsBalanceEditTextValue == balance; + AndroidUtilities.cancelRunOnUIThread(setStarsBalanceButtonText); + setStarsBalanceButtonText.run(); + starsBalanceEditTextAll = false; + } + }); + LinearLayout balanceEditTextLayout = new LinearLayout(context); + balanceEditTextLayout.setOrientation(LinearLayout.HORIZONTAL); + ImageView starImage = new ImageView(context); + starImage.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + starImage.setImageResource(R.drawable.star_small_inner); + balanceEditTextLayout.addView(starImage, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, Gravity.LEFT | Gravity.CENTER_VERTICAL, 14, 0, 0, 0)); + balanceEditTextLayout.addView(starsBalanceEditText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 1, Gravity.FILL)); + starsBalanceEditTextContainer.attachEditText(starsBalanceEditText); + starsBalanceEditTextContainer.addView(balanceEditTextLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP)); + starsBalanceLayout.addView(starsBalanceEditTextContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 18, 14, 18, 2)); + + LinearLayout starsBalanceButtonsLayout = new LinearLayout(context); + starsBalanceButtonsLayout.setOrientation(LinearLayout.HORIZONTAL); + + starsBalanceButton = new ButtonWithCounterView(context, resourcesProvider) { + @Override + protected boolean subTextSplitToWords() { + return false; } }; - balanceButton.setEnabled(MessagesController.getInstance(currentAccount).channelRevenueWithdrawalEnabled); - circularProgressDrawable.setCallback(balanceButton); - balanceButton.setText(getString(R.string.MonetizationWithdraw), false); - balanceButton.setVisibility(View.GONE); - balanceButton.setOnClickListener(v -> { - if (!v.isEnabled()) { + starsBalanceButton.setEnabled(false); + starsBalanceButton.setText(formatPluralString("MonetizationStarsWithdraw", 0), false); + starsBalanceButton.setVisibility(View.VISIBLE); + starsBalanceButton.setOnClickListener(v -> { + if (!v.isEnabled() || starsBalanceButton.isLoading() || balanceButton.isLoading()) { return; } + + final int now = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); + if (starsBalanceBlockedUntil > now) { + withdrawalBulletin = BulletinFactory.of(fragment).createSimpleBulletin(R.raw.timer_3, AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotStarsWithdrawalToast, BotStarsActivity.untilString(starsBalanceBlockedUntil - now)))).show(); + return; + } + + if (starsBalanceEditTextValue < MessagesController.getInstance(currentAccount).starsRevenueWithdrawalMin) { + Drawable starDrawable = getContext().getResources().getDrawable(R.drawable.star_small_inner).mutate(); + BulletinFactory.of(fragment).createSimpleBulletin(starDrawable, AndroidUtilities.replaceSingleTag(LocaleController.formatPluralString("BotStarsWithdrawMinLimit", (int) MessagesController.getInstance(currentAccount).starsRevenueWithdrawalMin), () -> { + Bulletin.hideVisible(); + long balance = starsBalance; + if (balance < MessagesController.getInstance(currentAccount).starsRevenueWithdrawalMin) { + starsBalanceEditTextAll = true; + starsBalanceEditTextValue = balance; + } else { + starsBalanceEditTextAll = false; + starsBalanceEditTextValue = MessagesController.getInstance(currentAccount).starsRevenueWithdrawalMin; + } + starsBalanceEditTextIgnore = true; + starsBalanceEditText.setText(Long.toString(starsBalanceEditTextValue)); + starsBalanceEditText.setSelection(starsBalanceEditText.getText().length()); + starsBalanceEditTextIgnore = false; + + AndroidUtilities.cancelRunOnUIThread(setStarsBalanceButtonText); + setStarsBalanceButtonText.run(); + })).show(); + return; + } + TwoStepVerificationActivity passwordFragment = new TwoStepVerificationActivity(); - passwordFragment.setDelegate(1, password -> initWithdraw(password, passwordFragment)); - fragment.presentFragment(passwordFragment); + passwordFragment.setDelegate(1, password -> initWithdraw(true, password, passwordFragment)); + starsBalanceButton.setLoading(true); + passwordFragment.preload(() -> { + starsBalanceButton.setLoading(false); + fragment.presentFragment(passwordFragment);; + }); }); - balanceLayout.addView(balanceButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.TOP | Gravity.FILL_HORIZONTAL, 18, 13, 18, 0)); + + starsAdsButton = new ButtonWithCounterView(context, resourcesProvider); + starsAdsButton.setEnabled(true); + starsAdsButton.setText(getString(R.string.MonetizationStarsAds), false); + starsAdsButton.setOnClickListener(v -> { + if (!v.isEnabled() || starsAdsButton.isLoading()) return; + + starsAdsButton.setLoading(true); + TLRPC.TL_payments_getStarsRevenueAdsAccountUrl req = new TLRPC.TL_payments_getStarsRevenueAdsAccountUrl(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TLRPC.TL_payments_starsRevenueAdsAccountUrl) { + Browser.openUrl(context, ((TLRPC.TL_payments_starsRevenueAdsAccountUrl) res).url); + } + AndroidUtilities.runOnUIThread(() -> { + starsAdsButton.setLoading(false); + }, 1000); + })); + }); + + starsBalanceButtonsLayout.addView(starsBalanceButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 1, Gravity.FILL)); + starsBalanceButtonsLayout.addView(new Space(context), LayoutHelper.createLinear(8, 48, 0, Gravity.FILL)); + starsBalanceButtonsLayout.addView(starsAdsButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 1, Gravity.FILL)); + starsBalanceLayout.addView(starsBalanceButtonsLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.TOP | Gravity.FILL_HORIZONTAL, 18, 13, 18, 0)); + + starsBalanceEditText.setOnEditorActionListener((textView, i, keyEvent) -> { + if (i == EditorInfo.IME_ACTION_NEXT) { + TwoStepVerificationActivity passwordFragment = new TwoStepVerificationActivity(); + passwordFragment.setDelegate(1, password -> initWithdraw(true, password, passwordFragment)); + starsBalanceButton.setLoading(true); + passwordFragment.preload(() -> { + starsBalanceButton.setLoading(false); + fragment.presentFragment(passwordFragment);; + }); + return true; + } + return false; + }); + setStarsBalanceButtonText = () -> { + final int now = ConnectionsManager.getInstance(currentAccount).getCurrentTime(); + starsBalanceButton.setEnabled(starsBalanceEditTextValue > 0 || starsBalanceBlockedUntil > now); + if (now < starsBalanceBlockedUntil) { + starsBalanceButton.setText(getString(R.string.MonetizationStarsWithdrawUntil), true); + + if (lock == null) { + lock = new SpannableStringBuilder("l"); + ColoredImageSpan coloredImageSpan = new ColoredImageSpan(R.drawable.mini_switch_lock); + coloredImageSpan.setTopOffset(1); + lock.setSpan(coloredImageSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + SpannableStringBuilder buttonLockedText = new SpannableStringBuilder(); + buttonLockedText.append(lock).append(BotStarsActivity.untilString(starsBalanceBlockedUntil - now)); + starsBalanceButton.setSubText(buttonLockedText, true); + + if (withdrawalBulletin != null && withdrawalBulletin.getLayout() instanceof Bulletin.LottieLayout && withdrawalBulletin.getLayout().isAttachedToWindow()) { + ((Bulletin.LottieLayout) withdrawalBulletin.getLayout()).textView.setText(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotStarsWithdrawalToast, BotStarsActivity.untilString(starsBalanceBlockedUntil - now)))); + } + + AndroidUtilities.cancelRunOnUIThread(this.setStarsBalanceButtonText); + AndroidUtilities.runOnUIThread(this.setStarsBalanceButtonText, 1000); + } else { + starsBalanceButton.setSubText(null, true); + starsBalanceButton.setText(StarsIntroActivity.replaceStars(starsBalanceEditTextAll ? getString(R.string.MonetizationStarsWithdrawAll) : LocaleController.formatPluralStringComma("MonetizationStarsWithdraw", (int) starsBalanceEditTextValue, ' '), starRef), true); + } + }; listView = new UniversalRecyclerView(fragment, this::fillItems, this::onClick, this::onLongClick); addView(listView); @@ -234,16 +495,26 @@ protected boolean verifyDrawable(@NonNull Drawable who) { addView(progress, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); } - private void initWithdraw(TLRPC.InputCheckPasswordSRP password, TwoStepVerificationActivity passwordFragment) { + private void initWithdraw(boolean stars, TLRPC.InputCheckPasswordSRP password, TwoStepVerificationActivity passwordFragment) { if (fragment == null) return; Activity parentActivity = fragment.getParentActivity(); TLRPC.User currentUser = UserConfig.getInstance(currentAccount).getCurrentUser(); if (parentActivity == null || currentUser == null) return; - TL_stats.TL_getBroadcastRevenueWithdrawalUrl req = new TL_stats.TL_getBroadcastRevenueWithdrawalUrl(); - req.channel = MessagesController.getInstance(currentAccount).getInputChannel(-dialogId); - req.password = password != null ? password : new TLRPC.TL_inputCheckPasswordEmpty(); - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + TLObject r; + if (stars) { + TLRPC.TL_payments_getStarsRevenueWithdrawalUrl req = new TLRPC.TL_payments_getStarsRevenueWithdrawalUrl(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); + req.password = password != null ? password : new TLRPC.TL_inputCheckPasswordEmpty(); + req.stars = starsBalanceEditTextValue; + r = req; + } else { + TL_stats.TL_getBroadcastRevenueWithdrawalUrl req = new TL_stats.TL_getBroadcastRevenueWithdrawalUrl(); + req.channel = MessagesController.getInstance(currentAccount).getInputChannel(-dialogId); + req.password = password != null ? password : new TLRPC.TL_inputCheckPasswordEmpty(); + r = req; + } + ConnectionsManager.getInstance(currentAccount).sendRequest(r, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (error != null) { if ("PASSWORD_MISSING".equals(error.text) || error.text.startsWith("PASSWORD_TOO_FRESH_") || error.text.startsWith("SESSION_TOO_FRESH_")) { if (passwordFragment != null) { @@ -333,7 +604,7 @@ private void initWithdraw(TLRPC.InputCheckPasswordSRP password, TwoStepVerificat TLRPC.account_Password currentPassword = (TLRPC.account_Password) response2; passwordFragment.setCurrentPasswordInfo(null, currentPassword); TwoStepVerificationActivity.initPasswordNewAlgo(currentPassword); - initWithdraw(passwordFragment.getNewSrpPassword(), passwordFragment); + initWithdraw(stars, passwordFragment.getNewSrpPassword(), passwordFragment); } }), ConnectionsManager.RequestFlagWithoutLogin); } else { @@ -348,6 +619,9 @@ private void initWithdraw(TLRPC.InputCheckPasswordSRP password, TwoStepVerificat passwordFragment.finishFragment(); if (response instanceof TL_stats.TL_broadcastRevenueWithdrawalUrl) { Browser.openUrl(getContext(), ((TL_stats.TL_broadcastRevenueWithdrawalUrl) response).url); + } else if (response instanceof TLRPC.TL_payments_starsRevenueWithdrawalUrl) { + Browser.openUrl(getContext(), ((TLRPC.TL_payments_starsRevenueWithdrawalUrl) response).url); + loadStarsStats(); } } })); @@ -369,10 +643,65 @@ private void setBalance(long crypto_amount, long amount) { ssb.setSpan(balanceTitleSizeSpan, index, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } balanceTitle.setText(ssb); - balanceSubtitle.setText("~" + BillingController.getInstance().formatCurrency(amount, "USD")); + balanceSubtitle.setText("≈" + BillingController.getInstance().formatCurrency(amount, "USD")); + } + + private void setStarsBalance(long crypto_amount, int blockedUntil) { + if (balanceTitle == null || balanceSubtitle == null) + return; + long amount = (long) (stars_rate * crypto_amount * 100.0); + SpannableStringBuilder ssb = new SpannableStringBuilder(StarsIntroActivity.replaceStarsWithPlain("XTR " + LocaleController.formatNumber(crypto_amount, ' '), 1f)); + int index = TextUtils.indexOf(ssb, "."); + if (index >= 0) { + ssb.setSpan(balanceTitleSizeSpan, index, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + starsBalance = crypto_amount; + starsBalanceTitle.setText(ssb); + starsBalanceSubtitle.setText("≈" + BillingController.getInstance().formatCurrency(amount, "USD")); + if (starsBalanceEditTextAll) { + starsBalanceEditTextIgnore = true; + starsBalanceEditText.setText(Long.toString(starsBalanceEditTextValue = crypto_amount)); + starsBalanceEditText.setSelection(starsBalanceEditText.getText().length()); + starsBalanceEditTextIgnore = false; + + balanceButton.setEnabled(starsBalanceEditTextValue > 0); + } + starsBalanceBlockedUntil = blockedUntil; + + AndroidUtilities.cancelRunOnUIThread(setStarsBalanceButtonText); + setStarsBalanceButtonText.run(); + } + + + private SpannableStringBuilder lock; + private Runnable setStarsBalanceButtonText; + + private double ton_rate; + private double stars_rate; + + private void loadStarsStats() { + TLRPC.TL_payments_getStarsRevenueStats req2 = new TLRPC.TL_payments_getStarsRevenueStats(); + req2.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); + req2.dark = Theme.isCurrentThemeDark(); + ConnectionsManager.getInstance(currentAccount).sendRequest(req2, (res2, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res2 instanceof TLRPC.TL_payments_starsRevenueStats) { + TLRPC.TL_payments_starsRevenueStats stats = (TLRPC.TL_payments_starsRevenueStats) res2; + + stars_rate = stats.usd_rate; + starsRevenueChart = StatisticActivity.createViewData(stats.revenue_graph, getString(R.string.MonetizationGraphStarsRevenue), 2); + if (starsRevenueChart != null && starsRevenueChart.chartData != null && starsRevenueChart.chartData.lines != null && !starsRevenueChart.chartData.lines.isEmpty() && starsRevenueChart.chartData.lines.get(0) != null) { + starsRevenueChart.chartData.lines.get(0).colorKey = Theme.key_statisticChartLine_golden; + starsRevenueChart.chartData.yRate = (float) (1.0 / stars_rate / 100.0); + } + setupBalances(stats.status); + + if (listView != null) { + listView.adapter.update(true); + } + } + })); } - private double usd_rate; private void initLevel() { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); if (chat != null) { @@ -388,6 +717,8 @@ private void initLevel() { } })); + loadStarsStats(); + TLObject req; if (ChatObject.isMegagroup(chat)) { return; @@ -401,7 +732,7 @@ private void initLevel() { TLRPC.ChatFull chatFull = MessagesController.getInstance(currentAccount).getChatFull(-dialogId); if (chatFull != null) { stats_dc = chatFull.stats_dc; - switchOffValue = chatFull.restricted_sponsored; + initialSwitchOffValue = switchOffValue = chatFull.restricted_sponsored; } if (stats_dc == -1) return; ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { @@ -417,7 +748,7 @@ private void initLevel() { impressionsChart.useHourFormat = true; } - usd_rate = stats.usd_rate; + ton_rate = stats.usd_rate; setupBalances(stats.balances); progress.animate().alpha(0).setDuration(380).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).withEndAction(() -> { @@ -428,19 +759,20 @@ private void initLevel() { } }), null, null, 0, stats_dc, ConnectionsManager.ConnectionTypeGeneric, true); } + public void setupBalances(TLRPC.TL_broadcastRevenueBalances balances) { - if (usd_rate == 0) { + if (ton_rate == 0) { return; } availableValue.crypto_amount = balances.available_balance; - availableValue.amount = (long) (availableValue.crypto_amount / 1_000_000_000.0 * usd_rate * 100.0); + availableValue.amount = (long) (availableValue.crypto_amount / 1_000_000_000.0 * ton_rate * 100.0); setBalance(availableValue.crypto_amount, availableValue.amount); availableValue.currency = "USD"; lastWithdrawalValue.crypto_amount = balances.current_balance; - lastWithdrawalValue.amount = (long) (lastWithdrawalValue.crypto_amount / 1_000_000_000.0 * usd_rate * 100.0); + lastWithdrawalValue.amount = (long) (lastWithdrawalValue.crypto_amount / 1_000_000_000.0 * ton_rate * 100.0); lastWithdrawalValue.currency = "USD"; lifetimeValue.crypto_amount = balances.overall_revenue; - lifetimeValue.amount = (long) (lifetimeValue.crypto_amount / 1_000_000_000.0 * usd_rate * 100.0); + lifetimeValue.amount = (long) (lifetimeValue.crypto_amount / 1_000_000_000.0 * ton_rate * 100.0); lifetimeValue.currency = "USD"; proceedsAvailable = true; balanceButton.setVisibility(balances.available_balance > 0 || BuildVars.DEBUG_PRIVATE_VERSION ? View.VISIBLE : View.GONE); @@ -449,6 +781,34 @@ public void setupBalances(TLRPC.TL_broadcastRevenueBalances balances) { listView.adapter.update(true); } } + public void setupBalances(TLRPC.TL_starsRevenueStatus balances) { + if (stars_rate == 0) { + return; + } + availableValue.contains2 = true; + availableValue.crypto_amount2 = balances.available_balance; + availableValue.amount2 = (long) (availableValue.crypto_amount2 * stars_rate * 100.0); + setStarsBalance(availableValue.crypto_amount2, balances.next_withdrawal_at); + availableValue.currency = "USD"; + lastWithdrawalValue.contains2 = true; + lastWithdrawalValue.crypto_amount2 = balances.current_balance; + lastWithdrawalValue.amount2 = (long) (lastWithdrawalValue.crypto_amount2 * stars_rate * 100.0); + lastWithdrawalValue.currency = "USD"; + lifetimeValue.contains2 = true; + lifetimeValue.crypto_amount2 = balances.overall_revenue; + lifetimeValue.amount2 = (long) (lifetimeValue.crypto_amount2 * stars_rate * 100.0); + lifetimeValue.currency = "USD"; + proceedsAvailable = true; + starsBalanceButton.setVisibility(balances.available_balance > 0 || BuildVars.DEBUG_PRIVATE_VERSION ? View.VISIBLE : View.GONE); + + if (listView != null && listView.adapter != null) { + listView.adapter.update(true); + } + } + + public void reloadTransactions() { + transactionsLayout.reloadTransactions(); + } @Override protected void onAttachedToWindow() { @@ -461,6 +821,14 @@ protected void onAttachedToWindow() { protected void onDetachedFromWindow() { instance = null; super.onDetachedFromWindow(); + if (actionBar != null) { + actionBar.setCastShadows(true); + } + } + + private ActionBar actionBar; + public void setActionBar(ActionBar actionBar) { + this.actionBar = actionBar; } private void checkLearnSheet() { @@ -471,19 +839,19 @@ private void checkLearnSheet() { } private boolean switchOffValue = false; + private boolean initialSwitchOffValue = false; private StatisticActivity.ChartViewData impressionsChart; private StatisticActivity.ChartViewData revenueChart; + private StatisticActivity.ChartViewData starsRevenueChart; private boolean proceedsAvailable = false; - private final ProceedOverview availableValue = ProceedOverview.as(getString(R.string.MonetizationOverviewAvailable)); - private final ProceedOverview lastWithdrawalValue = ProceedOverview.as(getString(R.string.MonetizationOverviewLastWithdrawal)); - private final ProceedOverview lifetimeValue = ProceedOverview.as(getString(R.string.MonetizationOverviewTotal)); - - private final ArrayList transactions = new ArrayList<>(); - private int transactionsTotalCount; + private final ProceedOverview availableValue = ProceedOverview.as("TON", "XTR", getString(R.string.MonetizationOverviewAvailable)); + private final ProceedOverview lastWithdrawalValue = ProceedOverview.as("TON", "XTR", getString(R.string.MonetizationOverviewLastWithdrawal)); + private final ProceedOverview lifetimeValue = ProceedOverview.as("TON", "XTR", getString(R.string.MonetizationOverviewTotal)); private final static int CHECK_SWITCHOFF = 1; private final static int BUTTON_LOAD_MORE_TRANSACTIONS = 2; + private final static int STARS_BALANCE = 3; private void fillItems(ArrayList items, UniversalAdapter adapter) { int stats_dc = -1; @@ -501,35 +869,37 @@ private void fillItems(ArrayList items, UniversalAdapter adapter) { items.add(UItem.asChart(StatisticActivity.VIEW_TYPE_STACKBAR, stats_dc, revenueChart)); items.add(UItem.asShadow(-2, null)); } + if (starsRevenueChart != null && !starsRevenueChart.isEmpty) { + items.add(UItem.asChart(StatisticActivity.VIEW_TYPE_STACKBAR, stats_dc, starsRevenueChart)); + items.add(UItem.asShadow(-3, null)); + } if (proceedsAvailable) { items.add(UItem.asBlackHeader(getString(R.string.MonetizationOverview))); items.add(UItem.asProceedOverview(availableValue)); items.add(UItem.asProceedOverview(lastWithdrawalValue)); items.add(UItem.asProceedOverview(lifetimeValue)); - items.add(UItem.asShadow(-3, null)); + items.add(UItem.asShadow(-4, null)); } if (chat != null && chat.creator) { items.add(UItem.asBlackHeader(getString(R.string.MonetizationBalance))); items.add(UItem.asCustom(balanceLayout)); - items.add(UItem.asShadow(-4, balanceInfo)); - } - if (!transactions.isEmpty() || transactionsTotalCount > 0) { - items.add(UItem.asBlackHeader(getString(R.string.MonetizationTransactions))); - for (TL_stats.BroadcastRevenueTransaction t : transactions) { - items.add(UItem.asTransaction(t)); - } - if (transactionsTotalCount - transactions.size() > 0) { - items.add(UItem.asButton(BUTTON_LOAD_MORE_TRANSACTIONS, R.drawable.arrow_more, formatPluralString("MonetizationMoreTransactions", transactionsTotalCount - transactions.size())).accent()); - } - items.add(UItem.asShadow(-5, null)); + items.add(UItem.asShadow(-5, balanceInfo)); } if (chat != null && chat.creator) { final int switchOffLevel = MessagesController.getInstance(currentAccount).channelRestrictSponsoredLevelMin; items.add(UItem.asCheck(CHECK_SWITCHOFF, PeerColorActivity.withLevelLock(getString(R.string.MonetizationSwitchOff), currentBoostLevel < switchOffLevel ? switchOffLevel : 0)).setChecked(currentBoostLevel >= switchOffLevel && switchOffValue)); - items.add(UItem.asShadow(-6, getString(R.string.MonetizationSwitchOffInfo))); + items.add(UItem.asShadow(-8, getString(R.string.MonetizationSwitchOffInfo))); + } + if (chat != null && chat.creator) { + items.add(UItem.asBlackHeader(getString(R.string.MonetizationStarsBalance))); + items.add(UItem.asCustom(STARS_BALANCE, starsBalanceLayout)); + items.add(UItem.asShadow(-6, starsBalanceInfo)); + } + if (transactionsLayout.hasTransactions()) { + items.add(UItem.asFullscreenCustom(transactionsLayout, 0)); + } else { + items.add(UItem.asShadow(-10, null)); } - items.add(UItem.asShadow(-7, null)); - items.add(UItem.asShadow(-8, null)); } private void onClick(UItem item, View view, int position, float x, float y) { @@ -549,55 +919,16 @@ private void onClick(UItem item, View view, int position, float x, float y) { AndroidUtilities.cancelRunOnUIThread(sendCpmUpdateRunnable); AndroidUtilities.runOnUIThread(sendCpmUpdateRunnable, 1000); listView.adapter.update(true); - } else if (item.object instanceof TL_stats.BroadcastRevenueTransaction) { - showTransactionSheet((TL_stats.BroadcastRevenueTransaction) item.object, dialogId); - } else if (item.id == BUTTON_LOAD_MORE_TRANSACTIONS) { - loadTransactions(); - } - } - - public void reloadTransactions() { - if (loadingTransactions) return; - transactions.clear(); - transactionsTotalCount = 0; - loadingTransactions = false; - loadTransactions(); - } - - private boolean loadingTransactions = false; - private void loadTransactions() { - if (loadingTransactions) return; - if (transactions.size() >= transactionsTotalCount && transactionsTotalCount != 0) return; - TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); - if (chat == null || !chat.creator) { - return; } - - loadingTransactions = true; - TL_stats.TL_getBroadcastRevenueTransactions req = new TL_stats.TL_getBroadcastRevenueTransactions(); - req.channel = MessagesController.getInstance(currentAccount).getInputChannel(-dialogId); - req.offset = transactions.size(); - req.limit = transactions.isEmpty() ? 5 : 20; - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { - if (res instanceof TL_stats.TL_broadcastRevenueTransactions) { - TL_stats.TL_broadcastRevenueTransactions r = (TL_stats.TL_broadcastRevenueTransactions) res; - transactionsTotalCount = r.count; - transactions.addAll(r.transactions); - - if (listView != null) { - listView.adapter.update(true); - } - loadingTransactions = false; - } else if (err != null) { - BulletinFactory.showError(err); - } - })); } private final Runnable sendCpmUpdateRunnable = this::sendCpmUpdate; private void sendCpmUpdate() { AndroidUtilities.cancelRunOnUIThread(sendCpmUpdateRunnable); + if (switchOffValue == initialSwitchOffValue) + return; + TLRPC.TL_channels_restrictSponsoredMessages req = new TLRPC.TL_channels_restrictSponsoredMessages(); req.channel = MessagesController.getInstance(currentAccount).getInputChannel(-dialogId); req.restricted = switchOffValue; @@ -605,6 +936,7 @@ private void sendCpmUpdate() { if (err != null) { BulletinFactory.showError(err); } else if (res instanceof TLRPC.Updates) { + initialSwitchOffValue = switchOffValue; MessagesController.getInstance(currentAccount).processUpdates((TLRPC.Updates) res, false); } })); @@ -660,8 +992,9 @@ public static class ProceedOverviewCell extends LinearLayout { private final Theme.ResourcesProvider resourcesProvider; private final LinearLayout layout; - private final AnimatedEmojiSpan.TextViewEmojis cryptoAmountView; - private final TextView amountView; + private final LinearLayout[] amountContainer = new LinearLayout[2]; + private final AnimatedEmojiSpan.TextViewEmojis[] cryptoAmountView = new AnimatedEmojiSpan.TextViewEmojis[2]; + private final TextView amountView[] = new TextView[2]; private final TextView titleView; private final DecimalFormat formatter; @@ -674,18 +1007,24 @@ public ProceedOverviewCell(Context context, Theme.ResourcesProvider resourcesPro layout = new LinearLayout(context); layout.setOrientation(HORIZONTAL); - addView(layout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 22, 9, 22, 0)); - - cryptoAmountView = new AnimatedEmojiSpan.TextViewEmojis(context); - cryptoAmountView.setTypeface(AndroidUtilities.bold()); - cryptoAmountView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - cryptoAmountView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); - layout.addView(cryptoAmountView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM, 0, 0, 5, 0)); - - amountView = new AnimatedEmojiSpan.TextViewEmojis(context); - amountView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); - amountView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText, resourcesProvider)); - layout.addView(amountView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); + addView(layout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 22, 9, 22, 0)); + + for (int i = 0; i < 2; ++i) { + amountContainer[i] = new LinearLayout(context); + amountContainer[i].setOrientation(HORIZONTAL); + layout.addView(amountContainer[i], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 1, Gravity.FILL)); + + cryptoAmountView[i] = new AnimatedEmojiSpan.TextViewEmojis(context); + cryptoAmountView[i].setTypeface(AndroidUtilities.bold()); + cryptoAmountView[i].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + cryptoAmountView[i].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + amountContainer[i].addView(cryptoAmountView[i], LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM, 0, 0, 5, 0)); + + amountView[i] = new AnimatedEmojiSpan.TextViewEmojis(context); + amountView[i].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); + amountView[i].setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText, resourcesProvider)); + amountContainer[i].addView(amountView[i], LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM)); + } titleView = new TextView(context); titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); @@ -702,13 +1041,34 @@ public ProceedOverviewCell(Context context, Theme.ResourcesProvider resourcesPro public void set(ProceedOverview value) { titleView.setText(value.text); - SpannableStringBuilder cryptoAmount = new SpannableStringBuilder(replaceTON("TON " + formatter.format(value.crypto_amount / 1_000_000_000.0), cryptoAmountView.getPaint(), .87f, true)); - int index = TextUtils.indexOf(cryptoAmount, "."); - if (index >= 0) { - cryptoAmount.setSpan(new RelativeSizeSpan(13f / 16f), index, cryptoAmount.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + for (int i = 0; i < 2; ++i) { + final String crypto_currency = i == 0 ? value.crypto_currency : value.crypto_currency2; + final long crypto_amount = i == 0 ? value.crypto_amount : value.crypto_amount2; + final long amount = i == 0 ? value.amount : value.amount2; + + if (i == 1 && !value.contains2) continue; + + CharSequence s = crypto_currency + " "; + if ("TON".equalsIgnoreCase(crypto_currency)) { + s += formatter.format(crypto_amount / 1_000_000_000.0); + s = replaceTON(s, cryptoAmountView[i].getPaint(), .87f, true); + } else if ("XTR".equalsIgnoreCase(crypto_currency)) { + s += LocaleController.formatNumber(crypto_amount, ' '); + s = StarsIntroActivity.replaceStarsWithPlain(s, .8f); + } else { + s += Long.toString(crypto_amount); + } + SpannableStringBuilder cryptoAmount = new SpannableStringBuilder(s); + if ("TON".equalsIgnoreCase(crypto_currency)) { + int index = TextUtils.indexOf(cryptoAmount, "."); + if (index >= 0) { + cryptoAmount.setSpan(new RelativeSizeSpan(13f / 16f), index, cryptoAmount.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + cryptoAmountView[i].setText(cryptoAmount); + amountView[i].setText("≈" + BillingController.getInstance().formatCurrency(amount, value.currency)); } - cryptoAmountView.setText(cryptoAmount); - amountView.setText("~" + BillingController.getInstance().formatCurrency(value.amount, value.currency)); } @Override @@ -718,13 +1078,29 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } public static class ProceedOverview { + + public String crypto_currency; public CharSequence text; public long crypto_amount; public long amount; public String currency; - public static ProceedOverview as(CharSequence text) { + public boolean contains2; + public String crypto_currency2; + public long crypto_amount2; + public long amount2; + + public static ProceedOverview as(String cryptoCurrency, CharSequence text) { ProceedOverview o = new ProceedOverview(); + o.crypto_currency = cryptoCurrency; + o.text = text; + return o; + } + + public static ProceedOverview as(String cryptoCurrency, String cryptoCurrency2, CharSequence text) { + ProceedOverview o = new ProceedOverview(); + o.crypto_currency = cryptoCurrency; + o.crypto_currency2 = cryptoCurrency2; o.text = text; return o; } @@ -907,11 +1283,11 @@ private static byte[] crc16(byte[] data) { return result; } - private void showTransactionSheet(TL_stats.BroadcastRevenueTransaction transaction, long dialogId) { - BottomSheet sheet = new BottomSheet(getContext(), false, resourcesProvider); + private static void showTransactionSheet(Context context, int currentAccount, TL_stats.BroadcastRevenueTransaction transaction, long dialogId, Theme.ResourcesProvider resourcesProvider) { + BottomSheet sheet = new BottomSheet(context, false, resourcesProvider); sheet.fixNavigationBar(); - LinearLayout layout = new LinearLayout(getContext()); + LinearLayout layout = new LinearLayout(context); layout.setOrientation(LinearLayout.VERTICAL); long dateFrom, dateTo; @@ -947,7 +1323,14 @@ private void showTransactionSheet(TL_stats.BroadcastRevenueTransaction transacti return; } - TextView textView = new TextView(getContext()); + DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); + symbols.setDecimalSeparator('.'); + final DecimalFormat formatter = new DecimalFormat("#.##", symbols); + formatter.setMinimumFractionDigits(2); + formatter.setMaximumFractionDigits(12); + formatter.setGroupingUsed(false); + + TextView textView = new TextView(context); textView.setGravity(Gravity.CENTER); textView.setTypeface(AndroidUtilities.bold()); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); @@ -963,7 +1346,7 @@ private void showTransactionSheet(TL_stats.BroadcastRevenueTransaction transacti textView.setText(amountText); layout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 24, 0, 6)); - textView = new TextView(getContext()); + textView = new TextView(context); textView.setGravity(Gravity.CENTER); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText, resourcesProvider)); @@ -982,7 +1365,7 @@ private void showTransactionSheet(TL_stats.BroadcastRevenueTransaction transacti } layout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 0, 0, 0)); - textView = new TextView(getContext()); + textView = new TextView(context); textView.setGravity(Gravity.CENTER); textView.setTypeface(AndroidUtilities.bold()); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); @@ -1007,7 +1390,7 @@ private void showTransactionSheet(TL_stats.BroadcastRevenueTransaction transacti // layout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 8, 0, 0)); // } else { if (transaction instanceof TL_stats.TL_broadcastRevenueTransactionProceeds) { - FrameLayout chipLayout = new FrameLayout(getContext()); + FrameLayout chipLayout = new FrameLayout(context); chipLayout.setBackground(Theme.createRoundRectDrawable(dp(28), dp(28), Theme.getColor(Theme.key_groupcreate_spanBackground, resourcesProvider))); String ownerName; @@ -1022,14 +1405,14 @@ private void showTransactionSheet(TL_stats.BroadcastRevenueTransaction transacti owner = user; } - BackupImageView chipAvatar = new BackupImageView(getContext()); + BackupImageView chipAvatar = new BackupImageView(context); chipAvatar.setRoundRadius(dp(28)); AvatarDrawable avatarDrawable = new AvatarDrawable(); avatarDrawable.setInfo(owner); chipAvatar.setForUserOrChat(owner, avatarDrawable); chipLayout.addView(chipAvatar, LayoutHelper.createFrame(28, 28, Gravity.LEFT | Gravity.TOP)); - TextView chipText = new TextView(getContext()); + TextView chipText = new TextView(context); chipText.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); chipText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); chipText.setSingleLine(); @@ -1039,12 +1422,12 @@ private void showTransactionSheet(TL_stats.BroadcastRevenueTransaction transacti layout.addView(chipLayout, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 28, Gravity.CENTER_HORIZONTAL, 42, 10, 42, 0)); } - ButtonWithCounterView button = new ButtonWithCounterView(getContext(), resourcesProvider); + ButtonWithCounterView button = new ButtonWithCounterView(context, resourcesProvider); if (transaction instanceof TL_stats.TL_broadcastRevenueTransactionWithdrawal && (((TL_stats.TL_broadcastRevenueTransactionWithdrawal) transaction).flags & 2) != 0) { TL_stats.TL_broadcastRevenueTransactionWithdrawal t = (TL_stats.TL_broadcastRevenueTransactionWithdrawal) transaction; button.setText(getString(R.string.MonetizationTransactionDetailWithdrawButton), false); button.setOnClickListener(v -> { - Browser.openUrl(getContext(), t.transaction_url); + Browser.openUrl(context, t.transaction_url); }); } else { button.setText(getString(R.string.OK), false); @@ -1055,8 +1438,7 @@ private void showTransactionSheet(TL_stats.BroadcastRevenueTransaction transacti layout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, Gravity.FILL_HORIZONTAL | Gravity.TOP, 18, 30, 18, 14)); sheet.setCustomView(layout); - - fragment.showDialog(sheet); + sheet.show(); } private void showLearnSheet() { @@ -1169,4 +1551,408 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(Math.min(MeasureSpec.getSize(widthMeasureSpec), dp(325)), MeasureSpec.getMode(widthMeasureSpec)), heightMeasureSpec); } } + + public static class ChannelTransactionsView extends LinearLayout { + + private final int currentAccount; + private final ViewPagerFixed viewPager; + private final PageAdapter adapter; + private final ViewPagerFixed.TabsView tabsView; + private final long dialogId; + private final Runnable updateParentList; + + public static final int STARS_TRANSACTIONS = 0; + public static final int TON_TRANSACTIONS = 1; + + private final ArrayList tonTransactions = new ArrayList<>(); + private int tonTransactionsTotalCount; + + private final ArrayList starsTransactions = new ArrayList<>(); + private String starsLastOffset = ""; + + private class PageAdapter extends ViewPagerFixed.Adapter { + + private final Context context; + private final int currentAccount; + private final int classGuid; + private final Theme.ResourcesProvider resourcesProvider; + private final long dialogId; + + public PageAdapter(Context context, int currentAccount, long dialogId, int classGuid, Theme.ResourcesProvider resourcesProvider) { + this.context = context; + this.currentAccount = currentAccount; + this.classGuid = classGuid; + this.resourcesProvider = resourcesProvider; + this.dialogId = dialogId; + fill(); + } + + private final ArrayList items = new ArrayList<>(); + + public void fill() { + items.clear(); + if (!tonTransactions.isEmpty()) + items.add(UItem.asSpace(TON_TRANSACTIONS)); + if (!starsTransactions.isEmpty()) + items.add(UItem.asSpace(STARS_TRANSACTIONS)); + } + + @Override + public int getItemCount() { + return items.size(); + } + + @Override + public View createView(int viewType) { + return new Page(context, dialogId, viewType, currentAccount, classGuid, () -> loadTransactions(viewType), resourcesProvider); + } + + @Override + public void bindView(View view, int position, int viewType) {} + + @Override + public int getItemViewType(int position) { + if (position < 0 || position >= items.size()) + return TON_TRANSACTIONS; + return items.get(position).intValue; + } + + @Override + public String getItemTitle(int position) { + final int viewType = getItemViewType(position); + switch (viewType) { + case STARS_TRANSACTIONS: return getString(R.string.MonetizationTransactionsStars); + case TON_TRANSACTIONS: return getString(R.string.MonetizationTransactionsTON); + default: return ""; + } + } + } + + public RecyclerListView getCurrentListView() { + View currentView = viewPager.getCurrentView(); + if (!(currentView instanceof Page)) return null; + return ((Page) currentView).listView; + } + + public ChannelTransactionsView(Context context, int currentAccount, long dialogId, int classGuid, Runnable updateList, Theme.ResourcesProvider resourcesProvider) { + super(context); + this.currentAccount = currentAccount; + this.dialogId = dialogId; + this.updateParentList = updateList; + + setOrientation(VERTICAL); + + viewPager = new ViewPagerFixed(context); + viewPager.setAdapter(adapter = new PageAdapter(context, currentAccount, dialogId, classGuid, resourcesProvider)); + tabsView = viewPager.createTabsView(true, 3); + + View separatorView = new View(context); + separatorView.setBackgroundColor(Theme.getColor(Theme.key_divider, resourcesProvider)); + + addView(tabsView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + addView(separatorView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 1f / AndroidUtilities.density)); + addView(viewPager, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + setBackgroundColor(Theme.getColor(Theme.key_dialogBackground, resourcesProvider)); + + loadTransactions(TON_TRANSACTIONS); + loadTransactions(STARS_TRANSACTIONS); + } + + private void updateTabs() { + adapter.fill(); + viewPager.fillTabs(false); + viewPager.updateCurrent(); + } + + public void reloadTransactions() { + final boolean hadTransactions = hasTransactions(); + for (int i = 0; i < 2; ++i) { + final int type = i; + if (loadingTransactions[type]) return; + if (type == TON_TRANSACTIONS) { + tonTransactions.clear(); + tonTransactionsTotalCount = 3; + } else { + starsTransactions.clear(); + starsLastOffset = ""; + } + loadingTransactions[type] = false; + loadTransactions(type); + } + if (hasTransactions() != hadTransactions && updateParentList != null) { + updateTabs(); + updateParentList.run(); + } + } + + private void updateLists(boolean animated) { + for (int i = 0; i < viewPager.getViewPages().length; ++i) { + View page = viewPager.getViewPages()[i]; + if (page instanceof Page) { + ((Page) page).listView.adapter.update(animated); + } + } + } + + public boolean hasTransactions() { + return !tonTransactions.isEmpty() || !starsTransactions.isEmpty(); + } + public boolean hasTransactions(int type) { + if (type == TON_TRANSACTIONS) return !tonTransactions.isEmpty(); + if (type == STARS_TRANSACTIONS) return !starsTransactions.isEmpty(); + return false; + } + + private boolean[] loadingTransactions = new boolean[] { false, false }; + private void loadTransactions(int type) { + if (loadingTransactions[type]) return; + + final boolean hadTransactions = hasTransactions(); + final boolean hadTheseTransactions = hasTransactions(type); + if (type == TON_TRANSACTIONS) { + if (tonTransactions.size() >= tonTransactionsTotalCount && tonTransactionsTotalCount != 0) + return; + loadingTransactions[type] = true; + TL_stats.TL_getBroadcastRevenueTransactions req = new TL_stats.TL_getBroadcastRevenueTransactions(); + req.channel = MessagesController.getInstance(currentAccount).getInputChannel(-dialogId); + req.offset = tonTransactions.size(); + req.limit = tonTransactions.isEmpty() ? 5 : 20; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TL_stats.TL_broadcastRevenueTransactions) { + TL_stats.TL_broadcastRevenueTransactions r = (TL_stats.TL_broadcastRevenueTransactions) res; + tonTransactionsTotalCount = r.count; + tonTransactions.addAll(r.transactions); + + updateLists(true); + loadingTransactions[type] = false; + } else if (err != null) { + BulletinFactory.showError(err); + } + if (hasTransactions() != hadTransactions && updateParentList != null) { + updateParentList.run(); + } + if (hasTransactions(type) != hadTheseTransactions) { + updateTabs(); + } + })); + } else if (type == STARS_TRANSACTIONS) { + if (starsLastOffset == null) + return; + loadingTransactions[type] = true; + TLRPC.TL_payments_getStarsTransactions req = new TLRPC.TL_payments_getStarsTransactions(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); + req.offset = starsLastOffset; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TLRPC.TL_payments_starsStatus) { + TLRPC.TL_payments_starsStatus r = (TLRPC.TL_payments_starsStatus) res; + MessagesController.getInstance(currentAccount).putUsers(r.users, false); + MessagesController.getInstance(currentAccount).putChats(r.chats, false); + starsTransactions.addAll(r.history); + starsLastOffset = r.next_offset; + updateLists(true); + loadingTransactions[type] = false; + } else if (err != null) { + BulletinFactory.showError(err); + } + if (hasTransactions() != hadTransactions && updateParentList != null) { + updateParentList.run(); + } + if (hasTransactions(type) != hadTheseTransactions) { + updateTabs(); + } + })); + } + } + + public class Page extends FrameLayout { + + private final UniversalRecyclerView listView; + private final Theme.ResourcesProvider resourcesProvider; + private final int currentAccount; + private final int type; + private final long bot_id; + private final Runnable loadMore; + + public Page(Context context, long bot_id, int type, int currentAccount, int classGuid, Runnable loadMore, Theme.ResourcesProvider resourcesProvider) { + super(context); + + this.type = type; + this.currentAccount = currentAccount; + this.bot_id = bot_id; + this.resourcesProvider = resourcesProvider; + this.loadMore = loadMore; + + listView = new UniversalRecyclerView(context, currentAccount, classGuid, true, this::fillItems, this::onClick, null, resourcesProvider); + addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + listView.setOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + scheduleLoadTransactions(); + } + }); + } + + private void scheduleLoadTransactions() { + if (!Page.this.listView.canScrollVertically(1)) { + AndroidUtilities.cancelRunOnUIThread(loadMore); + AndroidUtilities.runOnUIThread(loadMore, 250); + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + listView.adapter.update(false); + } + + private void fillItems(ArrayList items, UniversalAdapter adapter) { + if (type == STARS_TRANSACTIONS) { + for (TLRPC.StarsTransaction t : starsTransactions) { + items.add(StarsIntroActivity.StarsTransactionView.Factory.asTransaction(t, true)); + } + if (!TextUtils.isEmpty(starsLastOffset)) { + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + } + } else if (type == TON_TRANSACTIONS) { + for (TL_stats.BroadcastRevenueTransaction t : tonTransactions) { + items.add(UItem.asTransaction(t)); + } + if (tonTransactionsTotalCount - tonTransactions.size() > 0) { + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + } + } + } + + private void onClick(UItem item, View view, int position, float x, float y) { + if (item.object instanceof TLRPC.StarsTransaction) { + StarsIntroActivity.showTransactionSheet(getContext(), true, dialogId, currentAccount, (TLRPC.StarsTransaction) item.object, resourcesProvider); + } else if (item.object instanceof TL_stats.BroadcastRevenueTransaction) { + showTransactionSheet(getContext(), currentAccount, (TL_stats.BroadcastRevenueTransaction) item.object, dialogId, resourcesProvider); + } + } + + } + + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + private NestedScrollingParentHelper nestedScrollingParentHelper = new NestedScrollingParentHelper(this); + + @Override + public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, int[] consumed) { + try { + if (target == listView && transactionsLayout.isAttachedToWindow()) { + RecyclerListView innerListView = transactionsLayout.getCurrentListView(); + int bottom = ((View) transactionsLayout.getParent()).getBottom(); + if (actionBar != null) { + actionBar.setCastShadows(!isAttachedToWindow() || listView.getHeight() - bottom < 0); + } + if (listView.getHeight() - bottom >= 0) { + consumed[1] = dyUnconsumed; + innerListView.scrollBy(0, dyUnconsumed); + } + } + } catch (Throwable e) { + FileLog.e(e); + AndroidUtilities.runOnUIThread(() -> { + try { + RecyclerListView innerListView = transactionsLayout.getCurrentListView(); + if (innerListView != null && innerListView.getAdapter() != null) { + innerListView.getAdapter().notifyDataSetChanged(); + } + } catch (Throwable e2) { + + } + }); + } + } + + @Override + public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) { + + } + + @Override + public boolean onNestedPreFling(View target, float velocityX, float velocityY) { + return super.onNestedPreFling(target, velocityX, velocityY); + } + + @Override + public void onNestedPreScroll(View target, int dx, int dy, int[] consumed, int type) { + if (target == listView && transactionsLayout.isAttachedToWindow()) { + boolean searchVisible = false; + int t = ((View) transactionsLayout.getParent()).getTop() - AndroidUtilities.statusBarHeight - ActionBar.getCurrentActionBarHeight(); + int bottom = ((View) transactionsLayout.getParent()).getBottom(); + if (dy < 0) { + boolean scrolledInner = false; + if (actionBar != null) { + actionBar.setCastShadows(!isAttachedToWindow() || listView.getHeight() - bottom < 0); + } + if (listView.getHeight() - bottom >= 0) { + RecyclerListView innerListView = transactionsLayout.getCurrentListView(); + LinearLayoutManager linearLayoutManager = (LinearLayoutManager) innerListView.getLayoutManager(); + int pos = linearLayoutManager.findFirstVisibleItemPosition(); + if (pos != RecyclerView.NO_POSITION) { + RecyclerView.ViewHolder holder = innerListView.findViewHolderForAdapterPosition(pos); + int top = holder != null ? holder.itemView.getTop() : -1; + int paddingTop = innerListView.getPaddingTop(); + if (top != paddingTop || pos != 0) { + consumed[1] = pos != 0 ? dy : Math.max(dy, (top - paddingTop)); + innerListView.scrollBy(0, dy); + scrolledInner = true; + } + } + } + if (searchVisible) { + if (!scrolledInner && t < 0) { + consumed[1] = dy - Math.max(t, dy); + } else { + consumed[1] = dy; + } + } + } else { + if (searchVisible) { + RecyclerListView innerListView = transactionsLayout.getCurrentListView(); + consumed[1] = dy; + if (t > 0) { + consumed[1] -= dy; + } + if (innerListView != null && consumed[1] > 0) { + innerListView.scrollBy(0, consumed[1]); + } + } + } + } + } + + @Override + public boolean onStartNestedScroll(View child, View target, int axes, int type) { + return axes == ViewCompat.SCROLL_AXIS_VERTICAL; + } + + @Override + public void onNestedScrollAccepted(View child, View target, int axes, int type) { + nestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes); + } + + @Override + public void onStopNestedScroll(View target, int type) { + nestedScrollingParentHelper.onStopNestedScroll(target); + } + + @Override + public void onStopNestedScroll(View child) { + + } + + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Charts/BarChartView.java b/TMessagesProj/src/main/java/org/telegram/ui/Charts/BarChartView.java index 4109cb1463..9eb5762a79 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Charts/BarChartView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Charts/BarChartView.java @@ -3,6 +3,7 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; +import android.util.Log; import androidx.core.graphics.ColorUtils; @@ -105,12 +106,12 @@ protected void drawChart(Canvas canvas) { line.lineColor, line.blendColor, 0)); } - paint.setAlpha((int) (transitionAlpha * 255)); + paint.setAlpha((int) (0xFF * transitionAlpha)); canvas.drawLines(line.linesPath, 0, j, paint); if (selected) { line.paint.setStrokeWidth(p); - line.paint.setAlpha((int) (transitionAlpha * 255)); + line.paint.setAlpha((int) (0xFF * transitionAlpha)); canvas.drawLine(selectedX, selectedY, selectedX, getMeasuredHeight() - chartBottom, line.paint @@ -179,7 +180,7 @@ protected void drawSelection(Canvas canvas) { @Override public BarViewData createLineViewData(ChartData.Line line) { - return new BarViewData(line); + return new BarViewData(line, resourcesProvider); } @Override diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Charts/BaseChartView.java b/TMessagesProj/src/main/java/org/telegram/ui/Charts/BaseChartView.java index 6b4928d770..6a5fc6dfc4 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Charts/BaseChartView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Charts/BaseChartView.java @@ -1126,7 +1126,7 @@ public void animateLegend(boolean show) { } public void moveLegend(float offset) { - if (chartData == null || selectedIndex == -1 || !legendShowing) return; + if (chartData == null || selectedIndex < 0 || selectedIndex >= chartData.x.length || !legendShowing) return; legendSignatureView.setData(selectedIndex, chartData.x[selectedIndex], (ArrayList) lines, false, chartData.yTooltipFormatter, chartData.yRate); legendSignatureView.setVisibility(VISIBLE); legendSignatureView.measure( @@ -1210,7 +1210,7 @@ public void setData(T chartData) { pickerMaxHeight = 0; pickerMinHeight = Integer.MAX_VALUE; initPickerMaxHeight(); - if (chartData.yTooltipFormatter == ChartData.FORMATTER_TON) { + if (chartData.yTooltipFormatter == ChartData.FORMATTER_TON || chartData.yTooltipFormatter == ChartData.FORMATTER_XTR) { legendSignatureView.setSize(2 * lines.size()); } else { legendSignatureView.setSize(lines.size()); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Charts/LinearBarChartView.java b/TMessagesProj/src/main/java/org/telegram/ui/Charts/LinearBarChartView.java index b4b92a5268..3c31ad2fa0 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Charts/LinearBarChartView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Charts/LinearBarChartView.java @@ -3,6 +3,7 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; +import android.util.Log; import org.telegram.ui.Charts.data.ChartData; import org.telegram.ui.Charts.view_data.LineViewData; @@ -111,7 +112,7 @@ protected void drawChart(Canvas canvas) { transitionAlpha = transitionParams.progress; } line.paint.setAlpha((int) (255 * line.alpha * transitionAlpha)); - if(endXIndex - startXIndex > 100){ + if (endXIndex - startXIndex > 100) { line.paint.setStrokeCap(Paint.Cap.SQUARE); } else { line.paint.setStrokeCap(Paint.Cap.ROUND); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Charts/data/ChartData.java b/TMessagesProj/src/main/java/org/telegram/ui/Charts/data/ChartData.java index eded41e50a..983b57eccf 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Charts/data/ChartData.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Charts/data/ChartData.java @@ -10,6 +10,7 @@ import org.json.JSONObject; import org.telegram.messenger.SegmentTree; import org.telegram.ui.ActionBar.ThemeColors; +import org.telegram.ui.Stars.StarsController; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -30,6 +31,7 @@ public class ChartData { public float oneDayPercentage = 0f; public static final int FORMATTER_TON = 1; + public static final int FORMATTER_XTR = 2; public int xTickFormatter = 0; public int xTooltipFormatter = 0; @@ -112,6 +114,7 @@ public ChartData(JSONObject jsonObject) throws JSONException { public int getFormatter(String value) { if (TextUtils.isEmpty(value)) return 0; if (value.contains("TON")) return FORMATTER_TON; + if (value.contains(StarsController.currency)) return FORMATTER_XTR; return 0; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/BarViewData.java b/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/BarViewData.java index f678131b19..cccc5650fb 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/BarViewData.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/BarViewData.java @@ -2,17 +2,23 @@ import android.graphics.Paint; +import androidx.core.graphics.ColorUtils; + +import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Charts.data.ChartData; public class BarViewData extends LineViewData { + private Theme.ResourcesProvider resourcesProvider; + public final Paint unselectedPaint = new Paint(); public int blendColor = 0; - public BarViewData(ChartData.Line line) { + public BarViewData(ChartData.Line line, Theme.ResourcesProvider resourcesProvider) { super(line, false); + this.resourcesProvider = resourcesProvider; paint.setStyle(Paint.Style.STROKE); unselectedPaint.setStyle(Paint.Style.STROKE); paint.setAntiAlias(false); @@ -20,5 +26,6 @@ public BarViewData(ChartData.Line line) { public void updateColors() { super.updateColors(); + blendColor = ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider), lineColor,0.3f); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/ChartHorizontalLinesData.java b/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/ChartHorizontalLinesData.java index b44114a5b2..f7e77ebae6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/ChartHorizontalLinesData.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/ChartHorizontalLinesData.java @@ -9,10 +9,12 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BillingController; +import org.telegram.messenger.LocaleController; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ChannelMonetizationLayout; import org.telegram.ui.Charts.data.ChartData; import org.telegram.ui.Components.AnimatedEmojiSpan; +import org.telegram.ui.Stars.StarsIntroActivity; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -71,7 +73,7 @@ public ChartHorizontalLinesData( if (k > 0) { float v2 = (values[i] / k); if (skipFloatValues) { - if (v2 - ((long) v2) < 0.01f || formatter == ChartData.FORMATTER_TON) { + if (v2 - ((long) v2) < 0.01f || formatter == ChartData.FORMATTER_TON || formatter == ChartData.FORMATTER_XTR) { valuesStr2[i] = format(1, secondTextPaint, (long) v2, formatter); } else { valuesStr2[i] = ""; @@ -119,7 +121,7 @@ public ChartHorizontalLinesData( if (k > 0) { float v = (values[i] / k); if (skipFloatValues) { - if (v - ((long) v) < 0.01f || formatter == ChartData.FORMATTER_TON) { + if (v - ((long) v) < 0.01f || formatter == ChartData.FORMATTER_TON || formatter == ChartData.FORMATTER_XTR) { valuesStr2[i] = format(1, secondTextPaint, (long) v, formatter); } else { valuesStr2[i] = ""; @@ -136,7 +138,7 @@ public ChartHorizontalLinesData( public CharSequence format(int a, TextPaint paint, long v, int formatter) { if (formatter == ChartData.FORMATTER_TON) { if (a == 1) { - return "~" + BillingController.getInstance().formatCurrency(v, "USD"); + return "≈" + BillingController.getInstance().formatCurrency(v, "USD"); } if (formatterTON == null) { DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.US); @@ -148,6 +150,11 @@ public CharSequence format(int a, TextPaint paint, long v, int formatter) { } formatterTON.setMaximumFractionDigits(v > 1_000_000_000 ? 2 : 6); return ChannelMonetizationLayout.replaceTON("TON " + formatterTON.format(v / 1_000_000_000.0), paint, .8f, -dp(.66f), false); + } else if (formatter == ChartData.FORMATTER_XTR) { + if (a == 1) { + return "≈" + BillingController.getInstance().formatCurrency(v, "USD"); + } + return StarsIntroActivity.replaceStarsWithPlain("XTR " + LocaleController.formatNumber(v, ' '), .65f); } return AndroidUtilities.formatWholeNumber((int) v, 0); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LegendSignatureView.java b/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LegendSignatureView.java index 345d325a5a..fe53cfbf98 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LegendSignatureView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Charts/view_data/LegendSignatureView.java @@ -28,6 +28,7 @@ import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.RadialProgressView; +import org.telegram.ui.Stars.StarsIntroActivity; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -177,7 +178,7 @@ public void setData( for (int i = 0; i < n; i++) { Holder h = holders[i]; int formatterIndex = i % 2; - LineViewData l = lines.get(formatter == ChartData.FORMATTER_TON ? i / 2 : i); + LineViewData l = lines.get(formatter == ChartData.FORMATTER_TON || formatter == ChartData.FORMATTER_XTR ? i / 2 : i); if (!l.enabled) { h.root.setVisibility(View.GONE); @@ -188,7 +189,9 @@ public void setData( h.root.setVisibility(View.VISIBLE); h.value.setText(formatWholeNumber(l.line.y[index], formatter, formatterIndex, h.value, k)); if (formatter == ChartData.FORMATTER_TON) { - h.signature.setText(LocaleController.formatString(formatterIndex == 0 ? R.string.MonetizationChartInTON : R.string.MonetizationChartInUSD, l.line.name)); + h.signature.setText(LocaleController.formatString(formatterIndex == 0 ? R.string.ChartInTON : R.string.ChartInUSD, l.line.name)); + } else if (formatter == ChartData.FORMATTER_XTR) { + h.signature.setText(StarsIntroActivity.replaceStarsWithPlain(LocaleController.formatString(formatterIndex == 0 ? R.string.ChartInXTR : R.string.ChartInUSD, l.line.name), .7f)); } else { h.signature.setText(l.line.name); } @@ -247,7 +250,13 @@ public CharSequence formatWholeNumber(long v, int formatter, int formatterIndex, formatterTON.setMaximumFractionDigits(v > 1_000_000_000 ? 2 : 6); return ChannelMonetizationLayout.replaceTON("TON " + formatterTON.format(v / 1_000_000_000.), textView.getPaint(), .82f, false); } else { - return "~" + BillingController.getInstance().formatCurrency((long) (v / k), "USD"); + return "≈" + BillingController.getInstance().formatCurrency((long) (v / k), "USD"); + } + } else if (formatter == ChartData.FORMATTER_XTR) { + if (formatterIndex == 0) { + return StarsIntroActivity.replaceStarsWithPlain("XTR " + LocaleController.formatNumber(v, ' '), .7f); + } else { + return "≈" + BillingController.getInstance().formatCurrency((long) (v / k), "USD"); } } float num_ = v; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java index 413a19d2b5..c796d425e7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java @@ -103,7 +103,6 @@ import android.widget.LinearLayout; import android.widget.Space; import android.widget.TextView; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -241,7 +240,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.Stars.StarsController; import org.telegram.ui.Stars.StarsIntroActivity; +import org.telegram.ui.Stories.SelfStoryViewsPage; import org.telegram.ui.Stories.StoriesListPlaceProvider; import org.telegram.ui.Stories.StoriesUtilities; import org.telegram.ui.Stories.recorder.HintView2; @@ -249,6 +250,7 @@ import org.telegram.ui.Stories.recorder.StoryEntry; import org.telegram.ui.Stories.recorder.StoryRecorder; import org.telegram.ui.bots.BotCommandsMenuView; +import org.telegram.ui.bots.BotWebViewAttachedSheet; import org.telegram.ui.bots.BotWebViewSheet; import java.io.BufferedReader; @@ -647,7 +649,7 @@ public ArrayList getFilteredMessages() { private boolean ignoreAttachOnPause; - private boolean allowStickersPanel; + private boolean allowStickersPanel = true; private boolean allowContextBotPanel; private boolean allowContextBotPanelSecond = true; private AnimatorSet runningAnimation; @@ -868,12 +870,19 @@ public int getColor(int key) { destroyTextureView(); }; + private Bitmap scrimBlurBitmap; + private BitmapShader scrimBlurBitmapShader; + private Paint scrimBlurBitmapPaint; + private Matrix scrimBlurMatrix; + private Paint scrimPaint; private Paint actionBarBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private float scrimPaintAlpha = 0f; private View scrimView; private float scrimViewAlpha = 1f; + private float scrimViewProgress = 0f; private Integer scrimViewReaction; + private int scrimViewReactionOffset; private boolean scrimViewReactionAnimated; private int popupAnimationIndex = -1; private AnimatorSet scrimAnimatorSet; @@ -1040,6 +1049,7 @@ public void run() { private final static int OPTION_SPEED_PROMO = 103; private final static int OPTION_OPEN_PROFILE = 104; private final static int OPTION_FACT_CHECK = 106; + private final static int OPTION_EDIT_PRICE = 107; private final static int[] allowedNotificationsDuringChatListAnimations = new int[]{ NotificationCenter.messagesRead, @@ -1175,7 +1185,9 @@ public void showHeaderItem(boolean show) { headerItem.setVisibility(View.GONE); } } - + if (avatarContainer != null) { + avatarContainer.ignoreTouches = !show; + } } public long getTopicId() { @@ -1272,7 +1284,9 @@ public TextView getOrCreateWebBotTitleView() { webBotTitle.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); webBotTitle.setTypeface(AndroidUtilities.bold()); webBotTitle.setGravity(Gravity.CENTER_VERTICAL); - actionBar.addView(webBotTitle, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 72, 0, 72, 0)); + webBotTitle.setSingleLine(true); + webBotTitle.setEllipsize(TextUtils.TruncateAt.END); + actionBar.addView(webBotTitle, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 72, 0, 72 + 34, 0)); } return webBotTitle; } @@ -1302,7 +1316,7 @@ default void onReport() { @Override public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { - return ChatActivity.this.getPlaceForPhoto(messageObject, fileLocation, needPreview, false); + return ChatActivity.this.getPlaceForPhoto(messageObject, fileLocation, index, needPreview, false); } @Override @@ -1311,6 +1325,24 @@ public boolean validateGroupId(long groupId) { return groupedMessages != null && groupedMessages.messages.size() > 1; } }; + private PhotoViewer.PhotoViewerProvider photoViewerPaidMediaProvider = new PhotoViewer.EmptyPhotoViewerProvider() { + + @Override + public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { + return ChatActivity.this.getPlaceForPhoto(messageObject, fileLocation, index, needPreview, false); + } + + @Override + public boolean validateGroupId(long groupId) { + MessageObject.GroupedMessages groupedMessages = groupedMessagesMap.get(groupId); + return groupedMessages != null && groupedMessages.messages.size() > 1; + } + + @Override + public boolean forceAllInGroup() { + return true; + } + }; private ArrayList botContextResults; private PhotoViewer.PhotoViewerProvider botContextProvider = new PhotoViewer.EmptyPhotoViewerProvider() { @@ -2033,7 +2065,7 @@ public void onWindowSizeChanged(int size) { } } else { allowStickersPanel = true; - if (suggestEmojiPanel.getVisibility() == View.INVISIBLE) { + if (suggestEmojiPanel.getVisibility() == View.INVISIBLE && !isInPreviewMode()) { suggestEmojiPanel.setVisibility(View.VISIBLE); } } @@ -2120,11 +2152,11 @@ public void onStickersExpandedChange() { AndroidUtilities.requestAdjustResize(getParentActivity(), classGuid); } if (mentionContainer != null) { - mentionContainer.animate().alpha(chatActivityEnterView.isStickersExpanded() ? 0 : 1f).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); + mentionContainer.animate().alpha(chatActivityEnterView.isStickersExpanded() || isInPreviewMode() ? 0 : 1f).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); } if (suggestEmojiPanel != null) { suggestEmojiPanel.setVisibility(View.VISIBLE); - suggestEmojiPanel.animate().alpha(chatActivityEnterView.isStickersExpanded() ? 0 : 1f).setInterpolator(CubicBezierInterpolator.DEFAULT).withEndAction(() -> { + suggestEmojiPanel.animate().alpha(chatActivityEnterView.isStickersExpanded() || isInPreviewMode() ? 0 : 1f).setInterpolator(CubicBezierInterpolator.DEFAULT).withEndAction(() -> { if (suggestEmojiPanel != null && chatActivityEnterView.isStickersExpanded()) { suggestEmojiPanel.setVisibility(View.GONE); } @@ -2754,6 +2786,7 @@ protected void updateSearchingHashtag(String hashtag) { showMessagesSearchListView(true); searchingHashtag = hashtag; searchingQuery = searchingHashtag; + checkHashtagStories(false); clearChatData(true); startMessageAppearTransitionMs = 0; firstMessagesLoaded = false; @@ -3086,12 +3119,20 @@ protected Theme.ResourcesProvider getResourcesProvider() { @Override protected boolean canShowQuote() { + final boolean noforwards = ( + chatActivity != null && chatActivity.getMessagesController().isChatNoForwards(chatActivity.getCurrentChat()) || + selectedView != null && selectedView.getMessageObject() != null && selectedView.getMessageObject().messageOwner != null && selectedView.getMessageObject().messageOwner.noforwards + ); return !isFactCheck && ( - chatActivity != null && chatActivity.getCurrentEncryptedChat() == null && !chatActivity.textSelectionHelper.isDescription && - selectedView != null && selectedView.getMessageObject() != null && selectedView.getMessageObject().type != MessageObject.TYPE_STORY && - !selectedView.getMessageObject().isVoiceTranscriptionOpen() && !selectedView.getMessageObject().isInvoice() && + chatActivity != null && chatActivity.getCurrentEncryptedChat() == null && + (selectedView == null || + selectedView.getMessageObject() != null && selectedView.getMessageObject().type != MessageObject.TYPE_STORY && + !selectedView.getMessageObject().isVoiceTranscriptionOpen() && !selectedView.getMessageObject().isInvoice() && + !chatActivity.textSelectionHelper.isDescription + ) && !chatActivity.getMessagesController().getTranslateController().isTranslatingDialog(chatActivity.dialog_id) && - !UserObject.isService(chatActivity.dialog_id) + !UserObject.isService(chatActivity.dialog_id) && + (!noforwards || (chatActivity.getCurrentChat() == null || ChatObject.canWriteToChat(chatActivity.getCurrentChat()))) ); } @@ -6252,6 +6293,7 @@ public boolean onTouchEvent(MotionEvent event) { @Override protected void onDraw(Canvas canvas) { + if (scrimBlurBitmap != null) return; float clipTop = chatListView.getY() + chatListViewPaddingTop - getY(); clipTop -= AndroidUtilities.dp(4); if (clipTop > 0) { @@ -6541,7 +6583,7 @@ protected void onContextClick(TLRPC.BotInlineResult result) { int[] size = MessageObject.getInlineResultWidthAndHeight(result); EmbedBottomSheet.show(ChatActivity.this, null, botContextProvider, result.title != null ? result.title : "", result.description, result.content.url, result.content.url, size[0], size[1], isKeyboardVisible()); } else { - processExternalUrl(0, result.content.url, null, null, false); + processExternalUrl(0, result.content.url, null, null, false, false); } } @@ -6549,11 +6591,20 @@ protected void onContextClick(TLRPC.BotInlineResult result) { @Override protected void onScrolled(boolean atTop, boolean atBottom) { if (wasAtTop != atTop) { - AndroidUtilities.updateViewShow(suggestEmojiPanel, atTop, false, true); + AndroidUtilities.updateViewShow(suggestEmojiPanel, !isInPreviewMode() && atTop, false, true); wasAtTop = atTop; } } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (getAlpha() <= 0f) return false; + return super.dispatchTouchEvent(ev); + } }; + if (isInPreviewMode()) { + mentionContainer.setAlpha(0f); + } contentView.addView(mentionContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 110, Gravity.LEFT | Gravity.BOTTOM)); contentPreviewViewerDelegate = new ContentPreviewViewer.ContentPreviewViewerDelegate() { @@ -6971,11 +7022,28 @@ public void getOutline(View view, Outline outline) { messagesSearchListContainer.setAlpha(0.0f); contentView.addView(messagesSearchListContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 48)); - messagesSearchListView = new RecyclerListView(context, themeDelegate); + messagesSearchListView = new RecyclerListView(context, themeDelegate) { + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (messagesSearchAdapter != null) { + messagesSearchAdapter.attach(); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (messagesSearchAdapter != null) { + messagesSearchAdapter.detach(); + } + } + }; LinearLayoutManager messagesSearchLayoutManager = new LinearLayoutManager(context); messagesSearchLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); messagesSearchListView.setLayoutManager(messagesSearchLayoutManager); messagesSearchListView.setAdapter(messagesSearchAdapter = new MessagesSearchAdapter(context, themeDelegate, searchType, dialog_id == getUserConfig().getClientUserId())); + checkHashtagStories(true); DefaultItemAnimator itemAnimator = new DefaultItemAnimator(); itemAnimator.setSupportsChangeAnimations(false); itemAnimator.setDelayAnimations(false); @@ -6986,7 +7054,12 @@ public void getOutline(View view, Outline outline) { messagesSearchListView.setOnItemClickListener((view, position) -> { if (chatMode == MODE_SEARCH) { Object obj = messagesSearchAdapter.getItem(position); - if (obj instanceof MessageObject) { + if (position == 0 && messagesSearchAdapter.containsStories) { + Bundle args = new Bundle(); + args.putInt("type", MediaActivity.TYPE_STORIES_SEARCH); + args.putString("hashtag", messagesSearchAdapter.storiesList.query); + presentFragment(new MediaActivity(args, null)); + } else if (obj instanceof MessageObject) { openMessageInOriginalDialog((MessageObject) obj); } } else if (searchingReaction != null) { @@ -7543,6 +7616,20 @@ public void setVisibility(int visibility) { } } }); + replyLayout.setOnLongClickListener(v -> { + if (fieldPanelShown == 1 && editingMessageObject != null) { + scrollToMessageId(editingMessageObject.getId(), 0, true, 0, true, 0); + return true; + } else if (messagePreviewParams != null) { + if (fieldPanelShown == 2) { + if (replyingMessageObject != null) { + scrollToMessageId(replyingMessageObject.getId(), 0, true, 0, true, 0); + return true; + } + } + } + return false; + }); replyIconImageView = new ImageView(context); replyIconImageView.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_chat_replyPanelIcons), PorterDuff.Mode.MULTIPLY)); @@ -7637,6 +7724,7 @@ public void draw(Canvas canvas) { suggestEmojiPanel = new SuggestEmojiView(context, currentAccount, chatActivityEnterView, themeDelegate), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 160, Gravity.LEFT | Gravity.BOTTOM) ); + suggestEmojiPanel.setVisibility(allowStickersPanel && !isInPreviewMode() && (chatActivityEnterView == null || !chatActivityEnterView.isStickersExpanded()) ? View.VISIBLE : View.GONE); final ChatActivityEnterTopView.EditView editView = new ChatActivityEnterTopView.EditView(context); editView.setMotionEventSplittingEnabled(false); @@ -9593,13 +9681,17 @@ private void playReactionAnimation(Integer messageId) { } private void dimBehindView(View view, boolean enable) { + dimBehindView(view, false, enable); + } + + private void dimBehindView(View view, boolean blur, boolean enable) { setScrimView(view); - dimBehindView(enable ? 0.2f : 0, view != reactionsMentiondownButton && view != mentiondownButton); + dimBehindView(enable ? 0.2f : 0, blur, view != reactionsMentiondownButton && view != mentiondownButton); } private void dimBehindView(View view, float value) { setScrimView(view); - dimBehindView(value, view != reactionsMentiondownButton && view != mentiondownButton); + dimBehindView(value, false, view != reactionsMentiondownButton && view != mentiondownButton); } private void setScrimView(View scrimView) { @@ -9617,7 +9709,7 @@ private void setScrimView(View scrimView) { } } public void dimBehindView(boolean enable) { - dimBehindView(enable ? 0.2f : 0, true); + dimBehindView(enable ? 0.2f : 0, false, true); } private void checkInstantCameraView() { @@ -9633,7 +9725,7 @@ protected void clipBlur(Canvas canvas) { contentView.addView(instantCameraView, 21, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); } - private void dimBehindView(float value, boolean hidePagedownButtons) { + private void dimBehindView(float value, boolean blur, boolean hidePagedownButtons) { boolean enable = value > 0; if (scrimView instanceof ChatMessageCell) { ChatMessageCell cell = (ChatMessageCell) scrimView; @@ -9651,17 +9743,35 @@ private void dimBehindView(float value, boolean hidePagedownButtons) { scrimAnimatorSet = new AnimatorSet(); ArrayList animators = new ArrayList<>(); ValueAnimator scrimPaintAlphaAnimator; + final float max = Math.max(scrimPaintAlpha, value); if (enable) { scrimViewAlpha = 1f; + scrimViewProgress = 0; if (scrimViewAlphaAnimator != null) { scrimViewAlphaAnimator.cancel(); } animators.add(scrimPaintAlphaAnimator = ValueAnimator.ofFloat(0, value)); + + if (blur) { + AndroidUtilities.makeGlobalBlurBitmap(bitmap -> { + scrimBlurBitmap = bitmap; + + scrimBlurBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + scrimBlurBitmapPaint.setShader(scrimBlurBitmapShader = new BitmapShader(scrimBlurBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); + ColorMatrix colorMatrix = new ColorMatrix(); + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, Theme.isCurrentThemeDark() ? .08f : +.25f); + AndroidUtilities.adjustBrightnessColorMatrix(colorMatrix, Theme.isCurrentThemeDark() ? -.02f : -.07f); + scrimBlurBitmapPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); + scrimBlurMatrix = new Matrix(); + }, 14); + } } else { + scrimViewProgress = scrimPaintAlpha / max; animators.add(scrimPaintAlphaAnimator = ValueAnimator.ofFloat(scrimPaintAlpha, 0)); } scrimPaintAlphaAnimator.addUpdateListener(a -> { scrimPaintAlpha = (float) a.getAnimatedValue(); + scrimViewProgress = scrimPaintAlpha / max; if (fragmentView != null) { fragmentView.invalidate(); } @@ -9679,10 +9789,20 @@ private void dimBehindView(float value, boolean hidePagedownButtons) { } scrimAnimatorSet.playTogether(animators); scrimAnimatorSet.setDuration(enable ? 150 : 220); + final ChatMessageCell cell = scrimView instanceof ChatMessageCell ? (ChatMessageCell) scrimView : null; scrimAnimatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (!enable) { + if (scrimBlurBitmap != null) { + scrimBlurBitmapShader = null; + scrimBlurBitmapPaint = null; + scrimBlurBitmap.recycle(); + scrimBlurBitmap = null; + } + if (cell != null) { + cell.invalidate(); + } setScrimView(null); scrimViewReaction = null; contentView.invalidate(); @@ -10858,9 +10978,9 @@ private void updateChatListViewTopPadding() { if (pinnedMessageView != null && pinnedMessageView.getVisibility() == View.VISIBLE) { pinnedViewH = Math.max(0, AndroidUtilities.dp(48) + pinnedMessageEnterOffset); } - if (isThreadChat() && (!isTopic || pinnedOnlyStarterMessage())) { - pinnedViewH = 0; - } +// if (isThreadChat() && (!isTopic || pinnedOnlyStarterMessage())) { +// pinnedViewH = 0; +// } if (actionBarSearchTags != null) { pinnedViewH = Math.max(pinnedViewH, actionBarSearchTags.getCurrentHeight()); } @@ -11503,10 +11623,26 @@ public void jumpToDate(int date) { private boolean approved; public void processInlineBotWebView(TLRPC.TL_inlineBotWebView object) { final Runnable open = () -> { - BotWebViewSheet webViewSheet = new BotWebViewSheet(getContext(), getResourceProvider()); - webViewSheet.setParentActivity(getParentActivity()); - webViewSheet.requestWebView(currentAccount, currentUser != null ? currentUser.id : currentChat.id, mentionContainer.getAdapter().getFoundContextBot().id, object.text, object.url, BotWebViewSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON, 0, false, BotWebViewSheet.FLAG_FROM_INLINE_SWITCH); - showDialog(webViewSheet); + final BotWebViewAttachedSheet.WebViewRequestProps props = BotWebViewAttachedSheet.WebViewRequestProps.of(currentAccount, currentUser != null ? currentUser.id : currentChat.id, mentionContainer.getAdapter().getFoundContextBot().id, object.text, object.url, BotWebViewSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON, 0, false, null, false, null, null, BotWebViewSheet.FLAG_FROM_INLINE_SWITCH, false); + if (LaunchActivity.instance != null && LaunchActivity.instance.getBottomSheetTabs() != null && LaunchActivity.instance.getBottomSheetTabs().tryReopenTab(props) != null) { + return; + } + if (AndroidUtilities.isTablet()) { + BotWebViewSheet webViewSheet = new BotWebViewSheet(getContext(), getResourceProvider()); + webViewSheet.setDefaultFullsize(false); + webViewSheet.setNeedsContext(true); + webViewSheet.setParentActivity(getParentActivity()); + webViewSheet.requestWebView(null, props); + webViewSheet.show(); + } else { + BotWebViewAttachedSheet webViewSheet = createBotViewer(); + webViewSheet.setDefaultFullsize(false); + webViewSheet.setNeedsContext(true); + webViewSheet.setParentActivity(getParentActivity()); + webViewSheet.requestWebView(null, props); + webViewSheet.show(); + } + }; if (approved) { open.run(); @@ -11611,6 +11747,7 @@ public void didPressedButton(int button, boolean arg, boolean notify, int schedu info.canDeleteAfter = photoEntry.canDeleteAfter; info.updateStickersOrder = SendMessagesHelper.checkUpdateStickersOrder(photoEntry.caption); info.hasMediaSpoilers = photoEntry.hasSpoiler; + info.stars = photoEntry.starsAmount; photos.add(info); photoEntry.reset(); } @@ -13552,11 +13689,11 @@ public void showFieldPanel(boolean show, MessageObject messageObjectToReply, Mes if (canEditMedia) { replyObjectTextView.setText(LocaleController.getString("EditMessageMedia", R.string.EditMessageMedia)); } else if (messageObjectToEdit.messageText != null || messageObjectToEdit.caption != null) { - String mess = messageObjectToEdit.caption != null ? messageObjectToEdit.caption.toString() : messageObjectToEdit.messageText.toString(); + CharSequence mess = messageObjectToEdit.caption != null ? messageObjectToEdit.caption : messageObjectToEdit.messageText; if (mess.length() > 150) { - mess = mess.substring(0, 150); + mess = mess.subSequence(0, 150); } - mess = mess.replace('\n', ' '); + mess = AndroidUtilities.replaceNewLines(mess); Spannable cs = new SpannableStringBuilder(Emoji.replaceEmoji(mess, replyObjectTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(14), false)); MediaDataController.addTextStyleRuns(messageObjectToEdit, cs); if (messageObjectToEdit.messageOwner != null) { @@ -15060,6 +15197,7 @@ private void removeSelectedMessageHighlight() { public static final int PROGRESS_INSTANT = 2; public static final int PROGRESS_BOT_BUTTON = 3; public static final int PROGRESS_GIFT = 4; + public static final int PROGRESS_PAID_MEDIA = 5; private int progressDialogAtMessageId; private int progressDialogAtMessageType; @@ -16026,7 +16164,7 @@ protected boolean isActionBarVisible() { } private void drawChildElement(Canvas canvas, float listTop, ChatMessageCell cell, int type) { - canvas.save(); + int restoreCount = canvas.save(); float canvasOffsetX = chatListView.getLeft() + cell.getLeft(); float canvasOffsetY = chatListView.getY() + cell.getY(); float alpha = cell.shouldDrawAlphaLayer() ? cell.getAlpha() : 1f; @@ -16039,14 +16177,31 @@ private void drawChildElement(Canvas canvas, float listTop, ChatMessageCell cell cell.drawNamesLayout(canvas, alpha); } else if (type == 2) { cell.drawCaptionLayout(canvas, cell.getCurrentPosition() != null && (cell.getCurrentPosition().flags & MessageObject.POSITION_FLAG_LEFT) == 0, alpha); - } else { + } else if (type == 3) { final boolean selectionOnly = cell.getCurrentPosition() != null && (cell.getCurrentPosition().flags & MessageObject.POSITION_FLAG_LEFT) == 0; if (!selectionOnly) { cell.drawReactionsLayout(canvas, alpha); } + } else if (type == 4) { + final boolean selectionOnly = cell.getCurrentPosition() != null && (cell.getCurrentPosition().flags & MessageObject.POSITION_FLAG_LEFT) == 0; + if (!selectionOnly) { + if (scrimViewReaction != null) { + final float scrimProgress = scrimPaintAlpha * scrimViewAlpha / 0.2f; + + canvas.save(); + cell.drawScrimReaction(canvas, scrimViewReaction, scrimProgress); + canvas.restore(); + + canvas.restore(); + canvas.save(); + canvas.translate(canvasOffsetX, canvasOffsetY); + cell.drawScrimReactionPreview(this, canvas, scrimViewReactionOffset, scrimViewReaction, scrimProgress); + canvas.restore(); + } + } } cell.setInvalidatesParent(false); - canvas.restore(); + canvas.restoreToCount(restoreCount); } @Override @@ -16129,8 +16284,17 @@ protected void dispatchDraw(Canvas canvas) { } } if (scrimViewReaction == null || scrimView == null) { - scrimPaint.setAlpha((int) (255 * scrimPaintAlpha * (scrimView != null ? scrimViewAlpha : 1f))); - canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimPaint); + if (scrimBlurBitmapPaint != null) { + scrimBlurMatrix.reset(); + final float s = (float) getMeasuredWidth() / scrimBlurBitmap.getWidth(); + scrimBlurMatrix.postScale(s, s); + scrimBlurBitmapShader.setLocalMatrix(scrimBlurMatrix); + scrimBlurBitmapPaint.setAlpha((int) (0xFF * scrimViewProgress)); + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimBlurBitmapPaint); + } else { + scrimPaint.setAlpha((int) (0xFF * scrimPaintAlpha * (scrimView != null ? scrimViewAlpha : 1f))); + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimPaint); + } } if (scrimViewReaction != null && scrimViewReactionAnimated) { @@ -16238,10 +16402,16 @@ protected void dispatchDraw(Canvas canvas) { float viewClipRight = chatListView.getRight(); float viewClipBottom = chatListView.getY() + chatListView.getMeasuredHeight() - blurredViewBottomOffset; + float clipTop = 0, clipBottom = 0; if (mentionContainer != null) { - viewClipTop += mentionContainer.clipTop(); - viewClipBottom -= mentionContainer.clipBottom(); + clipTop = Math.max(clipTop, mentionContainer.clipTop()); + clipBottom = Math.max(clipBottom, mentionContainer.clipBottom()); + } + if (chatActivityEnterView != null && chatActivityEnterView.botCommandsMenuContainer != null) { + clipBottom = Math.max(clipBottom, chatActivityEnterView.botCommandsMenuContainer.clipBottom()); } + viewClipTop += clipTop; + viewClipBottom -= clipBottom; if (cell == null || !cell.getTransitionParams().animateBackgroundBoundsInner) { viewClipLeft = Math.max(viewClipLeft, chatListView.getLeft() + child.getX()); @@ -16298,10 +16468,21 @@ protected void dispatchDraw(Canvas canvas) { } } if (scrimViewReaction != null && cell != null && scrimGroup == null) { - scrimPaint.setAlpha((int) (255 * scrimPaintAlpha * scrimViewAlpha)); - canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimPaint); + if (scrimBlurBitmapPaint != null) { + scrimBlurMatrix.reset(); + final float s = (float) getMeasuredWidth() / scrimBlurBitmap.getWidth(); + scrimBlurMatrix.postScale(s, s); + scrimBlurBitmapShader.setLocalMatrix(scrimBlurMatrix); + scrimBlurBitmapPaint.setAlpha((int) (0xFF * scrimViewProgress)); + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimBlurBitmapPaint); + } else { + scrimPaint.setAlpha((int) (255 * scrimPaintAlpha * scrimViewAlpha)); + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimPaint); + } if (viewClipTop < viewClipBottom) { + final float scrimProgress = scrimPaintAlpha * scrimViewAlpha / 0.2f; + float alpha = child.getAlpha() * scrimViewAlpha; if (alpha < 1f) { canvas.saveLayerAlpha(viewClipLeft, viewClipTop, viewClipRight, viewClipBottom, (int) (255 * alpha), Canvas.ALL_SAVE_FLAG); @@ -16310,7 +16491,12 @@ protected void dispatchDraw(Canvas canvas) { } canvas.clipRect(viewClipLeft, viewClipTop, viewClipRight, viewClipBottom); canvas.translate(chatListView.getLeft() + child.getX(), chatListView.getY() + child.getY()); - cell.drawScrimReaction(canvas, scrimViewReaction); + cell.drawScrimReaction(canvas, scrimViewReaction, scrimProgress); + canvas.restore(); + + canvas.save(); + canvas.translate(chatListView.getLeft() + child.getX(), chatListView.getY() + child.getY()); + cell.drawScrimReactionPreview(this, canvas, scrimViewReactionOffset, scrimViewReaction, scrimProgress); canvas.restore(); } } @@ -16330,10 +16516,6 @@ protected void dispatchDraw(Canvas canvas) { } drawNamesAfter.clear(); } - if (scrimViewReaction != null && scrimGroup != null) { - scrimPaint.setAlpha((int) (255 * scrimPaintAlpha * scrimViewAlpha)); - canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimPaint); - } size = drawCaptionAfter.size(); if (size > 0) { for (int a = 0; a < size; a++) { @@ -16354,13 +16536,45 @@ protected void dispatchDraw(Canvas canvas) { } drawChildElement(canvas, listTop, cell, 3); } + } + if (scrimViewReaction != null && scrimGroup != null) { + if (scrimBlurBitmapPaint != null) { + scrimBlurMatrix.reset(); + final float s = (float) getMeasuredWidth() / scrimBlurBitmap.getWidth(); + scrimBlurMatrix.postScale(s, s); + scrimBlurBitmapShader.setLocalMatrix(scrimBlurMatrix); + scrimBlurBitmapPaint.setAlpha((int) (0xFF * scrimViewProgress)); + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimBlurBitmapPaint); + } else { + scrimPaint.setAlpha((int) (255 * scrimPaintAlpha * scrimViewAlpha)); + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimPaint); + } + } + size = drawReactionsAfter.size(); + if (size > 0) { + for (int a = 0; a < size; a++) { + ChatMessageCell cell = drawReactionsAfter.get(a); + if (cell.getCurrentPosition() == null && !cell.getTransitionParams().animateBackgroundBoundsInner) { + continue; + } + drawChildElement(canvas, listTop, cell, 4); + } drawReactionsAfter.clear(); } } if (scrimViewReaction == null && scrimViewAlpha < 1f) { - scrimPaint.setAlpha((int) (255 * scrimPaintAlpha * (1f - scrimViewAlpha))); - canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimPaint); + if (scrimBlurBitmapPaint != null) { + scrimBlurMatrix.reset(); + final float s = (float) getMeasuredWidth() / scrimBlurBitmap.getWidth(); + scrimBlurMatrix.postScale(s, s); + scrimBlurBitmapShader.setLocalMatrix(scrimBlurMatrix); + scrimBlurBitmapPaint.setAlpha((int) (0xFF * scrimViewProgress)); + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimBlurBitmapPaint); + } else { + scrimPaint.setAlpha((int) (255 * scrimPaintAlpha * (1f - scrimViewAlpha))); + canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), scrimPaint); + } } } @@ -17841,9 +18055,9 @@ public void updateTitle(boolean animated) { } else if (UserObject.isUserSelf(user)) { avatarContainer.setTitle(LocaleController.getString(R.string.MyNotes)); } else if (user != null) { - avatarContainer.setTitle(UserObject.getUserName(user)); + avatarContainer.setTitle(AndroidUtilities.removeDiacritics(UserObject.getUserName(user))); } else if (chat != null) { - avatarContainer.setTitle(chat.title); + avatarContainer.setTitle(AndroidUtilities.removeDiacritics(chat.title)); } else { avatarContainer.setTitle(""); } @@ -17872,7 +18086,7 @@ public void updateTitle(boolean animated) { } else if (chatMode == MODE_PINNED) { avatarContainer.setTitle(LocaleController.formatPluralString("PinnedMessagesCount", getPinnedMessagesCount())); } else if (currentChat != null) { - avatarContainer.setTitle(currentChat.title, currentChat.scam, currentChat.fake, currentChat.verified, false, currentChat.emoji_status, animated); + avatarContainer.setTitle(AndroidUtilities.removeDiacritics(currentChat.title), currentChat.scam, currentChat.fake, currentChat.verified, false, currentChat.emoji_status, animated); } else if (currentUser != null) { if (currentUser.self) { avatarContainer.setTitle(LocaleController.getString("SavedMessages", R.string.SavedMessages)); @@ -17880,10 +18094,10 @@ public void updateTitle(boolean animated) { if (!TextUtils.isEmpty(currentUser.phone)) { avatarContainer.setTitle(PhoneFormat.getInstance().format("+" + currentUser.phone), currentUser.scam, currentUser.fake, currentUser.verified, getMessagesController().isPremiumUser(currentUser), currentUser.emoji_status, animated); } else { - avatarContainer.setTitle(UserObject.getUserName(currentUser), currentUser.scam, currentUser.fake, currentUser.verified, getMessagesController().isPremiumUser(currentUser), currentUser.emoji_status, animated); + avatarContainer.setTitle(AndroidUtilities.removeDiacritics(UserObject.getUserName(currentUser)), currentUser.scam, currentUser.fake, currentUser.verified, getMessagesController().isPremiumUser(currentUser), currentUser.emoji_status, animated); } } else { - avatarContainer.setTitle(UserObject.getUserName(currentUser), currentUser.scam, currentUser.fake, currentUser.verified, getMessagesController().isPremiumUser(currentUser), currentUser.emoji_status, animated); + avatarContainer.setTitle(AndroidUtilities.removeDiacritics(UserObject.getUserName(currentUser)), currentUser.scam, currentUser.fake, currentUser.verified, getMessagesController().isPremiumUser(currentUser), currentUser.emoji_status, animated); } } setParentActivityTitle(avatarContainer.getTitleTextView().getText()); @@ -18186,7 +18400,7 @@ private void openEditingMessageInPhotoEditor() { PhotoViewer.getInstance().openPhotoForSelect(photos, 0, 2, false, new PhotoViewer.EmptyPhotoViewerProvider() { @Override public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { - return ChatActivity.this.getPlaceForPhoto(object, null, needPreview, true); + return ChatActivity.this.getPlaceForPhoto(object, null, index, needPreview, true); } @Override @@ -18239,7 +18453,7 @@ public boolean closeKeyboard() { }, this); } - private PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, boolean needPreview, boolean onlyIfVisible) { + private PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview, boolean onlyIfVisible) { int count = chatListView.getChildCount(); for (int a = 0; a < count; a++) { @@ -18250,7 +18464,7 @@ private PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageOb ChatMessageCell cell = (ChatMessageCell) view; MessageObject message = cell.getMessageObject(); if (message != null && message.getId() == messageObject.getId()) { - imageReceiver = cell.getPhotoImage(); + imageReceiver = cell.getPhotoImage(index); } } } else if (view instanceof ChatActionCell) { @@ -20825,7 +21039,7 @@ public void didReceivedNotification(int id, int account, final Object... args) { int msgId = (Integer) args[1]; MessageObject messageObject = messagesDict[did == dialog_id ? 0 : 1].get(msgId); if (messageObject != null) { - messageObject.messageOwner.media.extended_media = (TLRPC.MessageExtendedMedia) args[2]; + messageObject.messageOwner.media.extended_media = (ArrayList) args[2]; messageObject.forceUpdate = true; messageObject.setType(); updateMessageAnimated(messageObject, false); @@ -22562,7 +22776,7 @@ public boolean processSwitchButton(TLRPC.TL_keyboardButtonSwitchInline button) { inlineReturn = 0; chatActivityEnterView.setFieldText(query); } else { - getMediaDataController().saveDraft(inlineReturn, 0, query, null, null, false); + getMediaDataController().saveDraft(inlineReturn, 0, query, null, null, false, 0); if (parentLayout.getFragmentStack().size() > 1) { BaseFragment prevFragment = parentLayout.getFragmentStack().get(parentLayout.getFragmentStack().size() - 2); if (prevFragment instanceof ChatActivity && ((ChatActivity) prevFragment).dialog_id == inlineReturn) { @@ -24606,7 +24820,9 @@ public void openAttachBotLayout(String botUsername) { public void openAttachBotLayout(long botId, String startCommand, boolean justAdded) { openAttachMenu(); createChatAttachView(); - chatAttachAlert.showBotLayout(botId, startCommand, justAdded, false); + if (chatAttachAlert != null) { + chatAttachAlert.showBotLayout(botId, startCommand, justAdded, false); + } } @Override @@ -25498,8 +25714,11 @@ public void onAnimationCancel(Animator animation) { buttonTextView.setText(null); buttonTextView.setOnClickListener(null); } else { - SpannableString string = new SpannableString(botButton.text); - Emoji.replaceEmoji(string, buttonTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false); + CharSequence string = new SpannableString(botButton.text); + if (botButton instanceof TLRPC.TL_keyboardButtonBuy && pinnedMessageObject != null && MessageObject.getMedia(pinnedMessageObject.messageOwner) instanceof TLRPC.TL_messageMediaInvoice) { + string = StarsIntroActivity.replaceStars(string); + } + string = Emoji.replaceEmoji(string, buttonTextView.getPaint().getFontMetricsInt(), AndroidUtilities.dp(20), false); buttonTextView.setText(string); final MessageObject buttonMessage = pinnedMessageObject; buttonTextView.setOnClickListener(e -> { @@ -25521,7 +25740,7 @@ public void onAnimationCancel(Animator animation) { return false; } if (botButton instanceof TLRPC.TL_keyboardButtonUrl) { - openClickableLink(null, botButton.url, true, null, buttonMessage); + openClickableLink(null, botButton.url, true, null, buttonMessage, false); try { buttonTextView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } catch (Exception ignore) {} @@ -26604,6 +26823,12 @@ public void setInPreviewMode(boolean value) { if (chatActivityEnterView != null) { chatActivityEnterView.setVisibility(!value ? View.VISIBLE : View.INVISIBLE); } + if (suggestEmojiPanel != null) { + suggestEmojiPanel.setVisibility(allowStickersPanel && !value && (chatActivityEnterView == null || !chatActivityEnterView.isStickersExpanded()) ? View.VISIBLE : View.GONE); + } + if (mentionContainer != null) { + mentionContainer.animate().alpha(chatActivityEnterView.isStickersExpanded() || isInPreviewMode() ? 0 : 1f).setInterpolator(CubicBezierInterpolator.DEFAULT).start(); + } if (actionBar != null) { actionBar.setBackButtonDrawable(!value ? new BackDrawable(false) : null); if (headerItem != null) { @@ -26886,7 +27111,7 @@ public void onPause() { } else { draftThreadId = threadMessageId; } - getMediaDataController().saveDraft(dialog_id, draftThreadId, message[0], entities, (replyMessage != null && !replyMessage.isTopicMainMessage && replyMessage.replyToForumTopic == null && !ignoreDraft) ? replyMessage.messageOwner : null, replyingQuote, !searchWebpage, false); + getMediaDataController().saveDraft(dialog_id, draftThreadId, message[0], entities, (replyMessage != null && !replyMessage.isTopicMainMessage && replyMessage.replyToForumTopic == null && !ignoreDraft) ? replyMessage.messageOwner : null, replyingQuote, chatActivityEnterView != null ? chatActivityEnterView.getEffectId() : 0, !searchWebpage, false); getMessagesController().cancelTyping(0, dialog_id, threadMessageId); } if (chatMode == 0 || chatMode == MODE_SAVED) { @@ -27190,9 +27415,11 @@ public void applyDraftMaybe(boolean canClear) { } }, 700); } + chatActivityEnterView.setEffectId(draftMessage.effect); } } else if (canClear && draftMessage == null) { chatActivityEnterView.setFieldText(""); + chatActivityEnterView.setEffectId(0); hideFieldPanel(true); } if (replyingMessageObject == null || threadMessageObject == replyingMessageObject) { @@ -27586,7 +27813,7 @@ private boolean createMenu(View v, boolean single, boolean listView, float x, fl req.peer = getMessagesController().getInputPeer(message.messageOwner.peer_id); getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (response instanceof TLRPC.TL_payments_paymentReceiptStars) { - StarsIntroActivity.showTransactionSheet(getContext(), currentAccount, (TLRPC.TL_payments_paymentReceiptStars) response, resourceProvider); + StarsIntroActivity.showTransactionSheet(getContext(), false, currentAccount, (TLRPC.TL_payments_paymentReceiptStars) response, resourceProvider); } else if (response instanceof TLRPC.PaymentReceipt) { presentFragment(new PaymentFormActivity((TLRPC.PaymentReceipt) response)); } @@ -27676,6 +27903,7 @@ private boolean createMenu(View v, boolean single, boolean listView, float x, fl } allowPin = allowPin && message.getId() > 0 && (message.messageOwner.action == null || message.messageOwner.action instanceof TLRPC.TL_messageActionEmpty) && !message.isExpiredStory() && message.type != MessageObject.TYPE_STORY_MENTION; boolean noforwards = getMessagesController().isChatNoForwards(currentChat) || message.messageOwner.noforwards; + boolean noforwardsOrPaidMedia = noforwards || message.type == MessageObject.TYPE_PAID_MEDIA; boolean allowUnpin = message.getDialogId() != mergeDialogId && allowPin && (pinnedMessageObjects.containsKey(message.getId()) || groupedMessages != null && !groupedMessages.messages.isEmpty() && pinnedMessageObjects.containsKey(groupedMessages.messages.get(0).getId())) && !message.isExpiredStory(); boolean allowEdit = message.canEditMessage(currentChat) && !chatActivityEnterView.hasAudioToSend() && message.getDialogId() != mergeDialogId && message.type != MessageObject.TYPE_STORY; if (allowEdit && groupedMessages != null) { @@ -27811,7 +28039,7 @@ public void setAutoDeleteHistory(int time, int action) { } } if (type == -1) { - if ((selectedObject.type == MessageObject.TYPE_TEXT || selectedObject.isAnimatedEmoji() || selectedObject.isAnimatedEmojiStickers() || getMessageCaption(selectedObject, selectedObjectGroup) != null) && !noforwards && !message.isExpiredStory()) { + if ((selectedObject.type == MessageObject.TYPE_TEXT || selectedObject.isAnimatedEmoji() || selectedObject.isAnimatedEmojiStickers() || getMessageCaption(selectedObject, selectedObjectGroup) != null) && !noforwardsOrPaidMedia && !message.isExpiredStory()) { items.add(LocaleController.getString("Copy", R.string.Copy)); options.add(OPTION_COPY); icons.add(R.drawable.msg_copy); @@ -27862,6 +28090,11 @@ public void setAutoDeleteHistory(int time, int action) { options.add(OPTION_EDIT); icons.add(R.drawable.msg_edit); } +// if (message.scheduled && message.type == MessageObject.TYPE_PAID_MEDIA && message.canEditMessage(currentChat)) { +// items.add(LocaleController.getString(R.string.PaidMediaPriceButton)); +// options.add(OPTION_EDIT_PRICE); +// icons.add(R.drawable.menu_feature_paid); +// } if (selectedObject.contentType == 0 && !selectedObject.isMediaEmptyWebpage() && selectedObject.getId() > 0 && !selectedObject.isOut() && (currentChat != null || currentUser != null && currentUser.bot)) { items.add(LocaleController.getString("ReportChat", R.string.ReportChat)); options.add(OPTION_REPORT_CHAT); @@ -27883,7 +28116,7 @@ public void setAutoDeleteHistory(int time, int action) { items.add(LocaleController.getString("Retry", R.string.Retry)); options.add(OPTION_RETRY); icons.add(R.drawable.msg_retry); - if (!noforwards) { + if (!noforwardsOrPaidMedia) { items.add(LocaleController.getString("Copy", R.string.Copy)); options.add(OPTION_COPY); icons.add(R.drawable.msg_copy); @@ -27909,12 +28142,12 @@ 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) && !isInsideContainer) { + if ((allowChatActions || !noforwardsOrPaidMedia && 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.menu_reply); } - if ((selectedObject.type == MessageObject.TYPE_TEXT || selectedObject.isDice() || selectedObject.isAnimatedEmoji() || selectedObject.isAnimatedEmojiStickers() || getMessageCaption(selectedObject, selectedObjectGroup) != null) && !noforwards && !selectedObject.sponsoredCanReport) { + if ((selectedObject.type == MessageObject.TYPE_TEXT || selectedObject.isDice() || selectedObject.isAnimatedEmoji() || selectedObject.isAnimatedEmojiStickers() || getMessageCaption(selectedObject, selectedObjectGroup) != null) && !noforwardsOrPaidMedia && !selectedObject.sponsoredCanReport) { items.add(LocaleController.getString("Copy", R.string.Copy)); options.add(OPTION_COPY); icons.add(R.drawable.msg_copy); @@ -27957,24 +28190,24 @@ public void setAutoDeleteHistory(int time, int action) { options.add(OPTION_STOP_POLL_OR_QUIZ); icons.add(R.drawable.msg_pollstop); } - } else if (selectedObject.isMusic() && !noforwards && !selectedObject.isVoiceOnce() && !selectedObject.isRoundOnce()) { + } else if (selectedObject.isMusic() && !noforwardsOrPaidMedia && !selectedObject.isVoiceOnce() && !selectedObject.isRoundOnce()) { items.add(LocaleController.getString("SaveToMusic", R.string.SaveToMusic)); options.add(OPTION_SAVE_TO_DOWNLOADS_OR_MUSIC); icons.add(R.drawable.msg_download); - } else if (selectedObject.isDocument() && !noforwards && !selectedObject.isVoiceOnce() && !selectedObject.isRoundOnce()) { + } else if (selectedObject.isDocument() && !noforwardsOrPaidMedia && !selectedObject.isVoiceOnce() && !selectedObject.isRoundOnce()) { items.add(LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads)); options.add(OPTION_SAVE_TO_DOWNLOADS_OR_MUSIC); icons.add(R.drawable.msg_download); } } - } else if (type == 3 && !noforwards) { + } else if (type == 3 && !noforwardsOrPaidMedia) { if (selectedObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage && MessageObject.isNewGifDocument(selectedObject.messageOwner.media.webpage.document)) { items.add(LocaleController.getString("SaveToGIFs", R.string.SaveToGIFs)); options.add(OPTION_ADD_TO_GIFS); icons.add(R.drawable.msg_gif); } } else if (type == 4) { - if (!noforwards && !selectedObject.hasRevealedExtendedMedia()) { + if (!noforwardsOrPaidMedia && !selectedObject.hasRevealedExtendedMedia()) { if (selectedObject.isVideo()) { if (!selectedObject.needDrawBluredPreview()) { items.add(LocaleController.getString("SaveToGallery", R.string.SaveToGallery)); @@ -28015,7 +28248,7 @@ public void setAutoDeleteHistory(int time, int action) { items.add(LocaleController.getString("ApplyLocalizationFile", R.string.ApplyLocalizationFile)); options.add(OPTION_APPLY_LOCALIZATION_OR_THEME); icons.add(R.drawable.msg_language); - if (!noforwards && !selectedObject.isVoiceOnce() && !selectedObject.isRoundOnce()) { + if (!noforwardsOrPaidMedia && !selectedObject.isVoiceOnce() && !selectedObject.isRoundOnce()) { items.add(LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads)); options.add(OPTION_SAVE_TO_DOWNLOADS_OR_MUSIC); icons.add(R.drawable.msg_download); @@ -28027,7 +28260,7 @@ public void setAutoDeleteHistory(int time, int action) { items.add(LocaleController.getString("ApplyThemeFile", R.string.ApplyThemeFile)); options.add(OPTION_APPLY_LOCALIZATION_OR_THEME); icons.add(R.drawable.msg_theme); - if (!noforwards && !selectedObject.isVoiceOnce() && !selectedObject.isRoundOnce()) { + if (!noforwardsOrPaidMedia && !selectedObject.isVoiceOnce() && !selectedObject.isRoundOnce()) { items.add(LocaleController.getString("SaveToDownloads", R.string.SaveToDownloads)); options.add(OPTION_SAVE_TO_DOWNLOADS_OR_MUSIC); icons.add(R.drawable.msg_download); @@ -28035,7 +28268,7 @@ public void setAutoDeleteHistory(int time, int action) { options.add(OPTION_SHARE); icons.add(R.drawable.msg_shareout); } - } else if (type == 6 && !noforwards && !selectedObject.hasRevealedExtendedMedia()) { + } else if (type == 6 && !noforwardsOrPaidMedia && !selectedObject.hasRevealedExtendedMedia()) { if (!selectedObject.needDrawBluredPreview() && !selectedObject.isVoiceOnce() && !selectedObject.isRoundOnce()) { items.add(LocaleController.getString("SaveToGallery", R.string.SaveToGallery)); options.add(OPTION_SAVE_TO_GALLERY2); @@ -28081,7 +28314,7 @@ public void setAutoDeleteHistory(int time, int action) { icons.add(R.drawable.msg_addcontact); } if (!TextUtils.isEmpty(selectedObject.messageOwner.media.phone_number)) { - if (!noforwards) { + if (!noforwardsOrPaidMedia) { items.add(LocaleController.getString("Copy", R.string.Copy)); options.add(OPTION_COPY_PHONE_NUMBER); icons.add(R.drawable.msg_copy); @@ -28131,6 +28364,11 @@ public void setAutoDeleteHistory(int time, int action) { options.add(OPTION_EDIT); icons.add(R.drawable.msg_edit); } +// if (message.scheduled && message.type == MessageObject.TYPE_PAID_MEDIA && allowEdit) { +// items.add(LocaleController.getString(R.string.PaidMediaPriceButton)); +// options.add(OPTION_EDIT_PRICE); +// icons.add(R.drawable.menu_feature_paid); +// } if (chatMode == MODE_SCHEDULED && selectedObject.canEditMessageScheduleTime(currentChat)) { items.add(LocaleController.getString("MessageScheduleEditTime", R.string.MessageScheduleEditTime)); options.add(OPTION_EDIT_SCHEDULE_TIME); @@ -28164,7 +28402,7 @@ public void setAutoDeleteHistory(int time, int action) { options.add(OPTION_REPLY); icons.add(R.drawable.menu_reply); } - if ((selectedObject.type == MessageObject.TYPE_TEXT || selectedObject.isAnimatedEmoji() || selectedObject.isAnimatedEmojiStickers() || getMessageCaption(selectedObject, selectedObjectGroup) != null) && !noforwards) { + if ((selectedObject.type == MessageObject.TYPE_TEXT || selectedObject.isAnimatedEmoji() || selectedObject.isAnimatedEmojiStickers() || getMessageCaption(selectedObject, selectedObjectGroup) != null) && !noforwardsOrPaidMedia) { items.add(LocaleController.getString("Copy", R.string.Copy)); options.add(OPTION_COPY); icons.add(R.drawable.msg_copy); @@ -28183,7 +28421,7 @@ public void setAutoDeleteHistory(int time, int action) { options.add(OPTION_VIEW_IN_TOPIC); icons.add(R.drawable.msg_viewintopic); } - if (type == 4 && !noforwards && !selectedObject.hasRevealedExtendedMedia() && !selectedObject.needDrawBluredPreview()) { + if (type == 4 && !noforwardsOrPaidMedia && !selectedObject.hasRevealedExtendedMedia() && !selectedObject.needDrawBluredPreview()) { if (selectedObject.isVideo()) { items.add(LocaleController.getString("SaveToGallery", R.string.SaveToGallery)); options.add(OPTION_SAVE_TO_GALLERY); @@ -28234,7 +28472,7 @@ public void setAutoDeleteHistory(int time, int action) { icons.add(R.drawable.msg_addcontact); } if (!TextUtils.isEmpty(selectedObject.messageOwner.media.phone_number)) { - if (!noforwards) { + if (!noforwardsOrPaidMedia) { items.add(LocaleController.getString("Copy", R.string.Copy)); options.add(OPTION_COPY_PHONE_NUMBER); icons.add(R.drawable.msg_copy); @@ -28410,7 +28648,7 @@ public Object instantiateItem(ViewGroup container, int position) { if (index >= 0) { reactionCount = counters.get(index); } - ReactedUsersListView v = new ReactedUsersListView(container.getContext(), themeDelegate, currentAccount, message, reactionCount, false) + ReactedUsersListView v = new ReactedUsersListView(container.getContext(), themeDelegate, currentAccount, message, reactionCount, false, true) .setSeenUsers(reactedView.getSeenUsers()) .setOnCustomEmojiSelectedListener((reactedUsersListView1, customEmojiStickerSets) -> { EmojiPacksAlert alert = new EmojiPacksAlert(ChatActivity.this, getParentActivity(), themeDelegate, customEmojiStickerSets) { @@ -28499,7 +28737,7 @@ public void onPageScrollStateChanged(int state) { } else { linearLayout.addView(new ActionBarPopupWindow.GapView(contentView.getContext(), themeDelegate), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); - reactedUsersListView = new ReactedUsersListView(contentView.getContext(), themeDelegate, currentAccount, message, null, false) + reactedUsersListView = new ReactedUsersListView(contentView.getContext(), themeDelegate, currentAccount, message, null, false, true) .setSeenUsers(reactedView.getSeenUsers()) .setOnCustomEmojiSelectedListener((reactedUsersListView1, customEmojiStickerSets) -> { EmojiPacksAlert alert = new EmojiPacksAlert(ChatActivity.this, getParentActivity(), themeDelegate, customEmojiStickerSets) { @@ -28967,7 +29205,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } String toLangValue = fromLang != null && fromLang.equals(toLang) ? toLangDefault : toLang; ArrayList entities = selectedObject != null && selectedObject.messageOwner != null ? selectedObject.messageOwner.entities : null; - TranslateAlert2 alert = TranslateAlert2.showAlert(getParentActivity(), this, currentAccount, inputPeer, messageIdToTranslate[0], fromLang, toLangValue, finalMessageText, entities, noforwards, onLinkPress, () -> dimBehindView(false)); + TranslateAlert2 alert = TranslateAlert2.showAlert(getParentActivity(), this, currentAccount, inputPeer, messageIdToTranslate[0], fromLang, toLangValue, finalMessageText, entities, noforwardsOrPaidMedia, onLinkPress, () -> dimBehindView(false)); alert.setDimBehind(false); closeMenu(false); @@ -29013,7 +29251,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { } String toLangValue = fromLang[0] != null && fromLang[0].equals(toLang) ? toLangDefault : toLang; ArrayList entities = selectedObject != null && selectedObject.messageOwner != null ? selectedObject.messageOwner.entities : null; - TranslateAlert2 alert = TranslateAlert2.showAlert(getParentActivity(), this, currentAccount, inputPeer, messageIdToTranslate[0], fromLang[0], toLangValue, finalMessageText, entities, noforwards, onLinkPress, () -> dimBehindView(false)); + TranslateAlert2 alert = TranslateAlert2.showAlert(getParentActivity(), this, currentAccount, inputPeer, messageIdToTranslate[0], fromLang[0], toLangValue, finalMessageText, entities, noforwardsOrPaidMedia, onLinkPress, () -> dimBehindView(false)); alert.setDimBehind(false); closeMenu(false); @@ -29034,7 +29272,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (selectedObject == null || i >= options.size() || getParentActivity() == null) { return; } - TranslateAlert2 alert = TranslateAlert2.showAlert(getParentActivity(), this, currentAccount, inputPeer, messageIdToTranslate[0], "und", toLang, finalMessageText, null, noforwards, onLinkPress, () -> dimBehindView(false)); + TranslateAlert2 alert = TranslateAlert2.showAlert(getParentActivity(), this, currentAccount, inputPeer, messageIdToTranslate[0], "und", toLang, finalMessageText, null, noforwardsOrPaidMedia, onLinkPress, () -> dimBehindView(false)); alert.setDimBehind(false); closeMenu(false); @@ -30376,6 +30614,17 @@ private void processSelectedOption(int option) { selectedObjectToEditCaption = null; break; } + case OPTION_EDIT_PRICE: { + final MessageObject msg = selectedObject; + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) selectedObject.messageOwner.media; + StarsIntroActivity.showMediaPriceSheet(getContext(), paidMedia.stars_amount, false, (stars, done) -> { + StarsController.getInstance(currentAccount).updateMediaPrice(msg, stars, done); + }, themeDelegate); + selectedObject = null; + selectedObjectGroup = null; + selectedObjectToEditCaption = null; + break; + } case OPTION_PIN: { final int mid; if (selectedObjectGroup != null && !selectedObjectGroup.messages.isEmpty()) { @@ -31059,7 +31308,7 @@ public boolean onBackPressed() { if (secretVoicePlayer != null && secretVoicePlayer.isShown()) { secretVoicePlayer.dismiss(); return false; - } else if (closeStoryViewer()) { + } else if (closeSheet()) { return false; } else if (selectionReactionsOverlay != null && !selectionReactionsOverlay.onBackPressed()) { return false; @@ -31494,6 +31743,7 @@ public void openHashtagSearch(String hashtag) { } searchingHashtag = hashtag; searchingQuery = searchingHashtag; + checkHashtagStories(true); if (!actionBar.isSearchFieldVisible()) { AndroidUtilities.updateViewVisibilityAnimated(avatarContainer, false, 0.95f, true); if (headerItem != null) { @@ -31656,6 +31906,10 @@ public boolean isSecretChat() { return currentEncryptedChat != null; } + public boolean isChannel() { + return ChatObject.isChannelAndNotMegaGroup(currentChat); + } + public boolean canScheduleMessage() { if (chatMode == MODE_QUICK_REPLIES) return false; if (getMessagesController().isForum(getDialogId()) && !isTopic) { @@ -32329,7 +32583,7 @@ private void setCellSelectionBackground(MessageObject message, ChatMessageCell m messageCell.setChecked(true, groupedMessages == null, animated); } - private void openClickableLink(CharacterStyle url, String str, boolean longPress, final ChatMessageCell cell, final MessageObject messageObject) { + private void openClickableLink(CharacterStyle url, String str, boolean longPress, final ChatMessageCell cell, final MessageObject messageObject, boolean forceNoIV) { if (longPress) { if (str.startsWith("@")) { if (cell != null) { @@ -32338,99 +32592,106 @@ private void openClickableLink(CharacterStyle url, String str, boolean longPress didLongPressUsername(cell, url, str.substring(1)); return; } - BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity(), false, themeDelegate); - int timestamp = -1; - if (str.startsWith("video?")) { - timestamp = Utilities.parseInt(str); - } - if (timestamp >= 0) { - builder.setTitle(AndroidUtilities.formatDuration(timestamp, false)); - } else { - String formattedUrl = str; - try { + if (url == null) { + BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity(), false, themeDelegate); + int timestamp = -1; + if (str.startsWith("video?")) { + timestamp = Utilities.parseInt(str); + } + if (timestamp >= 0) { + builder.setTitle(AndroidUtilities.formatDuration(timestamp, false)); + } else { + String formattedUrl = str; try { - Uri uri = Uri.parse(formattedUrl); - formattedUrl = Browser.replaceHostname(uri, IDN.toUnicode(uri.getHost(), IDN.ALLOW_UNASSIGNED)); + try { + Uri uri = Uri.parse(formattedUrl); + formattedUrl = Browser.replaceHostname(uri, IDN.toUnicode(uri.getHost(), IDN.ALLOW_UNASSIGNED)); + } catch (Exception e) { + FileLog.e(e, false); + } + formattedUrl = URLDecoder.decode(formattedUrl.replaceAll("\\+", "%2b"), "UTF-8"); } catch (Exception e) { - FileLog.e(e, false); + FileLog.e(e); } - formattedUrl = URLDecoder.decode(formattedUrl.replaceAll("\\+", "%2b"), "UTF-8"); - } catch (Exception e) { - FileLog.e(e); + builder.setTitle(formattedUrl); + builder.setTitleMultipleLines(true); } - builder.setTitle(formattedUrl); - builder.setTitleMultipleLines(true); - } - final int finalTimestamp = timestamp; - boolean noforwards = getMessagesController().isChatNoForwards(currentChat) || (messageObject != null && messageObject.messageOwner != null && messageObject.messageOwner.noforwards); - builder.setItems(noforwards ? new CharSequence[] {LocaleController.getString("Open", R.string.Open)} : new CharSequence[]{LocaleController.getString("Open", R.string.Open), LocaleController.getString("Copy", R.string.Copy)}, (dialog, which) -> { - if (which == 0) { - if (str.startsWith("video?")) { - didPressMessageUrl(url, false, messageObject, cell); - } else { - logSponsoredClicked(messageObject); - openClickableLink(url, str, false, cell, messageObject); - } - } else if (which == 1) { - if (str.startsWith("video?") && messageObject != null && !messageObject.scheduled) { - MessageObject messageObject1 = messageObject; - boolean isMedia = messageObject.isVideo() || messageObject.isRoundVideo() || messageObject.isVoice() || messageObject.isMusic(); - if (!isMedia && messageObject.replyMessageObject != null) { - messageObject1 = messageObject.replyMessageObject; + final int finalTimestamp = timestamp; + boolean noforwards = getMessagesController().isChatNoForwards(currentChat) || (messageObject != null && messageObject.messageOwner != null && messageObject.messageOwner.noforwards); + builder.setItems(noforwards ? new CharSequence[] {LocaleController.getString("Open", R.string.Open)} : new CharSequence[]{LocaleController.getString("Open", R.string.Open), LocaleController.getString("Copy", R.string.Copy)}, (dialog, which) -> { + if (which == 0) { + if (str.startsWith("video?")) { + didPressMessageUrl(url, false, messageObject, cell); + } else { + logSponsoredClicked(messageObject); + openClickableLink(url, str, false, cell, messageObject, true); } - long dialogId = messageObject1.getDialogId(); - int messageId = messageObject1.getId(); - String link = null; - - if (messageObject1.messageOwner.fwd_from != null) { - if (messageObject1.messageOwner.fwd_from.saved_from_peer != null) { - dialogId = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.saved_from_peer); - messageId = messageObject1.messageOwner.fwd_from.saved_from_msg_id; - } else if (messageObject1.messageOwner.fwd_from.from_id != null) { - dialogId = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.from_id); - messageId = messageObject1.messageOwner.fwd_from.channel_post; + } else if (which == 1) { + if (str.startsWith("video?") && messageObject != null && !messageObject.scheduled) { + MessageObject messageObject1 = messageObject; + boolean isMedia = messageObject.isVideo() || messageObject.isRoundVideo() || messageObject.isVoice() || messageObject.isMusic(); + if (!isMedia && messageObject.replyMessageObject != null) { + messageObject1 = messageObject.replyMessageObject; + } + long dialogId = messageObject1.getDialogId(); + int messageId = messageObject1.getId(); + String link = null; + + if (messageObject1.messageOwner.fwd_from != null) { + if (messageObject1.messageOwner.fwd_from.saved_from_peer != null) { + dialogId = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.saved_from_peer); + messageId = messageObject1.messageOwner.fwd_from.saved_from_msg_id; + } else if (messageObject1.messageOwner.fwd_from.from_id != null) { + dialogId = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.from_id); + messageId = messageObject1.messageOwner.fwd_from.channel_post; + } } - } - if (DialogObject.isChatDialog(dialogId)) { - TLRPC.Chat currentChat = MessagesController.getInstance(currentAccount).getChat(-dialogId); - String username = ChatObject.getPublicUsername(currentChat); - if (currentChat != null && username != null) { - link = "https://t.me/" + username + "/" + messageId + "?t=" + finalTimestamp; + if (DialogObject.isChatDialog(dialogId)) { + TLRPC.Chat currentChat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + String username = ChatObject.getPublicUsername(currentChat); + if (currentChat != null && username != null) { + link = "https://t.me/" + username + "/" + messageId + "?t=" + finalTimestamp; + } + } else { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + String username = UserObject.getPublicUsername(user); + if (user != null && username != null) { + link = "https://t.me/" + username + "/" + messageId + "?t=" + finalTimestamp; + } } - } else { - TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); - String username = UserObject.getPublicUsername(user); - if (user != null && username != null) { - link = "https://t.me/" + username + "/" + messageId + "?t=" + finalTimestamp; + if (link == null) { + return; } + AndroidUtilities.addToClipboard(link); + } else { + AndroidUtilities.addToClipboard(str); } - if (link == null) { + createUndoView(); + if (undoView == null) { return; } - AndroidUtilities.addToClipboard(link); - } else { - AndroidUtilities.addToClipboard(str); - } - createUndoView(); - if (undoView == null) { - return; + if (str.startsWith("@")) { + undoView.showWithAction(0, UndoView.ACTION_USERNAME_COPIED, null); + } else if (str.startsWith("#") || str.startsWith("$")) { + undoView.showWithAction(0, UndoView.ACTION_HASHTAG_COPIED, null); + } else { + undoView.showWithAction(0, UndoView.ACTION_LINK_COPIED, null); + } } - if (str.startsWith("@")) { - undoView.showWithAction(0, UndoView.ACTION_USERNAME_COPIED, null); - } else if (str.startsWith("#") || str.startsWith("$")) { - undoView.showWithAction(0, UndoView.ACTION_HASHTAG_COPIED, null); - } else { - undoView.showWithAction(0, UndoView.ACTION_LINK_COPIED, null); + }); + builder.setOnPreDismissListener(di -> { + if (cell != null) { + cell.resetPressedLink(-1); } - } - }); - builder.setOnPreDismissListener(di -> { + }); + showDialog(builder.create()); + } else { if (cell != null) { cell.resetPressedLink(-1); } - }); - showDialog(builder.create()); + didLongPressLink(cell, messageObject, url, str); + } } else { logSponsoredClicked(messageObject); String username = Browser.extractUsername(str); @@ -32445,7 +32706,7 @@ private void openClickableLink(CharacterStyle url, String str, boolean longPress } else if (str.startsWith("@")) { getMessagesController().openByUserName(username, ChatActivity.this, 0, makeProgressForLink(cell, url)); } else { - processExternalUrl(0, str, url, cell, false); + processExternalUrl(0, str, url, cell, false, false); } } else if (str.startsWith("#") || str.startsWith("$")) { if (chatMode == MODE_SCHEDULED || chatMode == MODE_PINNED || chatMode == MODE_SEARCH) { @@ -32457,7 +32718,7 @@ private void openClickableLink(CharacterStyle url, String str, boolean longPress openHashtagSearch(str); } } else { - processExternalUrl(0, str, url, cell, false); + processExternalUrl(0, str, url, cell, false, false); } } } @@ -32533,7 +32794,35 @@ public void end(boolean replaced) { } : null; } - private void processExternalUrl(int type, String url, CharacterStyle span, ChatMessageCell cell, boolean forceAlert) { + private Browser.Progress makeProgressForPaidMedia(ChatMessageCell cell) { + if (progressDialogCurrent != null) { + progressDialogCurrent.cancel(true); + progressDialogCurrent = null; + } + return progressDialogCurrent = cell != null && cell.getMessageObject() != null ? new Browser.Progress() { + @Override + public void init() { + progressDialogAtMessageId = cell.getMessageObject().getId(); + progressDialogAtMessageType = PROGRESS_PAID_MEDIA; + progressDialogBotButtonUrl = null; + + cell.invalidate(); + } + + @Override + public void end(boolean replaced) { + if (!replaced) { + AndroidUtilities.runOnUIThread(() -> { + if (progressDialogAtMessageId == cell.getMessageObject().getId()) { + resetProgressDialogLoading(); + } + }, 240); + } + } + } : null; + } + + private void processExternalUrl(int type, String url, CharacterStyle span, ChatMessageCell cell, boolean forceAlert, boolean forceNoIV) { try { String host = AndroidUtilities.getHostAuthority(url); if ((currentEncryptedChat == null || getMessagesController().secretWebpagePreview == 1) && getMessagesController().authDomains.contains(host)) { @@ -32549,17 +32838,17 @@ private void processExternalUrl(int type, String url, CharacterStyle span, ChatM if (span instanceof URLSpanReplacement && (((URLSpanReplacement) span).getTextStyleRun().flags & TextStyleSpan.FLAG_STYLE_TEXT_URL) != 0) { forceNotInternalForApps = true; } - AlertsCreator.showOpenUrlAlert(ChatActivity.this, url, true, true, true, forceNotInternalForApps, makeProgressForLink(cell, span), themeDelegate); + AlertsCreator.showOpenUrlAlert(ChatActivity.this, url, true, !forceNoIV, true, forceNotInternalForApps, makeProgressForLink(cell, span), themeDelegate); } else if (type == 1) { - AlertsCreator.showOpenUrlAlert(ChatActivity.this, url, true, true, false, makeProgressForLink(cell, span), themeDelegate); + AlertsCreator.showOpenUrlAlert(ChatActivity.this, url, true, !forceNoIV, false, makeProgressForLink(cell, span), themeDelegate); } } else { if (type == 0) { - Browser.openUrl(getParentActivity(), Uri.parse(url), true, true, makeProgressForLink(cell, span)); + Browser.openUrl(getParentActivity(), Uri.parse(url), true, !forceNoIV, makeProgressForLink(cell, span)); } else if (type == 1) { - Browser.openUrl(getParentActivity(), Uri.parse(url), inlineReturn == 0, false, makeProgressForLink(cell, span)); + Browser.openUrl(getParentActivity(), Uri.parse(url), inlineReturn == 0, !forceNoIV, makeProgressForLink(cell, span)); } else if (type == 2) { - Browser.openUrl(getParentActivity(), Uri.parse(url), inlineReturn == 0, true, makeProgressForLink(cell, span)); + Browser.openUrl(getParentActivity(), Uri.parse(url), inlineReturn == 0, !forceNoIV, makeProgressForLink(cell, span)); } } } @@ -32669,69 +32958,21 @@ private void didPressMessageUrl(CharacterStyle url, boolean longPress, MessageOb didLongPressCard(cell, url, str.substring(5)); cell.resetPressedLink(-1); } else { - openClickableLink(url, str, longPress, cell, messageObject); + openClickableLink(url, str, longPress, cell, messageObject, false); } } else { final String urlFinal = ((URLSpan) url).getURL(); - if (longPress) { - final ChatMessageCell finalCell = cell; - BottomSheet.Builder builder = new BottomSheet.Builder(getParentActivity(), false, themeDelegate); - String formattedUrl = urlFinal; - try { - try { - Uri uri = Uri.parse(formattedUrl); - formattedUrl = Browser.replaceHostname(uri, IDN.toUnicode(uri.getHost(), IDN.ALLOW_UNASSIGNED)); - } catch (Exception e) { - FileLog.e(e, false); - } - formattedUrl = URLDecoder.decode(formattedUrl.replaceAll("\\+", "%2b"), "UTF-8"); - } catch (Exception e) { - FileLog.e(e); + if (urlFinal != null && urlFinal.startsWith("tel:")) { + didPressPhoneNumber(cell, url, urlFinal.substring(4)); + } else if (longPress) { + didLongPressLink(cell, messageObject, url, urlFinal); + if (cell != null) { + cell.resetPressedLink(-1); } - builder.setTitle(formattedUrl); - builder.setTitleMultipleLines(true); - final MessageObject finalMessageObject = messageObject; - builder.setItems(noforwards ? new CharSequence[] {LocaleController.getString("Open", R.string.Open)} : new CharSequence[]{LocaleController.getString("Open", R.string.Open), LocaleController.getString("Copy", R.string.Copy)}, (dialog, which) -> { - if (which == 0) { - logSponsoredClicked(finalMessageObject); - processExternalUrl(1, urlFinal, url, finalCell, false); - } else if (which == 1) { - String url1 = urlFinal; - boolean tel = false; - boolean mail = false; - if (url1.startsWith("mailto:")) { - url1 = url1.substring(7); - mail = true; - } else if (url1.startsWith("tel:")) { - url1 = url1.substring(4); - tel = true; - } - AndroidUtilities.addToClipboard(url1); - createUndoView(); - if (undoView == null) { - return; - } - if (mail) { - undoView.showWithAction(0, UndoView.ACTION_EMAIL_COPIED, null); - } else if (tel) { - undoView.showWithAction(0, UndoView.ACTION_PHONE_COPIED, null); - } else { - undoView.showWithAction(0, UndoView.ACTION_LINK_COPIED, null); - } - } - }); - builder.setOnPreDismissListener(di -> { - if (finalCell != null) { - finalCell.resetPressedLink(-1); - } - }); - showDialog(builder.create()); } else { logSponsoredClicked(messageObject); boolean forceAlert = url instanceof URLSpanReplacement; - if (urlFinal != null && urlFinal.startsWith("tel:")) { - didPressPhoneNumber(cell, url, urlFinal.substring(4)); - } else if (url instanceof URLSpanReplacement && (urlFinal == null || !urlFinal.startsWith("mailto:")) || AndroidUtilities.shouldShowUrlInAlert(urlFinal)) { + if (url instanceof URLSpanReplacement && (urlFinal == null || !urlFinal.startsWith("mailto:")) || AndroidUtilities.shouldShowUrlInAlert(urlFinal)) { if (openLinkInternally(urlFinal, cell, url, messageObject != null ? messageObject.getId() : 0)) { return; } @@ -32753,7 +32994,7 @@ private void didPressMessageUrl(CharacterStyle url, boolean longPress, MessageOb if (Browser.urlMustNotHaveConfirmation(urlFinal)) { forceAlert = false; } - processExternalUrl(2, urlFinal, url, cell, forceAlert); + processExternalUrl(2, urlFinal, url, cell, forceAlert, false); } } } @@ -33303,7 +33544,7 @@ public boolean canDrawOutboundsContent() { hideFieldPanel(false); } } else { - processExternalUrl(0, url, null, null, false); + processExternalUrl(0, url, null, null, false, false); } }); } else if (viewType == 4) { @@ -34461,6 +34702,7 @@ public void onSearchPressed(EditText editText) { searchingHashtag = "#" + searchingQuery; } searchingQuery = searchingHashtag; + checkHashtagStories(true); HashtagSearchController.getInstance(currentAccount).putToHistory(searchingHashtag); hashtagHistoryView.update(); View view = searchViewPager.getCurrentView(); @@ -35269,7 +35511,7 @@ public void didLongPressBotButton(ChatMessageCell cell, TLRPC.KeyboardButton but return; } if (button instanceof TLRPC.TL_keyboardButtonUrl) { - openClickableLink(null, button.url, true, cell, cell.getMessageObject()); + openClickableLink(null, button.url, true, cell, cell.getMessageObject(), false); try { cell.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } catch (Exception ignore) {} @@ -35290,7 +35532,7 @@ public void didPressReaction(ChatMessageCell cell, TLRPC.ReactionCount reaction, return; } if (longpress || cell.areTags() && (isInsideContainer || searchingReaction != null && searchingReaction.isSame(reaction.reaction))) { - if (!ChatObject.isChannelAndNotMegaGroup(currentChat) || dialog_id >= 0) { + if (/*!ChatObject.isChannelAndNotMegaGroup(currentChat) || dialog_id >= 0*/ true) { cell.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); FrameLayout scrimPopupContainerLayout = new FrameLayout(getParentActivity()) { @Override @@ -35338,10 +35580,12 @@ protected void dispatchDraw(Canvas canvas) { shadowDrawable2.getPadding(backgroundPaddings); scrimPopupContainerLayout.setBackground(shadowDrawable2); + MessageObject messageObject = cell.getMessageObject(); float bottom = cell.reactionsLayoutInBubble.y + button.y + AndroidUtilities.dp(28); float left = cell.reactionsLayoutInBubble.x + button.x; int[] loc = new int[2]; cell.getLocationInWindow(loc); + boolean forceBottom = false; final boolean tags = getUserConfig().getClientUserId() == getDialogId() && !getMessagesController().getSavedMessagesController().unsupported; if (tags) { MessagesController.getGlobalMainSettings().edit().putInt("savedsearchtaghint", 1).apply(); @@ -35379,8 +35623,8 @@ protected void dispatchDraw(Canvas canvas) { removeTag.setColors(Theme.getColor(Theme.key_color_red), Theme.getColor(Theme.key_color_red)); popupLayout.addView(removeTag); scrimPopupContainerLayout.addView(popupLayout); - } else { - scrimPopupContainerLayout.addView(new ReactedUsersListView(getParentActivity(), themeDelegate, currentAccount, cell.getPrimaryMessageObject(), reaction, false) + } else if (messageObject != null && messageObject.messageOwner != null && messageObject.messageOwner.reactions != null && messageObject.messageOwner.reactions.can_see_list || dialog_id >= 0) { + scrimPopupContainerLayout.addView(new ReactedUsersListView(getParentActivity(), themeDelegate, currentAccount, cell.getPrimaryMessageObject(), reaction, false, false) .setOnCustomEmojiSelectedListener((reactedUsersListView1, customEmojiStickerSets) -> { EmojiPacksAlert alert = new EmojiPacksAlert(ChatActivity.this, getParentActivity(), themeDelegate, customEmojiStickerSets) { @Override @@ -35407,7 +35651,40 @@ public void dismiss() { presentFragment(fragment); closeMenu(); }), LayoutHelper.createFrame(240, LayoutHelper.WRAP_CONTENT)); - } + } else if (reaction.reaction instanceof TLRPC.TL_reactionCustomEmoji) { + TLRPC.TL_reactionCustomEmoji customEmoji = (TLRPC.TL_reactionCustomEmoji) reaction.reaction; + TLRPC.InputStickerSet inputStickerSet = AnimatedEmojiDrawable.getDocumentFetcher(currentAccount).findStickerSet(customEmoji.document_id); + if (inputStickerSet != null) { + ArrayList arr = new ArrayList(); + arr.add(inputStickerSet); + MessageContainsEmojiButton setButton = new MessageContainsEmojiButton(currentAccount, getContext(), themeDelegate, arr, MessageContainsEmojiButton.SINGLE_REACTION_TYPE); + setButton.setOnClickListener(v -> { + new EmojiPacksAlert(new BaseFragment() { + @Override + public int getCurrentAccount() { + return currentAccount; + } + + @Override + public Context getContext() { + return ChatActivity.this.getContext(); + } + + @Override + public Theme.ResourcesProvider getResourceProvider() { + return themeDelegate; + } + }, getContext(), themeDelegate, arr).show(); + closeMenu(); + }); + scrimPopupContainerLayout.addView(setButton, LayoutHelper.createFrame(240, LayoutHelper.WRAP_CONTENT)); + forceBottom = true; + } else { + scrimPopupContainerLayout.setVisibility(View.GONE); + } + } else { + scrimPopupContainerLayout.setVisibility(View.GONE); + } scrimPopupWindow = new ActionBarPopupWindow(scrimPopupContainerLayout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT) { @Override @@ -35463,21 +35740,27 @@ public void dismiss() { cell.getLocationInWindow(location); cellY = location[1]; } - if (height < totalHeight / 2f && cellY + cell.reactionsLayoutInBubble.y + button.y > totalHeight / 2f) { + if (height < totalHeight / 2f && cellY + cell.reactionsLayoutInBubble.y + button.y > totalHeight / 2f && !forceBottom) { + scrimViewReactionOffset = -(height - dp(12)); popupY = (int) (cellY + cell.reactionsLayoutInBubble.y + button.y - height); } else { + scrimViewReactionOffset = 0; popupY = (int) (cellY + cell.reactionsLayoutInBubble.y + button.y + button.height); } } else { + scrimViewReactionOffset = 0; popupY = inBubbleMode ? 0 : AndroidUtilities.statusBarHeight; } + if (scrimPopupContainerLayout.getVisibility() != View.VISIBLE) { + scrimViewReactionOffset = 0; + } scrimPopupWindow.showAtLocation(chatListView, Gravity.LEFT | Gravity.TOP, scrimPopupX = popupX, scrimPopupY = popupY); chatListView.stopScroll(); chatLayoutManager.setCanScrollVertically(false); scrimViewReaction = reaction.reaction.hashCode(); scrimViewReactionAnimated = reaction.reaction instanceof TLRPC.TL_reactionCustomEmoji && LiteMode.isEnabled(LiteMode.FLAG_ANIMATED_EMOJI_KEYBOARD); - dimBehindView(cell, true); + dimBehindView(cell, !tags, true); hideHints(false); if (topUndoView != null) { topUndoView.hide(true, 1); @@ -35979,6 +36262,121 @@ public void needReloadPolls() { invalidateMessagesVisiblePart(); } + @Override + public void didPressGroupImage(ChatMessageCell cell, ImageReceiver imageReceiver, TLRPC.MessageExtendedMedia media, float x, float y) { + final MessageObject message = cell.getMessageObject(); + if (media instanceof TLRPC.TL_messageExtendedMediaPreview) { + final Browser.Progress progress = makeProgressForPaidMedia(cell); + Runnable cancel = StarsController.getInstance(currentAccount).pay(message, progress::end); + if (cancel != null) { + progress.onCancel(cancel); + progress.init(); + } + return; + } + final TLRPC.Message omsg = message.messageOwner; + if (!(message.messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia)) return; + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) message.messageOwner.media; + ArrayList messages = new ArrayList<>(); + int index = -1; + for (int i = 0; i < paidMedia.extended_media.size(); ++i) { + TLRPC.MessageExtendedMedia extMedia = paidMedia.extended_media.get(i); + if (extMedia instanceof TLRPC.TL_messageExtendedMedia) { + TLRPC.TL_messageExtendedMedia emedia = (TLRPC.TL_messageExtendedMedia) extMedia; + if (emedia == media) { + index = messages.size(); + } + TLRPC.TL_message msg = new TLRPC.TL_message(); + msg.id = omsg.id; + msg.from_id = omsg.from_id; + msg.from_boosts_applied = omsg.from_boosts_applied; + msg.peer_id = omsg.peer_id; + msg.saved_peer_id = omsg.saved_peer_id; + msg.date = omsg.date; + msg.expire_date = omsg.expire_date; + msg.action = omsg.action; + msg.message = omsg.message; + msg.flags = omsg.flags; + msg.flags2 = omsg.flags2; + msg.mentioned = omsg.mentioned; + msg.media_unread = omsg.media_unread; + msg.out = omsg.out; + msg.unread = omsg.unread; + msg.entities = omsg.entities; + msg.via_bot_name = omsg.via_bot_name; + msg.reply_markup = omsg.reply_markup; + msg.views = omsg.views; + msg.forwards = omsg.forwards; + msg.replies = omsg.replies; + msg.edit_date = omsg.edit_date; + msg.silent = omsg.silent; + msg.post = omsg.post; + msg.from_scheduled = omsg.from_scheduled; + msg.legacy = omsg.legacy; + msg.edit_hide = omsg.edit_hide; + msg.pinned = omsg.pinned; + msg.fwd_from = omsg.fwd_from; + msg.via_bot_id = omsg.via_bot_id; + msg.via_business_bot_id = omsg.via_business_bot_id; + msg.reply_to = omsg.reply_to; + msg.post_author = omsg.post_author; + msg.grouped_id = omsg.grouped_id; + msg.reactions = omsg.reactions; + msg.restriction_reason = omsg.restriction_reason; + msg.ttl_period = omsg.ttl_period; + msg.quick_reply_shortcut_id = omsg.quick_reply_shortcut_id; + msg.effect = omsg.effect; + msg.noforwards = omsg.noforwards; + msg.invert_media = omsg.invert_media; + msg.offline = omsg.offline; + msg.factcheck = omsg.factcheck; + msg.send_state = omsg.send_state; + msg.fwd_msg_id = omsg.fwd_msg_id; + if (!TextUtils.isEmpty(emedia.attachPath)) { + msg.attachPath = emedia.attachPath; + } else if (paidMedia.extended_media.size() == 1) { + msg.attachPath = omsg.attachPath; + } + msg.params = omsg.params; + msg.random_id = omsg.random_id; + msg.local_id = omsg.local_id; + msg.dialog_id = omsg.dialog_id; + msg.ttl = omsg.ttl; + msg.destroyTime = omsg.destroyTime; + msg.destroyTimeMillis = omsg.destroyTimeMillis; + msg.layer = omsg.layer; + msg.seq_in = omsg.seq_in; + msg.seq_out = omsg.seq_out; + msg.with_my_score = omsg.with_my_score; + msg.replyMessage = omsg.replyMessage; + msg.reqId = omsg.reqId; + msg.realId = omsg.realId; + msg.stickerVerified = omsg.stickerVerified; + msg.isThreadMessage = omsg.isThreadMessage; + msg.voiceTranscription = omsg.voiceTranscription; + msg.voiceTranscriptionOpen = omsg.voiceTranscriptionOpen; + msg.voiceTranscriptionRated = omsg.voiceTranscriptionRated; + msg.voiceTranscriptionFinal = omsg.voiceTranscriptionFinal; + msg.voiceTranscriptionForce = omsg.voiceTranscriptionForce; + msg.voiceTranscriptionId = omsg.voiceTranscriptionId; + msg.premiumEffectWasPlayed = omsg.premiumEffectWasPlayed; + msg.originalLanguage = omsg.originalLanguage; + msg.translatedToLanguage = omsg.translatedToLanguage; + msg.translatedText = omsg.translatedText; + msg.replyStory = omsg.replyStory; + msg.media = emedia.media; + msg.quick_reply_shortcut = omsg.quick_reply_shortcut; + msg.noforwards = true; + MessageObject msgObj = new MessageObject(message.currentAccount, msg, false, true); + messages.add(msgObj); + } + } + if (index <= -1 || messages.isEmpty()) return; + + PhotoViewer.getInstance().setParentActivity(ChatActivity.this, themeDelegate); + PhotoViewer.getInstance().openPhoto(messages, index, getDialogId(), 0, 0, photoViewerPaidMediaProvider); + } + @Override public void didPressImage(ChatMessageCell cell, float x, float y) { MessageObject message = cell.getMessageObject(); @@ -36367,9 +36765,7 @@ public void didPressGiveawayChatButton(ChatMessageCell cell, int pressedPos) { if (dialog_id != -channelId) { presentFragment(ChatActivity.of(-channelId)); } else { - ViewGroup v = getChatListView(); - AndroidUtilities.shakeViewSpring(v, 5); - BotWebViewVibrationEffect.APP_ERROR.vibrate(); + avatarContainer.openProfile(false); } } if (cell.getMessageObject().messageOwner.media instanceof TLRPC.TL_messageMediaGiveawayResults) { @@ -38717,6 +39113,98 @@ public void showPremiumFloodWaitBulletin(final boolean isUpload) { ).setDuration(8000).show(true); } + public void didLongPressLink(ChatMessageCell cell, MessageObject messageObject, CharacterStyle span, String str) { + final ItemOptions options = ItemOptions.makeOptions(ChatActivity.this, cell, true); + final ScrimOptions dialog = new ScrimOptions(getContext(), themeDelegate); + options.setOnDismiss(dialog::dismissFast); + + options.add(R.drawable.msg_openin, getString(R.string.Open), () -> { + if (str.startsWith("video?")) { + didPressMessageUrl(span, false, messageObject, cell); + } else { + logSponsoredClicked(messageObject); + openClickableLink(span, str, false, cell, messageObject, false); + } + }); + + TLRPC.MessageMedia media = MessageObject.getMedia(messageObject); + if (media instanceof TLRPC.TL_messageMediaWebPage && media.webpage != null && media.webpage.cached_page != null && TextUtils.equals(media.webpage.url, str)) { + options.add(R.drawable.menu_instant_view, getString(R.string.OpenInstantView), () -> { + if (messageObject.messageOwner.media != null && messageObject.messageOwner.media.webpage != null && messageObject.messageOwner.media.webpage.cached_page != null) { + ArticleViewer.getInstance().setParentActivity(getParentActivity(), ChatActivity.this); + ArticleViewer.getInstance().open(messageObject); + } + }); + } + + options.add(R.drawable.msg_copy, getString(R.string.CopyLink), () -> { + if (str.startsWith("video?") && messageObject != null && !messageObject.scheduled) { + MessageObject messageObject1 = messageObject; + boolean isMedia = messageObject.isVideo() || messageObject.isRoundVideo() || messageObject.isVoice() || messageObject.isMusic(); + if (!isMedia && messageObject.replyMessageObject != null) { + messageObject1 = messageObject.replyMessageObject; + } + long dialogId = messageObject1.getDialogId(); + int messageId = messageObject1.getId(); + String link = null; + + if (messageObject1.messageOwner.fwd_from != null) { + if (messageObject1.messageOwner.fwd_from.saved_from_peer != null) { + dialogId = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.saved_from_peer); + messageId = messageObject1.messageOwner.fwd_from.saved_from_msg_id; + } else if (messageObject1.messageOwner.fwd_from.from_id != null) { + dialogId = MessageObject.getPeerId(messageObject1.messageOwner.fwd_from.from_id); + messageId = messageObject1.messageOwner.fwd_from.channel_post; + } + } + int timestamp = -1; + if (str.startsWith("video?")) { + timestamp = Utilities.parseInt(str); + } + if (DialogObject.isChatDialog(dialogId)) { + TLRPC.Chat currentChat = MessagesController.getInstance(currentAccount).getChat(-dialogId); + String username = ChatObject.getPublicUsername(currentChat); + if (currentChat != null && username != null) { + link = "https://t.me/" + username + "/" + messageId + "?t=" + timestamp; + } + } else { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); + String username = UserObject.getPublicUsername(user); + if (user != null && username != null) { + link = "https://t.me/" + username + "/" + messageId + "?t=" + timestamp; + } + } + if (link == null) { + return; + } + AndroidUtilities.addToClipboard(link); + } else { + AndroidUtilities.addToClipboard(str); + } + createUndoView(); + if (undoView == null) { + return; + } + if (str.startsWith("@")) { + undoView.showWithAction(0, UndoView.ACTION_USERNAME_COPIED, null); + } else if (str.startsWith("#") || str.startsWith("$")) { + undoView.showWithAction(0, UndoView.ACTION_HASHTAG_COPIED, null); + } else { + undoView.showWithAction(0, UndoView.ACTION_LINK_COPIED, null); + } + }); + + dialog.setItemOptions(options); + if (span instanceof URLSpanReplacement) { + SpannableString s = new SpannableString(((URLSpanReplacement) span).getURL()); + s.setSpan(span, 0, s.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + dialog.setScrim(cell, span, s); + } else { + dialog.setScrim(cell, span, null); + } + showDialog(dialog); + } + public void didLongPressCard(ChatMessageCell cell, CharacterStyle link, String card) { final Browser.Progress progress = makeProgressForLink(cell, link); TLRPC.TL_payments_getBankCardData req = new TLRPC.TL_payments_getBankCardData(); @@ -38746,7 +39234,7 @@ public void didLongPressCard(ChatMessageCell cell, CharacterStyle link, String c } dialog.setItemOptions(options); - dialog.setScrim(cell, link); + dialog.setScrim(cell, link, null); showDialog(dialog); }), null, null, 0, getMessagesController().webFileDatacenterId, ConnectionsManager.ConnectionTypeGeneric, true); progress.onCancel(() -> getConnectionsManager().cancelRequest(reqId, true)); @@ -38784,6 +39272,11 @@ public void didLongPressUsername(ChatMessageCell cell, CharacterStyle link, Stri AndroidUtilities.addToClipboard("@" + username); BulletinFactory.of(ChatActivity.this).createCopyBulletin(getString(R.string.UsernameCopied)).show(); }); + if (selling) { + options.add(R.drawable.msg_ton, getString(R.string.BuyUsernameOnFragment), () -> { + Browser.openUrl(getContext(), "https://fragment.com/username/" + username); + }); + } options.addGap(); if (did != 0) { options.addProfile(obj, getString(isUser ? R.string.ViewProfile : (isChannel ? R.string.ViewChannelProfile : R.string.ViewGroupProfile)), () -> { @@ -38794,34 +39287,70 @@ public void didLongPressUsername(ChatMessageCell cell, CharacterStyle link, Stri } dialog.setItemOptions(options); - dialog.setScrim(cell, link); + dialog.setScrim(cell, link, null); showDialog(dialog); }; - if (cachedObject != null) { - open.run(cachedObject, false); - } else { - TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); - req.username = username; - int reqId = getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { - progress.end(); + if (BuildVars.DEBUG_PRIVATE_VERSION) { + TLRPC.TL_account_checkUsername req2 = new TLRPC.TL_account_checkUsername(); + req2.username = username; + int reqId2 = getConnectionsManager().sendRequest(req2, (res2, err2) -> AndroidUtilities.runOnUIThread(() -> { + final boolean selling = err2 != null && "USERNAME_PURCHASE_AVAILABLE".equals(err2.text); + if (cachedObject != null || err2 == null && res2 instanceof TLRPC.TL_boolTrue) { + open.run(cachedObject, selling); + } else { + TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); + req.username = username; + int reqId = getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + progress.end(); - TLObject obj = null; - if (res instanceof TLRPC.TL_contacts_resolvedPeer) { - TLRPC.TL_contacts_resolvedPeer r = (TLRPC.TL_contacts_resolvedPeer) res; - getMessagesController().putUsers(r.users, false); - getMessagesController().putChats(r.chats, false); + TLObject obj = null; + if (res instanceof TLRPC.TL_contacts_resolvedPeer) { + TLRPC.TL_contacts_resolvedPeer r = (TLRPC.TL_contacts_resolvedPeer) res; + getMessagesController().putUsers(r.users, false); + getMessagesController().putChats(r.chats, false); - long did = DialogObject.getPeerDialogId(r.peer); - if (did >= 0) { - obj = getMessagesController().getUser(did); - } else if (did < 0) { - obj = getMessagesController().getChat(-did); - } + long did = DialogObject.getPeerDialogId(r.peer); + if (did >= 0) { + obj = getMessagesController().getUser(did); + } else if (did < 0) { + obj = getMessagesController().getChat(-did); + } + } + open.run(obj, selling); + })); + progress.onCancel(() -> getConnectionsManager().cancelRequest(reqId, true)); + progress.init(); } - open.run(obj, false); })); - progress.onCancel(() -> getConnectionsManager().cancelRequest(reqId, true)); + progress.onCancel(() -> getConnectionsManager().cancelRequest(reqId2, true)); progress.init(); + } else { + if (cachedObject != null) { + open.run(cachedObject, false); + } else { + TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); + req.username = username; + int reqId = getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + progress.end(); + + TLObject obj = null; + if (res instanceof TLRPC.TL_contacts_resolvedPeer) { + TLRPC.TL_contacts_resolvedPeer r = (TLRPC.TL_contacts_resolvedPeer) res; + getMessagesController().putUsers(r.users, false); + getMessagesController().putChats(r.chats, false); + + long did = DialogObject.getPeerDialogId(r.peer); + if (did >= 0) { + obj = getMessagesController().getUser(did); + } else if (did < 0) { + obj = getMessagesController().getChat(-did); + } + } + open.run(obj, false); + })); + progress.onCancel(() -> getConnectionsManager().cancelRequest(reqId, true)); + progress.init(); + } } } @@ -38950,6 +39479,9 @@ public void didPressPhoneNumber(ChatMessageCell cell, CharacterStyle link, Strin VoIPHelper.startCall(user, true, userInfo != null && userInfo.video_calls_available, getParentActivity(), userInfo, getAccountInstance()); }); } + options.add(R.drawable.msg_calls_regular, getString(R.string.VoiceCallViaCarrier), () -> { + Browser.openUrl(getContext(), "tel:" + phone); + }); options.add(R.drawable.msg_copy, getString(R.string.CopyNumber), () -> { AndroidUtilities.addToClipboard(phone); BulletinFactory.of(this).createCopyBulletin(LocaleController.getString(R.string.PhoneCopied)).show(); @@ -38962,7 +39494,7 @@ public void didPressPhoneNumber(ChatMessageCell cell, CharacterStyle link, Strin } dialog.setItemOptions(options); - dialog.setScrim(cell, link); + dialog.setScrim(cell, link, null); showDialog(dialog); }; if (contact != null) { @@ -39063,4 +39595,10 @@ public boolean dispatchTouchEvent(MotionEvent event) { } + private void checkHashtagStories(boolean instant) { + if (searchType != SEARCH_PUBLIC_POSTS) return; + if (messagesSearchAdapter == null) return; + messagesSearchAdapter.searchStories(searchingHashtag, instant); + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java index 4e922c19e1..650dd60006 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatEditActivity.java @@ -8,6 +8,7 @@ package org.telegram.ui; +import static org.telegram.messenger.AndroidUtilities.dp; import static org.telegram.messenger.LocaleController.getString; import android.animation.Animator; @@ -29,6 +30,7 @@ import android.text.InputFilter; import android.text.InputType; import android.text.SpannableString; +import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; import android.text.TextUtils; @@ -64,6 +66,7 @@ import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; +import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; @@ -88,16 +91,21 @@ import org.telegram.ui.Components.EditTextEmoji; import org.telegram.ui.Components.ImageUpdater; import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.LoadingSpan; import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Components.RadialProgressView; import org.telegram.ui.Components.Reactions.ChatCustomReactionsEditActivity; import org.telegram.ui.Components.Reactions.ReactionsUtils; import org.telegram.ui.Components.SizeNotifierFrameLayout; import org.telegram.ui.Components.UndoView; +import org.telegram.ui.Stars.BotStarsActivity; +import org.telegram.ui.Stars.BotStarsController; +import org.telegram.ui.Stars.StarsController; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.concurrent.CountDownLatch; public class ChatEditActivity extends BaseFragment implements ImageUpdater.ImageUpdaterDelegate, NotificationCenter.NotificationCenterDelegate { @@ -153,6 +161,7 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image private ShadowSectionCell deleteInfoCell; private TextCell publicLinkCell; + private TextCell balanceCell; private TextCell editIntroCell; private TextCell editCommandsCell; private TextCell changeBotSettingsCell; @@ -255,9 +264,9 @@ public boolean onDeletePhoto(int index) { hasUploadedPhoto = true; if (cameraDrawable == null) { - cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); + cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, dp(50), dp(50), false, null); } - setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); + setAvatarCell.imageView.setTranslationX(-dp(8)); setAvatarCell.imageView.setAnimation(cameraDrawable); })); @@ -344,6 +353,9 @@ public boolean onFragmentCreate() { forum = false; canForum = false; NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.userInfoDidLoad); + if (currentUser.bot) { + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.botStarsUpdated); + } } imageUpdater.parentFragment = this; imageUpdater.setDelegate(this); @@ -381,6 +393,9 @@ public void onFragmentDestroy() { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.chatAvailableReactionsUpdated); } else { NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.userInfoDidLoad); + if (currentUser.bot) { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.botStarsUpdated); + } } NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.updateInterfaces); if (nameTextView != null) { @@ -485,7 +500,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { measureChildWithMargins(actionBar, widthMeasureSpec, 0, heightMeasureSpec, 0); int keyboardSize = measureKeyboardHeight(); - if (keyboardSize > AndroidUtilities.dp(20)) { + if (keyboardSize > dp(20)) { ignoreLayout = true; nameTextView.hideEmojiView(); ignoreLayout = false; @@ -500,7 +515,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (nameTextView != null && nameTextView.isPopupView(child)) { if (AndroidUtilities.isInMultiwindow || AndroidUtilities.isTablet()) { if (AndroidUtilities.isTablet()) { - child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(Math.min(AndroidUtilities.dp(AndroidUtilities.isTablet() ? 200 : 320), heightSize - AndroidUtilities.statusBarHeight + getPaddingTop()), MeasureSpec.EXACTLY)); + child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(Math.min(dp(AndroidUtilities.isTablet() ? 200 : 320), heightSize - AndroidUtilities.statusBarHeight + getPaddingTop()), MeasureSpec.EXACTLY)); } else { child.measure(MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(heightSize - AndroidUtilities.statusBarHeight + getPaddingTop(), MeasureSpec.EXACTLY)); } @@ -518,7 +533,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { final int count = getChildCount(); int keyboardSize = measureKeyboardHeight(); - int paddingBottom = keyboardSize <= AndroidUtilities.dp(20) && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isTablet() ? nameTextView.getEmojiPadding() : 0; + int paddingBottom = keyboardSize <= dp(20) && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isTablet() ? nameTextView.getEmojiPadding() : 0; setBottomClip(paddingBottom); for (int i = 0; i < count; i++) { @@ -629,7 +644,7 @@ public void invalidate(int l, int t, int r, int b) { super.invalidate(l, t, r, b); } }; - avatarImage.setRoundRadius(forum ? AndroidUtilities.dp(16) : AndroidUtilities.dp(32)); + avatarImage.setRoundRadius(forum ? dp(16) : dp(32)); if (currentUser != null || ChatObject.canChangeChatInfo(currentChat)) { frameLayout.addView(avatarImage, LayoutHelper.createFrame(64, 64, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 16, 12, LocaleController.isRTL ? 16 : 0, 8)); @@ -649,7 +664,7 @@ protected void onDraw(Canvas canvas) { frameLayout.addView(avatarOverlay, LayoutHelper.createFrame(64, 64, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 16, 12, LocaleController.isRTL ? 16 : 0, 8)); avatarProgressView = new RadialProgressView(context); - avatarProgressView.setSize(AndroidUtilities.dp(30)); + avatarProgressView.setSize(dp(30)); avatarProgressView.setProgressColor(0xffffffff); avatarProgressView.setNoProgress(false); frameLayout.addView(avatarProgressView, LayoutHelper.createFrame(64, 64, Gravity.TOP | (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT), LocaleController.isRTL ? 0 : 16, 12, LocaleController.isRTL ? 16 : 0, 8)); @@ -734,7 +749,7 @@ public void afterTextChanged(Editable s) { setAvatarCell = new TextCell(context) { @Override protected void onDraw(Canvas canvas) { - canvas.drawLine(LocaleController.isRTL ? 0 : AndroidUtilities.dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? AndroidUtilities.dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); + canvas.drawLine(LocaleController.isRTL ? 0 : dp(20), getMeasuredHeight() - 1, getMeasuredWidth() - (LocaleController.isRTL ? dp(20) : 0), getMeasuredHeight() - 1, Theme.dividerPaint); } }; setAvatarCell.setBackgroundDrawable(Theme.getSelectorDrawable(false)); @@ -760,9 +775,9 @@ protected void onDraw(Canvas canvas) { hasUploadedPhoto = true; if (cameraDrawable == null) { - cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); + cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, dp(50), dp(50), false, null); } - setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); + setAvatarCell.imageView.setTranslationX(-dp(8)); setAvatarCell.imageView.setAnimation(cameraDrawable); })); } @@ -790,7 +805,7 @@ protected void onDraw(Canvas canvas) { descriptionTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); descriptionTextView.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); descriptionTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - descriptionTextView.setPadding(0, 0, 0, AndroidUtilities.dp(6)); + descriptionTextView.setPadding(0, 0, 0, dp(6)); descriptionTextView.setBackgroundDrawable(null); descriptionTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); descriptionTextView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); @@ -802,7 +817,7 @@ protected void onDraw(Canvas canvas) { descriptionTextView.setFilters(inputFilters); descriptionTextView.setHint(getString("DescriptionOptionalPlaceholder", R.string.DescriptionOptionalPlaceholder)); descriptionTextView.setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); - descriptionTextView.setCursorSize(AndroidUtilities.dp(20)); + descriptionTextView.setCursorSize(dp(20)); descriptionTextView.setCursorWidth(1.5f); if (descriptionTextView.isEnabled()) { settingsContainer.addView(descriptionTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 23, 15, 23, 9)); @@ -995,7 +1010,7 @@ public void afterTextChanged(Editable editable) { return; } forum = !forum; - avatarImage.animateToRoundRadius(forum ? AndroidUtilities.dp(16) : AndroidUtilities.dp(32)); + avatarImage.animateToRoundRadius(forum ? dp(16) : dp(32)); ((TextCell) v).setChecked(forum); updateFields(false, true); }); @@ -1006,7 +1021,7 @@ public void afterTextChanged(Editable editable) { ActionBarMenu menu = actionBar.createMenu(); if (currentUser != null || ChatObject.canChangeChatInfo(currentChat) || signCell != null || historyCell != null) { - doneButton = menu.addItemWithWidth(done_button, R.drawable.ic_ab_done, AndroidUtilities.dp(56)); + doneButton = menu.addItemWithWidth(done_button, R.drawable.ic_ab_done, dp(56)); doneButton.setContentDescription(getString("Done", R.string.Done)); } @@ -1150,8 +1165,32 @@ public void afterTextChanged(Editable editable) { presentFragment(new ChangeUsernameActivity(args)); }); + if (currentUser.bot && currentUser.bot_can_edit) { + balanceCell = new TextCell(context); + balanceCell.setBackground(Theme.getSelectorDrawable(false)); + balanceCell.setPrioritizeTitleOverValue(true); + infoContainer.addView(balanceCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + BotStarsController c = BotStarsController.getInstance(currentAccount); + balanceCell.setOnClickListener(v -> { + if (!c.isBalanceAvailable(userId)) + return; + presentFragment(new BotStarsActivity(userId)); + }); + if (!c.isBalanceAvailable(userId)) { + SpannableStringBuilder loadingStr = new SpannableStringBuilder("x"); + loadingStr.setSpan(new LoadingSpan(balanceCell.valueTextView, dp(30)), 0, loadingStr.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + balanceCell.setTextAndValueAndIcon(getString(R.string.BotBalance), loadingStr, R.drawable.menu_premium_main, false); + } else { + balanceCell.setTextAndValueAndIcon(getString(R.string.BotBalance), LocaleController.formatNumber(c.getBalance(userId), ' '), R.drawable.menu_premium_main, false); + } + balanceCell.setVisibility(c.hasStars(userId) ? View.VISIBLE : View.GONE); + } updatePublicLinksCount(); + ActionBarPopupWindow.GapView gap = new ActionBarPopupWindow.GapView(context, getResourceProvider(), Theme.key_windowBackgroundGray); + gap.setTag(R.id.fit_width_tag, 1); + infoContainer.addView(gap, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 8)); + editIntroCell = new TextCell(context); editIntroCell.setBackground(Theme.getSelectorDrawable(false)); editIntroCell.setTextAndIcon(getString(R.string.BotEditIntro), R.drawable.msg_log, true); @@ -1247,7 +1286,7 @@ public void updateDrawState(TextPaint ds) { undoView = new UndoView(context); sizeNotifierFrameLayout.addView(undoView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.LEFT, 8, 0, 8, 8)); - nameTextView.setText(Emoji.replaceEmoji(currentUser != null ? ContactsController.formatName(currentUser) : currentChat.title, nameTextView.getEditText().getPaint().getFontMetricsInt(), AndroidUtilities.dp(16), true)); + nameTextView.setText(Emoji.replaceEmoji(currentUser != null ? ContactsController.formatName(currentUser) : currentChat.title, nameTextView.getEditText().getPaint().getFontMetricsInt(), dp(16), true)); nameTextView.setSelection(nameTextView.length()); if (info != null) { descriptionTextView.setText(info.about); @@ -1272,9 +1311,9 @@ private void updatePublicLinksCount() { } } - publicLinkCell.setTextAndValueAndIcon(getString(R.string.BotPublicLinks), LocaleController.formatString(R.string.BotPublicLinksCount, usernamesActive, currentUser.usernames.size()), R.drawable.msg_link2, true); + publicLinkCell.setTextAndValueAndIcon(getString(R.string.BotPublicLinks), LocaleController.formatString(R.string.BotPublicLinksCount, usernamesActive, currentUser.usernames.size()), R.drawable.msg_link2, balanceCell != null && balanceCell.getVisibility() == View.VISIBLE); } else { - publicLinkCell.setTextAndValueAndIcon(getString(R.string.BotPublicLink), "t.me/" + currentUser.username, R.drawable.msg_link2, true); + publicLinkCell.setTextAndValueAndIcon(getString(R.string.BotPublicLink), "t.me/" + currentUser.username, R.drawable.msg_link2, balanceCell != null && balanceCell.getVisibility() == View.VISIBLE); } } @@ -1323,9 +1362,9 @@ private void setAvatar() { setAvatarCell.setTextAndIcon(getString("ChatSetPhotoOrVideo", R.string.ChatSetPhotoOrVideo), R.drawable.msg_addphoto, true); } if (cameraDrawable == null) { - cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); + cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, dp(50), dp(50), false, null); } - setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); + setAvatarCell.imageView.setTranslationX(-dp(8)); setAvatarCell.imageView.setAnimation(cameraDrawable); } if (PhotoViewer.hasInstance() && PhotoViewer.getInstance().isVisible()) { @@ -1378,6 +1417,17 @@ public void didReceivedNotification(int id, int account, Object... args) { } updateReactionsCell(true); } + } else if (id == NotificationCenter.botStarsUpdated) { + if ((long) args[0] == userId) { + if (balanceCell != null) { + BotStarsController c = BotStarsController.getInstance(currentAccount); + balanceCell.setVisibility(c.hasStars(userId) ? View.VISIBLE : View.GONE); + balanceCell.setValue(LocaleController.formatNumber(c.getBalance(userId), ' '), true); + if (publicLinkCell != null) { + publicLinkCell.setNeedDivider(c.hasStars(userId)); + } + } + } } } @@ -1455,9 +1505,9 @@ public void didUploadPhoto(final TLRPC.InputFile photo, final TLRPC.InputFile vi avatarImage.setImage(ImageLocation.getForLocal(avatar), "50_50", avatarDrawable, currentUser != null ? currentUser : currentChat); setAvatarCell.setTextAndIcon(getString("ChatSetNewPhoto", R.string.ChatSetNewPhoto), R.drawable.msg_addphoto, true); if (cameraDrawable == null) { - cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null); + cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, dp(50), dp(50), false, null); } - setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8)); + setAvatarCell.imageView.setTranslationX(-dp(8)); setAvatarCell.imageView.setAnimation(cameraDrawable); showAvatarProgress(true, false); } 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 9d1614e933..e754591e16 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiDrawable.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/AnimatedEmojiDrawable.java @@ -79,6 +79,7 @@ public class AnimatedEmojiDrawable extends Drawable { public static final int CACHE_TYPE_ALERT_STANDARD_EMOJI = 21; public static final int CACHE_TYPE_FORUM_TOPIC_PULL_DOWN = 22; public static final int CACHE_TYPE_MESSAGE_EFFECT_MINI = 23; + public static final int CACHE_TYPE_ALERT_PREVIEW_LARGE_140 = 24; public int rawDrawIndex; @@ -498,7 +499,7 @@ public void setupEmojiThumb(String emoji) { private void updateSize() { if (this.cacheType == CACHE_TYPE_MESSAGES) { sizedp = (int) ((Math.abs(Theme.chat_msgTextPaint.ascent()) + Math.abs(Theme.chat_msgTextPaint.descent())) * 1.15f / AndroidUtilities.density); - } else if (this.cacheType == CACHE_TYPE_MESSAGES_LARGE || this.cacheType == CACHE_TYPE_ALERT_PREVIEW_LARGE || this.cacheType == CACHE_TYPE_COLORABLE || this.cacheType == CACHE_TYPE_STANDARD_EMOJI || this.cacheType == CACHE_TYPE_ALERT_STANDARD_EMOJI) { + } else if (this.cacheType == CACHE_TYPE_MESSAGES_LARGE || this.cacheType == CACHE_TYPE_ALERT_PREVIEW_LARGE || this.cacheType == CACHE_TYPE_COLORABLE || this.cacheType == CACHE_TYPE_STANDARD_EMOJI) { sizedp = (int) ((Math.abs(Theme.chat_msgTextPaintEmoji[2].ascent()) + Math.abs(Theme.chat_msgTextPaintEmoji[2].descent())) * 1.15f / AndroidUtilities.density); } else if (this.cacheType == STANDARD_LOTTIE_FRAME) { sizedp = (int) ((Math.abs(Theme.chat_msgTextPaintEmoji[0].ascent()) + Math.abs(Theme.chat_msgTextPaintEmoji[0].descent())) * 1.15f / AndroidUtilities.density); @@ -506,6 +507,8 @@ private void updateSize() { sizedp = 100; } else if (cacheType == CACHE_TYPE_FORUM_TOPIC_LARGE || cacheType == CACHE_TYPE_FORUM_TOPIC_PULL_DOWN) { sizedp = 56; + } else if (cacheType == CACHE_TYPE_ALERT_PREVIEW_LARGE_140) { + sizedp = 140; } else if (this.cacheType == CACHE_TYPE_MESSAGE_EFFECT_MINI) { sizedp = 14; } else { @@ -571,6 +574,9 @@ private void initDocument(boolean force) { if (cacheType == CACHE_TYPE_ALERT_PREVIEW_STATIC || cacheType == CACHE_TYPE_ALERT_PREVIEW_STATIC_WITH_THUMB) { onlyStaticPreview = true; } + if (cacheType == CACHE_TYPE_ALERT_PREVIEW_LARGE_140) { + onlyStaticPreview = false; + } String filter = sizedp + "_" + sizedp; if (cacheType == CACHE_TYPE_RENDERING_VIDEO) { filter += "_d_nostream"; @@ -650,7 +656,7 @@ private void initDocument(boolean force) { updateAutoRepeat(imageReceiver); - if (cacheType == CACHE_TYPE_ALERT_PREVIEW_STATIC || cacheType == CACHE_TYPE_ALERT_PREVIEW_STATIC_WITH_THUMB || cacheType == CACHE_TYPE_ALERT_PREVIEW || cacheType == CACHE_TYPE_ALERT_PREVIEW_TAB_STRIP || cacheType == CACHE_TYPE_ALERT_PREVIEW_LARGE) { + if (cacheType == CACHE_TYPE_ALERT_PREVIEW_STATIC || cacheType == CACHE_TYPE_ALERT_PREVIEW_STATIC_WITH_THUMB || cacheType == CACHE_TYPE_ALERT_PREVIEW || cacheType == CACHE_TYPE_ALERT_PREVIEW_TAB_STRIP || cacheType == CACHE_TYPE_ALERT_PREVIEW_LARGE || cacheType == CACHE_TYPE_ALERT_PREVIEW_LARGE_140) { imageReceiver.setLayerNum(7); } if (cacheType == CACHE_TYPE_ALERT_EMOJI_STATUS || cacheType == CACHE_TYPE_ALERT_STANDARD_EMOJI) { @@ -670,12 +676,20 @@ private void initDocument(boolean force) { int roundRadius = 0; if (cacheType == CACHE_TYPE_ALERT_PREVIEW_TAB_STRIP || cacheType == CACHE_TYPE_TAB_STRIP) { roundRadius = AndroidUtilities.dp(6); + } else if (cacheType == CACHE_TYPE_ALERT_PREVIEW_LARGE_140) { + roundRadius = AndroidUtilities.dp(14); } imageReceiver.setRoundRadius(roundRadius); updateAttachState(); invalidate(); } + public boolean preloading; + public void preload() { + this.preloading = true; + updateAttachState(); + } + 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); @@ -749,6 +763,7 @@ public void addView(View callback) { if (callback instanceof SelectAnimatedEmojiDialog.EmojiListView) { throw new RuntimeException(); } + preloading = false; if (views == null) { views = new ArrayList<>(10); } @@ -762,6 +777,7 @@ public void addView(AnimatedEmojiSpan.InvalidateHolder holder) { if (holders == null) { holders = new ArrayList<>(10); } + preloading = false; if (!holders.contains(holder)) { holders.add(holder); } @@ -772,6 +788,7 @@ public void removeView(AnimatedEmojiSpan.InvalidateHolder holder) { if (holders != null) { holders.remove(holder); } + preloading = false; updateAttachState(); } @@ -779,6 +796,7 @@ public void removeView(View view) { if (views != null) { views.remove(view); } + preloading = false; updateAttachState(); } @@ -789,7 +807,7 @@ private void updateAttachState() { if (imageReceiver == null) { return; } - boolean attach = (views != null && views.size() > 0) || (holders != null && holders.size() > 0); + boolean attach = (views != null && views.size() > 0) || (holders != null && holders.size() > 0) || preloading; if (attach != attached) { attached = attach; if (attached) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java index f53f8b54fc..cc74baa2e2 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BackupImageView.java @@ -8,17 +8,24 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.text.TextUtils; import android.view.View; +import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.SecureDocument; @@ -371,4 +378,61 @@ public void onAnimationUpdate(ValueAnimator animation) { roundRadiusAnimator.start(); } } + + public Text blurText; + private Path blurTextClipPath; + private ColorFilter blurTextBgColorFilter; + + public void setBlurredText(CharSequence str) { + if (TextUtils.isEmpty(str)) { + blurText = null; + return; + } + + blurText = new Text(str, 16.5f, AndroidUtilities.bold()); + if (blurTextBgColorFilter == null) { + ColorMatrix colorMatrix = new ColorMatrix(); + colorMatrix.setSaturation(1.2f); + AndroidUtilities.adjustBrightnessColorMatrix(colorMatrix, -.2f); + blurTextBgColorFilter = new ColorMatrixColorFilter(colorMatrix); + } + } + + public void drawBlurredText(Canvas canvas, float alpha) { + if (blurText == null) return; + + if (blurTextClipPath == null) { + blurTextClipPath = new Path(); + } else blurTextClipPath.rewind(); + + final float W, H; + if (width != -1 && height != -1) { + W = width; + H = height; + } else { + W = getMeasuredWidth(); + H = getMeasuredHeight(); + } + + final float w = blurText.getCurrentWidth() + dp(18); + final float h = dp(28); + final float l = (W - w) / 2f; + final float cy = H / 2f; + + AndroidUtilities.rectTmp.set(l, cy - h / 2f, l + w, cy + h / 2f); + blurTextClipPath.addRoundRect(AndroidUtilities.rectTmp, h / 2f, h / 2f, Path.Direction.CW); + + canvas.save(); + canvas.clipPath(blurTextClipPath); + if (blurImageReceiver != null && blurAllowed) { + blurImageReceiver.setColorFilter(blurTextBgColorFilter); + float wasAlpha = blurImageReceiver.getAlpha(); + blurImageReceiver.setAlpha(alpha); + blurImageReceiver.draw(canvas); + blurImageReceiver.setAlpha(wasAlpha); + blurImageReceiver.setColorFilter(null); + } + blurText.draw(canvas, l + dp(9), cy, 0xFFFFFFFF, alpha); + canvas.restore(); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurringShader.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurringShader.java index 0a66d4e207..59bcf9269e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurringShader.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BlurringShader.java @@ -33,6 +33,7 @@ import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.R; import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.Theme; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -983,6 +984,7 @@ public Drawable makeDrawable(float offsetX, float offsetY, Drawable base, float float alpha = 1f; private final Paint dimPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private final Rect rect = new Rect(); @Nullable private Paint getPaint() { @@ -1012,7 +1014,6 @@ private Paint getPaint() { @Override public void draw(@NonNull Canvas canvas) { Paint paint = getPaint(); - Rect bounds = getBounds(); if (paint != null) { if (base != null) { @@ -1021,12 +1022,12 @@ public void draw(@NonNull Canvas canvas) { base.draw(canvas); canvas.drawRect(bounds, paint); canvas.restore(); - getPadding(AndroidUtilities.rectTmp2); + getPadding(rect); AndroidUtilities.rectTmp.set( - bounds.left + AndroidUtilities.rectTmp2.left, - bounds.top + AndroidUtilities.rectTmp2.top, - bounds.right - AndroidUtilities.rectTmp2.right, - bounds.bottom - AndroidUtilities.rectTmp2.bottom + bounds.left + rect.left, + bounds.top + rect.top, + bounds.right - rect.right, + bounds.bottom - rect.bottom ); dimPaint.setColor(0x66000000); canvas.drawRoundRect(AndroidUtilities.rectTmp, r, r, dimPaint); 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 7d0503ad1b..3a0ef68132 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/BulletinFactory.java @@ -1,11 +1,16 @@ package org.telegram.ui.Components; +import android.app.DownloadManager; import android.content.Context; +import android.content.Intent; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Bundle; +import android.os.Environment; +import android.provider.MediaStore; import android.text.Spannable; import android.text.SpannableString; import android.text.SpannableStringBuilder; @@ -13,6 +18,7 @@ import android.text.TextPaint; import android.text.TextUtils; import android.text.style.ClickableSpan; +import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -22,6 +28,7 @@ import androidx.annotation.CheckResult; import androidx.annotation.NonNull; +import androidx.core.content.FileProvider; import androidx.core.graphics.ColorUtils; import org.telegram.messenger.AndroidUtilities; @@ -49,6 +56,7 @@ import org.telegram.ui.PremiumPreviewFragment; import org.telegram.ui.Stories.recorder.HintView2; +import java.io.File; import java.util.ArrayList; import java.util.List; @@ -100,17 +108,17 @@ public enum FileType { MEDIA("MediaSavedHint", Icon.SAVED_TO_GALLERY), - PHOTO_TO_DOWNLOADS("PhotoSavedToDownloadsHint", R.string.PhotoSavedToDownloadsHint, Icon.SAVED_TO_DOWNLOADS), - VIDEO_TO_DOWNLOADS("VideoSavedToDownloadsHint", R.string.VideoSavedToDownloadsHint, Icon.SAVED_TO_DOWNLOADS), + PHOTO_TO_DOWNLOADS("PhotoSavedToDownloadsHintLinked", R.string.PhotoSavedToDownloadsHintLinked, Icon.SAVED_TO_DOWNLOADS), + VIDEO_TO_DOWNLOADS("VideoSavedToDownloadsHintLinked", R.string.VideoSavedToDownloadsHintLinked, Icon.SAVED_TO_DOWNLOADS), GIF("GifSavedHint", R.string.GifSavedHint, Icon.SAVED_TO_GIFS), - GIF_TO_DOWNLOADS("GifSavedToDownloadsHint", R.string.GifSavedToDownloadsHint, Icon.SAVED_TO_DOWNLOADS), + GIF_TO_DOWNLOADS("GifSavedToDownloadsHintLinked", R.string.GifSavedToDownloadsHintLinked, Icon.SAVED_TO_DOWNLOADS), AUDIO("AudioSavedHint", R.string.AudioSavedHint, Icon.SAVED_TO_MUSIC), AUDIOS("AudiosSavedHint", Icon.SAVED_TO_MUSIC), - UNKNOWN("FileSavedHint", R.string.FileSavedHint, Icon.SAVED_TO_DOWNLOADS), - UNKNOWNS("FilesSavedHint", Icon.SAVED_TO_DOWNLOADS); + UNKNOWN("FileSavedHintLinked", R.string.FileSavedHintLinked, Icon.SAVED_TO_DOWNLOADS), + UNKNOWNS("FilesSavedHintLinked", Icon.SAVED_TO_DOWNLOADS); private final String localeKey; private final int localeRes; @@ -721,7 +729,13 @@ public Bulletin createDownloadBulletin(FileType fileType, int filesAmount, int b layout = new Bulletin.LottieLayout(getContext(), resourcesProvider); } layout.setAnimation(fileType.icon.resId, fileType.icon.layers); - layout.textView.setText(fileType.getText(filesAmount)); + layout.textView.setText(AndroidUtilities.replaceSingleTag(fileType.getText(filesAmount), () -> { + if (LaunchActivity.instance == null || LaunchActivity.instance.isFinishing()) return; + + Intent intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + LaunchActivity.instance.startActivity(intent); + })); if (fileType.icon.paddingBottom != 0) { layout.setIconPaddingBottom(fileType.icon.paddingBottom); } 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 0996500227..ca7ed2c90b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatActivityEnterView.java @@ -63,11 +63,7 @@ import android.text.TextPaint; import android.text.TextUtils; import android.text.TextWatcher; -import android.text.style.CharacterStyle; import android.text.style.ImageSpan; -import android.text.style.ParagraphStyle; -import android.text.style.SuggestionSpan; -import android.util.Log; import android.util.Property; import android.util.TypedValue; import android.view.ActionMode; @@ -182,6 +178,7 @@ import org.telegram.ui.bots.BotCommandsMenuContainer; import org.telegram.ui.bots.BotCommandsMenuView; import org.telegram.ui.bots.BotKeyboardView; +import org.telegram.ui.bots.BotWebViewAttachedSheet; import org.telegram.ui.bots.BotWebViewMenuContainer; import org.telegram.ui.bots.BotWebViewSheet; import org.telegram.ui.bots.ChatActivityBotWebViewButton; @@ -210,7 +207,7 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific private boolean overrideKeyboardAnimation; private Runnable animationEndRunnable; private float attachLayoutTranslationX; - private float attachLayoutPaddingTranslationX; + protected float attachLayoutPaddingTranslationX; private float attachLayoutAlpha = 1f; private float attachLayoutPaddingAlpha = 1f; private float messageTextTranslationX; @@ -392,7 +389,7 @@ default boolean onceVoiceAvailable() { private String botMenuWebViewTitle; private String botMenuWebViewUrl; - private BotWebViewMenuContainer botWebViewMenuContainer; + public BotWebViewMenuContainer botWebViewMenuContainer; private ChatActivityBotWebViewButton botWebViewButton; @Nullable @@ -2718,7 +2715,8 @@ public boolean onTouchEvent(MotionEvent motionEvent) { if (!hasRecordVideo || calledRecordRunnable) { startedDraggingX = -1; if (hasRecordVideo && isInVideoMode()) { - delegate.needStartRecordVideo(1, true, 0, voiceOnce ? 0x7FFFFFFF : 0, messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0); + delegate.needStartRecordVideo(1, true, 0, voiceOnce ? 0x7FFFFFFF : 0, effectId); + sendButton.setEffect(effectId = 0); } else { if (recordingAudioVideo && isInScheduleMode()) { AlertsCreator.createScheduleDatePickerDialog(parentActivity, parentFragment.getDialogId(), (notify, scheduleDate) -> MediaController.getInstance().stopRecording(1, notify, scheduleDate, false), () -> MediaController.getInstance().stopRecording(0, false, 0, false), resourcesProvider); @@ -2757,7 +2755,8 @@ public boolean onTouchEvent(MotionEvent motionEvent) { if (slideToCancelProgress < 0.7f) { if (hasRecordVideo && isInVideoMode()) { CameraController.getInstance().cancelOnInitRunnable(onFinishInitCameraRunnable); - delegate.needStartRecordVideo(2, true, 0, voiceOnce ? 0x7FFFFFFF : 0, messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0); + delegate.needStartRecordVideo(2, true, 0, voiceOnce ? 0x7FFFFFFF : 0, effectId); + sendButton.setEffect(effectId = 0); } else { delegate.needStartRecordAudio(0); MediaController.getInstance().stopRecording(0, false, 0, voiceOnce); @@ -2784,7 +2783,8 @@ public boolean onTouchEvent(MotionEvent motionEvent) { if (alpha < 0.45) { if (hasRecordVideo && isInVideoMode()) { CameraController.getInstance().cancelOnInitRunnable(onFinishInitCameraRunnable); - delegate.needStartRecordVideo(2, true, 0, voiceOnce ? 0x7FFFFFFF : 0, messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0); + delegate.needStartRecordVideo(2, true, 0, voiceOnce ? 0x7FFFFFFF : 0, effectId); + sendButton.setEffect(effectId = 0); } else { delegate.needStartRecordAudio(0); MediaController.getInstance().stopRecording(0, false, 0, voiceOnce); @@ -2807,7 +2807,8 @@ public boolean onTouchEvent(MotionEvent motionEvent) { startedDraggingX = -1; if (hasRecordVideo && isInVideoMode()) { CameraController.getInstance().cancelOnInitRunnable(onFinishInitCameraRunnable); - delegate.needStartRecordVideo(1, true, 0, voiceOnce ? 0x7FFFFFFF : 0, messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0); + delegate.needStartRecordVideo(1, true, 0, voiceOnce ? 0x7FFFFFFF : 0, effectId); + sendButton.setEffect(effectId = 0); } else if (!sendVoiceEnabled) { delegate.needShowMediaBanHint(); } else { @@ -2865,7 +2866,8 @@ public boolean onTouchEvent(MotionEvent motionEvent) { if (alpha == 0) { if (hasRecordVideo && isInVideoMode()) { CameraController.getInstance().cancelOnInitRunnable(onFinishInitCameraRunnable); - delegate.needStartRecordVideo(2, true, 0, voiceOnce ? 0x7FFFFFFF : 0, messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0); + delegate.needStartRecordVideo(2, true, 0, voiceOnce ? 0x7FFFFFFF : 0, effectId); + sendButton.setEffect(effectId = 0); } else { delegate.needStartRecordAudio(0); MediaController.getInstance().stopRecording(0, false, 0, voiceOnce); @@ -3554,7 +3556,8 @@ protected void dispatchDraw(Canvas canvas) { private void resetRecordedState() { if (videoToSendMessageObject != null) { CameraController.getInstance().cancelOnInitRunnable(onFinishInitCameraRunnable); - delegate.needStartRecordVideo(2, true, 0, voiceOnce ? 0x7FFFFFFF : 0, messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0); + delegate.needStartRecordVideo(2, true, 0, voiceOnce ? 0x7FFFFFFF : 0, effectId); + sendButton.setEffect(effectId = 0); } else { MessageObject playing = MediaController.getInstance().getPlayingMessageObject(); if (playing != null && playing == audioToSendMessageObject) { @@ -3965,11 +3968,20 @@ private void openWebViewMenu() { createBotWebViewMenuContainer(); Runnable onRequestWebView = () -> { AndroidUtilities.hideKeyboard(this); + BotWebViewAttachedSheet.WebViewRequestProps props = BotWebViewAttachedSheet.WebViewRequestProps.of(currentAccount, dialog_id, dialog_id, botMenuWebViewTitle, botMenuWebViewUrl, BotWebViewSheet.TYPE_BOT_MENU_BUTTON, 0, false, null, false, null, null, 0, false); + if (LaunchActivity.instance != null && LaunchActivity.instance.getBottomSheetTabs() != null && LaunchActivity.instance.getBottomSheetTabs().tryReopenTab(props) != null) { + if (botCommandsMenuButton != null) { + botCommandsMenuButton.setOpened(false); + } + return; + } if (AndroidUtilities.isTablet()) { - BotWebViewSheet webViewSheet = new BotWebViewSheet(getContext(), parentFragment.getResourceProvider()); + BotWebViewSheet webViewSheet = new BotWebViewSheet(getContext(), resourcesProvider); + webViewSheet.setDefaultFullsize(false); + webViewSheet.setNeedsContext(true); webViewSheet.setParentActivity(parentActivity); - webViewSheet.requestWebView(currentAccount, dialog_id, dialog_id, botMenuWebViewTitle, botMenuWebViewUrl, BotWebViewSheet.TYPE_BOT_MENU_BUTTON, 0, false); - parentFragment.showDialog(webViewSheet); + webViewSheet.requestWebView(null, props); + webViewSheet.show(); if (botCommandsMenuButton != null) { botCommandsMenuButton.setOpened(false); @@ -4105,30 +4117,37 @@ protected boolean drawChild(Canvas canvas, View child, long drawingTime) { private Rect blurBounds = new Rect(); @Override protected void onDraw(Canvas canvas) { - if (shouldDrawBackground) { - int top = animatedTop; - top += Theme.chat_composeShadowDrawable.getIntrinsicHeight() * (1f - composeShadowAlpha); - if (topView != null && topView.getVisibility() == View.VISIBLE) { - top += (1f - topViewEnterProgress) * topView.getLayoutParams().height; - } - int bottom = top + Theme.chat_composeShadowDrawable.getIntrinsicHeight(); + drawBackground(canvas, true); + } + + public void drawBackground(Canvas canvas, boolean withComposeShadowDrawable) { + if (!shouldDrawBackground) { + return; + } + int top = animatedTop; + top += Theme.chat_composeShadowDrawable.getIntrinsicHeight() * (1f - composeShadowAlpha); + if (topView != null && topView.getVisibility() == View.VISIBLE) { + top += (1f - topViewEnterProgress) * topView.getLayoutParams().height; + } + int bottom = top + Theme.chat_composeShadowDrawable.getIntrinsicHeight(); + if (withComposeShadowDrawable) { Theme.chat_composeShadowDrawable.setAlpha((int) (composeShadowAlpha * 0xFF)); Theme.chat_composeShadowDrawable.setBounds(0, top, getMeasuredWidth(), bottom); Theme.chat_composeShadowDrawable.draw(canvas); - bottom += chatSearchExpandOffset; + } + bottom += chatSearchExpandOffset; - if (allowBlur) { - backgroundPaint.setColor(getThemedColor(Theme.key_chat_messagePanelBackground)); - if (SharedConfig.chatBlurEnabled() && sizeNotifierLayout != null) { - blurBounds.set(0, bottom, getWidth(), getHeight()); - sizeNotifierLayout.drawBlurRect(canvas, getTop(), blurBounds, backgroundPaint, false); - } else { - canvas.drawRect(0, bottom, getWidth(), getHeight(), backgroundPaint); - } + if (allowBlur) { + backgroundPaint.setColor(getThemedColor(Theme.key_chat_messagePanelBackground)); + if (SharedConfig.chatBlurEnabled() && sizeNotifierLayout != null) { + blurBounds.set(0, bottom, getWidth(), getHeight()); + sizeNotifierLayout.drawBlurRect(canvas, getTop(), blurBounds, backgroundPaint, false); } else { - canvas.drawRect(0, bottom, getWidth(), getHeight(), getThemedPaint(Theme.key_paint_chatComposeBackground)); + canvas.drawRect(0, bottom, getWidth(), getHeight(), backgroundPaint); } + } else { + canvas.drawRect(0, bottom, getWidth(), getHeight(), getThemedPaint(Theme.key_paint_chatComposeBackground)); } } @@ -4286,7 +4305,12 @@ public void dismiss() { messageSendPreview.dismiss(false); } AndroidUtilities.cancelRunOnUIThread(dismissSendPreview); - messageSendPreview = new MessageSendPreview(getContext(), resourcesProvider); + messageSendPreview = new MessageSendPreview(getContext(), resourcesProvider) { + @Override + protected void onEffectChange(long effectId) { + ChatActivityEnterView.this.setEffectId(effectId); + } + }; messageSendPreview.setOnDismissListener(di -> { messageSendPreview = null; }); @@ -4390,7 +4414,7 @@ public void dismiss() { messageSendPreview.setMessageObjects(messages); if (containsSendMessage && audioToSend == null) { - messageSendPreview.setEditText(messageEditText, this::drawMessageEditText, this::onDraw); + messageSendPreview.setEditText(messageEditText, this::drawMessageEditText, canvas -> drawBackground(canvas, false)); } messageSendPreview.setSendButton(sendButton, true, v -> { sentFromPreview = System.currentTimeMillis(); @@ -4405,6 +4429,7 @@ public void dismiss() { }); if ((containsSendMessage || putCameraTexture) && dialog_id >= 0) { messageSendPreview.allowEffectSelector(parentFragment); + messageSendPreview.setEffectId(effectId); } ItemOptions options = ItemOptions.makeOptions(this, resourcesProvider, sendButton); @@ -4517,10 +4542,11 @@ public void onItemClick(View view, int position) { SendMessagesHelper.SendMessageParams params = SendMessagesHelper.SendMessageParams.of(command, dialog_id, replyingMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false); params.quick_reply_shortcut = parentFragment != null ? parentFragment.quickReplyShortcut : null; params.quick_reply_shortcut_id = parentFragment != null ? parentFragment.getQuickReplyId() : 0; - params.effect_id = messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0; + params.effect_id = effectId; SendMessagesHelper.getInstance(currentAccount).sendMessage(params); setFieldText(""); botCommandsMenuContainer.dismiss(); + sendButton.setEffect(effectId = 0); } } } @@ -5320,7 +5346,8 @@ public boolean isRecordLocked() { public void cancelRecordingAudioVideo() { if (hasRecordVideo && isInVideoMode()) { CameraController.getInstance().cancelOnInitRunnable(onFinishInitCameraRunnable); - delegate.needStartRecordVideo(5, true, 0, voiceOnce ? 0x7FFFFFFF : 0, messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0); + delegate.needStartRecordVideo(5, true, 0, voiceOnce ? 0x7FFFFFFF : 0, effectId); + sendButton.setEffect(effectId = 0); } else { delegate.needStartRecordAudio(0); MediaController.getInstance().stopRecording(0, false, 0, false); @@ -6420,7 +6447,8 @@ private void sendMessageInternal(boolean notify, int scheduleDate, boolean allow return; } if (videoToSendMessageObject != null) { - delegate.needStartRecordVideo(4, notify, scheduleDate, voiceOnce ? 0x7FFFFFFF : 0, messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0); + delegate.needStartRecordVideo(4, notify, scheduleDate, voiceOnce ? 0x7FFFFFFF : 0, effectId); + sendButton.setEffect(effectId = 0); hideRecordedAudioPanel(true); checkSendButton(true); AndroidUtilities.runOnUIThread(() -> { @@ -6440,7 +6468,8 @@ private void sendMessageInternal(boolean notify, int scheduleDate, boolean allow SendMessagesHelper.SendMessageParams params = SendMessagesHelper.SendMessageParams.of(audioToSend, null, audioToSendPath, dialog_id, replyingMessageObject, getThreadMessage(), null, null, null, null, notify, scheduleDate, voiceOnce ? 0x7FFFFFFF : 0, null, null, false); params.quick_reply_shortcut = parentFragment != null ? parentFragment.quickReplyShortcut : null; params.quick_reply_shortcut_id = parentFragment != null ? parentFragment.getQuickReplyId() : 0; - params.effect_id = messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0; + params.effect_id = effectId; + sendButton.setEffect(effectId = 0); if (!delegate.hasForwardingMessages()) { MessageObject.SendAnimationData sendAnimationData = new MessageObject.SendAnimationData(); sendAnimationData.fromPreview = System.currentTimeMillis() - sentFromPreview < 200; @@ -6864,7 +6893,8 @@ public boolean processSendingText(CharSequence text, boolean notify, int schedul SendMessagesHelper.SendMessageParams params = SendMessagesHelper.SendMessageParams.of(message[0].toString(), dialog_id, replyingMessageObject, replyToTopMsg, messageWebPage, messageWebPageSearch, entities, null, null, notify, scheduleDate, sendAnimationData, updateStickersOrder); params.quick_reply_shortcut = parentFragment != null ? parentFragment.quickReplyShortcut : null; params.quick_reply_shortcut_id = parentFragment != null ? parentFragment.getQuickReplyId() : 0; - params.effect_id = messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0; + params.effect_id = effectId; + sendButton.setEffect(effectId = 0); applyStoryToSendMessageParams(params); params.invert_media = parentFragment != null && parentFragment.messagePreviewParams != null && parentFragment.messagePreviewParams.webpageTop; if (parentFragment != null && parentFragment.getCurrentChat() != null && !ChatObject.canSendEmbed(parentFragment.getCurrentChat())) { @@ -8631,7 +8661,8 @@ public void setCommand(MessageObject messageObject, String command, boolean long } sendMessageParams.quick_reply_shortcut = parentFragment != null ? parentFragment.quickReplyShortcut : null; sendMessageParams.quick_reply_shortcut_id = parentFragment != null ? parentFragment.getQuickReplyId() : 0; - sendMessageParams.effect_id = messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0; + sendMessageParams.effect_id = effectId; + sendButton.setEffect(effectId = 0); applyStoryToSendMessageParams(sendMessageParams); SendMessagesHelper.getInstance(currentAccount).sendMessage(sendMessageParams); } @@ -8691,6 +8722,18 @@ public void setEditingBusinessLink(TLRPC.TL_businessChatLink businessLink) { } private boolean captionAbove; + private long effectId; + + public void setEffectId(long effectId) { + this.effectId = effectId; + if (sendButton != null) { + sendButton.setEffect(effectId); + } + } + + public long getEffectId() { + return effectId; + } private MessageObject editingMessageObjectPreview(MessageObject msg, boolean applyCaption) { MessageObject previewMessage = new MessageObject(msg.currentAccount, msg.messageOwner, true, true) { @@ -9403,6 +9446,14 @@ public void updateScheduleButton(boolean animated) { return; } scheduledButton.setTag(visible ? 1 : null); + } else if (notifyButton != null) { + int newVisibility = !hasScheduled && notifyVisible ? VISIBLE : GONE; + if (newVisibility != notifyButton.getVisibility()) { + notifyButton.setVisibility(newVisibility); + if (attachLayout != null) { + attachLayout.setPivotX(dp((botButton == null || botButton.getVisibility() == GONE) && (notifyButton == null || notifyButton.getVisibility() == GONE) ? 48 : 96)); + } + } } if (scheduledButtonAnimation != null) { scheduledButtonAnimation.cancel(); @@ -9420,6 +9471,8 @@ public void updateScheduleButton(boolean animated) { if (giftButton != null && giftButton.getVisibility() == VISIBLE) { scheduledButton.setTranslationX(-dp(48)); } + } else if (notifyButton != null) { + notifyButton.setVisibility(notifyVisible ? VISIBLE : GONE); } } else if (scheduledButton != null) { if (visible) { @@ -9794,7 +9847,8 @@ public boolean didPressedBotButton(final TLRPC.KeyboardButton button, final Mess SendMessagesHelper.SendMessageParams params = SendMessagesHelper.SendMessageParams.of(button.text, dialog_id, replyMessageObject, getThreadMessage(), null, false, null, null, null, true, 0, null, false); params.quick_reply_shortcut = parentFragment != null ? parentFragment.quickReplyShortcut : null; params.quick_reply_shortcut_id = parentFragment != null ? parentFragment.getQuickReplyId() : 0; - params.effect_id = messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0; + params.effect_id = effectId; + sendButton.setEffect(effectId = 0); SendMessagesHelper.getInstance(currentAccount).sendMessage(params); } else if (button instanceof TLRPC.TL_keyboardButtonUrl) { if (Browser.urlMustNotHaveConfirmation(button.url)) { @@ -9813,18 +9867,35 @@ public boolean didPressedBotButton(final TLRPC.KeyboardButton button, final Mess Runnable onRequestWebView = new Runnable() { @Override public void run() { - if (sizeNotifierLayout.measureKeyboardHeight() > dp(20)) { + if (sizeNotifierLayout.measureKeyboardHeight() > dp(20) || isPopupShowing()) { + hidePopup(false); AndroidUtilities.hideKeyboard(ChatActivityEnterView.this); AndroidUtilities.runOnUIThread(this, 150); return; } - BotWebViewSheet webViewSheet = new BotWebViewSheet(getContext(), resourcesProvider); - webViewSheet.setParentActivity(parentActivity); - webViewSheet.requestWebView(currentAccount, messageObject.messageOwner.dialog_id, botId, button.text, button.url, button instanceof TLRPC.TL_keyboardButtonSimpleWebView ? BotWebViewSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON : BotWebViewSheet.TYPE_WEB_VIEW_BUTTON, replyMessageObject != null ? replyMessageObject.messageOwner.id : 0, false); - if (parentFragment != null) { - parentFragment.showDialog(webViewSheet); + if (parentFragment == null) { + return; + } + + BotWebViewAttachedSheet.WebViewRequestProps props = BotWebViewAttachedSheet.WebViewRequestProps.of(currentAccount, messageObject.messageOwner.dialog_id, botId, button.text, button.url, button instanceof TLRPC.TL_keyboardButtonSimpleWebView ? BotWebViewSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON : BotWebViewSheet.TYPE_WEB_VIEW_BUTTON, replyMessageObject != null ? replyMessageObject.messageOwner.id : 0, false, null, false, null, null, 0, false); + if (LaunchActivity.instance != null && LaunchActivity.instance.getBottomSheetTabs() != null && LaunchActivity.instance.getBottomSheetTabs().tryReopenTab(props) != null) { + if (botCommandsMenuButton != null) { + botCommandsMenuButton.setOpened(false); + } + return; + } + if (AndroidUtilities.isTablet()) { + BotWebViewSheet webViewSheet = new BotWebViewSheet(getContext(), resourcesProvider); + webViewSheet.setParentActivity(parentActivity); + webViewSheet.requestWebView(null, props); + webViewSheet.show(); } else { + BotWebViewAttachedSheet webViewSheet = parentFragment.createBotViewer(); + webViewSheet.setDefaultFullsize(false); + webViewSheet.setNeedsContext(true); + webViewSheet.setParentActivity(parentActivity); + webViewSheet.requestWebView(null, props); webViewSheet.show(); } } @@ -9907,7 +9978,7 @@ public void run() { return true; } long did = dids.get(0).dialogId; - MediaDataController.getInstance(currentAccount).saveDraft(did, 0, "@" + UserObject.getPublicUsername(user) + " " + button.query, null, null, true); + MediaDataController.getInstance(currentAccount).saveDraft(did, 0, "@" + UserObject.getPublicUsername(user) + " " + button.query, null, null, true, 0); if (did != dialog_id) { if (!DialogObject.isEncryptedDialog(did)) { Bundle args1 = new Bundle(); @@ -10585,6 +10656,9 @@ private void showPopup(int show, int contentType, boolean allowAnimation) { keyboardHeightLand = MessagesController.getGlobalEmojiSettings().getInt("kbd_height_land3", dp(200)); } int currentHeight = AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y ? keyboardHeightLand : keyboardHeight; + if (parentFragment != null && parentFragment.getParentLayout() != null) { + currentHeight -= parentFragment.getParentLayout().getBottomTabsHeight(false); + } /*if (!samePannelWasVisible && !anotherPanelWasVisible) { currentHeight = 0; } else */ @@ -11036,6 +11110,9 @@ public void onSizeChanged(int height, boolean isWidthGreater) { if (isPopupShowing()) { int newHeight = isWidthGreater ? keyboardHeightLand : keyboardHeight; + if (parentFragment != null && parentFragment.getParentLayout() != null) { + newHeight -= parentFragment.getParentLayout().getBottomTabsHeight(false); + } if (currentPopupContentType == POPUP_CONTENT_BOT_KEYBOARD && !botKeyboardView.isFullSize()) { newHeight = Math.min(botKeyboardView.getKeyboardHeight(), newHeight); } @@ -11794,7 +11871,8 @@ public boolean onTouchEvent(MotionEvent event) { public void onCancelButtonPressed() { if (hasRecordVideo && isInVideoMode()) { CameraController.getInstance().cancelOnInitRunnable(onFinishInitCameraRunnable); - delegate.needStartRecordVideo(5, true, 0, voiceOnce ? 0x7FFFFFFF : 0, messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0); + delegate.needStartRecordVideo(5, true, 0, voiceOnce ? 0x7FFFFFFF : 0, effectId); + sendButton.setEffect(effectId = 0); } else { delegate.needStartRecordAudio(0); MediaController.getInstance().stopRecording(0, false, 0, voiceOnce); @@ -12061,7 +12139,8 @@ protected void onDraw(Canvas canvas) { if (isInVideoMode()) { if (t >= 59500 && !stoppedInternal) { startedDraggingX = -1; - delegate.needStartRecordVideo(3, true, 0, voiceOnce ? 0x7FFFFFFF : 0, messageSendPreview != null ? messageSendPreview.getSelectedEffect() : 0); + delegate.needStartRecordVideo(3, true, 0, voiceOnce ? 0x7FFFFFFF : 0, effectId); + sendButton.setEffect(effectId = 0); stoppedInternal = true; } } @@ -12538,6 +12617,7 @@ public static class SendButton extends View { private final Drawable drawable; private final Drawable inactiveDrawable; private final Drawable drawableInverse; + private final AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable emojiDrawable; public boolean center; @@ -12554,6 +12634,7 @@ public SendButton(Context context, int resId, Theme.ResourcesProvider resourcesP drawable = context.getResources().getDrawable(resId).mutate(); inactiveDrawable = context.getResources().getDrawable(resId).mutate(); drawableInverse = context.getResources().getDrawable(resId).mutate(); + emojiDrawable = new AnimatedEmojiDrawable.SwapAnimatedEmojiDrawable(this, dp(14)); loadingPaint.setStyle(Paint.Style.STROKE); loadingPaint.setStrokeWidth(dp(2)); @@ -12571,7 +12652,20 @@ public SendButton(Context context, int resId, Theme.ResourcesProvider resourcesP @Override protected boolean verifyDrawable(@NonNull Drawable who) { - return who == count || super.verifyDrawable(who); + return who == count || who == emojiDrawable || super.verifyDrawable(who); + } + + public void setEffect(long effectId) { + TLRPC.TL_availableEffect effect = MessagesController.getInstance(UserConfig.selectedAccount).getEffect(effectId); + setEmoji(effect != null ? Emoji.getEmojiDrawable(effect.emoticon) : null); + } + + public void setEmoji(Drawable drawable) { + emojiDrawable.set(drawable, true); + } + + public void copyEmojiTo(SendButton sendButton) { + sendButton.setEmoji(emojiDrawable.getDrawable()); } @Override @@ -12641,6 +12735,14 @@ protected void onDraw(Canvas canvas) { if (openProgress < 1) { drawable.setBounds(x, y, x + drawable.getIntrinsicWidth(), y + drawable.getIntrinsicHeight()); drawable.draw(canvas); + + final int cx = getMeasuredWidth() / 2 + dp(12); + final int cy = getMeasuredHeight() / 2 + dp(12); + final int r = dp(8); + + emojiDrawable.setBounds(cx-r, cy-r, cx+r, cy+r); + emojiDrawable.setAlpha((int) (0xFF * (1f - openProgress))); + emojiDrawable.draw(canvas); } if (openProgress > 0) { final int cx = getMeasuredWidth() / 2; 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 f892520099..c90f0fa6db 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlert.java @@ -8,6 +8,7 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.LocaleController.formatPluralString; import static org.telegram.messenger.LocaleController.getString; import android.Manifest; @@ -101,6 +102,7 @@ import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.ActionBarMenuSubItem; import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; @@ -122,6 +124,7 @@ import org.telegram.ui.PhotoViewer; import org.telegram.ui.PremiumPreviewFragment; import org.telegram.ui.Stars.StarsController; +import org.telegram.ui.Stars.StarsIntroActivity; import org.telegram.ui.Stories.recorder.StoryEntry; import org.telegram.ui.WebAppDisclaimerAlert; import org.telegram.ui.bots.BotWebViewContainer; @@ -219,7 +222,7 @@ public void onCloseRequested(Runnable callback) { } @Override - public void onWebAppSetActionBarColor(int color, boolean isOverrideColor) { + public void onWebAppSetActionBarColor(int colorKey, int color, boolean isOverrideColor) { int from = ((ColorDrawable) actionBar.getBackground()).getColor(); int to = color; @@ -254,7 +257,7 @@ public void onWebAppOpenInvoice(TLRPC.InputInvoice inputInvoice, String slug, TL if (response instanceof TLRPC.TL_payments_paymentFormStars) { final AlertDialog progressDialog = new AlertDialog(getContext(), AlertDialog.ALERT_TYPE_SPINNER); progressDialog.showDelayed(150); - StarsController.getInstance(currentAccount).openPaymentForm(inputInvoice, (TLRPC.TL_payments_paymentFormStars) response, () -> { + StarsController.getInstance(currentAccount).openPaymentForm(null, inputInvoice, (TLRPC.TL_payments_paymentFormStars) response, () -> { progressDialog.dismiss(); }, status -> { webViewLayout.getWebViewContainer().onInvoiceStatusUpdate(slug, status); @@ -2302,7 +2305,7 @@ public void setTranslationY(float translationY) { } if (view instanceof AttachButton) { final Activity activity = lastFragment.getParentActivity(); - int num = (Integer) view.getTag(); + int num = view.getTag() instanceof Integer ? (Integer) view.getTag() : -1; if (num == 1) { if (!photosEnabled && !videosEnabled && checkCanRemoveRestrictionsByBoosts()) { return; @@ -2384,7 +2387,7 @@ public void setTranslationY(float translationY) { } } else if (num == 11) { openQuickRepliesLayout(); - } else { + } else if (view.getTag() instanceof Integer) { delegate.didPressedButton((Integer) view.getTag(), true, true, 0, 0, isCaptionAbove(), false); } int left = view.getLeft(); @@ -3141,6 +3144,34 @@ R.raw.position_above, getString(R.string.CaptionBelow), } }); } + if (chatActivity != null && ChatObject.isChannelAndNotMegaGroup(chatActivity.getCurrentChat()) && chatActivity.getCurrentChatInfo() != null && chatActivity.getCurrentChatInfo().paid_media_allowed) { + ActionBarMenuSubItem item = options.add(R.drawable.menu_feature_paid, getString(R.string.PaidMediaButton), null).getLast(); + item.setOnClickListener(v -> { + if (photoLayout == null) return; + StarsIntroActivity.showMediaPriceSheet(context, photoLayout.getStarsPrice(), true, (amount, done) -> { + done.run(); + photoLayout.setStarsPrice(amount); + if (amount != null && amount > 0) { + item.setText(getString(R.string.PaidMediaPriceButton)); + item.setSubtext(formatPluralString("Stars", (int) (long) amount)); + messageSendPreview.setStars(amount); + } else { + item.setText(getString(R.string.PaidMediaButton)); + item.setSubtext(null); + messageSendPreview.setStars(0); + } + }, resourcesProvider); + }); + long amount = photoLayout.getStarsPrice(); + if (amount > 0) { + item.setText(getString(R.string.PaidMediaPriceButton)); + item.setSubtext(formatPluralString("Stars", (int) amount)); + } else { + item.setText(getString(R.string.PaidMediaButton)); + item.setSubtext(null); + } + messageSendPreview.setStars(amount); + } options.setupSelectors(); messageSendPreview.setItemOptions(options); @@ -3518,6 +3549,9 @@ private void showLayout(AttachAlertLayout layout, long newId, boolean animated) @Override public void onAnimationEnd(Animator animation) { currentAttachLayout.setAlpha(0.0f); + currentAttachLayout.setTranslationY(AndroidUtilities.dp(78) + t); + ATTACH_ALERT_LAYOUT_TRANSLATION.set(currentAttachLayout, 1.0f); + actionBar.setAlpha(0f); SpringAnimation springAnimation = new SpringAnimation(nextAttachLayout, DynamicAnimation.TRANSLATION_Y, 0); springAnimation.getSpring().setDampingRatio(0.75f); springAnimation.getSpring().setStiffness(500.0f); @@ -3529,6 +3563,9 @@ public void onAnimationEnd(Animator animation) { containerView.invalidate(); }); springAnimation.addEndListener((animation1, canceled, value, velocity) -> { + nextAttachLayout.setTranslationY(0); + nextAttachLayout.onContainerTranslationUpdated(currentPanTranslationY); + containerView.invalidate(); onEnd.run(); updateSelectedPosition(0); }); @@ -3537,8 +3574,8 @@ public void onAnimationEnd(Animator animation) { } }); viewChangeAnimator = animator; - animator.start(); ATTACH_ALERT_LAYOUT_TRANSLATION.set(currentAttachLayout, 0f); + animator.start(); } else { currentAttachLayout.setAlpha(0.0f); onEnd.run(); @@ -4996,10 +5033,6 @@ public void notifyDataSetChanged() { quickRepliesButton = buttonsCount++; } musicButton = buttonsCount++; - - if (user != null && user.bot) { - contactButton = buttonsCount++; - } } super.notifyDataSetChanged(); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java index ab94173dcf..8500fcba02 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java @@ -638,6 +638,7 @@ public void onDestroy() { try { if (receiverRegistered) { ApplicationLoader.applicationContext.unregisterReceiver(receiver); + receiverRegistered = false; } } catch (Exception e) { FileLog.e(e); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertLocationLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertLocationLayout.java index 12e4d28f7c..94c2a79f6b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertLocationLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertLocationLayout.java @@ -691,7 +691,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { } }; listView.setClipToPadding(false); - listView.setAdapter(adapter = new LocationActivityAdapter(context, locationType, dialogId, true, resourcesProvider, parentAlert.isStoryLocationPicker, parentAlert.isBizLocationPicker)); + listView.setAdapter(adapter = new LocationActivityAdapter(context, locationType, dialogId, true, resourcesProvider, parentAlert.isStoryLocationPicker, false, parentAlert.isBizLocationPicker)); DefaultItemAnimator itemAnimator = new DefaultItemAnimator(); itemAnimator.setDurations(350); itemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java index 28e8900c4e..68948b7780 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayout.java @@ -10,6 +10,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; +import static org.telegram.messenger.LocaleController.formatPluralString; import static org.telegram.messenger.LocaleController.getString; import android.Manifest; @@ -97,6 +98,7 @@ import org.telegram.ui.ChatActivity; import org.telegram.ui.LaunchActivity; import org.telegram.ui.PhotoViewer; +import org.telegram.ui.Stars.StarsIntroActivity; import org.telegram.ui.Stories.recorder.AlbumButton; import java.io.ByteArrayOutputStream; @@ -104,6 +106,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -225,9 +228,11 @@ public class ChatAttachAlertPhotoLayout extends ChatAttachAlert.AttachAlertLayou public final static int media_gap = 5; public final static int preview = 6; public final static int caption = 7; + public final static int stars = 8; private ActionBarMenuSubItem spoilerItem; private ActionBarMenuSubItem compressItem; + private ActionBarMenuSubItem starsItem; protected ActionBarMenuSubItem previewItem; public MessagePreviewView.ToggleButton captionItem; @@ -344,6 +349,7 @@ private void setCurrentSpoilerVisible(int i, boolean visible) { PhotoAttachPhotoCell cell = (PhotoAttachPhotoCell) view; if (cell.getPhotoEntry() == entry) { cell.setHasSpoiler(visible, 250f); + cell.setStarsPrice(getStarsPrice(), selectedPhotos.size() > 1); } } }); @@ -549,6 +555,48 @@ protected void updateCheckedPhotoIndices() { } } + protected void updateCheckedPhotos() { + if (!(parentAlert.baseFragment instanceof ChatActivity)) { + return; + } + int count = gridView.getChildCount(); + for (int a = 0; a < count; a++) { + View view = gridView.getChildAt(a); + if (view instanceof PhotoAttachPhotoCell) { + PhotoAttachPhotoCell cell = (PhotoAttachPhotoCell) view; + int position = gridView.getChildAdapterPosition(view); + if (adapter.needCamera && selectedAlbumEntry == galleryAlbumEntry) { + position--; + } + MediaController.PhotoEntry photoEntry = getPhotoEntryAtPosition(position); + cell.setHasSpoiler(photoEntry != null && photoEntry.hasSpoiler); + if (parentAlert.baseFragment instanceof ChatActivity && parentAlert.allowOrder) { + cell.setChecked(photoEntry != null ? selectedPhotosOrder.indexOf(photoEntry.imageId) : -1, photoEntry != null && selectedPhotos.containsKey(photoEntry.imageId), true); + } else { + cell.setChecked(-1, photoEntry != null && selectedPhotos.containsKey(photoEntry.imageId), true); + } + } + } + count = cameraPhotoRecyclerView.getChildCount(); + for (int a = 0; a < count; a++) { + View view = cameraPhotoRecyclerView.getChildAt(a); + if (view instanceof PhotoAttachPhotoCell) { + PhotoAttachPhotoCell cell = (PhotoAttachPhotoCell) view; + int position = cameraPhotoRecyclerView.getChildAdapterPosition(view); + if (adapter.needCamera && selectedAlbumEntry == galleryAlbumEntry) { + position--; + } + MediaController.PhotoEntry photoEntry = getPhotoEntryAtPosition(position); + cell.setHasSpoiler(photoEntry != null && photoEntry.hasSpoiler); + if (parentAlert.baseFragment instanceof ChatActivity && parentAlert.allowOrder) { + cell.setChecked(photoEntry != null ? selectedPhotosOrder.indexOf(photoEntry.imageId) : -1, photoEntry != null && selectedPhotos.containsKey(photoEntry.imageId), true); + } else { + cell.setChecked(-1, photoEntry != null && selectedPhotos.containsKey(photoEntry.imageId), true); + } + } + } + } + private MediaController.PhotoEntry getPhotoEntryAtPosition(int position) { if (position < 0) { return null; @@ -641,6 +689,7 @@ R.raw.position_above, getString(R.string.CaptionBelow), parentAlert.selectedMenuItem.addColoredGap(media_gap); spoilerItem = parentAlert.selectedMenuItem.addSubItem(spoiler, R.drawable.msg_spoiler, LocaleController.getString("EnablePhotoSpoiler", R.string.EnablePhotoSpoiler)); parentAlert.selectedMenuItem.addSubItem(caption, captionItem); + starsItem = parentAlert.selectedMenuItem.addSubItem(stars, R.drawable.menu_feature_paid, getString(R.string.PaidMediaButton)); parentAlert.selectedMenuItem.setFitSubItems(true); gridView = new RecyclerListView(context, resourcesProvider) { @@ -1544,6 +1593,9 @@ private int maxCount() { private int addToSelectedPhotos(MediaController.PhotoEntry object, int index) { Object key = object.imageId; if (selectedPhotos.containsKey(key)) { + object.starsAmount = 0; + object.hasSpoiler = false; + selectedPhotos.remove(key); int position = selectedPhotosOrder.indexOf(key); if (position >= 0) { @@ -1557,13 +1609,73 @@ private int addToSelectedPhotos(MediaController.PhotoEntry object, int index) { } return position; } else { + object.starsAmount = getStarsPrice(); + object.hasSpoiler = getStarsPrice() > 0; + object.isChatPreviewSpoilerRevealed = false; + object.isAttachSpoilerRevealed = false; + + boolean changed = checkSelectedCount(true); selectedPhotos.put(key, object); selectedPhotosOrder.add(key); - updatePhotosCounter(true); + if (changed) { + updateCheckedPhotos(); + } else { + updatePhotosCounter(true); + } return -1; } } + private boolean checkSelectedCount(boolean beforeAdding) { + boolean changed = false; + if (getStarsPrice() > 0) { + while (selectedPhotos.size() > 10 - (beforeAdding ? 1 : 0) && !selectedPhotosOrder.isEmpty()) { + Object key = selectedPhotosOrder.get(0); + Object firstPhoto = selectedPhotos.get(key); + if (!(firstPhoto instanceof MediaController.PhotoEntry)) { + break; + } + addToSelectedPhotos((MediaController.PhotoEntry) firstPhoto, -1); + changed = true; + } + } + return changed; + } + + public long getStarsPrice() { + for (HashMap.Entry entry : selectedPhotos.entrySet()) { + MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) entry.getValue(); + return photoEntry.starsAmount; + } + return 0; + } + + public void setStarsPrice(long stars) { + if (!selectedPhotos.isEmpty()) { + for (HashMap.Entry entry : selectedPhotos.entrySet()) { + MediaController.PhotoEntry photoEntry = (MediaController.PhotoEntry) entry.getValue(); + photoEntry.starsAmount = stars; + photoEntry.hasSpoiler = stars > 0; + photoEntry.isChatPreviewSpoilerRevealed = false; + photoEntry.isAttachSpoilerRevealed = false; + } + } + onSelectedItemsCountChanged(getSelectedItemsCount()); + if (checkSelectedCount(false)) { + updateCheckedPhotos(); + } + } + + private void updatePhotoStarsPrice() { + gridView.forAllChild(view -> { + if (view instanceof PhotoAttachPhotoCell) { + PhotoAttachPhotoCell cell = (PhotoAttachPhotoCell) view; + cell.setHasSpoiler(cell.getPhotoEntry() != null && cell.getPhotoEntry().hasSpoiler, 250f); + cell.setStarsPrice(cell.getPhotoEntry() != null ? cell.getPhotoEntry().starsAmount : 0, selectedPhotos.size() > 1); + } + }); + } + private void clearSelectedPhotos() { spoilerItem.setText(LocaleController.getString(R.string.EnablePhotoSpoiler)); spoilerItem.setAnimatedIcon(R.raw.photo_spoiler); @@ -3008,7 +3120,7 @@ public void updateSelected(HashMap newSelectedPhotos, ArrayList< } MediaController.PhotoEntry photoEntry = getPhotoEntryAtPosition(position); if (photoEntry != null) { - cell.setPhotoEntry(photoEntry, adapter.needCamera && selectedAlbumEntry == galleryAlbumEntry, position == adapter.getItemCount() - 1); + cell.setPhotoEntry(photoEntry, selectedPhotos.size() > 1, adapter.needCamera && selectedAlbumEntry == galleryAlbumEntry, position == adapter.getItemCount() - 1); if (parentAlert.baseFragment instanceof ChatActivity && parentAlert.allowOrder) { cell.setChecked(selectedPhotosOrder.indexOf(photoEntry.imageId), selectedPhotos.containsKey(photoEntry.imageId), false); } else { @@ -3186,6 +3298,11 @@ public void onMenuItemClick(int id) { } } else if (id == preview) { parentAlert.updatePhotoPreview(parentAlert.getCurrentAttachLayout() != parentAlert.getPhotoPreviewLayout()); + } else if (id == stars) { + StarsIntroActivity.showMediaPriceSheet(getContext(), getStarsPrice(), true, (price, done) -> { + done.run(); + setStarsPrice(price); + }, resourcesProvider); } else if (id >= 10) { selectedAlbumEntry = dropDownAlbums.get(id - 10); if (selectedAlbumEntry == galleryAlbumEntry) { @@ -3215,7 +3332,7 @@ public void onSelectedItemsCountChanged(int count) { parentAlert.selectedMenuItem.showSubItem(open_in); hasCompress = false; parentAlert.selectedMenuItem.hideSubItem(compress); - } else if (documentsEnabled) { + } else if (documentsEnabled && getStarsPrice() <= 0) { hasCompress = true; parentAlert.selectedMenuItem.showSubItem(compress); } else { @@ -3223,9 +3340,14 @@ public void onSelectedItemsCountChanged(int count) { parentAlert.selectedMenuItem.hideSubItem(compress); } } else { - hasGroup = true; - parentAlert.selectedMenuItem.showSubItem(group); - if (documentsEnabled) { + if (getStarsPrice() <= 0) { + hasGroup = true; + parentAlert.selectedMenuItem.showSubItem(group); + } else { + hasGroup = false; + parentAlert.selectedMenuItem.hideSubItem(group); + } + if (documentsEnabled && getStarsPrice() <= 0) { hasCompress = true; parentAlert.selectedMenuItem.showSubItem(compress); } else { @@ -3247,8 +3369,9 @@ public void onSelectedItemsCountChanged(int count) { compressItem.setText(LocaleController.getString(R.string.SendAsFile)); } } - final boolean hasSpoiler = count > 0 && (parentAlert == null || parentAlert.baseFragment instanceof ChatActivity && !((ChatActivity) parentAlert.baseFragment).isSecretChat()); + final boolean hasSpoiler = count > 0 && getStarsPrice() <= 0 && (parentAlert == null || parentAlert.baseFragment instanceof ChatActivity && !((ChatActivity) parentAlert.baseFragment).isSecretChat()); final boolean hasCaption = count > 0 && parentAlert != null && parentAlert.hasCaption() && parentAlert.baseFragment instanceof ChatActivity; + final boolean hasStars = count > 0 && (parentAlert != null && parentAlert.baseFragment instanceof ChatActivity && ChatObject.isChannelAndNotMegaGroup(((ChatActivity) parentAlert.baseFragment).getCurrentChat()) && ((ChatActivity) parentAlert.baseFragment).getCurrentChatInfo() != null && ((ChatActivity) parentAlert.baseFragment).getCurrentChatInfo().paid_media_allowed); if (!hasSpoiler) { spoilerItem.setText(LocaleController.getString(R.string.EnablePhotoSpoiler)); spoilerItem.setAnimatedIcon(R.raw.photo_spoiler); @@ -3266,6 +3389,25 @@ public void onSelectedItemsCountChanged(int count) { } else { parentAlert.selectedMenuItem.hideSubItem(media_gap); } + if (hasStars) { + updateStarsItem(); + updatePhotoStarsPrice(); + parentAlert.selectedMenuItem.showSubItem(stars); + } else { + parentAlert.selectedMenuItem.hideSubItem(stars); + } + } + + private void updateStarsItem() { + if (starsItem == null) return; + long amount = getStarsPrice(); + if (amount > 0) { + starsItem.setText(getString(R.string.PaidMediaPriceButton)); + starsItem.setSubtext(formatPluralString("Stars", (int) amount)); + } else { + starsItem.setText(getString(R.string.PaidMediaButton)); + starsItem.setSubtext(null); + } } @Override @@ -4091,6 +4233,7 @@ public void getOutline(View view, Outline outline) { } parentAlert.updateCountButton(added ? 1 : 2); cell.setHasSpoiler(photoEntry.hasSpoiler); + cell.setStarsPrice(photoEntry.starsAmount, selectedPhotos.size() > 1); }); return new RecyclerListView.Holder(cell); } @@ -4128,7 +4271,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (photoEntry == null) { return; } - cell.setPhotoEntry(photoEntry, needCamera && selectedAlbumEntry == galleryAlbumEntry, position == getItemCount() - 1); + cell.setPhotoEntry(photoEntry, selectedPhotos.size() > 1, needCamera && selectedAlbumEntry == galleryAlbumEntry, position == getItemCount() - 1); if (parentAlert.baseFragment instanceof ChatActivity && parentAlert.allowOrder) { cell.setChecked(selectedPhotosOrder.indexOf(photoEntry.imageId), selectedPhotos.containsKey(photoEntry.imageId), false); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayoutPreview.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayoutPreview.java index ca8607738a..6bfc512061 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayoutPreview.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertPhotoLayoutPreview.java @@ -1,5 +1,6 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.dp; import static org.telegram.messenger.MessageObject.POSITION_FLAG_BOTTOM; import static org.telegram.messenger.MessageObject.POSITION_FLAG_LEFT; import static org.telegram.messenger.MessageObject.POSITION_FLAG_RIGHT; @@ -61,8 +62,10 @@ import org.telegram.ui.Cells.ChatActionCell; import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.spoilers.SpoilerEffect; +import org.telegram.ui.Components.spoilers.SpoilerEffect2; import org.telegram.ui.LaunchActivity; import org.telegram.ui.PhotoViewer; +import org.telegram.ui.Stars.StarsIntroActivity; import java.util.ArrayList; import java.util.Arrays; @@ -967,6 +970,9 @@ public void fromPhotoLayout(ChatAttachAlertPhotoLayout photoLayout) { } public void fromPhotoArrays() { + for (int i = 0; i < groupCells.size(); ++i) { + groupCells.get(i).detach(); + } groupCells.clear(); ArrayList photos = new ArrayList<>(); final int photosOrderSize = photosOrder.size(), @@ -1913,6 +1919,13 @@ private class PreviewGroupCell { private float groupWidth = 0, groupHeight = 0; private float previousGroupWidth = 0, previousGroupHeight = 0; public ArrayList media = new ArrayList<>(); + public long stars; + + public void detach() { + for (int i = 0; i < media.size(); ++i) { + media.get(i).detach(); + } + } private class MediaCell { public PreviewGroupCell groupCell = PreviewGroupCell.this; @@ -1939,7 +1952,7 @@ private class MediaCell { private String videoDurationText = null; - private SpoilerEffect spoilerEffect = new SpoilerEffect(); + private SpoilerEffect2 spoilerEffect; private Path path = new Path(); private float[] radii = new float[8]; @@ -1966,6 +1979,13 @@ public void startCrossfade() { invalidate(); } + public void detach() { + if (spoilerEffect != null) { + spoilerEffect.detach(PreviewGroupsView.this); + spoilerEffect = null; + } + } + private void setImage(MediaController.PhotoEntry photoEntry) { this.photoEntry = photoEntry; if (photoEntry != null && photoEntry.isVideo) { @@ -2360,10 +2380,10 @@ public boolean draw(Canvas canvas, float t, boolean ignoreBounds) { blurredImage.setAlpha(scale); blurredImage.draw(canvas); - int sColor = Color.WHITE; - spoilerEffect.setColor(ColorUtils.setAlphaComponent(sColor, (int) (Color.alpha(sColor) * 0.325f * scale))); - spoilerEffect.setBounds(0, 0, getWidth(), getHeight()); - spoilerEffect.draw(canvas); + if (spoilerEffect == null) { + spoilerEffect = SpoilerEffect2.getInstance(PreviewGroupsView.this); + } + spoilerEffect.draw(canvas, PreviewGroupsView.this, getWidth(), getHeight()); canvas.restore(); invalidate(); @@ -2442,11 +2462,13 @@ private void setGroup(GroupCalculator group, boolean animated) { groupWidth = group.width / 1000f; groupHeight = group.height; lastMediaUpdate = animated ? now : 0; + stars = 0; List photoEntries = new ArrayList<>(group.positions.keySet()); final int photoEntriesCount = photoEntries.size(); for (int j = 0; j < photoEntriesCount; ++j) { MediaController.PhotoEntry photoEntry = photoEntries.get(j); MessageObject.GroupedMessagePosition pos = group.positions.get(photoEntry); + stars = Math.max(stars, photoEntry.starsAmount); MediaCell properCell = null; final int mediaCount = media.size(); for (int i = 0; i < mediaCount; ++i) { @@ -2473,6 +2495,7 @@ private void setGroup(GroupCalculator group, boolean animated) { if (!group.positions.containsKey(cell.photoEntry)) { // old cell, remove it if (cell.scale <= 0 && cell.lastUpdate + cell.updateDuration <= now) { + cell.detach(); media.remove(i); i--; mediaCount--; @@ -2504,6 +2527,11 @@ public float maxHeight() { return getT() >= 0.95f ? this.groupHeight * maxHeight * getPreviewScale() : measure(); } + private RectF buttonTextRect = new RectF(); + private Text buttonText; + private long buttonTextPrice; + private Paint buttonTextBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Theme.MessageDrawable messageBackground = (Theme.MessageDrawable) getThemedDrawable(Theme.key_drawable_msgOutMedia); private Theme.MessageDrawable.PathDrawParams backgroundCacheParams = new Theme.MessageDrawable.PathDrawParams(); public boolean draw(Canvas canvas) { @@ -2540,6 +2568,7 @@ public boolean draw(Canvas canvas) { width = right - left; height = bottom - top; + final int count = media.size(); for (int i = 0; i < count; ++i) { MediaCell cell = media.get(i); @@ -2553,8 +2582,30 @@ public boolean draw(Canvas canvas) { update = true; } } + + drawStarsButton(canvas); + return update; } + + public void drawStarsButton(Canvas canvas) { + if (stars <= 0) return; + + if (buttonText == null || buttonTextPrice != stars) { + buttonText = new Text(StarsIntroActivity.replaceStarsWithPlain(LocaleController.formatPluralStringComma("UnlockPaidContent", (int) (buttonTextPrice = stars)), .7f), 14, AndroidUtilities.bold()); + } + final float buttonWidth = dp(14 + 14) + buttonText.getCurrentWidth(); + final float buttonHeight = dp(32); + buttonTextRect.set( + left + (width - buttonWidth) / 2f, + top + (height - buttonHeight) / 2f, + left + (width + buttonWidth) / 2f, + top + (height + buttonHeight) / 2f + ); + buttonTextBgPaint.setColor(0x60000000); + canvas.drawRoundRect(buttonTextRect, buttonHeight / 2f, buttonHeight / 2f, buttonTextBgPaint); + buttonText.draw(canvas, left + width / 2f - buttonWidth / 2f + dp(14), top + height / 2f, 0xFFFFFFFF, 1f); + } } } 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 9e83d2670e..c3e244de79 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAvatarContainer.java @@ -55,6 +55,7 @@ import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Business.BusinessLinksController; import org.telegram.ui.ChatActivity; +import org.telegram.ui.LaunchActivity; import org.telegram.ui.ProfileActivity; import org.telegram.ui.Stories.StoriesUtilities; import org.telegram.ui.TopicsFragment; @@ -310,7 +311,9 @@ public boolean onTouchEvent(MotionEvent event) { if (parentFragment != null && (parentFragment.getChatMode() == 0 || parentFragment.getChatMode() == ChatActivity.MODE_SAVED)) { if ((!parentFragment.isThreadChat() || parentFragment.isTopic) && !UserObject.isReplyUser(parentFragment.getCurrentUser())) { - setOnClickListener(v -> openProfile(false)); + setOnClickListener(v -> { + openProfile(false); + }); } TLRPC.Chat chat = parentFragment.getCurrentChat(); @@ -368,6 +371,13 @@ protected void dispatchDraw(Canvas canvas) { canvas.restore(); } + public boolean ignoreTouches; + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (ignoreTouches) return false; + return super.dispatchTouchEvent(ev); + } + protected boolean canSearch() { return false; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmbedBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmbedBottomSheet.java index a02a5e766f..e415e16047 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/EmbedBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/EmbedBottomSheet.java @@ -390,7 +390,7 @@ public void onHideCustomView() { webView.setWebViewClient(new WebViewClient() { @Override public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) { - if (LaunchActivity.instance != null && LaunchActivity.instance.isFinishing()) { + if (!AndroidUtilities.isSafeToShow(getContext())) { return true; } new AlertDialog.Builder(getContext(), resourcesProvider) diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java index 42d35f81f8..0d5a3782db 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/GroupedPhotosListView.java @@ -75,6 +75,7 @@ public interface GroupedPhotosListViewDelegate { void onShowAnimationStart(); void onStopScrolling(); boolean validGroupId(long groupId); + boolean forceAll(); } public GroupedPhotosListView(Context context) { @@ -134,12 +135,13 @@ public void fillList() { changed = true; currentGroupId = localGroupId; } - if (currentGroupId != 0) { + final boolean forcedGroup = imagesArr.size() > 1 && delegate.forceAll(); + if (currentGroupId != 0 || forcedGroup) { hasPhotos = true; int max = Math.min(currentIndex + 10, imagesArr.size()); for (int a = currentIndex; a < max; a++) { MessageObject object = imagesArr.get(a); - if (slideshowMessageId != 0 || object.getGroupIdForUse() == currentGroupId) { + if (slideshowMessageId != 0 || forcedGroup || object.getGroupIdForUse() == currentGroupId) { newCount++; } else { break; @@ -148,7 +150,7 @@ public void fillList() { int min = Math.max(currentIndex - 10, 0); for (int a = currentIndex - 1; a >= min; a--) { MessageObject object = imagesArr.get(a); - if (slideshowMessageId != 0 || object.getGroupIdForUse() == currentGroupId) { + if (slideshowMessageId != 0 || forcedGroup || object.getGroupIdForUse() == currentGroupId) { newCount++; } else { break; @@ -282,11 +284,12 @@ public void onAnimationEnd(Animator animation) { animateToItem = -1; animateToItemFast = false; } else if (imagesArr != null && !imagesArr.isEmpty()) { - if (currentGroupId != 0 || slideshowMessageId != 0) { + final boolean forcedGroup = delegate.forceAll() && imagesArr.size() > 1; + if (currentGroupId != 0 || forcedGroup || slideshowMessageId != 0) { int max = Math.min(currentIndex + 10, imagesArr.size()); for (int a = currentIndex; a < max; a++) { MessageObject object = imagesArr.get(a); - if (slideshowMessageId != 0 || object.getGroupIdForUse() == currentGroupId) { + if (slideshowMessageId != 0 || forcedGroup || object.getGroupIdForUse() == currentGroupId) { currentObjects.add(object); currentPhotos.add(ImageLocation.getForObject(FileLoader.getClosestPhotoSizeWithSize(object.photoThumbs, 56, true), object.photoThumbsObject)); } else { @@ -299,7 +302,7 @@ public void onAnimationEnd(Animator animation) { int min = Math.max(currentIndex - 10, 0); for (int a = currentIndex - 1; a >= min; a--) { MessageObject object = imagesArr.get(a); - if (slideshowMessageId != 0 || object.getGroupIdForUse() == currentGroupId) { + if (slideshowMessageId != 0 || forcedGroup || object.getGroupIdForUse() == currentGroupId) { currentObjects.add(0, object); currentPhotos.add(0, ImageLocation.getForObject(FileLoader.getClosestPhotoSizeWithSize(object.photoThumbs, 56, true), object.photoThumbsObject)); currentImage++; 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 715d9bc12a..5b64c65273 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/InstantCameraView.java @@ -155,7 +155,7 @@ public class InstantCameraView extends FrameLayout implements NotificationCenter private volatile boolean cameraTextureAvailable; private final int[] position = new int[2]; - private final int[] cameraTexture = new int[2]; + private final int[] cameraTexture = new int[] { Integer.MIN_VALUE, Integer.MIN_VALUE }; private final int[] oldCameraTexture = new int[1]; private float cameraTextureAlpha = 1.0f; @@ -1653,9 +1653,13 @@ public void finish() { if (!eglContext.equals(egl10.eglGetCurrentContext()) || !eglSurface.equals(egl10.eglGetCurrentSurface(EGL10.EGL_DRAW))) { egl10.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext); } - if (cameraTexture != null && cameraTexture[0] != 0) { + if (cameraTexture != null && cameraTexture[0] != Integer.MIN_VALUE) { GLES20.glDeleteTextures(1, cameraTexture, 0); - cameraTexture[0] = 0; + cameraTexture[0] = Integer.MIN_VALUE; + } + if (cameraTexture != null && cameraTexture[1] != Integer.MIN_VALUE) { + GLES20.glDeleteTextures(1, cameraTexture, 1); + cameraTexture[1] = Integer.MIN_VALUE; } } if (eglSurface != null) { @@ -2563,10 +2567,13 @@ private void handleVideoFrameAvailable(long timestampNanos, Integer cameraId) { GLES20.glUniform2f(texelSizeHandle, (float) 1f / previewSize[surfaceIndex].getWidth() / 2f, (float) 1f / previewSize[surfaceIndex].getHeight() / 2f); } - GLES20.glUniformMatrix4fv(textureMatrixHandle, 1, false, mSTMatrix, 0); - GLES20.glUniform1f(alphaHandle, cameraTextureAlpha); - GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTexture[surfaceIndex]); - GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + final int tex = cameraTexture[surfaceIndex]; + if (tex != Integer.MIN_VALUE) { + GLES20.glUniformMatrix4fv(textureMatrixHandle, 1, false, mSTMatrix, 0); + GLES20.glUniform1f(alphaHandle, cameraTextureAlpha); + GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex); + GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); + } GLES20.glDisableVertexAttribArray(positionHandle); GLES20.glDisableVertexAttribArray(textureHandle); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java index a6a15a2f41..c29f27b10b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ItemOptions.java @@ -328,7 +328,7 @@ public ItemOptions addGap() { } public ItemOptions addSpaceGap() { - if (layout == lastLayout) { + if (!(layout instanceof LinearLayout)) { layout = new LinearLayout(context); ((LinearLayout) layout).setOrientation(LinearLayout.VERTICAL); layout.addView(lastLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); @@ -339,7 +339,7 @@ public ItemOptions addSpaceGap() { dismiss(); } }); - addView(lastLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, -8, 0, 0)); + layout.addView(lastLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, -8, 0, 0)); return this; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingSpan.java index fa803822e0..000b4180dc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/LoadingSpan.java @@ -1,5 +1,7 @@ package org.telegram.ui.Components; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.graphics.Canvas; import android.graphics.Paint; import android.text.style.ReplacementSpan; @@ -18,9 +20,10 @@ public class LoadingSpan extends ReplacementSpan { private LoadingDrawable drawable; public int yOffset; + private float scaleY = 1f; public LoadingSpan(View view, int size) { - this(view, size, AndroidUtilities.dp(2)); + this(view, size, dp(2)); } public LoadingSpan(View view, int size, int yOffset) { @@ -51,6 +54,10 @@ public void setColors(int color1, int color2) { this.drawable.color2 = color2; } + public void setScaleY(float scaleY) { + this.scaleY = scaleY; + } + public void setView(View view) { this.view = view; } @@ -70,7 +77,12 @@ public int getSize(@NonNull Paint paint, CharSequence charSequence, int i, int i @Override public void draw(@NonNull Canvas canvas, CharSequence charSequence, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { - drawable.setBounds((int) x, top + yOffset, (int) x + size, bottom - AndroidUtilities.dp(2) + yOffset); + drawable.setBounds( + (int) x, + (int) (top + (bottom - dp(2) - top) / 2f * (1f - scaleY) + yOffset), + (int) x + size, + (int) (bottom - dp(2) - ((bottom - dp(2)) - top) / 2f * (1f - scaleY) + yOffset) + ); if (paint != null) { drawable.setAlpha(paint.getAlpha()); } 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 631f42c6dc..cec1bfac69 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/MediaActivity.java @@ -68,6 +68,7 @@ public class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha public static final int TYPE_MEDIA = 0; public static final int TYPE_STORIES = 1; public static final int TYPE_ARCHIVED_CHANNEL_STORIES = 2; + public static final int TYPE_STORIES_SEARCH = 3; private int type; @@ -76,6 +77,7 @@ public class MediaActivity extends BaseFragment implements SharedMediaLayout.Sha private TLRPC.UserFull currentUserInfo; private long dialogId; private long topicId; + private String hashtag; private FrameLayout titlesContainer; private FrameLayout[] titles = new FrameLayout[2]; private SimpleTextView[] nameTextView = new SimpleTextView[2]; @@ -110,6 +112,7 @@ public boolean onFragmentCreate() { type = getArguments().getInt("type", TYPE_MEDIA); dialogId = getArguments().getLong("dialog_id"); topicId = getArguments().getLong("topic_id", 0); + hashtag = getArguments().getString("hashtag", ""); int defaultTab = SharedMediaLayout.TAB_PHOTOVIDEO; if (type == TYPE_ARCHIVED_CHANNEL_STORIES) { defaultTab = SharedMediaLayout.TAB_ARCHIVED_STORIES; @@ -340,32 +343,36 @@ protected void drawList(Canvas blurCanvas, boolean top, ArrayList SELECTION_PROGRESS_PROPERTY = new SimpleFloatPropertyCompat("selectionProgress", obj -> obj.selectionProgress, (obj, value) -> { obj.selectionProgress = value; - if (!obj.forceUseCenter) { + if (!obj.forceUseCenter || obj.forceForceUseCenter) { obj.outlinePaint.setStrokeWidth(AndroidUtilities.lerp(obj.strokeWidthRegular, obj.strokeWidthSelected, obj.selectionProgress)); obj.updateColor(); } obj.invalidate(); }).setMultiplier(SPRING_MULTIPLIER); + private final static SimpleFloatPropertyCompat TITLE_PROGRESS_PROPERTY = + new SimpleFloatPropertyCompat("titleProgress", obj -> obj.titleProgress, (obj, value) -> { + obj.titleProgress = value; + if (!obj.forceUseCenter || obj.forceForceUseCenter) { + obj.updateColor(); + } + obj.invalidate(); + }).setMultiplier(SPRING_MULTIPLIER); private final static SimpleFloatPropertyCompat ERROR_PROGRESS_PROPERTY = new SimpleFloatPropertyCompat("errorProgress", obj -> obj.errorProgress, (obj, value) -> { @@ -46,6 +55,9 @@ public class OutlineTextContainerView extends FrameLayout { private SpringAnimation selectionSpring = new SpringAnimation(this, SELECTION_PROGRESS_PROPERTY); private float selectionProgress; + private SpringAnimation titleSpring = new SpringAnimation(this, TITLE_PROGRESS_PROPERTY); + private float titleProgress; + private SpringAnimation errorSpring = new SpringAnimation(this, ERROR_PROGRESS_PROPERTY); private float errorProgress; @@ -53,7 +65,7 @@ public class OutlineTextContainerView extends FrameLayout { private float strokeWidthSelected = AndroidUtilities.dp(1.6667f); private EditText attachedEditText; - private boolean forceUseCenter; + private boolean forceUseCenter, forceForceUseCenter; private final Theme.ResourcesProvider resourcesProvider; @@ -80,6 +92,12 @@ public void setForceUseCenter(boolean forceUseCenter) { invalidate(); } + public void setForceForceUseCenter(boolean forceForceUseCenter) { + this.forceUseCenter = forceForceUseCenter; + this.forceForceUseCenter = forceForceUseCenter; + invalidate(); + } + public EditText getAttachedEditText() { return attachedEditText; } @@ -100,26 +118,48 @@ private void setColor(int color) { } public void updateColor() { - int textSelectionColor = ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhiteHintText, resourcesProvider), Theme.getColor(Theme.key_windowBackgroundWhiteValueText, resourcesProvider), forceUseCenter ? 0f : selectionProgress); + int textSelectionColor = ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhiteHintText, resourcesProvider), Theme.getColor(Theme.key_windowBackgroundWhiteValueText, resourcesProvider), forceUseCenter && !forceForceUseCenter ? 0f : titleProgress); textPaint.setColor(ColorUtils.blendARGB(textSelectionColor, Theme.getColor(Theme.key_text_RedBold, resourcesProvider), errorProgress)); - int selectionColor = ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhiteInputField, resourcesProvider), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated, resourcesProvider), forceUseCenter ? 0f : selectionProgress); + int selectionColor = ColorUtils.blendARGB(Theme.getColor(Theme.key_windowBackgroundWhiteInputField, resourcesProvider), Theme.getColor(Theme.key_windowBackgroundWhiteInputFieldActivated, resourcesProvider), forceUseCenter && !forceForceUseCenter ? 0f : selectionProgress); setColor(ColorUtils.blendARGB(selectionColor, Theme.getColor(Theme.key_text_RedBold, resourcesProvider), errorProgress)); } - public void animateSelection(float newValue) { - animateSelection(newValue, true); + public void animateSelection(boolean selected) { + animateSelection(selected ? 1f : 0f, selected ? 1f : 0f, true); } - public void animateSelection(float newValue, boolean animate) { + public void animateSelection(float selected) { + animateSelection(selected, selected, true); + } + + public void animateSelection(float selected, float title) { + animateSelection(selected, title, true); + } + + public void animateSelection(boolean selected, boolean title) { + animateSelection(selected ? 1f : 0f, title ? 1f : 0f, true); + } + + public void animateSelection(float selected, boolean animated) { + animateSelection(selected, selected, animated); + } + + public void animateSelection(boolean selected, boolean title, boolean animated) { + animateSelection(selected ? 1f : 0f, title ? 1f : 0f, animated); + } + + public void animateSelection(float selected, float title, boolean animate) { if (!animate) { - selectionProgress = newValue; + selectionProgress = selected; + titleProgress = title; if (!forceUseCenter) { outlinePaint.setStrokeWidth(strokeWidthRegular + (strokeWidthSelected - strokeWidthRegular) * selectionProgress); } updateColor(); return; } - animateSpring(selectionSpring, newValue); + animateSpring(selectionSpring, selected); + animateSpring(titleSpring, title); } public void animateError(float newValue) { @@ -139,6 +179,12 @@ private void animateSpring(SpringAnimation spring, float newValue) { .start(); } + private float leftPadding; + public void setLeftPadding(float padding) { + this.leftPadding = padding; + invalidate(); + } + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -147,10 +193,11 @@ protected void onDraw(Canvas canvas) { float topY = getPaddingTop() + textOffset; float centerY = getHeight() / 2f + textPaint.getTextSize() / 2f; boolean useCenter = attachedEditText != null && attachedEditText.length() == 0 && TextUtils.isEmpty(attachedEditText.getHint()) || forceUseCenter; - float textY = useCenter ? topY + (centerY - topY) * (1f - selectionProgress) : topY; + float textY = useCenter ? topY + (centerY - topY) * (1f - titleProgress) : topY; + float textX = useCenter ? leftPadding * (1f - titleProgress) : 0; float stroke = outlinePaint.getStrokeWidth(); - float scaleX = useCenter ? 0.75f + 0.25f * (1f - selectionProgress) : 0.75f; + float scaleX = useCenter ? 0.75f + 0.25f * (1f - titleProgress) : 0.75f; float textWidth = textPaint.measureText(mText) * scaleX; canvas.save(); @@ -165,14 +212,14 @@ protected void onDraw(Canvas canvas) { float activeLeft = left + textWidth + AndroidUtilities.dp(PADDING_LEFT - PADDING_TEXT); float fromLeft = left + textWidth / 2f; - canvas.drawLine(fromLeft + (activeLeft - fromLeft) * (useCenter ? selectionProgress : 1f), lineY, right, lineY, outlinePaint); + canvas.drawLine(fromLeft + (activeLeft - fromLeft) * (useCenter ? titleProgress : 1f), lineY, right, lineY, outlinePaint); float fromRight = left + textWidth / 2f + AndroidUtilities.dp(PADDING_TEXT); - canvas.drawLine(left, lineY, fromRight + (left - fromRight) * (useCenter ? selectionProgress : 1f), lineY, outlinePaint); + canvas.drawLine(left, lineY, fromRight + (left - fromRight) * (useCenter ? titleProgress : 1f), lineY, outlinePaint); canvas.save(); canvas.scale(scaleX, scaleX, getPaddingLeft() + AndroidUtilities.dp(PADDING_LEFT + PADDING_TEXT), textY); - canvas.drawText(mText, getPaddingLeft() + AndroidUtilities.dp(PADDING_LEFT), textY, textPaint); + canvas.drawText(mText, getPaddingLeft() + AndroidUtilities.dp(PADDING_LEFT) + textX, textY, textPaint); canvas.restore(); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/OverlayActionBarLayoutDialog.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/OverlayActionBarLayoutDialog.java index 06468b6349..ee209f223d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/OverlayActionBarLayoutDialog.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/OverlayActionBarLayoutDialog.java @@ -35,7 +35,7 @@ public OverlayActionBarLayoutDialog(@NonNull Context context, Theme.ResourcesPro super(context, R.style.TransparentDialog); this.resourcesProvider = resourcesProvider; - actionBarLayout = INavigationLayout.newLayout(context); + actionBarLayout = INavigationLayout.newLayout(context, false); actionBarLayout.setFragmentStack(new ArrayList<>()); actionBarLayout.presentFragment(new INavigationLayout.NavigationParams(new EmptyFragment()).setNoAnimation(true)); actionBarLayout.setDelegate(this); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/RenderView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/RenderView.java index 318079c882..f3e9e7244f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/RenderView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/RenderView.java @@ -480,7 +480,7 @@ private void checkBitmap() { transformedBitmap = true; } if (blurBitmap != null && (blurBitmap.getWidth() != paintingSize.width || blurBitmap.getHeight() != paintingSize.height)) { - Bitmap b = Bitmap.createBitmap((int) paintingSize.width, (int) paintingSize.height, Bitmap.Config.ALPHA_8); + Bitmap b = Bitmap.createBitmap((int) paintingSize.width, (int) paintingSize.height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(b); canvas.drawBitmap(blurBitmap, null, new RectF(0, 0, paintingSize.width, paintingSize.height), null); blurBitmap = b; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Texture.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Texture.java index f4b862575b..7473797c8b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Texture.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Texture.java @@ -6,6 +6,7 @@ import android.os.Build; import android.util.Log; +import org.telegram.messenger.FileLog; import org.telegram.ui.Components.Size; import java.nio.ByteBuffer; @@ -66,16 +67,21 @@ public int texture() { GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); -// int width = bitmap.getWidth(); -// int height = bitmap.getHeight(); -// int[] pixels = new int[width * height]; -// bitmap.getPixels(pixels, 0, width, 0, 0, width, height); -// for (int i = 0; i < pixels.length; i += 1) { -// int argb = pixels[i]; -// pixels[i] = argb & 0xff00ff00 | ((argb & 0xff) << 16) | ((argb >> 16) & 0xff); -// } -// GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, IntBuffer.wrap(pixels)); - GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); + try { + GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap, GLES20.GL_UNSIGNED_BYTE, 0); + } catch (Exception e) { + FileLog.e(e); + + int width = bitmap.getWidth(); + int height = bitmap.getHeight(); + int[] pixels = new int[width * height]; + bitmap.getPixels(pixels, 0, width, 0, 0, width, height); + for (int i = 0; i < pixels.length; i += 1) { + int argb = pixels[i]; + pixels[i] = argb & 0xff00ff00 | ((argb & 0xff) << 16) | ((argb >> 16) & 0xff); + } + GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, IntBuffer.wrap(pixels)); + } if (!bitmap.isRecycled() && Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { int px = bitmap.getPixel(0, 0); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EntityView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EntityView.java index b20f44e8db..dad03d4d34 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EntityView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/EntityView.java @@ -592,10 +592,11 @@ protected void updatePosition() { private float scale = 1f; public void scale(float scale) { + float oldScale = this.scale; this.scale *= scale; float newScale = Math.max(this.scale, 0.1f); newScale = Utilities.clamp(newScale, getMaxScale(), getMinScale()); - if (allowHaptic() && (newScale >= getMaxScale() || newScale <= getMinScale())) { + if (allowHaptic() && (newScale >= getMaxScale() || newScale <= getMinScale()) != (oldScale >= getMaxScale() || oldScale <= getMinScale())) { try { performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING); } catch (Exception ignore) { 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 9a2cc846d1..565d3a19f3 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 @@ -1444,6 +1444,7 @@ public boolean canShowWidget(Integer id) { if (widget == EmojiBottomSheet.WIDGET_PHOTO) { showPhotoAlert(); } + return true; }); emojiBottomSheet.setOnDismissListener(di -> { onOpenCloseStickersAlert(false); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LinkPreview.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LinkPreview.java new file mode 100644 index 0000000000..acbf2bbc3d --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LinkPreview.java @@ -0,0 +1,530 @@ +package org.telegram.ui.Components.Paint.Views; + +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.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.text.TextUtils; +import android.view.View; + +import androidx.core.graphics.ColorUtils; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.ImageLoader; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; +import org.telegram.tgnet.AbstractSerializedData; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.ChatMessageCell; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.Text; +import org.telegram.ui.Stories.recorder.HintView2; + +public class LinkPreview extends View { + + private int currentAccount; + public int maxWidth; + private boolean relayout = true; + private boolean animated; + + public final float density; + public final int padx, pady; + + private float textScale = 1; + private final TextPaint layoutPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private StaticLayout layout; + private float layoutWidth, layoutLeft; + + private final RectF padding = new RectF(4, 4.33f, 7.66f, 3); + private final float iconPadding = 3.25f; + private final float flagIconPadding = 2.25f; + private final float iconSize = 30f; + + private final Drawable icon; + private final Paint outlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private boolean messageAbove; + private Text messageText; + + private Paint previewPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private boolean hasTitle; + private Text titleText; + private boolean hasSiteName; + private Text siteNameText; + + private boolean hasDescription; + private final TextPaint descriptionPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); + private StaticLayout descriptionLayout; + private float descriptionLayoutWidth, descriptionLayoutLeft; + + public boolean hasPhoto; + private boolean smallPhoto; + private final ImageReceiver photoImage = new ImageReceiver(this); + + public LinkPreview(Context context, float density) { + super(context); + this.density = density; + photoImage.setInvalidateAll(true); + + padx = (int) (3 * density); + pady = (int) (1 * density); + + icon = context.getResources().getDrawable(R.drawable.story_link).mutate(); + layoutPaint.setTextSize(24 * density); + layoutPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rcondensedbold.ttf")); + } + + public void setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + this.relayout = true; + } + + private boolean video; + public void setVideoTexture() { + this.video = true; + } + + public int type, color; + private WebPagePreview webpage; + + private float w, h; + private float previewHeight; + private float photoHeight; + + private void setupLayout() { + if (!relayout || webpage == null) { + return; + } + + final String text = TextUtils.isEmpty(webpage.name) ? fromUrl(webpage.url) : webpage.name; + if (withPreview()) { + final TLRPC.WebPage preview = this.webpage.webpage; + float maxWidth = this.maxWidth - padx - padx; + + h = 0; + w = 0; + previewHeight = 0; + + final TLRPC.User user = UserConfig.getInstance(currentAccount).getCurrentUser(); + final int colorId = UserObject.getColorId(user); + MessagesController.PeerColors peerColors = MessagesController.getInstance(currentAccount).peerColors; + MessagesController.PeerColor color = peerColors == null || colorId < 7 ? null : peerColors.getColor(colorId); + previewPaint.setColor(color == null ? Theme.getColor(Theme.keys_avatar_nameInMessage[colorId % Theme.keys_avatar_nameInMessage.length]) : color.getColor1()); + + h += 7.33f * density; + messageAbove = webpage.captionAbove; + messageText = new Text(text, 16).setTextSizePx(16 * density).setMaxWidth(maxWidth - 20 * density); + w = Math.max(w, Math.min(messageText.getCurrentWidth() + 20 * density, maxWidth)); + h += messageText.getHeight(); + h += 7 * density; + + hasPhoto = preview.photo != null || MessageObject.isVideoDocument(preview.document); + smallPhoto = !webpage.largePhoto; + final int photoSide = video && (webpage.flags & 4) != 0 ? webpage.photoSize : (int) (smallPhoto ? 48 : maxWidth / density - 40) * 2; + photoImage.setRoundRadius((int) (4 * density)); + int pw = 0, ph = 0; + if (preview.photo != null) { + TLRPC.PhotoSize thumbPhotoSize = FileLoader.getClosestPhotoSizeWithSize(preview.photo.sizes, 1, false, null, false); + TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(preview.photo.sizes, (int) (photoSide * density), false, thumbPhotoSize, false); + if (photoSize != null) { + pw = photoSize.w; + ph = photoSize.h; + } + photoImage.setImage(ImageLocation.getForPhoto(photoSize, preview.photo), photoSide + "_" + photoSide, video ? null : ImageLocation.getForPhoto(thumbPhotoSize, preview.photo), video ? null : photoSide + "_" + photoSide,0, null, null, 0); + } else if (preview.document != null) { + TLRPC.PhotoSize thumbPhotoSize = FileLoader.getClosestPhotoSizeWithSize(preview.document.thumbs, 1, false, null, false); + TLRPC.PhotoSize photoSize = FileLoader.getClosestPhotoSizeWithSize(preview.document.thumbs, (int) (photoSide * density), false, thumbPhotoSize, false); + if (photoSize != null) { + pw = photoSize.w; + ph = photoSize.h; + } + photoImage.setImage(ImageLocation.getForDocument(photoSize, preview.document), photoSide + "_" + photoSide, video ? null : ImageLocation.getForDocument(thumbPhotoSize, preview.document), video ? null : photoSide + "_" + photoSide,0, null, null, 0); + } + + int lines = 0; + previewHeight += 5.66f * density; + hasSiteName = !TextUtils.isEmpty(preview.site_name); + if (hasSiteName) { + siteNameText = new Text(preview.site_name, 14, AndroidUtilities.bold()).setTextSizePx(14 * density).setMaxWidth((int) Math.ceil(maxWidth - 40 * density - (hasPhoto && smallPhoto ? (48 + 12) * density : 0))); + w = Math.max(w, Math.min(siteNameText.getCurrentWidth() + 40 * density + (hasPhoto && smallPhoto ? (48 + 12) * density : 0), maxWidth)); + previewHeight += siteNameText.getHeight(); + previewHeight += 2.66f * density; + lines += siteNameText.getLineCount(); + } + + hasTitle = !TextUtils.isEmpty(preview.title); + if (hasTitle) { + titleText = new Text(preview.title, 14, AndroidUtilities.bold()).setTextSizePx(14 * density).setMaxWidth((int) Math.ceil(maxWidth - 40 * density - (hasPhoto && smallPhoto ? (48 + 12) * density : 0))); + w = Math.max(w, Math.min(titleText.getCurrentWidth() + 40 * density + (hasPhoto && smallPhoto ? (48 + 12) * density : 0), maxWidth)); + previewHeight += titleText.getHeight(); + previewHeight += 2.66f * density; + lines += titleText.getLineCount(); + } + + hasDescription = !TextUtils.isEmpty(preview.description); + if (hasDescription) { + descriptionPaint.setTextSize(14 * density); + descriptionLayout = ChatMessageCell.generateStaticLayout(preview.description, descriptionPaint, (int) Math.ceil(Math.max(1, maxWidth - 40 * density)), (int) Math.ceil(Math.max(1, maxWidth - (40 + (hasPhoto && smallPhoto ? 48 + 12 : 0)) * density)), 3 - lines, 4); + descriptionLayoutWidth = 0; + descriptionLayoutLeft = Float.MAX_VALUE; + for (int i = 0; i < descriptionLayout.getLineCount(); ++i) { + boolean photoLine = hasPhoto && smallPhoto && i < (3 - lines); + descriptionLayoutWidth = Math.max(descriptionLayoutWidth, descriptionLayout.getLineWidth(i) + (photoLine ? 48 * density : 0)); + descriptionLayoutLeft = Math.min(descriptionLayoutLeft, descriptionLayout.getLineLeft(i)); + } + w = Math.max(w, Math.min(descriptionLayoutWidth + 40 * density, maxWidth)); + + previewHeight += descriptionLayout.getHeight(); + previewHeight += 2.66f * density; + } + + if (hasPhoto && !smallPhoto) { + if (pw <= 0 || ph <= 0) { + photoHeight = 120 * density; + } else { + final float photoWidth = Math.max(0, w - 40 * density); + photoHeight = Math.min(photoWidth / pw * ph, 200 * density); + } + previewHeight += photoHeight; + previewHeight += 2.66f * density; + } + + previewHeight += 7 * density; + h += previewHeight; + + h += 11 * density; + + } else { + + float maxWidth = this.maxWidth - padx - padx - (padding.left + iconSize + iconPadding + padding.right) * density; + textScale = 1f; + layout = new StaticLayout(TextUtils.ellipsize(text, layoutPaint, (int) Math.ceil(maxWidth), TextUtils.TruncateAt.END), layoutPaint, (int) Math.ceil(maxWidth), Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + + layoutWidth = 0; + layoutLeft = Float.MAX_VALUE; + for (int i = 0; i < layout.getLineCount(); ++i) { + layoutWidth = Math.max(layoutWidth, layout.getLineWidth(i)); + layoutLeft = Math.min(layoutLeft, layout.getLineLeft(i)); + } + if (layout.getLineCount() > 2) { + textScale = .3f; + } else { + textScale = Math.min(1, maxWidth / layoutWidth); + } + + w = (padding.left + iconSize + iconPadding + padding.right) * density + layoutWidth * textScale; + h = (padding.top + padding.bottom) * density + Math.max(iconSize * density, layout.getHeight() * textScale); + } + + if (!animated) { + captionAbove.set(this.messageAbove, true); + photoSmallProgress.set(this.smallPhoto, true); + photoAlphaProgress.set(this.hasPhoto, true); + previewHeightProgress.set(this.previewHeight, true); + } else { + invalidate(); + } + + relayout = false; + } + + public void pushPhotoToCache() { + if (!hasPhoto) return; + if (!photoImage.hasImageLoaded() || photoImage.getBitmap() == null) return; + ImageLoader.getInstance().putImageToCache(new BitmapDrawable(photoImage.getBitmap()), photoImage.getImageKey(), false); + } + + public int getPhotoSide() { + return (int) (smallPhoto ? 48 : (maxWidth - padx - padx) / density - 40) * 2; + } + + public boolean withPreview() { + return webpage != null && webpage.webpage != null; + } + + public int backgroundColor; + + public void setType(int type, int color) { + if (this.type == 1) return; + if (type == 0) { + backgroundColor = color; + final int textColor = AndroidUtilities.computePerceivedBrightness(color) >= .721f ? Color.BLACK : Color.WHITE; + layoutPaint.setColor(textColor); + icon.setColorFilter(new PorterDuffColorFilter(textColor, PorterDuff.Mode.SRC_IN)); + } else if (type == 1) { + backgroundColor = 0xFF000000; + layoutPaint.setColor(0xFFFFFFFF); + icon.setColorFilter(new PorterDuffColorFilter(0xFFFFFFFF, PorterDuff.Mode.SRC_IN)); + } else if (type == 2) { + backgroundColor = 0x4C000000; + layoutPaint.setColor(0xFFFFFFFF); + icon.setColorFilter(null); + } else { + backgroundColor = 0xFFFFFFFF; + layoutPaint.setColor(0xFF3391D4); + icon.setColorFilter(new PorterDuffColorFilter(0xFF3391D4, PorterDuff.Mode.SRC_IN)); + } + invalidate(); + } + + public void set(int currentAccount, WebPagePreview webpage) { + set(currentAccount, webpage, false); + } + + public void set(int currentAccount, WebPagePreview webpage, boolean animated) { + this.currentAccount = currentAccount; + if (this.webpage != webpage || animated) { + this.webpage = webpage; + this.relayout = true; + this.animated = animated; + this.requestLayout(); + } + } + + public static String fromUrl(String url) { + return url; + } + + + private final RectF bounds = new RectF(); + private final RectF rect = new RectF(); + private final Path path = new Path(); + private final Path path2 = new Path(); + + private final RectF rect1 = new RectF(); + private final RectF rect2 = new RectF(); + + private final AnimatedFloat captionAbove = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat photoAlphaProgress = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat photoSmallProgress = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat previewProgress = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat previewHeightProgress = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat width = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat height = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + @Override + protected void dispatchDraw(Canvas canvas) { + drawInternal(canvas); + } + + public float getRadius() { + return withPreview() ? 16.66f * density : .2f * h; + } + + public void drawInternal(Canvas canvas) { + setupLayout(); + + final float w = width.set(this.w); + final float h = height.set(this.h); + final float preview = previewProgress.set(withPreview()); + final float r = AndroidUtilities.lerp(.2f * h, 16.66f * density, preview); + + bounds.set(padx, pady, padx + w, pady + h); + outlinePaint.setColor(ColorUtils.blendARGB(backgroundColor, 0xFF202429, preview)); + + path2.rewind(); + path2.addRoundRect(bounds, r, r, Path.Direction.CW); + canvas.drawPath(path2, outlinePaint); + + if (preview > 0) { + canvas.save(); + canvas.clipPath(path2); + canvas.translate(padx, pady); + + final float above = captionAbove.set(this.messageAbove); + + float y = 0; + y += 7.33f * density; + if (messageText != null && above > 0) { + messageText + .draw(canvas, 10 * density, y + messageText.getHeight() / 2f - (messageText.getHeight() + 15 * density) * (1f - above), 0xFF1A9CFF, preview); + y += (messageText.getHeight() + 7 * density) * above; + } + + final float previewHeight = previewHeightProgress.set(this.previewHeight); + float linkY = y; + previewPaint.setAlpha((int) (0xFF * .1f)); + rect.set(10 * density, linkY, w - 10 * density, linkY + previewHeight); + path.rewind(); + path.addRoundRect(rect, 5 * density, 5 * density, Path.Direction.CW); + canvas.drawPath(path, previewPaint); + canvas.save(); + canvas.clipPath(path); + previewPaint.setAlpha(0xFF); + canvas.drawRect(10 * density, linkY, 13 * density, linkY + previewHeight, previewPaint); + canvas.restore(); + + y += 5.66f * density; + if (hasSiteName && siteNameText != null) { + siteNameText + .draw(canvas, 20 * density, y + siteNameText.getHeight() / 2f, previewPaint.getColor(), preview); + y += siteNameText.getHeight() + 2.66f * density; + } + + if (hasTitle && titleText != null) { + titleText + .draw(canvas, 20 * density, y + titleText.getHeight() / 2f, 0xFFFFFFFF, preview); + y += titleText.getHeight() + 2.66f * density; + } + + if (hasDescription && descriptionLayout != null) { + canvas.save(); + canvas.translate(20 * density - descriptionLayoutLeft, y); + descriptionPaint.setColor(0xFFFFFFFF); + descriptionPaint.setAlpha((int) (0xFF * preview)); + descriptionLayout.draw(canvas); + canvas.restore(); + y += descriptionLayout.getHeight() + 2.66f * density; + } + + float photoAlpha = photoAlphaProgress.set(this.hasPhoto); + if (photoAlpha > 0) { + final float smallPhoto = photoSmallProgress.set(this.smallPhoto); + rect1.set(20 * density, y + 2.66f * density, w - 20 * density, y + 2.66f * density + photoHeight); + rect2.set(w - 10 * density - 6 * density - 48 * density, linkY + 6 * density, w - 10 * density - 6 * density, linkY + 6 * density + 48 * density); + AndroidUtilities.lerp(rect1, rect2, smallPhoto, rect); + photoImage.setImageCoords(rect.left, rect.top, rect.width(), rect.height()); + photoImage.setAlpha(photoAlpha * preview); + photoImage.draw(canvas); + y += (1f - smallPhoto) * (2.66f * density + photoHeight); + } + + y += 7f * density; + y += 5f * density; + + if (messageText != null && (1f - above) > 0) { + messageText + .draw(canvas, 10 * density, y + messageText.getHeight() / 2f + (messageText.getHeight() + 15 * density) * above, 0xFF1A9CFF, preview); + y += (messageText.getHeight() + 7 * density) * (1f - above); + } + + canvas.restore(); + } + + if (preview < 1) { + icon.setBounds( + padx + (int) (padding.left * density), + pady + (int) ((h - iconSize * density) / 2), + padx + (int) ((padding.left + iconSize) * density), + pady + (int) ((h + iconSize * density) / 2) + ); + icon.setAlpha((int) (0xFF * (1f - preview))); + icon.draw(canvas); + + if (layout != null) { + canvas.save(); + canvas.translate(padx + (padding.left + iconSize + iconPadding) * density, pady + h / 2f); + canvas.scale(textScale, textScale); + canvas.translate(-layoutLeft, -layout.getHeight() / 2f); + layoutPaint.setAlpha((int) (0xFF * (1f - preview))); + layout.draw(canvas); + canvas.restore(); + } + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + setupLayout(); + setMeasuredDimension(padx + (int) Math.ceil(w) + padx, pady + (int) Math.ceil(h) + pady); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + photoImage.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + photoImage.onDetachedFromWindow(); + } + + public static class WebPagePreview extends TLObject { + public static final int constructor = 0xDAB228AB; + + public int flags; + + public String name; + public String url; + public TLRPC.WebPage webpage; + + public boolean largePhoto; + public boolean captionAbove = true; + + public int photoSize; + + public static WebPagePreview TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) { + if (WebPagePreview.constructor != constructor) { + if (exception) { + throw new RuntimeException(String.format("can't parse magic %x in WebPagePreview", constructor)); + } else { + return null; + } + } + WebPagePreview result = new WebPagePreview(); + result.readParams(stream, exception); + return result; + } + + @Override + public void serializeToStream(AbstractSerializedData stream) { + stream.writeInt32(constructor); + flags = (webpage != null) ? flags | 1 : flags &~ 1; + flags = !TextUtils.isEmpty(name) ? flags | 2 : flags &~ 2; + flags = largePhoto ? flags | 8 : flags &~ 8; + flags = captionAbove ? flags | 16 : flags &~ 16; + stream.writeInt32(flags); + stream.writeString(url); + if ((flags & 1) != 0) { + webpage.serializeToStream(stream); + } + if ((flags & 2) != 0) { + stream.writeString(name); + } + if ((flags & 4) != 0) { + stream.writeInt32(photoSize); + } + } + + @Override + public void readParams(AbstractSerializedData stream, boolean exception) { + flags = stream.readInt32(exception); + largePhoto = (flags & 8) != 0; + captionAbove = (flags & 16) != 0; + url = stream.readString(exception); + if ((flags & 1) != 0) { + webpage = TLRPC.WebPage.TLdeserialize(stream, stream.readInt32(exception), exception); + } + if ((flags & 2) != 0) { + name = stream.readString(exception); + } + if ((flags & 4) != 0) { + photoSize = stream.readInt32(exception); + } + } + + public boolean isPreviewEmpty() { + return webpage == null; + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LinkView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LinkView.java new file mode 100644 index 0000000000..ed9e0176e4 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LinkView.java @@ -0,0 +1,220 @@ +package org.telegram.ui.Components.Paint.Views; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.view.Gravity; +import android.view.ViewGroup; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.tgnet.TLRPC; +import org.telegram.tgnet.tl.TL_stories; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.Point; +import org.telegram.ui.Components.Rect; + +public class LinkView extends EntityView { + + public final LinkPreview marker; + private int currentColor; + private int currentType; + + public LinkPreview.WebPagePreview link; + public TL_stories.MediaArea mediaArea; + + @Override + protected float getStickyPaddingLeft() { + return marker.padx; + } + + @Override + protected float getStickyPaddingTop() { + return marker.pady; + } + + @Override + protected float getStickyPaddingRight() { + return marker.padx; + } + + @Override + protected float getStickyPaddingBottom() { + return marker.pady; + } + + public LinkView(Context context, Point position, int currentAccount, LinkPreview.WebPagePreview link, TL_stories.MediaArea mediaArea, float density, int maxWidth, int type, int color) { + super(context, position); + + marker = new LinkPreview(context, density); + marker.setMaxWidth(maxWidth); + setLink(currentAccount, link, mediaArea); + marker.setType(currentType = type, currentColor = color); + addView(marker, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP)); + + setClipChildren(false); + setClipToPadding(false); + + updatePosition(); + } + + public void setLink(int currentAccount, LinkPreview.WebPagePreview link, TL_stories.MediaArea area) { + this.link = link; + this.mediaArea = area; + marker.set(currentAccount, link); + updateSelectionView(); + } + + public void setMaxWidth(int maxWidth) { + marker.setMaxWidth(maxWidth); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + updatePosition(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + updatePosition(); + } + + public void setType(int type) { + marker.setType(currentType = type, currentColor); + } + + public void setType(int type, int color) { + marker.setType(currentType = type, currentColor = color); + } + + public void setColor(int color) { + setType(currentType, color); + } + + public int getColor() { + return currentColor; + } + + public int getType() { + return currentType; + } + + @Override + protected float getMaxScale() { + return 1.5f; + } + + @Override + public Rect getSelectionBounds() { + ViewGroup parentView = (ViewGroup) getParent(); + if (parentView == null) { + return new Rect(); + } + float scale = parentView.getScaleX(); + float width = getMeasuredWidth() * getScale() + AndroidUtilities.dp(64) / scale; + float height = getMeasuredHeight() * getScale() + AndroidUtilities.dp(64) / scale; + float left = (getPositionX() - width / 2.0f) * scale; + float right = left + width * scale; + return new Rect(left, (getPositionY() - height / 2f) * scale, right - left, height * scale); + } + + protected TextViewSelectionView createSelectionView() { + return new TextViewSelectionView(getContext()); + } + + public class TextViewSelectionView extends SelectionView { + + private final Paint clearPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + public TextViewSelectionView(Context context) { + super(context); + clearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + } + + @Override + protected int pointInsideHandle(float x, float y) { + float thickness = AndroidUtilities.dp(1.0f); + float radius = AndroidUtilities.dp(19.5f); + + float inset = radius + thickness; + float width = getMeasuredWidth() - inset * 2; + float height = getMeasuredHeight() - inset * 2; + + float middle = inset + height / 2.0f; + + if (x > inset - radius && y > middle - radius && x < inset + radius && y < middle + radius) { + return SELECTION_LEFT_HANDLE; + } else if (x > inset + width - radius && y > middle - radius && x < inset + width + radius && y < middle + radius) { + return SELECTION_RIGHT_HANDLE; + } + + if (x > inset && x < width && y > inset && y < height) { + return 0; + } + + return 0; + } + + private Path path = new Path(); + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + int count = canvas.getSaveCount(); + + float alpha = getShowAlpha(); + if (alpha <= 0) { + return; + } else if (alpha < 1) { + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), (int) (0xFF * alpha), Canvas.ALL_SAVE_FLAG); + } + + float thickness = AndroidUtilities.dp(2.0f); + float radius = AndroidUtilities.dpf2(5.66f); + + float inset = radius + thickness + AndroidUtilities.dp(15); + + float width = getMeasuredWidth() - inset * 2; + float height = getMeasuredHeight() - inset * 2; + + AndroidUtilities.rectTmp.set(inset, inset, inset + width, inset + height); + + float R = AndroidUtilities.dp(12); + float rx = Math.min(R, width / 2f), ry = Math.min(R, height / 2f); + + path.rewind(); + AndroidUtilities.rectTmp.set(inset, inset, inset + rx * 2, inset + ry * 2); + path.arcTo(AndroidUtilities.rectTmp, 180, 90); + AndroidUtilities.rectTmp.set(inset + width - rx * 2, inset, inset + width, inset + ry * 2); + path.arcTo(AndroidUtilities.rectTmp, 270, 90); + canvas.drawPath(path, paint); + + path.rewind(); + AndroidUtilities.rectTmp.set(inset, inset + height - ry * 2, inset + rx * 2, inset + height); + path.arcTo(AndroidUtilities.rectTmp, 180, -90); + AndroidUtilities.rectTmp.set(inset + width - rx * 2, inset + height - ry * 2, inset + width, inset + height); + path.arcTo(AndroidUtilities.rectTmp, 90, -90); + canvas.drawPath(path, paint); + + canvas.drawCircle(inset, inset + height / 2.0f, radius, dotStrokePaint); + canvas.drawCircle(inset, inset + height / 2.0f, radius - AndroidUtilities.dp(1) + 1, dotPaint); + + canvas.drawCircle(inset + width, inset + height / 2.0f, radius, dotStrokePaint); + canvas.drawCircle(inset + width, inset + height / 2.0f, radius - AndroidUtilities.dp(1) + 1, dotPaint); + + canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + + canvas.drawLine(inset, inset + ry, inset, inset + height - ry, paint); + canvas.drawLine(inset + width, inset + ry, inset + width, inset + height - ry, paint); + canvas.drawCircle(inset + width, inset + height / 2.0f, radius + AndroidUtilities.dp(1) - 1, clearPaint); + canvas.drawCircle(inset, inset + height / 2.0f, radius + AndroidUtilities.dp(1) - 1, clearPaint); + + canvas.restoreToCount(count); + } + } +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LocationMarker.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LocationMarker.java index 72986f3c72..e6bc6c97f6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LocationMarker.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LocationMarker.java @@ -68,9 +68,10 @@ public class LocationMarker extends View { private StaticLayout layout; private float layoutWidth, layoutLeft; + public final int type; public final int padx, pady; - public LocationMarker(Context context, float density) { + public LocationMarker(Context context, float density, int type) { super(context); this.density = density; @@ -79,6 +80,7 @@ public LocationMarker(Context context, float density) { padx = (int) (3 * density); pady = (int) (1 * density); + this.type = type; icon = context.getResources().getDrawable(R.drawable.map_pin3).mutate(); textPaint.setTextSize(24 * density); textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rcondensedbold.ttf")); @@ -224,6 +226,7 @@ public String getText() { } public void setType(int type, int color) { + if (this.type == 1) return; if (type == 0) { outlinePaint.setColor(color); final int textColor = AndroidUtilities.computePerceivedBrightness(color) >= .721f ? Color.BLACK : Color.WHITE; @@ -288,6 +291,10 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { private final RectF bounds = new RectF(); private final Path path = new Path(); + public float getRadius() { + return .2f * h; + } + @Override protected void dispatchDraw(Canvas canvas) { setupLayout(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LocationView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LocationView.java index 347aa67bda..b5cffa37d6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LocationView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/LocationView.java @@ -71,7 +71,7 @@ public static String geo(double Lat, double Long) { public LocationView(Context context, Point position, int currentAccount, TLRPC.MessageMedia location, TL_stories.MediaArea mediaArea, float density, int maxWidth, int type, int color) { super(context, position); - marker = new LocationMarker(context, density); + marker = new LocationMarker(context, density, 0); marker.setMaxWidth(maxWidth); setLocation(currentAccount, location, mediaArea); marker.setType(currentType = type, currentColor = color); 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 5a361eafdf..637dceab27 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 @@ -960,11 +960,12 @@ private ChatMessageCell getCell() { return null; } - public void getBubbleBounds(RectF rect) { + public float getBubbleBounds(RectF rect) { float left = Integer.MAX_VALUE; float right = Integer.MIN_VALUE; float top = Integer.MAX_VALUE; float bottom = Integer.MIN_VALUE; + float radius = 0; for (int i = 0; i < listView.getChildCount(); ++i) { View child = listView.getChildAt(i); if (child instanceof ChatMessageCell) { @@ -980,9 +981,9 @@ public void getBubbleBounds(RectF rect) { if (groupedMessages == null) { // pinned bottom cleft += dp(8); } - cright = container.getX() + child.getX() + cell.getBackgroundDrawableRight() - dp(1); - ctop = container.getY() + child.getY() + cell.getBackgroundDrawableTop() + dp(1.33f); - cbottom = container.getY() + child.getY() + cell.getBackgroundDrawableBottom() - dp(.66f); + cright = container.getX() + child.getX() + cell.getBackgroundDrawableRight() - dp(1.66f); + ctop = container.getY() + child.getY() + cell.getBackgroundDrawableTop() + dp(2); + cbottom = container.getY() + child.getY() + cell.getBackgroundDrawableBottom() - dp(1); } left = Math.min(left, cleft); left = Math.min(left, cright); @@ -995,6 +996,7 @@ public void getBubbleBounds(RectF rect) { } } rect.set(left, top, right, bottom); + return dp(SharedConfig.bubbleRadius); } public void invalidateAll() { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/StoryLinkPreviewDialog.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/StoryLinkPreviewDialog.java new file mode 100644 index 0000000000..4e330b4af0 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Paint/Views/StoryLinkPreviewDialog.java @@ -0,0 +1,423 @@ +package org.telegram.ui.Components.Paint.Views; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.LocaleController.getString; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.app.Dialog; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; +import android.graphics.Insets; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.os.Message; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.WindowInsetsCompat; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessageObject; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.ItemOptions; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.MessagePreviewView; +import org.telegram.ui.SecretVoicePlayer; +import org.telegram.ui.Stories.DarkThemeResourceProvider; +import org.telegram.ui.Stories.recorder.PreviewView; + +public class StoryLinkPreviewDialog extends Dialog { + + private final int currentAccount; + private final Theme.ResourcesProvider resourcesProvider = new DarkThemeResourceProvider(); + + private final FrameLayout windowView; + private final LinearLayout containerView; + + private final FrameLayout previewContainer; + private final FrameLayout previewInnerContainer; + private final ImageView backgroundView; + private final FrameLayout actionBarContainer; + private final TextView titleTextView, subtitleTextView; + + private final LinkPreview linkView; + + private final Rect insets = new Rect(); + private Bitmap blurBitmap; + private BitmapShader blurBitmapShader; + private Paint blurBitmapPaint; + private Matrix blurMatrix; + + private final MessagePreviewView.ToggleButton captionButton; + private final MessagePreviewView.ToggleButton photoButton; + + private float openProgress; + + public StoryLinkPreviewDialog(Context context, int currentAccount) { + super(context, R.style.TransparentDialog); + this.currentAccount = currentAccount; + + windowView = new FrameLayout(context) { + @Override + protected void dispatchDraw(Canvas canvas) { + if (openProgress > 0 && blurBitmapPaint != null) { + blurMatrix.reset(); + final float s = (float) getWidth() / blurBitmap.getWidth(); + blurMatrix.postScale(s, s); + blurBitmapShader.setLocalMatrix(blurMatrix); + + blurBitmapPaint.setAlpha((int) (0xFF * openProgress)); + canvas.drawRect(0, 0, getWidth(), getHeight(), blurBitmapPaint); + } + super.dispatchDraw(canvas); + } + + @Override + public boolean dispatchKeyEventPreIme(KeyEvent event) { + if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { + onBackPressed(); + return true; + } + return super.dispatchKeyEventPreIme(event); + } + }; + windowView.setOnClickListener(v -> { + onBackPressed(); + }); + + containerView = new LinearLayout(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(Math.min(MeasureSpec.getSize(widthMeasureSpec), dp(600)), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(Math.min(MeasureSpec.getSize(heightMeasureSpec), dp(800)), MeasureSpec.EXACTLY) + ); + } + }; + containerView.setOrientation(LinearLayout.VERTICAL); + windowView.addView(containerView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 8, 8, 8, 8)); + + previewContainer = new FrameLayout(context) { + private final Path path = new Path(); + private final RectF rect = new RectF(); + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + path.rewind(); + rect.set(0,0,getMeasuredWidth(),getMeasuredHeight()); + path.addRoundRect(rect, dp(10), dp(10), Path.Direction.CW); + if (linkView != null) { + linkView.setMaxWidth(getMeasuredWidth() - dp(32)); + } + } + + @Override + public void draw(Canvas canvas) { + canvas.save(); + canvas.clipPath(path); + super.draw(canvas); + canvas.restore(); + } + }; + previewContainer.setWillNotDraw(false); + containerView.addView(previewContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 1, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 0, 0, 0, 0)); + + actionBarContainer = new FrameLayout(context); + actionBarContainer.setBackgroundColor(0xFF1F1F1F); + previewContainer.addView(actionBarContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 56, Gravity.FILL_HORIZONTAL | Gravity.TOP)); + + titleTextView = new TextView(context); + titleTextView.setText(LocaleController.getString(R.string.StoryLinkPreviewTitle)); + titleTextView.setTextColor(0xFFFFFFFF); + titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + titleTextView.setTypeface(AndroidUtilities.bold()); + actionBarContainer.addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, 18, 8.33f, 18, 0)); + + subtitleTextView = new TextView(context); + subtitleTextView.setText(LocaleController.getString(R.string.StoryLinkPreviewSubtitle)); + subtitleTextView.setTextColor(0xFF7F7F7F); + subtitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + actionBarContainer.addView(subtitleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL_HORIZONTAL | Gravity.TOP, 18, 31, 18, 0)); + + previewInnerContainer = new FrameLayout(context) { + private final AnimatedFloat x = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + private final AnimatedFloat y = new AnimatedFloat(this, 0, 350, CubicBezierInterpolator.EASE_OUT_QUINT); + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (child == linkView) { + canvas.save(); + canvas.translate(x.set(child.getX()), y.set(child.getY())); + linkView.drawInternal(canvas); + canvas.restore(); + return true; + } + return super.drawChild(canvas, child, drawingTime); + } + }; + previewContainer.addView(previewInnerContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL, 0, 56, 0, 0)); + + backgroundView = new ImageView(context); + backgroundView.setScaleType(ImageView.ScaleType.CENTER_CROP); + previewInnerContainer.addView(backgroundView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.FILL)); + + linkView = new LinkPreview(context, AndroidUtilities.density) { + @Override + public void invalidate() { + previewInnerContainer.invalidate(); + super.invalidate(); + } + }; + previewInnerContainer.addView(linkView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER)); + + ItemOptions options = ItemOptions.makeOptions(windowView, resourcesProvider, windowView); + + captionButton = new MessagePreviewView.ToggleButton( + getContext(), + R.raw.position_below, getString(R.string.StoryLinkCaptionAbove), + R.raw.position_above, getString(R.string.StoryLinkCaptionBelow), + resourcesProvider + ); + captionButton.setOnClickListener(v -> { + link.captionAbove = !link.captionAbove; + captionButton.setState(!link.captionAbove, true); + linkView.set(currentAccount, link, true); + }); + options.addView(captionButton); + + photoButton = new MessagePreviewView.ToggleButton( + context, + R.raw.media_shrink, LocaleController.getString(R.string.LinkMediaLarger), + R.raw.media_enlarge, LocaleController.getString(R.string.LinkMediaSmaller), + resourcesProvider + ); + photoButton.setOnClickListener(v -> { + link.largePhoto = !link.largePhoto; + photoButton.setState(!link.largePhoto, true); + linkView.set(currentAccount, link, true); + }); + options.addView(photoButton); + options.addGap(); + options.add(R.drawable.msg_select, getString(R.string.ApplyChanges), this::dismiss); + options.add(R.drawable.msg_delete, LocaleController.getString(R.string.DoNotLinkPreview), true, () -> { + if (whenDone != null) { + whenDone.run(null); + whenDone = null; + } + dismiss(); + }); + containerView.addView(options.getLayout(), LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, Gravity.RIGHT | Gravity.BOTTOM)); + + if (Build.VERSION.SDK_INT >= 21) { + windowView.setFitsSystemWindows(true); + windowView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + @NonNull + @Override + public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets insets) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + Insets r = insets.getInsets(WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.systemBars()); + StoryLinkPreviewDialog.this.insets.set(r.left, r.top, r.right, r.bottom); + } else { + StoryLinkPreviewDialog.this.insets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(), insets.getStableInsetRight(), insets.getStableInsetBottom()); + } + windowView.setPadding(StoryLinkPreviewDialog.this.insets.left, StoryLinkPreviewDialog.this.insets.top, StoryLinkPreviewDialog.this.insets.right, StoryLinkPreviewDialog.this.insets.bottom); + windowView.requestLayout(); + if (Build.VERSION.SDK_INT >= 30) { + return WindowInsets.CONSUMED; + } else { + return insets.consumeSystemWindowInsets(); + } + } + }); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Window window = getWindow(); + window.setWindowAnimations(R.style.DialogNoAnimation); + setContentView(windowView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + + WindowManager.LayoutParams params = window.getAttributes(); + params.width = ViewGroup.LayoutParams.MATCH_PARENT; + params.height = ViewGroup.LayoutParams.MATCH_PARENT; + params.gravity = Gravity.FILL; + params.dimAmount = 0; + params.flags &= ~WindowManager.LayoutParams.FLAG_DIM_BEHIND; + params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; + params.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + if (Build.VERSION.SDK_INT >= 21) { + params.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | + WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | + WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | + WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION | + WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; + } + params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; + params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + if (Build.VERSION.SDK_INT >= 28) { + params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + window.setAttributes(params); + + windowView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE); + AndroidUtilities.setLightNavigationBar(windowView, !Theme.isCurrentThemeDark()); + } + + private ValueAnimator openAnimator; + private void animateOpenTo(boolean open, Runnable after) { + if (openAnimator != null) { + openAnimator.cancel(); + } + + openAnimator = ValueAnimator.ofFloat(openProgress, open ? 1 : 0); + openAnimator.addUpdateListener(anm -> { + openProgress = (float) anm.getAnimatedValue(); + containerView.setAlpha(openProgress); + containerView.setScaleX(AndroidUtilities.lerp(.9f, 1f, openProgress)); + containerView.setScaleY(AndroidUtilities.lerp(.9f, 1f, openProgress)); + windowView.invalidate(); + }); + openAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + openProgress = open ? 1 : 0; + containerView.setAlpha(openProgress); + containerView.setScaleX(AndroidUtilities.lerp(.9f, 1f, openProgress)); + containerView.setScaleY(AndroidUtilities.lerp(.9f, 1f, openProgress)); + windowView.invalidate(); + if (after != null) { + AndroidUtilities.runOnUIThread(after); + } + } + }); + openAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + openAnimator.setDuration(open ? 420 : 320); + openAnimator.start(); + } + + private void prepareBlur(View withoutView) { + if (withoutView != null) { + withoutView.setVisibility(View.INVISIBLE); + } + AndroidUtilities.makeGlobalBlurBitmap(bitmap -> { + if (withoutView != null) { + withoutView.setVisibility(View.VISIBLE); + } + blurBitmap = bitmap; + + blurBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + blurBitmapPaint.setShader(blurBitmapShader = new BitmapShader(blurBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)); + ColorMatrix colorMatrix = new ColorMatrix(); + AndroidUtilities.adjustSaturationColorMatrix(colorMatrix, Theme.isCurrentThemeDark() ? .08f : +.25f); + AndroidUtilities.adjustBrightnessColorMatrix(colorMatrix, Theme.isCurrentThemeDark() ? -.02f : -.07f); + blurBitmapPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix)); + blurMatrix = new Matrix(); + }, 14); + } + + @Override + public void show() { + if (!AndroidUtilities.isSafeToShow(getContext())) return; + super.show(); + prepareBlur(null); + animateOpenTo(true, null); + } + + public boolean isShowing() { + return !dismissing; + } + + private boolean dismissing = false; + + @Override + public void dismiss() { + if (dismissing) return; + if (whenDone != null) { + whenDone.run(link); + whenDone = null; + } + dismissing = true; + animateOpenTo(false, () -> { + AndroidUtilities.runOnUIThread(super::dismiss); + }); + windowView.invalidate(); + } + + private LinkPreview.WebPagePreview link; + private Utilities.Callback whenDone; + + public void set(LinkPreview.WebPagePreview link, Utilities.Callback whenDone) { + this.link = link; + final boolean hasPhoto = link != null && link.webpage != null && (link.webpage.photo != null || MessageObject.isVideoDocument(link.webpage.document)); + photoButton.setVisibility(hasPhoto ? View.VISIBLE : View.GONE); + linkView.set(currentAccount, link, false); + captionButton.setState(!link.captionAbove, false); + photoButton.setState(!link.largePhoto, false); + this.whenDone = whenDone; + } + + public void setStoryPreviewView(PreviewView previewView) { + backgroundView.setImageDrawable(new Drawable() { + @Override + public void draw(@NonNull Canvas canvas) { + canvas.save(); + canvas.translate(getBounds().left, getBounds().top); + previewView.draw(canvas); + canvas.restore(); + } + @Override + public void setAlpha(int alpha) {} + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) {} + @Override + public int getOpacity() { + return PixelFormat.TRANSPARENT; + } + @Override + public int getIntrinsicWidth() { + return previewView.getWidth(); + } + @Override + public int getIntrinsicHeight() { + return previewView.getHeight(); + } + }); + } + + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java index 934511cea0..d9ea26ec9d 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PasscodeView.java @@ -1184,7 +1184,7 @@ private void checkFingerprint() { if (Build.VERSION.SDK_INT < 23) { return; } - Activity parentActivity = (Activity) getContext(); + Activity parentActivity = AndroidUtilities.findActivity(getContext()); if (parentActivity != null && fingerprintView.getVisibility() == VISIBLE && !ApplicationLoader.mainInterfacePaused && (!(parentActivity instanceof LaunchActivity) || ((LaunchActivity) parentActivity).allowShowFingerprintDialog(this))) { try { if (BiometricManager.from(getContext()).canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG) == BiometricManager.BIOMETRIC_SUCCESS && FingerprintController.isKeyReady() && !FingerprintController.checkDeviceFingerprintsChanged()) { @@ -1227,7 +1227,7 @@ public void onShow(boolean fingerprint, boolean animated) { } private boolean hasFingerprint() { - Activity parentActivity = (Activity) getContext(); + Activity parentActivity = AndroidUtilities.findActivity(getContext()); if (Build.VERSION.SDK_INT >= 23 && parentActivity != null && SharedConfig.useFingerprintLock) { try { FingerprintManagerCompat fingerprintManager = FingerprintManagerCompat.from(ApplicationLoader.applicationContext); @@ -1241,7 +1241,7 @@ private boolean hasFingerprint() { private void checkFingerprintButton() { boolean hasFingerprint = false; - Activity parentActivity = (Activity) getContext(); + Activity parentActivity = AndroidUtilities.findActivity(getContext()); if (Build.VERSION.SDK_INT >= 23 && parentActivity != null && SharedConfig.useFingerprintLock) { try { FingerprintManagerCompat fingerprintManager = FingerprintManagerCompat.from(ApplicationLoader.applicationContext); @@ -1267,7 +1267,7 @@ private void checkFingerprintButton() { public void onShow(boolean fingerprint, boolean animated, int x, int y, Runnable onShow, Runnable onStart) { checkFingerprintButton(); checkRetryTextView(); - Activity parentActivity = (Activity) getContext(); + Activity parentActivity = AndroidUtilities.findActivity(getContext()); if (SharedConfig.passcodeType == SharedConfig.PASSCODE_TYPE_PASSWORD) { if (!animated && retryTextView.getVisibility() != VISIBLE && passwordEditText != null) { passwordEditText.requestFocus(); @@ -1278,7 +1278,7 @@ public void onShow(boolean fingerprint, boolean animated, int x, int y, Runnable View currentFocus = parentActivity.getCurrentFocus(); if (currentFocus != null) { currentFocus.clearFocus(); - AndroidUtilities.hideKeyboard(((Activity) getContext()).getCurrentFocus()); + AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus()); } } } 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 54730afacb..cab9da968e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/PollVotesAlert.java @@ -514,7 +514,7 @@ public PollVotesAlert(ChatActivity parentFragment, MessageObject message) { TLRPC.TL_messageMediaPoll mediaPoll = (TLRPC.TL_messageMediaPoll) messageObject.messageOwner.media; poll = mediaPoll.poll; Context context = parentFragment.getParentActivity(); - peer = parentFragment.getMessagesController().getInputPeer((int) message.getDialogId()); + peer = parentFragment.getMessagesController().getInputPeer(message.getDialogId()); ArrayList loadedVoters = new ArrayList<>(); int count = mediaPoll.results.results.size(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayMessageCell.java index 996223b3dd..1e39f7ec65 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayMessageCell.java @@ -458,6 +458,7 @@ public void draw(Canvas canvas, int marginTop, int marginLeft, Theme.ResourcesPr if (selectorDrawable == null) { selectorDrawable = Theme.createRadSelectorDrawable(selectorColor = Theme.getColor(Theme.key_listSelector), 12, 12); + selectorDrawable.setCallback(parentView); } textPaint.setColor(Theme.chat_msgTextPaint.getColor()); @@ -604,7 +605,8 @@ public void draw(Canvas canvas, int marginTop, int marginLeft, Theme.ResourcesPr Theme.setSelectorDrawableColor(selectorDrawable, selectorColor = rippleColor, true); } selectorDrawable.setBounds(clickRect[pressedPos]); - selectorDrawable.draw(canvas); + selectorDrawable.setCallback(parentView); +// selectorDrawable.draw(canvas); } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayResultsMessageCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayResultsMessageCell.java index 376ae6a908..cecce71858 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayResultsMessageCell.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Premium/boosts/cells/msg/GiveawayResultsMessageCell.java @@ -431,6 +431,7 @@ public void draw(Canvas canvas, int marginTop, int marginLeft, Theme.ResourcesPr if (selectorDrawable == null) { selectorDrawable = Theme.createRadSelectorDrawable(selectorColor = Theme.getColor(Theme.key_listSelector), 12, 12); + selectorDrawable.setCallback(parentView); } textPaint.setColor(Theme.chat_msgTextPaint.getColor()); @@ -563,7 +564,8 @@ public void draw(Canvas canvas, int marginTop, int marginLeft, Theme.ResourcesPr Theme.setSelectorDrawableColor(selectorDrawable, selectorColor = rippleColor, true); } selectorDrawable.setBounds(clickRect[pressedPos]); - selectorDrawable.draw(canvas); + selectorDrawable.setCallback(parentView); +// selectorDrawable.draw(canvas); } if (links != null && links.draw(canvas)) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/QuoteSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/QuoteSpan.java index a5419e8f2c..1849ab5e74 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/QuoteSpan.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/QuoteSpan.java @@ -244,7 +244,7 @@ public static ArrayList updateQuoteBlocks(View view, Layout layout, Array if (spannable instanceof SpannableStringBuilder) { SpannableStringBuilder ssb = (SpannableStringBuilder) spannable; - final boolean hasPad = ssb.charAt(block.span.end - 1) == '\n'; + final boolean hasPad = block.span.end - 1 >= 0 && ssb.charAt(block.span.end - 1) == '\n'; final boolean needsPad = block.hasButton() && block.span.end - 2 >= 0 && layout.getLineRight(layout.getLineForOffset(block.span.end - 1)) - dp(12) > block.width - block.buttonWidth(); if (hasPad != needsPad) { int newEnd = block.span.end; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java index 5513fd7e67..2b032635ea 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ReactedUsersListView.java @@ -66,12 +66,15 @@ public class ReactedUsersListView extends FrameLayout { MessageContainsEmojiButton messageContainsEmojiButton; Theme.ResourcesProvider resourcesProvider; - public ReactedUsersListView(Context context, Theme.ResourcesProvider resourcesProvider, int currentAccount, MessageObject message, TLRPC.ReactionCount reactionCount, boolean addPadding) { + private boolean showReactionPreview; + + public ReactedUsersListView(Context context, Theme.ResourcesProvider resourcesProvider, int currentAccount, MessageObject message, TLRPC.ReactionCount reactionCount, boolean addPadding, boolean showReactionPreview) { super(context); this.currentAccount = currentAccount; this.message = message; this.filter = reactionCount == null ? null : reactionCount.reaction; this.resourcesProvider = resourcesProvider; + this.showReactionPreview = showReactionPreview; predictiveCount = reactionCount == null ? VISIBLE_ITEMS : reactionCount.count; listView = new RecyclerListView(context, resourcesProvider) { @Override @@ -100,7 +103,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int View view = null; switch (viewType) { case USER_VIEW_TYPE: - view = new ReactedUserHolderView(ReactedUserHolderView.STYLE_DEFAULT, currentAccount, context, null); + view = new ReactedUserHolderView(ReactedUserHolderView.STYLE_DEFAULT, currentAccount, context, resourcesProvider, true, showReactionPreview); break; default: case CUSTOM_EMOJI_VIEW_TYPE: diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionImageHolder.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionImageHolder.java index 3699262f27..a57f6cc2d9 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionImageHolder.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Reactions/ReactionImageHolder.java @@ -90,7 +90,7 @@ public static void preload(int currentAccount, ReactionsLayoutInBubble.VisibleRe FileLoader.getInstance(currentAccount).loadFile(defaultReaction.select_animation, reaction, FileLoader.PRIORITY_LOW, 0); } } else { - new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES_LARGE, currentAccount, reaction.documentId); + new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_MESSAGES_LARGE, currentAccount, reaction.documentId).preload(); } } 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 33fdf2b9ed..dd3ca0ad2a 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 @@ -17,6 +17,7 @@ import android.text.Spanned; import android.text.TextPaint; import android.text.TextUtils; +import android.util.Log; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -39,8 +40,10 @@ import org.telegram.messenger.SvgHelper; import org.telegram.messenger.UserConfig; import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AdjustPanLayoutHelper; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.Components.AnimatedEmojiDrawable; @@ -106,6 +109,7 @@ public static void initPaints(Theme.ResourcesProvider resourcesProvider) { MessageObject messageObject; Theme.ResourcesProvider resourcesProvider; private Integer scrimViewReaction; + private float scrimProgress; int availableWidth; private int lastDrawnWidth; @@ -340,6 +344,8 @@ public void measure(int availableWidth, int gravity) { drawServiceShaderBackground = 0f; } + private final RectF scrimRect = new RectF(); + public void draw(Canvas canvas, float animationProgress, Integer drawOnlyReaction) { if (isEmpty && outButtons.isEmpty()) { return; @@ -355,7 +361,13 @@ public void draw(Canvas canvas, float animationProgress, Integer drawOnlyReactio } for (int i = 0; i < reactionButtons.size(); i++) { ReactionButton reactionButton = reactionButtons.get(i); - if ((Objects.equals(reactionButton.reaction.hashCode(), scrimViewReaction)) || (drawOnlyReaction != null && reactionButton.reaction.hashCode() != drawOnlyReaction)) { + if (scrimViewReaction == null && drawOnlyReaction == null && scrimProgress < .5f) { + reactionButton.detachPreview(); + } + if (Objects.equals(reactionButton.reaction.hashCode(), scrimViewReaction)) { + continue; + } + if (drawOnlyReaction != null && reactionButton.reaction.hashCode() != drawOnlyReaction) { continue; } canvas.save(); @@ -385,6 +397,40 @@ public void draw(Canvas canvas, float animationProgress, Integer drawOnlyReactio } } + public void drawPreview(View view, Canvas canvas, int offset, Integer drawOnlyReaction) { + if (isEmpty && outButtons.isEmpty()) { + return; + } + for (int i = 0; i < reactionButtons.size(); i++) { + ReactionButton reactionButton = reactionButtons.get(i); + if (Objects.equals(reactionButton.reaction.hashCode(), scrimViewReaction)) { + continue; + } + if (drawOnlyReaction != null && reactionButton.reaction.hashCode() != drawOnlyReaction) { + continue; + } + if (drawOnlyReaction != null) { + AndroidUtilities.rectTmp.set(reactionButton.drawingImageRect); + final float scrimSize = dp(140), p = dp(14); + final int parentWidth = parentView != null ? parentView.getParentWidth() : AndroidUtilities.displaySize.x; + final float left = Utilities.clamp(AndroidUtilities.rectTmp.left - dp(12), parentWidth - scrimSize - dp(24), dp(24)); + scrimRect.set(left, AndroidUtilities.rectTmp.top - p - scrimSize + offset, left + scrimSize, AndroidUtilities.rectTmp.top - p + offset); + + final float progress = CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(scrimProgress); + AndroidUtilities.lerp(AndroidUtilities.rectTmp, scrimRect, progress, scrimRect); + + reactionButton.attachPreview(view); + AndroidUtilities.rectTmp2.set((int) scrimRect.left, (int) scrimRect.top, (int) scrimRect.right, (int) scrimRect.bottom); + if (1f - progress > 0) { + canvas.saveLayerAlpha(scrimRect, (int) (0xFF * (1f - progress)), Canvas.ALL_SAVE_FLAG); + reactionButton.drawImage(canvas, AndroidUtilities.rectTmp2, 1f); + canvas.restore(); + } + reactionButton.drawPreview(view, canvas, scrimRect, progress); + } + } + } + public void recordDrawingState() { lastDrawingReactionButtons.clear(); for (int i = 0; i < reactionButtons.size(); i++) { @@ -523,6 +569,10 @@ public void setScrimReaction(Integer scrimViewReaction) { this.scrimViewReaction = scrimViewReaction; } + public void setScrimProgress(float scrimProgress) { + this.scrimProgress = scrimProgress; + } + public class ReactionLayoutButton extends ReactionButton { public ReactionLayoutButton(ReactionButton reuseFrom, TLRPC.ReactionCount reactionCount, boolean isSmall, boolean isTag) { super(reuseFrom, currentAccount, parentView, reactionCount, isSmall, isTag, resourcesProvider); @@ -743,7 +793,7 @@ public void draw(Canvas canvas, float x, float y, float progress, float alpha, b drawingImageRect.set((int) x, (int) y, dp(14), dp(14)); imageReceiver.setImageCoords(drawingImageRect); imageReceiver.setRoundRadius(0); - drawImage(canvas, alpha); + drawImage(canvas, drawingImageRect, alpha); return; } @@ -838,8 +888,7 @@ public void draw(Canvas canvas, float x, float y, float progress, float alpha, b X -= dp(2); } drawingImageRect.set((int) x + X, (int) y + Y, (int) x + X + size, (int) y + Y + size); - imageReceiver.setImageCoords(drawingImageRect); - drawImage(canvas, alpha); + drawImage(canvas, drawingImageRect, alpha); } float tx = 0; @@ -901,8 +950,11 @@ protected boolean drawTagDot() { return true; } - private void drawImage(Canvas canvas, float alpha) { + private void drawImage(Canvas canvas, Rect bounds, float alpha) { ImageReceiver imageReceiver = animatedEmojiDrawable != null ? animatedEmojiDrawable.getImageReceiver() : this.imageReceiver; + if (bounds != null) { + imageReceiver.setImageCoords(bounds); + } if (animatedEmojiDrawable != null && animatedEmojiDrawableColor != lastDrawnTextColor) { animatedEmojiDrawable.setColorFilter(new PorterDuffColorFilter(animatedEmojiDrawableColor = lastDrawnTextColor, PorterDuff.Mode.SRC_IN)); } @@ -997,6 +1049,7 @@ public void detach() { if (animatedEmojiDrawable != null) { animatedEmojiDrawable.removeView(parentView); } + detachPreview(); } public void startAnimation() { @@ -1018,6 +1071,64 @@ public void startAnimation() { } } } + + + public ImageReceiver previewImageReceiver; + public AnimatedEmojiDrawable previewAnimatedEmojiDrawable; + + public void attachPreview(View view) { + if (previewImageReceiver != null || previewAnimatedEmojiDrawable != null) return; + View parent = parentView != null && parentView.getParent() instanceof View ? (View) parentView.getParent() : parentView; + if (reaction != null) { + if (visibleReaction.emojicon != null) { + TLRPC.TL_availableReaction r = MediaDataController.getInstance(currentAccount).getReactionsMap().get(visibleReaction.emojicon); + if (r != null && r.activate_animation != null) { + //imageReceiver.setImage(ImageLocation.getForDocument(r.static_icon), "40_40", svgThumb, "webp", r, 1); + SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(r.static_icon, Theme.key_windowBackgroundGray, 1.0f); + previewImageReceiver = new ImageReceiver(parent); + previewImageReceiver.setLayerNum(7); + previewImageReceiver.onAttachedToWindow(); + previewImageReceiver.setRoundRadius(dp(14)); + previewImageReceiver.setAllowStartLottieAnimation(true); + previewImageReceiver.setAllowStartAnimation(true); + previewImageReceiver.setAutoRepeat(1); + previewImageReceiver.setAllowDecodeSingleFrame(true); + previewImageReceiver.setImage(ImageLocation.getForDocument(r.activate_animation), "140_140", svgThumb, null, r, 1); + } + } else if (visibleReaction.documentId != 0) { + previewAnimatedEmojiDrawable = new AnimatedEmojiDrawable(AnimatedEmojiDrawable.CACHE_TYPE_ALERT_PREVIEW_LARGE_140, currentAccount, visibleReaction.documentId); + previewAnimatedEmojiDrawable.addView(parent); + } + } + } + + public void drawPreview(View view, Canvas canvas, RectF rect, float alpha) { + if (alpha <= 0) return; + if (previewImageReceiver != null) { + previewImageReceiver.setImageCoords(rect); + previewImageReceiver.setAlpha(alpha); + previewImageReceiver.draw(canvas); + } else if (previewAnimatedEmojiDrawable != null) { + previewAnimatedEmojiDrawable.setBounds((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom); + previewAnimatedEmojiDrawable.setAlpha((int) (0xFF * alpha)); + previewAnimatedEmojiDrawable.draw(canvas); + } + if (view != null) { + view.invalidate(); + } + } + + public void detachPreview() { + if (previewImageReceiver == null && previewAnimatedEmojiDrawable == null) return; + if (previewImageReceiver != null) { + previewImageReceiver.onDetachedFromWindow(); + previewImageReceiver = null; + } else if (previewAnimatedEmojiDrawable != null) { + View parent = parentView != null && parentView.getParent() instanceof View ? (View) parentView.getParent() : parentView; + previewAnimatedEmojiDrawable.removeView(parent); + previewAnimatedEmojiDrawable = null; + } + } } float lastX; @@ -1045,12 +1156,10 @@ public boolean chekTouchEvent(MotionEvent event) { } final ReactionButton selectedButtonFinal = lastSelectedButton; - if (messageObject.messageOwner.reactions.can_see_list || messageObject.getDialogId() >= 0) { - AndroidUtilities.runOnUIThread(longPressRunnable = () -> { - parentView.getDelegate().didPressReaction(parentView, selectedButtonFinal.reactionCount, true); - longPressRunnable = null; - }, ViewConfiguration.getLongPressTimeout()); - } + AndroidUtilities.runOnUIThread(longPressRunnable = () -> { + parentView.getDelegate().didPressReaction(parentView, selectedButtonFinal.reactionCount, true); + longPressRunnable = null; + }, ViewConfiguration.getLongPressTimeout()); pressed = true; break; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrimOptions.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrimOptions.java index 42ac848906..f9ea7ab055 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrimOptions.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ScrimOptions.java @@ -17,18 +17,16 @@ import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PathMeasure; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; -import android.graphics.Region; import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.text.Layout; import android.text.Spanned; import android.text.StaticLayout; import android.text.TextPaint; @@ -48,14 +46,11 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.MessageObject; -import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; -import org.telegram.tgnet.TLRPC; -import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.ChatMessageCell; -import org.telegram.ui.MessageSendPreview; +import org.telegram.ui.ChatActivity; import java.util.ArrayList; @@ -81,7 +76,9 @@ public class ScrimOptions extends Dialog { private ChatMessageCell scrimCell; private boolean isGroup; private Drawable scrimDrawable; - private float scrimDrawableTx, scrimDrawableTy; + private float scrimDrawableTx1, scrimDrawableTy1; + private float scrimDrawableTx2, scrimDrawableTy2; + private float scrimDrawableSw = 1f, scrimDrawableSh = 1f; public ScrimOptions(@NonNull Context context, Theme.ResourcesProvider resourcesProvider) { super(context, R.style.TransparentDialog); @@ -105,7 +102,13 @@ protected void dispatchDraw(Canvas canvas) { if (scrimDrawable != null) { scrimDrawable.setAlpha((int) (0xFF * openProgress)); canvas.save(); - canvas.translate(scrimDrawableTx * openProgress, scrimDrawableTy * openProgress); + canvas.translate(scrimDrawableTx2 + scrimDrawableTx1 * openProgress, scrimDrawableTy2 + scrimDrawableTy1 * openProgress); + final float scale = AndroidUtilities.lerp(AndroidUtilities.lerp(Math.min(scrimDrawableSw, scrimDrawableSh), Math.max(scrimDrawableSw, scrimDrawableSh), .75f), 1f, openProgress); + canvas.scale( + scale, scale, + -scrimDrawableTx2 + scrimDrawable.getBounds().left + scrimDrawable.getBounds().width() / 2f * scrimDrawableSw, + -scrimDrawableTy2 + scrimDrawable.getBounds().top + scrimDrawable.getBounds().height() / 2f * scrimDrawableSh + ); scrimDrawable.draw(canvas); canvas.restore(); } @@ -142,7 +145,7 @@ public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets i Insets r = insets.getInsets(WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.systemBars()); ScrimOptions.this.insets.set(r.left, r.top, r.right, r.bottom); } else { - ScrimOptions.this.insets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(), insets.getStableInsetRight(), insets.getStableInsetBottom()); + ScrimOptions.this.insets.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()); } containerView.setPadding(ScrimOptions.this.insets.left, ScrimOptions.this.insets.top, ScrimOptions.this.insets.right, ScrimOptions.this.insets.bottom); windowView.requestLayout(); @@ -325,26 +328,31 @@ public void layout() { } if (optionsContainer != null) { + final float boundsLeft = bounds.left + scrimDrawableTx2; + final float boundsRight = bounds.right + scrimDrawableTx2; + final float boundsTop = bounds.top + scrimDrawableTy2; + final float boundsBottom = bounds.bottom + scrimDrawableTy2; + boolean right = false; boolean bottom = false; - if (bounds.right - optionsContainer.getMeasuredWidth() < dp(8)) { + if (boundsRight - optionsContainer.getMeasuredWidth() < dp(8)) { optionsView.setPivotX(dp(6)); - optionsContainer.setX(Math.min(containerView.getWidth() - optionsContainer.getWidth(), bounds.left - dp(10)) - containerView.getX()); + optionsContainer.setX(Math.min(containerView.getWidth() - optionsContainer.getWidth(), boundsLeft - dp(10)) - containerView.getX()); } else { right = true; optionsView.setPivotX(optionsView.getMeasuredWidth() - dp(6)); - optionsContainer.setX(Math.max(dp(8), bounds.right + dp(4) - optionsContainer.getMeasuredWidth()) - containerView.getX()); + optionsContainer.setX(Math.max(dp(8), boundsRight + dp(4) - optionsContainer.getMeasuredWidth()) - containerView.getX()); } - scrimDrawableTx = right ? optionsContainer.getX() + optionsContainer.getWidth() - dp(6) - bounds.right : optionsContainer.getX() + dp(10) - bounds.left; - scrimDrawableTy = 0f; + scrimDrawableTx1 = right ? optionsContainer.getX() + optionsContainer.getWidth() - dp(6) - boundsRight : optionsContainer.getX() + dp(10) - boundsLeft; + scrimDrawableTy1 = 0f; - if (bounds.bottom + optionsContainer.getMeasuredHeight() > windowView.getMeasuredHeight() - dp(16)) { + if (boundsBottom + optionsContainer.getMeasuredHeight() > windowView.getMeasuredHeight() - dp(16)) { bottom = true; optionsView.setPivotY(optionsView.getMeasuredHeight() - dp(6)); - optionsContainer.setY(bounds.top - dp(4) - optionsContainer.getMeasuredHeight() - containerView.getY()); + optionsContainer.setY(boundsTop - dp(4) - optionsContainer.getMeasuredHeight() - containerView.getY()); } else { optionsView.setPivotY(dp(6)); - optionsContainer.setY(Math.min(windowView.getHeight() - optionsContainer.getMeasuredHeight() - dp(16), bounds.bottom) - containerView.getY()); + optionsContainer.setY(Math.min(windowView.getHeight() - optionsContainer.getMeasuredHeight() - dp(16), boundsBottom) - containerView.getY()); } options.setSwipebackGravity(right, bottom); } @@ -354,7 +362,7 @@ public void setScrim(ChatMessageCell cell) { } - public void setScrim(ChatMessageCell cell, CharacterStyle link) { + public void setScrim(ChatMessageCell cell, CharacterStyle link, CharSequence replaceText) { if (cell == null) return; scrimCell = cell; @@ -411,11 +419,37 @@ public void setScrim(ChatMessageCell cell, CharacterStyle link) { if (layout == null) return; + RectF realPathBounds = null; + if (replaceText != null) { + int line = layout.getLineForOffset(start); + y += layout.getLineTop(line); + float xoffset = layout.getPrimaryHorizontal(start); + float xwidth = layout.getLineWidth(line); + + final LinkPath path = new LinkPath(true); + path.setCurrentLayout(layout, start, 0); + layout.getSelectionPath(start, end, path); + realPathBounds = new RectF(); + path.computeBounds(realPathBounds, true); + + layout = MessageObject.makeStaticLayout(replaceText, layout.getPaint(), layout.getWidth(), 1f, 0f, true); + start = 0; + end = replaceText.length(); + float l = layout.getWidth(), r = 0; + for (int i = 0; i < layout.getLineCount(); ++i) { + l = Math.min(l, layout.getLineLeft(i)); + r = Math.max(r, layout.getLineRight(i)); + } + + x += Math.min(xoffset, xwidth - Math.max(0, r - l)); + } + final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); backgroundPaint.setColor(Theme.getColor(messageObject.isOutOwner() ? Theme.key_chat_outBubble : Theme.key_chat_inBubble, resourcesProvider)); backgroundPaint.setPathEffect(new CornerPathEffect(dp(5))); final LinkPath path = new LinkPath(true); + path.setUseCornerPathImplementation(true); path.setCurrentLayout(layout, start, 0); layout.getSelectionPath(start, end, path); path.closeRects(); @@ -449,7 +483,18 @@ public void setScrim(ChatMessageCell cell, CharacterStyle link) { paint.setTextSize(layout.getPaint().getTextSize()); paint.setTextAlign(layout.getPaint().getTextAlign()); paint.setTypeface(layout.getPaint().getTypeface()); - final StaticLayout finalLayout = new StaticLayout(layout.getText(), paint, layout.getWidth(), layout.getAlignment(), 1f, 0f, false); + final StaticLayout finalLayout; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + StaticLayout.Builder builder = + StaticLayout.Builder.obtain(layout.getText(), 0, layout.getText().length(), paint, layout.getWidth()) + .setLineSpacing(0f, 1f) + .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY) + .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE) + .setAlignment(Layout.Alignment.ALIGN_NORMAL); + finalLayout = builder.build(); + } else { + finalLayout = new StaticLayout(layout.getText(), paint, layout.getWidth(), Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + } final int finalBlockNum = blockNum; final int[] pos = new int[2]; cell.getLocationOnScreen(pos); @@ -470,11 +515,18 @@ public void draw(@NonNull Canvas canvas) { canvas.translate(pos2[0], pos2[1]); if (cell != null && cell.drawBackgroundInParent()) { - canvas.translate(-pos2[0], -pos2[1]); - canvas.translate(pos[0], pos[1]); - cell.drawBackgroundInternal(canvas, true); - canvas.translate(-pos[0], -pos[1]); - canvas.translate(pos2[0], pos2[1]); + if (cell.currentBackgroundDrawable != null && cell.currentBackgroundDrawable.getPaint() != null) { + canvas.save(); + canvas.translate(0, -cell.currentBackgroundDrawable.getTopY()); + canvas.drawPaint(cell.currentBackgroundDrawable.getPaint()); + canvas.restore(); + } else { + canvas.translate(-pos2[0], -pos2[1]); + canvas.translate(pos[0], pos[1]); + cell.drawBackgroundInternal(canvas, true); + canvas.translate(-pos[0], -pos[1]); + canvas.translate(pos2[0], pos2[1]); + } if (finalBitmap != null) { canvas.save(); canvas.drawBitmap(finalBitmap, pathBounds.left, pathBounds.top, bitmapPaint); @@ -486,7 +538,6 @@ public void draw(@NonNull Canvas canvas) { canvas.clipPath(path); canvas.save(); - canvas.translate(0, dp(1.33f)); finalLayout.draw(canvas); // if (cell != null && cell.linkBlockNum == finalBlockNum) { // cell.links.draw(canvas); @@ -512,6 +563,19 @@ public int getOpacity() { int left = (int) (pos[0] + x + pathBounds.left + path.getRadius() / 2f); int top = (int) (pos[1] + y + pathBounds.top); scrimDrawable.setBounds(left, top, left + (int) pathBounds.width(), top + (int) pathBounds.height()); + + if (replaceText != null) { + if (left + pathBounds.width() > AndroidUtilities.displaySize.x - dp(8)) { + scrimDrawableTx2 -= (left + pathBounds.width()) - (AndroidUtilities.displaySize.x - dp(8)); + } + if (top + pathBounds.height() > AndroidUtilities.displaySize.y - AndroidUtilities.navigationBarHeight - dp(8)) { + scrimDrawableTy2 -= (top + pathBounds.height()) - (AndroidUtilities.displaySize.y - AndroidUtilities.navigationBarHeight - dp(8)); + } + if (realPathBounds != null) { + scrimDrawableSw = realPathBounds.width() / pathBounds.width(); + scrimDrawableSh = realPathBounds.height() / pathBounds.height(); + } + } } } 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 a4f7f1d08b..edad4f68c7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/SharedMediaLayout.java @@ -503,9 +503,9 @@ public float getPhotoVideoOptionsAlpha(float progress) { return 0; } float alpha = 0; - if (mediaPages[1] != null && (mediaPages[1].selectedType == TAB_PHOTOVIDEO || mediaPages[1].selectedType == TAB_STORIES || mediaPages[1].selectedType == TAB_ARCHIVED_STORIES || mediaPages[1].selectedType == TAB_SAVED_DIALOGS)) + if (mediaPages[1] != null && (mediaPages[1].selectedType == TAB_PHOTOVIDEO || mediaPages[1].selectedType == TAB_STORIES && TextUtils.isEmpty(getStoriesHashtag()) || mediaPages[1].selectedType == TAB_ARCHIVED_STORIES || mediaPages[1].selectedType == TAB_SAVED_DIALOGS)) alpha += progress; - if (mediaPages[0] != null && (mediaPages[0].selectedType == TAB_PHOTOVIDEO || mediaPages[0].selectedType == TAB_STORIES || mediaPages[0].selectedType == TAB_ARCHIVED_STORIES || mediaPages[0].selectedType == TAB_SAVED_DIALOGS)) + if (mediaPages[0] != null && (mediaPages[0].selectedType == TAB_PHOTOVIDEO || mediaPages[0].selectedType == TAB_STORIES && TextUtils.isEmpty(getStoriesHashtag()) || mediaPages[0].selectedType == TAB_ARCHIVED_STORIES || mediaPages[0].selectedType == TAB_SAVED_DIALOGS)) alpha += 1f - progress; return alpha; } @@ -1432,6 +1432,10 @@ public boolean hasInternet() { return profileActivity.getConnectionsManager().getConnectionState() == ConnectionsManager.ConnectionStateConnected; } + public int overrideColumnsCount() { + return -1; + } + public SharedMediaLayout(Context context, long did, SharedMediaPreloader preloader, int commonGroupsCount, ArrayList sortedUsers, TLRPC.ChatFull chatInfo, TLRPC.UserFull userInfo, int initialTab, BaseFragment parent, Delegate delegate, int viewType, Theme.ResourcesProvider resourcesProvider) { super(context); this.viewType = viewType; @@ -1480,8 +1484,8 @@ public SharedMediaLayout(Context context, long did, SharedMediaPreloader preload profileActivity = parent; actionBar = profileActivity.getActionBar(); - mediaColumnsCount[0] = SharedConfig.mediaColumnsCount; - mediaColumnsCount[1] = SharedConfig.storiesColumnsCount; + mediaColumnsCount[0] = overrideColumnsCount() <= 0 ? SharedConfig.mediaColumnsCount : overrideColumnsCount(); + mediaColumnsCount[1] = overrideColumnsCount() <= 0 ? SharedConfig.storiesColumnsCount : overrideColumnsCount(); profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.mediaDidLoad); profileActivity.getNotificationCenter().addObserver(this, NotificationCenter.messagesDeleted); @@ -1679,11 +1683,7 @@ public void onLayout(int l, int t, int r, int b) { photoVideoOptionsItem.setTranslationY(dp(10)); photoVideoOptionsItem.setVisibility(View.INVISIBLE); -// Drawable calendarDrawable = ContextCompat.getDrawable(context, R.drawable.ic_ab_other).mutate(); -// calendarDrawable.setColorFilter(new PorterDuffColorFilter(getThemedColor(Theme.key_actionBarActionModeDefaultIcon), PorterDuff.Mode.MULTIPLY)); -// photoVideoOptionsItem.setImageDrawable(calendarDrawable); -// photoVideoOptionsItem.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - if (!isArchivedOnlyStoriesView()) { + if (!isArchivedOnlyStoriesView() && !isSearchingStories()) { actionBar.addView(photoVideoOptionsItem, LayoutHelper.createFrame(48, 56, Gravity.RIGHT | Gravity.BOTTOM)); optionsSearchImageView = new RLottieImageView(context); @@ -2168,7 +2168,7 @@ public void setTranslationX(float translationX) { invalidateBlur(); } }; - addView(mediaPage, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, customTabs() ? 0 : 48, 0, 0)); + addView(mediaPage, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, mediaPageTopMargin(), 0, 0)); if (a == 1) { mediaPage.setTranslationX(AndroidUtilities.displaySize.x); } @@ -3223,6 +3223,10 @@ public void setStoriesFilter(boolean photos, boolean videos) { } } + public int mediaPageTopMargin() { + return customTabs() ? 0 : 48; + } + protected void invalidateBlur() { } @@ -4094,6 +4098,9 @@ public boolean isSearchItemVisible(int type) { if (type == TAB_GROUPUSERS) { return delegate.canSearchMembers(); } + if (isSearchingStories()) { + return false; + } return ( type != TAB_PHOTOVIDEO && type != TAB_STORIES && @@ -4209,7 +4216,7 @@ public void setNewMediaCounts(int[] mediaCounts) { } private void loadFastScrollData(boolean force) { - if (topicId != 0) { + if (topicId != 0 || isSearchingStories()) { return; } for (int k = 0; k < supportedFastScrollTypes.length; k++) { @@ -5791,6 +5798,9 @@ private void updateTabs(boolean animated) { 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++; } + if (isSearchingStories() != scrollSlidingTextTabStrip.hasTab(TAB_STORIES)) { + changed++; + } if (!isStoriesView()) { if ((chatUsersAdapter.chatInfo == null) == scrollSlidingTextTabStrip.hasTab(TAB_GROUPUSERS)) { changed++; @@ -5873,6 +5883,12 @@ public Animator onDisappear(ViewGroup sceneRoot, View view, TransitionValues sta if (changed > 3) { idToView = null; } + if (isSearchingStories()) { + if (!scrollSlidingTextTabStrip.hasTab(TAB_STORIES)) { + scrollSlidingTextTabStrip.addTextTab(TAB_STORIES, getString(R.string.ProfileStories), idToView); + } + scrollSlidingTextTabStrip.animationDuration = 420; + } 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()) { if (isArchivedOnlyStoriesView()) { if (!scrollSlidingTextTabStrip.hasTab(TAB_ARCHIVED_STORIES)) { @@ -6297,10 +6313,10 @@ private void switchToCurrentSelectedMode(boolean animated) { } else { mediaPages[a].emptyView.stickerView.setVisibility(View.VISIBLE); mediaPages[a].emptyView.setStickerType(StickerEmptyView.STICKER_TYPE_ALBUM); - mediaPages[a].emptyView.button.setVisibility(View.VISIBLE); + mediaPages[a].emptyView.button.setVisibility(!isSearchingStories() ? View.VISIBLE : View.GONE); mediaPages[a].emptyView.button.setText(addPostText(), false); } - mediaPages[a].emptyView.title.setText(isStoriesView() ? getString(R.string.NoPublicStoriesTitle2) : getString(R.string.NoStoriesTitle)); + mediaPages[a].emptyView.title.setText(!isSearchingStories() ? isStoriesView() ? getString(R.string.NoPublicStoriesTitle2) : getString(R.string.NoStoriesTitle) : getString(R.string.NoHashtagStoriesTitle)); mediaPages[a].emptyView.subtitle.setText(isStoriesView() ? getString(R.string.NoStoriesSubtitle2) : ""); mediaPages[a].emptyView.button.setOnClickListener(v -> { profileActivity.getMessagesController().getMainSettings().edit().putBoolean("story_keep", true).apply(); @@ -7352,6 +7368,9 @@ public float getScrollProgress(RecyclerListView listView) { } public boolean fastScrollIsVisible(RecyclerListView listView) { + if (isSearchingStories()) { + return false; + } int parentCount = this == photoVideoAdapter || this == storiesAdapter || this == archivedStoriesAdapter ? mediaColumnsCount[0] : animateToColumnsCount; int cellCount = (int) Math.ceil(getTotalItemsCount() / (float) parentCount); if (listView.getChildCount() == 0) { @@ -8652,7 +8671,9 @@ public int getStoriesCount(int tab) { return 0; } - private class StoriesAdapter extends SharedPhotoVideoAdapter { + private StoriesController.StoriesList searchStoriesList; + + public class StoriesAdapter extends SharedPhotoVideoAdapter { private final boolean isArchive; @Nullable @@ -8665,7 +8686,17 @@ private class StoriesAdapter extends SharedPhotoVideoAdapter { public StoriesAdapter(Context context, boolean isArchive) { super(context); this.isArchive = isArchive; - if (isArchive && !isStoriesView() || !isArchive && isArchivedOnlyStoriesView()) { + if (!TextUtils.isEmpty(getStoriesHashtag())) { + if (searchStoriesList == null) { + searchStoriesList = new StoriesController.SearchStoriesList(profileActivity.getCurrentAccount(), getStoriesHashtag()); + } + storiesList = searchStoriesList; + } else if (getStoriesArea() != null) { + if (searchStoriesList == null) { + searchStoriesList = new StoriesController.SearchStoriesList(profileActivity.getCurrentAccount(), getStoriesArea()); + } + storiesList = searchStoriesList; + } else if (isArchive && !isStoriesView() || !isArchive && isArchivedOnlyStoriesView()) { storiesList = null; } else { storiesList = profileActivity.getMessagesController().getStoriesController().getStoriesList(dialog_id, isArchive ? StoriesController.StoriesList.TYPE_ARCHIVE : StoriesController.StoriesList.TYPE_PINNED); @@ -8824,6 +8855,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } MessageObject messageObject = storiesList.messageObjects.get(position); cell.isStoryPinned = messageObject != null && storiesList.isPinned(messageObject.getId()); + cell.isSearchingHashtag = isSearchingStories(); cell.setMessageObject(messageObject, columnsCount()); if (isActionModeShowed && messageObject != null) { cell.setChecked(selectedFiles[messageObject.getDialogId() == dialog_id ? 0 : 1].indexOfKey(messageObject.getId()) >= 0, false); @@ -9678,4 +9710,16 @@ public boolean canEditStories() { public void openStoryRecorder() { StoryRecorder.getInstance(profileActivity.getParentActivity(), profileActivity.getCurrentAccount()).open(null); } + + public String getStoriesHashtag() { + return null; + } + + public TL_stories.MediaArea getStoriesArea() { + return null; + } + + public boolean isSearchingStories() { + return !TextUtils.isEmpty(getStoriesHashtag()) || getStoriesArea() != null; + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/Text.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/Text.java index 819cabdfc0..bec5cae449 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/Text.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/Text.java @@ -26,6 +26,7 @@ public class Text { private final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); private StaticLayout layout; private float width, left; + private float maxWidth = 999999; public Text(CharSequence text, float textSizeDp) { this(text, textSizeDp, null); @@ -37,10 +38,29 @@ public Text(CharSequence text, float textSizeDp, Typeface typeface) { setText(text); } + public Text setTextSizePx(float px) { + paint.setTextSize(px); + return this; + } + public void setText(CharSequence text) { - layout = new StaticLayout(AndroidUtilities.replaceNewLines(text), paint, 99999, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); - width = layout.getLineCount() > 0 ? layout.getLineWidth(0) : 0; - left = layout.getLineCount() > 0 ? layout.getLineLeft(0) : 0; + layout = new StaticLayout(AndroidUtilities.replaceNewLines(text), paint, (int) Math.max(maxWidth, 1), Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false); + width = 0; + left = 0; + for (int i = 0; i < layout.getLineCount(); ++i) { + width = Math.max(width, layout.getLineWidth(i)); + left = Math.max(left, layout.getLineLeft(i)); + } + } + + public Text setMaxWidth(float maxWidth) { + this.maxWidth = maxWidth; + setText(layout.getText()); + return this; + } + + public int getLineCount() { + return layout.getLineCount(); } private boolean hackClipBounds; @@ -117,13 +137,25 @@ public void draw(Canvas canvas, float x, float cy) { private LinearGradient ellipsizeGradient; private Matrix ellipsizeMatrix; private Paint ellipsizePaint; + private int vertPad; + + public Text setVerticalClipPadding(int pad) { + vertPad = pad; + return this; + } + + public Text setShadow(float shadowAlpha) { + paint.setShadowLayer(dp(1), 0, dp(.66f), Theme.multAlpha(0xFF000000, shadowAlpha)); + return this; + } + public void draw(Canvas canvas) { if (layout == null) { return; } if (!doNotSave && ellipsizeWidth >= 0 && width > ellipsizeWidth) { - canvas.saveLayerAlpha(0, 0, ellipsizeWidth, layout.getHeight(), 0xFF, Canvas.ALL_SAVE_FLAG); + canvas.saveLayerAlpha(0, -vertPad, ellipsizeWidth - 1, layout.getHeight() + vertPad, 0xFF, Canvas.ALL_SAVE_FLAG); } if (hackClipBounds) { canvas.drawText(layout.getText().toString(), 0, -paint.getFontMetricsInt().ascent, paint); @@ -160,6 +192,10 @@ public float getCurrentWidth() { return width; } + public float getHeight() { + return layout.getHeight(); + } + @NonNull public CharSequence getText() { if (layout == null || layout.getText() == null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UItem.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UItem.java index 5713f303ea..ecb4140962 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UItem.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UItem.java @@ -64,6 +64,12 @@ public UItem(int viewType, boolean selectable) { super(viewType, selectable); } + public static UItem asCustom(int id, View view) { + UItem i = new UItem(UniversalAdapter.VIEW_TYPE_CUSTOM, false); + i.id = id; + i.view = view; + return i; + } public static UItem asCustom(View view) { UItem i = new UItem(UniversalAdapter.VIEW_TYPE_CUSTOM, false); i.view = view; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/UniversalAdapter.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/UniversalAdapter.java index 8bf5522736..4ac3facdff 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/UniversalAdapter.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/UniversalAdapter.java @@ -273,6 +273,9 @@ public void update(boolean animated) { fillItems.run(items, this); if (listView != null && listView.isComputingLayout()) { listView.post(() -> { + if (listView.isComputingLayout()) { + return; + } if (animated) { setItems(oldItems, items); } else { @@ -378,24 +381,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { }; break; case VIEW_TYPE_FULLSCREEN_CUSTOM: - view = new FrameLayout(context) { - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int maxHeight = MeasureSpec.getSize(heightMeasureSpec); - widthMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY); - measureChildren(widthMeasureSpec, heightMeasureSpec); - int height = 0; - for (int i = 0; i < getChildCount(); ++i) { - View child = getChildAt(i); - height = Math.max(height, child.getMeasuredHeight()); - } - if (maxHeight > 0) { - maxHeight -= AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight(); - height = Math.min(height, maxHeight); - } - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); - } - }; + view = new FullscreenCustomFrameLayout(context); view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.MATCH_PARENT)); break; case VIEW_TYPE_FILTER_CHAT: @@ -694,7 +680,6 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi break; case VIEW_TYPE_CUSTOM: case VIEW_TYPE_FULLY_CUSTOM: - case VIEW_TYPE_FULLSCREEN_CUSTOM: FrameLayout frameLayout = (FrameLayout) holder.itemView; if (frameLayout.getChildCount() != (item.view == null ? 0 : 1) || frameLayout.getChildAt(0) != item.view) { frameLayout.removeAllViews(); @@ -710,6 +695,17 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi } } break; + case VIEW_TYPE_FULLSCREEN_CUSTOM: + FullscreenCustomFrameLayout frameLayout2 = (FullscreenCustomFrameLayout) holder.itemView; + frameLayout2.setMinusHeight(item.intValue); + if (frameLayout2.getChildCount() != (item.view == null ? 0 : 1) || frameLayout2.getChildAt(0) != item.view) { + frameLayout2.removeAllViews(); + if (item.view != null) { + AndroidUtilities.removeFromParent(item.view); + frameLayout2.addView(item.view, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + } + } + break; case VIEW_TYPE_FILTER_CHAT: case VIEW_TYPE_FILTER_CHAT_CHECK: UserCell userCell = (UserCell) holder.itemView; @@ -1000,8 +996,35 @@ public UItem findItem(int itemId) { return null; } - private int getThemedColor(int key) { + protected int getThemedColor(int key) { return Theme.getColor(key, resourcesProvider); } + + private class FullscreenCustomFrameLayout extends FrameLayout { + private int minusHeight = 0; + public FullscreenCustomFrameLayout(Context context) { + super(context); + } + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int maxHeight = View.MeasureSpec.getSize(heightMeasureSpec); + widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(widthMeasureSpec), View.MeasureSpec.EXACTLY); + measureChildren(widthMeasureSpec, heightMeasureSpec); + int height = 0; + for (int i = 0; i < getChildCount(); ++i) { + View child = getChildAt(i); + height = Math.max(height, child.getMeasuredHeight()); + } + if (maxHeight > 0) { + maxHeight -= minusHeight; + height = Math.min(height, maxHeight); + } + super.onMeasure(widthMeasureSpec, View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)); + } + + public void setMinusHeight(int minusHeight) { + this.minusHeight = minusHeight; + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java b/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java index cc500c9cad..cf3f0e4094 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Components/ViewPagerFixed.java @@ -782,6 +782,18 @@ public void setPosition(int position) { } } + public void updateCurrent() { + if (viewTypes[0] != adapter.getItemViewType(currentPosition)) { + updateViewForIndex(0); + if (viewPages[1] != null) { + viewsByType.put(viewTypes[1], viewPages[1]); + removeView(viewPages[1]); + viewPages[1] = null; + } + viewPages[0].setTranslationX(0); + } + } + protected void onItemSelected(View currentPage, View oldPage, int position, int oldPosition) { } @@ -1322,6 +1334,7 @@ protected boolean canHighlightChildAt(View child, float x, float y) { return super.canHighlightChildAt(child, x, y); } }; + listView.setOverScrollMode(OVER_SCROLL_NEVER); if (hasStableIds) { listView.setItemAnimator(null); } else { @@ -1915,4 +1928,5 @@ private RecyclerListView findRecyclerView(View view) { public void setAllowDisallowInterceptTouch(boolean allowDisallowInterceptTouch) { this.allowDisallowInterceptTouch = allowDisallowInterceptTouch; } + } 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 84f96430f4..6e63555941 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 @@ -33,6 +33,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.Map; import java.util.Objects; import javax.microedition.khronos.egl.EGL10; @@ -47,25 +48,41 @@ public class SpoilerEffect2 { private final double MIN_DELTA; private final double MAX_DELTA; + public static final int TYPE_DEFAULT = 0; + public static final int TYPE_PREVIEW = 1; + + public final int type; + public static boolean supports() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; } - private static SpoilerEffect2 instance; + private static HashMap instance; public static SpoilerEffect2 getInstance(View view) { + return getInstance(TYPE_DEFAULT, view); + } + + public static SpoilerEffect2 getInstance(int type, View view) { + return getInstance(type, view, getRootView(view)); + } + + public static SpoilerEffect2 getInstance(int type, View view, ViewGroup rootView) { if (view == null || !supports()) { return null; } if (instance == null) { + instance = new HashMap<>(); + } + SpoilerEffect2 e = instance.get(type); + if (e == null) { final int sz = getSize(); - ViewGroup rootView = getRootView(view); if (rootView == null) { return null; } - instance = new SpoilerEffect2(makeTextureViewContainer(rootView), sz, sz); + instance.put(type, e = new SpoilerEffect2(type, makeTextureViewContainer(rootView), sz, sz)); } - instance.attach(view); - return instance; + e.attach(view); + return e; } private static ViewGroup getRootView(View view) { @@ -81,8 +98,16 @@ private static ViewGroup getRootView(View view) { } public static void pause(boolean pause) { - if (instance != null && instance.thread != null) { - instance.thread.pause(pause); + if (instance == null) return; + for (SpoilerEffect2 s : instance.values()) { + if (s.thread != null) s.thread.pause(pause); + } + } + + public static void pause(int type, boolean pause) { + if (instance == null) return; + for (SpoilerEffect2 s : instance.values()) { + if (s.type == type && s.thread != null) s.thread.pause(pause); } } @@ -229,11 +254,12 @@ private void destroy() { } } - private SpoilerEffect2(ViewGroup container, int width, int height) { + private SpoilerEffect2(int type, ViewGroup container, int width, int height) { MAX_FPS = (int) AndroidUtilities.screenRefreshRate; MIN_DELTA = 1.0 / MAX_FPS; MAX_DELTA = MIN_DELTA * 4; + this.type = type; this.width = width; this.height = height; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java index ebe39b7e0e..082b3ff5d3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java @@ -7232,7 +7232,7 @@ public void onPause() { @Override public boolean onBackPressed() { - if (closeStoryViewer()) { + if (closeSheet()) { return false; } else if (rightSlidingDialogContainer.hasFragment()) { if (rightSlidingDialogContainer.getFragment().onBackPressed()) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ExternalActionActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ExternalActionActivity.java index cb6800d150..7240b22659 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ExternalActionActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ExternalActionActivity.java @@ -93,7 +93,7 @@ protected void onCreate(Bundle savedInstanceState) { Theme.createDialogsResources(this); Theme.createChatResources(this, false); - actionBarLayout = INavigationLayout.newLayout(this); + actionBarLayout = INavigationLayout.newLayout(this, false); drawerLayoutContainer = new DrawerLayoutContainer(this); drawerLayoutContainer.setAllowOpenDrawer(false, false); @@ -153,7 +153,7 @@ protected boolean isActionBarVisible() { }); - layersActionBarLayout = INavigationLayout.newLayout(this); + layersActionBarLayout = INavigationLayout.newLayout(this, false); layersActionBarLayout.setRemoveActionBarExtraHeight(true); layersActionBarLayout.setBackgroundView(shadowTablet); layersActionBarLayout.setUseAlphaAnimations(true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java index 5517e16a1b..c27b0637ea 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/GroupCallActivity.java @@ -7377,7 +7377,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { options.add(1); } } - if (participant.peer.channel_id != 0 && !ChatObject.isMegagroup(currentAccount, participant.peer.channel_id)) { + if (participant.peer != null && participant.peer.channel_id != 0 && !ChatObject.isMegagroup(currentAccount, participant.peer.channel_id)) { items.add(LocaleController.getString("VoipGroupOpenChannel", R.string.VoipGroupOpenChannel)); icons.add(R.drawable.msg_channel); options.add(8); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ImageReceiverSpan.java b/TMessagesProj/src/main/java/org/telegram/ui/ImageReceiverSpan.java new file mode 100644 index 0000000000..50f3938030 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/ImageReceiverSpan.java @@ -0,0 +1,108 @@ +package org.telegram.ui; + +import static org.telegram.messenger.AndroidUtilities.dp; + +import android.graphics.Canvas; +import android.graphics.Paint; +import android.text.style.ReplacementSpan; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ImageReceiver; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Components.AvatarDrawable; + +public class ImageReceiverSpan extends ReplacementSpan { + + private final Paint shadowPaint; + public final ImageReceiver imageReceiver; + private float sz, radius; + private final int currentAccount; + + private View parent; + + public ImageReceiverSpan(View parent, int currentAccount) { + this(parent, currentAccount, 18); + } + + public ImageReceiverSpan(View parent, int currentAccount, float sz) { + this.currentAccount = currentAccount; + this.imageReceiver = new ImageReceiver(parent); + imageReceiver.setCurrentAccount(currentAccount); + setSize(sz); + + this.shadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + shadowPaint.setShadowLayer(dp(1), 0, dp(.66f), 0x33000000); + + setParent(parent); + } + + public void setSize(float sz) { + this.sz = sz; + } + + public void setRoundRadius(float radiusDp) { + imageReceiver.setRoundRadius((int) (radius = dp(radiusDp))); + } + + public void setParent(View parent) { + if (this.parent == parent) return; + if (this.parent != null) { + this.parent.removeOnAttachStateChangeListener(parentAttachListener); + if (this.parent.isAttachedToWindow() && !parent.isAttachedToWindow()) { + imageReceiver.onDetachedFromWindow(); + } + } + if ((this.parent == null || !this.parent.isAttachedToWindow()) && parent != null && parent.isAttachedToWindow()) { + imageReceiver.onAttachedToWindow(); + } + this.parent = parent; + imageReceiver.setParentView(parent); + if (parent != null) { + parent.addOnAttachStateChangeListener(parentAttachListener); + } + } + + private final View.OnAttachStateChangeListener parentAttachListener = new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(@NonNull View v) { + imageReceiver.onAttachedToWindow(); + } + @Override + public void onViewDetachedFromWindow(@NonNull View v) { + imageReceiver.onDetachedFromWindow(); + } + }; + + @Override + public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) { + return dp(sz); + } + + private float translateX, translateY; + private int shadowPaintAlpha = 0xFF; + + @Override + public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + if (shadowPaintAlpha != paint.getAlpha()) { + shadowPaint.setAlpha(shadowPaintAlpha = paint.getAlpha()); + shadowPaint.setShadowLayer(dp(1), 0, dp(.66f), Theme.multAlpha(0x33000000, shadowPaintAlpha / 255f)); + } + final float l = translateX + x; + final float t = translateY + (top + bottom) / 2f - dp(sz) / 2f; + AndroidUtilities.rectTmp.set(l, t, l + dp(sz), t + dp(sz)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, radius, radius, shadowPaint); + imageReceiver.setImageCoords(l, t, dp(sz), dp(sz)); + imageReceiver.setAlpha(paint.getAlpha() / 255f); + imageReceiver.draw(canvas); + } + + public void translate(float x, float y) { + this.translateX = x; + this.translateY = y; + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java index 442bb942aa..baec1f5672 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java @@ -129,8 +129,11 @@ import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.tl.TL_chatlists; import org.telegram.tgnet.tl.TL_stories; +import org.telegram.ui.ActionBar.ActionBarLayout; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheetTabs; +import org.telegram.ui.ActionBar.BottomSheetTabsOverlay; import org.telegram.ui.ActionBar.DrawerLayoutContainer; import org.telegram.ui.ActionBar.INavigationLayout; import org.telegram.ui.ActionBar.Theme; @@ -189,6 +192,7 @@ import org.telegram.ui.Stories.StoriesListPlaceProvider; import org.telegram.ui.Stories.StoryViewer; import org.telegram.ui.Stories.recorder.StoryRecorder; +import org.telegram.ui.bots.BotWebViewAttachedSheet; import org.telegram.ui.bots.BotWebViewSheet; import org.webrtc.voiceengine.WebRtcAudioTrack; @@ -250,15 +254,16 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati private ImageView themeSwitchImageView; private View themeSwitchSunView; private RLottieDrawable themeSwitchSunDrawable; - private INavigationLayout actionBarLayout; - private INavigationLayout layersActionBarLayout; - private INavigationLayout rightActionBarLayout; + private ActionBarLayout actionBarLayout; + private ActionBarLayout layersActionBarLayout; + private ActionBarLayout rightActionBarLayout; private RelativeLayout launchLayout; private FrameLayout shadowTablet; private FrameLayout shadowTabletSide; private SizeNotifierFrameLayout backgroundTablet; private FrameLayout frameLayout; private FireworksOverlay fireworksOverlay; + private BottomSheetTabsOverlay bottomSheetTabsOverlay; public DrawerLayoutContainer drawerLayoutContainer; private DrawerLayoutAdapter drawerLayoutAdapter; private PasscodeViewDialog passcodeDialog; @@ -388,7 +393,7 @@ protected void onCreate(Bundle savedInstanceState) { SharedConfig.lastPauseTime = (int) (SystemClock.elapsedRealtime() / 1000); } AndroidUtilities.fillStatusBarHeight(this, false); - actionBarLayout = INavigationLayout.newLayout(this); + actionBarLayout = new ActionBarLayout(this, true); frameLayout = new FrameLayout(this) { @Override @@ -441,6 +446,14 @@ public void closeDrawer(boolean fast) { selectAnimatedEmojiDialog = null; } } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (actionBarLayout.getParent() == this) { + actionBarLayout.parentDraw(this, canvas); + } + super.dispatchDraw(canvas); + } }; drawerLayoutContainer.setClipChildren(false); drawerLayoutContainer.setClipToPadding(false); @@ -461,6 +474,7 @@ protected void onDraw(Canvas canvas) { frameLayout.addView(themeSwitchSunView, LayoutHelper.createFrame(48, 48)); themeSwitchSunView.setVisibility(View.GONE); } + frameLayout.addView(bottomSheetTabsOverlay = new BottomSheetTabsOverlay(this)); frameLayout.addView(fireworksOverlay = new FireworksOverlay(this) { { setVisibility(GONE); @@ -564,12 +578,12 @@ public boolean drawChild(Canvas canvas, View child, long drawingTime) { botRequest.write_allowed = true; ConnectionsManager.getInstance(currentAccount).sendRequest(botRequest, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { attachMenuBot.inactive = attachMenuBot.side_menu_disclaimer_needed = false; - showAttachMenuBot(attachMenuBot, null); + showAttachMenuBot(attachMenuBot, null, true); MediaDataController.getInstance(currentAccount).updateAttachMenuBotsInCache(); }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); }, null); } else { - showAttachMenuBot(attachMenuBot, null); + showAttachMenuBot(attachMenuBot, null, true); } return; } @@ -977,14 +991,29 @@ public void onViewDetachedFromWindow(View v) { RestrictedLanguagesSelectActivity.checkRestrictedLanguages(false); } - private void showAttachMenuBot(TLRPC.TL_attachMenuBot attachMenuBot, String startApp) { + private void showAttachMenuBot(TLRPC.TL_attachMenuBot attachMenuBot, String startApp, boolean sidemenu) { drawerLayoutContainer.closeDrawer(); BaseFragment lastFragment = getLastFragment(); - BotWebViewSheet webViewSheet = new BotWebViewSheet(this, lastFragment != null ? lastFragment.getResourceProvider() : null); - webViewSheet.setParentActivity(this); - webViewSheet.requestWebView(currentAccount, attachMenuBot.bot_id, attachMenuBot.bot_id, attachMenuBot.short_name, null, BotWebViewSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON, 0, false, null, null, false, startApp, null, BotWebViewSheet.FLAG_FROM_SIDE_MENU); - webViewSheet.show(); - visibleDialogs.add(webViewSheet); + if (lastFragment == null) return; + BotWebViewAttachedSheet.WebViewRequestProps props = BotWebViewAttachedSheet.WebViewRequestProps.of(currentAccount, attachMenuBot.bot_id, attachMenuBot.bot_id, attachMenuBot.short_name, null, BotWebViewSheet.TYPE_SIMPLE_WEB_VIEW_BUTTON, 0, false, null, false, startApp, null, BotWebViewSheet.FLAG_FROM_SIDE_MENU, false); + if (getBottomSheetTabs() != null && getBottomSheetTabs().tryReopenTab(props) != null) { + return; + } + if (AndroidUtilities.isTablet()) { + BotWebViewSheet webViewSheet = new BotWebViewSheet(this, lastFragment.getResourceProvider()); + webViewSheet.setNeedsContext(false); + webViewSheet.setDefaultFullsize(sidemenu); + webViewSheet.setParentActivity(this); + webViewSheet.requestWebView(null, props); + webViewSheet.show(); + } else { + BotWebViewAttachedSheet webViewSheet = lastFragment.createBotViewer(); + webViewSheet.setNeedsContext(false); + webViewSheet.setDefaultFullsize(sidemenu); + webViewSheet.setParentActivity(this); + webViewSheet.requestWebView(null, props); + webViewSheet.show(); + } } @Override @@ -1067,6 +1096,14 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) { backgroundTablet.layout(0, 0, backgroundTablet.getMeasuredWidth(), backgroundTablet.getMeasuredHeight()); shadowTablet.layout(0, 0, shadowTablet.getMeasuredWidth(), shadowTablet.getMeasuredHeight()); } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (layersActionBarLayout != null) { + layersActionBarLayout.parentDraw(this, canvas); + } + super.dispatchDraw(canvas); + } }; if (i != -1) { drawerLayoutContainer.addView(launchLayout, i, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); @@ -1090,7 +1127,7 @@ protected boolean isActionBarVisible() { } launchLayout.addView(actionBarLayout.getView()); - rightActionBarLayout = INavigationLayout.newLayout(this); + rightActionBarLayout = new ActionBarLayout(this, false); rightActionBarLayout.setFragmentStack(rightFragmentsStack); rightActionBarLayout.setDelegate(this); launchLayout.addView(rightActionBarLayout.getView()); @@ -1132,7 +1169,7 @@ protected boolean isActionBarVisible() { }); - layersActionBarLayout = INavigationLayout.newLayout(this); + layersActionBarLayout = new ActionBarLayout(this, true); layersActionBarLayout.setRemoveActionBarExtraHeight(true); layersActionBarLayout.setBackgroundView(shadowTablet); layersActionBarLayout.setUseAlphaAnimations(true); @@ -1281,6 +1318,10 @@ public FireworksOverlay getFireworksOverlay() { return fireworksOverlay; } + public BottomSheetTabsOverlay getBottomSheetTabsOverlay() { + return bottomSheetTabsOverlay; + } + private void openSettings(boolean expanded) { Bundle args = new Bundle(); args.putLong("user_id", UserConfig.getInstance(currentAccount).clientUserId); @@ -2083,6 +2124,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool String setAsAttachBot = null; String attachMenuBotToOpen = null; String attachMenuBotChoose = null; + boolean botCompact = false; int storyId = 0; final String scheme = data.getScheme(); if (scheme != null) { @@ -2318,6 +2360,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool setAsAttachBot = data.getQueryParameter("startattach"); attachMenuBotChoose = data.getQueryParameter("choose"); attachMenuBotToOpen = data.getQueryParameter("attach"); + botCompact = TextUtils.equals(data.getQueryParameter("mode"), "compact"); threadId = Utilities.parseLong(data.getQueryParameter("thread")); text = data.getQueryParameter("text"); if (data.getQuery() != null) { @@ -2770,7 +2813,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool if (message != null && message.startsWith("@")) { message = " " + message; } - runLinkRequest(intentAccount[0], username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, login, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 0, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, startApp, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug); + runLinkRequest(intentAccount[0], username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, login, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 0, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, startApp, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact); } else { try (Cursor cursor = getContentResolver().query(intent.getData(), null, null, null, null)) { if (cursor != null) { @@ -2952,7 +2995,7 @@ private boolean handleIntent(Intent intent, boolean isNew, boolean restore, bool if (!AndroidUtilities.isTablet()) { actionBarLayout.removeAllFragments(); } else { - if (!layersActionBarLayout.getFragmentStack().isEmpty()) { + if (layersActionBarLayout != null && !layersActionBarLayout.getFragmentStack().isEmpty()) { for (int a = 0; a < layersActionBarLayout.getFragmentStack().size() - 1; a++) { layersActionBarLayout.removeFragmentFromStack(layersActionBarLayout.getFragmentStack().get(0)); a--; @@ -3726,9 +3769,9 @@ private void runLinkRequest(final int intentAccount, final boolean forceNotInternalForApps, final int storyId, final boolean isBoost, - final String chatLinkSlug) { + final String chatLinkSlug, boolean botCompact) { if (state == 0 && ChatActivity.SCROLL_DEBUG_DELAY && progress != null) { - Runnable runnable = () -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug); + Runnable runnable = () -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact); progress.init(); progress.onCancel(() -> AndroidUtilities.cancelRunOnUIThread(runnable)); AndroidUtilities.runOnUIThread(runnable, 7500); @@ -3738,7 +3781,7 @@ private void runLinkRequest(final int intentAccount, if (account != intentAccount) { switchToAccount(account, true); } - runLinkRequest(account, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug); + runLinkRequest(account, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, 1, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact); }).show(); return; } else if (code != null) { @@ -3844,7 +3887,7 @@ private void runLinkRequest(final int intentAccount, if (response instanceof TLRPC.TL_payments_paymentFormStars) { Runnable callback = navigateToPremiumGiftCallback; navigateToPremiumGiftCallback = null; - StarsController.getInstance(currentAccount).openPaymentForm(invoiceSlug, (TLRPC.TL_payments_paymentFormStars) response, () -> { + StarsController.getInstance(currentAccount).openPaymentForm(null, invoiceSlug, (TLRPC.TL_payments_paymentFormStars) response, () -> { try { dismissLoading.run(); } catch (Exception e) { @@ -3929,7 +3972,7 @@ private void runLinkRequest(final int intentAccount, getAttachMenuBot.bot = MessagesController.getInstance(intentAccount).getInputUser(peerId); ConnectionsManager.getInstance(intentAccount).sendRequest(getAttachMenuBot, (response1, error1) -> AndroidUtilities.runOnUIThread(() -> { if (error1 != null) { - AndroidUtilities.runOnUIThread(() -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, null, null, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug)); + AndroidUtilities.runOnUIThread(() -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, null, null, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact)); } else if (response1 instanceof TLRPC.TL_attachMenuBotsBot) { TLRPC.TL_attachMenuBotsBot bot = (TLRPC.TL_attachMenuBotsBot) response1; TLRPC.TL_attachMenuBot attachBot = bot.bot; @@ -3950,7 +3993,7 @@ private void runLinkRequest(final int intentAccount, } }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); - processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, botAttachable, true); + processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, botAttachable, true, botCompact); }, null); } else if (attachBot.request_write_access || forceNotInternalForApps) { AtomicBoolean allowWrite = new AtomicBoolean(true); @@ -3968,15 +4011,15 @@ private void runLinkRequest(final int intentAccount, } }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); - processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false); + processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false, botCompact); }); } else { - processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false); + processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false, botCompact); } } })); } else { - processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false); + processWebAppBot(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, botAppMaybe, botAppStartParam, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, user, dismissLoading, false, false, botCompact); } return; } @@ -4590,7 +4633,7 @@ public void onError() { } if (MessagesController.getInstance(intentAccount).checkCanOpenChat(args13, fragment13)) { NotificationCenter.getInstance(intentAccount).postNotificationName(NotificationCenter.closeChats); - MediaDataController.getInstance(intentAccount).saveDraft(did, 0, message, null, null, false); + MediaDataController.getInstance(intentAccount).saveDraft(did, 0, message, null, null, false, 0); getActionBarLayout().presentFragment(new ChatActivity(args13), true, false, true, false); } return true; @@ -4992,7 +5035,7 @@ private void processWebAppBot(final int intentAccount, final boolean isBoost, final String chatLinkSlug, TLRPC.User user, - Runnable dismissLoading, boolean botAttachable, boolean ignoreInactive) { + Runnable dismissLoading, boolean botAttachable, boolean ignoreInactive, boolean botCompact) { TLRPC.TL_messages_getBotApp getBotApp = new TLRPC.TL_messages_getBotApp(); TLRPC.TL_inputBotAppShortName app = new TLRPC.TL_inputBotAppShortName(); @@ -5004,7 +5047,7 @@ private void processWebAppBot(final int intentAccount, progress.end(); } if (error1 != null) { - AndroidUtilities.runOnUIThread(() -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, null, null, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug)); + AndroidUtilities.runOnUIThread(() -> runLinkRequest(intentAccount, username, group, sticker, emoji, botUser, botChat, botChannel, botChatAdminParams, message, contactToken, folderSlug, text, hasUrl, messageId, channelId, threadId, commentId, game, auth, lang, unsupportedUrl, code, loginToken, wallPaper, inputInvoiceSlug, theme, voicechat, videochat, livestream, state, videoTimestamp, setAsAttachBot, attachMenuBotToOpen, attachMenuBotChoose, null, null, progress, forceNotInternalForApps, storyId, isBoost, chatLinkSlug, botCompact)); } else { TLRPC.TL_messages_botApp botApp = (TLRPC.TL_messages_botApp) response1; AndroidUtilities.runOnUIThread(() -> { @@ -5013,14 +5056,31 @@ private void processWebAppBot(final int intentAccount, AtomicBoolean allowWrite = new AtomicBoolean(); BaseFragment lastFragment = mainFragmentsStack == null || mainFragmentsStack.isEmpty() ? null : mainFragmentsStack.get(mainFragmentsStack.size() - 1); Runnable loadBotSheet = () -> { - if (!isActive || isFinishing()) return; - BotWebViewSheet sheet = new BotWebViewSheet(LaunchActivity.this, lastFragment == null ? null : lastFragment.getResourceProvider()); - sheet.setParentActivity(LaunchActivity.this); - sheet.requestWebView(intentAccount, user.id, user.id, null, null, BotWebViewSheet.TYPE_WEB_VIEW_BOT_APP, 0, false, lastFragment, botApp.app, allowWrite.get(), botAppStartParam, user); - sheet.show(); - visibleDialogs.add(sheet); - if (botApp.inactive || forceNotInternalForApps) { - sheet.showJustAddedBulletin(); + if (lastFragment == null || !isActive || isFinishing() || isDestroyed()) return; + BotWebViewAttachedSheet.WebViewRequestProps props = BotWebViewAttachedSheet.WebViewRequestProps.of(intentAccount, user.id, user.id, null, null, BotWebViewSheet.TYPE_WEB_VIEW_BOT_APP, 0, false, botApp.app, allowWrite.get(), botAppStartParam, user, 0, botCompact); + if (getBottomSheetTabs() != null && getBottomSheetTabs().tryReopenTab(props) != null) { + return; + } + if (AndroidUtilities.isTablet()) { + BotWebViewSheet sheet = new BotWebViewSheet(LaunchActivity.this, lastFragment != null ? lastFragment.getResourceProvider() : null); + sheet.setDefaultFullsize(!botCompact); + sheet.setNeedsContext(false); + sheet.setParentActivity(LaunchActivity.this); + sheet.requestWebView(lastFragment, props); + sheet.show(); + if (botApp.inactive || forceNotInternalForApps) { + sheet.showJustAddedBulletin(); + } + } else { + BotWebViewAttachedSheet sheet = lastFragment.createBotViewer(); + sheet.setDefaultFullsize(!botCompact); + sheet.setNeedsContext(false); + sheet.setParentActivity(LaunchActivity.this); + sheet.requestWebView(lastFragment, props); + sheet.show(); + if (botApp.inactive || forceNotInternalForApps) { + sheet.showJustAddedBulletin(); + } } }; @@ -5074,7 +5134,7 @@ private void processAttachedMenuBotFromShortcut(long botId) { for (int i = 0; i < menuBots.bots.size(); i++) { if (menuBots.bots.get(i).bot_id == botId) { if (getLastFragment() != null) { - showAttachMenuBot(menuBots.bots.get(i), null); + showAttachMenuBot(menuBots.bots.get(i), null, false); } return; } @@ -5148,7 +5208,7 @@ private void processAttachMenuBot(int intentAccount, long peerId, String attachM MessagesController.getInstance(intentAccount).putUsers(attachMenuBotsBot.users, false); TLRPC.TL_attachMenuBot attachMenuBot = attachMenuBotsBot.bot; if (startAppParam != null) { - showAttachMenuBot(attachMenuBot, startAppParam); + showAttachMenuBot(attachMenuBot, startAppParam, false); return; } BaseFragment lastFragment_ = mainFragmentsStack.get(mainFragmentsStack.size() - 1); @@ -7416,6 +7476,9 @@ public void onBackPressed() { finish(); return; } + if (bottomSheetTabsOverlay != null && bottomSheetTabsOverlay.onBackPressed()) { + return; + } if (SearchTagsList.onBackPressedRenameTagAlert()) { return; } else if (ContentPreviewViewer.hasInstance() && ContentPreviewViewer.getInstance().isVisible()) { @@ -7919,6 +7982,20 @@ public void setNavigationBarColor(int color, boolean checkButtons) { } } } + BottomSheetTabs bottomSheetTabs = getBottomSheetTabs(); + if (bottomSheetTabs != null) { + bottomSheetTabs.setNavigationBarColor(color); + } + } + + public BottomSheetTabs getBottomSheetTabs() { + if (rightActionBarLayout != null && rightActionBarLayout.getBottomSheetTabs() != null) { + return rightActionBarLayout.getBottomSheetTabs(); + } + if (actionBarLayout != null && actionBarLayout.getBottomSheetTabs() != null) { + return actionBarLayout.getBottomSheetTabs(); + } + return null; } private ValueAnimator navBarAnimator; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java index c496e9538d..e01fcd49f7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LocationActivity.java @@ -8,6 +8,7 @@ package org.telegram.ui; +import static androidx.core.view.ViewCompat.TYPE_TOUCH; import static org.telegram.messenger.AndroidUtilities.dp; import android.Manifest; @@ -19,6 +20,7 @@ import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.app.Activity; +import android.app.Notification; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -61,6 +63,9 @@ import androidx.collection.LongSparseArray; import androidx.core.graphics.ColorUtils; +import androidx.core.view.NestedScrollingParent3; +import androidx.core.view.NestedScrollingParentHelper; +import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -80,6 +85,7 @@ import org.telegram.messenger.R; import org.telegram.messenger.UserObject; import org.telegram.tgnet.TLRPC; +import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenu; import org.telegram.ui.ActionBar.ActionBarMenuItem; @@ -91,6 +97,7 @@ import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Adapters.LocationActivityAdapter; import org.telegram.ui.Adapters.LocationActivitySearchAdapter; +import org.telegram.ui.Cells.GraySectionCell; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.LocationCell; import org.telegram.ui.Cells.LocationDirectionCell; @@ -110,6 +117,8 @@ import org.telegram.ui.Components.MapPlaceholderDrawable; import org.telegram.ui.Components.ProximitySheet; import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.SharedMediaLayout; +import org.telegram.ui.Components.SizeNotifierFrameLayout; import org.telegram.ui.Components.UndoView; import org.telegram.ui.Stories.recorder.HintView2; @@ -136,6 +145,7 @@ public class LocationActivity extends BaseFragment implements NotificationCenter private ActionBarMenuItem searchItem; private MapOverlayView overlayView; private HintView2 hintView; + public boolean fromStories; private UndoView[] undoView = new UndoView[2]; private boolean canUndo; @@ -431,6 +441,15 @@ public LocationActivity(int type) { AndroidUtilities.fixGoogleMapsBug(); } + private SharedMediaLayout sharedMediaLayout; + private GraySectionCell sharedMediaHeader; + private TL_stories.MediaArea searchStoriesArea; + + public LocationActivity searchStories(TL_stories.MediaArea area) { + searchStoriesArea = area; + return this; + } + private boolean initialMaxZoom; public void setInitialMaxZoom(boolean initialMaxZoom) { this.initialMaxZoom = initialMaxZoom; @@ -662,30 +681,7 @@ public void onTextChanged(EditText editText) { } } - fragmentView = new FrameLayout(context) { - private boolean first = true; - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - - if (changed) { - fixLayoutInternal(first); - first = false; - } else { - updateClipView(true); - } - } - - @Override - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - boolean result = super.drawChild(canvas, child, drawingTime); - if (child == actionBar && parentLayout != null) { - parentLayout.drawHeaderShadow(canvas, actionBar.getMeasuredHeight()); - } - return result; - } - }; + fragmentView = new NestedFrameLayout(context); FrameLayout frameLayout = (FrameLayout) fragmentView; fragmentView.setBackgroundColor(getThemedColor(Theme.key_dialogBackground)); @@ -996,7 +992,7 @@ public void getOutline(View view, Outline outline) { emptyView.addView(emptySubtitleTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 0, 6, 0, 0)); listView = new RecyclerListView(context); - listView.setAdapter(adapter = new LocationActivityAdapter(context, locationType, dialogId, false, getResourceProvider(), false, locationType == ChatAttachAlertLocationLayout.LOCATION_TYPE_BIZ) { + listView.setAdapter(adapter = new LocationActivityAdapter(context, locationType, dialogId, false, getResourceProvider(), false, fromStories, locationType == ChatAttachAlertLocationLayout.LOCATION_TYPE_BIZ) { @Override protected void onDirectionClick() { openDirections(null); @@ -1025,10 +1021,84 @@ public void setLiveLocations(ArrayList liveLocations) { super.setLiveLocations(liveLocations); } }); + listView.setLayoutManager(layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); + if (searchStoriesArea != null) { + sharedMediaHeader = new GraySectionCell(context, resourceProvider); + sharedMediaLayout = new SharedMediaLayout(context, 0, new SharedMediaLayout.SharedMediaPreloader(this), 0, null, null, null, SharedMediaLayout.TAB_STORIES, this, new SharedMediaLayout.Delegate() { + @Override + public void scrollToSharedMedia() { + + } + + @Override + public boolean onMemberClick(TLRPC.ChatParticipant participant, boolean b, boolean resultOnly, View view) { + return false; + } + + @Override + public TLRPC.Chat getCurrentChat() { + return null; + } + + @Override + public boolean isFragmentOpened() { + return true; + } + + @Override + public RecyclerListView getListView() { + return listView; + } + + @Override + public boolean canSearchMembers() { + return false; + } + + @Override + public void updateSelectedMediaTabText() { + final int count = sharedMediaLayout == null ? 0 : sharedMediaLayout.getStoriesCount(SharedMediaLayout.TAB_STORIES); + sharedMediaHeader.setText(LocaleController.formatPluralString("LocationStories", count)); + if (adapter.setSharedMediaLayoutVisible(count > 0)) { + listView.smoothScrollBy(0, dp(200)); + } + } + }, SharedMediaLayout.VIEW_TYPE_MEDIA_ACTIVITY, getResourceProvider()) { + @Override + public TL_stories.MediaArea getStoriesArea() { + return searchStoriesArea; + } + + @Override + protected boolean customTabs() { + return true; + } + + @Override + public int mediaPageTopMargin() { + return 32; + } + + @Override + public int overrideColumnsCount() { + return 3; + } + }; + sharedMediaLayout.setBackgroundColor(getThemedColor(Theme.key_dialogBackground)); + sharedMediaLayout.addView(sharedMediaHeader, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 32, Gravity.TOP | Gravity.FILL_HORIZONTAL)); + adapter.setSharedMediaLayout(sharedMediaLayout); + listView.setOverScrollMode(View.OVER_SCROLL_NEVER); + + DefaultItemAnimator itemAnimator = new DefaultItemAnimator(); + itemAnimator.setSupportsChangeAnimations(false); + itemAnimator.setDelayAnimations(false); + itemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + itemAnimator.setDurations(350); + listView.setItemAnimator(itemAnimator); + } adapter.setMyLocationDenied(locationDenied, false); adapter.setUpdateRunnable(() -> updateClipView(false)); listView.setVerticalScrollBarEnabled(false); - listView.setLayoutManager(layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP)); if (messageObject != null && messageObject.messageOwner != null && messageObject.messageOwner.media != null && !TextUtils.isEmpty(messageObject.messageOwner.media.address)) { adapter.setAddressNameOverride(messageObject.messageOwner.media.address); @@ -2245,6 +2315,9 @@ private void fixLayoutInternal(final boolean resume) { } else { overScrollHeight = viewHeight - dp(66) - height; } + if (sharedMediaLayout != null && sharedMediaLayout.getStoriesCount(SharedMediaLayout.TAB_STORIES) > 0) { + overScrollHeight -= dp(200); + } FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) listView.getLayoutParams(); layoutParams.topMargin = height; @@ -3011,4 +3084,151 @@ public String getAddressName() { public boolean isLightStatusBar() { return ColorUtils.calculateLuminance(getThemedColor(Theme.key_windowBackgroundWhite)) > 0.7f; } + + private class NestedFrameLayout extends SizeNotifierFrameLayout implements NestedScrollingParent3 { + + private NestedScrollingParentHelper nestedScrollingParentHelper; + + private boolean first = true; + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + + if (changed) { + fixLayoutInternal(first); + first = false; + } else { + updateClipView(true); + } + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + boolean result = super.drawChild(canvas, child, drawingTime); + if (child == actionBar && parentLayout != null) { + parentLayout.drawHeaderShadow(canvas, actionBar.getMeasuredHeight()); + } + return result; + } + + public NestedFrameLayout(Context context) { + super(context); + nestedScrollingParentHelper = new NestedScrollingParentHelper(this); + } + + @Override + public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, int[] consumed) { + try { + if (target == listView && sharedMediaLayout != null && sharedMediaLayout.isAttachedToWindow()) { + RecyclerListView innerListView = sharedMediaLayout.getCurrentListView(); + int top = sharedMediaLayout.getTop(); + if (top == 0) { + consumed[1] = dyUnconsumed; + innerListView.scrollBy(0, dyUnconsumed); + } + } + } catch (Throwable e) { + FileLog.e(e); + AndroidUtilities.runOnUIThread(() -> { + try { + RecyclerListView innerListView = sharedMediaLayout.getCurrentListView(); + if (innerListView != null && innerListView.getAdapter() != null) { + innerListView.getAdapter().notifyDataSetChanged(); + } + } catch (Throwable e2) { + + } + }); + } + } + + @Override + public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) { + + } + + @Override + public boolean onNestedPreFling(View target, float velocityX, float velocityY) { + return super.onNestedPreFling(target, velocityX, velocityY); + } + + @Override + public void onNestedPreScroll(View target, int dx, int dy, int[] consumed, int type) { + if (target == listView && sharedMediaLayout != null && sharedMediaLayout.isAttachedToWindow()) { + boolean searchVisible = actionBar.isSearchFieldVisible(); + int t = sharedMediaLayout.getTop(); + if (dy < 0) { + boolean scrolledInner = false; + if (t <= 0) { + RecyclerListView innerListView = sharedMediaLayout.getCurrentListView(); + if (innerListView != null) { + LinearLayoutManager linearLayoutManager = (LinearLayoutManager) innerListView.getLayoutManager(); + int pos = linearLayoutManager.findFirstVisibleItemPosition(); + if (pos != RecyclerView.NO_POSITION) { + RecyclerView.ViewHolder holder = innerListView.findViewHolderForAdapterPosition(pos); + int top = holder != null ? holder.itemView.getTop() : -1; + int paddingTop = innerListView.getPaddingTop(); + if (top != paddingTop || pos != 0) { + consumed[1] = pos != 0 ? dy : Math.max(dy, (top - paddingTop)); + innerListView.scrollBy(0, dy); + scrolledInner = true; + } + } + } + } + if (searchVisible) { + if (!scrolledInner && t < 0) { + consumed[1] = dy - Math.max(t, dy); + } else { + consumed[1] = dy; + } + } + } else { + if (searchVisible) { + RecyclerListView innerListView = sharedMediaLayout.getCurrentListView(); + consumed[1] = dy; + if (t > 0) { + consumed[1] -= dy; + } + if (innerListView != null && consumed[1] > 0) { + innerListView.scrollBy(0, consumed[1]); + } + } + } + } + } + + @Override + public boolean onStartNestedScroll(View child, View target, int axes, int type) { + return sharedMediaLayout != null && axes == ViewCompat.SCROLL_AXIS_VERTICAL; + } + + @Override + public void onNestedScrollAccepted(View child, View target, int axes, int type) { + nestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes); + } + + @Override + public void onStopNestedScroll(View target, int type) { + nestedScrollingParentHelper.onStopNestedScroll(target); + } + + @Override + public void onStopNestedScroll(View child) { + + } + + @Override + protected void drawList(Canvas blurCanvas, boolean top, ArrayList views) { + super.drawList(blurCanvas, top, views); + if (sharedMediaLayout != null) { + blurCanvas.save(); + blurCanvas.translate(0, listView.getY()); + sharedMediaLayout.drawListForBlur(blurCanvas, views); + blurCanvas.restore(); + } + } + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java index 6a20eec470..02d24674da 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java @@ -1770,7 +1770,9 @@ private void fillNextCodeParams(Bundle params, TLRPC.auth_SentCode res, boolean final String phone = params.getString("phoneFormated"); if (r.play_integrity_nonce != null) { IntegrityManager integrityManager = IntegrityManagerFactory.create(getContext()); - Task integrityTokenResponse = integrityManager.requestIntegrityToken(IntegrityTokenRequest.builder().setNonce(Utilities.bytesToHex(r.play_integrity_nonce)).setCloudProjectNumber(760348033671L).build()); + final String nonce = new String(Base64.encode(r.play_integrity_nonce, Base64.URL_SAFE)); + FileLog.d("getting classic integrity with nonce = " + nonce); + Task integrityTokenResponse = integrityManager.requestIntegrityToken(IntegrityTokenRequest.builder().setNonce(nonce).setCloudProjectNumber(r.play_integrity_project_id).build()); integrityTokenResponse .addOnSuccessListener(result -> { final String token = result.token(); @@ -5753,7 +5755,12 @@ public void didReceivedNotification(int id, int account, Object... args) { .requestIdToken(BuildVars.GOOGLE_AUTH_CLIENT_ID) .requestEmail() .build()); - googleClient.signOut().addOnCompleteListener(command -> getParentActivity().startActivityForResult(googleClient.getSignInIntent(), BasePermissionsActivity.REQUEST_CODE_SIGN_IN_WITH_GOOGLE)); + googleClient.signOut().addOnCompleteListener(command -> { + if (getParentActivity() == null || getParentActivity().isFinishing()) { + return; + } + getParentActivity().startActivityForResult(googleClient.getSignInIntent(), BasePermissionsActivity.REQUEST_CODE_SIGN_IN_WITH_GOOGLE); + }); }); } @@ -9549,7 +9556,7 @@ public void clearViews() { } actionBar = null; } - clearStoryViewers(); + clearSheets(); parentLayout = null; } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/MessageSendPreview.java b/TMessagesProj/src/main/java/org/telegram/ui/MessageSendPreview.java index 68960176d6..1c359a17ba 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/MessageSendPreview.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/MessageSendPreview.java @@ -16,6 +16,7 @@ import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; @@ -82,6 +83,9 @@ import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.SizeNotifierFrameLayout; import org.telegram.ui.Components.SmoothScroller; +import org.telegram.ui.Components.Text; +import org.telegram.ui.Components.spoilers.SpoilerEffect2; +import org.telegram.ui.Stars.StarsIntroActivity; import org.telegram.ui.Stories.recorder.KeyboardNotifier; import java.util.ArrayList; @@ -115,12 +119,16 @@ public class MessageSendPreview extends Dialog implements NotificationCenter.Not private int messagesContainerTopPadding; private final RecyclerListView chatListView; + private final RecyclerView.Adapter adapter; private final GridLayoutManagerFixed chatLayoutManager; private final ArrayList messageObjects = new ArrayList<>(); private int messageObjectsWidth; private final LongSparseArray groupedMessagesMap = new LongSparseArray<>(); private ChatMessageCell mainMessageCell; private int mainMessageCellId; + private int getMainMessageCellPosition() { + return groupedMessagesMap.isEmpty() || messageObjects.size() < 10 ? 0 : messageObjects.size() % 10; + } private EditTextCaption editText; private Paint editTextBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private Utilities.Callback2> drawEditText; @@ -141,6 +149,8 @@ public class MessageSendPreview extends Dialog implements NotificationCenter.Not private boolean layoutDone; public boolean allowRelayout; + private SpoilerEffect2 spoilerEffect2; + public MessageSendPreview(Context context, Theme.ResourcesProvider resourcesProvider) { super(context, R.style.TransparentDialog); this.context = context; @@ -179,6 +189,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } } }; + spoilerEffect2 = SpoilerEffect2.getInstance(SpoilerEffect2.TYPE_PREVIEW, windowView, windowView); windowView.setOnClickListener(v -> { onBackPressed(); }); @@ -305,7 +316,7 @@ protected void dispatchDraw(Canvas canvas) { canvas.restore(); } else { destCell.getLocationInWindow(pos2); - final int chatListViewTy = (int) ((View) destCell.getParent()).getTranslationY(); + final int chatListViewTy = (destCell.getParent() instanceof View) ? (int) ((View) destCell.getParent()).getTranslationY() : 0; if (this.chatListViewTy > chatListViewTy && destCellPos[1] - pos2[1] > this.chatListViewTy) { } else { @@ -414,7 +425,7 @@ public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets i Insets r = insets.getInsets(WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.systemBars()); MessageSendPreview.this.insets.set(r.left, r.top, r.right, r.bottom); } else { - MessageSendPreview.this.insets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(), insets.getStableInsetRight(), insets.getStableInsetBottom()); + MessageSendPreview.this.insets.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()); } containerView.setPadding(MessageSendPreview.this.insets.left, MessageSendPreview.this.insets.top, MessageSendPreview.this.insets.right, MessageSendPreview.this.insets.bottom); windowView.requestLayout(); @@ -471,6 +482,7 @@ protected void dispatchDraw(Canvas canvas) { canvas.save(); drawChatBackgroundElements(canvas); super.dispatchDraw(canvas); + drawChatForegroundElements(canvas); canvas.save(); float topAlpha = top.set(canScrollVertically(-1)); @@ -662,6 +674,114 @@ private void drawChatBackgroundElements(Canvas canvas) { } } } + + private void drawChatForegroundElements(Canvas canvas) { + int count = getChildCount(); + MessageObject.GroupedMessages lastDrawnGroup = null; + + for (int a = 0; a < count; a++) { + View child = getChildAt(a); + if (child instanceof ChatMessageCell) { + ChatMessageCell cell = (ChatMessageCell) child; + MessageObject.GroupedMessages group = cell.getCurrentMessagesGroup(); + if (group != null && group == lastDrawnGroup) { + continue; + } + lastDrawnGroup = group; + if (group == null) { + drawStarsPrice(canvas, cell.getBoundsLeft(), cell.getY(), cell.getBoundsRight(), cell.getY() + cell.getHeight()); + } + } + } + MessageObject.GroupedMessages scrimGroup = null; + for (int k = 0; k < 3; k++) { + drawingGroups.clear(); + if (k == 2 && !chatListView.isFastScrollAnimationRunning()) { + continue; + } + for (int i = 0; i < count; i++) { + View child = chatListView.getChildAt(i); + if (child instanceof ChatMessageCell) { + ChatMessageCell cell = (ChatMessageCell) child; + if (child.getY() > chatListView.getHeight() || child.getY() + child.getHeight() < 0) { + continue; + } + MessageObject.GroupedMessages group = cell.getCurrentMessagesGroup(); + if (group == null || (k == 0 && group.messages.size() == 1) || (k == 1 && !group.transitionParams.drawBackgroundForDeletedItems)) { + continue; + } + if ((k == 0 && cell.getMessageObject().deleted) || (k == 1 && !cell.getMessageObject().deleted)) { + continue; + } + if ((k == 2 && !cell.willRemovedAfterAnimation()) || (k != 2 && cell.willRemovedAfterAnimation())) { + continue; + } + + if (!drawingGroups.contains(group)) { + group.transitionParams.left = 0; + group.transitionParams.top = 0; + group.transitionParams.right = 0; + group.transitionParams.bottom = 0; + + group.transitionParams.pinnedBotton = false; + group.transitionParams.pinnedTop = false; + group.transitionParams.cell = cell; + drawingGroups.add(group); + } + + group.transitionParams.pinnedTop = cell.isPinnedTop(); + group.transitionParams.pinnedBotton = cell.isPinnedBottom(); + + int left = (int) (cell.getX() + cell.getBackgroundDrawableLeft()); + int right = (int) (cell.getX() + cell.getBackgroundDrawableRight()); + int top = (int) (cell.getY() + cell.getBackgroundDrawableTop()); + int bottom = (int) (cell.getY() + cell.getBackgroundDrawableBottom()); + + if ((cell.getCurrentPosition().flags & MessageObject.POSITION_FLAG_TOP) == 0) { + top -= dp(10); + } + + if ((cell.getCurrentPosition().flags & MessageObject.POSITION_FLAG_BOTTOM) == 0) { + bottom += dp(10); + } + + if (cell.willRemovedAfterAnimation()) { + group.transitionParams.cell = cell; + } + + if (group.transitionParams.top == 0 || top < group.transitionParams.top) { + group.transitionParams.top = top; + } + if (group.transitionParams.bottom == 0 || bottom > group.transitionParams.bottom) { + group.transitionParams.bottom = bottom; + } + if (group.transitionParams.left == 0 || left < group.transitionParams.left) { + group.transitionParams.left = left; + } + if (group.transitionParams.right == 0 || right > group.transitionParams.right) { + group.transitionParams.right = right; + } + } + } + + for (int i = 0; i < drawingGroups.size(); i++) { + MessageObject.GroupedMessages group = drawingGroups.get(i); + float x = group.transitionParams.cell.getNonAnimationTranslationX(true); + float l = (group.transitionParams.left + x + group.transitionParams.offsetLeft); + float t = (group.transitionParams.top + group.transitionParams.offsetTop); + float r = (group.transitionParams.right + x + group.transitionParams.offsetRight); + float b = (group.transitionParams.bottom + group.transitionParams.offsetBottom); + if (t < -dp(20)) { + t = -dp(20); + } + if (b > chatListView.getMeasuredHeight() + dp(20)) { + b = chatListView.getMeasuredHeight() + dp(20); + } + drawStarsPrice(canvas, l, t, r, b); + group.transitionParams.cell = null; + } + } + } }; chatListView.setOnClickListener(v -> { onBackPressed(); @@ -849,7 +969,7 @@ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, Recycle } } }); - chatListView.setAdapter(new RecyclerView.Adapter() { + chatListView.setAdapter(adapter = new RecyclerView.Adapter() { @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { @@ -867,8 +987,10 @@ public boolean canPerformActions() { public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { MessageObject messageObject = messageObjects.get(getItemCount() - 1 - position); ChatMessageCell cell = (ChatMessageCell) holder.itemView; - cell.setMessageObject(messageObject, getValidGroupedMessage(messageObject), false, false); - if (position == (groupedMessagesMap.isEmpty() || getItemCount() < 10 ? 0 : getItemCount() % 10) && !messageObject.needDrawForwarded()) { + MessageObject.GroupedMessages group = getValidGroupedMessage(messageObject); + cell.setInvalidatesParent(group != null); + cell.setMessageObject(messageObject, group, false, false); + if (position == getMainMessageCellPosition() && !messageObject.needDrawForwarded()) { mainMessageCell = cell; mainMessageCellId = messageObject.getId(); } @@ -950,6 +1072,11 @@ public MessageCell(Context context, int currentAccount, boolean canDrawBackgroun super(context, currentAccount, canDrawBackgroundInParent, sharedResources, resourcesProvider); } + @Override + protected SpoilerEffect2 makeSpoilerEffect() { + return SpoilerEffect2.getInstance(SpoilerEffect2.TYPE_PREVIEW, this, windowView); + } + @Override public boolean isPressed() { return false; @@ -1100,6 +1227,7 @@ public int getFillColor() { } }; this.anchorSendButton.copyCountTo(this.sendButton); + this.anchorSendButton.copyEmojiTo(this.sendButton); this.sendButton.center = sendButton.center; this.sendButton.open.set(sendButton.open.get(), true); this.sendButton.setOnClickListener(onClick); @@ -1167,6 +1295,10 @@ public void onReactionClicked(View view, ReactionsLayoutInBubble.VisibleReaction messageObject.messageOwner.flags2 &=~ 4; } } + if (sendButton != null) { + sendButton.setEffect(messageObject.messageOwner.effect); + } + onEffectChange(messageObject.messageOwner.effect); } else if (cameraRect != null) { boolean clear = false; if (visibleReaction.effectId == effectId) { @@ -1175,6 +1307,10 @@ public void onReactionClicked(View view, ReactionsLayoutInBubble.VisibleReaction } else { effectId = visibleReaction.effectId; } + if (sendButton != null) { + sendButton.setEffect(effectId); + } + onEffectChange(effectId); if (!premiumLocked) { TLRPC.TL_availableEffect effect = effectId == 0 ? null : MessagesController.getInstance(currentAccount).getEffect(effectId); if (effectDrawable != null) { @@ -1242,6 +1378,22 @@ public void onReactionClicked(View view, ReactionsLayoutInBubble.VisibleReaction }); } + public void setEffectId(long effectId) { + this.effectId = effectId; + final int position = getMainMessageCellPosition(); + MessageObject messageObject = position >= 0 && position < messageObjects.size() ? messageObjects.get(position) : null; + if (messageObject != null) { + messageObject.messageOwner.flags2 |= 4; + messageObject.messageOwner.effect = effectId; + } + if (effectSelector != null) { + TLRPC.TL_availableEffect effect = MessagesController.getInstance(currentAccount).getEffect(effectId); + if (effect != null) { + effectSelector.setSelectedReactionAnimated(ReactionsLayoutInBubble.VisibleReaction.fromTL(effect)); + } + } + } + public void showEffectSelector() { if (effectSelectorShown) return; layoutDone = false; @@ -1271,6 +1423,10 @@ public long getSelectedEffect() { return 0; } + protected void onEffectChange(long effectId) { + + } + public void hideEffectSelector() { if (effectSelector == null) return; if (!effectSelectorShown) return; @@ -1361,6 +1517,7 @@ public boolean isShowing() { @Override public void show() { if (!AndroidUtilities.isSafeToShow(getContext())) return; + SpoilerEffect2.pause(SpoilerEffect2.TYPE_DEFAULT, true); super.show(); prepareBlur(null); if (effectsView != null) { @@ -1461,6 +1618,10 @@ public void dismissInto(ChatMessageCell cell, float clipTop, float clipBottom) { fromPart = VisiblePart.of(mainMessageCell); } animateOpenTo(false, () -> { + SpoilerEffect2.pause(SpoilerEffect2.TYPE_DEFAULT, false); + if (spoilerEffect2 != null) { + spoilerEffect2.detach(windowView); + } AndroidUtilities.runOnUIThread(super::dismiss); }); windowView.invalidate(); @@ -1484,6 +1645,10 @@ public void dismiss() { anchorSendButton.invalidate(); } animateOpenTo(false, () -> { + SpoilerEffect2.pause(SpoilerEffect2.TYPE_DEFAULT, false); + if (spoilerEffect2 != null) { + spoilerEffect2.detach(windowView); + } AndroidUtilities.runOnUIThread(super::dismiss); }); windowView.invalidate(); @@ -1682,4 +1847,42 @@ private int getWidthForMessage(MessageObject object) { dummyMessageCell.isMegagroup = false; return dummyMessageCell.computeWidth(object, groupedMessagesMap.get(object.getGroupId())); } + + private Text buttonText; + private Paint buttonBgPaint; + + public void setStars(long stars) { + buttonText = stars <= 0 ? null : new Text(StarsIntroActivity.replaceStarsWithPlain(LocaleController.formatPluralStringComma("UnlockPaidContent", (int) stars), .7f), 14, AndroidUtilities.bold()); + if (buttonBgPaint == null) { + buttonBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + buttonBgPaint.setColor(0x40000000); + } + chatListView.invalidate(); + for (int i = 0; i < messageObjects.size(); ++i) { + MessageObject msg = messageObjects.get(i); + if (msg != null && msg.messageOwner != null && msg.messageOwner.media != null) { + msg.messageOwner.media.spoiler = stars > 0; + } + } + adapter.notifyDataSetChanged(); + } + + public void drawStarsPrice(Canvas canvas, float l, float t, float r, float b) { + if (buttonText == null || buttonBgPaint == null) return; + final float cx = (l + r) / 2f, cy = (t + b) / 2f; + + final float buttonWidth = dp(14 + 14) + buttonText.getCurrentWidth(); + final float buttonHeight = dp(32); + AndroidUtilities.rectTmp.set( + cx - buttonWidth / 2f, + cy - buttonHeight / 2f, + cx + buttonWidth / 2f, + cy + buttonHeight / 2f + ); + canvas.save(); + canvas.drawRoundRect(AndroidUtilities.rectTmp, buttonHeight / 2f, buttonHeight / 2f, buttonBgPaint); + buttonText.draw(canvas, cx - buttonWidth / 2f + dp(14), cy, 0xFFFFFFFF, 1f); + canvas.restore(); + } + } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java index 3f09c685e4..cd2b469d86 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PaymentFormActivity.java @@ -1172,6 +1172,9 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { webView.setWebViewClient(new WebViewClient() { @Override public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) { + if (!AndroidUtilities.isSafeToShow(getContext())) { + return true; + } new AlertDialog.Builder(getContext(), resourcesProvider) .setTitle(getString(R.string.ChromeCrashTitle)) .setMessage(AndroidUtilities.replaceSingleTag(getString(R.string.ChromeCrashMessage), () -> Browser.openUrl(getContext(), "https://play.google.com/store/apps/details?id=com.google.android.webview"))) @@ -1717,7 +1720,7 @@ protected void onDraw(Canvas canvas) { } } } else if (currentStep == STEP_SHIPPING_METHODS) { - int count = requestedInfo.shipping_options.size(); + int count = requestedInfo.shipping_options == null ? 0 : requestedInfo.shipping_options.size(); radioCells = new RadioCell[count]; for (int a = 0; a < count; a++) { TLRPC.TL_shippingOption shippingOption = requestedInfo.shipping_options.get(a); @@ -2417,7 +2420,7 @@ public boolean onTouchEvent(MotionEvent event) { webView.setWebViewClient(new WebViewClient() { @Override public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) { - if (LaunchActivity.instance != null && LaunchActivity.instance.isFinishing()) { + if (!AndroidUtilities.isSafeToShow(getContext())) { return true; } new AlertDialog.Builder(getContext(), resourcesProvider) @@ -4158,7 +4161,7 @@ private void sendData() { req2.peer = MessagesController.getInstance(currentAccount).getInputPeer(message[0].peer_id); ConnectionsManager.getInstance(currentAccount).sendRequest(req2, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { if (response2 instanceof TLRPC.TL_payments_paymentReceiptStars) { - StarsIntroActivity.showTransactionSheet(getContext(), currentAccount, (TLRPC.TL_payments_paymentReceiptStars) response2, getResourceProvider()); + StarsIntroActivity.showTransactionSheet(getContext(), false, currentAccount, (TLRPC.TL_payments_paymentReceiptStars) response2, getResourceProvider()); } else if (response2 instanceof TLRPC.PaymentReceipt) { BaseFragment lastFragment = LaunchActivity.getLastFragment(); if (lastFragment != null) { @@ -4220,7 +4223,7 @@ private void sendData() { req2.peer = MessagesController.getInstance(currentAccount).getInputPeer(message.peer_id); ConnectionsManager.getInstance(currentAccount).sendRequest(req2, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { if (response2 instanceof TLRPC.TL_payments_paymentReceiptStars) { - StarsIntroActivity.showTransactionSheet(getContext(), currentAccount, (TLRPC.TL_payments_paymentReceiptStars) response2, getResourceProvider()); + StarsIntroActivity.showTransactionSheet(getContext(), false, currentAccount, (TLRPC.TL_payments_paymentReceiptStars) response2, getResourceProvider()); } else if (response2 instanceof TLRPC.PaymentReceipt) { BaseFragment lastFragment = LaunchActivity.getLastFragment(); if (lastFragment != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java index 9b56e8b003..c01e758687 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoPickerActivity.java @@ -1907,7 +1907,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (selectedAlbum != null) { MediaController.PhotoEntry photoEntry = selectedAlbum.photos.get(position); - cell.setPhotoEntry(photoEntry, true, false); + cell.setPhotoEntry(photoEntry, selectedPhotosOrder.size() > 1, true, false); cell.setChecked(allowIndices ? selectedPhotosOrder.indexOf(photoEntry.imageId) : -1, selectedPhotos.containsKey(photoEntry.imageId), false); showing = PhotoViewer.isShowingImage(photoEntry.path); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java index 21eec1fb15..6579c80786 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/PhotoViewer.java @@ -1422,7 +1422,7 @@ public void run() { lastSaveTime = SystemClock.elapsedRealtime(); Utilities.globalQueue.postRunnable(() -> { SharedPreferences.Editor editor = ApplicationLoader.applicationContext.getSharedPreferences("media_saved_pos", Activity.MODE_PRIVATE).edit(); - editor.putFloat(saveFor, value).commit(); + editor.putFloat(shouldSavePositionForCurrentVideo, value).commit(); }); } } @@ -2762,6 +2762,10 @@ default boolean canLoadMoreAvatars() { return true; } default void onReleasePlayerBeforeClose(int currentIndex) {}; + + default boolean forceAllInGroup() { + return false; + } } private class FrameLayoutDrawer extends SizeNotifierFrameLayoutPhoto { @@ -5619,6 +5623,11 @@ public boolean validGroupId(long groupId) { } return true; } + + @Override + public boolean forceAll() { + return placeProvider != null && placeProvider.forceAllInGroup(); + } }); for (int a = 0; a < 3; a++) { @@ -12828,7 +12837,7 @@ private void onPhotoShow(final MessageObject messageObject, final TLRPC.FileLoca } } isFirstLoading = false; - } else if (MediaDataController.getMediaType(currentMessageObject.messageOwner) == sharedMediaType) { + } else if (MediaDataController.getMediaType(currentMessageObject.messageOwner) == sharedMediaType && (placeProvider == null || !placeProvider.forceAllInGroup())) { MediaDataController.getInstance(currentAccount).getMediaCount(currentDialogId, topicId, sharedMediaType, classGuid, true); if (mergeDialogId != 0) { MediaDataController.getInstance(currentAccount).getMediaCount(mergeDialogId, topicId, sharedMediaType, classGuid, true); @@ -13008,7 +13017,9 @@ private void setIsAboutToSwitchToIndex(int index, boolean init, boolean animated if (masksItemVisible) { setItemVisible(masksItem, false, false); } - if (!pipAvailable) { + if (noforwards) { + setItemVisible(pipItem, false, true); + } else if (!pipAvailable) { pipItem.setEnabled(false); setItemVisible(pipItem, true, !masksItemVisible && editItem.getAlpha() <= 0, 0.5f); } else { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java index 73aa8c71be..ef0b5512ff 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/ProfileActivity.java @@ -3951,7 +3951,8 @@ public boolean onItemClick(View view, int position) { Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? (!SharedConfig.isUsingCamera2(currentAccount) ? "Use Camera 2 API" : "Use old Camera 1 API") : null, BuildVars.DEBUG_VERSION ? "Clear bot biometry data" : null, BuildVars.DEBUG_PRIVATE_VERSION ? "Clear all login tokens" : null, - SharedConfig.canBlurChat() && Build.VERSION.SDK_INT >= 31 ? (SharedConfig.useNewBlur ? "back to cpu blur" : "use new gpu blur") : null + SharedConfig.canBlurChat() && Build.VERSION.SDK_INT >= 31 ? (SharedConfig.useNewBlur ? "back to cpu blur" : "use new gpu blur") : null, + BuildVars.DEBUG_PRIVATE_VERSION ? (SharedConfig.botTabs3DEffect ? "disable tabs 3d effect" : "enable tabs 3d effect") : null }; builder.setItems(items, (dialog, which) -> { @@ -4196,7 +4197,7 @@ protected void onSend(LongSparseArray dids, int count, TLRPC.TL_fo } else if (which == 23) { SharedConfig.toggleSurfaceInStories(); for (int i = 0; i < getParentLayout().getFragmentStack().size(); i++) { - getParentLayout().getFragmentStack().get(i).clearStoryViewers(); + getParentLayout().getFragmentStack().get(i).clearSheets(); } } else if (which == 24) { SharedConfig.togglePhotoViewerBlur(); @@ -4212,6 +4213,8 @@ protected void onSend(LongSparseArray dids, int count, TLRPC.TL_fo AuthTokensHelper.clearLogInTokens(); } else if (which == 30) { SharedConfig.toggleUseNewBlur(); + } else if (which == 31) { + SharedConfig.setBotTabs3DEffect(!SharedConfig.botTabs3DEffect); } }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); @@ -5711,7 +5714,7 @@ private void onWriteButtonClick() { setAvatarCell.getImageView().playAnimation(); } } else { - if (playProfileAnimation != 0 && parentLayout.getFragmentStack().get(parentLayout.getFragmentStack().size() - 2) instanceof ChatActivity) { + if (playProfileAnimation != 0 && parentLayout != null && parentLayout.getFragmentStack() != null && parentLayout.getFragmentStack().size() >= 2 && parentLayout.getFragmentStack().get(parentLayout.getFragmentStack().size() - 2) instanceof ChatActivity) { finishFragment(); } else { TLRPC.User user = getMessagesController().getUser(userId); @@ -7841,7 +7844,7 @@ public UndoView getUndoView() { } public boolean onBackPressed() { - if (closeStoryViewer()) { + if (closeSheet()) { return false; } return actionBar.isEnabled() && (sharedMediaRow == -1 || sharedMediaLayout == null || !sharedMediaLayout.closeActionMode()); @@ -8609,10 +8612,10 @@ private void updateRowsIds() { if (!getMessagesController().premiumFeaturesBlocked()) { businessRow = rowCount++; } - if (premiumRow >= -1 || starsRow >= 0 || businessRow >= 0) { + if (!getMessagesController().premiumPurchaseBlocked()) { premiumGiftingRow = rowCount++; } - if (premiumRow >= 0 || premiumGiftingRow >= 0) { + if (premiumRow >= 0 || starsRow >= 0 || businessRow >= 0 || premiumGiftingRow >= 0) { premiumSectionsRow = rowCount++; } helpHeaderRow = rowCount++; @@ -12893,12 +12896,18 @@ private void checkPhotoDescriptionAlpha() { // onlineTextView[1].setAlpha(1f - expandProgress); onlineTextView[1].setTranslationX(onlineX + customPhotoOffset); avatarContainer2.invalidate(); + if (showStatusButton != null) { + showStatusButton.setAlpha2(1f - currentExpandAnimatorValue); + } } } else { if (onlineTextView[2] != null) { onlineTextView[2].setAlpha(0); onlineTextView[3].setAlpha(0); } + if (showStatusButton != null) { + showStatusButton.setAlpha2(1f); + } } } else { @@ -12906,12 +12915,17 @@ private void checkPhotoDescriptionAlpha() { if (onlineTextView[2] != null) { onlineTextView[2].setAlpha(photoDescriptionProgress); } + if (showStatusButton != null) { + showStatusButton.setAlpha2(1f - photoDescriptionProgress); + } } else { if (onlineTextView[2] != null) { onlineTextView[2].setAlpha(0); } + if (showStatusButton != null) { + showStatusButton.setAlpha2(1f); + } } - } } @@ -13108,6 +13122,7 @@ public void setTextColor(int textColor) { @Override public void draw(@NonNull Canvas canvas) { + final float alpha = this.alpha * this.alpha2; if (alpha <= 0) return; AndroidUtilities.rectTmp.set(getBounds()); canvas.save(); @@ -13124,13 +13139,18 @@ public void draw(@NonNull Canvas canvas) { canvas.restore(); } - private float alpha = 1f; + private float alpha = 1f, alpha2 = 1f; @Override public void setAlpha(int alpha) { this.alpha = alpha / 255f; invalidateSelf(); } + public void setAlpha2(float alpha) { + this.alpha2 = alpha; + invalidateSelf(); + } + @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/SecretVoicePlayer.java b/TMessagesProj/src/main/java/org/telegram/ui/SecretVoicePlayer.java index 4007d18164..b21309e0c6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/SecretVoicePlayer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/SecretVoicePlayer.java @@ -196,7 +196,7 @@ public WindowInsets onApplyWindowInsets(@NonNull View v, @NonNull WindowInsets i Insets r = insets.getInsets(WindowInsetsCompat.Type.displayCutout() | WindowInsetsCompat.Type.systemBars()); SecretVoicePlayer.this.insets.set(r.left, r.top, r.right, r.bottom); } else { - SecretVoicePlayer.this.insets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(), insets.getStableInsetRight(), insets.getStableInsetBottom()); + SecretVoicePlayer.this.insets.set(insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(), insets.getSystemWindowInsetRight(), insets.getSystemWindowInsetBottom()); } containerView.setPadding(SecretVoicePlayer.this.insets.left, SecretVoicePlayer.this.insets.top, SecretVoicePlayer.this.insets.right, SecretVoicePlayer.this.insets.bottom); windowView.requestLayout(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsActivity.java new file mode 100644 index 0000000000..c58c090aa6 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsActivity.java @@ -0,0 +1,765 @@ +package org.telegram.ui.Stars; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.AndroidUtilities.dpf2; +import static org.telegram.messenger.LocaleController.formatPluralString; +import static org.telegram.messenger.LocaleController.getString; +import static org.telegram.ui.ActionBar.Theme.key_statisticChartLine_golden; + +import android.app.Activity; +import android.content.Context; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.text.Editable; +import android.text.InputType; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.PasswordTransformationMethod; +import android.text.style.RelativeSizeSpan; +import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.core.view.NestedScrollingParent3; +import androidx.core.view.NestedScrollingParentHelper; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.BillingController; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.UserConfig; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.browser.Browser; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.tgnet.tl.TL_stats; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BackDrawable; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChannelMonetizationLayout; +import org.telegram.ui.Charts.data.ChartData; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.ChatAvatarContainer; +import org.telegram.ui.Components.CircularProgressDrawable; +import org.telegram.ui.Components.ColoredImageSpan; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EditTextBoldCursor; +import org.telegram.ui.Components.FlickerLoadingView; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.OutlineTextContainerView; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.SizeNotifierFrameLayout; +import org.telegram.ui.Components.UItem; +import org.telegram.ui.Components.UniversalAdapter; +import org.telegram.ui.Components.UniversalRecyclerView; +import org.telegram.ui.GradientHeaderActivity; +import org.telegram.ui.StatisticActivity; +import org.telegram.ui.Stories.recorder.ButtonWithCounterView; +import org.telegram.ui.TwoStepVerificationActivity; +import org.telegram.ui.TwoStepVerificationSetupActivity; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +public class BotStarsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate { + + public final long bot_id; + + private ChatAvatarContainer avatarContainer; + private UniversalRecyclerView listView; + + private TLRPC.TL_payments_starsRevenueStats lastStats; + private TLRPC.TL_starsRevenueStatus lastStatsStatus; + + private StatisticActivity.ChartViewData revenueChartData; + private final ChannelMonetizationLayout.ProceedOverview availableValue = ChannelMonetizationLayout.ProceedOverview.as("XTR", getString(R.string.BotStarsOverviewAvailableBalance)); + private final ChannelMonetizationLayout.ProceedOverview totalValue = ChannelMonetizationLayout.ProceedOverview.as("XTR", getString(R.string.BotStarsOverviewTotalBalance)); + private final ChannelMonetizationLayout.ProceedOverview totalProceedsValue = ChannelMonetizationLayout.ProceedOverview.as("XTR", getString(R.string.BotStarsOverviewTotalProceeds)); + private final CharSequence withdrawInfo; + + private StarsIntroActivity.StarsTransactionsLayout transactionsLayout; + + private int balanceBlockedUntil; + private LinearLayout balanceLayout; + private RelativeSizeSpan balanceTitleSizeSpan; + private AnimatedTextView balanceTitle; + private AnimatedTextView balanceSubtitle; + private OutlineTextContainerView balanceEditTextContainer; + private boolean balanceEditTextIgnore = false; + private boolean balanceEditTextAll = true; + private long balanceEditTextValue; + private EditTextBoldCursor balanceEditText; + private ButtonWithCounterView balanceButton; + private ColoredImageSpan[] starRef = new ColoredImageSpan[1]; + private int shakeDp = 4; + + private double rate; + + public BotStarsActivity(long botId) { + this.bot_id = botId; + + BotStarsController.getInstance(currentAccount).preloadRevenueStats(bot_id); + BotStarsController.getInstance(currentAccount).invalidateTransactions(bot_id, true); + + withdrawInfo = AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(getString(R.string.BotStarsWithdrawInfo), () -> { + Browser.openUrl(getContext(), getString(R.string.BotStarsWithdrawInfoLink)); + }), true); + } + + @Override + public View createView(Context context) { + + NestedFrameLayout frameLayout = new NestedFrameLayout(context); + + avatarContainer = new ChatAvatarContainer(context, null, false); + avatarContainer.setOccupyStatusBar(!AndroidUtilities.isTablet()); + avatarContainer.getAvatarImageView().setScaleX(0.9f); + avatarContainer.getAvatarImageView().setScaleY(0.9f); + avatarContainer.setRightAvatarPadding(-dp(3)); + actionBar.addView(avatarContainer, 0, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT, !inPreviewMode ? 50 : 0, 0, 40, 0)); + + TLRPC.User bot = getMessagesController().getUser(bot_id); + avatarContainer.setUserAvatar(bot, true); + avatarContainer.setTitle(UserObject.getUserName(bot)); + avatarContainer.hideSubtitle(); + + actionBar.setBackButtonDrawable(new BackDrawable(false)); + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(final int id) { + if (id == -1) { + finishFragment(); + } + } + }); + avatarContainer.setTitleColors(Theme.getColor(Theme.key_player_actionBarTitle), Theme.getColor(Theme.key_player_actionBarSubtitle)); + actionBar.setItemsColor(Theme.getColor(Theme.key_player_actionBarTitle), false); + actionBar.setItemsColor(Theme.getColor(Theme.key_player_actionBarTitle), true); + actionBar.setItemsBackgroundColor(Theme.getColor(Theme.key_actionBarActionModeDefaultSelector), false); + actionBar.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); + + transactionsLayout = new StarsIntroActivity.StarsTransactionsLayout(context, currentAccount, bot_id, getClassGuid(), getResourceProvider()); + + balanceLayout = new LinearLayout(context) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + heightMeasureSpec + ); + } + }; + balanceLayout.setOrientation(LinearLayout.VERTICAL); + balanceLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite, getResourceProvider())); + balanceLayout.setPadding(0, 0, 0, dp(17)); + + balanceTitle = new AnimatedTextView(context, false, true, true); + balanceTitle.setTypeface(AndroidUtilities.bold()); + balanceTitle.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, getResourceProvider())); + balanceTitle.setTextSize(dp(32)); + balanceTitle.setGravity(Gravity.CENTER); + balanceTitleSizeSpan = new RelativeSizeSpan(65f / 96f); + balanceLayout.addView(balanceTitle, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 38, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 22, 15, 22, 0)); + + balanceSubtitle = new AnimatedTextView(context, true, true, true); + balanceSubtitle.setGravity(Gravity.CENTER); + balanceSubtitle.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText, getResourceProvider())); + balanceSubtitle.setTextSize(dp(14)); + balanceLayout.addView(balanceSubtitle, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 17, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 22, 4, 22, 0)); + + balanceEditTextContainer = new OutlineTextContainerView(context) { + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + if (balanceEditText != null && !balanceEditText.isFocusable()) { + balanceEditText.setFocusable(true); + balanceEditText.setFocusableInTouchMode(true); + int position = listView.findPositionByItemId(BALANCE); + if (position >= 0 && position < listView.adapter.getItemCount()) { + listView.stopScroll(); + listView.smoothScrollToPosition(position); + } + balanceEditText.requestFocus(); + } + return super.dispatchTouchEvent(event); + } + }; + balanceEditTextContainer.setText(getString(R.string.BotStarsWithdrawPlaceholder)); + balanceEditTextContainer.setLeftPadding(dp(14 + 22)); + balanceEditText = new EditTextBoldCursor(context) { + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + AndroidUtilities.hideKeyboard(this); + } + }; + balanceEditText.setFocusable(false); + balanceEditText.setTextColor(getThemedColor(Theme.key_windowBackgroundWhiteBlackText)); + balanceEditText.setCursorSize(AndroidUtilities.dp(20)); + balanceEditText.setCursorWidth(1.5f); + balanceEditText.setBackground(null); + balanceEditText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + balanceEditText.setMaxLines(1); + int padding = AndroidUtilities.dp(16); + balanceEditText.setPadding(dp(6), padding, padding, padding); + balanceEditText.setInputType(InputType.TYPE_CLASS_NUMBER); + balanceEditText.setTypeface(Typeface.DEFAULT); + balanceEditText.setHighlightColor(getThemedColor(Theme.key_chat_inTextSelectionHighlight)); + balanceEditText.setHandlesColor(getThemedColor(Theme.key_chat_TextSelectionCursor)); + balanceEditText.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + balanceEditText.setOnFocusChangeListener((v, hasFocus) -> balanceEditTextContainer.animateSelection(hasFocus ? 1f : 0f)); + balanceEditText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + @Override + public void afterTextChanged(Editable s) { + long balance = BotStarsController.getInstance(currentAccount).getBalance(bot_id); + balanceEditTextValue = TextUtils.isEmpty(s) ? 0 : Long.parseLong(s.toString()); + if (balanceEditTextValue > balance) { + balanceEditTextValue = balance; + balanceEditTextIgnore = true; + balanceEditText.setText(Long.toString(balanceEditTextValue)); + balanceEditText.setSelection(balanceEditText.getText().length()); + balanceEditTextIgnore = false; + } + balanceEditTextAll = balanceEditTextValue == balance; + AndroidUtilities.cancelRunOnUIThread(setBalanceButtonText); + setBalanceButtonText.run(); + if (balanceEditTextIgnore) return; + balanceEditTextAll = false; + } + }); + LinearLayout balanceEditTextLayout = new LinearLayout(context); + balanceEditTextLayout.setOrientation(LinearLayout.HORIZONTAL); + ImageView starImage = new ImageView(context); + starImage.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + starImage.setImageResource(R.drawable.star_small_inner); + balanceEditTextLayout.addView(starImage, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, Gravity.LEFT | Gravity.CENTER_VERTICAL, 14, 0, 0, 0)); + balanceEditTextLayout.addView(balanceEditText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 1, Gravity.FILL)); + balanceEditTextContainer.attachEditText(balanceEditText); + balanceEditTextContainer.addView(balanceEditTextLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP)); + balanceEditText.setOnEditorActionListener((textView, i, keyEvent) -> { + if (i == EditorInfo.IME_ACTION_NEXT) { + withdraw(); + return true; + } + return false; + }); + balanceLayout.addView(balanceEditTextContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 18, 14, 18, 2)); + + final CircularProgressDrawable circularProgressDrawable = new CircularProgressDrawable(dp(15), dpf2(2), Theme.getColor(Theme.key_featuredStickers_buttonText, getResourceProvider())) { + @Override + public int getIntrinsicWidth() { + return dp(24); + } + @Override + public int getIntrinsicHeight() { + return dp(24); + } + }; + circularProgressDrawable.setBounds(0, 0, dp(24), dp(24)); + + balanceButton = new ButtonWithCounterView(context, getResourceProvider()) { + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == circularProgressDrawable || super.verifyDrawable(who); + } + + @Override + protected boolean subTextSplitToWords() { + return false; + } + }; + balanceButton.setEnabled(MessagesController.getInstance(currentAccount).channelRevenueWithdrawalEnabled); + circularProgressDrawable.setCallback(balanceButton); + balanceButton.setText(getString(R.string.BotStarsButtonWithdrawAll), false); + balanceButton.setOnClickListener(v -> { + withdraw(); + }); + balanceLayout.addView(balanceButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.TOP | Gravity.FILL_HORIZONTAL, 18, 13, 18, 0)); + + + listView = new UniversalRecyclerView(this, this::fillItems, this::onItemClick, this::onItemLongClick); + listView.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundGray)); + frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + + return fragmentView = frameLayout; + } + + private Bulletin withdrawalBulletin; + private void withdraw() { + if (!balanceButton.isEnabled() || balanceButton.isLoading()) { + return; + } + + final int now = getConnectionsManager().getCurrentTime(); + if (balanceBlockedUntil > now) { + withdrawalBulletin = BulletinFactory.of(this).createSimpleBulletin(R.raw.timer_3, AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotStarsWithdrawalToast, untilString(balanceBlockedUntil - now)))).show(); + return; + } + + if (balanceEditTextValue < getMessagesController().starsRevenueWithdrawalMin) { + Drawable starDrawable = getContext().getResources().getDrawable(R.drawable.star_small_inner).mutate(); + BulletinFactory.of(this).createSimpleBulletin(starDrawable, AndroidUtilities.replaceSingleTag(LocaleController.formatPluralString("BotStarsWithdrawMinLimit", (int) getMessagesController().starsRevenueWithdrawalMin), () -> { + Bulletin.hideVisible(); + long balance = BotStarsController.getInstance(currentAccount).getBalance(bot_id); + if (balance < getMessagesController().starsRevenueWithdrawalMin) { + balanceEditTextAll = true; + balanceEditTextValue = balance; + } else { + balanceEditTextAll = false; + balanceEditTextValue = getMessagesController().starsRevenueWithdrawalMin; + } + balanceEditTextIgnore = true; + balanceEditText.setText(Long.toString(balanceEditTextValue)); + balanceEditText.setSelection(balanceEditText.getText().length()); + balanceEditTextIgnore = false; + + AndroidUtilities.cancelRunOnUIThread(setBalanceButtonText); + setBalanceButtonText.run(); + })).show(); + return; + } + + final long stars = balanceEditTextValue; + TwoStepVerificationActivity passwordFragment = new TwoStepVerificationActivity(); + passwordFragment.setDelegate(1, password -> initWithdraw(stars, password, passwordFragment)); + balanceButton.setLoading(true); + passwordFragment.preload(() -> { + balanceButton.setLoading(false); + presentFragment(passwordFragment);; + }); + } + + private final int BALANCE = 1; + + private void fillItems(ArrayList items, UniversalAdapter adapter) { + final BotStarsController s = BotStarsController.getInstance(currentAccount); + items.add(UItem.asChart(StatisticActivity.VIEW_TYPE_STACKBAR, stats_dc, revenueChartData)); + items.add(UItem.asShadow(-1, null)); + items.add(UItem.asBlackHeader(getString(R.string.BotStarsOverview))); + TLRPC.TL_payments_starsRevenueStats stats = s.getRevenueStats(bot_id); + if (stats != null && stats.status != null) { + availableValue.crypto_amount = stats.status.available_balance; + availableValue.currency = "USD"; + availableValue.amount = (long) (stats.status.available_balance * rate * 100.0); + totalValue.crypto_amount = stats.status.current_balance; + totalValue.currency = "USD"; + totalValue.amount = (long) (stats.status.current_balance * rate * 100.0); + totalProceedsValue.crypto_amount = stats.status.overall_revenue; + totalProceedsValue.currency = "USD"; + totalProceedsValue.amount = (long) (stats.status.overall_revenue * rate * 100.0); + setBalance(stats.status.available_balance, stats.status.next_withdrawal_at); + } + items.add(UItem.asProceedOverview(availableValue)); + items.add(UItem.asProceedOverview(totalValue)); + items.add(UItem.asProceedOverview(totalProceedsValue)); + items.add(UItem.asShadow(-2, getString(R.string.BotStarsOverviewInfo))); + items.add(UItem.asBlackHeader(getString(R.string.BotStarsAvailableBalance))); + items.add(UItem.asCustom(BALANCE, balanceLayout)); + items.add(UItem.asShadow(-3, withdrawInfo)); + items.add(UItem.asFullscreenCustom(transactionsLayout, 0)); + } + + private void onItemClick(UItem item, View view, int pos, float x, float y) { + if (item.instanceOf(StarsIntroActivity.StarsTransactionView.Factory.class)) { + TLRPC.StarsTransaction t = (TLRPC.StarsTransaction) item.object; + StarsIntroActivity.showTransactionSheet(getContext(), true, bot_id, currentAccount, t, getResourceProvider()); + } + } + + private void setBalance(long crypto_amount, int blockedUntil) { + if (balanceTitle == null || balanceSubtitle == null) + return; + long amount = (long) (rate * crypto_amount * 100.0); + SpannableStringBuilder ssb = new SpannableStringBuilder(StarsIntroActivity.replaceStarsWithPlain("XTR " + LocaleController.formatNumber(crypto_amount, ' '), 1f)); + int index = TextUtils.indexOf(ssb, "."); + if (index >= 0) { + ssb.setSpan(balanceTitleSizeSpan, index, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + balanceTitle.setText(ssb); + balanceSubtitle.setText("≈" + BillingController.getInstance().formatCurrency(amount, "USD")); + if (balanceEditTextAll) { + balanceEditTextIgnore = true; + balanceEditText.setText(Long.toString(balanceEditTextValue = crypto_amount)); + balanceEditText.setSelection(balanceEditText.getText().length()); + balanceEditTextIgnore = false; + + balanceButton.setEnabled(balanceEditTextValue > 0); + } + balanceBlockedUntil = blockedUntil; + + AndroidUtilities.cancelRunOnUIThread(setBalanceButtonText); + setBalanceButtonText.run(); + } + + private SpannableStringBuilder lock; + private Runnable setBalanceButtonText = () -> { + final int now = getConnectionsManager().getCurrentTime(); + balanceButton.setEnabled(balanceEditTextValue > 0 || balanceBlockedUntil > now); + if (now < balanceBlockedUntil) { + balanceButton.setText(getString(R.string.BotStarsButtonWithdrawUntil), true); + + if (lock == null) { + lock = new SpannableStringBuilder("l"); + ColoredImageSpan coloredImageSpan = new ColoredImageSpan(R.drawable.mini_switch_lock); + coloredImageSpan.setTopOffset(1); + lock.setSpan(coloredImageSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + SpannableStringBuilder buttonLockedText = new SpannableStringBuilder(); + buttonLockedText.append(lock).append(untilString(balanceBlockedUntil - now)); + balanceButton.setSubText(buttonLockedText, true); + + if (withdrawalBulletin != null && withdrawalBulletin.getLayout() instanceof Bulletin.LottieLayout && withdrawalBulletin.getLayout().isAttachedToWindow()) { + ((Bulletin.LottieLayout) withdrawalBulletin.getLayout()).textView.setText(AndroidUtilities.replaceTags(LocaleController.formatString(R.string.BotStarsWithdrawalToast, untilString(balanceBlockedUntil - now)))); + } + + AndroidUtilities.cancelRunOnUIThread(this.setBalanceButtonText); + AndroidUtilities.runOnUIThread(this.setBalanceButtonText, 1000); + } else { + balanceButton.setSubText(null, true); + balanceButton.setText(StarsIntroActivity.replaceStars(balanceEditTextAll ? getString(R.string.BotStarsButtonWithdrawAll) : LocaleController.formatPluralStringComma("BotStarsButtonWithdraw", (int) balanceEditTextValue, ' '), starRef), true); + } + }; + + public static String untilString(int t) { + final int d = t / (60 * 60 * 24); + t -= d * (60 * 60 * 24); + final int h = t / (60 * 60); + t -= h * (60 * 60); + final int m = t / 60; + t -= m * 60; + final int s = t; + + if (d == 0) { + if (h == 0) { + return String.format(Locale.ENGLISH, "%02d:%02d", m, s); + } + return String.format(Locale.ENGLISH, "%02d:%02d:%02d", h, m, s); + } + return LocaleController.formatString(R.string.PeriodDHM, String.format(Locale.ENGLISH, "%02d", d), String.format(Locale.ENGLISH, "%02d", h), String.format(Locale.ENGLISH, "%02d", m)); + } + + private boolean onItemLongClick(UItem item, View view, int pos, float x, float y) { + return false; + } + + private int stats_dc = -1; + @Override + public boolean onFragmentCreate() { + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.botStarsUpdated); + checkStats(); + return super.onFragmentCreate(); + } + + private void checkStats() { + TLRPC.TL_payments_starsRevenueStats stats = BotStarsController.getInstance(currentAccount).getRevenueStats(bot_id); + if (stats == lastStats && (stats == null ? null : stats.status) == lastStatsStatus) { + return; + } + + lastStats = stats; + lastStatsStatus = stats == null ? null : stats.status; + if (stats != null) { + rate = stats.usd_rate; + revenueChartData = StatisticActivity.createViewData(stats.revenue_graph, getString(R.string.BotStarsChartRevenue), 2); + if (revenueChartData != null && revenueChartData.chartData != null && revenueChartData.chartData.lines != null && !revenueChartData.chartData.lines.isEmpty() && revenueChartData.chartData.lines.get(0) != null) { + revenueChartData.chartData.lines.get(0).colorKey = Theme.key_statisticChartLine_golden; + revenueChartData.chartData.yRate = (float) (1.0 / rate / 100.0); + } + setBalance(stats.status.available_balance, stats.status.next_withdrawal_at); + if (listView != null) { + listView.adapter.update(true); + } + } + } + + @Override + public void onFragmentDestroy() { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.botStarsUpdated); + super.onFragmentDestroy(); + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.botStarsUpdated) { + if ((long) args[0] == bot_id) { + checkStats(); + } + } + } + + @Override + public boolean isLightStatusBar() { + return AndroidUtilities.computePerceivedBrightness(Theme.getColor(Theme.key_windowBackgroundWhite)) > 0.721f; + } + + + private class NestedFrameLayout extends SizeNotifierFrameLayout implements NestedScrollingParent3 { + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + private NestedScrollingParentHelper nestedScrollingParentHelper; + + public NestedFrameLayout(Context context) { + super(context); + nestedScrollingParentHelper = new NestedScrollingParentHelper(this); + } + + @Override + public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type, int[] consumed) { + try { + if (target == listView && transactionsLayout.isAttachedToWindow()) { + RecyclerListView innerListView = transactionsLayout.getCurrentListView(); + int bottom = ((View) transactionsLayout.getParent()).getBottom(); + actionBar.setCastShadows(listView.getHeight() - bottom < 0); + if (listView.getHeight() - bottom >= 0) { + consumed[1] = dyUnconsumed; + innerListView.scrollBy(0, dyUnconsumed); + } + } + } catch (Throwable e) { + FileLog.e(e); + AndroidUtilities.runOnUIThread(() -> { + try { + RecyclerListView innerListView = transactionsLayout.getCurrentListView(); + if (innerListView != null && innerListView.getAdapter() != null) { + innerListView.getAdapter().notifyDataSetChanged(); + } + } catch (Throwable e2) { + + } + }); + } + } + + @Override + public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) { + + } + + @Override + public boolean onNestedPreFling(View target, float velocityX, float velocityY) { + return super.onNestedPreFling(target, velocityX, velocityY); + } + + @Override + public void onNestedPreScroll(View target, int dx, int dy, int[] consumed, int type) { + if (target == listView && transactionsLayout.isAttachedToWindow()) { + boolean searchVisible = actionBar.isSearchFieldVisible(); + int t = ((View) transactionsLayout.getParent()).getTop() - AndroidUtilities.statusBarHeight - ActionBar.getCurrentActionBarHeight(); + int bottom = ((View) transactionsLayout.getParent()).getBottom(); + if (dy < 0) { + boolean scrolledInner = false; + actionBar.setCastShadows(listView.getHeight() - bottom < 0); + if (listView.getHeight() - bottom >= 0) { + RecyclerListView innerListView = transactionsLayout.getCurrentListView(); + LinearLayoutManager linearLayoutManager = (LinearLayoutManager) innerListView.getLayoutManager(); + int pos = linearLayoutManager.findFirstVisibleItemPosition(); + if (pos != RecyclerView.NO_POSITION) { + RecyclerView.ViewHolder holder = innerListView.findViewHolderForAdapterPosition(pos); + int top = holder != null ? holder.itemView.getTop() : -1; + int paddingTop = innerListView.getPaddingTop(); + if (top != paddingTop || pos != 0) { + consumed[1] = pos != 0 ? dy : Math.max(dy, (top - paddingTop)); + innerListView.scrollBy(0, dy); + scrolledInner = true; + } + } + } + if (searchVisible) { + if (!scrolledInner && t < 0) { + consumed[1] = dy - Math.max(t, dy); + } else { + consumed[1] = dy; + } + } + } else { + if (searchVisible) { + RecyclerListView innerListView = transactionsLayout.getCurrentListView(); + consumed[1] = dy; + if (t > 0) { + consumed[1] -= dy; + } + if (innerListView != null && consumed[1] > 0) { + innerListView.scrollBy(0, consumed[1]); + } + } + } + } + } + + @Override + public boolean onStartNestedScroll(View child, View target, int axes, int type) { + return axes == ViewCompat.SCROLL_AXIS_VERTICAL; + } + + @Override + public void onNestedScrollAccepted(View child, View target, int axes, int type) { + nestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes); + } + + @Override + public void onStopNestedScroll(View target, int type) { + nestedScrollingParentHelper.onStopNestedScroll(target); + } + + @Override + public void onStopNestedScroll(View child) { + + } + } + + + private void initWithdraw(long stars, TLRPC.InputCheckPasswordSRP password, TwoStepVerificationActivity passwordFragment) { + Activity parentActivity = getParentActivity(); + TLRPC.User currentUser = UserConfig.getInstance(currentAccount).getCurrentUser(); + if (parentActivity == null || currentUser == null) return; + + TLRPC.TL_payments_getStarsRevenueWithdrawalUrl req = new TLRPC.TL_payments_getStarsRevenueWithdrawalUrl(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(bot_id); + req.stars = stars; + req.password = password != null ? password : new TLRPC.TL_inputCheckPasswordEmpty(); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error != null) { + if ("PASSWORD_MISSING".equals(error.text) || error.text.startsWith("PASSWORD_TOO_FRESH_") || error.text.startsWith("SESSION_TOO_FRESH_")) { + if (passwordFragment != null) { + passwordFragment.needHideProgress(); + } + AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity); + builder.setTitle(LocaleController.getString("EditAdminTransferAlertTitle", R.string.EditAdminTransferAlertTitle)); + + LinearLayout linearLayout = new LinearLayout(parentActivity); + linearLayout.setPadding(AndroidUtilities.dp(24), AndroidUtilities.dp(2), AndroidUtilities.dp(24), 0); + linearLayout.setOrientation(LinearLayout.VERTICAL); + builder.setView(linearLayout); + + TextView messageTextView = new TextView(parentActivity); + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + messageTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.WithdrawChannelAlertText))); + linearLayout.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + LinearLayout linearLayout2 = new LinearLayout(parentActivity); + linearLayout2.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.addView(linearLayout2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 11, 0, 0)); + + ImageView dotImageView = new ImageView(parentActivity); + dotImageView.setImageResource(R.drawable.list_circle); + dotImageView.setPadding(LocaleController.isRTL ? AndroidUtilities.dp(11) : 0, AndroidUtilities.dp(9), LocaleController.isRTL ? 0 : AndroidUtilities.dp(11), 0); + dotImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogTextBlack), PorterDuff.Mode.MULTIPLY)); + + messageTextView = new TextView(parentActivity); + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + messageTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("EditAdminTransferAlertText1", R.string.EditAdminTransferAlertText1))); + if (LocaleController.isRTL) { + linearLayout2.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + linearLayout2.addView(dotImageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT)); + } else { + linearLayout2.addView(dotImageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + linearLayout2.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + + linearLayout2 = new LinearLayout(parentActivity); + linearLayout2.setOrientation(LinearLayout.HORIZONTAL); + linearLayout.addView(linearLayout2, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 11, 0, 0)); + + dotImageView = new ImageView(parentActivity); + dotImageView.setImageResource(R.drawable.list_circle); + dotImageView.setPadding(LocaleController.isRTL ? AndroidUtilities.dp(11) : 0, AndroidUtilities.dp(9), LocaleController.isRTL ? 0 : AndroidUtilities.dp(11), 0); + dotImageView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogTextBlack), PorterDuff.Mode.MULTIPLY)); + + messageTextView = new TextView(parentActivity); + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + messageTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + messageTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString("EditAdminTransferAlertText2", R.string.EditAdminTransferAlertText2))); + if (LocaleController.isRTL) { + linearLayout2.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + linearLayout2.addView(dotImageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT)); + } else { + linearLayout2.addView(dotImageView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + linearLayout2.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + } + + if ("PASSWORD_MISSING".equals(error.text)) { + builder.setPositiveButton(LocaleController.getString("EditAdminTransferSetPassword", R.string.EditAdminTransferSetPassword), (dialogInterface, i) -> presentFragment(new TwoStepVerificationSetupActivity(TwoStepVerificationSetupActivity.TYPE_INTRO, null))); + builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + } else { + messageTextView = new TextView(parentActivity); + messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack)); + messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); + messageTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP); + messageTextView.setText(LocaleController.getString("EditAdminTransferAlertText3", R.string.EditAdminTransferAlertText3)); + linearLayout.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 11, 0, 0)); + + builder.setNegativeButton(LocaleController.getString("OK", R.string.OK), null); + } + if (passwordFragment != null) { + passwordFragment.showDialog(builder.create()); + } else { + showDialog(builder.create()); + } + } else if ("SRP_ID_INVALID".equals(error.text)) { + TLRPC.TL_account_getPassword getPasswordReq = new TLRPC.TL_account_getPassword(); + ConnectionsManager.getInstance(currentAccount).sendRequest(getPasswordReq, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { + if (error2 == null) { + TLRPC.account_Password currentPassword = (TLRPC.account_Password) response2; + passwordFragment.setCurrentPasswordInfo(null, currentPassword); + TwoStepVerificationActivity.initPasswordNewAlgo(currentPassword); + initWithdraw(stars, passwordFragment.getNewSrpPassword(), passwordFragment); + } + }), ConnectionsManager.RequestFlagWithoutLogin); + } else { + if (passwordFragment != null) { + passwordFragment.needHideProgress(); + passwordFragment.finishFragment(); + } + BulletinFactory.showError(error); + } + } else { + passwordFragment.needHideProgress(); + passwordFragment.finishFragment(); + if (response instanceof TLRPC.TL_payments_starsRevenueWithdrawalUrl) { + balanceEditTextAll = true; + Browser.openUrl(getContext(), ((TLRPC.TL_payments_starsRevenueWithdrawalUrl) response).url); + } + } + })); + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsController.java b/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsController.java new file mode 100644 index 0000000000..927a10b73e --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stars/BotStarsController.java @@ -0,0 +1,250 @@ +package org.telegram.ui.Stars; + +import static org.telegram.messenger.LocaleController.formatPluralString; +import static org.telegram.messenger.LocaleController.getCurrencyExpDivider; +import static org.telegram.messenger.LocaleController.getString; + +import android.app.Activity; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.LongSparseArray; + +import androidx.annotation.NonNull; + +import com.android.billingclient.api.BillingClient; +import com.android.billingclient.api.BillingFlowParams; +import com.android.billingclient.api.ProductDetails; +import com.android.billingclient.api.QueryProductDetailsParams; + +import org.json.JSONObject; +import org.telegram.messenger.AccountInstance; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.ApplicationLoader; +import org.telegram.messenger.BillingController; +import org.telegram.messenger.BuildVars; +import org.telegram.messenger.DialogObject; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessageObject; +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.messenger.Utilities; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChannelMonetizationLayout; +import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.PaymentFormActivity; +import org.telegram.ui.bots.BotWebViewSheet; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; + +public class BotStarsController { + + private static volatile BotStarsController[] Instance = new BotStarsController[UserConfig.MAX_ACCOUNT_COUNT]; + private static final Object[] lockObjects = new Object[UserConfig.MAX_ACCOUNT_COUNT]; + static { + for (int i = 0; i < UserConfig.MAX_ACCOUNT_COUNT; i++) { + lockObjects[i] = new Object(); + } + } + + public static BotStarsController getInstance(int num) { + BotStarsController localInstance = Instance[num]; + if (localInstance == null) { + synchronized (lockObjects[num]) { + localInstance = Instance[num]; + if (localInstance == null) { + Instance[num] = localInstance = new BotStarsController(num); + } + } + } + return localInstance; + } + + public final int currentAccount; + + private BotStarsController(int account) { + currentAccount = account; + } + + private final HashMap lastLoadedStats = new HashMap<>(); + private final HashMap stats = new HashMap<>(); + + public long getBalance(long bot_id) { + TLRPC.TL_payments_starsRevenueStats botStats = getRevenueStats(bot_id); + return botStats == null ? 0 : botStats.status.current_balance; + } + + public boolean isBalanceAvailable(long bot_id) { + return getRevenueStats(bot_id) != null; + } + + public TLRPC.TL_payments_starsRevenueStats getRevenueStats(long bot_id) { + return getRevenueStats(bot_id, false); + } + + public boolean hasStars(long bot_id) { + TLRPC.TL_payments_starsRevenueStats stats = getRevenueStats(bot_id); + return stats != null && stats.status != null && (stats.status.available_balance > 0 || stats.status.overall_revenue > 0 || stats.status.current_balance > 0); + } + + public void preloadRevenueStats(long bot_id) { + Long lastLoaded = lastLoadedStats.get(bot_id); + TLRPC.TL_payments_starsRevenueStats botStats = stats.get(bot_id); + getRevenueStats(bot_id, lastLoaded == null || System.currentTimeMillis() - lastLoaded > 1000 * 30); + } + + public TLRPC.TL_payments_starsRevenueStats getRevenueStats(long bot_id, boolean force) { + Long lastLoaded = lastLoadedStats.get(bot_id); + TLRPC.TL_payments_starsRevenueStats botStats = stats.get(bot_id); + if (lastLoaded == null || System.currentTimeMillis() - lastLoaded > 1000 * 60 * 5 || force) { + TLRPC.TL_payments_getStarsRevenueStats req = new TLRPC.TL_payments_getStarsRevenueStats(); + req.dark = Theme.isCurrentThemeDark(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(bot_id); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TLRPC.TL_payments_starsRevenueStats) { + TLRPC.TL_payments_starsRevenueStats r = (TLRPC.TL_payments_starsRevenueStats) res; + stats.put(bot_id, r); + } else { + stats.put(bot_id, null); + } + lastLoadedStats.put(bot_id, System.currentTimeMillis()); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.botStarsUpdated, bot_id); + })); + } + return botStats; + } + + public void onUpdate(TLRPC.TL_updateStarsRevenueStatus update) { + if (update == null) return; + long dialogId = DialogObject.getPeerDialogId(update.peer); + if (dialogId < 0) { + if (ChannelMonetizationLayout.instance != null && ChannelMonetizationLayout.instance.dialogId == DialogObject.getPeerDialogId(update.peer)) { + ChannelMonetizationLayout.instance.setupBalances(update.status); + ChannelMonetizationLayout.instance.reloadTransactions(); + } + } else { + TLRPC.TL_payments_starsRevenueStats s = getRevenueStats(dialogId, true); + if (s != null) { + s.status = update.status; + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.botStarsUpdated, dialogId); + } + invalidateTransactions(dialogId, true); + } + } + + + public static final int ALL_TRANSACTIONS = 0; + public static final int INCOMING_TRANSACTIONS = 1; + public static final int OUTGOING_TRANSACTIONS = 2; + + private class TransactionsState { + public final ArrayList[] transactions = new ArrayList[] { new ArrayList<>(), new ArrayList<>(), new ArrayList<>() }; + public final boolean[] transactionsExist = new boolean[3]; + private final String[] offset = new String[3]; + private final boolean[] loading = new boolean[3]; + private final boolean[] endReached = new boolean[3]; + } + + private final HashMap transactions = new HashMap<>(); + + @NonNull + private TransactionsState getTransactionsState(long bot_id) { + TransactionsState state = transactions.get(bot_id); + if (state == null) { + transactions.put(bot_id, state = new TransactionsState()); + } + return state; + } + + @NonNull + public ArrayList getTransactions(long bot_id, int type) { + TransactionsState state = getTransactionsState(bot_id); + return state.transactions[type]; + } + + public void invalidateTransactions(long bot_id, boolean load) { + final TransactionsState state = getTransactionsState(bot_id); + for (int i = 0; i < 3; ++i) { + if (state.loading[i]) continue; + state.transactions[i].clear(); + state.offset[i] = null; + state.loading[i] = false; + state.endReached[i] = false; + if (load) + loadTransactions(bot_id, i); + } + } + + public void preloadTransactions(long bot_id) { + final TransactionsState state = getTransactionsState(bot_id); + for (int i = 0; i < 3; ++i) { + if (!state.loading[i] && !state.endReached[i] && state.offset[i] == null) { + loadTransactions(bot_id, i); + } + } + } + + public void loadTransactions(long bot_id, int type) { + final TransactionsState state = getTransactionsState(bot_id); + if (state.loading[type] || state.endReached[type]) { + return; + } + + state.loading[type] = true; + + TLRPC.TL_payments_getStarsTransactions req = new TLRPC.TL_payments_getStarsTransactions(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(bot_id); + req.inbound = type == INCOMING_TRANSACTIONS; + req.outbound = type == OUTGOING_TRANSACTIONS; + req.offset = state.offset[type]; + if (req.offset == null) { + req.offset = ""; + } + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + state.loading[type] = false; + if (res instanceof TLRPC.TL_payments_starsStatus) { + TLRPC.TL_payments_starsStatus r = (TLRPC.TL_payments_starsStatus) res; + MessagesController.getInstance(currentAccount).putUsers(r.users, false); + MessagesController.getInstance(currentAccount).putChats(r.chats, false); + + state.transactions[type].addAll(r.history); + state.transactionsExist[type] = !state.transactions[type].isEmpty() || state.transactionsExist[type]; + state.endReached[type] = (r.flags & 1) == 0; + state.offset[type] = state.endReached[type] ? null : r.next_offset; + +// state.updateBalance(r.balance); + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.botStarsTransactionsLoaded, bot_id); + } + })); + } + + public boolean isLoadingTransactions(long bot_id, int type) { + final TransactionsState state = getTransactionsState(bot_id); + return state.loading[type]; + } + + public boolean didFullyLoadTransactions(long bot_id, int type) { + final TransactionsState state = getTransactionsState(bot_id); + return state.endReached[type]; + } + + public boolean hasTransactions(long bot_id) { + return hasTransactions(bot_id, ALL_TRANSACTIONS); + } + + public boolean hasTransactions(long bot_id, int type) { + final TransactionsState state = getTransactionsState(bot_id); + return !state.transactions[type].isEmpty(); + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsController.java b/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsController.java index 0a32655738..a8ddd51094 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsController.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsController.java @@ -19,6 +19,8 @@ import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.BillingController; import org.telegram.messenger.BuildVars; +import org.telegram.messenger.DialogObject; +import org.telegram.messenger.FileRefController; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; @@ -94,7 +96,7 @@ public long getBalance(Runnable loaded) { MessagesController.getInstance(currentAccount).putChats(r.chats, false); if (transactions[ALL_TRANSACTIONS].isEmpty()) { - for (TLRPC.TL_starsTransaction t : r.history) { + for (TLRPC.StarsTransaction t : r.history) { transactions[ALL_TRANSACTIONS].add(t); transactions[t.stars > 0 ? INCOMING_TRANSACTIONS : OUTGOING_TRANSACTIONS].add(t); } @@ -155,6 +157,10 @@ private static boolean isCollapsed(long stars) { private boolean optionsLoading, optionsLoaded; private ArrayList options; + public ArrayList getOptionsCached() { + return options; + } + public ArrayList getOptions() { if (optionsLoading || optionsLoaded) { return options; @@ -243,7 +249,7 @@ private void bulletinError(String err) { public static final int INCOMING_TRANSACTIONS = 1; public static final int OUTGOING_TRANSACTIONS = 2; - public final ArrayList[] transactions = new ArrayList[] { new ArrayList<>(), new ArrayList<>(), new ArrayList<>() }; + public final ArrayList[] transactions = new ArrayList[] { new ArrayList<>(), new ArrayList<>(), new ArrayList<>() }; public final boolean[] transactionsExist = new boolean[3]; private final String[] offset = new String[3]; private final boolean[] loading = new boolean[3]; @@ -447,25 +453,27 @@ public void buy(Activity activity, TLRPC.TL_starsTopupOption option, Utilities.C })); } - public void pay(MessageObject messageObject, Runnable whenShown) { + public Runnable pay(MessageObject messageObject, Runnable whenShown) { final Context context = LaunchActivity.instance != null ? LaunchActivity.instance : ApplicationLoader.applicationContext; final Theme.ResourcesProvider resourcesProvider = getResourceProvider(); - if (context == null) { - return; + if (messageObject == null || context == null) { + return null; } - if (!(MessageObject.getMedia(messageObject) instanceof TLRPC.TL_messageMediaInvoice)) { - return; - } +// if (!(MessageObject.getMedia(messageObject) instanceof TLRPC.TL_messageMediaInvoice)) { +// return; +// } - if (whenShown != null) { - whenShown.run(); - } + long did = messageObject.getDialogId(); + int msg_id = messageObject.getId(); +// if (messageObject.messageOwner != null && messageObject.messageOwner.fwd_from != null && messageObject.messageOwner.fwd_from.from_id != null) { +// did = DialogObject.getPeerDialogId(messageObject.messageOwner.fwd_from.from_id); +// } TLRPC.TL_inputInvoiceMessage inputInvoice = new TLRPC.TL_inputInvoiceMessage(); - inputInvoice.peer = MessagesController.getInstance(currentAccount).getInputPeer(messageObject.getDialogId()); - inputInvoice.msg_id = messageObject.getId(); + inputInvoice.peer = MessagesController.getInstance(currentAccount).getInputPeer(did); + inputInvoice.msg_id = msg_id; TLRPC.TL_payments_getPaymentForm req = new TLRPC.TL_payments_getPaymentForm(); final JSONObject themeParams = BotWebViewSheet.makeThemeParams(resourcesProvider); @@ -476,9 +484,9 @@ public void pay(MessageObject messageObject, Runnable whenShown) { } req.invoice = inputInvoice; - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + final int reqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { if (res instanceof TLRPC.TL_payments_paymentFormStars) { - openPaymentForm(inputInvoice, (TLRPC.TL_payments_paymentFormStars) res, whenShown, null); + openPaymentForm(messageObject, inputInvoice, (TLRPC.TL_payments_paymentFormStars) res, whenShown, null); } else { bulletinError(err, "NO_PAYMENT_FORM"); } @@ -486,11 +494,13 @@ public void pay(MessageObject messageObject, Runnable whenShown) { whenShown.run(); } })); + + return () -> ConnectionsManager.getInstance(currentAccount).cancelRequest(reqId, true); } private boolean paymentFormOpened; - public void openPaymentForm(TLRPC.InputInvoice inputInvoice, TLRPC.TL_payments_paymentFormStars form, Runnable whenShown, Utilities.Callback whenAllDone) { + public void openPaymentForm(MessageObject messageObject, TLRPC.InputInvoice inputInvoice, TLRPC.TL_payments_paymentFormStars form, Runnable whenShown, Utilities.Callback whenAllDone) { if (form == null || form.invoice == null || paymentFormOpened) { return; } @@ -511,7 +521,7 @@ public void openPaymentForm(TLRPC.InputInvoice inputInvoice, TLRPC.TL_payments_p } return; } - openPaymentForm(inputInvoice, form, whenShown, whenAllDone); + openPaymentForm(messageObject, inputInvoice, form, whenShown, whenAllDone); }); return; } @@ -536,7 +546,7 @@ public void openPaymentForm(TLRPC.InputInvoice inputInvoice, TLRPC.TL_payments_p } final boolean[] allDone = new boolean[] { false }; - StarsIntroActivity.openConfirmPurchaseSheet(context, resourcesProvider, currentAccount, dialogId, product, stars, form.photo, whenDone -> { + StarsIntroActivity.openConfirmPurchaseSheet(context, resourcesProvider, currentAccount, messageObject, dialogId, product, stars, form.photo, whenDone -> { if (balance < stars) { if (!MessagesController.getInstance(currentAccount).starsPurchaseAvailable()) { paymentFormOpened = false; @@ -553,7 +563,7 @@ public void openPaymentForm(TLRPC.InputInvoice inputInvoice, TLRPC.TL_payments_p final boolean[] purchased = new boolean[] { false }; StarsIntroActivity.StarsNeededSheet sheet = new StarsIntroActivity.StarsNeededSheet(context, resourcesProvider, stars, bot, () -> { purchased[0] = true; - payAfterConfirmed(inputInvoice, form, success -> { + payAfterConfirmed(messageObject, inputInvoice, form, success -> { allDone[0] = true; if (whenAllDone != null) { whenAllDone.run(success ? "paid" : "failed"); @@ -575,7 +585,7 @@ public void openPaymentForm(TLRPC.InputInvoice inputInvoice, TLRPC.TL_payments_p }); sheet.show(); } else { - payAfterConfirmed(inputInvoice, form, success -> { + payAfterConfirmed(messageObject, inputInvoice, form, success -> { if (whenDone != null) { whenDone.run(true); } @@ -602,7 +612,7 @@ private void showNoSupportDialog(Context context, Theme.ResourcesProvider resour .show(); } - private void payAfterConfirmed(TLRPC.InputInvoice inputInvoice, TLRPC.TL_payments_paymentFormStars form, Utilities.Callback whenDone) { + private void payAfterConfirmed(MessageObject messageObject, TLRPC.InputInvoice inputInvoice, TLRPC.TL_payments_paymentFormStars form, Utilities.Callback whenDone) { if (form == null) { return; } @@ -619,7 +629,16 @@ private void payAfterConfirmed(TLRPC.InputInvoice inputInvoice, TLRPC.TL_payment _stars += price.amount; } final long stars = _stars; - final long dialogId = form.bot_id; + final long dialogId; + if (messageObject != null) { + if (messageObject.messageOwner != null && messageObject.messageOwner.fwd_from != null && messageObject.messageOwner.fwd_from.from_id != null) { + dialogId = DialogObject.getPeerDialogId(messageObject.messageOwner.fwd_from.from_id); + } else { + dialogId = messageObject.getDialogId(); + } + } else { + dialogId = form.bot_id; + } final String bot; if (dialogId >= 0) { bot = UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(dialogId)); @@ -644,10 +663,22 @@ private void payAfterConfirmed(TLRPC.InputInvoice inputInvoice, TLRPC.TL_payment TLRPC.TL_payments_paymentResult result = (TLRPC.TL_payments_paymentResult) res2; MessagesController.getInstance(currentAccount).processUpdates(result.updates, false); + final boolean media = messageObject != null && messageObject.messageOwner != null && messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia; Drawable starDrawable = context.getResources().getDrawable(R.drawable.star_small_inner).mutate(); - b.createSimpleBulletin(starDrawable, getString(R.string.StarsPurchaseCompleted), AndroidUtilities.replaceTags(formatPluralString("StarsPurchaseCompletedInfo", (int) stars, product, bot))).show(); + if (media) { + b.createSimpleBulletin(starDrawable, getString(R.string.StarsMediaPurchaseCompleted), AndroidUtilities.replaceTags(formatPluralString("StarsMediaPurchaseCompletedInfo", (int) stars, bot))).show(); + } else { + b.createSimpleBulletin(starDrawable, getString(R.string.StarsPurchaseCompleted), AndroidUtilities.replaceTags(formatPluralString("StarsPurchaseCompletedInfo", (int) stars, product, bot))).show(); + } invalidateTransactions(true); + + if (messageObject != null) { + TLRPC.TL_messages_getExtendedMedia req = new TLRPC.TL_messages_getExtendedMedia(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); + req.id.add(messageObject.getId()); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, null); + } } else if (err2 != null && "BALANCE_TOO_LOW".equals(err2.text)) { if (!MessagesController.getInstance(currentAccount).starsPurchaseAvailable()) { if (whenDone != null) { @@ -659,7 +690,7 @@ private void payAfterConfirmed(TLRPC.InputInvoice inputInvoice, TLRPC.TL_payment final boolean[] purchased = new boolean[] { false }; StarsIntroActivity.StarsNeededSheet sheet = new StarsIntroActivity.StarsNeededSheet(context, resourcesProvider, stars, bot, () -> { purchased[0] = true; - payAfterConfirmed(inputInvoice, form, success -> { + payAfterConfirmed(messageObject, inputInvoice, form, success -> { if (whenDone != null) { whenDone.run(success); } @@ -682,7 +713,7 @@ private void payAfterConfirmed(TLRPC.InputInvoice inputInvoice, TLRPC.TL_payment req.invoice = inputInvoice; ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res3, err3) -> AndroidUtilities.runOnUIThread(() -> { if (res3 instanceof TLRPC.TL_payments_paymentFormStars) { - payAfterConfirmed(inputInvoice, (TLRPC.TL_payments_paymentFormStars) res3, whenDone); + payAfterConfirmed(messageObject, inputInvoice, (TLRPC.TL_payments_paymentFormStars) res3, whenDone); } else { if (whenDone != null) { whenDone.run(false); @@ -695,6 +726,96 @@ private void payAfterConfirmed(TLRPC.InputInvoice inputInvoice, TLRPC.TL_payment whenDone.run(false); } b.createSimpleBulletin(R.raw.error, LocaleController.formatString(R.string.UnknownErrorCode, err2 != null ? err2.text : "FAILED_SEND_STARS")).show(); + + if (messageObject != null) { + TLRPC.TL_messages_getExtendedMedia req = new TLRPC.TL_messages_getExtendedMedia(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialogId); + req.id.add(messageObject.getId()); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, null); + } + } + })); + } + + public void updateMediaPrice(MessageObject msg, long price, Runnable done) { + updateMediaPrice(msg, price, done, false); + } + + private void updateMediaPrice(MessageObject msg, long price, Runnable done, boolean afterFileRef) { + if (msg == null) { + done.run(); + return; + } + + final long dialog_id = msg.getDialogId(); + final int msg_id = msg.getId(); + + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) msg.messageOwner.media; + + TLRPC.TL_messages_editMessage req = new TLRPC.TL_messages_editMessage(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialog_id); + req.flags |= 32768; + req.schedule_date = msg.messageOwner.date; + req.id = msg_id; + req.flags |= 16384; + + TLRPC.TL_inputMediaPaidMedia media = new TLRPC.TL_inputMediaPaidMedia(); + media.stars_amount = price; + for (int i = 0; i < paidMedia.extended_media.size(); ++i) { + TLRPC.MessageExtendedMedia emedia = paidMedia.extended_media.get(i); + if (!(emedia instanceof TLRPC.TL_messageExtendedMedia)) { + done.run(); + return; + } + TLRPC.MessageMedia imedia = ((TLRPC.TL_messageExtendedMedia) emedia).media; + if (imedia instanceof TLRPC.TL_messageMediaPhoto) { + TLRPC.TL_messageMediaPhoto mediaPhoto = (TLRPC.TL_messageMediaPhoto) imedia; + TLRPC.TL_inputMediaPhoto inputMedia = new TLRPC.TL_inputMediaPhoto(); + TLRPC.TL_inputPhoto photo = new TLRPC.TL_inputPhoto(); + photo.id = mediaPhoto.photo.id; + photo.access_hash = mediaPhoto.photo.access_hash; + photo.file_reference = mediaPhoto.photo.file_reference; + inputMedia.id = photo; + media.extended_media.add(inputMedia); + } else if (imedia instanceof TLRPC.TL_messageMediaDocument) { + TLRPC.TL_messageMediaDocument mediaDocument = (TLRPC.TL_messageMediaDocument) imedia; + TLRPC.TL_inputMediaDocument inputMedia = new TLRPC.TL_inputMediaDocument(); + TLRPC.TL_inputDocument doc = new TLRPC.TL_inputDocument(); + doc.id = mediaDocument.document.id; + doc.access_hash = mediaDocument.document.access_hash; + doc.file_reference = mediaDocument.document.file_reference; + inputMedia.id = doc; + media.extended_media.add(inputMedia); + } + } + req.media = media; + + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TLRPC.Updates) { + MessagesController.getInstance(currentAccount).processUpdates((TLRPC.Updates) res, false); + done.run(); + } else if (err != null && FileRefController.isFileRefError(err.text) && !afterFileRef) { + TLRPC.TL_messages_getScheduledMessages req2 = new TLRPC.TL_messages_getScheduledMessages(); + req2.peer = MessagesController.getInstance(currentAccount).getInputPeer(dialog_id); + req2.id.add(msg_id); + ConnectionsManager.getInstance(currentAccount).sendRequest(req2, (res2, err2) -> AndroidUtilities.runOnUIThread(() -> { + if (res2 instanceof TLRPC.TL_messages_messages) { + TLRPC.TL_messages_messages m = (TLRPC.TL_messages_messages) res2; + MessagesController.getInstance(currentAccount).putUsers(m.users, false); + MessagesController.getInstance(currentAccount).putChats(m.chats, false); + + if (m.messages.size() == 1 && m.messages.get(0) instanceof TLRPC.TL_message && m.messages.get(0).media instanceof TLRPC.TL_messageMediaPaidMedia) { + msg.messageOwner = m.messages.get(0); + updateMediaPrice(msg, price, done, true); + } else { + done.run(); + } + } else { + done.run(); + } + })); + } else { + done.run(); } })); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsIntroActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsIntroActivity.java index 5efc9628f6..7e712d1863 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsIntroActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stars/StarsIntroActivity.java @@ -13,22 +13,32 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.RectF; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.text.Editable; +import android.text.InputType; import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextPaint; import android.text.TextUtils; +import android.text.TextWatcher; import android.text.style.ClickableSpan; import android.text.style.ImageSpan; import android.text.style.ReplacementSpan; import android.util.TypedValue; import android.view.Gravity; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.OvershootInterpolator; +import android.view.inputmethod.EditorInfo; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; @@ -44,12 +54,17 @@ import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import org.checkerframework.checker.units.qual.Angle; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.BillingController; +import org.telegram.messenger.ChatObject; import org.telegram.messenger.DialogObject; +import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; @@ -64,6 +79,8 @@ import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.AvatarSpan; +import org.telegram.ui.Cells.ChatActionCell; +import org.telegram.ui.Cells.ChatMessageCell; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.SessionCell; import org.telegram.ui.ChatActivity; @@ -76,11 +93,13 @@ import org.telegram.ui.Components.ColoredImageSpan; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.EditTextBoldCursor; import org.telegram.ui.Components.FireworksOverlay; import org.telegram.ui.Components.FlickerLoadingView; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LinkSpanDrawable; import org.telegram.ui.Components.LoadingSpan; +import org.telegram.ui.Components.OutlineTextContainerView; import org.telegram.ui.Components.Premium.GLIcon.GLIconRenderer; import org.telegram.ui.Components.Premium.GLIcon.GLIconTextureView; import org.telegram.ui.Components.Premium.GLIcon.Icon3D; @@ -93,11 +112,16 @@ import org.telegram.ui.Components.UniversalAdapter; import org.telegram.ui.Components.UniversalRecyclerView; import org.telegram.ui.Components.ViewPagerFixed; +import org.telegram.ui.Components.spoilers.SpoilerEffect2; import org.telegram.ui.GradientHeaderActivity; +import org.telegram.ui.ImageReceiverSpan; import org.telegram.ui.LaunchActivity; +import org.telegram.ui.PhotoViewer; import org.telegram.ui.Stories.recorder.ButtonWithCounterView; import org.telegram.ui.Stories.recorder.HintView2; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -155,7 +179,7 @@ public void didReceivedNotification(int id, int account, Object... args) { public View createView(Context context) { useFillLastLayoutManager = false; particlesViewHeight = dp(32 + 190 + 16); - transactionsLayout = new StarsTransactionsLayout(context, currentAccount, getClassGuid(), getResourceProvider()); + transactionsLayout = new StarsTransactionsLayout(context, currentAccount, 0, getClassGuid(), getResourceProvider()); emptyLayout = new View(context) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @@ -828,6 +852,7 @@ public static class StarsTransactionsLayout extends LinearLayout implements Noti private final ViewPagerFixed viewPager; private final PageAdapter adapter; private final ViewPagerFixed.TabsView tabsView; + private final long bot_id; private static class PageAdapter extends ViewPagerFixed.Adapter { @@ -835,12 +860,14 @@ private static class PageAdapter extends ViewPagerFixed.Adapter { private final int currentAccount; private final int classGuid; private final Theme.ResourcesProvider resourcesProvider; + private final long bot_id; - public PageAdapter(Context context, int currentAccount, int classGuid, Theme.ResourcesProvider resourcesProvider) { + public PageAdapter(Context context, int currentAccount, long bot_id, int classGuid, Theme.ResourcesProvider resourcesProvider) { this.context = context; this.currentAccount = currentAccount; this.classGuid = classGuid; this.resourcesProvider = resourcesProvider; + this.bot_id = bot_id; fill(); } @@ -848,13 +875,24 @@ public PageAdapter(Context context, int currentAccount, int classGuid, Theme.Res public void fill() { items.clear(); - StarsController s = StarsController.getInstance(currentAccount); - items.add(UItem.asSpace(StarsController.ALL_TRANSACTIONS)); - if (s.hasTransactions(StarsController.INCOMING_TRANSACTIONS)) { - items.add(UItem.asSpace(StarsController.INCOMING_TRANSACTIONS)); - } - if (s.hasTransactions(StarsController.OUTGOING_TRANSACTIONS)) { - items.add(UItem.asSpace(StarsController.OUTGOING_TRANSACTIONS)); + if (bot_id == 0) { + StarsController s = StarsController.getInstance(currentAccount); + items.add(UItem.asSpace(StarsController.ALL_TRANSACTIONS)); + if (s.hasTransactions(StarsController.INCOMING_TRANSACTIONS)) { + items.add(UItem.asSpace(StarsController.INCOMING_TRANSACTIONS)); + } + if (s.hasTransactions(StarsController.OUTGOING_TRANSACTIONS)) { + items.add(UItem.asSpace(StarsController.OUTGOING_TRANSACTIONS)); + } + } else { + BotStarsController s = BotStarsController.getInstance(currentAccount); + items.add(UItem.asSpace(StarsController.ALL_TRANSACTIONS)); + if (s.hasTransactions(bot_id, BotStarsController.INCOMING_TRANSACTIONS)) { + items.add(UItem.asSpace(BotStarsController.INCOMING_TRANSACTIONS)); + } + if (s.hasTransactions(bot_id, BotStarsController.OUTGOING_TRANSACTIONS)) { + items.add(UItem.asSpace(BotStarsController.OUTGOING_TRANSACTIONS)); + } } } @@ -865,7 +903,7 @@ public int getItemCount() { @Override public View createView(int viewType) { - return new Page(context, viewType, currentAccount, classGuid, resourcesProvider); + return new Page(context, bot_id, viewType, currentAccount, classGuid, resourcesProvider); } @Override @@ -898,14 +936,15 @@ public void didReceivedNotification(int id, int account, Object... args) { } } - public StarsTransactionsLayout(Context context, int currentAccount, int classGuid, Theme.ResourcesProvider resourcesProvider) { + public StarsTransactionsLayout(Context context, int currentAccount, long bot_id, int classGuid, Theme.ResourcesProvider resourcesProvider) { super(context); this.currentAccount = currentAccount; + this.bot_id = bot_id; setOrientation(VERTICAL); viewPager = new ViewPagerFixed(context); - viewPager.setAdapter(adapter = new PageAdapter(context, currentAccount, classGuid, resourcesProvider)); + viewPager.setAdapter(adapter = new PageAdapter(context, currentAccount, bot_id, classGuid, resourcesProvider)); tabsView = viewPager.createTabsView(true, 3); View separatorView = new View(context); @@ -944,14 +983,24 @@ public static class Page extends FrameLayout implements NotificationCenter.Notif private final Theme.ResourcesProvider resourcesProvider; private final int currentAccount; private final int type; + private final long bot_id; - public Page(Context context, int type, int currentAccount, int classGuid, Theme.ResourcesProvider resourcesProvider) { + public Page(Context context, long bot_id, int type, int currentAccount, int classGuid, Theme.ResourcesProvider resourcesProvider) { super(context); this.type = type; this.currentAccount = currentAccount; + this.bot_id = bot_id; this.resourcesProvider = resourcesProvider; + loadTransactionsRunnable = () -> { + if (bot_id != 0) { + BotStarsController.getInstance(currentAccount).loadTransactions(bot_id, type); + } else { + StarsController.getInstance(currentAccount).loadTransactions(type); + } + }; + listView = new UniversalRecyclerView(context, currentAccount, classGuid, true, this::fillItems, this::onClick, null, resourcesProvider); addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); listView.setOnScrollListener(new RecyclerView.OnScrollListener() { @@ -962,9 +1011,7 @@ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { }); } - private final Runnable loadTransactionsRunnable = () -> { - StarsController.getInstance(Page.this.currentAccount).loadTransactions(Page.this.type); - }; + private final Runnable loadTransactionsRunnable; private void scheduleLoadTransactions() { if (!Page.this.listView.canScrollVertically(1)) { @@ -977,37 +1024,61 @@ private void scheduleLoadTransactions() { public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.starTransactionsLoaded) { listView.adapter.update(true); + } else if (id == NotificationCenter.botStarsTransactionsLoaded) { + if ((long) args[0] == bot_id) { + listView.adapter.update(true); + } } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.starTransactionsLoaded); + if (bot_id != 0) { + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.botStarsTransactionsLoaded); + } else { + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.starTransactionsLoaded); + } listView.adapter.update(false); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.starTransactionsLoaded); + if (bot_id != 0) { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.botStarsTransactionsLoaded); + } else { + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.starTransactionsLoaded); + } } private void fillItems(ArrayList items, UniversalAdapter adapter) { - final StarsController c = StarsController.getInstance(currentAccount); - for (TLRPC.TL_starsTransaction t : c.transactions[type]) { - items.add(StarsTransactionView.Factory.asTransaction(t)); - } - if (!c.didFullyLoadTransactions(type)) { - items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); - items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); - items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + if (bot_id != 0) { + final BotStarsController c = BotStarsController.getInstance(currentAccount); + for (TLRPC.StarsTransaction t : c.getTransactions(bot_id, type)) { + items.add(StarsTransactionView.Factory.asTransaction(t, true)); + } + if (!c.didFullyLoadTransactions(bot_id, type)) { + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + } + } else { + final StarsController c = StarsController.getInstance(currentAccount); + for (TLRPC.StarsTransaction t : c.transactions[type]) { + items.add(StarsTransactionView.Factory.asTransaction(t, false)); + } + if (!c.didFullyLoadTransactions(type)) { + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + items.add(UItem.asFlicker(items.size(), FlickerLoadingView.DIALOG_CELL_TYPE)); + } } } private void onClick(UItem item, View view, int position, float x, float y) { - if (item.object instanceof TLRPC.TL_starsTransaction) { - showTransactionSheet(getContext(), currentAccount, (TLRPC.TL_starsTransaction) item.object, resourcesProvider); + if (item.object instanceof TLRPC.StarsTransaction) { + showTransactionSheet(getContext(), false, 0, currentAccount, (TLRPC.StarsTransaction) item.object, resourcesProvider); } } @@ -1042,7 +1113,10 @@ public static class StarsTransactionView extends LinearLayout { private final int currentAccount; private final AvatarDrawable avatarDrawable; + private final FrameLayout imageViewContainer; private final BackupImageView imageView; + private final BackupImageView imageView2; + private int imageViewCount = 1; private final LinearLayout textLayout; private final TextView titleTextView; private final LinearLayout.LayoutParams titleTextViewParams; @@ -1061,10 +1135,29 @@ public StarsTransactionView(Context context, int currentAccount, Theme.Resources setOrientation(HORIZONTAL); + imageViewContainer = new FrameLayout(context) { + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (imageViewCount > 1) { + backgroundPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + AndroidUtilities.rectTmp.set(child.getX(), child.getY(), child.getX() + child.getWidth(), child.getY() + child.getHeight()); + AndroidUtilities.rectTmp.inset(-dp(1.66f), -dp(1.66f)); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(13), dp(13), backgroundPaint); + } + return super.drawChild(canvas, child, drawingTime); + } + }; + addView(imageViewContainer, LayoutHelper.createLinear(72, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.FILL_VERTICAL)); + + imageView2 = new BackupImageView(context); + imageView2.setRoundRadius(dp(46)); + imageViewContainer.addView(imageView2, LayoutHelper.createFrame(46, 46, Gravity.CENTER_VERTICAL, 13, 0, 13, 0)); + avatarDrawable = new AvatarDrawable(); imageView = new BackupImageView(context); imageView.setRoundRadius(dp(46)); - addView(imageView, LayoutHelper.createLinear(46, 46, Gravity.CENTER_VERTICAL, 13, 0, 13, 0)); + imageViewContainer.addView(imageView, LayoutHelper.createFrame(46, 46, Gravity.CENTER_VERTICAL, 13, 0, 13, 0)); textLayout = new LinearLayout(context); textLayout.setOrientation(VERTICAL); @@ -1112,7 +1205,7 @@ public static Drawable getPlatformDrawable(String platform) { } private boolean needDivider; - public void set(TLRPC.TL_starsTransaction transaction, boolean divider) { + public void set(TLRPC.StarsTransaction transaction, boolean bot, boolean divider) { long did = DialogObject.getPeerDialogId(transaction.peer.peer); threeLines = did != 0; @@ -1120,11 +1213,25 @@ public void set(TLRPC.TL_starsTransaction transaction, boolean divider) { subtitleTextView.setVisibility(threeLines ? View.VISIBLE : View.GONE); dateTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, threeLines ? 13 : 14); - dateTextView.setText(LocaleController.formatShortDateTime(transaction.date) + (transaction.refund ? " — " + getString(R.string.StarsRefunded) : "")); + dateTextView.setText(LocaleController.formatShortDateTime(transaction.date)); + if (transaction.refund) { + dateTextView.setText(TextUtils.concat(dateTextView.getText(), " — ", getString(R.string.StarsRefunded))); + } else if (transaction.failed) { + dateTextView.setText(TextUtils.concat(dateTextView.getText(), " — ", getString(R.string.StarsFailed))); + } else if (transaction.pending) { + dateTextView.setText(TextUtils.concat(dateTextView.getText(), " — ", getString(R.string.StarsPending))); + } + + imageView.setTranslationX(0); + imageView.setTranslationY(0); + imageView2.setVisibility(GONE); + imageView.setRoundRadius(dp(46)); if (did != 0) { + boolean deleted = false; String username; if (did >= 0) { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); + deleted = user == null; if (transaction.photo == null) { avatarDrawable.setInfo(user); imageView.setForUserOrChat(user, avatarDrawable); @@ -1132,16 +1239,53 @@ public void set(TLRPC.TL_starsTransaction transaction, boolean divider) { username = UserObject.getUserName(user); } else { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); + deleted = chat == null; if (transaction.photo == null) { avatarDrawable.setInfo(chat); imageView.setForUserOrChat(chat, avatarDrawable); } username = chat == null ? "" : chat.title; } - titleTextView.setText(transaction.title != null ? transaction.title : ""); - subtitleTextView.setText(username); - if (transaction.photo != null) { + if (!transaction.extended_media.isEmpty()) { + if (bot) { + titleTextView.setText(username); + subtitleTextView.setVisibility(VISIBLE); + subtitleTextView.setText(LocaleController.getString(R.string.StarMediaPurchase)); + } else { + titleTextView.setText(LocaleController.getString(R.string.StarMediaPurchase)); + subtitleTextView.setVisibility(deleted ? GONE : VISIBLE); + subtitleTextView.setText(username); + } + imageViewCount = 0; + for (int i = 0; i < Math.min(2, transaction.extended_media.size()); ++i) { + TLRPC.MessageMedia media = transaction.extended_media.get(i); + BackupImageView imageView = i == 0 ? this.imageView : this.imageView2; + + imageView.setRoundRadius(dp(12)); + ImageLocation location = null; + if (media instanceof TLRPC.TL_messageMediaPhoto) { + location = ImageLocation.getForPhoto(FileLoader.getClosestPhotoSizeWithSize(media.photo.sizes, dp(46), true), media.photo); + } else if (media instanceof TLRPC.TL_messageMediaDocument) { + location = ImageLocation.getForDocument(FileLoader.getClosestPhotoSizeWithSize(media.document.thumbs, dp(46), true), media.document); + } + imageView.setVisibility(View.VISIBLE); + imageView.setImage(location, "46_46", null, null, null, 0); + imageViewCount++; + } + for (int i = 0; i < imageViewCount; ++i) { + BackupImageView imageView = i == 0 ? this.imageView : this.imageView2; + imageView.setTranslationX(dp(2) + (i - imageViewCount / 2f) * dp(4.33f)); + imageView.setTranslationY((i - imageViewCount / 2f) * dp(4.33f)); + } + } else if (transaction.photo != null) { + titleTextView.setText(transaction.title != null ? transaction.title : ""); + subtitleTextView.setVisibility(deleted ? GONE : VISIBLE); + subtitleTextView.setText(username); imageView.setImage(ImageLocation.getForWebFile(WebFile.createWithWebDocument(transaction.photo)), "46_46", null, 0, null); + } else { + titleTextView.setText(transaction.title != null ? transaction.title : ""); + subtitleTextView.setVisibility(deleted ? GONE : VISIBLE); + subtitleTextView.setText(username); } } else if (transaction.peer instanceof TLRPC.TL_starsTransactionPeerAppStore) { titleTextView.setText(getString(R.string.StarsTransactionInApp)); @@ -1150,7 +1294,7 @@ public void set(TLRPC.TL_starsTransaction transaction, boolean divider) { titleTextView.setText(getString(R.string.StarsTransactionInApp)); imageView.setImageDrawable(getPlatformDrawable("android")); } else if (transaction.peer instanceof TLRPC.TL_starsTransactionPeerFragment) { - titleTextView.setText(getString(R.string.StarsTransactionFragment)); + titleTextView.setText(getString(bot ? R.string.StarsTransactionWithdrawFragment : R.string.StarsTransactionFragment)); imageView.setImageDrawable(getPlatformDrawable("fragment")); } else if (transaction.peer instanceof TLRPC.TL_starsTransactionPeerPremiumBot) { titleTextView.setText(getString(R.string.StarsTransactionBot)); @@ -1158,6 +1302,9 @@ public void set(TLRPC.TL_starsTransaction transaction, boolean divider) { } else if (transaction.peer instanceof TLRPC.TL_starsTransactionPeerUnsupported) { titleTextView.setText(getString(R.string.StarsTransactionUnsupported)); imageView.setImageDrawable(getPlatformDrawable("?")); + } else if (transaction.peer instanceof TLRPC.TL_starsTransactionPeerAds) { + titleTextView.setText(getString(R.string.StarsTransactionAds)); + imageView.setImageDrawable(getPlatformDrawable("ads")); } else { titleTextView.setText(""); imageView.setImageDrawable(null); @@ -1206,12 +1353,13 @@ public StarsTransactionView createView(Context context, int currentAccount, int @Override public void bindView(View view, UItem item, boolean divider) { - ((StarsTransactionView) view).set((TLRPC.TL_starsTransaction) item.object, divider); + ((StarsTransactionView) view).set((TLRPC.StarsTransaction) item.object, item.accent, divider); } - public static UItem asTransaction(TLRPC.TL_starsTransaction transaction) { + public static UItem asTransaction(TLRPC.StarsTransaction transaction, boolean bot) { UItem item = UItem.ofFactory(StarsTransactionView.Factory.class); item.object = transaction; + item.accent = bot; return item; } } @@ -1221,6 +1369,7 @@ public static BottomSheet openConfirmPurchaseSheet( Context context, Theme.ResourcesProvider resourcesProvider, int currentAccount, + MessageObject messageObject, long userId, String purchase, long stars, @@ -1239,21 +1388,82 @@ public static BottomSheet openConfirmPurchaseSheet( FrameLayout topView = new FrameLayout(context); topView.addView(makeParticlesView(context, 40, 0), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - BackupImageView imageView = new BackupImageView(context); - if (photo == null) { + if (messageObject != null && messageObject.messageOwner != null && messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia) { + BackupImageView imageView = new BackupImageView(context) { + private SpoilerEffect2 spoilerEffect2; + private Path clipPath = new Path(); + private RectF clipRect = new RectF(); + private Drawable lock = context.getResources().getDrawable(R.drawable.large_locked_post).mutate(); + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + if (spoilerEffect2 == null) { + spoilerEffect2 = SpoilerEffect2.getInstance(this); + } + if (spoilerEffect2 != null) { + clipRect.set(0,0,getWidth(),getHeight()); + clipPath.rewind(); + clipPath.addRoundRect(clipRect, dp(24), dp(24), Path.Direction.CW); + canvas.save(); + canvas.clipPath(clipPath); + spoilerEffect2.draw(canvas, this, getWidth(), getHeight(), 1f); + canvas.restore(); + } + lock.setBounds((getWidth()-lock.getIntrinsicWidth())/2, (getHeight()-lock.getIntrinsicHeight())/2, (getWidth()+lock.getIntrinsicWidth())/2, (getHeight()+lock.getIntrinsicHeight())/2); + lock.draw(canvas); + } + @Override + protected void onAttachedToWindow() { + if (spoilerEffect2 != null) { + spoilerEffect2.attach(this); + } + super.onAttachedToWindow(); + } + @Override + protected void onDetachedFromWindow() { + if (spoilerEffect2 != null) { + spoilerEffect2.detach(this); + } + super.onDetachedFromWindow(); + } + }; + imageView.setRoundRadius(dp(24)); + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) messageObject.messageOwner.media; + if (!paidMedia.extended_media.isEmpty()) { + TLRPC.MessageExtendedMedia extMedia = paidMedia.extended_media.get(0); + ImageLocation location = null; + if (extMedia instanceof TLRPC.TL_messageExtendedMediaPreview) { + TLRPC.TL_messageExtendedMediaPreview m = (TLRPC.TL_messageExtendedMediaPreview) extMedia; + location = ImageLocation.getForObject(m.thumb, messageObject.messageOwner); + } else if (extMedia instanceof TLRPC.TL_messageExtendedMedia) { + TLRPC.MessageMedia media = ((TLRPC.TL_messageExtendedMedia) extMedia).media; + if (media instanceof TLRPC.TL_messageMediaPhoto) { + location = ImageLocation.getForPhoto(FileLoader.getClosestPhotoSizeWithSize(media.photo.sizes, dp(80), true), media.photo); + } else if (media instanceof TLRPC.TL_messageMediaDocument) { + location = ImageLocation.getForDocument(FileLoader.getClosestPhotoSizeWithSize(media.document.thumbs, dp(80), true), media.document); + } + } + imageView.setImage(location, "80_80_b2", null, null, null, messageObject); + } + topView.addView(imageView, LayoutHelper.createFrame(80, 80, Gravity.CENTER)); + } else if (photo == null) { + BackupImageView imageView = new BackupImageView(context); imageView.setRoundRadius(dp(80)); AvatarDrawable avatarDrawable = new AvatarDrawable(); avatarDrawable.setInfo(user); imageView.setForUserOrChat(user, avatarDrawable); + topView.addView(imageView, LayoutHelper.createFrame(80, 80, Gravity.CENTER)); } else { + BackupImageView imageView = new BackupImageView(context); imageView.setRoundRadius(dp(80)); imageView.setImage(ImageLocation.getForWebFile(WebFile.createWithWebDocument(photo)), "80_80", null, 0, null); + topView.addView(imageView, LayoutHelper.createFrame(80, 80, Gravity.CENTER)); } - topView.addView(imageView, LayoutHelper.createFrame(80, 80, Gravity.CENTER)); StarsBalanceView balanceView = new StarsBalanceView(context, currentAccount); ScaleStateListAnimator.apply(balanceView); balanceView.setOnClickListener(v -> { + if (balanceView.lastBalance <= 0) return; BaseFragment lastFragment = LaunchActivity.getLastFragment(); if (lastFragment != null) { BaseFragment.BottomSheetParams bottomSheetParams = new BaseFragment.BottomSheetParams(); @@ -1277,7 +1487,47 @@ public static BottomSheet openConfirmPurchaseSheet( TextView subtitleView = new TextView(context); subtitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); subtitleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); - subtitleView.setText(AndroidUtilities.replaceTags(formatPluralStringComma("StarsConfirmPurchaseText", (int) stars, purchase, UserObject.getUserName(user)))); + if (messageObject != null && messageObject.messageOwner != null && messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPaidMedia) { + long did = messageObject.getDialogId(); + if (messageObject.messageOwner != null && messageObject.messageOwner.fwd_from != null && messageObject.messageOwner.fwd_from.from_id != null) { + did = DialogObject.getPeerDialogId(messageObject.messageOwner.fwd_from.from_id); + } + final String chatTitle; + if (did >= 0) { + chatTitle = UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(did)); + } else { + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); + chatTitle = chat == null ? "" : chat.title; + } + + int photosCount = 0, videosCount = 0; + TLRPC.TL_messageMediaPaidMedia paidMedia = (TLRPC.TL_messageMediaPaidMedia) messageObject.messageOwner.media; + for (int i = 0; i < paidMedia.extended_media.size(); ++i) { + TLRPC.MessageExtendedMedia extMedia = paidMedia.extended_media.get(i); + boolean isVideo = false; + if (extMedia instanceof TLRPC.TL_messageExtendedMediaPreview) { + TLRPC.TL_messageExtendedMediaPreview m = (TLRPC.TL_messageExtendedMediaPreview) extMedia; + isVideo = (m.flags & 4) != 0; + } else if (extMedia instanceof TLRPC.TL_messageExtendedMedia) { + TLRPC.MessageMedia media = ((TLRPC.TL_messageExtendedMedia) extMedia).media; + isVideo = media instanceof TLRPC.TL_messageMediaDocument; + } + if (isVideo) videosCount++; + else photosCount++; + } + + String c; + if (videosCount == 0) { + c = formatString(R.string.StarsConfirmPurchaseMedia1, photosCount == 1 ? getString(R.string.StarsConfirmPurchaseMedia_SinglePhoto) : formatPluralString("StarsConfirmPurchaseMedia_Photos", photosCount), chatTitle, formatPluralString("Stars", (int) stars)); + } else if (photosCount == 0) { + c = formatString(R.string.StarsConfirmPurchaseMedia1, videosCount == 1 ? getString(R.string.StarsConfirmPurchaseMedia_SingleVideo) : formatPluralString("StarsConfirmPurchaseMedia_Videos", videosCount), chatTitle, formatPluralString("Stars", (int) stars)); + } else { + c = formatString(R.string.StarsConfirmPurchaseMedia2, photosCount == 1 ? getString(R.string.StarsConfirmPurchaseMedia_SinglePhoto) : formatPluralString("StarsConfirmPurchaseMedia_Photos", photosCount), videosCount == 1 ? getString(R.string.StarsConfirmPurchaseMedia_SingleVideo) : formatPluralString("StarsConfirmPurchaseMedia_Videos", videosCount), chatTitle, formatPluralString("Stars", (int) stars)); + } + subtitleView.setText(AndroidUtilities.replaceTags(c)); + } else { + subtitleView.setText(AndroidUtilities.replaceTags(formatPluralStringComma("StarsConfirmPurchaseText", (int) stars, purchase, UserObject.getUserName(user)))); + } subtitleView.setMaxWidth(HintView2.cutInFancyHalf(subtitleView.getText(), subtitleView.getPaint())); subtitleView.setGravity(Gravity.CENTER); linearLayout.addView(subtitleView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 0, 6, 0, 24)); @@ -1375,6 +1625,8 @@ public StarsNeededSheet( ) { super(context, null, false, false, false, resourcesProvider); + topPadding = .2f; + this.whenPurchased = whenPurchased; NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.starOptionsLoaded); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.starBalanceUpdated); @@ -1449,16 +1701,18 @@ public void fillItems(ArrayList items, UniversalAdapter adapter) { if (options != null && !options.isEmpty()) { int count = 0; int hidden = 0; + boolean shownNearest = false; for (int id = 0; id < options.size(); ++id) { TLRPC.TL_starsTopupOption option = options.get(id); if (option.stars < starsNeeded) { continue; } - if (option.collapsed && !expanded) { + if (option.collapsed && !expanded && shownNearest) { hidden++; continue; } items.add(StarTierView.Factory.asStarTier(id, stars++, option)); + shownNearest = true; count++; } if (count > 0) { @@ -1544,6 +1798,7 @@ public HeaderView(Context context, int currentAccount, Theme.ResourcesProvider r balanceView = new StarsBalanceView(context, currentAccount); ScaleStateListAnimator.apply(balanceView); balanceView.setOnClickListener(v -> { + if (balanceView.lastBalance <= 0) return; BaseFragment lastFragment = LaunchActivity.getLastFragment(); if (lastFragment != null) { BaseFragment.BottomSheetParams bottomSheetParams = new BaseFragment.BottomSheetParams(); @@ -1573,6 +1828,10 @@ public HeaderView(Context context, int currentAccount, Theme.ResourcesProvider r } public static SpannableStringBuilder replaceStars(CharSequence cs) { + return replaceStars(cs, 1.13f); + } + + public static SpannableStringBuilder replaceStars(CharSequence cs, final float scale) { if (cs == null) return null; SpannableStringBuilder ssb; if (!(cs instanceof SpannableStringBuilder)) { @@ -1582,7 +1841,36 @@ public static SpannableStringBuilder replaceStars(CharSequence cs) { } SpannableString spacedStar = new SpannableString("⭐ "); ColoredImageSpan span = new ColoredImageSpan(R.drawable.msg_premium_liststar); - span.setScale(1.13f, 1.13f); + span.setScale(scale, scale); + spacedStar.setSpan(span, 0, spacedStar.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + AndroidUtilities.replaceMultipleCharSequence("⭐️", ssb, "⭐"); + AndroidUtilities.replaceMultipleCharSequence("⭐ ", ssb, "⭐"); + AndroidUtilities.replaceMultipleCharSequence("⭐", ssb, spacedStar); + AndroidUtilities.replaceMultipleCharSequence(StarsController.currency + " ", ssb, StarsController.currency); + AndroidUtilities.replaceMultipleCharSequence(StarsController.currency, ssb, spacedStar); + return ssb; + } + + + public static SpannableStringBuilder replaceStars(CharSequence cs, ColoredImageSpan[] spanRef) { + if (cs == null) return null; + SpannableStringBuilder ssb; + if (!(cs instanceof SpannableStringBuilder)) { + ssb = new SpannableStringBuilder(cs); + } else { + ssb = (SpannableStringBuilder) cs; + } + ColoredImageSpan span; + if (spanRef != null && spanRef[0] != null ) { + span = spanRef[0]; + } else { + span = new ColoredImageSpan(R.drawable.msg_premium_liststar); + span.setScale(1.13f, 1.13f); + } + if (spanRef != null) { + spanRef[0] = span; + } + SpannableString spacedStar = new SpannableString("⭐ "); spacedStar.setSpan(span, 0, spacedStar.length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); AndroidUtilities.replaceMultipleCharSequence("⭐️", ssb, "⭐"); AndroidUtilities.replaceMultipleCharSequence("⭐ ", ssb, "⭐"); @@ -1614,7 +1902,10 @@ public static SpannableStringBuilder replaceStarsWithPlain(CharSequence cs, floa return ssb; } - public static CharSequence getTransactionTitle(TLRPC.TL_starsTransaction t) { + public static CharSequence getTransactionTitle(boolean bot, TLRPC.StarsTransaction t) { + if (!t.extended_media.isEmpty()) { + return getString(R.string.StarMediaPurchase); + } if (t.title != null) { return t.title; } @@ -1627,7 +1918,7 @@ public static CharSequence getTransactionTitle(TLRPC.TL_starsTransaction t) { return chat == null ? "" : chat.title; } } else if (t.peer instanceof TLRPC.TL_starsTransactionPeerFragment) { - return getString(R.string.StarsTransactionFragment); + return getString(bot ? R.string.StarsTransactionWithdrawFragment : R.string.StarsTransactionFragment); } else if (t.peer instanceof TLRPC.TL_starsTransactionPeerPremiumBot) { return getString(R.string.StarsTransactionBot); } else { @@ -1635,8 +1926,8 @@ public static CharSequence getTransactionTitle(TLRPC.TL_starsTransaction t) { } } - public static BottomSheet showTransactionSheet(Context context, int currentAccount, TLRPC.TL_payments_paymentReceiptStars receipt, Theme.ResourcesProvider resourcesProvider) { - TLRPC.TL_starsTransaction t = new TLRPC.TL_starsTransaction(); + public static BottomSheet showTransactionSheet(Context context, boolean bot, int currentAccount, TLRPC.TL_payments_paymentReceiptStars receipt, Theme.ResourcesProvider resourcesProvider) { + TLRPC.StarsTransaction t = new TLRPC.StarsTransaction(); t.title = receipt.title; t.description = receipt.description; t.photo = receipt.photo; @@ -1645,10 +1936,10 @@ public static BottomSheet showTransactionSheet(Context context, int currentAccou t.date = receipt.date; t.stars = receipt.total_amount; t.id = receipt.transaction_id; - return showTransactionSheet(context, currentAccount, t, resourcesProvider); + return showTransactionSheet(context, bot, 0, currentAccount, t, resourcesProvider); } - public static BottomSheet showTransactionSheet(Context context, int currentAccount, TLRPC.TL_starsTransaction transaction, Theme.ResourcesProvider resourcesProvider) { + public static BottomSheet showTransactionSheet(Context context, boolean bot, long dialogId, int currentAccount, TLRPC.StarsTransaction transaction, Theme.ResourcesProvider resourcesProvider) { if (transaction == null || context == null) return null; @@ -1660,7 +1951,75 @@ public static BottomSheet showTransactionSheet(Context context, int currentAccou linearLayout.setPadding(dp(16), dp(16), dp(16), dp(8)); BackupImageView imageView = new BackupImageView(context); - if (transaction.peer instanceof TLRPC.TL_starsTransactionPeer) { + if (!transaction.extended_media.isEmpty()) { + imageView.setRoundRadius(dp(30)); + TLRPC.MessageMedia media = transaction.extended_media.get(0); + ImageLocation location = null; + if (media instanceof TLRPC.TL_messageMediaPhoto) { + location = ImageLocation.getForPhoto(FileLoader.getClosestPhotoSizeWithSize(media.photo.sizes, dp(100), true), media.photo); + } else if (media instanceof TLRPC.TL_messageMediaDocument) { + location = ImageLocation.getForDocument(FileLoader.getClosestPhotoSizeWithSize(media.document.thumbs, dp(100), true), media.document); + } + imageView.setImage(location, "100_100", null, null, null, 0); + linearLayout.addView(imageView, LayoutHelper.createLinear(100, 100, Gravity.CENTER, 0, 0, 0, 10)); + + imageView.setOnClickListener(v -> { + final long did = bot ? dialogId : DialogObject.getPeerDialogId(transaction.peer.peer); + ArrayList messages = new ArrayList<>(); + for (int i = 0; i < transaction.extended_media.size(); ++i) { + TLRPC.MessageMedia emedia = transaction.extended_media.get(i); + TLRPC.TL_message msg = new TLRPC.TL_message(); + msg.id = transaction.msg_id; + msg.dialog_id = did; + msg.from_id = new TLRPC.TL_peerChannel(); + msg.from_id.channel_id = -did; + msg.peer_id = new TLRPC.TL_peerChannel(); + msg.peer_id.channel_id = -did; + msg.date = transaction.date; + msg.flags |= TLRPC.MESSAGE_FLAG_HAS_MEDIA; + msg.media = emedia; + msg.noforwards = true; + MessageObject msgObj = new MessageObject(currentAccount, msg, false, false); + messages.add(msgObj); + } + if (messages.isEmpty()) return; + + PhotoViewer.getInstance().setParentActivity(LaunchActivity.getLastFragment(), resourcesProvider); + PhotoViewer.getInstance().openPhoto(messages, 0, did, 0, 0, new PhotoViewer.EmptyPhotoViewerProvider() { + + @Override + public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { + final ImageReceiver imageReceiver = imageView.getImageReceiver(); + int[] coords = new int[2]; + imageView.getLocationInWindow(coords); + PhotoViewer.PlaceProviderObject object = new PhotoViewer.PlaceProviderObject(); + object.viewX = coords[0]; + object.viewY = coords[1] - (Build.VERSION.SDK_INT >= 21 ? 0 : AndroidUtilities.statusBarHeight); + object.parentView = linearLayout; + object.animatingImageView = null; + object.imageReceiver = imageReceiver; + if (needPreview) { + object.thumb = imageReceiver.getBitmapSafe(); + } + object.radius = imageReceiver.getRoundRadius(true); + object.dialogId = did; + object.clipTopAddition = 0; // (int) (chatListViewPaddingTop - chatListViewPaddingVisibleOffset - AndroidUtilities.dp(4)); + object.clipBottomAddition = 0; // blurredViewBottomOffset; + return object; + } + + @Override + public boolean forceAllInGroup() { + return true; + } + + @Override + public boolean validateGroupId(long groupId) { + return false; + } + }); + }); + } else if (transaction.peer instanceof TLRPC.TL_starsTransactionPeer) { if (transaction.photo != null) { imageView.setRoundRadius(dp(50)); imageView.setImage(ImageLocation.getForWebFile(WebFile.createWithWebDocument(transaction.photo)), "100_100", null, 0, null); @@ -1689,6 +2048,8 @@ public static BottomSheet showTransactionSheet(Context context, int currentAccou platform = "premiumbot"; } else if (transaction.peer instanceof TLRPC.TL_starsTransactionPeerFragment) { platform = "fragment"; + } else if (transaction.peer instanceof TLRPC.TL_starsTransactionPeerAds) { + platform = "ads"; } CombinedDrawable drawable = (CombinedDrawable) SessionCell.createDrawable(100, platform); drawable.setIconSize(dp(40), dp(40)); @@ -1701,7 +2062,7 @@ public static BottomSheet showTransactionSheet(Context context, int currentAccou textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); textView.setTypeface(AndroidUtilities.bold()); textView.setGravity(Gravity.CENTER); - textView.setText(getTransactionTitle(transaction)); + textView.setText(getTransactionTitle(bot, transaction)); linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 20, 0, 20, 4)); textView = new TextView(context); @@ -1710,34 +2071,20 @@ public static BottomSheet showTransactionSheet(Context context, int currentAccou textView.setTypeface(AndroidUtilities.bold()); textView.setGravity(Gravity.CENTER); textView.setText(replaceStarsWithPlain((transaction.stars >= 0 ? "+" : "-") + LocaleController.formatNumber((int) Math.abs(transaction.stars), ' ') + " ⭐️", .8f)); + SpannableStringBuilder s = new SpannableStringBuilder(textView.getText()); if (transaction.refund) { - SpannableStringBuilder s = new SpannableStringBuilder(textView.getText()); - s.append(" "); - SpannableString refund = new SpannableString(getString(R.string.StarsRefunded)); - final int color = textView.getCurrentTextColor(); - refund.setSpan(new ReplacementSpan() { - private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - { backgroundPaint.setColor(Theme.multAlpha(color, .10f)); } - private final Text layout = new Text(getString(R.string.StarsRefunded), 13, AndroidUtilities.bold()); - - @Override - public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) { - return (int) (dp(6 + 6) + layout.getCurrentWidth()); - } - - @Override - public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { - AndroidUtilities.rectTmp.set(x, (top + bottom - dp(20)) / 2f, x + dp(6 + 6) + layout.getCurrentWidth(), (top + bottom + dp(20)) / 2f); - canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(4), dp(4), backgroundPaint); - layout.draw(canvas, x + dp(6), (top + bottom) / 2f, color, 1f); - } - }, 0, refund.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - s.append(refund); - textView.setText(s); - } + appendStatus(s, textView, getString(R.string.StarsRefunded)); + } else if (transaction.failed) { + textView.setTextColor(Theme.getColor(Theme.key_color_red, resourcesProvider)); + appendStatus(s, textView, getString(R.string.StarsFailed)); + } else if (transaction.pending) { + textView.setTextColor(Theme.getColor(Theme.key_color_yellow, resourcesProvider)); + appendStatus(s, textView, getString(R.string.StarsPending)); + } + textView.setText(s); linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 20, 0, 20, 4)); - if (transaction.description != null) { + if (transaction.description != null && transaction.extended_media.isEmpty()) { textView = new TextView(context); textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); @@ -1758,12 +2105,15 @@ public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, ((LinkSpanDrawable.LinksTextView) textView).setDisablePaddingsOffsetY(true); AvatarSpan avatarSpan = new AvatarSpan(textView, currentAccount, 24); CharSequence username; + boolean deleted = false; if (did >= 0) { TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(did); + deleted = user == null; username = UserObject.getUserName(user); avatarSpan.setUser(user); } else { TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); + deleted = chat == null; username = chat == null ? "" : chat.title; avatarSpan.setChat(chat); } @@ -1785,7 +2135,9 @@ public void updateDrawState(@NonNull TextPaint ds) { } }, 3, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); textView.setText(ssb); - tableView.addRowUnpadded(getString(R.string.StarsTransactionRecipient), textView); + if (!deleted) { + tableView.addRowUnpadded(getString(R.string.StarsTransactionRecipient), textView); + } } else if (transaction.peer instanceof TLRPC.TL_starsTransactionPeerFragment) { tableView.addRow(getString(R.string.StarsTransactionSource), getString(R.string.Fragment)); } else if (transaction.peer instanceof TLRPC.TL_starsTransactionPeerAppStore) { @@ -1796,6 +2148,81 @@ public void updateDrawState(@NonNull TextPaint ds) { tableView.addRow(getString(R.string.StarsTransactionSource), getString(R.string.StarsTransactionBot)); } + if (transaction.peer instanceof TLRPC.TL_starsTransactionPeer && (transaction.flags & 256) != 0) { + long did = DialogObject.getPeerDialogId(transaction.peer.peer); + if (bot) { + did = dialogId; + } + TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-did); + if (chat != null) { + textView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider); + textView.setPadding(dp(12.66f), dp(9.33f), dp(12.66f), dp(9.33f)); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setTextColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider)); + textView.setLinkTextColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider)); + textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + ((LinkSpanDrawable.LinksTextView) textView).setDisablePaddingsOffsetY(true); + SpannableStringBuilder ssb = new SpannableStringBuilder(""); + if (!transaction.extended_media.isEmpty()) { + int count = 0; + for (TLRPC.MessageMedia media : transaction.extended_media) { + ImageReceiverSpan span = new ImageReceiverSpan(textView, currentAccount, 24); + ImageLocation location = null; + if (media instanceof TLRPC.TL_messageMediaPhoto) { + location = ImageLocation.getForPhoto(FileLoader.getClosestPhotoSizeWithSize(media.photo.sizes, dp(24), true), media.photo); + } else if (media instanceof TLRPC.TL_messageMediaDocument) { + location = ImageLocation.getForDocument(FileLoader.getClosestPhotoSizeWithSize(media.document.thumbs, dp(24), true), media.document); + } + if (location != null) { + span.setRoundRadius(6); + span.imageReceiver.setImage(location, "24_24", null, null, null, 0); + SpannableString str = new SpannableString("x"); + str.setSpan(span, 0, str.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.append(str); + ssb.append(" "); + count++; + } + if (count >= 3) break; + } + } + ssb.append(" "); + int start = ssb.length(); + String username = ChatObject.getPublicUsername(chat); + if (TextUtils.isEmpty(username)) { + ssb.append(chat.title); + } else { + ssb.append(MessagesController.getInstance(currentAccount).linkPrefix + "/" + username + "/" + transaction.msg_id); + } + final long finalDialogId = did; + Runnable open = () -> { + sheet[0].dismiss(); + BaseFragment lastFragment = LaunchActivity.getSafeLastFragment(); + if (lastFragment != null) { + Bundle args = new Bundle(); + args.putLong("chat_id", -finalDialogId); + args.putInt("message_id", transaction.msg_id); + lastFragment.presentFragment(new ChatActivity(args)); + } + }; + ssb.setSpan(new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + open.run(); + } + + @Override + public void updateDrawState(@NonNull TextPaint ds) { + ds.setUnderlineText(false); + } + }, start, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + textView.setSingleLine(true); + textView.setEllipsize(TextUtils.TruncateAt.END); + textView.setText(ssb); + textView.setOnClickListener(v -> open.run()); + tableView.addRowUnpadded(getString(R.string.StarsTransactionMedia), textView); + } + } + if (!TextUtils.isEmpty(transaction.id)) { FrameLayout idLayout = new FrameLayout(context); idLayout.setPadding(dp(12.66f), dp(9.33f), dp(10.66f), dp(9.33f)); @@ -1825,6 +2252,10 @@ public void updateDrawState(@NonNull TextPaint ds) { tableView.addRow(getString(R.string.StarsTransactionDate), LocaleController.formatString(R.string.formatDateAtTime, LocaleController.getInstance().formatterGiveawayCard.format(new Date(transaction.date * 1000L)), LocaleController.getInstance().formatterDay.format(new Date(transaction.date * 1000L)))); linearLayout.addView(tableView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 17, 0, 0)); + if ((transaction.flags & 32) != 0) { + tableView.addRow(getString(R.string.StarsTransactionTONDate), LocaleController.formatString(R.string.formatDateAtTime, LocaleController.getInstance().formatterGiveawayCard.format(new Date(transaction.transaction_date * 1000L)), LocaleController.getInstance().formatterDay.format(new Date(transaction.transaction_date * 1000L)))); + } + textView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider); textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); textView.setLinkTextColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider)); @@ -1836,18 +2267,233 @@ public void updateDrawState(@NonNull TextPaint ds) { linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 15, 0, 15)); ButtonWithCounterView button = new ButtonWithCounterView(context, resourcesProvider); - button.setText(getString(R.string.OK), false); + if ((transaction.flags & 32) != 0) { + button.setText(getString(R.string.StarsTransactionViewInBlockchainExplorer), false); + } else { + button.setText(getString(R.string.OK), false); + } linearLayout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); b.setCustomView(linearLayout); sheet[0] = b.create(); + if ((transaction.flags & 32) != 0) { + button.setOnClickListener(v -> { + Browser.openUrl(context, transaction.transaction_url); + }); + } else { + button.setOnClickListener(v -> { + sheet[0].dismiss(); + }); + } + + sheet[0].fixNavigationBar(); + sheet[0].show(); + return sheet[0]; + } + + private static CharSequence appendStatus(SpannableStringBuilder s, TextView textView, String string) { + s.append(" "); + SpannableString refund = new SpannableString(string); + final int color = textView.getCurrentTextColor(); + refund.setSpan(new ReplacementSpan() { + private final Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + { backgroundPaint.setColor(Theme.multAlpha(color, .10f)); } + private final Text layout = new Text(string, 13, AndroidUtilities.bold()); + + @Override + public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) { + return (int) (dp(6 + 6) + layout.getCurrentWidth()); + } + + @Override + public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { + AndroidUtilities.rectTmp.set(x, (top + bottom - dp(20)) / 2f, x + dp(6 + 6) + layout.getCurrentWidth(), (top + bottom + dp(20)) / 2f); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(4), dp(4), backgroundPaint); + layout.draw(canvas, x + dp(6), (top + bottom) / 2f, color, 1f); + } + }, 0, refund.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + s.append(refund); + return s; + } + public static BottomSheet showMediaPriceSheet(Context context, long stars, boolean allowClear, Utilities.Callback2 whenDone, Theme.ResourcesProvider resourcesProvider) { + final BottomSheet.Builder b = new BottomSheet.Builder(context, false, resourcesProvider); + final BottomSheet[] sheet = new BottomSheet[1]; + + final LinearLayout linearLayout = new LinearLayout(context); + linearLayout.setOrientation(LinearLayout.VERTICAL); + linearLayout.setClipChildren(false); + linearLayout.setClipToPadding(false); + linearLayout.setPadding(dp(16), dp(16), dp(16), dp(8)); + + final TextView titleView = new TextView(context); + titleView.setTypeface(AndroidUtilities.bold()); + titleView.setText(getString(R.string.PaidContentTitle)); + titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); + titleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + linearLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 4, 0, 4, 18)); + + final EditTextBoldCursor editText = new EditTextBoldCursor(context); + final OutlineTextContainerView editTextContainer = new OutlineTextContainerView(context, resourcesProvider); + editTextContainer.setForceForceUseCenter(true); + editTextContainer.setText(getString(R.string.PaidContentPriceTitle)); + editTextContainer.setLeftPadding(dp(14 + 22)); + editText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + editText.setCursorSize(AndroidUtilities.dp(20)); + editText.setCursorWidth(1.5f); + editText.setBackground(null); + editText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); + editText.setMaxLines(1); + int padding = AndroidUtilities.dp(16); + editText.setPadding(dp(6), padding, padding, padding); + editText.setInputType(InputType.TYPE_CLASS_NUMBER); + editText.setTypeface(Typeface.DEFAULT); + editText.setSelectAllOnFocus(true); + editText.setHighlightColor(Theme.getColor(Theme.key_chat_inTextSelectionHighlight, resourcesProvider)); + editText.setHandlesColor(Theme.getColor(Theme.key_chat_TextSelectionCursor, resourcesProvider)); + editText.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); + editText.setOnFocusChangeListener((v, hasFocus) -> editTextContainer.animateSelection(hasFocus, !TextUtils.isEmpty(editText.getText()))); + LinearLayout editTextLayout = new LinearLayout(context); + editTextLayout.setOrientation(LinearLayout.HORIZONTAL); + ImageView starImage = new ImageView(context); + starImage.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + starImage.setImageResource(R.drawable.star_small_inner); + editTextLayout.addView(starImage, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, 0, Gravity.LEFT | Gravity.CENTER_VERTICAL, 14, 0, 0, 0)); + editTextLayout.addView(editText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 1, Gravity.FILL)); + editTextContainer.attachEditText(editText); + editTextContainer.addView(editTextLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP)); + linearLayout.addView(editTextContainer, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); + + final LinkSpanDrawable.LinksTextView infoView = new LinkSpanDrawable.LinksTextView(context); + infoView.setText(AndroidUtilities.replaceArrows(AndroidUtilities.replaceSingleTag(getString(R.string.PaidContentInfo), () -> { + Browser.openUrl(context, getString(R.string.PaidContentInfoLink)); + }), true)); + infoView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + infoView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider)); + infoView.setLinkTextColor(Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider)); + linearLayout.addView(infoView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 14, 3, 14, 24)); + + ButtonWithCounterView button = new ButtonWithCounterView(context, resourcesProvider); + button.setText(getString(stars > 0 ? R.string.PaidContentUpdateButton : R.string.PaidContentButton), false); + linearLayout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48)); + + final ButtonWithCounterView clearButton; + if (stars > 0 && allowClear) { + clearButton = new ButtonWithCounterView(context, false, resourcesProvider); + clearButton.setText(getString(R.string.PaidContentClearButton), false, false); + linearLayout.addView(clearButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 0, 4, 0, 0)); + } else { + clearButton = null; + } + + b.setCustomView(linearLayout); + sheet[0] = b.create(); + + editText.setText(stars <= 0 ? "" : Long.toString(stars)); + editText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + private boolean ignore; + private int shakeDp = 2; + @Override + public void afterTextChanged(Editable s) { + if (ignore) return; + + long input_stars = 0; + try { + input_stars = TextUtils.isEmpty(s) ? 0 : Long.parseLong(s.toString()); + if (input_stars > MessagesController.getInstance(UserConfig.selectedAccount).starsPaidPostAmountMax) { + ignore = true; + editText.setText(Long.toString(input_stars = MessagesController.getInstance(UserConfig.selectedAccount).starsPaidPostAmountMax)); + editText.setSelection(editText.getText().length()); + AndroidUtilities.shakeViewSpring(editTextContainer, shakeDp = -shakeDp); + } + } catch (Exception e) { + ignore = true; + editText.setText(stars <= 0 ? "" : Long.toString(stars)); + editText.setSelection(editText.getText().length()); + } + ignore = false; + + if (!allowClear) { + button.setEnabled(input_stars > 0); + } + + editTextContainer.animateSelection(editText.isFocused(), !TextUtils.isEmpty(editText.getText())); + } + }); + + final boolean[] loading = new boolean[] { false }; + editText.setOnEditorActionListener((textView, i, keyEvent) -> { + if (i == EditorInfo.IME_ACTION_NEXT) { + if (loading[0]) return true; + if (whenDone != null) { + button.setLoading(loading[0] = true); + whenDone.run(Long.parseLong(editText.getText().toString()), () -> { + AndroidUtilities.hideKeyboard(editText); + sheet[0].dismiss(); + }); + } else { + AndroidUtilities.hideKeyboard(editText); + sheet[0].dismiss(); + } + return true; + } + return false; + }); button.setOnClickListener(v -> { - sheet[0].dismiss(); + if (loading[0]) return; + if (whenDone != null) { + String s = editText.getText().toString(); + button.setLoading(loading[0] = true); + whenDone.run(TextUtils.isEmpty(s) ? 0 : Long.parseLong(s), () -> { + AndroidUtilities.hideKeyboard(editText); + sheet[0].dismiss(); + }); + } else { + AndroidUtilities.hideKeyboard(editText); + sheet[0].dismiss(); + } }); + if (clearButton != null) { + clearButton.setOnClickListener(v -> { + if (loading[0]) return; + if (whenDone != null) { + clearButton.setLoading(loading[0] = true); + whenDone.run(0L, () -> { + loading[0] = false; + AndroidUtilities.hideKeyboard(editText); + sheet[0].dismiss(); + }); + } else { + AndroidUtilities.hideKeyboard(editText); + sheet[0].dismiss(); + } + }); + } sheet[0].fixNavigationBar(); + sheet[0].setOnDismissListener(d -> { + AndroidUtilities.hideKeyboard(editText); + }); sheet[0].show(); + + BaseFragment lastFragment = LaunchActivity.getLastFragment(); + boolean keyboardVisible = false; + if (lastFragment instanceof ChatActivity) { + keyboardVisible = ((ChatActivity) lastFragment).needEnterText(); + } + AndroidUtilities.runOnUIThread(() -> { + sheet[0].setFocusable(true); + editText.requestFocus(); + AndroidUtilities.runOnUIThread(() -> AndroidUtilities.showKeyboard(editText)); + }, keyboardVisible ? 200 : 80); + return sheet[0]; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java index 7eb3d4690f..3055346b46 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/StatisticActivity.java @@ -609,6 +609,7 @@ protected void onTabAnimationUpdate(boolean manual) { } if (hasMonetization) { monetizationLayout = new ChannelMonetizationLayout(getContext(), StatisticActivity.this, currentAccount, -chatId, getResourceProvider()); + monetizationLayout.setActionBar(actionBar); } boolean showTabs = isBoostSupported && !onlyBoostsStat; if (showTabs && startFromBoosts) { @@ -2186,7 +2187,7 @@ public static class ChartViewData { public String errorMessage; public long activeZoom; public boolean viewShowed; - ChartData chartData; + public ChartData chartData; ChartData childChartData; String token; String zoomToken; 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 d75855ca3b..11a31bfb26 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/PeerStoriesView.java @@ -303,6 +303,7 @@ public class PeerStoriesView extends SizeNotifierFrameLayout implements Notifica int lastOpenedKeyboardHeight; boolean animateKeyboardOpening; public boolean keyboardVisible; + private boolean wasBigScreen; private boolean BIG_SCREEN; private int realKeyboardHeight; private int classGuid = ConnectionsManager.generateClassGuid(); @@ -741,7 +742,7 @@ private void drawLines(Canvas canvas) { } }); } - if (storyViewer.storiesList != null) { + if (storyViewer.storiesList != null && storyViewer.storiesList.type != StoriesController.StoriesList.TYPE_SEARCH) { if (storyPositionView == null) { storyPositionView = new StoryPositionView(); } @@ -830,16 +831,25 @@ public void onLinkClick(CharacterStyle span, View spoilersTextView) { } } else if (span instanceof URLSpanNoUnderline) { String str = ((URLSpanNoUnderline) span).getURL(); - String username = Browser.extractUsername(str); - if (username != null) { - username = username.toLowerCase(); - if (str.startsWith("@")) { - MessagesController.getInstance(currentAccount).openByUserName(username, storyViewer.fragment, 0, null); + if (str != null && str.startsWith("#")) { + Bundle args = new Bundle(); + args.putInt("type", MediaActivity.TYPE_STORIES_SEARCH); + args.putString("hashtag", str.substring(1)); + if (storyViewer != null) { + storyViewer.presentFragment(new MediaActivity(args, null)); + } + } else { + String username = Browser.extractUsername(str); + if (username != null) { + username = username.toLowerCase(); + if (str.startsWith("@")) { + MessagesController.getInstance(currentAccount).openByUserName(username, storyViewer.fragment, 0, null); + } else { + processExternalUrl(0, str, span, false); + } } else { processExternalUrl(0, str, span, false); } - } else { - processExternalUrl(0, str, span, false); } } else if (span instanceof URLSpan) { String url = ((URLSpan) span).getURL(); @@ -2373,7 +2383,7 @@ public void appendColors() { @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (!isEnabled()) { - AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); + AndroidUtilities.rectTmp.set(0, 0, getWidth() + (premiumBlockedText2 != null ? 1.5f * attachLayoutPaddingTranslationX : 0), getHeight()); boolean hit = AndroidUtilities.rectTmp.contains(ev.getX(), ev.getY()); if (ev.getAction() == MotionEvent.ACTION_DOWN) { if (hit && premiumBlockedText2 != null) { @@ -3108,7 +3118,7 @@ private void openRepostStory() { } } if (storyViewer.fragment != null) { - storyViewer.fragment.clearStoryViewers(); + storyViewer.fragment.clearSheets(); } storyViewer.instantClose(); editOpened = false; @@ -3231,7 +3241,7 @@ private void bindInternal(int startFromPosition) { headerView.titleView.setRightDrawable(null); } if (user != null) { - CharSequence text = ContactsController.formatName(user); + CharSequence text = AndroidUtilities.removeDiacritics(ContactsController.formatName(user)); text = Emoji.replaceEmoji(text, headerView.titleView.getPaint().getFontMetricsInt(), false); headerView.titleView.setText(text); } else { @@ -3253,7 +3263,7 @@ private void bindInternal(int startFromPosition) { isPremiumBlocked = isGroup && !ChatObject.canSendPlain(chat); avatarDrawable.setInfo(currentAccount, chat); headerView.backupImageView.getImageReceiver().setForUserOrChat(chat, avatarDrawable); - headerView.titleView.setText(chat.title); + headerView.titleView.setText(AndroidUtilities.removeDiacritics(chat.title)); if (chat != null && chat.verified) { Drawable verifyDrawable = ContextCompat.getDrawable(getContext(), R.drawable.verified_profile).mutate(); @@ -3998,6 +4008,9 @@ private void updatePosition(boolean preload) { if (allowShare) { allowShare = currentStory.allowScreenshots() && currentStory.storyItem.isPublic; } + if (allowShare) { + allowShare = currentStory.storyItem.pinned || !StoriesUtilities.isExpired(currentAccount, currentStory.storyItem); + } allowRepost = allowShare; if (allowRepost && isChannel) { final TLRPC.Chat chat = MessagesController.getInstance(currentAccount).getChat(-dialogId); @@ -5593,10 +5606,11 @@ public void updateCaption() { spannableStringBuilder = SpannableStringBuilder.valueOf(MessageObject.replaceAnimatedEmoji(spannableStringBuilder, text.entities, storyCaptionView.captionTextview.getPaint().getFontMetricsInt(), false)); SpannableStringBuilder.valueOf(Emoji.replaceEmoji(spannableStringBuilder, storyCaptionView.captionTextview.getPaint().getFontMetricsInt(), false)); TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); - if (dialogId < 0 || MessagesController.getInstance(currentAccount).storyEntitiesAllowed(user)) { + final boolean entitiesAllowed = dialogId < 0 || MessagesController.getInstance(currentAccount).storyEntitiesAllowed(user); + if (entitiesAllowed) { MessageObject.addLinks(true, spannableStringBuilder); - MessageObject.addEntitiesToText(spannableStringBuilder, text.entities, false, true, true, false); } + MessageObject.addEntitiesToText(spannableStringBuilder, text.entities, false, true, true, false, entitiesAllowed ? MessageObject.ENTITIES_ALL : MessageObject.ENTITIES_ONLY_HASHTAGS); caption = spannableStringBuilder; } } else { @@ -5607,10 +5621,11 @@ public void updateCaption() { spannableStringBuilder = SpannableStringBuilder.valueOf(MessageObject.replaceAnimatedEmoji(spannableStringBuilder, currentStory.storyItem.entities, storyCaptionView.captionTextview.getPaint().getFontMetricsInt(), false)); SpannableStringBuilder.valueOf(Emoji.replaceEmoji(spannableStringBuilder, storyCaptionView.captionTextview.getPaint().getFontMetricsInt(), false)); TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(dialogId); - if (dialogId < 0 || MessagesController.getInstance(currentAccount).storyEntitiesAllowed(user)) { + final boolean entitiesAllowed = dialogId < 0 || MessagesController.getInstance(currentAccount).storyEntitiesAllowed(user); + if (entitiesAllowed) { MessageObject.addLinks(true, spannableStringBuilder); - MessageObject.addEntitiesToText(spannableStringBuilder, currentStory.storyItem.entities, false, true, true, false); } + MessageObject.addEntitiesToText(spannableStringBuilder, currentStory.storyItem.entities, false, true, true, false, entitiesAllowed ? MessageObject.ENTITIES_ALL : MessageObject.ENTITIES_ONLY_HASHTAGS); caption = spannableStringBuilder; } } @@ -5958,6 +5973,9 @@ public void onAnimationEnd(Animator animation) { } else { enterViewBottomOffset = -top + heightWithKeyboard - viewPagerHeight; } + if (BIG_SCREEN != wasBigScreen) { + storyContainer.setLayoutParams(layoutParams); + } if (selfView != null) { layoutParams = (LayoutParams) selfView.getLayoutParams(); if (BIG_SCREEN) { @@ -5990,10 +6008,16 @@ public void onAnimationEnd(Animator animation) { ((LayoutParams) bottomActionsLinearLayout.getLayoutParams()).topMargin = top + viewPagerHeight - AndroidUtilities.dp(12) - AndroidUtilities.dp(40); int bottomPadding = isSelf ? AndroidUtilities.dp(40) : AndroidUtilities.dp(56); ((LayoutParams) storyCaptionView.getLayoutParams()).bottomMargin = bottomPadding; + if (wasBigScreen != BIG_SCREEN) { + storyCaptionView.setLayoutParams((LayoutParams) storyCaptionView.getLayoutParams()); + } storyCaptionView.blackoutBottomOffset = bottomPadding; } else { ((LayoutParams) bottomActionsLinearLayout.getLayoutParams()).topMargin = top + viewPagerHeight + AndroidUtilities.dp(12); ((LayoutParams) storyCaptionView.getLayoutParams()).bottomMargin = AndroidUtilities.dp(8); + if (wasBigScreen != BIG_SCREEN) { + storyCaptionView.setLayoutParams((LayoutParams) storyCaptionView.getLayoutParams()); + } storyCaptionView.blackoutBottomOffset = AndroidUtilities.dp(8); } @@ -6019,6 +6043,7 @@ public void onAnimationEnd(Animator animation) { headerView.forceLayout(); } super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(heightWithKeyboard, MeasureSpec.EXACTLY)); + wasBigScreen = BIG_SCREEN; } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsPage.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsPage.java index 89c1d4af48..02dbd480a7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsPage.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/SelfStoryViewsPage.java @@ -784,7 +784,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { view = new FixedHeightEmptyCell(getContext(), 70); break; case USER_ITEM: - view = new ReactedUserHolderView(ReactedUserHolderView.STYLE_STORY, currentAccount, getContext(), resourcesProvider, false) { + view = new ReactedUserHolderView(ReactedUserHolderView.STYLE_STORY, currentAccount, getContext(), resourcesProvider, false, true) { @Override public void openStory(long dialogId, Runnable onDone) { BaseFragment lastFragment = LaunchActivity.getLastFragment(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java index 92a475772c..9d10db6074 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesController.java @@ -54,6 +54,7 @@ import org.telegram.ui.Components.Premium.LimitReachedBottomSheet; import org.telegram.ui.Components.Reactions.ReactionImageHolder; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; +import org.telegram.ui.Components.Text; import org.telegram.ui.LaunchActivity; import org.telegram.ui.StatisticActivity; import org.telegram.ui.Stories.recorder.DraftsController; @@ -2394,6 +2395,130 @@ public void destroyStoryList(StoriesList list) { } } + public static class SearchStoriesList extends StoriesList { + + public final String query; + public final TL_stories.MediaArea queryArea; + + public SearchStoriesList(int currentAccount, String query) { + super(currentAccount, 0, TYPE_SEARCH, null); + this.query = query; + this.queryArea = null; + } + + public SearchStoriesList(int currentAccount, TL_stories.MediaArea area) { + super(currentAccount, 0, TYPE_SEARCH, null); + this.query = null; + this.queryArea = area; + } + + @Override + public boolean isOnlyCache() { + return false; + } + @Override + protected void invalidateCache() {} + @Override + protected void preloadCache() {} + @Override + protected void saveCache() {} + + @Override + protected boolean markAsRead(int storyId) { + return false; + } + + public void cancel() { + if (this.reqId != 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(this.reqId, true); + this.reqId = 0; + } + } + + private ArrayList> fakeDays = new ArrayList<>(); + + private boolean loading; + private String last_offset = ""; + private int reqId; + private int count; + + @Override + public boolean load(boolean force, int count, List ids) { + if (loading) return false; + if (last_offset == null) return false; + + TL_stories.TL_stories_searchPosts req = new TL_stories.TL_stories_searchPosts(); + req.offset = last_offset; + req.limit = count; + if (query != null) { + req.flags |= 1; + req.hashtag = query; + } + if (queryArea != null) { + req.flags |= 2; + req.area = queryArea; + } + + loading = true; + + this.reqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + this.reqId = 0; + if (res instanceof TL_stories.TL_foundStories) { + TL_stories.TL_foundStories r = (TL_stories.TL_foundStories) res; + MessagesController.getInstance(currentAccount).putUsers(r.users, false); + MessagesController.getInstance(currentAccount).putChats(r.chats, false); + for (TL_stories.TL_foundStory s : r.stories) { + s.storyItem.dialogId = DialogObject.getPeerDialogId(s.peer); + s.storyItem.messageId = messageObjects.size(); + MessageObject msg = new MessageObject(currentAccount, s.storyItem); + msg.generateThumbs(false); + ArrayList day = new ArrayList<>(); + day.add(messageObjects.size()); + fakeDays.add(day); + messageObjects.add(msg); + } + this.count = Math.max(messageObjects.size(), r.count); + if (r.stories.isEmpty()) { + this.count = messageObjects.size(); + } + last_offset = messageObjects.size() >= r.count || r.stories.isEmpty() ? null : r.next_offset; + this.loading = false; + + AndroidUtilities.cancelRunOnUIThread(super.notify); + AndroidUtilities.runOnUIThread(super.notify); + } + })); + + return true; + } + + @Override + public int getCount() { + return count; + } + + @Override + public int getLoadedCount() { + return messageObjects.size(); + } + + @Override + public boolean isLoading() { + return loading; + } + + @Override + protected ArrayList> getDays() { + return fakeDays; + } + + @Override + public MessageObject findMessageObject(int id) { + if (id < 0 || id >= messageObjects.size()) return null; + return messageObjects.get(id); + } + } + public static class StoriesList { private static HashMap lastLoadTime; @@ -2403,12 +2528,14 @@ public static class StoriesList { public int link() { final int id = maxLinkId++; links.add(id); - AndroidUtilities.cancelRunOnUIThread(destroyRunnable); + if (destroyRunnable != null) { + AndroidUtilities.cancelRunOnUIThread(destroyRunnable); + } return id; } public void unlink(int id) { links.remove((Integer) id); - if (links.isEmpty()) { + if (links.isEmpty() && destroyRunnable != null) { AndroidUtilities.cancelRunOnUIThread(destroyRunnable); AndroidUtilities.runOnUIThread(destroyRunnable, 1000 * 60 * 5); } @@ -2417,6 +2544,7 @@ public void unlink(int id) { public static final int TYPE_PINNED = 0; public static final int TYPE_ARCHIVE = 1; public static final int TYPE_STATISTICS = 2; + public static final int TYPE_SEARCH = 3; public final int currentAccount; public final long dialogId; @@ -2459,7 +2587,7 @@ public boolean showVideos() { NotificationCenter.getInstance(StoriesList.this.currentAccount).postNotificationName(NotificationCenter.storiesListUpdated, StoriesList.this); }; - public void fill(boolean notify) { + private void fill(boolean notify) { fill(this.messageObjects, showPhotos, showVideos); if (notify) { AndroidUtilities.cancelRunOnUIThread(this.notify); @@ -2516,7 +2644,7 @@ private boolean filter(MessageObject msg, boolean photos, boolean videos) { private boolean preloading, loading; private boolean invalidateAfterPreload; private boolean error; - private Runnable destroyRunnable; + private final Runnable destroyRunnable; private Utilities.CallbackReturn toLoad; @@ -2529,7 +2657,7 @@ private StoriesList(int currentAccount, long dialogId, int type, Utilities.Callb preloadCache(); } - private void preloadCache() { + protected void preloadCache() { if (preloading || loading || error) { return; } @@ -2696,7 +2824,7 @@ public static long day(MessageObject messageObject) { return year * 10000L + month * 100L + day; } - public ArrayList> getDays() { + protected ArrayList> getDays() { final ArrayList keys = new ArrayList<>(groupedByDay.keySet()); Collections.sort(keys, (a, b) -> (int) (b - a)); final ArrayList> days = new ArrayList<>(); @@ -2720,7 +2848,7 @@ public ArrayList> getDays() { return days; } - public void invalidateCache() { + protected void invalidateCache() { if (preloading) { invalidateAfterPreload = true; return; @@ -2746,7 +2874,7 @@ public void invalidateCache() { private boolean saving; - private void saveCache() { + protected void saveCache() { if (saving) { return; } @@ -2800,7 +2928,7 @@ private void saveCache() { }); } - public boolean markAsRead(int storyId) { + protected boolean markAsRead(int storyId) { if (seenStories.contains(storyId)) return false; seenStories.add(storyId); saveCache(); @@ -2812,7 +2940,7 @@ public boolean markAsRead(int storyId) { return true; } - private boolean canLoad() { + protected boolean canLoad() { if (lastLoadTime == null) { return true; } @@ -2824,7 +2952,7 @@ private boolean canLoad() { return System.currentTimeMillis() - time > 1000L * 60 * 2; } - private void resetCanLoad() { + protected void resetCanLoad() { if (lastLoadTime != null) { lastLoadTime.remove(Objects.hash(currentAccount, type, dialogId)); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesListPlaceProvider.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesListPlaceProvider.java index facf35620c..3a687c303e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesListPlaceProvider.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesListPlaceProvider.java @@ -200,7 +200,11 @@ public boolean findView(long dialogId, int messageId, int storyId, int type, Sto holder.drawAbove = (canvas, bounds, alpha, opening) -> { cell.drawDuration(canvas, bounds, alpha); cell.drawViews(canvas, bounds, alpha); - cell.drawPrivacy(canvas, bounds, alpha); + if (cell.isSearchingHashtag) { + cell.drawAuthor(canvas, bounds, alpha); + } else { + cell.drawPrivacy(canvas, bounds, alpha); + } if (fastScroll != null && fastScroll.isVisible && fastScroll.getVisibility() == View.VISIBLE) { canvas.saveLayerAlpha(0, 0, canvas.getWidth(), canvas.getHeight(), (int) (0xFF * alpha), Canvas.ALL_SAVE_FLAG); canvas.translate(loc[0], loc[1]); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesViewPager.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesViewPager.java index 2c4f565326..72a2d09c5e 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesViewPager.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoriesViewPager.java @@ -16,6 +16,7 @@ import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.FileLog; +import org.telegram.messenger.MessageObject; import org.telegram.messenger.UserConfig; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; @@ -98,7 +99,12 @@ public boolean isSelectedPeer() { pageLayout.setTag(position); if (days != null) { pageLayout.day = days.get(storyViewer.reversed ? days.size() - 1 - position : position); - pageLayout.dialogId = daysDialogId; + if (storyViewer.storiesList instanceof StoriesController.SearchStoriesList) { + MessageObject msg = storyViewer.storiesList.findMessageObject(pageLayout.day.get(0)); + pageLayout.dialogId = msg == null ? daysDialogId : msg.getDialogId(); + } else { + pageLayout.dialogId = daysDialogId; + } } else { pageLayout.day = null; pageLayout.dialogId = dialogs.get(position); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryMediaAreasView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryMediaAreasView.java index 726b5ae830..261e465dd8 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryMediaAreasView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryMediaAreasView.java @@ -5,6 +5,7 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; @@ -14,27 +15,37 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; +import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.text.SpannableString; import android.text.SpannableStringBuilder; import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.ForegroundColorSpan; +import android.text.style.LineHeightSpan; +import android.text.style.RelativeSizeSpan; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.animation.LinearInterpolator; import android.widget.FrameLayout; +import androidx.annotation.NonNull; + import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.R; import org.telegram.messenger.UserConfig; +import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.tl.TL_stories; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.AnimatedFloat; +import org.telegram.ui.Components.ButtonBounce; import org.telegram.ui.Components.ColoredImageSpan; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.LayoutHelper; @@ -69,6 +80,8 @@ public StoryMediaAreasView(Context context, View parentView, Theme.ResourcesProv parentHighlightScaleAlpha = new AnimatedFloat(parentView, 0, 360, CubicBezierInterpolator.EASE_OUT_QUINT); setClipChildren(false); addView(hintsContainer = new FrameLayout(context)); + + setLayerType(View.LAYER_TYPE_HARDWARE, null); } public static ArrayList getMediaAreasFor(StoryEntry entry) { @@ -209,12 +222,22 @@ public void onClick(View v) { return; } + + if (selectedArea.mediaArea instanceof TL_stories.TL_mediaAreaUrl) { + Browser.openUrl(getContext(), ((TL_stories.TL_mediaAreaUrl) selectedArea.mediaArea).url); + selectedArea = null; + invalidate(); + return; + } + LocationActivity fragment = new LocationActivity(3) { @Override protected boolean disablePermissionCheck() { return true; } }; + fragment.fromStories = true; + fragment.searchStories(selectedArea.mediaArea); fragment.setResourceProvider(resourcesProvider); TLRPC.TL_message message = new TLRPC.TL_message(); if (selectedArea.mediaArea instanceof TL_stories.TL_mediaAreaVenue) { @@ -258,30 +281,47 @@ protected boolean disablePermissionCheck() { hintView = null; } - boolean top = selectedArea.getTranslationY() < AndroidUtilities.dp(100); + final HintView2 thisHint = hintView = new HintView2(getContext()) + .setSelectorColor(0x28ffffff) + .setJointPx(0, selectedArea.getTranslationX() - dp(8)) + .setDuration(5000); + boolean multiline = false; SpannableStringBuilder text = new SpannableStringBuilder(); if (selectedArea.mediaArea instanceof TL_stories.TL_mediaAreaChannelPost) { text.append(LocaleController.getString(R.string.StoryViewMessage)); + } else if (selectedArea.mediaArea instanceof TL_stories.TL_mediaAreaUrl) { + thisHint.setMultilineText(multiline = true); + text.append(LocaleController.getString(R.string.StoryOpenLink)); + text.append("\n"); + TL_stories.TL_mediaAreaUrl area = (TL_stories.TL_mediaAreaUrl) selectedArea.mediaArea; + int start = text.length(); + text.append(TextUtils.ellipsize(area.url, hintView.getTextPaint(), AndroidUtilities.displaySize.x * .6f, TextUtils.TruncateAt.END)); + text.setSpan(new RelativeSizeSpan(0.85f), start, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + text.setSpan(new ForegroundColorSpan(Theme.multAlpha(Color.WHITE, 0.6f)), start, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + text.setSpan(new LineHeightSpan() { + @Override + public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int lineHeight, Paint.FontMetricsInt fm) { + fm.ascent -= dp(2); + fm.top -= dp(2); + } + }, start, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + thisHint.setInnerPadding(11, 7, 11, 7); } else { text.append(LocaleController.getString(R.string.StoryViewLocation)); } SpannableString arrowRight = new SpannableString(">"); ColoredImageSpan imageSpan = new ColoredImageSpan(R.drawable.photos_arrow); - imageSpan.translate(dp(2), dp(1)); + imageSpan.translate(dp(multiline ? 1 : 2), dp(multiline ? 0 : 1)); arrowRight.setSpan(imageSpan, 0, arrowRight.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); SpannableString arrowLeft = new SpannableString("<"); imageSpan = new ColoredImageSpan(R.drawable.attach_arrow_right); - imageSpan.translate(dp(-2), dp(1)); + imageSpan.translate(dp(multiline ? -1 : -2), dp(multiline ? 0 : 1)); imageSpan.setScale(-1, 1); arrowLeft.setSpan(imageSpan, 0, arrowLeft.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); AndroidUtilities.replaceCharSequence(">", text, AndroidUtilities.isRTL(text) ? arrowLeft : arrowRight); - final HintView2 thisHint = hintView = new HintView2(getContext(), top ? HintView2.DIRECTION_TOP : HintView2.DIRECTION_BOTTOM) - .setText(text) - .setSelectorColor(0x28ffffff) - .setJointPx(0, selectedArea.getTranslationX() - dp(8)) - .setDuration(5000); + thisHint.setText(text); thisHint.setOnHiddenListener(() -> { hintsContainer.removeView(thisHint); if (thisHint == hintView) { @@ -290,21 +330,24 @@ protected boolean disablePermissionCheck() { onHintVisible(false); } }); + final int h = multiline ? 100 : 50; + boolean top = selectedArea.getTranslationY() - dp(h) < AndroidUtilities.dp(100); + thisHint.setDirection(top ? HintView2.DIRECTION_TOP : HintView2.DIRECTION_BOTTOM); if (selectedArea.mediaArea instanceof TL_stories.TL_mediaAreaChannelPost && ( top ? selectedArea.getTranslationY() + selectedArea.getMeasuredHeight() / 2f > getMeasuredHeight() - dp(120) : - selectedArea.getTranslationY() - selectedArea.getMeasuredHeight() / 2f - dp(50) < dp(120) + selectedArea.getTranslationY() - selectedArea.getMeasuredHeight() / 2f - dp(h) < dp(120) )) { - hintView.setTranslationY(selectedArea.getTranslationY() - selectedArea.getMeasuredHeight() / 3f); + thisHint.setTranslationY(selectedArea.getTranslationY() - selectedArea.getMeasuredHeight() / 3f); } else if (top) { - hintView.setTranslationY(selectedArea.getTranslationY() + selectedArea.getMeasuredHeight() / 2f); + thisHint.setTranslationY(selectedArea.getTranslationY() + selectedArea.getMeasuredHeight() / 2f); } else { - hintView.setTranslationY(selectedArea.getTranslationY() - selectedArea.getMeasuredHeight() / 2f - dp(50)); + thisHint.setTranslationY(selectedArea.getTranslationY() - selectedArea.getMeasuredHeight() / 2f - dp(h)); } - hintView.setOnClickListener(view -> onClick(selectedArea)); - hintView.setPadding(dp(8), dp(8), dp(8), dp(8)); - hintsContainer.addView(hintView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 50)); - hintView.show(); + thisHint.setOnClickListener(view -> onClick(selectedArea)); + thisHint.setPadding(dp(8), dp(8), dp(8), dp(8)); + hintsContainer.addView(thisHint, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, h)); + thisHint.show(); onHintVisible(true); } @@ -431,20 +474,26 @@ private void drawHighlight(Canvas canvas) { canvas.save(); clipPath.rewind(); rectF.set(lastSelectedArea.getX(), lastSelectedArea.getY(), lastSelectedArea.getX() + lastSelectedArea.getMeasuredWidth(), lastSelectedArea.getY() + lastSelectedArea.getMeasuredHeight()); - final float s = AndroidUtilities.lerp(1.0f, 1.05f, parentScale); + final float s = AndroidUtilities.lerp(1.0f, 1.05f * (lastSelectedArea.bounceOnTap ? lastSelectedArea.bounce.getScale(.05f) : 1f), parentScale); canvas.scale(s, s, rectF.centerX(), rectF.centerY()); canvas.rotate(lastSelectedArea.getRotation(), rectF.centerX(), rectF.centerY()); - radii[0] = radii[1] = dp(16); - radii[2] = radii[3] = dp(16); - radii[4] = radii[5] = dp(16); - radii[6] = radii[7] = dp(8); - clipPath.addRoundRect(rectF, radii, Path.Direction.CW); + final float r = (lastSelectedArea.mediaArea.coordinates.flags & 1) != 0 ? + (float) (lastSelectedArea.mediaArea.coordinates.radius / 100.0 * getWidth()) : + .2f * lastSelectedArea.getMeasuredHeight(); + clipPath.addRoundRect(rectF, r, r, Path.Direction.CW); canvas.clipPath(clipPath); AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); rect.set(0, 0, parentBitmap.getWidth(), parentBitmap.getHeight()); canvas.rotate(-lastSelectedArea.getRotation(), rectF.centerX(), rectF.centerY()); canvas.drawBitmap(parentBitmap, rect, AndroidUtilities.rectTmp, null); canvas.restore(); + + canvas.save(); + canvas.translate(lastSelectedArea.getX(), lastSelectedArea.getY()); + canvas.rotate(lastSelectedArea.getRotation(), lastSelectedArea.getPivotX(), lastSelectedArea.getPivotY()); + canvas.scale(lastSelectedArea.getScaleX() * s, lastSelectedArea.getScaleY() * s, lastSelectedArea.getPivotX(), lastSelectedArea.getPivotY()); + lastSelectedArea.drawAbove(canvas); + canvas.restore(); } } else if (parentBitmap != null) { parentBitmap.recycle(); @@ -563,32 +612,94 @@ public static class AreaView extends View { public AreaView(Context context, View parent, TL_stories.MediaArea mediaArea) { super(context); this.mediaArea = mediaArea; - supportsBounds = mediaArea instanceof TL_stories.TL_mediaAreaGeoPoint || mediaArea instanceof TL_stories.TL_mediaAreaVenue; // || mediaArea instanceof TL_stories.TL_mediaAreaChannelPost; + supportsBounds = mediaArea instanceof TL_stories.TL_mediaAreaGeoPoint || mediaArea instanceof TL_stories.TL_mediaAreaVenue || mediaArea instanceof TL_stories.TL_mediaAreaUrl; supportsShining = mediaArea instanceof TL_stories.TL_mediaAreaGeoPoint || mediaArea instanceof TL_stories.TL_mediaAreaVenue; - scaleOnTap = false; // mediaArea instanceof TL_stories.TL_mediaAreaChannelPost; + final boolean hasRadius = mediaArea instanceof TL_stories.TL_mediaAreaGeoPoint || mediaArea instanceof TL_stories.TL_mediaAreaVenue || (mediaArea.coordinates.flags & 1) != 0; + scaleOnTap = hasRadius; + ripple = hasRadius; + bounceOnTap = hasRadius; highlightAlpha = new AnimatedFloat(parent, 0, 120, new LinearInterpolator()); strokeGradientPaint.setStyle(Paint.Style.STROKE); + rippleDrawable.setCallback(this); } private final Paint gradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint strokeGradientPaint = new Paint(Paint.ANTI_ALIAS_FLAG); private LinearGradient gradient, strokeGradient; private final Matrix gradientMatrix = new Matrix(); + private final Drawable rippleDrawable = Theme.createSelectorDrawable(0x45FFFFFF, Theme.RIPPLE_MASK_ALL); + public final ButtonBounce bounce = new ButtonBounce(this); private boolean supportsBounds = false; private boolean supportsShining = false; private boolean scaleOnTap; + private boolean bounceOnTap; + private boolean ripple; private boolean shining = false; private long startTime; + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (getParent() instanceof View) { + bounce.setAdditionalInvalidate(((View) getParent())::invalidate); + } + bounce.setPressed(true); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + rippleDrawable.setHotspot(event.getX(), event.getY()); + } + rippleDrawable.setState(new int[]{android.R.attr.state_pressed, android.R.attr.state_enabled}); + } else if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) { + bounce.setPressed(false); + rippleDrawable.setState(new int[]{}); + } + return super.dispatchTouchEvent(event) || true; + } + public void customDraw(Canvas canvas) { } + public void drawAbove(Canvas canvas) { + if (!ripple) { + return; + } + final float r = getInnerRadius(); + + AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); + clipPath.rewind(); + clipPath.addRoundRect(AndroidUtilities.rectTmp, r, r, Path.Direction.CW); + canvas.save(); + canvas.clipPath(clipPath); + rippleDrawable.setBounds(0, 0, getWidth(), getHeight()); + rippleDrawable.draw(canvas); + canvas.restore(); + } + + @Override + protected boolean verifyDrawable(@NonNull Drawable who) { + return who == rippleDrawable || super.verifyDrawable(who); + } + + public float getInnerRadius() { + if (getParent() instanceof View && mediaArea != null && mediaArea.coordinates != null) { + return (mediaArea.coordinates.flags & 1) != 0 ? + (float) (mediaArea.coordinates.radius / 100.0 * ((View) getParent()).getWidth() / getScaleX()) : + .2f * getMeasuredHeight(); + } else { + return .2f * getMeasuredHeight(); + } + } + + private final Path clipPath = new Path(); + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); + final float r = getInnerRadius(); + drawAbove(canvas); + if (supportsShining && shining && gradient != null) { float w = getMeasuredWidth() * .7f; float t = (System.currentTimeMillis() - startTime) / 600f; @@ -605,14 +716,14 @@ protected void onDraw(Canvas canvas) { gradient.setLocalMatrix(gradientMatrix); gradientPaint.setShader(gradient); AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); - canvas.drawRoundRect(AndroidUtilities.rectTmp, .2f * getMeasuredHeight(), .2f * getMeasuredHeight(), gradientPaint); + canvas.drawRoundRect(AndroidUtilities.rectTmp, r, r, gradientPaint); strokeGradient.setLocalMatrix(gradientMatrix); strokeGradientPaint.setShader(strokeGradient); final float sw = AndroidUtilities.dpf2(1.5f); strokeGradientPaint.setStrokeWidth(sw); AndroidUtilities.rectTmp.inset(sw / 2f, sw / 2f); - canvas.drawRoundRect(AndroidUtilities.rectTmp, .2f * getMeasuredHeight() - sw / 2f, .2f * getMeasuredHeight() - sw / 2f, strokeGradientPaint); + canvas.drawRoundRect(AndroidUtilities.rectTmp, r - sw / 2f, r - sw / 2f, strokeGradientPaint); invalidate(); } 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 0600c14e01..9e79edbd75 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/StoryViewer.java @@ -82,7 +82,7 @@ import java.util.ArrayList; import java.util.Objects; -public class StoryViewer implements NotificationCenter.NotificationCenterDelegate { +public class StoryViewer implements NotificationCenter.NotificationCenterDelegate, BaseFragment.AttachedSheet { public static boolean animationInProgress; @@ -161,6 +161,7 @@ public class StoryViewer implements NotificationCenter.NotificationCenterDelegat public final TransitionViewHolder transitionViewHolder = new TransitionViewHolder(); public PlaceProvider placeProvider; Dialog currentDialog; + BaseFragment.AttachedSheet currentSheet; private boolean allowTouchesByViewpager = false; boolean openedFromLightNavigationBar; ArrayList doOnAnimationReadyRunnables = new ArrayList<>(); @@ -1729,7 +1730,8 @@ public void checkSelfStoriesView() { } } - public void showDialog(Dialog dialog) { + @Override + public boolean showDialog(Dialog dialog) { try { currentDialog = dialog; dialog.setOnDismissListener(dialog1 -> { @@ -1740,12 +1742,28 @@ public void showDialog(Dialog dialog) { }); dialog.show(); updatePlayingMode(); + return true; } catch (Throwable e) { FileLog.e(e); currentDialog = null; + return false; } } + public boolean listenToAttachedSheet(BaseFragment.AttachedSheet sheet) { + currentSheet = sheet; + sheet.setOnDismissListener(() -> { + currentSheet = null; + updatePlayingMode(); + }); + return true; + } + + @Override + public void setOnDismissListener(Runnable onDismiss) { + // not used + } + public void cancelSwipeToReply() { if (swipeToReplyBackAnimator == null) { inSwipeToDissmissMode = false; @@ -1924,11 +1942,15 @@ private void updateTransitionParams() { if (storyItem == null && isSingleStory) { storyItem = singleStory; } - if (storiesList != null) { + long did = storiesViewPager.getCurrentDialogId(); + if (storiesList instanceof StoriesController.SearchStoriesList && storyItem != null) { + did = storyItem.dialogId; + storyId = storyItem.messageId; + } else if (storiesList != null) { storyId = dayStoryId; } transitionViewHolder.clear(); - if (placeProvider.findView(storiesViewPager.getCurrentDialogId(), messageId, storyId, storyItem == null ? -1 : storyItem.messageType, transitionViewHolder)) { + if (placeProvider.findView(did, messageId, storyId, storyItem == null ? -1 : storyItem.messageType, transitionViewHolder)) { transitionViewHolder.storyId = storyId; if (transitionViewHolder.view != null) { int[] loc = new int[2]; @@ -2031,6 +2053,7 @@ public boolean isPaused() { isInTouchMode || keyboardVisible || currentDialog != null || + currentSheet != null || allowTouchesByViewpager || isClosed || isRecording || @@ -2359,15 +2382,19 @@ public void onAnimationEnd(Animator animation) { release(); try { AndroidUtilities.runOnUIThread(() -> { - if (windowView == null) { - return; - } - if (ATTACH_TO_FRAGMENT) { - AndroidUtilities.removeFromParent(windowView); - } else { - windowManager.removeView(windowView); + try { + if (windowView == null) { + return; + } + if (ATTACH_TO_FRAGMENT) { + AndroidUtilities.removeFromParent(windowView); + } else { + windowManager.removeView(windowView); + } + windowView = null; + } catch (Exception e2) { + } - windowView = null; }); } catch (Exception e) { @@ -2388,6 +2415,7 @@ public void onAnimationEnd(Animator animation) { }, 16); } + @Override public void release() { lastUri = null; setInTouchMode(false); @@ -2403,8 +2431,8 @@ public void release() { MessagesController.getInstance(currentAccount).getStoriesController().stopAllPollers(); if (ATTACH_TO_FRAGMENT) { lockOrientation(false); - if (fragment != null && fragment.storyViewerStack != null) { - fragment.storyViewerStack.remove(this); + if (fragment != null && fragment.sheetsStack != null) { + fragment.sheetsStack.remove(this); } } @@ -2426,6 +2454,17 @@ public void close(boolean backAnimation) { } } + @Override + public View getWindowView() { + return windowView; + } + + @Override + public void dismiss() { + close(true); + } + + @Override public int getNavigationBarColor(int currentColor) { return ColorUtils.blendARGB(currentColor, Color.BLACK, getBlackoutAlpha()); } @@ -2434,6 +2473,7 @@ private float getBlackoutAlpha() { return progressToOpen * (0.5f + 0.5f * (1f - progressToDismiss)); } + @Override public boolean onBackPressed() { if (selfStoriesViewsOffset != 0) { if (selfStoryViewsView.onBackPressed()) { @@ -2449,6 +2489,7 @@ public boolean onBackPressed() { return true; } + @Override public boolean isShown() { return !isClosed; } @@ -2476,6 +2517,7 @@ private void setNavigationButtonsColor(boolean isOpening) { } } + @Override public boolean attachedToParent() { if (ATTACH_TO_FRAGMENT) { return windowView != null; @@ -2494,6 +2536,7 @@ public void setKeyboardHeightFromParent(int keyboardHeight) { } } + @Override public boolean isFullyVisible() { return fullyVisible; } @@ -2550,6 +2593,9 @@ public void dismissVisibleDialogs() { if (currentDialog != null) { currentDialog.dismiss(); } + if (currentSheet != null) { + currentSheet.dismiss(); + } PeerStoriesView peerView = getCurrentPeerView(); if (peerView != null) { if (peerView.reactionsContainerLayout != null && peerView.reactionsContainerLayout.getReactionsWindow() != null) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ButtonWithCounterView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ButtonWithCounterView.java index 8321e405c2..5b1e963678 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ButtonWithCounterView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/ButtonWithCounterView.java @@ -78,7 +78,7 @@ public ButtonWithCounterView(Context context, boolean filled, Theme.ResourcesPro } text.setGravity(Gravity.CENTER_HORIZONTAL); - subText = new AnimatedTextView.AnimatedTextDrawable(true, true, false); + subText = new AnimatedTextView.AnimatedTextDrawable(subTextSplitToWords(), true, false); subText.setAnimationProperties(.3f, 0, 250, CubicBezierInterpolator.EASE_OUT_QUINT); subText.setCallback(this); subText.setTextSize(dp(12)); @@ -96,6 +96,10 @@ public ButtonWithCounterView(Context context, boolean filled, Theme.ResourcesPro updateColors(); } + protected boolean subTextSplitToWords() { + return true; + } + public void disableRippleView() { removeView(rippleView); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DownloadButton.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DownloadButton.java index 975d73d465..58111987a6 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DownloadButton.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/DownloadButton.java @@ -382,7 +382,7 @@ public static class PreparingVideoToast extends View { private float doneLayoutWidth, doneLayoutLeft; public PreparingVideoToast(Context context) { - this(context, LocaleController.getString(R.string.PreparingSticker)); + this(context, LocaleController.getString(R.string.PreparingVideo)); } public PreparingVideoToast(Context context, String text) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/EmojiBottomSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/EmojiBottomSheet.java index 4b9df4609a..9bb84625a5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/EmojiBottomSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/EmojiBottomSheet.java @@ -18,6 +18,7 @@ import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Shader; @@ -1394,7 +1395,7 @@ public boolean canClickWidget(Integer id) { } public boolean hasWidgets() { - return onWidgetSelected != null && (canShowWidget(WIDGET_LOCATION) || canShowWidget(WIDGET_AUDIO) || canShowWidget(WIDGET_PHOTO) || canShowWidget(WIDGET_REACTION)); + return onWidgetSelected != null && (canShowWidget(WIDGET_LOCATION) || canShowWidget(WIDGET_AUDIO) || canShowWidget(WIDGET_PHOTO) || canShowWidget(WIDGET_REACTION) || canShowWidget(WIDGET_LINK)); } @Override @@ -1423,8 +1424,9 @@ private void onWidgetClick(int id) { return; } } - onWidgetSelected.run(id); - dismiss(); + if (onWidgetSelected.run(id)) { + dismiss(); + } } } @@ -1721,7 +1723,7 @@ public EmojiBottomSheet whenDocumentSelected(Utilities.Callback3Return onWidgetSelected; + private Utilities.CallbackReturn onWidgetSelected; public EmojiBottomSheet whenPlusSelected(Runnable listener) { this.onPlusSelected = listener; @@ -1735,7 +1737,7 @@ public EmojiBottomSheet whenPlusSelected(Runnable listener) { } return this; } - public EmojiBottomSheet whenWidgetSelected(Utilities.Callback listener) { + public EmojiBottomSheet whenWidgetSelected(Utilities.CallbackReturn listener) { this.onWidgetSelected = listener; View[] pages = viewPager.getViewPages(); for (int i = 0; i < pages.length; ++i) { @@ -2780,6 +2782,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { public static final int WIDGET_AUDIO = 1; public static final int WIDGET_PHOTO = 2; public static final int WIDGET_REACTION = 3; + public static final int WIDGET_LINK = 4; private class StoryWidgetsCell extends View { @@ -2802,7 +2805,9 @@ public StoryWidgetsCell(Context context) { if (canShowWidget(WIDGET_AUDIO)) widgets.add(new Button(WIDGET_AUDIO, R.drawable.filled_widget_music, LocaleController.getString(R.string.StoryWidgetAudio))); if (canShowWidget(WIDGET_PHOTO)) - widgets.add(new Button(WIDGET_PHOTO, R.drawable.files_gallery, LocaleController.getString(R.string.StoryWidgetPhoto))); + widgets.add(new Button(WIDGET_PHOTO, R.drawable.filled_premium_camera, LocaleController.getString(R.string.StoryWidgetPhoto))); + if (canShowWidget(WIDGET_LINK)) + widgets.add(new Button(WIDGET_LINK, R.drawable.msg_limit_links, LocaleController.getString(R.string.StoryWidgetLink)).needsPremium()); if (canShowWidget(WIDGET_REACTION)) widgets.add(new ReactionWidget()); } @@ -2825,9 +2830,11 @@ public void onAttachToWindow(boolean attached) { private class Button extends BaseWidget { Drawable drawable; + Drawable lockDrawable; StaticLayout layout; float textWidth; float textLeft; + Paint lockPaint; public Button(int id, int iconId, String string) { this.id = id; @@ -2842,12 +2849,25 @@ public Button(int id, int iconId, String string) { this.height = dpf2(36); } + public Button needsPremium() { + if (!UserConfig.getInstance(currentAccount).isPremium()) { + lockDrawable = getContext().getResources().getDrawable(R.drawable.msg_mini_lock3).mutate(); + lockDrawable.setColorFilter(new PorterDuffColorFilter(Theme.multAlpha(Color.WHITE, .60f), PorterDuff.Mode.SRC_IN)); + lockPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + lockPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); + } + return this; + } + public void draw(Canvas canvas, float left, float top) { bounds.set(left, top, left + width, top + height); final float scale = bounce.getScale(.05f); canvas.save(); canvas.scale(scale, scale, bounds.centerX(), bounds.centerY()); canvas.drawRoundRect(bounds, dp(8), dp(8), bgPaint); + if (lockDrawable != null) { + canvas.saveLayerAlpha(bounds, 0xFF, Canvas.ALL_SAVE_FLAG); + } drawable.setBounds( (int) (bounds.left + dp(6)), (int) (bounds.top + height / 2 - dp(24) / 2), @@ -2855,6 +2875,27 @@ public void draw(Canvas canvas, float left, float top) { (int) (bounds.top + height / 2 + dp(24) / 2) ); drawable.draw(canvas); + if (lockDrawable != null) { + AndroidUtilities.rectTmp.set( + bounds.left + dp(6 + 24 - 12 + .55f), + bounds.top + height - dp(5) - dp(12 + .55f), + bounds.left + dp(6 + 24 - .55f), + bounds.left + dp(6 + 24 + 1) + ); + canvas.drawRoundRect( + AndroidUtilities.rectTmp, + dp(6), dp(6), + lockPaint + ); + lockDrawable.setBounds( + (int) (bounds.left + dp(6 + 24 - 12)), + (int) (bounds.top + height - dp(5) - dp(12)), + (int) (bounds.left + dp(6 + 24)), + (int) (bounds.top + height - dp(5)) + ); + lockDrawable.draw(canvas); + canvas.restore(); + } canvas.translate(bounds.left + dp(6 + 24 + 4) - textLeft, bounds.top + height / 2 - layout.getHeight() / 2f); layout.draw(canvas); canvas.restore(); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintView2.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintView2.java index 6bc251a7d3..3e13cdbaca 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintView2.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/HintView2.java @@ -131,6 +131,10 @@ public class HintView2 extends View { private int iconWidth, iconHeight; private boolean iconLeft; + public HintView2(Context context) { + this(context, 0); + } + public HintView2(Context context, int direction) { super(context); this.direction = direction; @@ -146,6 +150,11 @@ public HintView2(Context context, int direction) { setTextColor(0xffffffff); } + public HintView2 setDirection(int direction) { + this.direction = direction; + return this; + } + public HintView2 setRounding(float roundingDp) { this.rounding = dp(roundingDp); backgroundPaint.setPathEffect(new CornerPathEffect(rounding)); 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 0d3e37a4ff..7ca22d37e8 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 @@ -3,6 +3,8 @@ 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.messenger.LocaleController.formatPluralString; +import static org.telegram.messenger.LocaleController.getString; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -86,6 +88,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.Theme; import org.telegram.ui.BubbleActivity; import org.telegram.ui.Cells.ChatMessageCell; @@ -113,6 +116,8 @@ import org.telegram.ui.Components.Paint.Views.EditTextOutline; import org.telegram.ui.Components.Paint.Views.EntitiesContainerView; import org.telegram.ui.Components.Paint.Views.EntityView; +import org.telegram.ui.Components.Paint.Views.LinkPreview; +import org.telegram.ui.Components.Paint.Views.LinkView; import org.telegram.ui.Components.Paint.Views.LocationView; import org.telegram.ui.Components.Paint.Views.MessageEntityView; import org.telegram.ui.Components.Paint.Views.PaintCancelView; @@ -128,6 +133,7 @@ import org.telegram.ui.Components.Paint.Views.StickerView; import org.telegram.ui.Components.Paint.Views.TextPaintView; import org.telegram.ui.Components.Point; +import org.telegram.ui.Components.Premium.PremiumFeatureBottomSheet; import org.telegram.ui.Components.RLottieDrawable; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.Reactions.ReactionsUtils; @@ -137,6 +143,7 @@ import org.telegram.ui.Components.SizeNotifierFrameLayoutPhoto; import org.telegram.ui.LaunchActivity; import org.telegram.ui.PhotoViewer; +import org.telegram.ui.PremiumPreviewFragment; import org.telegram.ui.Stories.DarkThemeResourceProvider; import org.telegram.ui.WrappedResourceProvider; @@ -277,9 +284,10 @@ public void set(float val) { private boolean invalidateReactionPosition; private BlurringShader.BlurManager blurManager; private PreviewView.TextureViewHolder videoTextureHolder; + private PreviewView previewView; @SuppressLint("NotifyDataSetChanged") - public PaintView(Context context, boolean fileFromGallery, File file, boolean isVideo, StoryRecorder.WindowView parent, Activity activity, int currentAccount, Bitmap bitmap, Bitmap blurBitmap, Bitmap originalBitmap, int originalRotation, ArrayList entities, StoryEntry entry, int viewWidth, int viewHeight, MediaController.CropState cropState, Runnable onInit, BlurringShader.BlurManager blurManager, Theme.ResourcesProvider resourcesProvider, PreviewView.TextureViewHolder videoTextureHolder) { + public PaintView(Context context, boolean fileFromGallery, File file, boolean isVideo, StoryRecorder.WindowView parent, Activity activity, int currentAccount, Bitmap bitmap, Bitmap blurBitmap, Bitmap originalBitmap, int originalRotation, ArrayList entities, StoryEntry entry, int viewWidth, int viewHeight, MediaController.CropState cropState, Runnable onInit, BlurringShader.BlurManager blurManager, Theme.ResourcesProvider resourcesProvider, PreviewView.TextureViewHolder videoTextureHolder, PreviewView previewView) { super(context, activity, true); setDelegate(this); this.blurManager = blurManager; @@ -290,6 +298,7 @@ public PaintView(Context context, boolean fileFromGallery, File file, boolean is this.parent = parent; this.w = viewWidth; this.h = viewHeight; + this.previewView = previewView; this.currentAccount = currentAccount; this.resourcesProvider = new Theme.ResourcesProvider() { @@ -627,7 +636,7 @@ public boolean onTouchEvent(MotionEvent event) { zoomOutText.setTextColor(Color.WHITE); zoomOutText.setTypeface(AndroidUtilities.bold()); zoomOutText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); - zoomOutText.setText(LocaleController.getString(R.string.PhotoEditorZoomOut)); + zoomOutText.setText(getString(R.string.PhotoEditorZoomOut)); zoomOutImage = new ImageView(context); zoomOutImage.setImageResource(R.drawable.photo_zoomout); zoomOutButton.addView(zoomOutImage, LayoutHelper.createLinear(24, 24, Gravity.CENTER_VERTICAL, 0, 0, 8, 0)); @@ -641,7 +650,7 @@ public boolean onTouchEvent(MotionEvent event) { undoAllButton = new TextView(context); undoAllButton.setBackground(Theme.createSelectorDrawable(0x30ffffff, Theme.RIPPLE_MASK_ROUNDRECT_6DP)); undoAllButton.setPadding(dp(8), 0, dp(8), 0); - undoAllButton.setText(LocaleController.getString(R.string.PhotoEditorClearAll)); + undoAllButton.setText(getString(R.string.PhotoEditorClearAll)); undoAllButton.setGravity(Gravity.CENTER_VERTICAL); undoAllButton.setTextColor(Color.WHITE); undoAllButton.setTypeface(AndroidUtilities.bold()); @@ -652,7 +661,7 @@ public boolean onTouchEvent(MotionEvent event) { cancelTextButton = new TextView(context); cancelTextButton.setBackground(Theme.createSelectorDrawable(0x30ffffff, Theme.RIPPLE_MASK_ROUNDRECT_6DP)); - cancelTextButton.setText(LocaleController.getString(R.string.Clear)); + cancelTextButton.setText(getString(R.string.Clear)); cancelTextButton.setPadding(dp(8), 0, dp(8), 0); cancelTextButton.setGravity(Gravity.CENTER_VERTICAL); cancelTextButton.setTextColor(Color.WHITE); @@ -674,7 +683,7 @@ public boolean onTouchEvent(MotionEvent event) { doneTextButton = new TextView(context); doneTextButton.setBackground(Theme.createSelectorDrawable(0x30ffffff, Theme.RIPPLE_MASK_ROUNDRECT_6DP)); - doneTextButton.setText(LocaleController.getString(R.string.Done)); + doneTextButton.setText(getString(R.string.Done)); doneTextButton.setPadding(dp(8), 0, dp(8), 0); doneTextButton.setGravity(Gravity.CENTER_VERTICAL); doneTextButton.setTextColor(Color.WHITE); @@ -1167,6 +1176,37 @@ private LocationView createLocationSticker(TLRPC.MessageMedia location, TL_stori return view; } + private LinkView createLinkSticker(LinkPreview.WebPagePreview link, TL_stories.MediaArea mediaArea, boolean select) { + onTextAdd(); + + forceChanges = true; + + Size paintingSize = getPaintingSize(); + Point position = startPositionRelativeToEntity(null); + float w = entitiesView.getMeasuredWidth() <= 0 ? this.w : entitiesView.getMeasuredWidth(); + int maxWidth = (int) w - dp(14 + 26 + 18); + LinkView view = new LinkView(getContext(), position, currentAccount, link, mediaArea, w / 360f, maxWidth, 3, colorSwatch == null ? 0xFFFFFFFF : colorSwatch.color); + if (position.x == entitiesView.getMeasuredWidth() / 2f) { + view.setStickyX(EntityView.STICKY_CENTER); + } + if (position.y == entitiesView.getMeasuredHeight() / 2f) { + view.setStickyY(EntityView.STICKY_CENTER); + } + view.setDelegate(this); + view.setMaxWidth(maxWidth); + entitiesView.addView(view, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); + if (currentCropState != null) { + view.scale(1.0f / currentCropState.cropScale); + view.rotate(-(currentCropState.transformRotation + currentCropState.cropRotate)); + } + + if (select) { + registerRemovalUndo(view); + selectEntity(view, false); + } + return view; + } + private TextPaintView createText(boolean select) { onTextAdd(); @@ -1309,6 +1349,8 @@ private boolean selectEntity(EntityView entityView, boolean changeOptions) { if (!entityView.hadMultitouch()) { if (entityView instanceof LocationView) { ((LocationView) entityView).setType((((LocationView) entityView).getType() + 1) % 4); + } else if (entityView instanceof LinkView) { + ((LinkView) entityView).setType((((LinkView) entityView).getType() + 1) % 4); } else if (!editingText) { if (entityView instanceof TextPaintView) { enteredThroughText = true; @@ -1359,7 +1401,7 @@ private boolean selectEntity(EntityView entityView, boolean changeOptions) { if (currentEntityView != null) { currentEntityView.select(selectionContainerView); - entitiesView.bringChildToFront(currentEntityView); +// entitiesView.bringChildToFront(currentEntityView); if (currentEntityView instanceof TextPaintView) { TextPaintView textPaintView = (TextPaintView) currentEntityView; @@ -1609,7 +1651,7 @@ protected void onDraw(Canvas canvas) { bottomLayout.addView(tabsLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 32 + 8, Gravity.BOTTOM, 52, 0, 52, 0)); drawTab = new TextView(context); - drawTab.setText(LocaleController.getString(R.string.PhotoEditorDraw).toUpperCase()); + drawTab.setText(getString(R.string.PhotoEditorDraw).toUpperCase()); drawTab.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector), Theme.RIPPLE_MASK_ROUNDRECT_6DP)); drawTab.setPadding(0, dp(8), 0, dp(8)); drawTab.setTextColor(Color.WHITE); @@ -1627,7 +1669,7 @@ protected void onDraw(Canvas canvas) { tabsLayout.addView(drawTab, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, 1f)); stickerTab = new TextView(context); - stickerTab.setText(LocaleController.getString(R.string.PhotoEditorSticker).toUpperCase()); + stickerTab.setText(getString(R.string.PhotoEditorSticker).toUpperCase()); stickerTab.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector), Theme.RIPPLE_MASK_ROUNDRECT_6DP)); stickerTab.setPadding(0, dp(8), 0, dp(8)); stickerTab.setOnClickListener(v -> openStickersView()); @@ -1640,7 +1682,7 @@ protected void onDraw(Canvas canvas) { tabsLayout.addView(stickerTab, LayoutHelper.createLinear(0, LayoutHelper.WRAP_CONTENT, 1f)); textTab = new TextView(context); - textTab.setText(LocaleController.getString(R.string.PhotoEditorText).toUpperCase()); + textTab.setText(getString(R.string.PhotoEditorText).toUpperCase()); textTab.setBackground(Theme.createSelectorDrawable(getThemedColor(Theme.key_listSelector), Theme.RIPPLE_MASK_ROUNDRECT_6DP)); textTab.setPadding(0, dp(8), 0, dp(8)); textTab.setTextColor(Color.WHITE); @@ -1784,7 +1826,7 @@ public boolean canClickWidget(Integer widgetId) { if (widgetsCount >= MessagesController.getInstance(currentAccount).storiesSuggestedReactionsLimitPremium) { container.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); BulletinFactory.of(container, resourcesProvider).createSimpleBulletin(R.raw.chats_infotip, - LocaleController.getString("LimitReached", R.string.LimitReached), + getString("LimitReached", R.string.LimitReached), LocaleController.formatPluralString("StoryReactionsWidgetLimit2", MessagesController.getInstance(currentAccount).storiesSuggestedReactionsLimitPremium) ).show(true); return false; @@ -1821,17 +1863,80 @@ protected boolean checkAudioPermission(Runnable granted) { if (widgetId == EmojiBottomSheet.WIDGET_LOCATION) { closing[0] = false; showLocationAlert(null, (location, area) -> appearAnimation(createLocationSticker(location, area, false))); + return true; } else if (widgetId == EmojiBottomSheet.WIDGET_PHOTO) { alert.dismiss(); onGalleryClick(); + return true; } else if (widgetId == EmojiBottomSheet.WIDGET_AUDIO) { closing[0] = false; showAudioAlert(this::onAudioSelect); + return true; } else if (widgetId == EmojiBottomSheet.WIDGET_REACTION) { forceChanges = true; ReactionWidgetEntityView reactionWidget = createReactionWidget(true); appearAnimation(reactionWidget); + return true; + } else if (widgetId == EmojiBottomSheet.WIDGET_LINK) { + if (!UserConfig.getInstance(currentAccount).isPremium()) { + alert.container.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + BulletinFactory.of(alert.container, resourcesProvider).createSimpleBulletin(R.raw.star_premium_2, + AndroidUtilities.premiumText(getString(R.string.StoryLinkPremium), () -> { + BaseFragment fragment = new BaseFragment() { + @Override + public int getCurrentAccount() { + return currentAccount; + } + @Override + public Context getContext() { + return PaintView.this.getContext(); + } + @Override + public Activity getParentActivity() { + return AndroidUtilities.findActivity(PaintView.this.getContext()); + } + + @Override + public Theme.ResourcesProvider getResourceProvider() { + return PaintView.this.resourcesProvider; + } + + @Override + public boolean presentFragment(BaseFragment fragment) { + BaseFragment fragment1 = LaunchActivity.getLastFragment(); + if (fragment1 == null) return false; + BaseFragment.BottomSheetParams bottomSheetParams = new BaseFragment.BottomSheetParams(); + bottomSheetParams.transitionFromLeft = true; + bottomSheetParams.allowNestedScroll = false; + fragment1.showAsSheet(fragment, bottomSheetParams); + return true; + } + }; + new PremiumFeatureBottomSheet(fragment, PremiumPreviewFragment.PREMIUM_FEATURE_STORIES, true).show(); + }) + ).show(true); + return false; + } + + int linksCount = 0; + for (int i = 0; i < entitiesView.getChildCount(); ++i) { + if (entitiesView.getChildAt(i) instanceof LinkView) + linksCount++; + } + + final int limit = 3; + if (linksCount >= limit) { + BulletinFactory.of(alert.container, resourcesProvider).createSimpleBulletin(R.raw.linkbroken, getString(R.string.StoryLinkLimitTitle), formatPluralString("StoryLinkLimitMessage", limit)).show(true); + return false; + } + + closing[0] = false; + showLinkAlert(null); + alert.dismiss(); + + return true; } + return false; }); alert.show(); onOpenCloseStickersAlert(true); @@ -1841,6 +1946,25 @@ protected boolean checkAudioPermission(Runnable granted) { return true; } + private void showLinkAlert(LinkView editingLinkView) { + StoryLinkSheet sheet = new StoryLinkSheet(getContext(), resourcesProvider, previewView, media -> { + if (editingLinkView != null) { + editingLinkView.setLink(currentAccount, media, null); + appearAnimation(editingLinkView); + } else { + appearAnimation(createLinkSticker(media, null, false)); + } + }); + if (editingLinkView != null) { + sheet.set(editingLinkView.link); + } + sheet.setOnDismissListener(di -> { + onOpenCloseStickersAlert(false); + }); + sheet.show(); + onOpenCloseStickersAlert(true); + } + private void showLocationAlert(LocationView editingLocationView, Utilities.Callback2 onLocationSelected) { ChatAttachAlert locationAlert = new ChatAttachAlert(getContext(), new ChatActivity(null) { @Override @@ -1885,6 +2009,10 @@ public void didSelectLocation(TLRPC.MessageMedia location, int locationType, boo if (loc.query_id == -1 || loc.query_id == -2) { TL_stories.TL_mediaAreaGeoPoint areaGeo = new TL_stories.TL_mediaAreaGeoPoint(); areaGeo.geo = location.geo; + areaGeo.address = ((TLRPC.TL_messageMediaVenue) location).geoAddress; + if (areaGeo.address != null) { + areaGeo.flags |= 1; + } Utilities.globalQueue.postRunnable(() -> { try { Geocoder gcd = new Geocoder(ApplicationLoader.applicationContext, LocaleController.getInstance().getCurrentLocale()); @@ -2173,9 +2301,13 @@ private void setupEntities() { layoutParams.height = entity.viewHeight; } } else if (entity.type == VideoEditedInfo.MediaEntity.TYPE_LOCATION) { - LocationView locationView = createLocationSticker(entity.mediaGeo, entity.mediaArea, false); + LocationView locationView = createLocationSticker(entity.media, entity.mediaArea, false); locationView.setType(entity.subType, entity.color); view = locationView; + } else if (entity.type == VideoEditedInfo.MediaEntity.TYPE_LINK) { + LinkView linkView = createLinkSticker(entity.linkSettings, entity.mediaArea, false); + linkView.setType(entity.subType, entity.color); + view = linkView; } else if (entity.type == VideoEditedInfo.MediaEntity.TYPE_REACTION) { ReactionWidgetEntityView entityView = createReactionWidget(false); entityView.setCurrentReaction(ReactionsLayoutInBubble.VisibleReaction.fromTL(entity.mediaArea.reaction), false); @@ -2478,7 +2610,7 @@ public Bitmap getBitmap(ArrayList entities, int res mediaEntity.text = locationView.marker.getText(); mediaEntity.color = locationView.getColor(); mediaEntity.density = locationView.marker.density; - mediaEntity.mediaGeo = locationView.location; + mediaEntity.media = locationView.location; mediaEntity.mediaArea = locationView.mediaArea; mediaEntity.mediaArea.coordinates = new TL_stories.TL_mediaAreaCoordinates(); TLRPC.Document emojiDocument = locationView.marker.getCountryCodeEmojiDocument(); @@ -2493,6 +2625,26 @@ public Bitmap getBitmap(ArrayList entities, int res } mediaEntity.entities.add(tlentity); } + } else if (entity instanceof LinkView) { + LinkView linkView = (LinkView) entity; + mediaEntity.type = VideoEditedInfo.MediaEntity.TYPE_LINK; + mediaEntity.subType = (byte) linkView.getType(); + mediaEntity.width = linkView.marker.getWidth(); + mediaEntity.height = linkView.marker.getHeight(); + mediaEntity.color = linkView.getColor(); + mediaEntity.density = linkView.marker.density; + mediaEntity.linkSettings = linkView.link; + if (linkView.marker.hasPhoto) { + linkView.marker.pushPhotoToCache(); + mediaEntity.linkSettings.flags |= 4; + mediaEntity.linkSettings.photoSize = linkView.marker.getPhotoSide(); + } + mediaEntity.mediaArea = new TL_stories.TL_mediaAreaUrl(); + if (linkView.link == null) { + continue; + } + ((TL_stories.TL_mediaAreaUrl) mediaEntity.mediaArea).url = linkView.link.webpage != null && !TextUtils.isEmpty(linkView.link.webpage.url) ? linkView.link.webpage.url : linkView.link.url; + mediaEntity.mediaArea.coordinates = new TL_stories.TL_mediaAreaCoordinates(); } else if (entity instanceof ReactionWidgetEntityView) { skipDrawToBitmap = true; ReactionWidgetEntityView reactionView = (ReactionWidgetEntityView) entity; @@ -2580,9 +2732,10 @@ public Bitmap getBitmap(ArrayList entities, int res mediaEntity.textViewHeight = mediaEntity.viewHeight / (float) entitiesView.getMeasuredHeight(); mediaEntity.scale = scaleX; + double radius = -1; if (entity instanceof MessageEntityView) { MessageEntityView mv = (MessageEntityView) entity; - mv.getBubbleBounds(AndroidUtilities.rectTmp); + radius = mv.getBubbleBounds(AndroidUtilities.rectTmp); AndroidUtilities.rectTmp.offset(mv.container.getX(), mv.container.getY()); AndroidUtilities.rectTmp.offset(mv.listView.getX(), mv.listView.getY()); mediaEntity.mediaArea.coordinates.x = (x + v.getWidth() / 2f - v.getWidth() / 2f * scaleX + AndroidUtilities.rectTmp.centerX() * scaleX) / entitiesView.getMeasuredWidth() * 100; @@ -2605,12 +2758,15 @@ public Bitmap getBitmap(ArrayList entities, int res mediaEntity.viewWidth = (int) (mediaEntity.viewHeight * a); mediaEntity.x = cx - mediaEntity.width / 2f; } - } else if (entity instanceof LocationView || entity instanceof ReactionWidgetEntityView) { + } else if (entity instanceof LocationView || entity instanceof LinkView || entity instanceof ReactionWidgetEntityView) { mediaEntity.mediaArea.coordinates.x = (mediaEntity.x + mediaEntity.width / 2f) * 100; mediaEntity.mediaArea.coordinates.y = (mediaEntity.y + mediaEntity.height / 2f) * 100; if (entity instanceof LocationView) { mediaEntity.mediaArea.coordinates.w = (mediaEntity.width - 2 * ((LocationView) entity).marker.padx * scaleX / (float) entitiesView.getMeasuredWidth()) * 100; mediaEntity.mediaArea.coordinates.h = (mediaEntity.height - 2 * ((LocationView) entity).marker.pady * scaleY / (float) entitiesView.getMeasuredHeight()) * 100; + } else if (entity instanceof LinkView) { + mediaEntity.mediaArea.coordinates.w = (mediaEntity.width - 2 * ((LinkView) entity).marker.padx * scaleX / (float) entitiesView.getMeasuredWidth()) * 100; + mediaEntity.mediaArea.coordinates.h = (mediaEntity.height - 2 * ((LinkView) entity).marker.pady * scaleY / (float) entitiesView.getMeasuredHeight()) * 100; } else if (entity instanceof ReactionWidgetEntityView) { float padW = 2 * ((ReactionWidgetEntityView) entity).getPadding() * scaleX / (float) entitiesView.getMeasuredWidth(); float padH = 2 * ((ReactionWidgetEntityView) entity).getPadding() * scaleX / (float) entitiesView.getMeasuredHeight(); @@ -2618,6 +2774,15 @@ public Bitmap getBitmap(ArrayList entities, int res mediaEntity.mediaArea.coordinates.h = (mediaEntity.height - padH) * 100; } mediaEntity.mediaArea.coordinates.rotation = -mediaEntity.rotation / Math.PI * 180; + if (entity instanceof LocationView) { + radius = ((LocationView) entity).marker.getRadius(); + } else if (entity instanceof LinkView) { + radius = ((LinkView) entity).marker.getRadius(); + } + } + if (mediaEntity.mediaArea != null && mediaEntity.mediaArea.coordinates != null && radius > 0) { + mediaEntity.mediaArea.coordinates.flags |= 1; + mediaEntity.mediaArea.coordinates.radius = (scaleX * radius / (float) entitiesView.getMeasuredWidth()) * 100; } } if ((drawEntities || drawMessage && mediaEntity.type == VideoEditedInfo.MediaEntity.TYPE_MESSAGE) && bitmap != null) { @@ -2716,10 +2881,10 @@ public void maybeShowDismissalAlert(PhotoViewer photoViewer, Activity parentActi return; } AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity, resourcesProvider); - builder.setMessage(LocaleController.getString("PhotoEditorDiscardAlert", R.string.PhotoEditorDiscardAlert)); - builder.setTitle(LocaleController.getString("DiscardChanges", R.string.DiscardChanges)); - builder.setPositiveButton(LocaleController.getString("PassportDiscard", R.string.PassportDiscard), (dialogInterface, i) -> okRunnable.run()); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.setMessage(getString("PhotoEditorDiscardAlert", R.string.PhotoEditorDiscardAlert)); + builder.setTitle(getString("DiscardChanges", R.string.DiscardChanges)); + builder.setPositiveButton(getString("PassportDiscard", R.string.PassportDiscard), (dialogInterface, i) -> okRunnable.run()); + builder.setNegativeButton(getString("Cancel", R.string.Cancel), null); photoViewer.showAlertDialog(builder); } else { okRunnable.run(); @@ -2747,10 +2912,10 @@ public void maybeDismiss(Activity parentActivity, Runnable okRunnable) { return; } AlertDialog.Builder builder = new AlertDialog.Builder(parentActivity, resourcesProvider); - builder.setMessage(LocaleController.getString("PhotoEditorDiscardAlert", R.string.PhotoEditorDiscardAlert)); - builder.setTitle(LocaleController.getString("DiscardChanges", R.string.DiscardChanges)); - builder.setPositiveButton(LocaleController.getString("PassportDiscard", R.string.PassportDiscard), (dialogInterface, i) -> okRunnable.run()); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.setMessage(getString("PhotoEditorDiscardAlert", R.string.PhotoEditorDiscardAlert)); + builder.setTitle(getString("DiscardChanges", R.string.DiscardChanges)); + builder.setPositiveButton(getString("PassportDiscard", R.string.PassportDiscard), (dialogInterface, i) -> okRunnable.run()); + builder.setNegativeButton(getString("Cancel", R.string.Cancel), null); builder.show(); } else { okRunnable.run(); @@ -3139,6 +3304,8 @@ private void setCurrentSwatch(Swatch swatch, boolean updateInterface, Integer pr ((TextPaintView) currentEntityView).setSwatch(new Swatch(swatch.color, swatch.colorLocation, swatch.brushWeight)); } else if (currentEntityView instanceof LocationView) { ((LocationView) currentEntityView).setColor(swatch.color); + } else if (currentEntityView instanceof LinkView) { + ((LinkView) currentEntityView).setColor(swatch.color); } } @@ -3500,10 +3667,10 @@ private void showMenuForEntity(final EntityView entityView) { deleteView.setSingleLine(); deleteView.setEllipsize(TextUtils.TruncateAt.END); deleteView.setTypeface(AndroidUtilities.bold()); - deleteView.setPadding(dp(16), 0, dp(16), 0); + deleteView.setPadding(dp(14), 0, dp(14), 0); deleteView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); deleteView.setTag(0); - deleteView.setText(LocaleController.getString("PaintDelete", R.string.PaintDelete)); + deleteView.setText(getString("PaintDelete", R.string.PaintDelete)); deleteView.setOnClickListener(v -> { if (entityView instanceof RoundView) { onTryDeleteRound(); @@ -3526,11 +3693,11 @@ private void showMenuForEntity(final EntityView entityView) { editView.setSingleLine(); editView.setEllipsize(TextUtils.TruncateAt.END); editView.setTypeface(AndroidUtilities.bold()); - editView.setPadding(dp(16), 0, dp(16), 0); + editView.setPadding(dp(14), 0, dp(14), 0); editView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); if ((keyboardNotifier.keyboardVisible() && !keyboardNotifier.ignoring) || emojiPadding > 0) { editView.setTag(3); - editView.setText(LocaleController.getString("Paste", R.string.Paste)); + editView.setText(getString("Paste", R.string.Paste)); editView.setOnClickListener(v -> { try { EditText editText = ((TextPaintView) entityView).getEditText(); @@ -3544,7 +3711,7 @@ private void showMenuForEntity(final EntityView entityView) { }); } else { editView.setTag(1); - editView.setText(LocaleController.getString("PaintEdit", R.string.PaintEdit)); + editView.setText(getString("PaintEdit", R.string.PaintEdit)); editView.setOnClickListener(v -> { selectEntity(entityView); editSelectedTextEntity(); @@ -3555,7 +3722,7 @@ private void showMenuForEntity(final EntityView entityView) { } parent.addView(editView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 44)); } else if (entityView instanceof LocationView) { - TextView editView = createActionLayoutButton(1, LocaleController.getString("PaintEdit", R.string.PaintEdit)); + TextView editView = createActionLayoutButton(1, getString("PaintEdit", R.string.PaintEdit)); editView.setOnClickListener(v -> { selectEntity(null); showLocationAlert((LocationView) entityView, (location, area) -> { @@ -3567,10 +3734,20 @@ private void showMenuForEntity(final EntityView entityView) { } }); parent.addView(editView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 44)); + } else if (entityView instanceof LinkView) { + TextView editView = createActionLayoutButton(1, getString("PaintEdit", R.string.PaintEdit)); + editView.setOnClickListener(v -> { + selectEntity(null); + showLinkAlert((LinkView) entityView); + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(true); + } + }); + parent.addView(editView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 44)); } if (entityView instanceof StickerView || entityView instanceof RoundView || entityView instanceof PhotoView || entityView instanceof ReactionWidgetEntityView) { - TextView flipView = createActionLayoutButton(4, LocaleController.getString("Flip", R.string.Flip)); + TextView flipView = createActionLayoutButton(4, getString("Flip", R.string.Flip)); flipView.setOnClickListener(v -> { if (entityView instanceof StickerView) { ((StickerView) entityView).mirror(true); @@ -3590,7 +3767,7 @@ private void showMenuForEntity(final EntityView entityView) { if (entityView instanceof PhotoView && ((PhotoView) entityView).hasSegmentedImage()) { PhotoView photoView = (PhotoView) entityView; - TextView cutView = createActionLayoutButton(5, LocaleController.getString(photoView.isSegmented() ? R.string.SegmentationUndoCutOut : R.string.SegmentationCutOut)); + TextView cutView = createActionLayoutButton(5, getString(photoView.isSegmented() ? R.string.SegmentationUndoCutOut : R.string.SegmentationCutOut)); cutView.setOnClickListener(v -> { photoView.toggleSegmented(true); if (photoView.isSegmented()) { @@ -3604,7 +3781,26 @@ private void showMenuForEntity(final EntityView entityView) { photoView.highlightSegmented(); } - if (!(entityView instanceof PhotoView) && !(entityView instanceof MessageEntityView) && !(entityView instanceof RoundView) && !(entityView instanceof LocationView) && !(entityView instanceof ReactionWidgetEntityView)) { + if (entitiesView.indexOfChild(entityView) != entitiesView.getChildCount() - 1 && !(entityView instanceof ReactionWidgetEntityView)) { + TextView bringToFrontView = new TextView(getContext()); + bringToFrontView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); + bringToFrontView.setLines(1); + bringToFrontView.setSingleLine(); + bringToFrontView.setEllipsize(TextUtils.TruncateAt.END); + bringToFrontView.setGravity(Gravity.CENTER_VERTICAL); + bringToFrontView.setTypeface(AndroidUtilities.bold()); + bringToFrontView.setPadding(dp(14), 0, dp(14), 0); + bringToFrontView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + bringToFrontView.setTag(2); + bringToFrontView.setText(getString(R.string.PaintBringToFront)); + bringToFrontView.setOnClickListener(v -> { + entityView.bringToFront(); + if (popupWindow != null && popupWindow.isShowing()) { + popupWindow.dismiss(true); + } + }); + parent.addView(bringToFrontView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, 44)); + } else if (!(entityView instanceof PhotoView) && !(entityView instanceof MessageEntityView) && !(entityView instanceof RoundView) && !(entityView instanceof LocationView) && !(entityView instanceof LinkView) && !(entityView instanceof ReactionWidgetEntityView)) { TextView duplicateView = new TextView(getContext()); duplicateView.setTextColor(getThemedColor(Theme.key_actionBarDefaultSubmenuItem)); duplicateView.setLines(1); @@ -3612,10 +3808,10 @@ private void showMenuForEntity(final EntityView entityView) { duplicateView.setEllipsize(TextUtils.TruncateAt.END); duplicateView.setGravity(Gravity.CENTER_VERTICAL); duplicateView.setTypeface(AndroidUtilities.bold()); - duplicateView.setPadding(dp(16), 0, dp(16), 0); + duplicateView.setPadding(dp(14), 0, dp(14), 0); duplicateView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); duplicateView.setTag(2); - duplicateView.setText(LocaleController.getString("PaintDuplicate", R.string.PaintDuplicate)); + duplicateView.setText(getString("PaintDuplicate", R.string.PaintDuplicate)); duplicateView.setOnClickListener(v -> { duplicateEntity(entityView); @@ -3653,7 +3849,7 @@ private TextView createActionLayoutButton(int tag, String title) { textView.setSingleLine(); textView.setEllipsize(TextUtils.TruncateAt.END); textView.setTypeface(AndroidUtilities.bold()); - textView.setPadding(dp(16), 0, dp(16), 0); + textView.setPadding(dp(14), 0, dp(14), 0); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); textView.setTag(tag); textView.setText(title); @@ -3762,6 +3958,11 @@ protected void dispatchDraw(Canvas canvas) { canvas.restore(); } } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } } private void showPopup(Runnable setupRunnable, View parent, int gravity, int x, int y, boolean blurAndBounce) { @@ -3809,7 +4010,7 @@ private void showPopup(Runnable setupRunnable, View parent, int gravity, int x, popupWindow.setOnDismissListener(() -> popupLayout.removeInnerViews()); } - popupLayout.measure(MeasureSpec.makeMeasureSpec(dp(1000), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(dp(1000), MeasureSpec.AT_MOST)); + popupLayout.measure(MeasureSpec.makeMeasureSpec(dp(10000), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(dp(10000), MeasureSpec.AT_MOST)); popupWindow.setFocusable(true); @@ -4741,10 +4942,10 @@ public void onCustomEmojiSelected(long documentId, TLRPC.Document document, Stri @Override public void onClearEmojiRecent() { AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), resourcesProvider); - builder.setTitle(LocaleController.getString("ClearRecentEmojiTitle", R.string.ClearRecentEmojiTitle)); - builder.setMessage(LocaleController.getString("ClearRecentEmojiText", R.string.ClearRecentEmojiText)); - builder.setPositiveButton(LocaleController.getString("ClearButton", R.string.ClearButton), (dialogInterface, i) -> emojiView.clearRecentEmoji()); - builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); + builder.setTitle(getString("ClearRecentEmojiTitle", R.string.ClearRecentEmojiTitle)); + builder.setMessage(getString("ClearRecentEmojiText", R.string.ClearRecentEmojiText)); + builder.setPositiveButton(getString("ClearButton", R.string.ClearButton), (dialogInterface, i) -> emojiView.clearRecentEmoji()); + builder.setNegativeButton(getString("Cancel", R.string.Cancel), null); builder.show(); } }); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewView.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewView.java index 4ef467a05a..8ec3aa8afc 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/PreviewView.java @@ -49,6 +49,7 @@ import org.telegram.messenger.ChatThemeController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; +import org.telegram.messenger.SharedConfig; import org.telegram.messenger.Utilities; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.EmojiThemes; diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryLinkSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryLinkSheet.java new file mode 100644 index 0000000000..66db8814c0 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/Stories/recorder/StoryLinkSheet.java @@ -0,0 +1,555 @@ +package org.telegram.ui.Stories.recorder; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.messenger.LocaleController.getString; + +import android.content.ClipboardManager; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.text.Editable; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.collection.LongSparseArray; +import androidx.recyclerview.widget.DefaultItemAnimator; +import androidx.recyclerview.widget.RecyclerView; + +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.Utilities; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.Cells.EditTextCell; +import org.telegram.ui.Cells.TextCheckCell; +import org.telegram.ui.Components.AnimatedTextView; +import org.telegram.ui.Components.BottomSheetWithRecyclerListView; +import org.telegram.ui.Components.CircularProgressDrawable; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.LoadingSpan; +import org.telegram.ui.Components.Paint.Views.LinkPreview; +import org.telegram.ui.Components.Paint.Views.StoryLinkPreviewDialog; +import org.telegram.ui.Components.RecyclerListView; +import org.telegram.ui.Components.ScaleStateListAnimator; +import org.telegram.ui.Components.UItem; +import org.telegram.ui.Components.UniversalAdapter; + +import java.util.ArrayList; +import java.util.regex.Pattern; + +public class StoryLinkSheet extends BottomSheetWithRecyclerListView implements NotificationCenter.NotificationCenterDelegate { + + private UniversalAdapter adapter; + private EditTextCell urlEditText; + private EditTextCell nameEditText; + private FrameLayout buttonContainer; + private ButtonWithCounterView button; + + private boolean ignoreUrlEdit; + + private Utilities.Callback whenDone; + + public StoryLinkSheet(Context context, Theme.ResourcesProvider resourcesProvider, PreviewView previewView, Utilities.Callback whenDone) { + super(context, null, true, false, false, true, ActionBarType.SLIDING, resourcesProvider); + this.whenDone = whenDone; + + fixNavigationBar(); + setSlidingActionBar(); + headerPaddingTop = dp(4); + headerPaddingBottom = dp(-15); + + urlEditText = new EditTextCell(context, getString(R.string.StoryLinkURLPlaceholder), true, -1, resourcesProvider); + urlEditText.whenHitEnter(this::processDone); + + String def = "https://"; + urlEditText.editText.setHandlesColor(0xFF419FE8); + urlEditText.editText.setCursorColor(0xff54a1db); + urlEditText.editText.setText(def); + urlEditText.editText.setSelection(def.length()); + TextView pasteTextView = new TextView(getContext()); + pasteTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); + pasteTextView.setTypeface(AndroidUtilities.bold()); + pasteTextView.setText(getString(R.string.Paste)); + pasteTextView.setPadding(dp(10), 0, dp(10), 0); + pasteTextView.setGravity(Gravity.CENTER); + int textColor = getThemedColor(Theme.key_windowBackgroundWhiteBlueText2); + pasteTextView.setTextColor(textColor); + pasteTextView.setBackground(Theme.createSimpleSelectorRoundRectDrawable(dp(6), Theme.multAlpha(textColor, .12f), Theme.multAlpha(textColor, .15f))); + ScaleStateListAnimator.apply(pasteTextView, .1f, 1.5f); + urlEditText.addView(pasteTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 26, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 4, 24, 3)); + + Runnable checkPaste = () -> { + ClipboardManager clipboardManager = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); + final boolean show = (TextUtils.isEmpty(urlEditText.editText.getText()) || TextUtils.equals(urlEditText.editText.getText(), def) || TextUtils.isEmpty(urlEditText.editText.getText().toString())) && clipboardManager != null && clipboardManager.hasPrimaryClip(); + pasteTextView.animate() + .alpha(show ? 1f : 0f) + .scaleX(show ? 1f : .7f) + .scaleY(show ? 1f : .7f) + .setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT) + .setDuration(300) + .start(); + }; + pasteTextView.setOnClickListener(v -> { + ClipboardManager clipboardManager = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); + CharSequence text = null; + try { + text = clipboardManager.getPrimaryClip().getItemAt(0).coerceToText(getContext()); + } catch (Exception e) { + FileLog.e(e); + } + if (text != null) { + urlEditText.editText.setText(text.toString()); + urlEditText.editText.setSelection(0, urlEditText.editText.getText().length()); + } + checkPaste.run(); + }); + checkPaste.run(); + urlEditText.editText.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) {} + @Override + public void afterTextChanged(Editable s) { + checkPaste.run(); + if (!ignoreUrlEdit) { + checkEditURL(s == null ? null : s.toString()); + } + } + }); + + nameEditText = new EditTextCell(context, getString(R.string.StoryLinkNamePlaceholder), true, -1, resourcesProvider); + nameEditText.whenHitEnter(this::processDone); + + buttonContainer = new FrameLayout(context); + button = new ButtonWithCounterView(context, resourcesProvider); + button.setText(getString(R.string.StoryLinkAdd), false); + button.setOnClickListener(v -> processDone()); + button.setEnabled(containsURL(urlEditText.getText().toString())); + buttonContainer.addView(button, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.FILL, 10, 10, 10, 10)); + + topPadding = .2f; + takeTranslationIntoAccount = true; + smoothKeyboardAnimationEnabled = true; + smoothKeyboardByBottom = true; + DefaultItemAnimator itemAnimator = new DefaultItemAnimator() { + @Override + protected void onMoveAnimationUpdate(RecyclerView.ViewHolder holder) { + super.onMoveAnimationUpdate(holder); + containerView.invalidate(); + } + }; + itemAnimator.setSupportsChangeAnimations(false); + itemAnimator.setDelayAnimations(false); + itemAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + itemAnimator.setDurations(350); + recyclerListView.setItemAnimator(itemAnimator); + recyclerListView.setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0); + recyclerListView.setOnItemClickListener((view, position) -> { + UItem item = adapter.getItem(position - 1); + if (item == null) return; + if (item.instanceOf(WebpagePreviewView.Factory.class) && webpage != null && !isPreviewEmpty(webpage)) { + StoryLinkPreviewDialog preview = new StoryLinkPreviewDialog(context, currentAccount); + LinkPreview.WebPagePreview settings = new LinkPreview.WebPagePreview(); + settings.url = urlEditText.editText.getText().toString(); + settings.name = nameOpen ? nameEditText.editText.getText().toString() : null; + settings.webpage = webpage; + settings.largePhoto = photoLarge; + settings.captionAbove = captionAbove; + preview.set(settings, newSettings -> { + if (newSettings == null) { + closePreview(null); + } else { + photoLarge = newSettings.largePhoto; + captionAbove = newSettings.captionAbove; + } + }); + preview.setStoryPreviewView(previewView); + preview.show(); + } else if (item.id == 2) { + if (view instanceof TextCheckCell) { + ((TextCheckCell) view).setChecked(nameOpen = !nameOpen); + adapter.update(true); + if (nameOpen) { + nameEditText.requestFocus(); + } else { + urlEditText.requestFocus(); + } + } + } + }); + + if (adapter != null) { + adapter.update(false); + } + } + + private void processDone() { + if (!button.isEnabled()) { + return; + } + if (whenDone != null) { + LinkPreview.WebPagePreview settings = new LinkPreview.WebPagePreview(); + settings.url = urlEditText.editText.getText().toString(); + settings.name = nameOpen ? nameEditText.editText.getText().toString() : null; + settings.webpage = webpage; + settings.largePhoto = photoLarge; + settings.captionAbove = captionAbove; + whenDone.run(settings); + whenDone = null; + } + dismiss(); + } + + public boolean editing; + public void set(LinkPreview.WebPagePreview settings) { + ignoreUrlEdit = true; + this.editing = true; + if (settings != null) { + webpage = settings.webpage; + loading = false; + + urlEditText.setText(settings.url); + nameEditText.setText(settings.name); + nameOpen = !TextUtils.isEmpty(settings.name); + + captionAbove = settings.captionAbove; + photoLarge = settings.largePhoto; + } else { + urlEditText.setText(""); + nameEditText.setText(""); + + captionAbove = true; + photoLarge = false; + } + button.setText(getString(R.string.StoryLinkEdit), false); + if (adapter != null) { + adapter.update(false); + } + button.setEnabled(containsURL(urlEditText.getText().toString())); + ignoreUrlEdit = false; + } + + @Override + protected CharSequence getTitle() { + return getString(R.string.StoryLinkCreate); + } + + @Override + protected RecyclerListView.SelectionAdapter createAdapter(RecyclerListView listView) { + return adapter = new UniversalAdapter(recyclerListView, getContext(), currentAccount, 0, true, this::fillItems, resourcesProvider) { + @Override + protected int getThemedColor(int key) { + if (key == Theme.key_dialogBackgroundGray) + return 0xFF0D0D0D; + return super.getThemedColor(key); + } + }; + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.didReceivedWebpagesInUpdates) { + if (webpageId == 0) return; + LongSparseArray webpages = (LongSparseArray) args[0]; + for (int i = 0; i < webpages.size(); ++i) { + TLRPC.WebPage page = webpages.valueAt(i); + if (page == null) continue; + if (webpageId == page.id) { + webpage = isPreviewEmpty(page) ? null : page; + loading = false; + webpageId = 0; + if (adapter != null) { + adapter.update(true); + } + break; + } + } + } + } + + private long webpageId; + private TLRPC.WebPage webpage; + private boolean loading; + private int reqId; + private String lastCheckedStr; + + private void checkEditURL(String str) { + if (str == null) return; + if (TextUtils.equals(str, lastCheckedStr)) return; + lastCheckedStr = str; + final boolean containsURL = containsURL(str); + AndroidUtilities.cancelRunOnUIThread(requestPreview); + if (containsURL) { + if (!loading || webpage != null) { + loading = true; + webpage = null; + if (adapter != null) { + adapter.update(true); + } + } + AndroidUtilities.runOnUIThread(requestPreview, 700); + } else { + if (loading || webpage != null) { + loading = false; + webpage = null; + if (reqId != 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(reqId, true); + reqId = 0; + } + if (adapter != null) { + adapter.update(true); + } + } + } + button.setEnabled(containsURL); + } + + private final Runnable requestPreview = () -> { + TLRPC.TL_messages_getWebPagePreview req = new TLRPC.TL_messages_getWebPagePreview(); + req.message = urlEditText.editText.getText().toString(); + reqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { + if (res instanceof TLRPC.TL_messageMediaWebPage) { + webpage = ((TLRPC.TL_messageMediaWebPage) res).webpage; + if (isPreviewEmpty(webpage)) { + webpageId = webpage == null ? 0 : webpage.id; + webpage = null; + } else { + webpageId = 0; + } + } else { + webpage = null; + webpageId = 0; + } + loading = webpageId != 0; + if (adapter != null) { + adapter.update(true); + } + })); + }; + + private void closePreview(View view) { + loading = false; + webpage = null; + if (reqId != 0) { + ConnectionsManager.getInstance(currentAccount).cancelRequest(reqId, true); + reqId = 0; + } + if (adapter != null) { + adapter.update(true); + } + } + + private Pattern urlPattern; + private boolean containsURL(String str) { + if (TextUtils.isEmpty(str)) return false; + if (urlPattern == null) { + urlPattern = Pattern.compile("((https?)://|(www|ftp)\\.)?[a-z0-9-]+(\\.[a-z0-9-]+)+([/?]?.+)"); + } + return urlPattern.matcher(str).find(); + } + + private boolean nameOpen; + private boolean captionAbove; + private boolean photoLarge; + + public void fillItems(ArrayList items, UniversalAdapter adapter) { + if (loading || webpage != null) { + items.add(WebpagePreviewView.Factory.item(webpage, this::closePreview)); + } + items.add(UItem.asCustom(urlEditText)); + items.add(UItem.asShadow(1, null)); + items.add(UItem.asCheck(2, getString(R.string.StoryLinkNameHeader)).setChecked(nameOpen)); + if (nameOpen) { + items.add(UItem.asCustom(nameEditText)); + } + items.add(UItem.asShadow(3, null)); + items.add(UItem.asCustom(buttonContainer)); + } + + public static class WebpagePreviewView extends FrameLayout { + + private final Paint separatorPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private final ImageView imageView; + private final ImageView loadingView; + private final AnimatedTextView titleView; + private final AnimatedTextView messageView; + private final ImageView closeView; + + private final SpannableString titleLoading; + private final SpannableString messageLoading; + + public WebpagePreviewView(Context context) { + super(context); + setWillNotDraw(false); + + separatorPaint.setColor(0xFF000000); + + imageView = new ImageView(context); + imageView.setScaleType(ImageView.ScaleType.CENTER); + imageView.setImageResource(R.drawable.filled_link); + imageView.setColorFilter(new PorterDuffColorFilter(0xFF1A9CFF, PorterDuff.Mode.SRC_IN)); + addView(imageView, LayoutHelper.createFrame(48, 48, Gravity.CENTER_VERTICAL | Gravity.LEFT, 9, 0, 0, 0)); + + loadingView = new ImageView(context); + loadingView.setBackground(new CircularProgressDrawable(dp(20), dp(2.4f), 0xFF1A9CFF) { + @Override + public int getIntrinsicHeight() { + return dp(26); + } + + @Override + public int getIntrinsicWidth() { + return dp(26); + } + }); + addView(loadingView, LayoutHelper.createFrame(48, 48, Gravity.CENTER_VERTICAL | Gravity.LEFT, 9, 0, 0, 0)); + + titleView = new AnimatedTextView(context); + titleView.setTextColor(0xFF1A9CFF); + titleView.setTextSize(dp(14.21f)); + titleView.setTypeface(AndroidUtilities.bold()); + titleView.setEllipsizeByGradient(true); + titleView.getDrawable().setOverrideFullWidth(AndroidUtilities.displaySize.x); + addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 24, Gravity.FILL_HORIZONTAL | Gravity.TOP, 57, 2.33f, 48, 0)); + + messageView = new AnimatedTextView(context); + messageView.setTextColor(0xFF808080); + messageView.setTextSize(dp(14.21f)); + messageView.setEllipsizeByGradient(true); + messageView.getDrawable().setOverrideFullWidth(AndroidUtilities.displaySize.x); + addView(messageView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 24, Gravity.FILL_HORIZONTAL | Gravity.TOP, 57, 20.66f, 48, 0)); + + int color = titleView.getTextColor(); + titleLoading = new SpannableString("x"); + LoadingSpan span = new LoadingSpan(titleView, dp(200)); + span.setScaleY(.8f); + span.setColors(Theme.multAlpha(color, .4f), Theme.multAlpha(color, .08f)); + titleLoading.setSpan(span, 0, titleLoading.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + color = messageView.getTextColor(); + messageLoading = new SpannableString("x"); + span = new LoadingSpan(messageView, dp(140)); + span.setScaleY(.8f); + span.setColors(Theme.multAlpha(color, .4f), Theme.multAlpha(color, .08f)); + messageLoading.setSpan(span, 0, messageLoading.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + closeView = new ImageView(context); + closeView.setColorFilter(new PorterDuffColorFilter(0x64FFFFFF, PorterDuff.Mode.MULTIPLY)); + closeView.setImageResource(R.drawable.input_clear); + closeView.setScaleType(ImageView.ScaleType.CENTER); + closeView.setBackground(Theme.createSelectorDrawable(0x19ffffff, 1, AndroidUtilities.dp(18))); + addView(closeView, LayoutHelper.createFrame(48, 48, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 0, 0, 4, 0)); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + canvas.drawRect(0, 0, getWidth(), AndroidUtilities.getShadowHeight(), separatorPaint); + canvas.drawRect(0, getHeight() - AndroidUtilities.getShadowHeight(), getWidth(), getHeight(), separatorPaint); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure( + MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(dp(48), MeasureSpec.EXACTLY) + ); + } + + public void set(TLRPC.WebPage webpage, View.OnClickListener onCloseClick, boolean animated) { + final boolean exist = webpage != null && !(webpage instanceof TLRPC.TL_webPagePending); + if (animated) { + imageView.animate().alpha(exist ? 1f : 0f).scaleX(exist ? 1f : .4f).scaleY(exist ? 1f : .4f).setDuration(320).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + loadingView.animate().alpha(exist ? 0f : 1f).scaleX(exist ? .4f : 1f).scaleY(exist ? .4f : 1f).setDuration(320).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); + } else { + imageView.setAlpha(exist ? 1f : 0f); + imageView.setScaleX(exist ? 1f : .4f); + imageView.setScaleY(exist ? 1f : .4f); + loadingView.setAlpha(exist ? 0f : 1f); + loadingView.setScaleX(exist ? .4f : 1f); + loadingView.setScaleY(exist ? .4f : 1f); + } + if (exist) { + titleView.setText(TextUtils.isEmpty(webpage.site_name) ? webpage.title : webpage.site_name, animated); + messageView.setText(webpage.description, animated); + } else { + titleView.setText(titleLoading, animated); + messageView.setText(messageLoading, animated); + } + closeView.setOnClickListener(onCloseClick); + } + + public static class Factory extends UItem.UItemFactory { + @Override + public WebpagePreviewView createView(Context context, int currentAccount, int classGuid, Theme.ResourcesProvider resourcesProvider) { + return new WebpagePreviewView(context); + } + + @Override + public void bindView(View view, UItem item, boolean divider) { + ((WebpagePreviewView) view).set( + item.object instanceof TLRPC.WebPage ? (TLRPC.WebPage) item.object : null, + item.clickCallback, + false + ); + } + + public static UItem item(TLRPC.WebPage webpage, View.OnClickListener onClose) { + UItem item = UItem.ofFactory(WebpagePreviewView.Factory.class); + item.object = webpage; + item.clickCallback = onClose; + return item; + } + } + } + + public static boolean isPreviewEmpty(TLRPC.MessageMedia media) { + return !(media instanceof TLRPC.TL_messageMediaWebPage) || isPreviewEmpty(media.webpage); + } + + public static boolean isPreviewEmpty(TLRPC.WebPage webpage) { + return ( + webpage instanceof TLRPC.TL_webPagePending || + TextUtils.isEmpty(webpage.title) && TextUtils.isEmpty(webpage.description) + ); + } + + + @Override + public void show() { + super.show(); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.didReceivedWebpagesInUpdates); + AndroidUtilities.runOnUIThread(() -> { + if (!isShowing()) return; + urlEditText.editText.requestFocus(); + AndroidUtilities.showKeyboard(urlEditText.editText); + }, 150); + } + + @Override + public void dismiss() { + AndroidUtilities.hideKeyboard(urlEditText.editText); + AndroidUtilities.hideKeyboard(nameEditText.editText); + super.dismiss(); + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.didReceivedWebpagesInUpdates); + } + +} 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 369bf0958d..112bbe2f50 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 @@ -4274,7 +4274,7 @@ private void createPhotoPaintView() { } } if (paintViewBlurBitmap == null) { - paintViewBlurBitmap = Bitmap.createBitmap(size.first, size.second, Bitmap.Config.ALPHA_8); + paintViewBlurBitmap = Bitmap.createBitmap(size.first, size.second, Bitmap.Config.ARGB_8888); } int w = previewContainer.getMeasuredWidth(), h = previewContainer.getMeasuredHeight(); @@ -4297,7 +4297,8 @@ private void createPhotoPaintView() { null, blurManager, resourcesProvider, - videoTextureHolder + videoTextureHolder, + previewView ) { @Override public void onEntityDraggedTop(boolean value) { diff --git a/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java index 6b8dcd5183..2669ea921b 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/TwoStepVerificationActivity.java @@ -166,14 +166,24 @@ public void setCurrentPasswordParams(TLRPC.account_Password password, byte[] pas @Override public boolean onFragmentCreate() { super.onFragmentCreate(); - if (currentPassword == null || currentPassword.current_algo == null || currentPasswordHash == null || currentPasswordHash.length <= 0) { - loadPasswordInfo(true, currentPassword != null); - } + if (!preloaded) preload(null); updateRows(); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.twoStepPasswordChanged); return true; } + public boolean preloaded; + public void preload(Runnable whenPreloaded) { + preloaded = false; + if (currentPassword == null || currentPassword.current_algo == null || currentPasswordHash == null || currentPasswordHash.length <= 0) { + loadPasswordInfo(true, currentPassword != null, whenPreloaded); + } else { + if (whenPreloaded != null) { + whenPreloaded.run(); + } + } + } + @Override public void onFragmentDestroy() { super.onFragmentDestroy(); @@ -640,7 +650,7 @@ public void didReceivedNotification(int id, int account, Object... args) { if (args != null && args.length > 0 && args[0] != null) { currentPasswordHash = (byte[]) args[0]; } - loadPasswordInfo(false, false); + loadPasswordInfo(false, false, null); updateRows(); } } @@ -702,7 +712,7 @@ public static void initPasswordNewAlgo(TLRPC.account_Password password) { } } - private void loadPasswordInfo(boolean first, final boolean silent) { + private void loadPasswordInfo(boolean first, final boolean silent, Runnable whenDone) { if (!silent) { loading = true; if (listAdapter != null) { @@ -724,6 +734,9 @@ private void loadPasswordInfo(boolean first, final boolean silent) { initPasswordNewAlgo(currentPassword); NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.didSetOrRemoveTwoStepPassword, currentPassword); } + if (whenDone != null) { + whenDone.run(); + } updateRows(); }), ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagWithoutLogin); } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/UserInfoActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/UserInfoActivity.java index 12183e006e..2484a5c842 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/UserInfoActivity.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/UserInfoActivity.java @@ -86,7 +86,7 @@ public void onFragmentDestroy() { @Override public View createView(Context context) { - firstNameEdit = new EditTextCell(context, getString(R.string.EditProfileFirstName), false, -1) { + firstNameEdit = new EditTextCell(context, getString(R.string.EditProfileFirstName), false, -1, resourceProvider) { @Override protected void onTextChanged(CharSequence newText) { super.onTextChanged(newText); @@ -96,7 +96,7 @@ protected void onTextChanged(CharSequence newText) { firstNameEdit.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); firstNameEdit.setDivider(true); firstNameEdit.hideKeyboardOnEnter(); - lastNameEdit = new EditTextCell(context, getString(R.string.EditProfileLastName), false, -1) { + lastNameEdit = new EditTextCell(context, getString(R.string.EditProfileLastName), false, -1, resourceProvider) { @Override protected void onTextChanged(CharSequence newText) { super.onTextChanged(newText); @@ -105,7 +105,7 @@ protected void onTextChanged(CharSequence newText) { }; lastNameEdit.setBackgroundColor(getThemedColor(Theme.key_windowBackgroundWhite)); lastNameEdit.hideKeyboardOnEnter(); - bioEdit = new EditTextCell(context, getString(R.string.EditProfileBioHint), true, getMessagesController().getAboutLimit()) { + bioEdit = new EditTextCell(context, getString(R.string.EditProfileBioHint), true, getMessagesController().getAboutLimit(), resourceProvider) { @Override protected void onTextChanged(CharSequence newText) { super.onTextChanged(newText); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/VoiceMessageEnterTransition.java b/TMessagesProj/src/main/java/org/telegram/ui/VoiceMessageEnterTransition.java index 3ae3356b55..f3ac9d6fe5 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/VoiceMessageEnterTransition.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/VoiceMessageEnterTransition.java @@ -138,7 +138,8 @@ public void onDraw(Canvas canvas) { // canvas.save(); // } - circlePaint.setColor(ColorUtils.blendARGB(getThemedColor(Theme.key_chat_messagePanelVoiceBackground), getThemedColor(messageView.getRadialProgress().getCircleColorKey()), progress)); + final int circleColorKey = messageView.getRadialProgress().getCircleColorKey(); + circlePaint.setColor(ColorUtils.blendARGB(getThemedColor(Theme.key_chat_messagePanelVoiceBackground), getThemedColor(circleColorKey < 0 ? Theme.key_chat_messagePanelVoiceBackground : circleColorKey), progress)); if (recordCircle != null) { recordCircle.drawWaves(canvas, cx, cy, 1f - hideWavesProgress); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotBiometry.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotBiometry.java index 96e4847652..f91b77807a 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotBiometry.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotBiometry.java @@ -66,6 +66,7 @@ public class BotBiometry { public boolean access_requested; private String encrypted_token; + private String iv; public BotBiometry(Context context, int currentAccount, long botId) { this.context = context; @@ -77,6 +78,7 @@ public BotBiometry(Context context, int currentAccount, long botId) { public void load() { SharedPreferences prefs = context.getSharedPreferences(PREF + currentAccount, Activity.MODE_PRIVATE); this.encrypted_token = prefs.getString(String.valueOf(botId), null); + this.iv = prefs.getString(String.valueOf(botId)+"_iv", null); this.access_granted = this.encrypted_token != null; this.access_requested = this.access_granted || prefs.getBoolean(botId + "_requested", false); this.disabled = prefs.getBoolean(botId + "_disabled", false); @@ -104,22 +106,25 @@ public void requestToken(String reason, Utilities.Callback2 whe String token = null; if (result != null) { try { - BiometricPrompt.CryptoObject cryptoObject = result.getCryptoObject(); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - if (!TextUtils.isEmpty(encrypted_token)) { - token = encrypted_token.split(";")[0]; + token = encrypted_token; + } else { + BiometricPrompt.CryptoObject cryptoObject; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + cryptoObject = makeCryptoObject(true); } else { - token = encrypted_token; + cryptoObject = result.getCryptoObject(); } - } else if (cryptoObject != null) { - if (!TextUtils.isEmpty(encrypted_token)) { - token = new String(cryptoObject.getCipher().doFinal(Utilities.hexToBytes(encrypted_token.split(";")[0])), StandardCharsets.UTF_8); + if (cryptoObject != null) { + if (!TextUtils.isEmpty(encrypted_token)) { + token = new String(cryptoObject.getCipher().doFinal(Utilities.hexToBytes(encrypted_token)), StandardCharsets.UTF_8); + } else { + token = encrypted_token; + } } else { - token = encrypted_token; - } - } else { - if (!TextUtils.isEmpty(encrypted_token)) { - throw new RuntimeException("No cryptoObject found"); + if (!TextUtils.isEmpty(encrypted_token)) { + throw new RuntimeException("No cryptoObject found"); + } } } } catch (Exception e) { @@ -139,14 +144,19 @@ public void updateToken(String reason, String token, Utilities.Callback BiometricPrompt.CryptoObject cryptoObject = result.getCryptoObject(); if (TextUtils.isEmpty(token)) { encrypted_token = null; + iv = null; } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { encrypted_token = token; + iv = null; } else { - if (cryptoObject == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { cryptoObject = makeCryptoObject(false); + } else { + cryptoObject = result.getCryptoObject(); } if (cryptoObject != null) { - encrypted_token = Utilities.bytesToHex(cryptoObject.getCipher().doFinal(token.getBytes(StandardCharsets.UTF_8))) + ";" + Utilities.bytesToHex(cryptoObject.getCipher().getIV()); + encrypted_token = Utilities.bytesToHex(cryptoObject.getCipher().doFinal(token.getBytes(StandardCharsets.UTF_8))); + iv = Utilities.bytesToHex(cryptoObject.getCipher().getIV()); } else { throw new RuntimeException("No cryptoObject found"); } @@ -188,11 +198,11 @@ public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationRes @Override public void onAuthenticationFailed() { FileLog.d("BotBiometry onAuthenticationFailed"); - if (callback != null) { - Utilities.Callback thisCallback = callback; - callback = null; - thisCallback.run(null); - } +// if (callback != null) { +// Utilities.Callback thisCallback = callback; +// callback = null; +// thisCallback.run(null); +// } } }); } @@ -203,7 +213,7 @@ private BiometricPrompt.CryptoObject makeCryptoObject(boolean decrypt) { Cipher cipher = getCipher(); SecretKey secretKey = getSecretKey(); if (decrypt) { - cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(Utilities.hexToBytes(encrypted_token.split(";")[1]))); + cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(Utilities.hexToBytes(iv))); } else { cipher.init(Cipher.ENCRYPT_MODE, secretKey); } @@ -240,14 +250,15 @@ private void prompt( promptInfoBuilder.setDescription(text); } final BiometricPrompt.PromptInfo promptInfo = promptInfoBuilder.build(); - if (cryptoObject != null && !decrypt) { + if (cryptoObject != null && !decrypt && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { try { if (TextUtils.isEmpty(token)) { encrypted_token = null; } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { encrypted_token = token; } else { - encrypted_token = Utilities.bytesToHex(cryptoObject.getCipher().doFinal(token.getBytes(StandardCharsets.UTF_8))) + ";" + Utilities.bytesToHex(cryptoObject.getCipher().getIV()); + encrypted_token = Utilities.bytesToHex(cryptoObject.getCipher().doFinal(token.getBytes(StandardCharsets.UTF_8))); + iv = Utilities.bytesToHex(cryptoObject.getCipher().getIV()); } save(); this.callback = null; @@ -258,7 +269,7 @@ private void prompt( } cryptoObject = makeCryptoObject(decrypt); } - if (cryptoObject != null) { + if (cryptoObject != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { prompt.authenticate(promptInfo, cryptoObject); } else { prompt.authenticate(promptInfo); @@ -273,11 +284,11 @@ private SecretKey getSecretKey() throws Exception { keyStore = KeyStore.getInstance("AndroidKeyStore"); keyStore.load(null); } - if (keyStore.containsAlias("6bot_" + botId)) { - return ((SecretKey) keyStore.getKey("6bot_" + botId, null)); + if (keyStore.containsAlias("9bot_" + botId)) { + return ((SecretKey) keyStore.getKey("9bot_" + botId, null)); } else { KeyGenParameterSpec.Builder keygenBuilder = new KeyGenParameterSpec.Builder( - "6bot_" + botId, + "9bot_" + botId, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT ); keygenBuilder.setBlockModes(KeyProperties.BLOCK_MODE_CBC); @@ -285,8 +296,6 @@ private SecretKey getSecretKey() throws Exception { keygenBuilder.setUserAuthenticationRequired(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { keygenBuilder.setUserAuthenticationParameters(60, KeyProperties.AUTH_BIOMETRIC_STRONG); - } else { - keygenBuilder.setUserAuthenticationValidityDurationSeconds(60); } if (Build.VERSION.SDK_INT >= 24) { keygenBuilder.setInvalidatedByBiometricEnrollment(true); @@ -345,8 +354,10 @@ public void save() { } if (access_granted) { edit.putString(String.valueOf(botId), encrypted_token == null ? "" : encrypted_token); + edit.putString(String.valueOf(botId)+"_iv", iv == null ? "" : iv); } else { edit.remove(String.valueOf(botId)); + edit.remove(String.valueOf(botId)+"_iv"); } if (disabled) { edit.putBoolean(botId + "_disabled", true); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotCommandsMenuContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotCommandsMenuContainer.java index ea8916b5d8..90efbd4a72 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotCommandsMenuContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotCommandsMenuContainer.java @@ -1,5 +1,7 @@ package org.telegram.ui.bots; +import static org.telegram.messenger.AndroidUtilities.dp; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -33,6 +35,7 @@ public class BotCommandsMenuContainer extends FrameLayout implements NestedScrol public RecyclerListView listView; Paint backgroundPaint = new Paint(); Paint topBackground = new Paint(Paint.ANTI_ALIAS_FLAG); + private float containerY; boolean dismissed = true; @@ -60,14 +63,25 @@ protected void dispatchDraw(Canvas canvas) { y = 0; } scrollYOffset = y; - y -= AndroidUtilities.dp(8); + y -= dp(8); if (y > 0) { - shadowDrawable.setBounds(-AndroidUtilities.dp(8), (int) y - AndroidUtilities.dp(24), getMeasuredWidth() + AndroidUtilities.dp(8), (int) y); + shadowDrawable.setBounds( + -dp(8), + (int) y - dp(24), + getMeasuredWidth() + dp(8), + (int) y + ); shadowDrawable.draw(canvas); } - canvas.drawRect(0, y, getMeasuredWidth(), getMeasuredHeight() + AndroidUtilities.dp(16), backgroundPaint); - AndroidUtilities.rectTmp.set(getMeasuredWidth() / 2f - AndroidUtilities.dp(12), y - AndroidUtilities.dp(4),getMeasuredWidth() / 2f + AndroidUtilities.dp(12), y); - canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(4), AndroidUtilities.dp(4), topBackground); + containerY = y - dp(16); + canvas.drawRect(0, y, getMeasuredWidth(), getMeasuredHeight() + dp(16), backgroundPaint); + AndroidUtilities.rectTmp.set( + getMeasuredWidth() / 2f - dp(12), + y - dp(4), + getMeasuredWidth() / 2f + dp(12), + y + ); + canvas.drawRoundRect(AndroidUtilities.rectTmp, dp(4), dp(4), topBackground); super.dispatchDraw(canvas); } }; @@ -78,6 +92,10 @@ protected void dispatchDraw(Canvas canvas) { setClipChildren(false); } + public float clipBottom() { + return Math.max(0, getMeasuredHeight() - (containerY + listView.getTranslationY())); + } + @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { return !dismissed && nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL; @@ -105,7 +123,7 @@ private void checkDismiss() { if (dismissed) { return; } - if (listView.getTranslationY() > AndroidUtilities.dp(16)) { + if (listView.getTranslationY() > dp(16)) { dismiss(); } else { playEnterAnim(false); @@ -189,7 +207,7 @@ public void show() { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (entering && !dismissed) { - listView.setTranslationY(listView.getMeasuredHeight() - listView.getPaddingTop() + AndroidUtilities.dp(16)); + listView.setTranslationY(listView.getMeasuredHeight() - listView.getPaddingTop() + dp(16)); playEnterAnim(true); entering = false; } @@ -214,7 +232,7 @@ public void dismiss() { if (!dismissed) { dismissed = true; cancelCurrentAnimation(); - currentAnimation = ObjectAnimator.ofFloat(listView, TRANSLATION_Y, listView.getTranslationY(), getMeasuredHeight() - scrollYOffset + AndroidUtilities.dp(40)); + currentAnimation = ObjectAnimator.ofFloat(listView, TRANSLATION_Y, listView.getTranslationY(), getMeasuredHeight() - scrollYOffset + dp(40)); currentAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -235,7 +253,7 @@ protected void onDismiss() { @Override public boolean dispatchTouchEvent(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getY() < scrollYOffset - AndroidUtilities.dp(24)) { + if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getY() < scrollYOffset - dp(24)) { return false; } return super.dispatchTouchEvent(ev); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotCommandsMenuView.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotCommandsMenuView.java index 49298c128f..6f549bd0c3 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotCommandsMenuView.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotCommandsMenuView.java @@ -69,6 +69,7 @@ protected void invalidateInternal() { private String menuText = LocaleController.getString(R.string.BotsMenuTitle); StaticLayout menuTextLayout; + private float menuTextWidth; boolean isOpened; public boolean isWebView; @@ -134,13 +135,15 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { backDrawable.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); textPaint.setTextSize(AndroidUtilities.dp(15)); lastSize = size; - int w = (int) textPaint.measureText(menuText); - menuTextLayout = StaticLayoutEx.createStaticLayout(menuText, textPaint, w, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0f, false, TextUtils.TruncateAt.END, w, 1); + CharSequence c = Emoji.replaceEmoji(menuText, textPaint.getFontMetricsInt(), false); + int w = (int) (AndroidUtilities.displaySize.x * .6f); + menuTextLayout = StaticLayoutEx.createStaticLayout(c, textPaint, w, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0f, false, TextUtils.TruncateAt.END, w, 1); + menuTextWidth = menuTextLayout.getLineCount() > 0 ? menuTextLayout.getLineWidth(0) : 0; } - onTranslationChanged((menuTextLayout.getWidth() + AndroidUtilities.dp(4)) * expandProgress); + onTranslationChanged((menuTextWidth + AndroidUtilities.dp(4)) * expandProgress); int width = AndroidUtilities.dp(40); if (expanded) { - width += menuTextLayout.getWidth() + AndroidUtilities.dp(4); + width += (int) menuTextWidth + AndroidUtilities.dp(4); } super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(32), MeasureSpec.EXACTLY)); @@ -174,7 +177,7 @@ protected void dispatchDraw(Canvas canvas) { } if (drawBackgroundDrawable) { - rectTmp.set(0, 0, AndroidUtilities.dp(40) + (menuTextLayout.getWidth() + AndroidUtilities.dp(4)) * expandProgress, getMeasuredHeight()); + rectTmp.set(0, 0, AndroidUtilities.dp(40) + (menuTextWidth + AndroidUtilities.dp(4)) * expandProgress, getMeasuredHeight()); canvas.drawRoundRect(rectTmp, AndroidUtilities.dp(16), AndroidUtilities.dp(16), paint); backgroundDrawable.setBounds((int) rectTmp.left, (int) rectTmp.top, (int) rectTmp.right, (int) rectTmp.bottom); backgroundDrawable.draw(canvas); @@ -206,7 +209,7 @@ protected void dispatchDraw(Canvas canvas) { } if (update) { - onTranslationChanged((menuTextLayout.getWidth() + AndroidUtilities.dp(4)) * expandProgress); + onTranslationChanged((menuTextWidth + AndroidUtilities.dp(4)) * expandProgress); } } super.dispatchDraw(canvas); diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewAttachedSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewAttachedSheet.java new file mode 100644 index 0000000000..a615d9ff31 --- /dev/null +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewAttachedSheet.java @@ -0,0 +1,1736 @@ +package org.telegram.ui.bots; + +import static org.telegram.messenger.AndroidUtilities.dp; +import static org.telegram.ui.Components.Bulletin.DURATION_PROLONG; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.text.TextPaint; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.core.graphics.ColorUtils; +import androidx.core.math.MathUtils; +import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.dynamicanimation.animation.SpringForce; + +import org.json.JSONObject; +import org.telegram.messenger.AndroidUtilities; +import org.telegram.messenger.AnimationNotificationsLocker; +import org.telegram.messenger.ContactsController; +import org.telegram.messenger.DialogObject; +import org.telegram.messenger.Emoji; +import org.telegram.messenger.FileLoader; +import org.telegram.messenger.FileLog; +import org.telegram.messenger.ImageLocation; +import org.telegram.messenger.LocaleController; +import org.telegram.messenger.MediaDataController; +import org.telegram.messenger.MessagesController; +import org.telegram.messenger.NotificationCenter; +import org.telegram.messenger.R; +import org.telegram.messenger.SendMessagesHelper; +import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; +import org.telegram.messenger.browser.Browser; +import org.telegram.tgnet.ConnectionsManager; +import org.telegram.tgnet.TLObject; +import org.telegram.tgnet.TLRPC; +import org.telegram.ui.ActionBar.ActionBar; +import org.telegram.ui.ActionBar.ActionBarMenu; +import org.telegram.ui.ActionBar.ActionBarMenuItem; +import org.telegram.ui.ActionBar.ActionBarMenuSubItem; +import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BackDrawable; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheetTabs; +import org.telegram.ui.ActionBar.INavigationLayout; +import org.telegram.ui.ActionBar.Theme; +import org.telegram.ui.ChatActivity; +import org.telegram.ui.Components.Bulletin; +import org.telegram.ui.Components.BulletinFactory; +import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.LayoutHelper; +import org.telegram.ui.Components.OverlayActionBarLayoutDialog; +import org.telegram.ui.Components.RadialProgressView; +import org.telegram.ui.Components.SimpleFloatPropertyCompat; +import org.telegram.ui.Components.SizeNotifierFrameLayout; +import org.telegram.ui.Components.VerticalPositionAutoAnimator; +import org.telegram.ui.DialogsActivity; +import org.telegram.ui.LaunchActivity; +import org.telegram.ui.PaymentFormActivity; +import org.telegram.ui.Stars.StarsController; + +import java.io.File; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.Locale; + +public class BotWebViewAttachedSheet implements NotificationCenter.NotificationCenterDelegate, BaseFragment.AttachedSheet { + public final static int TYPE_WEB_VIEW_BUTTON = 0, TYPE_SIMPLE_WEB_VIEW_BUTTON = 1, TYPE_BOT_MENU_BUTTON = 2, TYPE_WEB_VIEW_BOT_APP = 3; + + public final static int FLAG_FROM_INLINE_SWITCH = 1; + public final static int FLAG_FROM_SIDE_MENU = 2; + private int lineColor; + + public void showJustAddedBulletin() { + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(botId); + TLRPC.TL_attachMenuBot currentBot = null; + for (TLRPC.TL_attachMenuBot bot : MediaDataController.getInstance(currentAccount).getAttachMenuBots().bots) { + if (bot.bot_id == botId) { + currentBot = bot; + break; + } + } + if (currentBot == null) { + return; + } + String str; + if (currentBot.show_in_side_menu && currentBot.show_in_attach_menu) { + str = LocaleController.formatString("BotAttachMenuShortcatAddedAttachAndSide", R.string.BotAttachMenuShortcatAddedAttachAndSide, user.first_name); + } else if (currentBot.show_in_side_menu) { + str = LocaleController.formatString("BotAttachMenuShortcatAddedSide", R.string.BotAttachMenuShortcatAddedSide, user.first_name); + } else { + str = LocaleController.formatString("BotAttachMenuShortcatAddedAttach", R.string.BotAttachMenuShortcatAddedAttach, user.first_name); + } + AndroidUtilities.runOnUIThread(() -> { + BulletinFactory.of(windowView, resourcesProvider) + .createSimpleBulletin(R.raw.contact_check, AndroidUtilities.replaceTags(str)) + .setDuration(DURATION_PROLONG) + .show(true); + }, 200); + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + TYPE_WEB_VIEW_BUTTON, + TYPE_SIMPLE_WEB_VIEW_BUTTON, + TYPE_BOT_MENU_BUTTON, + TYPE_WEB_VIEW_BOT_APP + }) + public @interface WebViewType {} + + private final static int POLL_PERIOD = 60000; + + private final static SimpleFloatPropertyCompat ACTION_BAR_TRANSITION_PROGRESS_VALUE = new SimpleFloatPropertyCompat("actionBarTransitionProgress", obj -> obj.actionBarTransitionProgress, (obj, value) -> { + obj.actionBarTransitionProgress = value; + obj.windowView.invalidate(); + obj.actionBar.setAlpha(value); + obj.updateLightStatusBar(); + }).setMultiplier(100f); + private float actionBarTransitionProgress = 0f; + private SpringAnimation springAnimation; + + private Boolean wasLightStatusBar; + + public final WindowView windowView; + + @Override + public WindowView getWindowView() { + return windowView; + } + + private long lastSwipeTime; + + private ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer swipeContainer; + private BotWebViewContainer webViewContainer; + private ChatAttachAlertBotWebViewLayout.WebProgressView progressView; + private Theme.ResourcesProvider resourcesProvider; + private boolean ignoreLayout; + + private WebViewRequestProps requestProps; + private TLObject requestResponse; + private long requestResponseTime; + + private int currentAccount; + private long botId; + private long peerId; + private long queryId; + private int replyToMsgId; + private boolean silent; + private String buttonText; + private boolean forceExpnaded; + + private boolean defaultFullsize = false; + private Boolean fullsize = null; + private boolean needsContext; + + public static class MainButtonSettings { + + public boolean isVisible; + public boolean isActive; + public String text; + public int color; + public int textColor; + public boolean isProgressVisible; + + public static MainButtonSettings of(boolean isVisible, boolean isActive, String text, int color, int textColor, boolean isProgressVisible) { + MainButtonSettings s = new MainButtonSettings(); + s.isVisible = isVisible; + s.isActive = isActive; + s.text = text; + s.color = color; + s.textColor = textColor; + s.isProgressVisible = isProgressVisible; + return s; + } + + } + + public BottomSheetTabs.WebTabData saveState() { + BottomSheetTabs.WebTabData tab = new BottomSheetTabs.WebTabData(); + tab.actionBarColor = actionBarColor; + tab.actionBarColorKey = actionBarColorKey; + tab.overrideActionBarColor = overrideBackgroundColor; + tab.backgroundColor = backgroundPaint.getColor(); + tab.props = requestProps; + tab.ready = webViewContainer != null && webViewContainer.isPageLoaded(); + tab.themeIsDark = Theme.isCurrentThemeDark(); + tab.lastUrl = webViewContainer != null ? webViewContainer.getUrlLoaded() : null; + tab.expanded = swipeContainer != null && ((1f - Math.min(swipeContainer.getTopActionBarOffsetY(), swipeContainer.getTranslationY() - swipeContainer.getTopActionBarOffsetY()) / swipeContainer.getTopActionBarOffsetY()) > .5f) || forceExpnaded || getFullSize(); + tab.fullsize = getFullSize(); + tab.expandedOffset = swipeContainer != null ? swipeContainer.getOffsetY() : Float.MAX_VALUE; + tab.backButton = backButtonShown; + tab.settings = settingsItem != null && settingsItem.getVisibility() == View.VISIBLE; + tab.main = mainButtonSettings; + tab.confirmDismiss = needCloseConfirmation; + tab.needsContext = needsContext; + BotWebViewContainer.MyWebView webView = webViewContainer == null ? null : webViewContainer.getWebView(); + if (webView != null) { + webViewContainer.preserveWebView(); + tab.webView = webView; + tab.webViewProxy = webViewContainer == null ? null : webViewContainer.getProxy(); + tab.webViewWidth = webView.getWidth(); + tab.webViewHeight = webView.getHeight(); + tab.webViewScroll = webView.getScrollY(); + webView.onPause(); +// webView.pauseTimers(); + } + return tab; + } + + public boolean showExpanded; + public float showOffsetY = Float.MAX_VALUE; + + public boolean restoreState(BaseFragment fragment, BottomSheetTabs.WebTabData tab) { + if (tab == null || tab.props == null) return false; + if (tab.overrideActionBarColor) { + setBackgroundColor(tab.backgroundColor, false); + } + setActionBarColor(!tab.overrideActionBarColor ? Theme.getColor(tab.actionBarColorKey < 0 ? Theme.key_windowBackgroundWhite : tab.actionBarColorKey, resourcesProvider) : tab.actionBarColor, tab.overrideActionBarColor, false); + showExpanded = tab.expanded; + showOffsetY = tab.expandedOffset; + fullsize = tab.fullsize; + needsContext = tab.needsContext; + webViewContainer.setIsBackButtonVisible(backButtonShown = tab.backButton); + if (backDrawable != null) { + backDrawable.setRotation(backButtonShown ? 0f : 1f, false); + } + if (settingsItem != null) { + settingsItem.setVisibility(tab.settings ? View.VISIBLE : View.GONE); + } + if (tab.main != null) { + setMainButton(tab.main); + } + needCloseConfirmation = tab.confirmDismiss; + if (tab.webView != null) { +// tab.webView.resumeTimers(); + tab.webView.onResume(); + webViewContainer.replaceWebView(tab.webView, tab.webViewProxy); + webViewContainer.setState(tab.ready || tab.webView.isPageLoaded(), tab.lastUrl); + if (Theme.isCurrentThemeDark() != tab.themeIsDark) { +// webViewContainer.notifyThemeChanged(); + if (webViewContainer.getWebView() != null) { + webViewContainer.getWebView().animate().cancel(); + webViewContainer.getWebView().animate().alpha(0).start(); + } + + progressView.setLoadProgress(0); + progressView.setAlpha(1f); + progressView.setVisibility(View.VISIBLE); + + webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId)); + webViewContainer.loadFlickerAndSettingsItem(currentAccount, botId, settingsItem); + webViewContainer.setState(false, null); + if (webViewContainer.getWebView() != null) { + webViewContainer.getWebView().loadUrl("about:blank"); + } + + tab.props.response = null; + tab.props.responseTime = 0; + } + } else { + tab.props.response = null; + tab.props.responseTime = 0; + } + requestWebView(fragment, tab.props); + return true; + } + + private Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private Paint dimPaint = new Paint(); + private Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private int actionBarColorKey = -1; + private int actionBarColor; + private int navBarColor; + private boolean actionBarIsLight; + private Paint actionBarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + + private boolean overrideBackgroundColor; + + private BackDrawable backDrawable; + private ActionBar actionBar; + private Drawable actionBarShadow; + private ActionBarMenuSubItem settingsItem; + private TLRPC.BotApp currentWebApp; + + private boolean dismissed; + private boolean backButtonShown; + + private Activity parentActivity; + + private boolean mainButtonWasVisible, mainButtonProgressWasVisible; + private TextView mainButton; + private RadialProgressView radialProgressView; + + private boolean needCloseConfirmation; + + private VerticalPositionAutoAnimator mainButtonAutoAnimator, radialProgressAutoAnimator; + + private Runnable pollRunnable = () -> { + if (!dismissed && queryId != 0) { + TLRPC.TL_messages_prolongWebView prolongWebView = new TLRPC.TL_messages_prolongWebView(); + prolongWebView.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + prolongWebView.peer = MessagesController.getInstance(currentAccount).getInputPeer(peerId); + prolongWebView.query_id = queryId; + prolongWebView.silent = silent; + if (replyToMsgId != 0) { + prolongWebView.reply_to = SendMessagesHelper.getInstance(currentAccount).createReplyInput(replyToMsgId); + prolongWebView.flags |= 1; + } + ConnectionsManager.getInstance(currentAccount).sendRequest(prolongWebView, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (dismissed) { + return; + } + if (error != null) { + dismiss(); + } else { + AndroidUtilities.runOnUIThread(this.pollRunnable, POLL_PERIOD); + } + })); + } + }; + + private final BaseFragment fragment; + + public Context getContext() { + return fragment.getContext(); + } + + public BotWebViewAttachedSheet(@NonNull BaseFragment fragment) { + this.fragment = fragment; + this.resourcesProvider = fragment.getResourceProvider(); + lineColor = Theme.getColor(Theme.key_sheet_scrollUp); + + swipeContainer = new ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer(getContext()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int availableHeight = MeasureSpec.getSize(heightMeasureSpec); + + int padding; + if (!AndroidUtilities.isTablet() && AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y) { + padding = (int) (availableHeight / 3.5f); + } else { + padding = (availableHeight / 5 * 2); + } + if (padding < 0) { + padding = 0; + } + + if (getOffsetY() != padding && !dismissed) { + ignoreLayout = true; + setOffsetY(padding); + ignoreLayout = false; + } + + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec) - ActionBar.getCurrentActionBarHeight() - AndroidUtilities.statusBarHeight + dp(24) - (mainButtonWasVisible ? mainButton.getLayoutParams().height : 0), MeasureSpec.EXACTLY)); + } + + @Override + public void requestLayout() { + if (ignoreLayout) { + return; + } + super.requestLayout(); + } + }; + webViewContainer = new BotWebViewContainer(getContext(), resourcesProvider, getColor(Theme.key_windowBackgroundWhite)) { + @Override + public void onWebViewCreated() { + super.onWebViewCreated(); + swipeContainer.setWebView(webViewContainer.getWebView()); + } + }; + webViewContainer.setDelegate(new BotWebViewContainer.Delegate() { + private boolean sentWebViewData; + + @Override + public void onCloseRequested(Runnable callback) { + dismiss(false, callback); + } + + @Override + public void onWebAppSetupClosingBehavior(boolean needConfirmation) { + BotWebViewAttachedSheet.this.needCloseConfirmation = needConfirmation; + } + + @Override + public void onSendWebViewData(String data) { + if (queryId != 0 || sentWebViewData) { + return; + } + sentWebViewData = true; + + TLRPC.TL_messages_sendWebViewData sendWebViewData = new TLRPC.TL_messages_sendWebViewData(); + sendWebViewData.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + sendWebViewData.random_id = Utilities.random.nextLong(); + sendWebViewData.button_text = buttonText; + sendWebViewData.data = data; + ConnectionsManager.getInstance(currentAccount).sendRequest(sendWebViewData, (response, error) -> { + if (response instanceof TLRPC.TL_updates) { + MessagesController.getInstance(currentAccount).processUpdates((TLRPC.TL_updates) response, false); + } + AndroidUtilities.runOnUIThread(BotWebViewAttachedSheet.this::dismiss); + }); + } + + @Override + public void onWebAppSetActionBarColor(int colorKey, int color, boolean isOverrideColor) { + actionBarColorKey = colorKey; + setActionBarColor(color, isOverrideColor, true); + } + + @Override + public void onWebAppSetBackgroundColor(int color) { + setBackgroundColor(color, true); + } + + @Override + public void onSetBackButtonVisible(boolean visible) { + backButtonShown = visible; + backDrawable.setRotation(visible ? 0f : 1f, true); + } + + @Override + public void onSetSettingsButtonVisible(boolean visible) { + if (settingsItem != null) { + settingsItem.setVisibility(visible ? View.VISIBLE : View.GONE); + } + } + + @Override + public void onWebAppOpenInvoice(TLRPC.InputInvoice inputInvoice, String slug, TLObject response) { + BaseFragment parentFragment = ((LaunchActivity) parentActivity).getActionBarLayout().getLastFragment(); + PaymentFormActivity paymentFormActivity = null; + if (response instanceof TLRPC.TL_payments_paymentFormStars) { + AndroidUtilities.hideKeyboard(windowView); + final AlertDialog progressDialog = new AlertDialog(getContext(), AlertDialog.ALERT_TYPE_SPINNER); + progressDialog.showDelayed(150); + StarsController.getInstance(currentAccount).openPaymentForm(null, inputInvoice, (TLRPC.TL_payments_paymentFormStars) response, () -> { + progressDialog.dismiss(); + }, status -> { + webViewContainer.onInvoiceStatusUpdate(slug, status); + }); + return; + } else if (response instanceof TLRPC.PaymentForm) { + TLRPC.PaymentForm form = (TLRPC.PaymentForm) response; + MessagesController.getInstance(currentAccount).putUsers(form.users, false); + paymentFormActivity = new PaymentFormActivity(form, slug, parentFragment); + } else if (response instanceof TLRPC.PaymentReceipt) { + paymentFormActivity = new PaymentFormActivity((TLRPC.PaymentReceipt) response); + } + + if (paymentFormActivity != null) { + swipeContainer.stickTo(-swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY()); + + AndroidUtilities.hideKeyboard(windowView); + OverlayActionBarLayoutDialog overlayActionBarLayoutDialog = new OverlayActionBarLayoutDialog(getContext(), resourcesProvider); + overlayActionBarLayoutDialog.show(); + paymentFormActivity.setPaymentFormCallback(status -> { + if (status != PaymentFormActivity.InvoiceStatus.PENDING) { + overlayActionBarLayoutDialog.dismiss(); + } + + webViewContainer.onInvoiceStatusUpdate(slug, status.name().toLowerCase(Locale.ROOT)); + }); + paymentFormActivity.setResourcesProvider(resourcesProvider); + overlayActionBarLayoutDialog.addFragment(paymentFormActivity); + } + } + + @Override + public void onWebAppExpand() { + if (/* System.currentTimeMillis() - lastSwipeTime <= 1000 || */ swipeContainer.isSwipeInProgress()) { + return; + } + swipeContainer.stickTo(-swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY()); + } + + @Override + public void onWebAppSwitchInlineQuery(TLRPC.User botUser, String query, List chatTypes) { + if (chatTypes.isEmpty()) { + if (parentActivity instanceof LaunchActivity) { + BaseFragment lastFragment = ((LaunchActivity) parentActivity).getActionBarLayout().getLastFragment(); + if (lastFragment instanceof ChatActivity) { + ((ChatActivity) lastFragment).getChatActivityEnterView().setFieldText("@" + UserObject.getPublicUsername(botUser) + " " + query); + dismiss(); + } + } + } else { + Bundle args = new Bundle(); + args.putInt("dialogsType", DialogsActivity.DIALOGS_TYPE_START_ATTACH_BOT); + args.putBoolean("onlySelect", true); + + args.putBoolean("allowGroups", chatTypes.contains("groups")); + args.putBoolean("allowMegagroups", chatTypes.contains("groups")); + args.putBoolean("allowLegacyGroups", chatTypes.contains("groups")); + args.putBoolean("allowUsers", chatTypes.contains("users")); + args.putBoolean("allowChannels", chatTypes.contains("channels")); + args.putBoolean("allowBots", chatTypes.contains("bots")); + + DialogsActivity dialogsActivity = new DialogsActivity(args); + AndroidUtilities.hideKeyboard(windowView); + OverlayActionBarLayoutDialog overlayActionBarLayoutDialog = new OverlayActionBarLayoutDialog(getContext(), resourcesProvider); + dialogsActivity.setDelegate((fragment, dids, message1, param, topicsFragment) -> { + long did = dids.get(0).dialogId; + + Bundle args1 = new Bundle(); + args1.putBoolean("scrollToTopOnResume", true); + if (DialogObject.isEncryptedDialog(did)) { + args1.putInt("enc_id", DialogObject.getEncryptedChatId(did)); + } else if (DialogObject.isUserDialog(did)) { + args1.putLong("user_id", did); + } else { + args1.putLong("chat_id", -did); + } + args1.putString("start_text", "@" + UserObject.getPublicUsername(botUser) + " " + query); + + if (parentActivity instanceof LaunchActivity) { + BaseFragment lastFragment = ((LaunchActivity) parentActivity).getActionBarLayout().getLastFragment(); + if (MessagesController.getInstance(currentAccount).checkCanOpenChat(args1, lastFragment)) { + overlayActionBarLayoutDialog.dismiss(); + + dismissed = true; + AndroidUtilities.cancelRunOnUIThread(pollRunnable); + + webViewContainer.destroyWebView(); + NotificationCenter.getInstance(currentAccount).removeObserver(BotWebViewAttachedSheet.this, NotificationCenter.webViewResultSent); + NotificationCenter.getGlobalInstance().removeObserver(BotWebViewAttachedSheet.this, NotificationCenter.didSetNewTheme); + release(); + + lastFragment.presentFragment(new INavigationLayout.NavigationParams(new ChatActivity(args1)).setRemoveLast(true)); + } + } + return true; + }); + overlayActionBarLayoutDialog.show(); + overlayActionBarLayoutDialog.addFragment(dialogsActivity); + } + } + + @Override + public void onSetupMainButton(boolean isVisible, boolean isActive, String text, int color, int textColor, boolean isProgressVisible) { + setMainButton(MainButtonSettings.of(isVisible, isActive, text, color, textColor, isProgressVisible)); + } + + @Override + public String getWebAppName() { + if (currentWebApp != null) { + return currentWebApp.title; + } + return null; + } + + @Override + public boolean isClipboardAvailable() { + return MediaDataController.getInstance(currentAccount).botInAttachMenu(botId); + } + }); + + linePaint.setStyle(Paint.Style.FILL_AND_STROKE); + linePaint.setStrokeWidth(dp(4)); + linePaint.setStrokeCap(Paint.Cap.ROUND); + + dimPaint.setColor(0x40000000); + actionBarColor = getColor(Theme.key_windowBackgroundWhite); + navBarColor = getColor(Theme.key_windowBackgroundGray); + checkNavBarColor(); + windowView = new WindowView(getContext()); + windowView.setDelegate((keyboardHeight, isWidthGreater) -> { + if (keyboardHeight > dp(20)) { + swipeContainer.stickTo(-swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY()); + } + }); + windowView.addView(swipeContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 24, 0, 0)); + + mainButton = new TextView(getContext()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + }; + mainButton.setVisibility(View.GONE); + mainButton.setAlpha(0f); + mainButton.setSingleLine(); + mainButton.setGravity(Gravity.CENTER); + mainButton.setTypeface(AndroidUtilities.bold()); + int padding = dp(16); + mainButton.setPadding(padding, 0, padding, 0); + mainButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); + mainButton.setOnClickListener(v -> webViewContainer.onMainButtonPressed()); + windowView.addView(mainButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL)); + mainButtonAutoAnimator = VerticalPositionAutoAnimator.attach(mainButton); + + radialProgressView = new RadialProgressView(getContext()) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) getLayoutParams(); + params.rightMargin = dp(10); + } + }; + radialProgressView.setSize(dp(18)); + radialProgressView.setAlpha(0f); + radialProgressView.setScaleX(0.1f); + radialProgressView.setScaleY(0.1f); + radialProgressView.setVisibility(View.GONE); + windowView.addView(radialProgressView, LayoutHelper.createFrame(28, 28, Gravity.BOTTOM | Gravity.RIGHT, 0, 0, 10, 10)); + radialProgressAutoAnimator = VerticalPositionAutoAnimator.attach(radialProgressView); + + actionBarShadow = ContextCompat.getDrawable(getContext(), R.drawable.header_shadow).mutate(); + + actionBar = new ActionBar(getContext(), resourcesProvider) { + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + }; + actionBar.setBackgroundColor(Color.TRANSPARENT); + actionBar.setBackButtonDrawable(backDrawable = new BackDrawable(false)); + backDrawable.setAnimationTime(200.0f); + backDrawable.setColorFilter(null); + backDrawable.setRotation(1f, false); + updateActionBarColors(); + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + onCheckDismissByUser(); + } + } + }); + actionBar.setAlpha(0f); + windowView.addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL)); + + windowView.addView(progressView = new ChatAttachAlertBotWebViewLayout.WebProgressView(getContext(), resourcesProvider), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0, 0, 0)); + webViewContainer.setWebViewProgressListener(progress -> { + progressView.setLoadProgressAnimated(progress); + if (progress == 1f) { + ValueAnimator animator = ValueAnimator.ofFloat(1, 0).setDuration(200); + animator.setInterpolator(CubicBezierInterpolator.DEFAULT); + animator.addUpdateListener(animation -> progressView.setAlpha((Float) animation.getAnimatedValue())); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + progressView.setVisibility(View.GONE); + } + }); + animator.start(); + } + }); + + + if (springAnimation == null) { + springAnimation = new SpringAnimation(this, ACTION_BAR_TRANSITION_PROGRESS_VALUE) + .setSpring(new SpringForce() + .setStiffness(1200f) + .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY) + ); + } + + swipeContainer.addView(webViewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + swipeContainer.setScrollListener(()->{ + if (swipeContainer.getSwipeOffsetY() > 0) { + dimPaint.setAlpha((int) (0x40 * (1f - MathUtils.clamp(swipeContainer.getSwipeOffsetY() / (float)swipeContainer.getHeight(), 0, 1)))); + } else { + dimPaint.setAlpha(0x40); + } + windowView.invalidate(); + webViewContainer.invalidateViewPortHeight(); + + if (springAnimation != null) { + float progress = (1f - Math.min(swipeContainer.getTopActionBarOffsetY(), swipeContainer.getTranslationY() - swipeContainer.getTopActionBarOffsetY()) / swipeContainer.getTopActionBarOffsetY()); + float newPos = (progress > 0.5f ? 1 : 0) * 100f; + if (springAnimation.getSpring().getFinalPosition() != newPos) { + springAnimation.getSpring().setFinalPosition(newPos); + springAnimation.start(); + } + } + float offsetY = Math.max(0, swipeContainer.getSwipeOffsetY()); + mainButtonAutoAnimator.setOffsetY(offsetY); + radialProgressAutoAnimator.setOffsetY(offsetY); + lastSwipeTime = System.currentTimeMillis(); + }); + swipeContainer.setScrollEndListener(()-> webViewContainer.invalidateViewPortHeight(true)); + swipeContainer.setDelegate(() -> { +// if (can_minimize) { + dismiss(true, null); +// } else { +// if (!onCheckDismissByUser()) { +// swipeContainer.stickTo(0); +// } +// } + }); + swipeContainer.setTopActionBarOffsetY(ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight - dp(24)); + swipeContainer.setIsKeyboardVisible(obj -> windowView.getKeyboardHeight() >= dp(20)); + } + + public float getContainerTop() { + float transitionProgress = AndroidUtilities.isTablet() ? 0 : actionBarTransitionProgress; + return AndroidUtilities.isTablet() ? AndroidUtilities.lerp(swipeContainer.getTranslationY() + dp(12), AndroidUtilities.statusBarHeight / 2f, actionBarTransitionProgress) : + (AndroidUtilities.lerp(swipeContainer.getTranslationY(), AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight() / 2f, transitionProgress) + dp(12)); + } + + public void attachInternal() { + NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.didSetNewTheme); + + if (fragment.getLayoutContainer() != null) { + fragment.getLayoutContainer().addView(windowView); + } + } + + @Override + public void release() { + if (webViewContainer != null) { + webViewContainer.destroyWebView(); + } + if (fragment.sheetsStack != null) { + fragment.sheetsStack.remove(this); + } + if (windowView != null && windowView.getParent() instanceof ViewGroup) { + ((ViewGroup) windowView.getParent()).removeView(windowView); + } + } + + @Override + public boolean attachedToParent() { + return windowView != null; + } + + // @Override +// protected void onStart() { +// super.onStart(); +// +// Context context = getContext(); +// if (context instanceof ContextWrapper && !(context instanceof LaunchActivity)) { +// context = ((ContextWrapper) context).getBaseContext(); +// } +// if (context instanceof LaunchActivity) { +// ((LaunchActivity) context).addOverlayPasscodeView(passcodeView); +// } +// } +// +// @Override +// protected void onStop() { +// super.onStop(); +// +// Context context = getContext(); +// if (context instanceof ContextWrapper && !(context instanceof LaunchActivity)) { +// context = ((ContextWrapper) context).getBaseContext(); +// } +// if (context instanceof LaunchActivity) { +// ((LaunchActivity) context).removeOverlayPasscodeView(passcodeView); +// } +// } + + public void setParentActivity(Activity parentActivity) { + this.parentActivity = parentActivity; + } + + private void updateActionBarColors() { + if (!overrideBackgroundColor) { + backDrawable.setColor(getColor(Theme.key_windowBackgroundWhiteBlackText)); + backDrawable.setRotatedColor(getColor(Theme.key_windowBackgroundWhiteBlackText)); + actionBar.setTitleColor(getColor(Theme.key_windowBackgroundWhiteBlackText)); + actionBar.setItemsColor(getColor(Theme.key_windowBackgroundWhiteBlackText), false); + actionBar.setItemsBackgroundColor(getColor(Theme.key_actionBarWhiteSelector), false); + actionBar.setPopupBackgroundColor(getColor(Theme.key_actionBarDefaultSubmenuBackground), false); + actionBar.setPopupItemsColor(getColor(Theme.key_actionBarDefaultSubmenuItem), false, false); + actionBar.setPopupItemsColor(getColor(Theme.key_actionBarDefaultSubmenuItemIcon), true, false); + actionBar.setPopupItemsSelectorColor(getColor(Theme.key_dialogButtonSelector), false); + } + } + + private void updateLightStatusBar() { + boolean lightStatusBar; + if (overrideBackgroundColor) { + lightStatusBar = !actionBarIsLight && actionBarTransitionProgress >= 0.85f; + } else { + int color = Theme.getColor(Theme.key_windowBackgroundWhite, null, true); + lightStatusBar = !AndroidUtilities.isTablet() && ColorUtils.calculateLuminance(color) >= 0.721f && actionBarTransitionProgress >= 0.85f; + } + if (wasLightStatusBar != null && wasLightStatusBar == lightStatusBar) { + return; + } + wasLightStatusBar = lightStatusBar; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + int flags = windowView.getSystemUiVisibility(); + if (lightStatusBar) { + flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + } else { + flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; + } + windowView.setSystemUiVisibility(flags); + } + } + + public static JSONObject makeThemeParams(Theme.ResourcesProvider resourcesProvider) { + try { + JSONObject jsonObject = new JSONObject(); + final int backgroundColor = Theme.getColor(Theme.key_dialogBackground, resourcesProvider); + jsonObject.put("bg_color", backgroundColor); + jsonObject.put("section_bg_color", Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider)); + jsonObject.put("secondary_bg_color", Theme.getColor(Theme.key_windowBackgroundGray, resourcesProvider)); + jsonObject.put("text_color", Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider)); + jsonObject.put("hint_color", Theme.getColor(Theme.key_windowBackgroundWhiteHintText, resourcesProvider)); + jsonObject.put("link_color", Theme.getColor(Theme.key_windowBackgroundWhiteLinkText, resourcesProvider)); + jsonObject.put("button_color", Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider)); + jsonObject.put("button_text_color", Theme.getColor(Theme.key_featuredStickers_buttonText, resourcesProvider)); + jsonObject.put("header_bg_color", Theme.getColor(Theme.key_actionBarDefault, resourcesProvider)); + jsonObject.put("accent_text_color", Theme.blendOver(backgroundColor, Theme.getColor(Theme.key_windowBackgroundWhiteBlueText4, resourcesProvider))); + jsonObject.put("section_header_text_color", Theme.blendOver(backgroundColor, Theme.getColor(Theme.key_windowBackgroundWhiteBlueHeader, resourcesProvider))); + jsonObject.put("subtitle_text_color", Theme.blendOver(backgroundColor, Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider))); + jsonObject.put("destructive_text_color", Theme.blendOver(backgroundColor, Theme.getColor(Theme.key_text_RedRegular, resourcesProvider))); + jsonObject.put("section_separator_color", Theme.blendOver(backgroundColor, Theme.getColor(Theme.key_divider, resourcesProvider))); + return jsonObject; + } catch (Exception e) { + FileLog.e(e); + } + return null; + } + + public void setDefaultFullsize(boolean fullsize) { + if (this.defaultFullsize != fullsize) { + this.defaultFullsize = fullsize; + + if (swipeContainer != null) { + swipeContainer.setFullSize(getFullSize()); + } + } + } + + public void setNeedsContext(boolean needsContext) { + this.needsContext = needsContext; + } + + public boolean getFullSize() { + return fullsize == null ? defaultFullsize : fullsize; + } + + public static class WebViewRequestProps { + + public int currentAccount; + public long peerId; + public long botId; + public String buttonText; + public String buttonUrl; + public @WebViewType int type; + public int replyToMsgId; + public boolean silent; + public TLRPC.BotApp app; + public boolean allowWrite; + public String startParam; + public TLRPC.User botUser; + public int flags; + public boolean compact; + + public TLObject response; + public long responseTime; + + public static WebViewRequestProps of( + int currentAccount, + long peerId, + long botId, + String buttonText, + String buttonUrl, + @WebViewType int type, + int replyToMsgId, + boolean silent, + TLRPC.BotApp app, + boolean allowWrite, + String startParam, + TLRPC.User botUser, + int flags, + boolean compact + ) { + WebViewRequestProps p = new WebViewRequestProps(); + p.currentAccount = currentAccount; + p.peerId = peerId; + p.botId = botId; + p.buttonText = buttonText; + p.buttonUrl = buttonUrl; + p.type = type; + p.replyToMsgId = replyToMsgId; + p.silent = silent; + p.app = app; + p.allowWrite = allowWrite; + p.startParam = startParam; + p.botUser = botUser; + p.flags = flags; + p.compact = compact; + if (!compact && !TextUtils.isEmpty(buttonUrl)) { + try { + Uri uri = Uri.parse(buttonUrl); + p.compact = TextUtils.equals(uri.getQueryParameter("mode"), "compact"); + } catch (Exception e) { + FileLog.e(e); + } + } + return p; + } + + public void applyResponse(TLObject response) { + this.response = response; + this.responseTime = System.currentTimeMillis(); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof WebViewRequestProps)) + return false; + final WebViewRequestProps p = (WebViewRequestProps) obj; + return ( + currentAccount == p.currentAccount && + peerId == p.peerId && + botId == p.botId && + TextUtils.equals(buttonUrl, p.buttonUrl) && + type == p.type && + replyToMsgId == p.replyToMsgId && + silent == p.silent && + (app == null ? 0 : app.id) == (p.app == null ? 0 : p.app.id) && + allowWrite == p.allowWrite && + TextUtils.equals(startParam, p.startParam) && + (botUser == null ? 0 : botUser.id) == (p.botUser == null ? 0 : p.botUser.id) && + flags == p.flags + ); + } + } + + public void requestWebView(BaseFragment fragment, WebViewRequestProps props) { + this.requestProps = props; + this.currentAccount = props.currentAccount; + this.peerId = props.peerId; + this.botId = props.botId; + this.replyToMsgId = props.replyToMsgId; + this.silent = props.silent; + this.buttonText = props.buttonText; + this.currentWebApp = props.app; + + CharSequence title = UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(botId)); + try { + TextPaint tp = new TextPaint(); + tp.setTextSize(dp(20)); + title = Emoji.replaceEmoji(title, tp.getFontMetricsInt(), false); + } catch (Exception ignore) {} + actionBar.setTitle(title); + ActionBarMenu menu = actionBar.createMenu(); + menu.removeAllViews(); + + TLRPC.TL_attachMenuBot currentBot = null; + for (TLRPC.TL_attachMenuBot bot : MediaDataController.getInstance(currentAccount).getAttachMenuBots().bots) { + if (bot.bot_id == botId) { + currentBot = bot; + break; + } + } + + menu.addItem(R.id.menu_collapse_bot, R.drawable.arrow_more); + ActionBarMenuItem otherItem = menu.addItem(0, R.drawable.ic_ab_other); + otherItem.addSubItem(R.id.menu_open_bot, R.drawable.msg_bot, LocaleController.getString(R.string.BotWebViewOpenBot)); + settingsItem = otherItem.addSubItem(R.id.menu_settings, R.drawable.msg_settings, LocaleController.getString(R.string.BotWebViewSettings)); + settingsItem.setVisibility(View.GONE); + otherItem.addSubItem(R.id.menu_reload_page, R.drawable.msg_retry, LocaleController.getString(R.string.BotWebViewReloadPage)); + if (currentBot != null && MediaDataController.getInstance(currentAccount).canCreateAttachedMenuBotShortcut(currentBot.bot_id)) { + otherItem.addSubItem(R.id.menu_add_to_home_screen_bot, R.drawable.msg_home, LocaleController.getString(R.string.AddShortcut)); + } + otherItem.addSubItem(R.id.menu_tos_bot, R.drawable.menu_intro, LocaleController.getString(R.string.BotWebViewToS)); + if (currentBot != null && (currentBot.show_in_side_menu || currentBot.show_in_attach_menu)) { + otherItem.addSubItem(R.id.menu_delete_bot, R.drawable.msg_delete, LocaleController.getString(R.string.BotWebViewDeleteBot)); + } + + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { + @Override + public void onItemClick(int id) { + if (id == -1) { + if (!webViewContainer.onBackPressed()) { + onCheckDismissByUser(); + } + } else if (id == R.id.menu_open_bot) { + Bundle bundle = new Bundle(); + bundle.putLong("user_id", botId); + if (parentActivity instanceof LaunchActivity) { + ((LaunchActivity) parentActivity).presentFragment(new ChatActivity(bundle)); + } + dismiss(); + } else if (id == R.id.menu_tos_bot) { + Browser.openUrl(getContext(), LocaleController.getString(R.string.BotWebViewToSLink)); + } else if (id == R.id.menu_reload_page) { + if (webViewContainer.getWebView() != null) { + webViewContainer.getWebView().animate().cancel(); + webViewContainer.getWebView().animate().alpha(0).start(); + } + + progressView.setLoadProgress(0); + progressView.setAlpha(1f); + progressView.setVisibility(View.VISIBLE); + + webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId)); + webViewContainer.loadFlickerAndSettingsItem(currentAccount, botId, settingsItem); + webViewContainer.reload(); + } else if (id == R.id.menu_settings) { + webViewContainer.onSettingsButtonPressed(); + } else if (id == R.id.menu_delete_bot) { + deleteBot(currentAccount, botId, () -> dismiss()); + } else if (id == R.id.menu_add_to_home_screen_bot) { + MediaDataController.getInstance(currentAccount).installShortcut(botId, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT); + } else if (id == R.id.menu_collapse_bot) { + forceExpnaded = true; + dismiss(true, null); + } + } + }); + + final JSONObject themeParams = makeThemeParams(resourcesProvider); + + webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId)); + webViewContainer.loadFlickerAndSettingsItem(currentAccount, botId, settingsItem); + preloadShortcutBotIcon(props.botUser, currentBot); + if (props.response != null) { + loadFromResponse(true); + } else { + switch (props.type) { + case TYPE_BOT_MENU_BUTTON: { + TLRPC.TL_messages_requestWebView req = new TLRPC.TL_messages_requestWebView(); + req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(botId); + req.platform = "android"; + req.compact = props.compact; + + req.url = props.buttonUrl; + req.flags |= 2; + + if (themeParams != null) { + req.theme_params = new TLRPC.TL_dataJSON(); + req.theme_params.data = themeParams.toString(); + req.flags |= 4; + } + + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error != null) { + + } else if (requestProps != null) { + requestProps.applyResponse(response); + loadFromResponse(false); + } + })); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.webViewResultSent); + + break; + } + case TYPE_SIMPLE_WEB_VIEW_BUTTON: { + TLRPC.TL_messages_requestSimpleWebView req = new TLRPC.TL_messages_requestSimpleWebView(); + req.from_switch_webview = (props.flags & FLAG_FROM_INLINE_SWITCH) != 0; + req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + req.platform = "android"; + req.from_side_menu = (props.flags & FLAG_FROM_SIDE_MENU) != 0; + req.compact = props.compact; + if (themeParams != null) { + req.theme_params = new TLRPC.TL_dataJSON(); + req.theme_params.data = themeParams.toString(); + req.flags |= 1; + } + if (!TextUtils.isEmpty(props.buttonUrl)) { + req.flags |= 8; + req.url = props.buttonUrl; + } + if (!TextUtils.isEmpty(props.startParam)) { + req.start_param = props.startParam; + req.flags |= 16; + } + + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error != null) { + + } else if (requestProps != null) { + requestProps.applyResponse(response); + loadFromResponse(false); + } + })); + break; + } + case TYPE_WEB_VIEW_BUTTON: { + TLRPC.TL_messages_requestWebView req = new TLRPC.TL_messages_requestWebView(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(peerId); + req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + req.platform = "android"; + req.compact = props.compact; + if (props.buttonUrl != null) { + req.url = props.buttonUrl; + req.flags |= 2; + } + + if (replyToMsgId != 0) { + req.reply_to = SendMessagesHelper.getInstance(currentAccount).createReplyInput(replyToMsgId); + req.flags |= 1; + } + + if (themeParams != null) { + req.theme_params = new TLRPC.TL_dataJSON(); + req.theme_params.data = themeParams.toString(); + req.flags |= 4; + } + + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error != null) { + + } else if (requestProps != null) { + requestProps.applyResponse(response); + loadFromResponse(false); + } + })); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.webViewResultSent); + break; + } + case TYPE_WEB_VIEW_BOT_APP: { + TLRPC.TL_messages_requestAppWebView req = new TLRPC.TL_messages_requestAppWebView(); + TLRPC.TL_inputBotAppID botApp = new TLRPC.TL_inputBotAppID(); + botApp.id = props.app.id; + botApp.access_hash = props.app.access_hash; + + req.app = botApp; + req.write_allowed = props.allowWrite; + req.platform = "android"; + req.peer = fragment instanceof ChatActivity ? ((ChatActivity) fragment).getCurrentUser() != null ? MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentUser()) : MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentChat()) + : MessagesController.getInputPeer(props.botUser); + req.compact = props.compact; + + if (!TextUtils.isEmpty(props.startParam)) { + req.start_param = props.startParam; + req.flags |= 2; + } + + if (themeParams != null) { + req.theme_params = new TLRPC.TL_dataJSON(); + req.theme_params.data = themeParams.toString(); + req.flags |= 4; + } + + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { + if (error2 != null) { + + } else if (requestProps != null) { + requestProps.applyResponse(response2); + loadFromResponse(false); + } + }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); + } + } + } + } + + private void loadFromResponse(boolean fromTab) { + if (requestProps == null) return; + final long pollTimeout = Math.max(0, POLL_PERIOD - (System.currentTimeMillis() - requestProps.responseTime)); + String url = null; + fullsize = null; + if (requestProps.response instanceof TLRPC.TL_webViewResultUrl) { + TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) requestProps.response; + queryId = resultUrl.query_id; + url = resultUrl.url; + fullsize = resultUrl.fullsize; + } else if (requestProps.response instanceof TLRPC.TL_appWebViewResultUrl) { // deprecated + TLRPC.TL_appWebViewResultUrl result = (TLRPC.TL_appWebViewResultUrl) requestProps.response; + queryId = 0; + url = result.url; + } else if (requestProps.response instanceof TLRPC.TL_simpleWebViewResultUrl) { // deprecated + TLRPC.TL_simpleWebViewResultUrl resultUrl = (TLRPC.TL_simpleWebViewResultUrl) requestProps.response; + queryId = 0; + url = resultUrl.url; + } + if (url != null && !fromTab) { + webViewContainer.loadUrl(currentAccount, url); + } + AndroidUtilities.runOnUIThread(pollRunnable, pollTimeout); + if (swipeContainer != null) { + swipeContainer.setFullSize(getFullSize()); + } + } + + private void preloadShortcutBotIcon(TLRPC.User botUser, TLRPC.TL_attachMenuBot currentBot) { + if (currentBot != null && currentBot.show_in_side_menu && !MediaDataController.getInstance(currentAccount).isShortcutAdded(botId, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT)) { + TLRPC.User user = botUser; + if (user == null) { + user = MessagesController.getInstance(currentAccount).getUser(botId); + } + if (user != null && user.photo != null) { + File f = FileLoader.getInstance(currentAccount).getPathToAttach(user.photo.photo_small, true); + if (!f.exists()) { + MediaDataController.getInstance(currentAccount).preloadImage(ImageLocation.getForUser(user, ImageLocation.TYPE_SMALL), FileLoader.PRIORITY_LOW); + } + } + } + } + + public static void deleteBot(int currentAccount, long botId, Runnable onDone) { + String description; + TLRPC.TL_attachMenuBot currentBot = null; + for (TLRPC.TL_attachMenuBot bot : MediaDataController.getInstance(currentAccount).getAttachMenuBots().bots) { + if (bot.bot_id == botId) { + currentBot = bot; + break; + } + } + if (currentBot == null) { + return; + } + String botName = currentBot.short_name; + description = LocaleController.formatString("BotRemoveFromMenu", R.string.BotRemoveFromMenu, botName); + TLRPC.TL_attachMenuBot finalCurrentBot = currentBot; + new AlertDialog.Builder(LaunchActivity.getLastFragment().getContext()) + .setTitle(LocaleController.getString(R.string.BotRemoveFromMenuTitle)) + .setMessage(AndroidUtilities.replaceTags(description)) + .setPositiveButton(LocaleController.getString("OK", R.string.OK), (dialogInterface, i) -> { + TLRPC.TL_messages_toggleBotInAttachMenu req = new TLRPC.TL_messages_toggleBotInAttachMenu(); + req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + req.enabled = false; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + MediaDataController.getInstance(currentAccount).loadAttachMenuBots(false, true); + }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); + finalCurrentBot.show_in_side_menu = false; + NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.attachMenuBotsDidLoad); + MediaDataController.getInstance(currentAccount).uninstallShortcut(botId, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT); + if (onDone != null) { + onDone.run(); + } + }) + .setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null) + .show(); + } + + private int getColor(int key) { + return Theme.getColor(key, resourcesProvider); + } + + public void show() { + show(false); + } + public void show(boolean lowBounce) { + if (!AndroidUtilities.isSafeToShow(getContext())) return; + + windowView.setAlpha(0f); + windowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + v.removeOnLayoutChangeListener(this); + + swipeContainer.setSwipeOffsetY(lowBounce ? .5f * swipeContainer.getHeight() : swipeContainer.getHeight()); + windowView.setAlpha(1f); + + AnimationNotificationsLocker locker = new AnimationNotificationsLocker(); + locker.lock(); + updateShownAnimated(true); + + if (showOffsetY != Float.MAX_VALUE) { + swipeContainer.setSwipeOffsetAnimationDisallowed(true); + swipeContainer.setOffsetY(showOffsetY); + swipeContainer.setSwipeOffsetAnimationDisallowed(false); + } + + if (showExpanded || getFullSize()) { + swipeContainer.stickTo(-swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY(), locker::unlock); + } else { + new SpringAnimation(swipeContainer, ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer.SWIPE_OFFSET_Y, 0) + .setSpring(new SpringForce(0) + .setDampingRatio(lowBounce ? SpringForce.DAMPING_RATIO_NO_BOUNCY : SpringForce.DAMPING_RATIO_LOW_BOUNCY) + .setStiffness(lowBounce ? 800 : 500.0f) + ).addEndListener((animation, canceled, value, velocity) -> { + locker.unlock(); + }).start(); + } + swipeContainer.opened = true; + } + }); + + attachInternal(); + } + + private float shown; + private ValueAnimator shownAnimator; + private void updateShownAnimated(boolean shown) { + if (shownAnimator != null) { + shownAnimator.cancel(); + } + shownAnimator = ValueAnimator.ofFloat(this.shown, shown ? 1f : 0f); + shownAnimator.addUpdateListener(anm -> { + this.shown = (float) anm.getAnimatedValue(); + checkNavBarColor(); + }); + shownAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + BotWebViewAttachedSheet.this.shown = shown ? 1f : 0f; + checkNavBarColor(); + } + }); + shownAnimator.setDuration(200); + shownAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT); + shownAnimator.start(); + } + + public long getBotId() { + return botId; + } + + @Override + public boolean isShown() { + return !dismissed; + } + + @Override + public boolean showDialog(Dialog dialog) { + return false; + } + + @Override + public void setKeyboardHeightFromParent(int keyboardHeight) { + // TODO + } + + @Override + public int getNavigationBarColor(int color) { + return ColorUtils.blendARGB(color, navBarColor, shown); + } + + @Override + public boolean onBackPressed() { + if (webViewContainer.onBackPressed()) { + return true; + } +// if (can_minimize) { + dismiss(true, null); +// } else { +// onCheckDismissByUser(); +// } + return true; + } + + public void dismiss() { + dismiss(false, null); + } + + public boolean onCheckDismissByUser() { + if (needCloseConfirmation) { + String botName = null; + TLRPC.User user = MessagesController.getInstance(currentAccount).getUser(botId); + if (user != null) { + botName = ContactsController.formatName(user.first_name, user.last_name); + } + + AlertDialog dialog = new AlertDialog.Builder(getContext()) + .setTitle(botName) + .setMessage(LocaleController.getString(R.string.BotWebViewChangesMayNotBeSaved)) + .setPositiveButton(LocaleController.getString(R.string.BotWebViewCloseAnyway), (dialog2, which) -> dismiss()) + .setNegativeButton(LocaleController.getString(R.string.Cancel), null) + .create(); + dialog.show(); + TextView textView = (TextView) dialog.getButton(AlertDialog.BUTTON_POSITIVE); + textView.setTextColor(getColor(Theme.key_text_RedBold)); + return false; + } else { + dismiss(); + return true; + } + } + + private Runnable onDismissListener; + @Override + public void setOnDismissListener(Runnable listener) { + onDismissListener = listener; + } + + + public void dismiss(boolean intoTabs, Runnable callback) { + if (dismissed) { + return; + } + dismissed = true; + AndroidUtilities.cancelRunOnUIThread(pollRunnable); + + NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.webViewResultSent); + NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.didSetNewTheme); + + updateShownAnimated(false); + if (intoTabs && (LaunchActivity.instance == null || LaunchActivity.instance.getBottomSheetTabsOverlay() == null)) { + intoTabs = false; + } + if (intoTabs) { + if (springAnimation != null) { + springAnimation.getSpring().setFinalPosition(0); + springAnimation.start(); + } + LaunchActivity.instance.getBottomSheetTabsOverlay().dismissSheet(this); + } else { + webViewContainer.destroyWebView(); + swipeContainer.setFullSize(false); + swipeContainer.stickTo(swipeContainer.getHeight() + windowView.measureKeyboardHeight() + (getFullSize() ? dp(200) : 0), () -> { + release(); + if (callback != null) { + callback.run(); + } + if (onDismissListener != null) { + onDismissListener.run(); + onDismissListener = null; + } + }); + } + } + + @Override + public void didReceivedNotification(int id, int account, Object... args) { + if (id == NotificationCenter.webViewResultSent) { + long queryId = (long) args[0]; + + if (this.queryId == queryId) { + dismiss(); + } + } else if (id == NotificationCenter.didSetNewTheme) { + windowView.invalidate(); + webViewContainer.updateFlickerBackgroundColor(getColor(Theme.key_windowBackgroundWhite)); + updateActionBarColors(); + updateLightStatusBar(); + } + } + + public static int navigationBarColor(int actionBarColor) { +// final boolean isDark = AndroidUtilities.computePerceivedBrightness(actionBarColor) < 0.721f; +// final int themeNavBarColor = (Theme.isCurrentThemeDark() == isDark ? Theme.getColor(Theme.key_windowBackgroundGray) : (isDark ? 0xFF151E27 : 0xFFF0F0F0)); +// return Theme.adaptHue(themeNavBarColor, actionBarColor); +// return OKLCH.adapt(themeNavBarColor, actionBarColor); + return Theme.adaptHSV(actionBarColor, +.35f, -.1f); + } + + public void checkNavBarColor() { + if (attachedToParent() && LaunchActivity.instance != null) { + LaunchActivity.instance.checkSystemBarColors(true, true, true, false); + //LaunchActivity.instance.setNavigationBarColor(fragment.getNavigationBarColor(), false); + } + } + + @Override + public boolean isFullyVisible() { + return false; + } + + public class WindowView extends SizeNotifierFrameLayout { + public WindowView(Context context) { + super(context); + setWillNotDraw(false); + } + + private final Paint navbarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + @Override + protected void dispatchDraw(Canvas canvas) { + if (drawingFromOverlay) return; + super.dispatchDraw(canvas); + navbarPaint.setColor(navBarColor); + AndroidUtilities.rectTmp.set(0, getHeight() - getPaddingBottom(), getWidth(), getHeight() + AndroidUtilities.navigationBarHeight); + canvas.drawRect(AndroidUtilities.rectTmp, navbarPaint); + } + + @Override + protected boolean drawChild(Canvas canvas, View child, long drawingTime) { + if (drawingFromOverlay) return false; + return super.drawChild(canvas, child, drawingTime); + } + + @Override + protected void onDraw(Canvas canvas) { + if (drawingFromOverlay) return; + + super.onDraw(canvas); + + if (!overrideBackgroundColor) { + backgroundPaint.setColor(getColor(Theme.key_windowBackgroundWhite)); + } + AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); + canvas.drawRect(AndroidUtilities.rectTmp, dimPaint); + + actionBarPaint.setColor(actionBarColor); + float radius = dp(16) * (AndroidUtilities.isTablet() ? 1f : 1f - actionBarTransitionProgress); + AndroidUtilities.rectTmp.set(swipeContainer.getLeft(), AndroidUtilities.lerp(swipeContainer.getTranslationY(), 0, actionBarTransitionProgress), swipeContainer.getRight(), swipeContainer.getTranslationY() + dp(24) + radius); + canvas.drawRoundRect(AndroidUtilities.rectTmp, radius, radius, actionBarPaint); + + AndroidUtilities.rectTmp.set(swipeContainer.getLeft(), swipeContainer.getTranslationY() + dp(24), swipeContainer.getRight(), getHeight()); + canvas.drawRect(AndroidUtilities.rectTmp, backgroundPaint); + } + + @Override + public void draw(Canvas canvas) { + if (drawingFromOverlay) return; + + super.draw(canvas); + + float transitionProgress = AndroidUtilities.isTablet() ? 0 : actionBarTransitionProgress; + linePaint.setColor(lineColor); + linePaint.setAlpha((int) (linePaint.getAlpha() * (1f - Math.min(0.5f, transitionProgress) / 0.5f))); + + canvas.save(); + float scale = 1f - transitionProgress; + float y = getContainerTop(); + canvas.scale(scale, scale, getWidth() / 2f, y); + canvas.drawLine(getWidth() / 2f - dp(16), y, getWidth() / 2f + dp(16), y, linePaint); + canvas.restore(); + + actionBarShadow.setAlpha((int) (actionBar.getAlpha() * 0xFF)); + y = actionBar.getY() + actionBar.getTranslationY() + actionBar.getHeight(); + actionBarShadow.setBounds(0, (int)y, getWidth(), (int)(y + actionBarShadow.getIntrinsicHeight())); + actionBarShadow.draw(canvas); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + if (drawingFromOverlay) return false; + if (event.getAction() == MotionEvent.ACTION_DOWN && (event.getY() <= AndroidUtilities.lerp(swipeContainer.getTranslationY() + dp(24), 0, actionBarTransitionProgress) || + event.getX() > swipeContainer.getRight() || event.getX() < swipeContainer.getLeft())) { +// if (can_minimize) { + dismiss(true, null); +// } else { +// onCheckDismissByUser(); +// } + return true; + } + return super.onTouchEvent(event); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Bulletin.addDelegate(this, new Bulletin.Delegate() { + @Override + public int getTopOffset(int tag) { + return AndroidUtilities.statusBarHeight; + } + }); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + Bulletin.removeDelegate(this); + } + + private boolean drawingFromOverlay; + public void setDrawingFromOverlay(boolean drawingFromOverlay) { + if (this.drawingFromOverlay != drawingFromOverlay) { + this.drawingFromOverlay = drawingFromOverlay; + invalidate(); + } + } + + private final RectF rect = new RectF(); + private final Path clipPath = new Path(); + + + public float drawInto(Canvas canvas, RectF finalRect, float progress, RectF clipRect) { + rect.set(swipeContainer.getLeft(), swipeContainer.getTranslationY() + dp(24), swipeContainer.getRight(), getHeight()); + AndroidUtilities.lerpCentered(rect, finalRect, progress, clipRect); + + canvas.save(); + + clipPath.rewind(); + float radius = dp(16) * (AndroidUtilities.isTablet() ? 1f : 1f - actionBarTransitionProgress); + final float r = AndroidUtilities.lerp(radius, dp(10), progress); + clipPath.addRoundRect(clipRect, r, r, Path.Direction.CW); + canvas.clipPath(clipPath); + canvas.drawPaint(backgroundPaint); + + if (swipeContainer != null) { + canvas.save(); + canvas.translate(swipeContainer.getX(), Math.max(swipeContainer.getY(), clipRect.top) + progress * dp(51)); + swipeContainer.draw(canvas); + canvas.restore(); + } + + canvas.restore(); + + return r; + } + + } + + public void setBackgroundColor(int color, boolean animated) { + int from = backgroundPaint.getColor(); + if (animated) { + ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(200); + animator.setInterpolator(CubicBezierInterpolator.DEFAULT); + animator.addUpdateListener(animation -> { + backgroundPaint.setColor(ColorUtils.blendARGB(from, color, (Float) animation.getAnimatedValue())); + updateActionBarColors(); + windowView.invalidate(); + }); + animator.start(); + } else { + backgroundPaint.setColor(color); + updateActionBarColors(); + windowView.invalidate(); + } + } + + public void setActionBarColor(int color, boolean isOverrideColor, boolean animated) { + int from = actionBarColor; + int navBarFrom = navBarColor; + int to = color; + int navBarTo = navigationBarColor(color); + + BotWebViewMenuContainer.ActionBarColorsAnimating actionBarColorsAnimating = new BotWebViewMenuContainer.ActionBarColorsAnimating(); + actionBarColorsAnimating.setFrom(overrideBackgroundColor ? actionBarColor : 0, resourcesProvider); + overrideBackgroundColor = isOverrideColor; + actionBarIsLight = ColorUtils.calculateLuminance(color) < 0.721f; + actionBarColorsAnimating.setTo(overrideBackgroundColor ? to : 0, resourcesProvider); + + if (animated) { + ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(200); + animator.setInterpolator(CubicBezierInterpolator.DEFAULT); + animator.addUpdateListener(animation -> { + float progress = (float) animation.getAnimatedValue(); + actionBarColor = ColorUtils.blendARGB(from, to, progress); + navBarColor = ColorUtils.blendARGB(navBarFrom, navBarTo, progress); + checkNavBarColor(); + windowView.invalidate(); + actionBar.setBackgroundColor(actionBarColor); + + actionBarColorsAnimating.updateActionBar(actionBar, progress); + lineColor = actionBarColorsAnimating.getColor(Theme.key_sheet_scrollUp); + + windowView.invalidate(); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + float progress = 1f; + actionBarColor = ColorUtils.blendARGB(from, to, progress); + navBarColor = ColorUtils.blendARGB(navBarFrom, navBarTo, progress); + checkNavBarColor(); + windowView.invalidate(); + actionBar.setBackgroundColor(actionBarColor); + + actionBarColorsAnimating.updateActionBar(actionBar, progress); + lineColor = actionBarColorsAnimating.getColor(Theme.key_sheet_scrollUp); + + windowView.invalidate(); + } + }); + animator.start(); + } else { + float progress = 1f; + actionBarColor = to; + navBarColor = navBarTo; + checkNavBarColor(); + windowView.invalidate(); + actionBar.setBackgroundColor(actionBarColor); + + actionBarColorsAnimating.updateActionBar(actionBar, progress); + lineColor = actionBarColorsAnimating.getColor(Theme.key_sheet_scrollUp); + + windowView.invalidate(); + } + updateLightStatusBar(); + } + + private MainButtonSettings mainButtonSettings; + + public void setMainButton(MainButtonSettings s) { + mainButtonSettings = s; + + mainButton.setClickable(s.isActive); + mainButton.setText(s.text); + mainButton.setTextColor(s.textColor); + mainButton.setBackground(BotWebViewContainer.getMainButtonRippleDrawable(s.color)); + if (s.isVisible != mainButtonWasVisible) { + mainButtonWasVisible = s.isVisible; + mainButton.animate().cancel(); + if (s.isVisible) { + mainButton.setAlpha(0f); + mainButton.setVisibility(View.VISIBLE); + } + mainButton.animate().alpha(s.isVisible ? 1f : 0f).setDuration(150).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!s.isVisible) { + mainButton.setVisibility(View.GONE); + } + swipeContainer.requestLayout(); + } + }).start(); + } + radialProgressView.setProgressColor(s.textColor); + if (s.isProgressVisible != mainButtonProgressWasVisible) { + mainButtonProgressWasVisible = s.isProgressVisible; + radialProgressView.animate().cancel(); + if (s.isProgressVisible) { + radialProgressView.setAlpha(0f); + radialProgressView.setVisibility(View.VISIBLE); + } + radialProgressView.animate().alpha(s.isProgressVisible ? 1f : 0f) + .scaleX(s.isProgressVisible ? 1f : 0.1f) + .scaleY(s.isProgressVisible ? 1f : 0.1f) + .setDuration(250) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!s.isProgressVisible) { + radialProgressView.setVisibility(View.GONE); + } + } + }).start(); + } + } + +} diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewContainer.java index 3530bc9fc2..b49f189a06 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewContainer.java @@ -15,16 +15,19 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.net.http.SslError; import android.os.Build; import android.os.Message; import android.text.SpannableStringBuilder; import android.text.TextUtils; +import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.MotionEvent; @@ -34,8 +37,12 @@ import android.webkit.JavascriptInterface; import android.webkit.PermissionRequest; import android.webkit.RenderProcessGoneDetail; +import android.webkit.SslErrorHandler; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; +import android.webkit.WebResourceError; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; @@ -92,6 +99,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; @@ -100,9 +108,7 @@ public abstract class BotWebViewContainer extends FrameLayout implements Notific private final static int REQUEST_CODE_WEB_VIEW_FILE = 3000, REQUEST_CODE_WEB_PERMISSION = 4000, REQUEST_CODE_QR_CAMERA_PERMISSION = 5000; private final static int DIALOG_SEQUENTIAL_COOLDOWN_TIME = 3000; - private final static List WHITELISTED_SCHEMES = Arrays.asList("http", "https"); - - private WebView webView; + private MyWebView webView; private String mUrl; private Delegate delegate; private WebViewScrollListener webViewScrollListener; @@ -157,6 +163,8 @@ public BotWebViewContainer(@NonNull Context context, Theme.ResourcesProvider res super(context); this.resourcesProvider = resourcesProvider; + d("created new webview container"); + if (context instanceof Activity) { this.parentActivity = (Activity) context; } @@ -217,7 +225,7 @@ public void setViewPortByMeasureSuppressed(boolean viewPortByMeasureSuppressed) private void checkCreateWebView() { if (webView == null && !webViewNotAvailable) { try { - setupWebView(); + setupWebView(null); } catch (Throwable t) { FileLog.e(t); @@ -231,70 +239,30 @@ private void checkCreateWebView() { } } + public void replaceWebView(MyWebView webView, WebViewProxy proxy) { + setupWebView(webView, proxy); + } + + private void setupWebView(MyWebView replaceWith) { + setupWebView(replaceWith, null); + } + + private WebViewProxy webViewProxy; + public WebViewProxy getProxy() { + return webViewProxy; + } + @SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface"}) - private void setupWebView() { + private void setupWebView(MyWebView replaceWith, WebViewProxy proxy) { if (webView != null) { webView.destroy(); removeView(webView); } - webView = new WebView(getContext()) { - private int prevScrollX, prevScrollY; - - @Override - protected void onScrollChanged(int l, int t, int oldl, int oldt) { - super.onScrollChanged(l, t, oldl, oldt); - - if (webViewScrollListener != null) { - webViewScrollListener.onWebViewScrolled(this, getScrollX() - prevScrollX, getScrollY() - prevScrollY); - } - - prevScrollX = getScrollX(); - prevScrollY = getScrollY(); - } - - @Override - public void setScrollX(int value) { - super.setScrollX(value); - prevScrollX = value; - } - - @Override - public void setScrollY(int value) { - super.setScrollY(value); - prevScrollY = value; - } - - @Override - public boolean onCheckIsTextEditor() { - return BotWebViewContainer.this.isFocusable(); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY)); - } - - @SuppressLint("ClickableViewAccessibility") - @Override - public boolean onTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - lastClickMs = System.currentTimeMillis(); - } - return super.onTouchEvent(event); - } - - @Override - protected void onAttachedToWindow() { - AndroidUtilities.checkAndroidTheme(getContext(), true); - super.onAttachedToWindow(); - } - - @Override - protected void onDetachedFromWindow() { - AndroidUtilities.checkAndroidTheme(getContext(), false); - super.onDetachedFromWindow(); - } - }; + if (replaceWith != null) { + AndroidUtilities.removeFromParent(replaceWith); + } + webView = replaceWith == null ? new MyWebView(getContext()) : replaceWith; + webView.setContainers(this, webViewScrollListener); webView.setBackgroundColor(getColor(Theme.key_windowBackgroundWhite)); WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); @@ -313,248 +281,21 @@ protected void onDetachedFromWindow() { GeolocationPermissions.getInstance().clearAll(); webView.setVerticalScrollBarEnabled(false); - webView.setWebViewClient(new WebViewClient() { - @Override - public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) { - if (LaunchActivity.instance != null && LaunchActivity.instance.isFinishing()) { - return true; - } - new AlertDialog.Builder(getContext(), resourcesProvider) - .setTitle(getString(R.string.ChromeCrashTitle)) - .setMessage(AndroidUtilities.replaceSingleTag(getString(R.string.ChromeCrashMessage), () -> Browser.openUrl(getContext(), "https://play.google.com/store/apps/details?id=com.google.android.webview"))) - .setPositiveButton(getString(R.string.OK), null) - .setOnDismissListener(d -> { - if (delegate != null) { - delegate.onCloseRequested(null); - } - }) - .show(); - return true; - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - Uri uriNew = Uri.parse(url); - if (Browser.isInternalUri(uriNew, null)) { - if (WHITELISTED_SCHEMES.contains(uriNew.getScheme())) { - onOpenUri(uriNew); - } - return true; - } - return false; - } - - @Override - public void onPageFinished(WebView view, String url) { - setPageLoaded(url); - } - }); - webView.setWebChromeClient(new WebChromeClient() { - private Dialog lastPermissionsDialog; - - @Override - public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { - WebView newWebView = new WebView(view.getContext()); - newWebView.setWebViewClient(new WebViewClient() { - @Override - public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) { - if (LaunchActivity.instance != null && LaunchActivity.instance.isFinishing()) { - return true; - } - new AlertDialog.Builder(getContext(), resourcesProvider) - .setTitle(getString(R.string.ChromeCrashTitle)) - .setMessage(AndroidUtilities.replaceSingleTag(getString(R.string.ChromeCrashMessage), () -> Browser.openUrl(getContext(), "https://play.google.com/store/apps/details?id=com.google.android.webview"))) - .setPositiveButton(getString(R.string.OK), null) - .setOnDismissListener(d -> { - if (delegate != null) { - delegate.onCloseRequested(null); - } - }) - .show(); - return true; - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - onOpenUri(Uri.parse(url)); - return true; - } - }); - WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; - transport.setWebView(newWebView); - resultMsg.sendToTarget(); - return true; - } - - @Override - public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { - Activity activity = AndroidUtilities.findActivity(getContext()); - if (activity == null) { - return false; - } - - if (mFilePathCallback != null) { - mFilePathCallback.onReceiveValue(null); - } - - mFilePathCallback = filePathCallback; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - activity.startActivityForResult(fileChooserParams.createIntent(), REQUEST_CODE_WEB_VIEW_FILE); - } else { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("*/*"); - activity.startActivityForResult(Intent.createChooser(intent, getString(R.string.BotWebViewFileChooserTitle)), REQUEST_CODE_WEB_VIEW_FILE); - } - - return true; - } - - @Override - public void onProgressChanged(WebView view, int newProgress) { - if (webViewProgressListener != null) { - webViewProgressListener.accept(newProgress / 100f); - } - } - - @Override - public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { - if (parentActivity == null) { - callback.invoke(origin, false, false); - return; - } - lastPermissionsDialog = AlertsCreator.createWebViewPermissionsRequestDialog(parentActivity, resourcesProvider, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, R.raw.permission_request_location, LocaleController.formatString(R.string.BotWebViewRequestGeolocationPermission, UserObject.getUserName(botUser)), LocaleController.formatString(R.string.BotWebViewRequestGeolocationPermissionWithHint, UserObject.getUserName(botUser)), allow -> { - if (lastPermissionsDialog != null) { - lastPermissionsDialog = null; - - if (allow) { - runWithPermissions(new String[] {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, allowSystem -> { - callback.invoke(origin, allowSystem, false); - if (allowSystem) { - hasUserPermissions = true; - } - }); - } else { - callback.invoke(origin, false, false); - } - } - }); - lastPermissionsDialog.show(); - } - - @Override - public void onGeolocationPermissionsHidePrompt() { - if (lastPermissionsDialog != null){ - lastPermissionsDialog.dismiss(); - lastPermissionsDialog = null; - } - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - @Override - public void onPermissionRequest(PermissionRequest request) { - if (lastPermissionsDialog != null){ - lastPermissionsDialog.dismiss(); - lastPermissionsDialog = null; - } - - String[] resources = request.getResources(); - if (resources.length == 1) { - String resource = resources[0]; - - if (parentActivity == null) { - request.deny(); - return; - } - - switch (resource) { - case PermissionRequest.RESOURCE_AUDIO_CAPTURE: { - lastPermissionsDialog = AlertsCreator.createWebViewPermissionsRequestDialog(parentActivity, resourcesProvider, new String[] {Manifest.permission.RECORD_AUDIO}, R.raw.permission_request_microphone, LocaleController.formatString(R.string.BotWebViewRequestMicrophonePermission, UserObject.getUserName(botUser)), LocaleController.formatString(R.string.BotWebViewRequestMicrophonePermissionWithHint, UserObject.getUserName(botUser)), allow -> { - if (lastPermissionsDialog != null) { - lastPermissionsDialog = null; - - if (allow) { - runWithPermissions(new String[] {Manifest.permission.RECORD_AUDIO}, allowSystem -> { - if (allowSystem) { - request.grant(new String[] {resource}); - hasUserPermissions = true; - } else { - request.deny(); - } - }); - } else { - request.deny(); - } - } - }); - lastPermissionsDialog.show(); - break; - } - case PermissionRequest.RESOURCE_VIDEO_CAPTURE: { - lastPermissionsDialog = AlertsCreator.createWebViewPermissionsRequestDialog(parentActivity, resourcesProvider, new String[] {Manifest.permission.CAMERA}, R.raw.permission_request_camera, LocaleController.formatString(R.string.BotWebViewRequestCameraPermission, UserObject.getUserName(botUser)), LocaleController.formatString(R.string.BotWebViewRequestCameraPermissionWithHint, UserObject.getUserName(botUser)), allow -> { - if (lastPermissionsDialog != null) { - lastPermissionsDialog = null; - - if (allow) { - runWithPermissions(new String[] {Manifest.permission.CAMERA}, allowSystem -> { - if (allowSystem) { - request.grant(new String[] {resource}); - hasUserPermissions = true; - } else { - request.deny(); - } - }); - } else { - request.deny(); - } - } - }); - lastPermissionsDialog.show(); - break; - } - } - } else if ( - resources.length == 2 && - (PermissionRequest.RESOURCE_AUDIO_CAPTURE.equals(resources[0]) || PermissionRequest.RESOURCE_VIDEO_CAPTURE.equals(resources[0])) && - (PermissionRequest.RESOURCE_AUDIO_CAPTURE.equals(resources[1]) || PermissionRequest.RESOURCE_VIDEO_CAPTURE.equals(resources[1])) - ) { - lastPermissionsDialog = AlertsCreator.createWebViewPermissionsRequestDialog(parentActivity, resourcesProvider, new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, R.raw.permission_request_camera, LocaleController.formatString(R.string.BotWebViewRequestCameraMicPermission, UserObject.getUserName(botUser)), LocaleController.formatString(R.string.BotWebViewRequestCameraMicPermissionWithHint, UserObject.getUserName(botUser)), allow -> { - if (lastPermissionsDialog != null) { - lastPermissionsDialog = null; - - if (allow) { - runWithPermissions(new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, allowSystem -> { - if (allowSystem) { - request.grant(new String[] {resources[0], resources[1]}); - hasUserPermissions = true; - } else { - request.deny(); - } - }); - } else { - request.deny(); - } - } - }); - lastPermissionsDialog.show(); - } - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - @Override - public void onPermissionRequestCanceled(PermissionRequest request) { - if (lastPermissionsDialog != null){ - lastPermissionsDialog.dismiss(); - lastPermissionsDialog = null; - } - } - }); - webView.setAlpha(0f); + if (replaceWith == null) { + webView.setAlpha(0f); + } addView(webView); // We can't use javascript interface because of minSDK 16, it can be exploited because of reflection access if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - webView.addJavascriptInterface(new WebViewProxy(), "TelegramWebviewProxy"); + if (proxy != null) { + webViewProxy = proxy; + } + if (webViewProxy == null) { + webViewProxy = new WebViewProxy(this); + webView.addJavascriptInterface(webViewProxy, "TelegramWebviewProxy"); + } + webViewProxy.setContainer(this); } onWebViewCreated(); @@ -619,7 +360,12 @@ public boolean onBackPressed() { } private void setPageLoaded(String url) { + if (webView != null) { + webView.isPageLoaded = true; + } + if (isPageLoaded) { + d("setPageLoaded: already loaded"); return; } @@ -636,11 +382,26 @@ public void onAnimationEnd(Animator animation) { }); set.start(); mUrl = url; + d("setPageLoaded: isPageLoaded = true!"); isPageLoaded = true; BotWebViewContainer.this.setFocusable(true); delegate.onWebAppReady(); } + public void setState(boolean loaded, String url) { + d("setState(" + loaded + ", " + url + ")"); + isPageLoaded = loaded; + mUrl = url; + } + + public void setIsBackButtonVisible(boolean visible) { + isBackButtonVisible = visible; + } + + public String getUrlLoaded() { + return mUrl; + } + public boolean hasUserPermissions() { return hasUserPermissions; } @@ -825,7 +586,7 @@ public void setWebViewProgressListener(Consumer webViewProgressListener) this.webViewProgressListener = webViewProgressListener; } - public WebView getWebView() { + public MyWebView getWebView() { return webView; } @@ -913,6 +674,7 @@ public void reload() { lastClickMs = 0; hasUserPermissions = false; if (webView != null) { + webView.onResume(); webView.reload(); } }); @@ -927,6 +689,7 @@ public void loadUrl(int currentAccount, String url) { mUrl = url; checkCreateWebView(); if (webView != null) { + webView.onResume(); webView.loadUrl(url); } }); @@ -935,6 +698,7 @@ public void loadUrl(int currentAccount, String url) { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + d("attached"); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.didSetNewTheme); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.onActivityResultReceived); @@ -955,6 +719,7 @@ public int getBottomOffset(int tag) { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + d("detached"); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.didSetNewTheme); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.onActivityResultReceived); @@ -963,12 +728,21 @@ protected void onDetachedFromWindow() { Bulletin.removeDelegate(this); } + private boolean preserving; + public void preserveWebView() { + d("preserveWebView"); + preserving = true; + } + public void destroyWebView() { + d("destroyWebView"); if (webView != null) { if (webView.getParent() != null) { removeView(webView); } - webView.destroy(); + if (!preserving) { + webView.destroy(); + } isPageLoaded = false; } } @@ -1019,16 +793,20 @@ public void didReceivedNotification(int id, int account, Object... args) { } } - private void notifyThemeChanged() { + public void notifyThemeChanged() { notifyEvent("theme_changed", buildThemeParams()); } private void notifyEvent(String event, JSONObject eventData) { + d("notifyEvent " + event); evaluateJs("window.Telegram.WebView.receiveEvent('" + event + "', " + eventData + ");", false); } public void setWebViewScrollListener(WebViewScrollListener webViewScrollListener) { this.webViewScrollListener = webViewScrollListener; + if (webView != null) { + webView.setContainers(this, webViewScrollListener); + } } public void setDelegate(Delegate delegate) { @@ -1037,8 +815,10 @@ public void setDelegate(Delegate delegate) { private void onEventReceived(String eventType, String eventData) { if (webView == null || delegate == null) { + d("onEventReceived " + eventType + ": no webview or delegate!"); return; } + d("onEventReceived " + eventType); switch (eventType) { case "web_app_close": { delegate.onCloseRequested(null); @@ -1263,7 +1043,7 @@ public void didReceivedNotification(int id, int account, Object... args) { if (!TextUtils.isEmpty(overrideColorString)) { int color = Color.parseColor(overrideColorString); if (color != 0) { - delegate.onWebAppSetActionBarColor(color, true); + delegate.onWebAppSetActionBarColor(-1, color, true); } } else { String key = jsonObject.optString("color_key"); @@ -1279,7 +1059,7 @@ public void didReceivedNotification(int id, int account, Object... args) { } } if (themeKey >= 0) { - delegate.onWebAppSetActionBarColor(Theme.getColor(themeKey, resourcesProvider), false); + delegate.onWebAppSetActionBarColor(themeKey, Theme.getColor(themeKey, resourcesProvider), false); } } } catch (JSONException e) { @@ -1362,7 +1142,8 @@ public void didReceivedNotification(int id, int account, Object... args) { try { JSONObject jsonData = new JSONObject(eventData); Uri uri = Uri.parse(jsonData.optString("url")); - if (WHITELISTED_SCHEMES.contains(uri.getScheme())) { + if (MessagesController.getInstance(currentAccount).webAppAllowedProtocols != null && + MessagesController.getInstance(currentAccount).webAppAllowedProtocols.contains(uri.getScheme())) { onOpenUri(uri, jsonData.optBoolean("try_instant_view"), true); } } catch (Exception e) { @@ -1377,7 +1158,7 @@ public void didReceivedNotification(int id, int account, Object... args) { if (pathFull.startsWith("/")) { pathFull = pathFull.substring(1); } - onOpenUri(Uri.parse("https://t.me/" + pathFull)); + onOpenUri(Uri.parse("https://t.me/" + pathFull), false, true); } catch (JSONException e) { FileLog.e(e); } @@ -1975,10 +1756,21 @@ public void onWebViewCreated() { } - private class WebViewProxy { + public static class WebViewProxy { + public BotWebViewContainer container; + public WebViewProxy(BotWebViewContainer container) { + this.container = container; + } + public void setContainer(BotWebViewContainer container) { + this.container = container; + } @JavascriptInterface public void postEvent(String eventType, String eventData) { - AndroidUtilities.runOnUIThread(() -> onEventReceived(eventType, eventData)); + if (container == null) { + FileLog.d("webviewproxy.postEvent: no container"); + return; + } + AndroidUtilities.runOnUIThread(() -> container.onEventReceived(eventType, eventData)); } } @@ -2017,9 +1809,10 @@ default void onSendWebViewData(String data) {} * Called when WebView requests to set action bar color * * @param colorKey Color theme key + * @param color color * @param isOverrideColor */ - void onWebAppSetActionBarColor(int colorKey, boolean isOverrideColor); + void onWebAppSetActionBarColor(int colorKey, int color, boolean isOverrideColor); /** * Called when WebView requests to set background color @@ -2120,4 +1913,500 @@ public PopupButton(JSONObject obj) throws JSONException { } } } + + private static int tags = 0; + + public static class MyWebView extends WebView { + private final int tag = tags++; + private boolean isPageLoaded; + + public boolean isPageLoaded() { + return isPageLoaded; + } + + public void d(String s) { + FileLog.d("[webview] #" + tag + " " + s); + } + + public MyWebView(Context context) { + super(context); + d("created new webview"); + + setWebViewClient(new WebViewClient() { + + @Nullable + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { + d("shouldInterceptRequest " + (request == null ? null : request.getUrl())); + return super.shouldInterceptRequest(view, request); + } + + @Nullable + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + d("shouldInterceptRequest " + url); + return super.shouldInterceptRequest(view, url); + } + + @Override + public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) { + d("onRenderProcessGone priority=" + (detail == null ? null : detail.rendererPriorityAtExit()) + " didCrash=" + (detail == null ? null : detail.didCrash())); + if (!AndroidUtilities.isSafeToShow(getContext())) { + return true; + } + new AlertDialog.Builder(getContext(), botWebViewContainer == null ? null : botWebViewContainer.resourcesProvider) + .setTitle(getString(R.string.ChromeCrashTitle)) + .setMessage(AndroidUtilities.replaceSingleTag(getString(R.string.ChromeCrashMessage), () -> Browser.openUrl(getContext(), "https://play.google.com/store/apps/details?id=com.google.android.webview"))) + .setPositiveButton(getString(R.string.OK), null) + .setOnDismissListener(d -> { + if (botWebViewContainer != null && botWebViewContainer.delegate != null) { + botWebViewContainer.delegate.onCloseRequested(null); + } + }) + .show(); + return true; + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + Uri uriNew = Uri.parse(url); + if (botWebViewContainer != null && Browser.isInternalUri(uriNew, null)) { + if (MessagesController.getInstance(botWebViewContainer.currentAccount).webAppAllowedProtocols != null && + MessagesController.getInstance(botWebViewContainer.currentAccount).webAppAllowedProtocols.contains(uriNew.getScheme())) { + botWebViewContainer.onOpenUri(uriNew); + } + d("shouldOverrideUrlLoading("+url+") = true"); + return true; + } + d("shouldOverrideUrlLoading("+url+") = false"); + return false; + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + d("onPageStarted " + url); + super.onPageStarted(view, url, favicon); + } + + @Override + public void onPageFinished(WebView view, String url) { + isPageLoaded = true; + d("onPageFinished"); + if (botWebViewContainer != null) { + botWebViewContainer.setPageLoaded(url); + } else { + d("onPageFinished: no container"); + } + } + + @Override + public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { + d("onReceivedError: " + error.getErrorCode() + " " + error.getDescription()); + super.onReceivedError(view, request, error); + } + + @Override + public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + d("onReceivedError: " + errorCode + " " + description + " url=" + failingUrl); + super.onReceivedError(view, errorCode, description, failingUrl); + } + + @Override + public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { + d("onReceivedHttpError: statusCode=" + (errorResponse == null ? null : errorResponse.getStatusCode()) + " request=" + (request == null ? null : request.getUrl())); + super.onReceivedHttpError(view, request, errorResponse); + } + + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + d("onReceivedSslError: error="+error+" url=" + (error == null ? null : error.getUrl())); + super.onReceivedSslError(view, handler, error); + } + }); + setWebChromeClient(new WebChromeClient() { + private Dialog lastPermissionsDialog; + + @Override + public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { + d("onCreateWindow isDialog=" + isDialog + " isUserGesture=" + isUserGesture + " resultMsg=" + resultMsg); + WebView newWebView = new WebView(view.getContext()); + newWebView.setWebViewClient(new WebViewClient() { + @Override + public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) { + d("newWebView.onRenderProcessGone priority=" + (detail == null ? null : detail.rendererPriorityAtExit()) + " didCrash=" + (detail == null ? null : detail.didCrash())); + if (!AndroidUtilities.isSafeToShow(getContext())) { + return true; + } + new AlertDialog.Builder(getContext(), botWebViewContainer == null ? null : botWebViewContainer.resourcesProvider) + .setTitle(getString(R.string.ChromeCrashTitle)) + .setMessage(AndroidUtilities.replaceSingleTag(getString(R.string.ChromeCrashMessage), () -> Browser.openUrl(getContext(), "https://play.google.com/store/apps/details?id=com.google.android.webview"))) + .setPositiveButton(getString(R.string.OK), null) + .setOnDismissListener(d -> { + if (botWebViewContainer.delegate != null) { + botWebViewContainer.delegate.onCloseRequested(null); + } + }) + .show(); + return true; + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + if (botWebViewContainer != null) { + botWebViewContainer.onOpenUri(Uri.parse(url)); + } + return true; + } + }); + WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj; + transport.setWebView(newWebView); + resultMsg.sendToTarget(); + return true; + } + + @Override + public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, FileChooserParams fileChooserParams) { + Activity activity = AndroidUtilities.findActivity(getContext()); + if (activity == null) { + d("onShowFileChooser: no activity, false"); + return false; + } + if (botWebViewContainer == null) { + d("onShowFileChooser: no container, false"); + return false; + } + + if (botWebViewContainer.mFilePathCallback != null) { + botWebViewContainer.mFilePathCallback.onReceiveValue(null); + } + + botWebViewContainer.mFilePathCallback = filePathCallback; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + activity.startActivityForResult(fileChooserParams.createIntent(), REQUEST_CODE_WEB_VIEW_FILE); + } else { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + activity.startActivityForResult(Intent.createChooser(intent, getString(R.string.BotWebViewFileChooserTitle)), REQUEST_CODE_WEB_VIEW_FILE); + } + + d("onShowFileChooser: true"); + return true; + } + + @Override + public void onProgressChanged(WebView view, int newProgress) { + if (botWebViewContainer != null && botWebViewContainer.webViewProgressListener != null) { + d("onProgressChanged " + newProgress + "%"); + botWebViewContainer.webViewProgressListener.accept(newProgress / 100f); + } else { + d("onProgressChanged " + newProgress + "%: no container"); + } + } + + @Override + public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { + if (botWebViewContainer == null || botWebViewContainer.parentActivity == null) { + d("onGeolocationPermissionsShowPrompt: no container"); + callback.invoke(origin, false, false); + return; + } + d("onGeolocationPermissionsShowPrompt " + origin); + lastPermissionsDialog = AlertsCreator.createWebViewPermissionsRequestDialog(botWebViewContainer.parentActivity, botWebViewContainer.resourcesProvider, new String[] {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, R.raw.permission_request_location, LocaleController.formatString(R.string.BotWebViewRequestGeolocationPermission, UserObject.getUserName(botWebViewContainer.botUser)), LocaleController.formatString(R.string.BotWebViewRequestGeolocationPermissionWithHint, UserObject.getUserName(botWebViewContainer.botUser)), allow -> { + if (lastPermissionsDialog != null) { + lastPermissionsDialog = null; + + if (allow) { + botWebViewContainer.runWithPermissions(new String[] {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, allowSystem -> { + callback.invoke(origin, allowSystem, false); + if (allowSystem) { + botWebViewContainer.hasUserPermissions = true; + } + }); + } else { + callback.invoke(origin, false, false); + } + } + }); + lastPermissionsDialog.show(); + } + + @Override + public void onGeolocationPermissionsHidePrompt() { + if (lastPermissionsDialog != null) { + d("onGeolocationPermissionsHidePrompt: dialog.dismiss"); + lastPermissionsDialog.dismiss(); + lastPermissionsDialog = null; + } else { + d("onGeolocationPermissionsHidePrompt: no dialog"); + } + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public void onPermissionRequest(PermissionRequest request) { + if (lastPermissionsDialog != null){ + lastPermissionsDialog.dismiss(); + lastPermissionsDialog = null; + } + if (botWebViewContainer == null) { + d("onPermissionRequest: no container"); + request.deny(); + return; + } + d("onPermissionRequest " + request); + + String[] resources = request.getResources(); + if (resources.length == 1) { + String resource = resources[0]; + + if (botWebViewContainer.parentActivity == null) { + request.deny(); + return; + } + + switch (resource) { + case PermissionRequest.RESOURCE_AUDIO_CAPTURE: { + lastPermissionsDialog = AlertsCreator.createWebViewPermissionsRequestDialog(botWebViewContainer.parentActivity, botWebViewContainer.resourcesProvider, new String[] {Manifest.permission.RECORD_AUDIO}, R.raw.permission_request_microphone, LocaleController.formatString(R.string.BotWebViewRequestMicrophonePermission, UserObject.getUserName(botWebViewContainer.botUser)), LocaleController.formatString(R.string.BotWebViewRequestMicrophonePermissionWithHint, UserObject.getUserName(botWebViewContainer.botUser)), allow -> { + if (lastPermissionsDialog != null) { + lastPermissionsDialog = null; + + if (allow) { + botWebViewContainer.runWithPermissions(new String[] {Manifest.permission.RECORD_AUDIO}, allowSystem -> { + if (allowSystem) { + request.grant(new String[] {resource}); + botWebViewContainer.hasUserPermissions = true; + } else { + request.deny(); + } + }); + } else { + request.deny(); + } + } + }); + lastPermissionsDialog.show(); + break; + } + case PermissionRequest.RESOURCE_VIDEO_CAPTURE: { + lastPermissionsDialog = AlertsCreator.createWebViewPermissionsRequestDialog(botWebViewContainer.parentActivity, botWebViewContainer.resourcesProvider, new String[] {Manifest.permission.CAMERA}, R.raw.permission_request_camera, LocaleController.formatString(R.string.BotWebViewRequestCameraPermission, UserObject.getUserName(botWebViewContainer.botUser)), LocaleController.formatString(R.string.BotWebViewRequestCameraPermissionWithHint, UserObject.getUserName(botWebViewContainer.botUser)), allow -> { + if (lastPermissionsDialog != null) { + lastPermissionsDialog = null; + + if (allow) { + botWebViewContainer.runWithPermissions(new String[] {Manifest.permission.CAMERA}, allowSystem -> { + if (allowSystem) { + request.grant(new String[] {resource}); + botWebViewContainer.hasUserPermissions = true; + } else { + request.deny(); + } + }); + } else { + request.deny(); + } + } + }); + lastPermissionsDialog.show(); + break; + } + } + } else if ( + resources.length == 2 && + (PermissionRequest.RESOURCE_AUDIO_CAPTURE.equals(resources[0]) || PermissionRequest.RESOURCE_VIDEO_CAPTURE.equals(resources[0])) && + (PermissionRequest.RESOURCE_AUDIO_CAPTURE.equals(resources[1]) || PermissionRequest.RESOURCE_VIDEO_CAPTURE.equals(resources[1])) + ) { + lastPermissionsDialog = AlertsCreator.createWebViewPermissionsRequestDialog(botWebViewContainer.parentActivity, botWebViewContainer.resourcesProvider, new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, R.raw.permission_request_camera, LocaleController.formatString(R.string.BotWebViewRequestCameraMicPermission, UserObject.getUserName(botWebViewContainer.botUser)), LocaleController.formatString(R.string.BotWebViewRequestCameraMicPermissionWithHint, UserObject.getUserName(botWebViewContainer.botUser)), allow -> { + if (lastPermissionsDialog != null) { + lastPermissionsDialog = null; + + if (allow) { + botWebViewContainer.runWithPermissions(new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, allowSystem -> { + if (allowSystem) { + request.grant(new String[] {resources[0], resources[1]}); + botWebViewContainer.hasUserPermissions = true; + } else { + request.deny(); + } + }); + } else { + request.deny(); + } + } + }); + lastPermissionsDialog.show(); + } + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public void onPermissionRequestCanceled(PermissionRequest request) { + if (lastPermissionsDialog != null) { + d("onPermissionRequestCanceled: dialog.dismiss"); + lastPermissionsDialog.dismiss(); + lastPermissionsDialog = null; + } else { + d("onPermissionRequestCanceled: no dialog"); + } + } + }); + } + + private BotWebViewContainer botWebViewContainer; + private WebViewScrollListener webViewScrollListener; + + public void setContainers(BotWebViewContainer botWebViewContainer, WebViewScrollListener webViewScrollListener) { + d("setContainers(" + botWebViewContainer + ", " + webViewScrollListener + ")"); + this.botWebViewContainer = botWebViewContainer; + this.webViewScrollListener = webViewScrollListener; + } + + private int prevScrollX, prevScrollY; + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + + if (webViewScrollListener != null) { + webViewScrollListener.onWebViewScrolled(this, getScrollX() - prevScrollX, getScrollY() - prevScrollY); + } + + prevScrollX = getScrollX(); + prevScrollY = getScrollY(); + } + + @Override + public void setScrollX(int value) { + super.setScrollX(value); + prevScrollX = value; + } + + @Override + public void setScrollY(int value) { + super.setScrollY(value); + prevScrollY = value; + } + + @Override + public boolean onCheckIsTextEditor() { + if (botWebViewContainer == null) { + d("onCheckIsTextEditor: no container"); + return false; + } + final boolean r = botWebViewContainer.isFocusable(); + d("onCheckIsTextEditor: " + r); + return r; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.EXACTLY)); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + botWebViewContainer.lastClickMs = System.currentTimeMillis(); + } + return super.onTouchEvent(event); + } + + @Override + protected void onAttachedToWindow() { + d("attached"); + AndroidUtilities.checkAndroidTheme(getContext(), true); + super.onAttachedToWindow(); + } + + @Override + protected void onDetachedFromWindow() { + d("detached"); + AndroidUtilities.checkAndroidTheme(getContext(), false); + super.onDetachedFromWindow(); + } + + @Override + public void destroy() { + d("destroy"); + super.destroy(); + } + + @Override + public void loadUrl(@NonNull String url) { + d("loadUrl " + url); + super.loadUrl(url); + } + + @Override + public void loadUrl(@NonNull String url, @NonNull Map additionalHttpHeaders) { + d("loadUrl " + url + " " + additionalHttpHeaders); + super.loadUrl(url, additionalHttpHeaders); + } + + @Override + public void reload() { + d("reload"); + super.reload(); + } + + @Override + public void loadData(@NonNull String data, @Nullable String mimeType, @Nullable String encoding) { + d("loadData " + data + " " + mimeType + " " + encoding); + super.loadData(data, mimeType, encoding); + } + + @Override + public void loadDataWithBaseURL(@Nullable String baseUrl, @NonNull String data, @Nullable String mimeType, @Nullable String encoding, @Nullable String historyUrl) { + d("loadDataWithBaseURL " + baseUrl + " " + data + " " + mimeType + " " + encoding + " " + historyUrl); + super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl); + } + + @Override + public void stopLoading() { + d("stopLoading"); + super.stopLoading(); + } + + @Override + public void stopNestedScroll() { + d("stopNestedScroll"); + super.stopNestedScroll(); + } + + @Override + public void postUrl(@NonNull String url, @NonNull byte[] postData) { + d("postUrl " + url + " " + postData); + super.postUrl(url, postData); + } + + @Override + public void onPause() { + d("onPause"); + super.onPause(); + } + + @Override + public void onResume() { + d("onResume"); + super.onResume(); + } + + @Override + public void pauseTimers() { + d("pauseTimers"); + super.pauseTimers(); + } + + @Override + public void resumeTimers() { + d("resumeTimers"); + super.resumeTimers(); + } + } + + private final int tag = tags++; + public void d(String s) { + FileLog.d("[webviewcontainer] #" + tag + " " + s); + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewMenuContainer.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewMenuContainer.java index b3d29a6960..ab2a7766d7 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewMenuContainer.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewMenuContainer.java @@ -1,5 +1,6 @@ package org.telegram.ui.bots; +import static org.telegram.messenger.AndroidUtilities.dp; import static org.telegram.ui.ActionBar.Theme.key_windowBackgroundWhiteBlackText; import android.animation.Animator; @@ -7,14 +8,19 @@ import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.content.Context; +import android.graphics.Bitmap; 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.RectF; +import android.graphics.RenderNode; import android.os.Build; import android.os.Bundle; import android.text.Editable; +import android.util.Log; import android.util.SparseIntArray; import android.view.Gravity; import android.view.MotionEvent; @@ -48,15 +54,19 @@ import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.ActionBarMenuSubItem; import org.telegram.ui.ActionBar.AlertDialog; +import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheetTabs; import org.telegram.ui.ActionBar.INavigationLayout; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.ChatActivityEnterView; import org.telegram.ui.Components.ChatAvatarContainer; import org.telegram.ui.Components.CubicBezierInterpolator; +import org.telegram.ui.Components.HideViewAfterAnimation; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.SimpleFloatPropertyCompat; import org.telegram.ui.DialogsActivity; +import org.telegram.ui.LaunchActivity; import org.telegram.ui.PaymentFormActivity; import org.telegram.ui.Stars.StarsController; @@ -71,6 +81,10 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification obj.actionBarTransitionProgress = value; obj.invalidate(); obj.invalidateActionBar(); + + if (obj.botCollapseItem != null) { + obj.botCollapseItem.setAlpha(value); + } }).setMultiplier(100f); private float actionBarTransitionProgress; @@ -99,12 +113,15 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification private String botUrl; private boolean isLoaded; + private TLObject loadedResponse; + private long loadedResponseTime; private boolean dismissed; private Boolean wasLightStatusBar; private long queryId; private ActionBarMenuItem botMenuItem; + private ActionBarMenuItem botCollapseItem; private ActionBar.ActionBarMenuOnItemClick actionBarOnItemClick; private ActionBarMenuSubItem settingsItem; private ActionBarMenuSubItem addToHomeScreenItem; @@ -118,6 +135,7 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification private float overrideActionBarBackgroundProgress; private int overrideActionBarBackground; private boolean overrideBackgroundColor; + private int actionBarColorKey = -1; private boolean needCloseConfirmation; ActionBarColorsAnimating actionBarColors; @@ -143,8 +161,83 @@ public class BotWebViewMenuContainer extends FrameLayout implements Notification }; private boolean actionBarIsLight; private int lineColor; + private boolean preserving; + + public BottomSheetTabs.WebTabData saveState() { + preserving = true; + BottomSheetTabs.WebTabData tab = new BottomSheetTabs.WebTabData(); + + tab.actionBarColor = overrideBackgroundColor ? overrideActionBarBackground : actionBarColors.getColor(Theme.key_windowBackgroundWhite); + tab.actionBarColorKey = actionBarColorKey; + tab.overrideActionBarColor = overrideBackgroundColor; + tab.backgroundColor = backgroundPaint.getColor(); + + tab.props = new BotWebViewAttachedSheet.WebViewRequestProps(); + tab.props.currentAccount = currentAccount; + tab.props.botId = botId; + tab.props.peerId = botId; + tab.props.buttonUrl = botUrl; + tab.props.type = BotWebViewAttachedSheet.TYPE_BOT_MENU_BUTTON; + tab.props.response = loadedResponse; + tab.props.responseTime = loadedResponseTime; + tab.ready = webViewContainer != null && webViewContainer.isPageLoaded(); + tab.lastUrl = webViewContainer != null ? webViewContainer.getUrlLoaded() : null; + tab.themeIsDark = Theme.isCurrentThemeDark(); + tab.settings = settingsItem != null && settingsItem.getVisibility() == View.VISIBLE; + tab.main = mainButtonSettings; + tab.confirmDismiss = needCloseConfirmation; + tab.expanded = swipeContainer != null && ((1f - Math.min(swipeContainer.getTopActionBarOffsetY(), swipeContainer.getTranslationY() - swipeContainer.getTopActionBarOffsetY()) / swipeContainer.getTopActionBarOffsetY()) > .5f); + tab.needsContext = true; + + BotWebViewContainer.MyWebView webView = webViewContainer == null ? null : webViewContainer.getWebView(); + if (webView != null) { + webViewContainer.preserveWebView(); + tab.webView = webView; + tab.webViewProxy = webViewContainer == null ? null : webViewContainer.getProxy(); + tab.webViewWidth = webView.getWidth(); + tab.webViewScroll = webView.getScrollY(); + tab.webViewHeight = webView.getHeight(); + webView.onPause(); +// webView.pauseTimers(); + webView.setContainers(null, null); + } + return tab; + } + + // not really used, tab is already open from BotWebViewAttachedSheet place + public boolean restoreState(BaseFragment fragment, BottomSheetTabs.WebTabData tab) { + if (tab == null || tab.props == null) return false; +// setBackgroundColor(tab.backgroundColor, false); +// setActionBarColor(tab.actionBarColor, tab.overrideActionBarColor, false); + if (tab.webView != null) { +// tab.webView.resumeTimers(); + tab.webView.onResume(); + webViewContainer.replaceWebView(tab.webView, tab.webViewProxy); + } else { + tab.props.response = null; + tab.props.responseTime = 0; + } + currentAccount = tab.props.currentAccount; + botId = tab.props.botId; + botUrl = tab.props.buttonUrl; + loadedResponse = tab.props.response; + loadedResponseTime = tab.props.responseTime; + loadWebView(); + return true; + } private void checkBotMenuItem() { + if (botCollapseItem == null) { + ActionBarMenu menu = parentEnterView.getParentFragment().getActionBar().createMenu(); + botCollapseItem = menu.addItem(R.id.menu_collapse_bot, R.drawable.arrow_more); + menu.removeView(botCollapseItem); + menu.addView(botCollapseItem, 0); + botCollapseItem.setOnClickListener(v -> { + dismiss(true); + }); + botCollapseItem.setAlpha(actionBarTransitionProgress); + botCollapseItem.setVisibility(GONE); + } if (botMenuItem == null) { ActionBarMenu menu = parentEnterView.getParentFragment().getActionBar().createMenu(); botMenuItem = menu.addItem(1000, R.drawable.ic_ab_other); @@ -193,10 +286,11 @@ public void onWebAppSetupClosingBehavior(boolean needConfirmation) { } @Override - public void onWebAppSetActionBarColor(int color, boolean isOverrideColor) { + public void onWebAppSetActionBarColor(int colorKey, int color, boolean isOverrideColor) { int from = overrideActionBarBackground; int to = color; + actionBarColorKey = colorKey; actionBarColors = new BotWebViewMenuContainer.ActionBarColorsAnimating(); actionBarColors.setFrom(overrideBackgroundColor ? from : 0, null); overrideBackgroundColor = isOverrideColor; @@ -290,7 +384,7 @@ public void onWebAppOpenInvoice(TLRPC.InputInvoice inputInvoice, String slug, TL if (response instanceof TLRPC.TL_payments_paymentFormStars) { final AlertDialog progressDialog = new AlertDialog(getContext(), AlertDialog.ALERT_TYPE_SPINNER); progressDialog.showDelayed(150); - StarsController.getInstance(currentAccount).openPaymentForm(inputInvoice, (TLRPC.TL_payments_paymentFormStars) response, () -> { + StarsController.getInstance(currentAccount).openPaymentForm(null, inputInvoice, (TLRPC.TL_payments_paymentFormStars) response, () -> { progressDialog.dismiss(); }, status -> { webViewContainer.onInvoiceStatusUpdate(slug, status); @@ -312,12 +406,7 @@ public void onWebAppOpenInvoice(TLRPC.InputInvoice inputInvoice, String slug, TL @Override public void onSetupMainButton(boolean isVisible, boolean isActive, String text, int color, int textColor, boolean isProgressVisible) { - ChatActivityBotWebViewButton botWebViewButton = parentEnterView.getBotWebViewButton(); - botWebViewButton.setupButtonParams(isActive, text, color, textColor, isProgressVisible); - botWebViewButton.setOnClickListener(v -> webViewContainer.onMainButtonPressed()); - if (isVisible != botWebViewButtonWasVisible) { - animateBotButton(isVisible); - } + setMainButton(BotWebViewAttachedSheet.MainButtonSettings.of(isVisible, isActive, text, color, textColor, isProgressVisible)); } @Override @@ -393,12 +482,18 @@ public void requestLayout() { if (springAnimation != null) { float progress = (1f - Math.min(swipeContainer.getTopActionBarOffsetY(), swipeContainer.getTranslationY() - swipeContainer.getTopActionBarOffsetY()) / swipeContainer.getTopActionBarOffsetY()); - if (BotWebViewMenuContainer.this.getVisibility() != VISIBLE) { + if (BotWebViewMenuContainer.this.getVisibility() != VISIBLE || preserving) { progress = 0; } float newPos = (progress > 0.5f ? 1 : 0) * 100f; if (springAnimation.getSpring().getFinalPosition() != newPos) { springAnimation.getSpring().setFinalPosition(newPos); + if (newPos == 100) { + checkBotMenuItem(); + if (botCollapseItem != null) { + botCollapseItem.setVisibility(VISIBLE); + } + } springAnimation.start(); if (!webViewContainer.isBackButtonVisible()) { @@ -415,9 +510,10 @@ public void requestLayout() { swipeContainer.setScrollEndListener(()-> webViewContainer.invalidateViewPortHeight(true)); swipeContainer.addView(webViewContainer); swipeContainer.setDelegate(() -> { - if (!onCheckDismissByUser()) { - swipeContainer.stickTo(0); - } +// if (!onCheckDismissByUser()) { +// swipeContainer.stickTo(0); +// } + dismiss(true, null); }); swipeContainer.setTopActionBarOffsetY(ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight - AndroidUtilities.dp(24)); swipeContainer.setSwipeOffsetAnimationDisallowed(true); @@ -478,7 +574,8 @@ public boolean onBackPressed() { } if (getVisibility() == VISIBLE) { - onCheckDismissByUser(); + dismiss(true); +// onCheckDismissByUser(); return true; } @@ -563,6 +660,8 @@ public void onAttachedToWindow() { chatActivity.showHeaderItem(false); checkBotMenuItem(); botMenuItem.setVisibility(VISIBLE); + botCollapseItem.setVisibility(VISIBLE); +// botCollapseItem.animate().alpha(1f).setDuration(320).setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT).start(); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { @@ -598,6 +697,9 @@ public void onItemClick(int id) { if (botMenuItem != null) { botMenuItem.setVisibility(GONE); } + if (botCollapseItem != null) { + botCollapseItem.setVisibility(GONE); + } actionBar.setActionBarMenuOnItemClick(actionBarOnItemClick); } }); @@ -683,7 +785,7 @@ public void onPanTransitionEnd() { } private void updateLightStatusBar() { - int color = Theme.getColor(Theme.key_windowBackgroundWhite, null, true); + int color = backgroundPaint.getColor(); boolean lightStatusBar = ColorUtils.calculateLuminance(color) >= 0.9 && actionBarTransitionProgress >= 0.85f; if (wasLightStatusBar != null && wasLightStatusBar == lightStatusBar) { @@ -704,6 +806,7 @@ private void updateLightStatusBar() { @Override protected void onDraw(Canvas canvas) { + if (drawingFromOverlay) return; super.onDraw(canvas); if (!overrideBackgroundColor) { @@ -727,7 +830,8 @@ protected void onDraw(Canvas canvas) { @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN && event.getY() <= AndroidUtilities.lerp(swipeContainer.getTranslationY() + AndroidUtilities.dp(24), 0, actionBarTransitionProgress)) { - onCheckDismissByUser(); +// onCheckDismissByUser(); + dismiss(true); return true; } return super.onTouchEvent(event); @@ -735,6 +839,8 @@ public boolean onTouchEvent(MotionEvent event) { @Override public void draw(Canvas canvas) { + if (drawingFromOverlay) return; + super.draw(canvas); lineColor = actionBarColors.getColor(Theme.key_sheet_scrollUp); @@ -782,6 +888,8 @@ public void show(int currentAccount, long botId, String botUrl) { } if (!isLoaded) { + overrideBackgroundColor = false; + backgroundPaint.setColor(getColor(Theme.key_windowBackgroundWhite)); loadWebView(); } @@ -810,6 +918,11 @@ public void onLayoutChange(View v, int left, int top, int right, int bottom, int } private void loadWebView() { +// if (loadedResponse != null) { +// processResponse(loadedResponse); +// return; +// } + progressView.setLoadProgress(0); progressView.setAlpha(1f); progressView.setVisibility(VISIBLE); @@ -838,17 +951,23 @@ private void loadWebView() { req.from_bot_menu = true; ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - if (response instanceof TLRPC.TL_webViewResultUrl) { - isLoaded = true; + processResponse(response); + })); + } - TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) response; - queryId = resultUrl.query_id; - webViewContainer.loadUrl(currentAccount, resultUrl.url); - swipeContainer.setWebView(webViewContainer.getWebView()); + private void processResponse(TLObject response) { + if (response instanceof TLRPC.TL_webViewResultUrl) { + isLoaded = true; + loadedResponse = response; + loadedResponseTime = System.currentTimeMillis(); - AndroidUtilities.runOnUIThread(pollRunnable, POLL_PERIOD); - } - })); + TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) response; + queryId = resultUrl.query_id; + webViewContainer.loadUrl(currentAccount, resultUrl.url); + swipeContainer.setWebView(webViewContainer.getWebView()); + + AndroidUtilities.runOnUIThread(pollRunnable, POLL_PERIOD); + } } private int getColor(int key) { @@ -867,32 +986,67 @@ public void setOnDismissGlobalListener(Runnable callback) { * Dismisses menu */ public void dismiss() { - dismiss(null); + dismiss(false, null); } /** * Dismisses menu */ public void dismiss(Runnable callback) { + dismiss(false, callback); + } + + public void dismiss(boolean intoTabs) { + dismiss(intoTabs, null); + } + + public void dismiss(boolean intoTabs, Runnable callback) { if (dismissed) { return; } dismissed = true; - swipeContainer.stickTo(swipeContainer.getHeight() + parentEnterView.getSizeNotifierLayout().measureKeyboardHeight(), ()->{ - onDismiss(); - if (callback != null) { - callback.run(); + + if (intoTabs && (LaunchActivity.instance == null || LaunchActivity.instance.getBottomSheetTabsOverlay() == null)) { + intoTabs = false; + } + if (intoTabs) { + if (springAnimation != null) { + springAnimation.getSpring().setFinalPosition(0); + springAnimation.start(); } - if (globalOnDismissListener != null) { - globalOnDismissListener.run(); + ChatActivity chatActivity = parentEnterView.getParentFragment(); + if (chatActivity != null) { + ActionBar actionBar = chatActivity.getActionBar(); + if (actionBar != null) { + AndroidUtilities.updateImageViewImageAnimated(actionBar.getBackButton(), actionBar.getBackButtonDrawable()); + } } - }); + LaunchActivity.instance.getBottomSheetTabsOverlay().dismissSheet(this); + } else { + swipeContainer.stickTo(swipeContainer.getHeight() + parentEnterView.getSizeNotifierLayout().measureKeyboardHeight(), () -> { + onDismiss(); + if (callback != null) { + callback.run(); + } + if (globalOnDismissListener != null) { + globalOnDismissListener.run(); + } + }); + } } /** * Called when menu is fully dismissed */ public void onDismiss() { + ChatActivity chatActivity = parentEnterView == null ? null : parentEnterView.getParentFragment(); + if (botMenuItem != null) { + botMenuItem.setVisibility(GONE); + } + if (botCollapseItem != null) { + botCollapseItem.setVisibility(GONE); + } + setVisibility(GONE); needCloseConfirmation = false; @@ -933,6 +1087,7 @@ public void onAnimationEnd(Animator animation) { botWebViewButtonWasVisible = false; animateBotButton(false); } + mainButtonSettings = null; AndroidUtilities.runOnUIThread(()->{ if (savedEditText != null && parentEnterView.getEditField() != null) { @@ -940,20 +1095,20 @@ public void onAnimationEnd(Animator animation) { savedEditText = null; } if (savedReplyMessageObject != null) { - ChatActivity chatActivity = parentEnterView.getParentFragment(); if (chatActivity != null) { chatActivity.showFieldPanelForReply(savedReplyMessageObject); } savedReplyMessageObject = null; } if (savedEditMessageObject != null) { - ChatActivity chatActivity = parentEnterView.getParentFragment(); if (chatActivity != null) { chatActivity.showFieldPanelForEdit(true, savedEditMessageObject); } savedEditMessageObject = null; } }, delayRestoreText ? 200 : 0); + + preserving = false; } public boolean hasSavedText() { @@ -1043,4 +1198,60 @@ public void updateActionBar(ActionBar actionBar, float progress) { actionBar.setPopupItemsSelectorColor(getColor(Theme.key_dialogButtonSelector), false); } } + + + private boolean drawingFromOverlay; + public void setDrawingFromOverlay(boolean drawingFromOverlay) { + if (this.drawingFromOverlay != drawingFromOverlay) { + this.drawingFromOverlay = drawingFromOverlay; + invalidate(); + } + } + + @Override + protected void dispatchDraw(Canvas canvas) { + if (drawingFromOverlay) return; + super.dispatchDraw(canvas); + } + + private final RectF rect = new RectF(); + private final Path clipPath = new Path(); + + public float drawInto(Canvas canvas, RectF finalRect, float progress, RectF clipRect) { + rect.set(swipeContainer.getLeft(), swipeContainer.getTranslationY() + dp(24), swipeContainer.getRight(), getHeight()); + AndroidUtilities.lerpCentered(rect, finalRect, progress, clipRect); + + canvas.save(); + + clipPath.rewind(); + float radius = dp(16) * (AndroidUtilities.isTablet() ? 1f : 1f - actionBarTransitionProgress); + final float r = AndroidUtilities.lerp(radius, dp(10), progress); + clipPath.addRoundRect(clipRect, r, r, Path.Direction.CW); + canvas.clipPath(clipPath); + canvas.drawPaint(backgroundPaint); + + if (swipeContainer != null) { + canvas.saveLayerAlpha(clipRect, (int) (0xFF * (1f - progress)), Canvas.ALL_SAVE_FLAG); + canvas.translate(swipeContainer.getX(), Math.max(swipeContainer.getY(), clipRect.top) + progress * dp(51)); + swipeContainer.draw(canvas); + canvas.restore(); + } + + canvas.restore(); + + return r; + } + + private BotWebViewAttachedSheet.MainButtonSettings mainButtonSettings; + + public void setMainButton(BotWebViewAttachedSheet.MainButtonSettings s) { + mainButtonSettings = s; + + ChatActivityBotWebViewButton botWebViewButton = parentEnterView.getBotWebViewButton(); + botWebViewButton.setupButtonParams(s.isActive, s.text, s.color, s.textColor, s.isProgressVisible); + botWebViewButton.setOnClickListener(v -> webViewContainer.onMainButtonPressed()); + if (s.isVisible != botWebViewButtonWasVisible) { + animateBotButton(s.isVisible); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewSheet.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewSheet.java index fb1291bfde..3214b23f8f 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewSheet.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/BotWebViewSheet.java @@ -1,5 +1,6 @@ package org.telegram.ui.bots; +import static org.telegram.messenger.AndroidUtilities.dp; import static org.telegram.ui.Components.Bulletin.DURATION_PROLONG; import android.animation.Animator; @@ -13,6 +14,8 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; @@ -39,7 +42,6 @@ import org.json.JSONObject; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.AnimationNotificationsLocker; -import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ContactsController; import org.telegram.messenger.DialogObject; import org.telegram.messenger.Emoji; @@ -64,8 +66,8 @@ import org.telegram.ui.ActionBar.ActionBarMenuSubItem; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; +import org.telegram.ui.ActionBar.BottomSheetTabs; import org.telegram.ui.ActionBar.INavigationLayout; -import org.telegram.ui.ActionBar.OKLCH; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ChatActivity; import org.telegram.ui.Components.Bulletin; @@ -117,7 +119,7 @@ public void showJustAddedBulletin() { str = LocaleController.formatString("BotAttachMenuShortcatAddedAttach", R.string.BotAttachMenuShortcatAddedAttach, user.first_name); } AndroidUtilities.runOnUIThread(() -> { - BulletinFactory.of(frameLayout, resourcesProvider) + BulletinFactory.of(windowView, resourcesProvider) .createSimpleBulletin(R.raw.contact_check, AndroidUtilities.replaceTags(str)) .setDuration(DURATION_PROLONG) .show(true); @@ -137,7 +139,7 @@ public void showJustAddedBulletin() { private final static SimpleFloatPropertyCompat ACTION_BAR_TRANSITION_PROGRESS_VALUE = new SimpleFloatPropertyCompat("actionBarTransitionProgress", obj -> obj.actionBarTransitionProgress, (obj, value) -> { obj.actionBarTransitionProgress = value; - obj.frameLayout.invalidate(); + obj.windowView.invalidate(); obj.actionBar.setAlpha(value); @@ -148,7 +150,7 @@ public void showJustAddedBulletin() { private Boolean wasLightStatusBar; - private SizeNotifierFrameLayout frameLayout; + private WindowView windowView; private long lastSwipeTime; @@ -221,6 +223,98 @@ public void showJustAddedBulletin() { } }; + private int actionBarColorKey = -1; + private BotWebViewAttachedSheet.WebViewRequestProps requestProps; + private boolean backButtonShown; + private boolean forceExpnaded; + + private boolean defaultFullsize = false; + private Boolean fullsize = null; + private boolean needsContext; + + public BottomSheetTabs.WebTabData saveState() { + BottomSheetTabs.WebTabData tab = new BottomSheetTabs.WebTabData(); + tab.actionBarColor = actionBarColor; + tab.actionBarColorKey = actionBarColorKey; + tab.overrideActionBarColor = overrideBackgroundColor; + tab.backgroundColor = backgroundPaint.getColor(); + tab.props = requestProps; + tab.ready = webViewContainer != null && webViewContainer.isPageLoaded(); + tab.themeIsDark = Theme.isCurrentThemeDark(); + tab.lastUrl = webViewContainer != null ? webViewContainer.getUrlLoaded() : null; + tab.expanded = swipeContainer != null && swipeContainer.getSwipeOffsetY() < 0 || forceExpnaded || getFullSize(); + tab.fullsize = getFullSize(); + tab.expandedOffset = swipeContainer != null ? swipeContainer.getOffsetY() : Float.MAX_VALUE; + tab.needsContext = needsContext; + tab.backButton = backButtonShown; + tab.main = mainButtonSettings; + tab.confirmDismiss = needCloseConfirmation; + BotWebViewContainer.MyWebView webView = webViewContainer == null ? null : webViewContainer.getWebView(); + if (webView != null) { + webViewContainer.preserveWebView(); + tab.webView = webView; + tab.webViewProxy = webViewContainer == null ? null : webViewContainer.getProxy(); + tab.webViewWidth = webView.getWidth(); + tab.webViewHeight = webView.getHeight(); + webView.onPause(); +// webView.pauseTimers(); + } + return tab; + } + + public boolean showExpanded; + public float showOffsetY; + + public boolean restoreState(BaseFragment fragment, BottomSheetTabs.WebTabData tab) { + if (tab == null || tab.props == null) return false; + if (tab.overrideActionBarColor) { + setBackgroundColor(tab.backgroundColor, false); + } + setActionBarColor(!tab.overrideActionBarColor ? Theme.getColor(tab.actionBarColorKey < 0 ? Theme.key_windowBackgroundWhite : tab.actionBarColorKey, resourcesProvider) : tab.actionBarColor, tab.overrideActionBarColor, false); + showExpanded = tab.expanded; + showOffsetY = tab.expandedOffset; + webViewContainer.setIsBackButtonVisible(backButtonShown = tab.backButton); + AndroidUtilities.updateImageViewImageAnimated(actionBar.getBackButton(), backButtonShown ? R.drawable.ic_ab_back : R.drawable.ic_close_white); + needCloseConfirmation = tab.confirmDismiss; + fullsize = tab.fullsize; + needsContext = tab.needsContext; + if (tab.main != null) { + setMainButton(tab.main); + } + if (tab.webView != null) { +// tab.webView.resumeTimers(); + tab.webView.onResume(); + webViewContainer.replaceWebView(tab.webView, tab.webViewProxy); + webViewContainer.setState(tab.ready || tab.webView.isPageLoaded(), tab.lastUrl); + if (Theme.isCurrentThemeDark() != tab.themeIsDark) { +// webViewContainer.notifyThemeChanged(); + if (webViewContainer.getWebView() != null) { + webViewContainer.getWebView().animate().cancel(); + webViewContainer.getWebView().animate().alpha(0).start(); + } + + progressView.setLoadProgress(0); + progressView.setAlpha(1f); + progressView.setVisibility(View.VISIBLE); + + webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId)); + webViewContainer.loadFlickerAndSettingsItem(currentAccount, botId, settingsItem); + webViewContainer.setState(false, null); + if (webViewContainer.getWebView() != null) { + webViewContainer.getWebView().loadUrl("about:blank"); + } + + tab.props.response = null; + tab.props.responseTime = 0; + } + } else { + tab.props.response = null; + tab.props.responseTime = 0; + } + requestWebView(fragment, tab.props); + return true; + } + public BotWebViewSheet(@NonNull Context context, Theme.ResourcesProvider resourcesProvider) { super(context, R.style.TransparentDialog); this.resourcesProvider = resourcesProvider; @@ -302,71 +396,19 @@ public void onSendWebViewData(String data) { } @Override - public void onWebAppSetActionBarColor(int color, boolean isOverrideColor) { - int from = actionBarColor; - int navBarFrom = navBarColor; - int to = color; - int navBarTo = navigationBarColor(color); - - BotWebViewMenuContainer.ActionBarColorsAnimating actionBarColorsAnimating = new BotWebViewMenuContainer.ActionBarColorsAnimating(); - actionBarColorsAnimating.setFrom(overrideBackgroundColor ? actionBarColor : 0, resourcesProvider); - overrideBackgroundColor = isOverrideColor; - actionBarIsLight = ColorUtils.calculateLuminance(color) < 0.721f; - actionBarColorsAnimating.setTo(overrideBackgroundColor ? to : 0, resourcesProvider); - - ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(200); - animator.setInterpolator(CubicBezierInterpolator.DEFAULT); - animator.addUpdateListener(animation -> { - float progress = (float) animation.getAnimatedValue(); - actionBarColor = ColorUtils.blendARGB(from, to, progress); - navBarColor = ColorUtils.blendARGB(navBarFrom, navBarTo, progress); - AndroidUtilities.setNavigationBarColor(getWindow(), navBarColor, false); - AndroidUtilities.setLightNavigationBar(getWindow(), AndroidUtilities.computePerceivedBrightness(navBarColor) > .721f); - frameLayout.invalidate(); - actionBar.setBackgroundColor(actionBarColor); - - actionBarColorsAnimating.updateActionBar(actionBar, progress); - lineColor = actionBarColorsAnimating.getColor(Theme.key_sheet_scrollUp); - - frameLayout.invalidate(); - }); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - float progress = 1f; - actionBarColor = ColorUtils.blendARGB(from, to, progress); - navBarColor = ColorUtils.blendARGB(navBarFrom, navBarTo, progress); - AndroidUtilities.setNavigationBarColor(getWindow(), navBarColor, false); - AndroidUtilities.setLightNavigationBar(getWindow(), AndroidUtilities.computePerceivedBrightness(navBarColor) > .721f); - frameLayout.invalidate(); - actionBar.setBackgroundColor(actionBarColor); - - actionBarColorsAnimating.updateActionBar(actionBar, progress); - lineColor = actionBarColorsAnimating.getColor(Theme.key_sheet_scrollUp); - - frameLayout.invalidate(); - } - }); - animator.start(); - updateLightStatusBar(); + public void onWebAppSetActionBarColor(int colorKey, int color, boolean isOverrideColor) { + actionBarColorKey = colorKey; + setActionBarColor(color, isOverrideColor, true); } @Override public void onWebAppSetBackgroundColor(int color) { - int from = backgroundPaint.getColor(); - ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(200); - animator.setInterpolator(CubicBezierInterpolator.DEFAULT); - animator.addUpdateListener(animation -> { - backgroundPaint.setColor(ColorUtils.blendARGB(from, color, (Float) animation.getAnimatedValue())); - updateActionBarColors(); - frameLayout.invalidate(); - }); - animator.start(); + setBackgroundColor(color, true); } @Override public void onSetBackButtonVisible(boolean visible) { - AndroidUtilities.updateImageViewImageAnimated(actionBar.getBackButton(), visible ? R.drawable.ic_ab_back : R.drawable.ic_close_white); + AndroidUtilities.updateImageViewImageAnimated(actionBar.getBackButton(), (backButtonShown = visible) ? R.drawable.ic_ab_back : R.drawable.ic_close_white); } @Override @@ -381,10 +423,10 @@ public void onWebAppOpenInvoice(TLRPC.InputInvoice inputInvoice, String slug, TL BaseFragment parentFragment = ((LaunchActivity) parentActivity).getActionBarLayout().getLastFragment(); PaymentFormActivity paymentFormActivity = null; if (response instanceof TLRPC.TL_payments_paymentFormStars) { - AndroidUtilities.hideKeyboard(frameLayout); + AndroidUtilities.hideKeyboard(windowView); final AlertDialog progressDialog = new AlertDialog(getContext(), AlertDialog.ALERT_TYPE_SPINNER); progressDialog.showDelayed(150); - StarsController.getInstance(currentAccount).openPaymentForm(inputInvoice, (TLRPC.TL_payments_paymentFormStars) response, () -> { + StarsController.getInstance(currentAccount).openPaymentForm(null, inputInvoice, (TLRPC.TL_payments_paymentFormStars) response, () -> { progressDialog.dismiss(); }, status -> { webViewContainer.onInvoiceStatusUpdate(slug, status); @@ -401,7 +443,7 @@ public void onWebAppOpenInvoice(TLRPC.InputInvoice inputInvoice, String slug, TL if (paymentFormActivity != null) { swipeContainer.stickTo(-swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY()); - AndroidUtilities.hideKeyboard(frameLayout); + AndroidUtilities.hideKeyboard(windowView); OverlayActionBarLayoutDialog overlayActionBarLayoutDialog = new OverlayActionBarLayoutDialog(context, resourcesProvider); overlayActionBarLayoutDialog.show(); paymentFormActivity.setPaymentFormCallback(status -> { @@ -447,7 +489,7 @@ public void onWebAppSwitchInlineQuery(TLRPC.User botUser, String query, List { long did = dids.get(0).dialogId; @@ -488,48 +530,7 @@ public void onWebAppSwitchInlineQuery(TLRPC.User botUser, String query, List swipeContainer.getRight() || event.getX() < swipeContainer.getLeft())) { - onCheckDismissByUser(); - return true; - } - return super.onTouchEvent(event); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - Bulletin.addDelegate(this, new Bulletin.Delegate() { - @Override - public int getTopOffset(int tag) { - return AndroidUtilities.statusBarHeight; - } - }); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - Bulletin.removeDelegate(this); - } - }; - frameLayout.setDelegate((keyboardHeight, isWidthGreater) -> { + windowView = new WindowView(context); + windowView.setDelegate((keyboardHeight, isWidthGreater) -> { if (keyboardHeight > AndroidUtilities.dp(20)) { swipeContainer.stickTo(-swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY()); } }); - frameLayout.addView(swipeContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 24, 0, 0)); + windowView.addView(swipeContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 24, 0, 0)); mainButton = new TextView(context) { @Override @@ -669,7 +581,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mainButton.setPadding(padding, 0, padding, 0); mainButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); mainButton.setOnClickListener(v -> webViewContainer.onMainButtonPressed()); - frameLayout.addView(mainButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL)); + windowView.addView(mainButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL)); mainButtonAutoAnimator = VerticalPositionAutoAnimator.attach(mainButton); radialProgressView = new RadialProgressView(context) { @@ -690,7 +602,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { radialProgressView.setScaleX(0.1f); radialProgressView.setScaleY(0.1f); radialProgressView.setVisibility(View.GONE); - frameLayout.addView(radialProgressView, LayoutHelper.createFrame(28, 28, Gravity.BOTTOM | Gravity.RIGHT, 0, 0, 10, 10)); + windowView.addView(radialProgressView, LayoutHelper.createFrame(28, 28, Gravity.BOTTOM | Gravity.RIGHT, 0, 0, 10, 10)); radialProgressAutoAnimator = VerticalPositionAutoAnimator.attach(radialProgressView); actionBarShadow = ContextCompat.getDrawable(getContext(), R.drawable.header_shadow).mutate(); @@ -716,9 +628,9 @@ public void onItemClick(int id) { } }); actionBar.setAlpha(0f); - frameLayout.addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL)); + windowView.addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL)); - frameLayout.addView(progressView = new ChatAttachAlertBotWebViewLayout.WebProgressView(context, resourcesProvider) { + windowView.addView(progressView = new ChatAttachAlertBotWebViewLayout.WebProgressView(context, resourcesProvider) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (AndroidUtilities.isTablet() && !AndroidUtilities.isInMultiwindow && !AndroidUtilities.isSmallTablet()) { @@ -750,7 +662,7 @@ public void onAnimationEnd(Animator animation) { } else { dimPaint.setAlpha(0x40); } - frameLayout.invalidate(); + windowView.invalidate(); webViewContainer.invalidateViewPortHeight(); if (springAnimation != null) { @@ -768,17 +680,21 @@ public void onAnimationEnd(Animator animation) { }); swipeContainer.setScrollEndListener(()-> webViewContainer.invalidateViewPortHeight(true)); swipeContainer.setDelegate(() -> { - if (!onCheckDismissByUser()) { - swipeContainer.stickTo(0); - } +// if (can_minimize) { + dismiss(true, null); +// } else { +// if (!onCheckDismissByUser()) { +// swipeContainer.stickTo(0); +// } +// } }); swipeContainer.setTopActionBarOffsetY(ActionBar.getCurrentActionBarHeight() + AndroidUtilities.statusBarHeight - AndroidUtilities.dp(24)); - swipeContainer.setIsKeyboardVisible(obj -> frameLayout.getKeyboardHeight() >= AndroidUtilities.dp(20)); + swipeContainer.setIsKeyboardVisible(obj -> windowView.getKeyboardHeight() >= AndroidUtilities.dp(20)); passcodeView = new PasscodeView(context); - frameLayout.addView(passcodeView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); + windowView.addView(passcodeView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); - setContentView(frameLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + setContentView(windowView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); } @Override @@ -837,13 +753,13 @@ private void updateLightStatusBar() { wasLightStatusBar = lightStatusBar; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - int flags = frameLayout.getSystemUiVisibility(); + int flags = windowView.getSystemUiVisibility(); if (lightStatusBar) { flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } else { flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR; } - frameLayout.setSystemUiVisibility(flags); + windowView.setSystemUiVisibility(flags); } } @@ -875,10 +791,10 @@ protected void onCreate(Bundle savedInstanceState) { window.setStatusBarColor(Color.TRANSPARENT); } - frameLayout.setFitsSystemWindows(true); - frameLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + windowView.setFitsSystemWindows(true); + windowView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - frameLayout.setOnApplyWindowInsetsListener((v, insets) -> { + windowView.setOnApplyWindowInsetsListener((v, insets) -> { v.setPadding(0, 0, 0, insets.getSystemWindowInsetBottom()); if (Build.VERSION.SDK_INT >= 30) { return WindowInsets.CONSUMED; @@ -935,6 +851,7 @@ public static JSONObject makeThemeParams(Theme.ResourcesProvider resourcesProvid jsonObject.put("section_header_text_color", Theme.blendOver(backgroundColor, Theme.getColor(Theme.key_windowBackgroundWhiteBlueHeader, resourcesProvider))); jsonObject.put("subtitle_text_color", Theme.blendOver(backgroundColor, Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider))); jsonObject.put("destructive_text_color", Theme.blendOver(backgroundColor, Theme.getColor(Theme.key_text_RedRegular, resourcesProvider))); + jsonObject.put("section_separator_color", Theme.blendOver(backgroundColor, Theme.getColor(Theme.key_divider, resourcesProvider))); return jsonObject; } catch (Exception e) { FileLog.e(e); @@ -942,31 +859,38 @@ public static JSONObject makeThemeParams(Theme.ResourcesProvider resourcesProvid return null; } - public void requestWebView(int currentAccount, long peerId, long botId, String buttonText, String buttonUrl, @WebViewType int type, int replyToMsgId, boolean silent, int flags) { - requestWebView(currentAccount, peerId, botId, buttonText, buttonUrl, type, replyToMsgId, silent, null, null, false, null, null, flags); + public void setDefaultFullsize(boolean fullsize) { + if (this.defaultFullsize != fullsize) { + this.defaultFullsize = fullsize; + + if (swipeContainer != null) { + swipeContainer.setFullSize(getFullSize()); + } + } } - public void requestWebView(int currentAccount, long peerId, long botId, String buttonText, String buttonUrl, @WebViewType int type, int replyToMsgId, boolean silent) { - requestWebView(currentAccount, peerId, botId, buttonText, buttonUrl, type, replyToMsgId, silent, null, null, false, null, null, 0); + public void setNeedsContext(boolean needsContext) { + this.needsContext = needsContext; } - public void requestWebView(int currentAccount, long peerId, long botId, String buttonText, String buttonUrl, @WebViewType int type, int replyToMsgId, boolean silent, BaseFragment lastFragment, TLRPC.BotApp app, boolean allowWrite, String startParam, TLRPC.User botUser) { - requestWebView(currentAccount, peerId, botId, buttonText, buttonUrl, type, replyToMsgId, silent, lastFragment, app, allowWrite, startParam, botUser, 0); + public boolean getFullSize() { + return fullsize == null ? defaultFullsize : fullsize; } - public void requestWebView(int currentAccount, long peerId, long botId, String buttonText, String buttonUrl, @WebViewType int type, int replyToMsgId, boolean silent, BaseFragment lastFragment, TLRPC.BotApp app, boolean allowWrite, String startParam, TLRPC.User botUser, int flags) { - this.currentAccount = currentAccount; - this.peerId = peerId; - this.botId = botId; - this.replyToMsgId = replyToMsgId; - this.silent = silent; - this.buttonText = buttonText; - this.currentWebApp = app; + public void requestWebView(BaseFragment fragment, BotWebViewAttachedSheet.WebViewRequestProps props) { + this.requestProps = props; + this.currentAccount = props.currentAccount; + this.peerId = props.peerId; + this.botId = props.botId; + this.replyToMsgId = props.replyToMsgId; + this.silent = props.silent; + this.buttonText = props.buttonText; + this.currentWebApp = props.app; CharSequence title = UserObject.getUserName(MessagesController.getInstance(currentAccount).getUser(botId)); try { TextPaint tp = new TextPaint(); - tp.setTextSize(AndroidUtilities.dp(20)); + tp.setTextSize(dp(20)); title = Emoji.replaceEmoji(title, tp.getFontMetricsInt(), false); } catch (Exception ignore) {} actionBar.setTitle(title); @@ -981,6 +905,7 @@ public void requestWebView(int currentAccount, long peerId, long botId, String b } } + menu.addItem(R.id.menu_collapse_bot, R.drawable.arrow_more); ActionBarMenuItem otherItem = menu.addItem(0, R.drawable.ic_ab_other); otherItem.addSubItem(R.id.menu_open_bot, R.drawable.msg_bot, LocaleController.getString(R.string.BotWebViewOpenBot)); settingsItem = otherItem.addSubItem(R.id.menu_settings, R.drawable.msg_settings, LocaleController.getString(R.string.BotWebViewSettings)); @@ -993,6 +918,7 @@ public void requestWebView(int currentAccount, long peerId, long botId, String b if (currentBot != null && (currentBot.show_in_side_menu || currentBot.show_in_attach_menu)) { otherItem.addSubItem(R.id.menu_delete_bot, R.drawable.msg_delete, LocaleController.getString(R.string.BotWebViewDeleteBot)); } + actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { @@ -1028,6 +954,9 @@ public void onItemClick(int id) { deleteBot(currentAccount, botId, () -> dismiss()); } else if (id == R.id.menu_add_to_home_screen_bot) { MediaDataController.getInstance(currentAccount).installShortcut(botId, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT); + } else if (id == R.id.menu_collapse_bot) { + forceExpnaded = true; + dismiss(true, null); } } }); @@ -1036,132 +965,168 @@ public void onItemClick(int id) { webViewContainer.setBotUser(MessagesController.getInstance(currentAccount).getUser(botId)); webViewContainer.loadFlickerAndSettingsItem(currentAccount, botId, settingsItem); - preloadShortcutBotIcon(botUser, currentBot); - switch (type) { - case TYPE_BOT_MENU_BUTTON: { - TLRPC.TL_messages_requestWebView req = new TLRPC.TL_messages_requestWebView(); - req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); - req.peer = MessagesController.getInstance(currentAccount).getInputPeer(botId); - req.platform = "android"; - - req.url = buttonUrl; - req.flags |= 2; - - if (themeParams != null) { - req.theme_params = new TLRPC.TL_dataJSON(); - req.theme_params.data = themeParams.toString(); - req.flags |= 4; - } + preloadShortcutBotIcon(props.botUser, currentBot); + if (props.response != null) { + loadFromResponse(true); + } else { + switch (props.type) { + case TYPE_BOT_MENU_BUTTON: { + TLRPC.TL_messages_requestWebView req = new TLRPC.TL_messages_requestWebView(); + req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(botId); + req.platform = "android"; + req.compact = props.compact; - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - if (response instanceof TLRPC.TL_webViewResultUrl) { - TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) response; - queryId = resultUrl.query_id; - webViewContainer.loadUrl(currentAccount, resultUrl.url); - AndroidUtilities.runOnUIThread(pollRunnable, POLL_PERIOD); + req.url = props.buttonUrl; + req.flags |= 2; + + if (themeParams != null) { + req.theme_params = new TLRPC.TL_dataJSON(); + req.theme_params.data = themeParams.toString(); + req.flags |= 4; } - })); - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.webViewResultSent); - break; - } - case TYPE_SIMPLE_WEB_VIEW_BUTTON: { - TLRPC.TL_messages_requestSimpleWebView req = new TLRPC.TL_messages_requestSimpleWebView(); - req.from_switch_webview = (flags & FLAG_FROM_INLINE_SWITCH) != 0; - req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); - req.platform = "android"; - req.from_side_menu = (flags & FLAG_FROM_SIDE_MENU) != 0; - if (themeParams != null) { - req.theme_params = new TLRPC.TL_dataJSON(); - req.theme_params.data = themeParams.toString(); - req.flags |= 1; - } - if (!TextUtils.isEmpty(buttonUrl)) { - req.flags |= 8; - req.url = buttonUrl; - } - if (!TextUtils.isEmpty(startParam)) { - req.start_param = startParam; - req.flags |= 16; - } + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error != null) { - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - if (response instanceof TLRPC.TL_simpleWebViewResultUrl) { - TLRPC.TL_simpleWebViewResultUrl resultUrl = (TLRPC.TL_simpleWebViewResultUrl) response; - queryId = 0; - webViewContainer.loadUrl(currentAccount, resultUrl.url); - } - })); - break; - } - case TYPE_WEB_VIEW_BUTTON: { - TLRPC.TL_messages_requestWebView req = new TLRPC.TL_messages_requestWebView(); - req.peer = MessagesController.getInstance(currentAccount).getInputPeer(peerId); - req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); - req.platform = "android"; - if (buttonUrl != null) { - req.url = buttonUrl; - req.flags |= 2; - } + } else if (requestProps != null) { + requestProps.applyResponse(response); + loadFromResponse(false); + } + })); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.webViewResultSent); - if (replyToMsgId != 0) { - req.reply_to = SendMessagesHelper.getInstance(currentAccount).createReplyInput(replyToMsgId); - req.flags |= 1; + break; } + case TYPE_SIMPLE_WEB_VIEW_BUTTON: { + TLRPC.TL_messages_requestSimpleWebView req = new TLRPC.TL_messages_requestSimpleWebView(); + req.from_switch_webview = (props.flags & FLAG_FROM_INLINE_SWITCH) != 0; + req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + req.platform = "android"; + req.from_side_menu = (props.flags & FLAG_FROM_SIDE_MENU) != 0; + req.compact = props.compact; + if (themeParams != null) { + req.theme_params = new TLRPC.TL_dataJSON(); + req.theme_params.data = themeParams.toString(); + req.flags |= 1; + } + if (!TextUtils.isEmpty(props.buttonUrl)) { + req.flags |= 8; + req.url = props.buttonUrl; + } + if (!TextUtils.isEmpty(props.startParam)) { + req.start_param = props.startParam; + req.flags |= 16; + } - if (themeParams != null) { - req.theme_params = new TLRPC.TL_dataJSON(); - req.theme_params.data = themeParams.toString(); - req.flags |= 4; + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error != null) { + + } else if (requestProps != null) { + requestProps.applyResponse(response); + loadFromResponse(false); + } + })); + break; } + case TYPE_WEB_VIEW_BUTTON: { + TLRPC.TL_messages_requestWebView req = new TLRPC.TL_messages_requestWebView(); + req.peer = MessagesController.getInstance(currentAccount).getInputPeer(peerId); + req.bot = MessagesController.getInstance(currentAccount).getInputUser(botId); + req.platform = "android"; + req.compact = props.compact; + if (props.buttonUrl != null) { + req.url = props.buttonUrl; + req.flags |= 2; + } - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { - if (response instanceof TLRPC.TL_webViewResultUrl) { - TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) response; - queryId = resultUrl.query_id; - webViewContainer.loadUrl(currentAccount, resultUrl.url); - AndroidUtilities.runOnUIThread(pollRunnable, POLL_PERIOD); + if (replyToMsgId != 0) { + req.reply_to = SendMessagesHelper.getInstance(currentAccount).createReplyInput(replyToMsgId); + req.flags |= 1; } - })); - NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.webViewResultSent); - break; - } - case TYPE_WEB_VIEW_BOT_APP: { - TLRPC.TL_messages_requestAppWebView req = new TLRPC.TL_messages_requestAppWebView(); - TLRPC.TL_inputBotAppID botApp = new TLRPC.TL_inputBotAppID(); - botApp.id = app.id; - botApp.access_hash = app.access_hash; - - req.app = botApp; - req.write_allowed = allowWrite; - req.platform = "android"; - req.peer = lastFragment instanceof ChatActivity ? ((ChatActivity) lastFragment).getCurrentUser() != null ? MessagesController.getInputPeer(((ChatActivity) lastFragment).getCurrentUser()) : MessagesController.getInputPeer(((ChatActivity) lastFragment).getCurrentChat()) - : MessagesController.getInputPeer(botUser); - - if (!TextUtils.isEmpty(startParam)) { - req.start_param = startParam; - req.flags |= 2; - } - if (themeParams != null) { - req.theme_params = new TLRPC.TL_dataJSON(); - req.theme_params.data = themeParams.toString(); - req.flags |= 4; - } + if (themeParams != null) { + req.theme_params = new TLRPC.TL_dataJSON(); + req.theme_params.data = themeParams.toString(); + req.flags |= 4; + } - ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { - if (error2 == null) { - TLRPC.TL_appWebViewResultUrl result = (TLRPC.TL_appWebViewResultUrl) response2; - queryId = 0; - webViewContainer.loadUrl(currentAccount, result.url); + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { + if (error != null) { - AndroidUtilities.runOnUIThread(pollRunnable, POLL_PERIOD); + } else if (requestProps != null) { + requestProps.applyResponse(response); + loadFromResponse(false); + } + })); + NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.webViewResultSent); + break; + } + case TYPE_WEB_VIEW_BOT_APP: { + TLRPC.TL_messages_requestAppWebView req = new TLRPC.TL_messages_requestAppWebView(); + TLRPC.TL_inputBotAppID botApp = new TLRPC.TL_inputBotAppID(); + botApp.id = props.app.id; + botApp.access_hash = props.app.access_hash; + + req.app = botApp; + req.write_allowed = props.allowWrite; + req.platform = "android"; + req.peer = fragment instanceof ChatActivity ? ((ChatActivity) fragment).getCurrentUser() != null ? MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentUser()) : MessagesController.getInputPeer(((ChatActivity) fragment).getCurrentChat()) + : MessagesController.getInputPeer(props.botUser); + req.compact = props.compact; + + if (!TextUtils.isEmpty(props.startParam)) { + req.start_param = props.startParam; + req.flags |= 2; } - }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); + + if (themeParams != null) { + req.theme_params = new TLRPC.TL_dataJSON(); + req.theme_params.data = themeParams.toString(); + req.flags |= 4; + } + + ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response2, error2) -> AndroidUtilities.runOnUIThread(() -> { + if (error2 != null) { + + } else if (requestProps != null) { + requestProps.applyResponse(response2); + loadFromResponse(false); + } + }), ConnectionsManager.RequestFlagInvokeAfter | ConnectionsManager.RequestFlagFailOnServerErrors); + } } } } + private void loadFromResponse(boolean fromTab) { + if (requestProps == null) return; + final long pollTimeout = Math.max(0, POLL_PERIOD - (System.currentTimeMillis() - requestProps.responseTime)); + String url = null; + fullsize = null; + if (requestProps.response instanceof TLRPC.TL_webViewResultUrl) { + TLRPC.TL_webViewResultUrl resultUrl = (TLRPC.TL_webViewResultUrl) requestProps.response; + queryId = resultUrl.query_id; + url = resultUrl.url; + fullsize = resultUrl.fullsize; + } else if (requestProps.response instanceof TLRPC.TL_appWebViewResultUrl) { // deprecated + TLRPC.TL_appWebViewResultUrl result = (TLRPC.TL_appWebViewResultUrl) requestProps.response; + queryId = 0; + url = result.url; + } else if (requestProps.response instanceof TLRPC.TL_simpleWebViewResultUrl) { // deprecated + TLRPC.TL_simpleWebViewResultUrl resultUrl = (TLRPC.TL_simpleWebViewResultUrl) requestProps.response; + queryId = 0; + url = resultUrl.url; + } + if (url != null && !fromTab) { + webViewContainer.loadUrl(currentAccount, url); + } + AndroidUtilities.runOnUIThread(pollRunnable, pollTimeout); + if (swipeContainer != null) { + swipeContainer.setFullSize(getFullSize()); + } + } + private void preloadShortcutBotIcon(TLRPC.User botUser, TLRPC.TL_attachMenuBot currentBot) { if (currentBot != null && currentBot.show_in_side_menu && !MediaDataController.getInstance(currentAccount).isShortcutAdded(botId, MediaDataController.SHORTCUT_TYPE_ATTACHED_BOT)) { TLRPC.User user = botUser; @@ -1220,24 +1185,36 @@ private int getColor(int key) { @Override public void show() { if (!AndroidUtilities.isSafeToShow(getContext())) return; - frameLayout.setAlpha(0f); - frameLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + windowView.setAlpha(0f); + windowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { v.removeOnLayoutChangeListener(this); swipeContainer.setSwipeOffsetY(swipeContainer.getHeight()); - frameLayout.setAlpha(1f); + windowView.setAlpha(1f); AnimationNotificationsLocker locker = new AnimationNotificationsLocker(); locker.lock(); - new SpringAnimation(swipeContainer, ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer.SWIPE_OFFSET_Y, 0) - .setSpring(new SpringForce(0) - .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) - .setStiffness(500.0f) - ).addEndListener((animation, canceled, value, velocity) -> { - locker.unlock(); - }).start(); + + if (showOffsetY != Float.MAX_VALUE) { + swipeContainer.setSwipeOffsetAnimationDisallowed(true); + swipeContainer.setOffsetY(showOffsetY); + swipeContainer.setSwipeOffsetAnimationDisallowed(false); + } + + if (showExpanded || getFullSize()) { + swipeContainer.stickTo(-swipeContainer.getOffsetY() + swipeContainer.getTopActionBarOffsetY(), locker::unlock); + } else { + new SpringAnimation(swipeContainer, ChatAttachAlertBotWebViewLayout.WebViewSwipeContainer.SWIPE_OFFSET_Y, 0) + .setSpring(new SpringForce(0) + .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) + .setStiffness(500.0f) + ).addEndListener((animation, canceled, value, velocity) -> { + locker.unlock(); + }).start(); + } + swipeContainer.opened = true; } }); super.show(); @@ -1258,7 +1235,11 @@ public void onBackPressed() { if (webViewContainer.onBackPressed()) { return; } - onCheckDismissByUser(); +// if (can_minimize) { + dismiss(true, null); +// } else { +// onCheckDismissByUser(); +// } } @Override @@ -1291,26 +1272,41 @@ public boolean onCheckDismissByUser() { } public void dismiss(Runnable callback) { + dismiss(false, callback); + } + + public void dismiss(boolean intoTabs, Runnable callback) { if (dismissed) { return; } dismissed = true; AndroidUtilities.cancelRunOnUIThread(pollRunnable); - webViewContainer.destroyWebView(); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.webViewResultSent); NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.didSetNewTheme); - swipeContainer.stickTo(swipeContainer.getHeight() + frameLayout.measureKeyboardHeight(), ()->{ - try { - super.dismiss(); - } catch (Exception e) { - FileLog.e(e); - } - if (callback != null) { - callback.run(); + if (intoTabs && (LaunchActivity.instance == null || LaunchActivity.instance.getBottomSheetTabsOverlay() == null)) { + intoTabs = false; + } + if (intoTabs) { + if (springAnimation != null) { + springAnimation.getSpring().setFinalPosition(0); + springAnimation.start(); } - }); + LaunchActivity.instance.getBottomSheetTabsOverlay().dismissSheet(this); + } else { + webViewContainer.destroyWebView(); + swipeContainer.stickTo(swipeContainer.getHeight() + windowView.measureKeyboardHeight() + (getFullSize() ? dp(200) : 0), () -> { + super.dismiss(); + if (callback != null) { + callback.run(); + } + }); + } + } + + public void release() { + super.dismiss(); } @Override @@ -1322,7 +1318,7 @@ public void didReceivedNotification(int id, int account, Object... args) { dismiss(); } } else if (id == NotificationCenter.didSetNewTheme) { - frameLayout.invalidate(); + windowView.invalidate(); webViewContainer.updateFlickerBackgroundColor(getColor(Theme.key_windowBackgroundWhite)); updateActionBarColors(); updateLightStatusBar(); @@ -1336,4 +1332,282 @@ public static int navigationBarColor(int actionBarColor) { // return OKLCH.adapt(themeNavBarColor, actionBarColor); return Theme.adaptHSV(actionBarColor, +.35f, -.1f); } + + + public void setBackgroundColor(int color, boolean animated) { + int from = backgroundPaint.getColor(); + if (animated) { + ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(200); + animator.setInterpolator(CubicBezierInterpolator.DEFAULT); + animator.addUpdateListener(animation -> { + backgroundPaint.setColor(ColorUtils.blendARGB(from, color, (Float) animation.getAnimatedValue())); + updateActionBarColors(); + windowView.invalidate(); + }); + animator.start(); + } else { + backgroundPaint.setColor(color); + updateActionBarColors(); + windowView.invalidate(); + } + } + + public void setActionBarColor(int color, boolean isOverrideColor, boolean animated) { + int from = actionBarColor; + int navBarFrom = navBarColor; + int to = color; + int navBarTo = navigationBarColor(color); + + BotWebViewMenuContainer.ActionBarColorsAnimating actionBarColorsAnimating = new BotWebViewMenuContainer.ActionBarColorsAnimating(); + actionBarColorsAnimating.setFrom(overrideBackgroundColor ? actionBarColor : 0, resourcesProvider); + overrideBackgroundColor = isOverrideColor; + actionBarIsLight = ColorUtils.calculateLuminance(color) < 0.721f; + actionBarColorsAnimating.setTo(overrideBackgroundColor ? to : 0, resourcesProvider); + + if (animated) { + ValueAnimator animator = ValueAnimator.ofFloat(0, 1).setDuration(200); + animator.setInterpolator(CubicBezierInterpolator.DEFAULT); + animator.addUpdateListener(animation -> { + float progress = (float) animation.getAnimatedValue(); + actionBarColor = ColorUtils.blendARGB(from, to, progress); + navBarColor = ColorUtils.blendARGB(navBarFrom, navBarTo, progress); + checkNavBarColor(); + windowView.invalidate(); + actionBar.setBackgroundColor(actionBarColor); + + actionBarColorsAnimating.updateActionBar(actionBar, progress); + lineColor = actionBarColorsAnimating.getColor(Theme.key_sheet_scrollUp); + + windowView.invalidate(); + }); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + float progress = 1f; + actionBarColor = ColorUtils.blendARGB(from, to, progress); + navBarColor = ColorUtils.blendARGB(navBarFrom, navBarTo, progress); + checkNavBarColor(); + windowView.invalidate(); + actionBar.setBackgroundColor(actionBarColor); + + actionBarColorsAnimating.updateActionBar(actionBar, progress); + lineColor = actionBarColorsAnimating.getColor(Theme.key_sheet_scrollUp); + + windowView.invalidate(); + } + }); + animator.start(); + } else { + float progress = 1f; + actionBarColor = to; + navBarColor = navBarTo; + checkNavBarColor(); + windowView.invalidate(); + actionBar.setBackgroundColor(actionBarColor); + + actionBarColorsAnimating.updateActionBar(actionBar, progress); + lineColor = actionBarColorsAnimating.getColor(Theme.key_sheet_scrollUp); + + windowView.invalidate(); + } + updateLightStatusBar(); + } + + public void checkNavBarColor() { + if (!dismissed && LaunchActivity.instance != null) { + LaunchActivity.instance.checkSystemBarColors(true, true, true, false); + //LaunchActivity.instance.setNavigationBarColor(fragment.getNavigationBarColor(), false); + } + } + + public WindowView getWindowView() { + return windowView; + } + + public class WindowView extends SizeNotifierFrameLayout { + public WindowView(Context context) { + super(context); + } + + { + setClipChildren(false); + setClipToPadding(false); + setWillNotDraw(false); + } + + private final Paint navbarPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + @Override + protected void dispatchDraw(Canvas canvas) { + if (drawingFromOverlay) return; + + super.dispatchDraw(canvas); + + if (passcodeView.getVisibility() != View.VISIBLE) { + navbarPaint.setColor(navBarColor); + AndroidUtilities.rectTmp.set(0, getHeight() - getPaddingBottom(), getWidth(), getHeight() + AndroidUtilities.navigationBarHeight); + canvas.drawRect(AndroidUtilities.rectTmp, navbarPaint); + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (drawingFromOverlay) return; + + super.onDraw(canvas); + + if (passcodeView.getVisibility() != View.VISIBLE) { + if (!overrideBackgroundColor) { + backgroundPaint.setColor(getColor(Theme.key_windowBackgroundWhite)); + } + AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight()); + canvas.drawRect(AndroidUtilities.rectTmp, dimPaint); + + actionBarPaint.setColor(actionBarColor); + float radius = AndroidUtilities.dp(16) * (AndroidUtilities.isTablet() ? 1f : 1f - actionBarTransitionProgress); + AndroidUtilities.rectTmp.set(swipeContainer.getLeft(), AndroidUtilities.lerp(swipeContainer.getTranslationY(), 0, actionBarTransitionProgress), swipeContainer.getRight(), swipeContainer.getTranslationY() + AndroidUtilities.dp(24) + radius); + canvas.drawRoundRect(AndroidUtilities.rectTmp, radius, radius, actionBarPaint); + + AndroidUtilities.rectTmp.set(swipeContainer.getLeft(), swipeContainer.getTranslationY() + AndroidUtilities.dp(24), swipeContainer.getRight(), getHeight()); + canvas.drawRect(AndroidUtilities.rectTmp, backgroundPaint); + } + } + + @Override + public void draw(Canvas canvas) { + if (drawingFromOverlay) return; + + super.draw(canvas); + + float transitionProgress = AndroidUtilities.isTablet() ? 0 : actionBarTransitionProgress; + linePaint.setColor(lineColor); + linePaint.setAlpha((int) (linePaint.getAlpha() * (1f - Math.min(0.5f, transitionProgress) / 0.5f))); + + canvas.save(); + float scale = 1f - transitionProgress; + float y = AndroidUtilities.isTablet() ? AndroidUtilities.lerp(swipeContainer.getTranslationY() + AndroidUtilities.dp(12), AndroidUtilities.statusBarHeight / 2f, actionBarTransitionProgress) : + (AndroidUtilities.lerp(swipeContainer.getTranslationY(), AndroidUtilities.statusBarHeight + ActionBar.getCurrentActionBarHeight() / 2f, transitionProgress) + AndroidUtilities.dp(12)); + canvas.scale(scale, scale, getWidth() / 2f, y); + canvas.drawLine(getWidth() / 2f - AndroidUtilities.dp(16), y, getWidth() / 2f + AndroidUtilities.dp(16), y, linePaint); + canvas.restore(); + + actionBarShadow.setAlpha((int) (actionBar.getAlpha() * 0xFF)); + y = actionBar.getY() + actionBar.getTranslationY() + actionBar.getHeight(); + actionBarShadow.setBounds(0, (int)y, getWidth(), (int)(y + actionBarShadow.getIntrinsicHeight())); + actionBarShadow.draw(canvas); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN && (event.getY() <= AndroidUtilities.lerp(swipeContainer.getTranslationY() + AndroidUtilities.dp(24), 0, actionBarTransitionProgress) || + event.getX() > swipeContainer.getRight() || event.getX() < swipeContainer.getLeft())) { + dismiss(true, null); + return true; + } + return super.onTouchEvent(event); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + Bulletin.addDelegate(this, new Bulletin.Delegate() { + @Override + public int getTopOffset(int tag) { + return AndroidUtilities.statusBarHeight; + } + }); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + Bulletin.removeDelegate(this); + } + + private boolean drawingFromOverlay; + public void setDrawingFromOverlay(boolean drawingFromOverlay) { + if (this.drawingFromOverlay != drawingFromOverlay) { + this.drawingFromOverlay = drawingFromOverlay; + invalidate(); + } + } + + private final RectF rect = new RectF(); + private final Path clipPath = new Path(); + + + public float drawInto(Canvas canvas, RectF finalRect, float progress, RectF clipRect) { + rect.set(swipeContainer.getLeft(), swipeContainer.getTranslationY() + dp(24), swipeContainer.getRight(), getHeight()); + AndroidUtilities.lerpCentered(rect, finalRect, progress, clipRect); + + canvas.save(); + + clipPath.rewind(); + float radius = dp(16) * (AndroidUtilities.isTablet() ? 1f : 1f - actionBarTransitionProgress); + final float r = AndroidUtilities.lerp(radius, dp(10), progress); + clipPath.addRoundRect(clipRect, r, r, Path.Direction.CW); + canvas.clipPath(clipPath); + canvas.drawPaint(backgroundPaint); + + if (swipeContainer != null) { + canvas.save(); + canvas.translate(clipRect.left, Math.max(swipeContainer.getY(), clipRect.top) + progress * dp(51)); + swipeContainer.draw(canvas); + canvas.restore(); + } + + canvas.restore(); + + return r; + } + + } + + private BotWebViewAttachedSheet.MainButtonSettings mainButtonSettings; + public void setMainButton(BotWebViewAttachedSheet.MainButtonSettings s) { + mainButtonSettings = s; + + mainButton.setClickable(s.isActive); + mainButton.setText(s.text); + mainButton.setTextColor(s.textColor); + mainButton.setBackground(BotWebViewContainer.getMainButtonRippleDrawable(s.color)); + if (s.isVisible != mainButtonWasVisible) { + mainButtonWasVisible = s.isVisible; + mainButton.animate().cancel(); + if (s.isVisible) { + mainButton.setAlpha(0f); + mainButton.setVisibility(View.VISIBLE); + } + mainButton.animate().alpha(s.isVisible ? 1f : 0f).setDuration(150).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!s.isVisible) { + mainButton.setVisibility(View.GONE); + } + swipeContainer.requestLayout(); + } + }).start(); + } + radialProgressView.setProgressColor(s.textColor); + if (s.isProgressVisible != mainButtonProgressWasVisible) { + mainButtonProgressWasVisible = s.isProgressVisible; + radialProgressView.animate().cancel(); + if (s.isProgressVisible) { + radialProgressView.setAlpha(0f); + radialProgressView.setVisibility(View.VISIBLE); + } + radialProgressView.animate().alpha(s.isProgressVisible ? 1f : 0f) + .scaleX(s.isProgressVisible ? 1f : 0.1f) + .scaleY(s.isProgressVisible ? 1f : 0.1f) + .setDuration(250) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + if (!s.isProgressVisible) { + radialProgressView.setVisibility(View.GONE); + } + } + }).start(); + } + } } diff --git a/TMessagesProj/src/main/java/org/telegram/ui/bots/ChatAttachAlertBotWebViewLayout.java b/TMessagesProj/src/main/java/org/telegram/ui/bots/ChatAttachAlertBotWebViewLayout.java index e3d0c5382f..0e42f84032 100644 --- a/TMessagesProj/src/main/java/org/telegram/ui/bots/ChatAttachAlertBotWebViewLayout.java +++ b/TMessagesProj/src/main/java/org/telegram/ui/bots/ChatAttachAlertBotWebViewLayout.java @@ -42,6 +42,7 @@ import org.telegram.messenger.R; import org.telegram.messenger.SendMessagesHelper; import org.telegram.messenger.UserObject; +import org.telegram.messenger.Utilities; import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLRPC; @@ -696,7 +697,7 @@ public void showJustAddedBulletin() { public static class WebViewSwipeContainer extends FrameLayout { public final static SimpleFloatPropertyCompat SWIPE_OFFSET_Y = new SimpleFloatPropertyCompat<>("swipeOffsetY", WebViewSwipeContainer::getSwipeOffsetY, WebViewSwipeContainer::setSwipeOffsetY); - private GestureDetectorCompat gestureDetector; + private final GestureDetectorCompat gestureDetector; private boolean isScrolling; private boolean isSwipeDisallowed; @@ -723,6 +724,21 @@ public static class WebViewSwipeContainer extends FrameLayout { private GenericProvider isKeyboardVisible = obj -> false; + private boolean fullsize; + public boolean opened; + public void setFullSize(boolean fullsize) { + if (this.fullsize != fullsize) { + this.fullsize = fullsize; + if (fullsize) { + if (opened) { + stickTo(-getOffsetY() + getTopActionBarOffsetY()); + } + } else { + stickTo(0); + } + } + } + public WebViewSwipeContainer(@NonNull Context context) { super(context); @@ -730,7 +746,7 @@ public WebViewSwipeContainer(@NonNull Context context) { gestureDetector = new GestureDetectorCompat(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if (isSwipeDisallowed) { + if (isSwipeDisallowed || fullsize) { return false; } if (velocityY >= 700 && (webView == null || webView.getScrollY() == 0)) { @@ -795,6 +811,9 @@ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float d } swipeOffsetY = MathUtils.clamp(swipeOffsetY, -offsetY + topActionBarOffsetY, getHeight() - offsetY + topActionBarOffsetY); + if (fullsize) { + swipeOffsetY = Math.min(swipeOffsetY, -offsetY + topActionBarOffsetY); + } invalidateTranslation(); return true; } @@ -882,7 +901,11 @@ public void setOffsetY(float offsetY) { float progress = (value - wasOffsetY) / deltaOffsetY; if (wasOnTop) { - swipeOffsetY = MathUtils.clamp(swipeOffsetY - progress * Math.max(0, deltaOffsetY), -this.offsetY + topActionBarOffsetY, getHeight() - this.offsetY + topActionBarOffsetY); + swipeOffsetY = MathUtils.clamp( + swipeOffsetY - progress * Math.max(0, deltaOffsetY), + -this.offsetY + topActionBarOffsetY, + getHeight() - this.offsetY + topActionBarOffsetY + ); } if (scrollAnimator != null && scrollAnimator.getSpring().getFinalPosition() == -wasOffsetY + topActionBarOffsetY) { scrollAnimator.getSpring().setFinalPosition(-offsetY + topActionBarOffsetY); @@ -904,7 +927,11 @@ public void setOffsetY(float offsetY) { this.offsetY = offsetY; if (wasOnTop) { - swipeOffsetY = MathUtils.clamp(swipeOffsetY - Math.max(0, deltaOffsetY), -this.offsetY + topActionBarOffsetY, getHeight() - this.offsetY + topActionBarOffsetY); + swipeOffsetY = MathUtils.clamp( + swipeOffsetY - Math.max(0, deltaOffsetY), + -this.offsetY + topActionBarOffsetY, + getHeight() - this.offsetY + topActionBarOffsetY + ); } invalidateTranslation(); } @@ -959,7 +986,9 @@ public boolean dispatchTouchEvent(MotionEvent ev) { isSwipeDisallowed = false; isScrolling = false; - if (flingInProgress) { + if (fullsize) { + + } else if (flingInProgress) { flingInProgress = false; } else { if (swipeOffsetY <= -swipeStickyRange) { @@ -986,6 +1015,9 @@ public void stickTo(float offset) { } public void stickTo(float offset, Runnable callback) { + if (fullsize) { + offset = -getOffsetY() + getTopActionBarOffsetY(); + } if (swipeOffsetY == offset || scrollAnimator != null && scrollAnimator.getSpring().getFinalPosition() == offset) { if (callback != null) { callback.run(); @@ -1005,7 +1037,7 @@ public void stickTo(float offset, Runnable callback) { } scrollAnimator = new SpringAnimation(this, SWIPE_OFFSET_Y, offset) .setSpring(new SpringForce(offset) - .setStiffness(1400) + .setStiffness(1200) .setDampingRatio(SpringForce.DAMPING_RATIO_NO_BOUNCY)) .addEndListener((animation, canceled, value, velocity) -> { if (animation == scrollAnimator) { diff --git a/TMessagesProj/src/main/res/drawable-hdpi/large_locked_post.png b/TMessagesProj/src/main/res/drawable-hdpi/large_locked_post.png new file mode 100644 index 0000000000000000000000000000000000000000..725967a3f3029fed5d5352612bc51aa5a65b1696 GIT binary patch literal 1092 zcmeAS@N?(olHy`uVBq!ia0vp^HXzKw1|+Ti+$;i8jKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(;Zvw#`F1}W^m|1t)se4VF@V@L+; z+gY|5B90=*--k4*bcLF5N%1hcUT|`mA`&LZv!XR&;xP#pLGF!0TP}H7a|^TV^kWf~ z=JrljoVRhq1_4p~&Lsja%?kn-KYxGpuED-1@6yuC%lv1s*FSr`xAOn@w6Z+=bs=0r zMgnjc5W;q<(qi4!EbouT4Bwj1Ch!W(Wbr?cTd+Jv&p-Izey0Vg1rk0DX-Za$W}j9{ znHSno-Vm*@vcuQ?Sxb!{$3wFM`^+V6`)+Le`QSzBZ-;G6Hy^Tc*e`w0Rw34#yTpdW z%r$rOiUMDGL+hwbej93!c^$p_KE$v2=&UVYT$2|qUh4mPdyMq4#6%H3^@-w)S0>fJ{(J>g;tJRF!r)Ueb&+mi&s{J$6 zQd%zfF37idamptp@8d$@Sr#us`1u7UT&-XUQ~UNN?E~ZHw`;yxn|}^E`SXHOgy%FH z(Wjzoj%e)DnwX!dF7{mX*h{`Wp~BT?jwPtC{&OK!3n+5aOuSw4rp0eR;SX&hPo8pm z>9L0wew+I&{iJE+`i@U50{5#O{;*A6wROj@GW#uSR-O&FIsMg~d6}e@ef^F6{^Dga znYPE@%}T6p{k7!cw>aO;OLI@ux_HKI+0UAPpfv28_|r8fw2E)NUu4K}&Gm-UPOb{U z`Hbfe?3lj5&OGVd5AS2=k6NiDNPhJ{z->N1I*LQSi`g?k?yKmUf)A5im3MTnxe<0t z{DAod=Bmb=_{#TxW)=Beuys}b-kj6tu$b$Kn?umg#)GLF!p{5NpUE=0H>3LYw)diD zl{O0uPprDO`=foP*6&rn_q6$*i_hdS;Rq}FdfNK3-R7z|#{0@Q($;4`O+UT+iRgwq ze&@KZzl;lDEpPn!_C@WQ$D7Z*mU^eYBbmjB=Zw1SPaUoJ|LdFT9>iW^(fNL8o6>yO zr<|FJH{F*jbgF#(dwx&mwk>yz#8l7iyUY@!dMk=iu-pIRTa|N+#jo}qSp1o{LHPE5 zx9LjvHJ25bJUx)fUUci3>Z8?nC+3-@2~60pCd=O6c*iPD*J$|}UAsB)R>v+bo2M|( z!;9nO%KX2KcedmP%unJzy*O>cgho_2?c4$RTCu~5FYi3?0p(y%S3j3^P6Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91B%lKT1ONa40RR91Bme*a04fgS`~Uz0rAb6VR9Fekn7wNhQ53~@O_UTu z3L&6EYz&C4f+3(u6$=Fm3oA)u6_HfN*1{%Y5eQf;xP_gNe?ZY-qD2CNtq`IjqJ>x` z5~9ZOcXr=h=i|+rmxah4IC&rU-23jlnYr^ewOWs%-c3G9lJwzs4vxTMjx@C|j_b5* z^?H3*lluk|nS@RF4OT%KX(#uRP}B|)bd;d?;rUeXg#IUJyCReA)-e5a>{pCu*|AlL zx(10Hf(@9)5`$@YyezP6P9tp^igl>WFF23m5PY4Awnl$E=;SfV2g=u=m_r_E`IH>s zdlzAscD7s{XHg8OSb}25C52G8$#!Ow{-wAjt5w9h3>8z*8r8QCkv%iIny6&Z^~00Z zDb;F<32mnuK_7&p&=7r_aCqdLt_K%zMzp$p2ED<4pjdIL=+_hd0DSBG=KPV1v*_(X zUnyFdL2%IpRdc~BN=>3U1)stln8pT!8JMm^DbIEn`#3Zdizk+*vo009a}(%B;18#_ z=C^fR`#9%T7^1T})yx<%^g;U^OlORq0oo_faF5Pms9QGy-$8@Eccnn20xM{3eIMIk zFs51X2bNaglwNg2&}Sn9<_6&+{0xI{9(Gl9ttpIk0x_7<4EWfY40O<~x710pO?x6B ztu$#(7i&u51I|MqMWJxT?dihb=Wb?zR&UrmyaMB@Z#JB*X*smsumzXpVpi)Pp*;eX zh!1a=oPqs*@9Zo(-Ckiq7TY0H1z**Us9yh0Uj^+Z)Y0M>G@PL`5A_DcBxP&rIMYeU zw(Y1UW5C&F2R?+VS%Yu_K7dgd8q z5Ea0mP|#=N4pikVjTAKkgsI+cF0%8 zIQ7oYAlpKhlzyrhR_Ro}edceie4u;{irLMnptV!~{ik%WaHX!YH~!gR30cclt9YuS z+xeSrDWs9sU-T?!u8rEEoR1#*-uQ=AKC%56{{=A19~#wZ?l=Gd002ovPDHLkV1mnJ BPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91B%lKT1ONa40RR91Bme*a04fgS`~Uz0Vo5|nR9Fe^m(NQTQ5460l9eS9 zwNTV_V=WR>S}CbLDX6HW)L-DTRf`~`g|v|%gm$i4v};qVD2jxNh=Qb@CP=?Tp;~EW zKYV?@^X_@vH#2YM$x99L!1tXq=X}pSXYS0q_fA#SrsQWJm&34IiN2fkbicab_nWcQIY0i^;L9ujBGG1$T5uh#0F!4JgP<-knIN2uIOrzu4#deM z&YKQ5(Dkw766gR^z(kD<#F7)75mJl;-d-UlROEuw0K)Pc$C)CcE^xMhPmVo}PF%2v z>v4>Nd0--2zS>BEe2tSv^z``%mtYNe;YbVU<_O7i5bp$Px|Aq($V&-Kd{;sVV_}XF zhaB-2x<8iWoWwOV){GwSFwlD^$hJo=PSH$pJpoL#rUmb&G&o_+Xm)0SWIU-E#o*|X zNL2V>n2)dB#Zb9Xi({+KS2>ZFV22me-Zl$rRG=G}>@!j+4bk42;RKFmN>bRXu&vHp z>;}ult^+3BnzAkzoMBacZ^SrFmi|e$cs-2cIG@GpU5?zlfL8R=)G0KIy@+oHNV-zD zjy$jOUkcx66m2Vsq8XZWj5h8j@sTm;XSfTFf6VDPWw8a96UPaBu4bDfOzIt4mzTO! zN7Nq&l35U(4Aj)R{O!X{QMVsxsgVp7;#_E!)_h?s6vUujGteR|QSJQf7zsxUSktY; ztJO*^TU=GM#bQO8z?yEZDn<=$JKDGPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91B%lKT1ONa40RR91Bme*a04fgS`~Uz0mPtfGR9Fe^mrY1iQ545#G}9sk z%^)j;B1tIFRz(z)2wBkwK^vDXDgw7<2!#Zd1!Zl@g|#ne(;{31_CeH27gGz+xcjmq~&o^(U!6N}AR_#KFcr*mf$iNtnTI}Gz~ zJRa}OT~#0kCvzG2?Mi{#h0Agl&CZ!TWh_{04zDgimd<^q`#pRyC;}SG`U=o;dPy4A zC)=hDO_s^D$%)t7c)>kQdrad&e>38PCfDQ76=Y7cD)7TJJ?w8n{IJQ1Q{~Uk`kW^1 zrr{_2LVp9rz?k8*`}4Cpr%5?@Z<=*ZCr-Z8aKtU2ju#a7EQ}PdfwUW}28)4ixx7Uw zW7!l~#UeP8ocRvu)DRj5#U}L&|H;8eGZyC#Ixp~EL*xjU08y;VTb(^aq$`RLoJr7X z6_R&|KrMmiU>+C+4}g0`?)U>&^p1Q^Lw5zFWx(EHzu!!5InW(%K=&Cw7NhVOkm)yH zkPQBZ1HDSY2#~d@Ws&JyX8bt*w;BHrH$fBilx1!NXSX4Fj;X_S-24=$a7TKSfbLYw z<~@-gG39>Oe6&|U)WJN+&N%0oB-rVLTf)2=|G36j^kv=JNxk-qTo8o+BH z`vXn_&+_BxNpdIn0<1k@a1`tZhrmV93*G{2dUelugaRgT4uEk}^cBA^fR-JCSPLGR z!Z7!eWE-f?R`EvQoOXnU7_aj(a?B{u{$>xj4P5)il;0nYgyI-G(Ec#(~>b;uVI6OEkK@B8@5PP$kyyFCeDBJ*{92_)Rh8 z?B*<8PORUMr+tpXo+0gJWS-p|f_h5^fc~LYfX_f1EJ0(%l+#1(f1|9H)rJ<)5IbwG z<_*3MtOYksOin-1HIQE980#lgOgU?+QSBNaE!nwPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91B%lKT1ONa40RR91Bme*a04fgS`~Uz0^GQTOR9Fekm^)|`Q51%EeMLnC z6A=`3K?R#cLGYDED+|R3+E@x=CqxXWbt=I}A*Ksh2wF&jrmzzce1U>!p@m`!6Rms{ z;wx(W{@HVeJJ*@)ZgwF#@b9_zod2AAX6~IkceYe&flN$HbOfli%Mc9>R#V`W#RZCSQLdhv!7+iLxnRJvpr3;6 zF|XdfVl4+M5#^p+Z+r=ISj<)W^Xm>SASwq?E|-A9G;DW}2R+{836;5}H1ZA$gR`!=Zh~Wy%XUH+hbc zfwa(F(tRr3hwwZnZUY!%uSf~Xg>MJyFj%1e6yygn*U5V?j3w_N{e$2IzKy95$2#DW z*=H~=13%r_g#Jcs=6y$B`;?1bNu@bfmEKK~Q7YB$8_GgD^UUxJ{UUVIUIhG(>WaTc z{!R3|fbOp6NhlhNYP9QXM^_CO5(maRR%U-R1un%_%q|rdbjp01^0i@{x3M2F&WPKT^oJRD zrN*d%|B7+O(COl)T`{{f+m#FsLDM)6wC|r86hSd8;6OH{u!R z(XW_IDlF)-**~I}I6v0-boX64RAIaK&{}+jg8!Uxb>;0MqAO;TN(w@+2jemzCkhi* zsta=?gB|2ag3Xkb0oM(-2 zoMfwGGPa%A_L}`PG2<~O>id{U4PKDWz>>_SST+6kdkM=JXan(SKs&!L;QJIaM8B5! zm|jD&4tlvV$-7$7^20ltu&Y~v-*TBwn*A?&OP#siSxj%r$58m{NJ&8h{%r8l68Wa8 z?t8Oc3+RPSxc|L6QZi2eq8Ln#lQiHTu)$-cts#BuVGf8tgaB3gW6bG^4BP=m{z+G??6z=q4 zUGVBV!dg)?=aBDL!4=G$&mg-?GwVY!xKr_`^dNW7cUGqrYH@*LT=a?6Qy73FJ>(aW zNTBZ~y$-eooayJZaV~t^GSDEJ|(4(AR+uHg-ZFs#$Ma3uv!)ifu3_`R7{W__X(L?>;S>nJRojO~8s# z-({ub8(z{oC6@j@PX54X0JE-xe!1(Q6W(ZltXzQ@?5LMX$p) zH@3O_N!`nFj3-6c@P5ne{9dbF7jllh@3CiXKKM+?*z(R^Uv^91+n4hWn~0jf%2KiH z-v02>QmN$&+4FoK7vJ11C9$sH$djJQuUB1s=+$y0eqwdvr`>zJvh>!Sd^>B;oQa!x zBV`)rhP6j$zm2*j^LtHqym-IzhII~`&HpXVTH?Rm_1&{#)0DUKqpp3Nue3zS*@Ll$ z&C@qaBuFq-d|`&^N~6~`SJ%mI3yZDh`X203$FL;RHScF)h~ryT!A7ay%$e@aEsIyJ z+^aI{ePE@09YgE`^A025vvbOt0?(@KKc;+}arT=1S$r85w>WoY{Z$EMbbny|!B_1k z-XZ1He_vZ{+R?0Fy3E&?{i)%f4^b67JHu^`Z%C`-#m9H#aw%SV}$mkNvxBT zj=Sy5={We~Z~D34^Nfr2E_W=iyZ5{{@A=;TZQI0_1g$LT77Lo>toh7ItnYzfkH>Uh zJM)!Y!cSE+TW#63FK{j|;Q6lbvfcB9gJhY)bBlArxtjuPc+CUan0St)o_Ky#Bh2#U zjvK3D89y+U8%>DG-|dv1nfAV*4g#R;ku5_dcoJ%(jiU!b15p!cDgv`PyX2KP&6a ziI2GloNJfeKfHYA3b%XP7bO-vWacX6Ti+bJ`F2zE1d&7?wXomc`2M|IwD|Ei-i1pK zUSkwjuxz)jVCBtN^-=l8mXK9bx7t}*T~@y)5U`=Y;mLs+7u^@KygnwDvvK}`iCZsT zJi0R8{CszHQ_NE%6RptLW8B z`%1UB*G`;fxu$yq_udU$QEipCo;fAF5z>_2FUzs(-61Itlg$$nzE3~#IZN`(ww9I$ zl08xfn&#+4wk}hVtz2!oaQ~{MvrH8&U(WdU_&U2vdI5*kvUr2+tQlg_PYg>7UlbjQ z-C|cWdx7u27A2qAiw=ra|5&8S(0*<0&ESnLXM8s#1RNLt8&V;EVWU^gn>`l0AMzgE zYVxsv?({$P=eJ7oF8*;@eomVG%+obHxc|LVzkJ3;cXOemdqn?2%T+5vW6gg4Nw=Kw hM|y6O?*H4Ln2#zgO}cP&-8oSD@pScbS?83{1OQ?~CeZ)@ literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_instant_view.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_instant_view.png new file mode 100644 index 0000000000000000000000000000000000000000..bd03d366baffed933d212adebed6f3c3a2d33a91 GIT binary patch literal 663 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDq2jfr-)6#WBQ# z_w7_${pLWCi%`ktyhLTq7Cf@0h9a@g2bUsYh6EW5E|$>x*n8>+hX9Xxn$2y#y9 za@o_e;{N`<&p+Da%C9xKJLk#sdDZVM|Nq{RpPQA{@bvS~3g(pVNgeqItDMywDnwH@ zn>ikuvtrs4zGG?$47+0Q>Aq!sQ#o&XaL*pgS*ky81aAEKRG(GyMyvmX+5=Oa_vJ_@ z8E{Vf-tykn%lHPfwZiUpQHjf9u`%Z?aZj9xuMI{2J` z$A-JUFCKauOW!ik{+E3^)q3%>=SNsrXDpqjdg+(DUSNQ~-A=1_n+^$G-F$N4_1y5HU7S30*ZTDo{$t#sHKbEc?O_VFgd3%2o>crGgTe~DWM4fug4Dv literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/menu_premium_main.png b/TMessagesProj/src/main/res/drawable-mdpi/menu_premium_main.png new file mode 100644 index 0000000000000000000000000000000000000000..0f578be0d3cc2dd108b56a07cb416475ebd9e525 GIT binary patch literal 728 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDq2jfyv3!#WBQ# zck9%H-Xet}$IFAR2#9XZjK?)SRJkKdhJ@#Vqe>iysMz3(;tJo|5|(f^xGTRwLm zO%lGfds#eR(}VC^_grKT&wi-9BUfOHirF$z1D*8A?H`@Ro?rTQ{i>l(gZ_p`PDiJC z|LxfOeMlfZs)019w{Db%%E|&WV!uOoov+ruBn`SNv?Vp|2yTk0T z5`TArY?OAZG25%HlJ7sZXI2=?QSsM|+kiKa}p*8ejvh&uk96+ zw|@J}@S1h~)sNLzqAtF>a&d|`Pd@wGmz^#fIWP1z-;1x_^E}Ar;4eOt!zFu;HJAyW z-yGM(_Th8WhMcQ<7ra+8WG}mDcJ2G8maqE)*37y7arJ_Eax3yp^bYJ=-LZAEU1QF< zfHg7k9^21Z{j61-)uepEZx-v#a0wn>=`C}zF7-53EsF`htbB=mGq2Ix*UuMxwn%zX z(z8mf*T?p*VBlI-r@2Yt3V-ITKDE^F?NR^jtHM1z{xRP&NNf^nSfUC_Ri3VXF6*2U Fng9}eKiU8Q literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-mdpi/msg_ton.png b/TMessagesProj/src/main/res/drawable-mdpi/msg_ton.png new file mode 100644 index 0000000000000000000000000000000000000000..eee393e9f390b447317fdfcae4b3798488aa9246 GIT binary patch literal 746 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjjKx9jP7LeL$-D$|Tv8)E(|mmy zw18|52FCVG1{RPKAeI7R1_tH@j10^`nh_+nfC(-uv49!D1}S`GTDq2jfhok(#WBQ# zck5JZe~~~52U!&}hx8g(E`y1ajz}aWs@t|+6$>#D5$SU4*=S~H@`r=lyS{-lTd7lx zC1c@&#)kewZi*LARGfRWck=5ueAXg0CvU#5eQ)!wW_E0bLgl>vpfc&amQT!I{HCQ= zAGF_{bWt&)$#;ct@im^gne0+g1sq<|atCWA_%o!9mq*-wwVTtGc>~WW_ZhFQr}(aL z72V6c)pp^5L!vt(q9Z&bq^kmc7OCDX^!yPqW$HXu-llJ2+9C4dj`1tbUp#*4$EnrT zx1_HXtbKHpzikog7t;I#6J$v$-4kh`mO>4WqQjJpd?vQ&jO**5)U zwVUF5P~jQ-$}`vML}DhEf0Q|vVE1KN7UyyIuPVG{H{Te=zB#J#!b?cuyWGCU@I6vH zI_;XwO>O&xo;H?0=ni>T7IJ-kc$MTlwVB~JB=>kEt(3feOS9?Cm-~jk+FMWVZ=Jt^ z?R5cD-lgCZTN5|?JhCX7y3%Nd#F6}i`8V3-KZv?bnsiC!-G|^kpZgwskrlmi#4`DF z%o|_RkLv;hpLx!+dUJyRP;y0tStw7@N2RE#A;*_0{1cD3QSl;jw$8oknBAd#-PsC-Cl=`1_ZiIIeeEW_R73|IPh&|I+mv8icPeeDNiOFL8F^BN5xse~kPg VZ`ifIulNi~XP&NpF6*2UngAhMKED6} literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xhdpi/large_locked_post.png b/TMessagesProj/src/main/res/drawable-xhdpi/large_locked_post.png new file mode 100644 index 0000000000000000000000000000000000000000..a7e1a3a30d6c5d14c6282658194dae63c80ba0b9 GIT binary patch literal 1386 zcmZWpX*kpg9RANiwvu={5NXEWv3TS*i)18bWEi(WC^KUmg@jngVBM<4tYh?yk!+a} ztsId?B)M`IO+-mCj><7KXm)(q-4FZWy?(#<^P7pcw~^c{zZU=iNi4?7Nu>B)NQjI0 zhV;sPkw7AxY%GE5eua5aup)V2$v7OKA@ULcBs~y-?^Z;R7XbiZOb7rIDP;G?g#P=* zGhzSv-JCN3m9Dmurxv60Z!LqwI@-iPh<(3B$8j?$Y)&A|15LFd%YPW(BFz zg2CDzKd90~J0!)J@BuBc$G!K3Pvp0ph}KW=eHlg`;0EyDEtm$>6o2OkqWM0Bq485+ zs^a-33yaRKBr-gQDy;*xekvggQTE#n_Y9JFwo*mJmgeT>{cK8_sMzB0$wCRb>BTGFnzvo}t@t(G~)#zCMf%U?nU{Lpf#mJzCOHV=A zU2_5pom~y{Il0!<;bLkt+4TslJeJyDI48_(W`yr}#r@K)n1S_KW*y5{#k|FJ)sISV zY~=|89hWdwCj}~UCLs}A;#b51x^U;tgJ{phwnnu|!|ITqALkif6Zl;J)}+TC@Y~9r zOD!U*ktO95^Znx(y*&N#1W+)P8`5U%7st7}xih_B94O4vGRHpH2Z@Y^N4|6{Xy}?P z-{T);yP$qU1-Vnlz7i%}^z3EkoGo5RSHI71C$Rd|ADpd0oDv`~zR;7)}pOU;4 zHN^%EquA>DR7Pky#DL0xn>~KJe+@p~_Av3`B;jUv!`7{$H0R2VNv9H-nO_fZS|2=F z*Z9+X@nW*lb?vg2B_t$gZ;2ad0`;MCKH;hGUq2^kCd;ybq%p?ya`^>1T0;<X6>5pf8$-QPS>^XOR>qu%=jGN-8XFrf8*&`$L8;0?L z0~YtkIWv@i+=q$^!Ts&Xv4r$8s98tfR^v$DUs|X8H!D0D?+{R$B6%!A8{OYt8>$Lk zo0=VtOJS5lP1HtaH!Z*h5 zG8mhjN9VaWnVo!Z+Q)i3=ia%&&f?VT>CX$**-}#pG0;*Z8x*fWN0gIR@P2C6j?dfiYw5QmN)avuE(AM?La zha7wPrtRD-Mvn8MR1L3R(}V6q(BY6 zMJp~3{Emxxx+UV<+JPhgX^zlTDHF`JLK|h&U;_#!K=1vwxZpO lB3FX`zbF5U%bPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NH%}GQ-RA>d|np=pKRTRf(L~F<_ z%e>GH@}+{x>P?ECVy1*HkP%V%V5W8hM=8sPAgJdav>pqb2!bBcpvW{r$DpSo3JRe~ z2Rx(Q6AH5C^!q#CIw3wby^Gz0bGru>1*ja^RT?gVSV72slU5itL4^522u;A`+M*h{BQkp4SDqZfj0;3F^% z>dK!G#N7%O{$sr$*nIE^m;x#Joyubclut>E%FSSYUpqp8+rU>K6g6-kcnqupOMwla z0p$zb^`x?SAH+KKCHj%RbOOgu1ATTc6BO2i^XmqMv$)`XL+V8_w%7GnYLBxa@S2Z6 z{(CSA&MqZpR>%j?JdjT>V-$um-Neo00E~OD``3Adym$61a3m!D)y=-aZr~!$f8lGOBs^tczRvSNIch+)H7~@03;y!S0T|OND zCDfMTEFT!^Gy0`cW4_U_f%h4Z9Lx&tjNqspeoknKt!@WK+N4ahGBld7@U#G!OuM#a zXCdng`qgPul8mvA2UEagG)ah5LR_@;PNXZ^5mnB>6DzVi*;WxYT9~DC#u1wZDKb2WX>x`I@>MO6TbXpi-tFLErILf1T z1+Y`4&MDc)18jBn@ncnf-T)7RHDC+)9C#UzHYB2iq+^GjowOq9_&!%qJ`7Cu&82`uLtD;-N?YB=$`?) z$c#}w71|}rZ@IeLU4AA@*FLrGk*?}8NGPy>&T*B!H(}nW3ZFN)7@u@Gq4K0NSlVTf zP{3R3xXS(rHi7dJv|3g8S_Vw!UPaMreEJF8xs2pQDfr+P8}7#1IIcQH8c2G z1fB&tx1{70m44C4iUg;$13e9)SagD21+)RZ5(K7rMzon-5aY&l*vX>bbPLE|K zEz9^0=*7(BCd=fc{4zVR`vhh{Prgu*zuDDy+Uh%?=RT9WEt8q9#ZF^# zHBcvREa7bo^j`$!RPioX`HsuaWC^M_Wz7&SUh!_zAE9>^8#2e(Ap8dYJt!8sm}8cB(f9+*sYiO9atV5^-{ zuXHCsO7tULS=xx80$W{Zjyi|Q3yrRnEoZzn@K;%ctM&RiptF#D_Fu@a1D97i2n^u( zE9G&@>Ej3nn}LZgDN6#M*+sa&6^qv&o9B%UutR#yq6TX5Vd8vmJ7OF}SM_lPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NHkx4{BRA>e5nMsUPMHt7Y$)FHp zT!;#&7?v?n6ihq_8kgWk0_YJqxI|Bi>(P^liN=EhE+-G_!Ker079%ijF@g&R#LGY& zW>Ab8h%CW{!5Q1%zj@!|o9e3W_qwMqroZH$`s%Cyx4i0lRrO|idb-m28K_pPr-AvM z&(whoS@GLx{{^!1vTW`$R zj*Uc9@Sg|H0jFoNj?x|ggOy6977y~KMm^kZ! zd?trgU|S0Q0ij$Ka@*dLWh!yYv_%n!poc=4|lF~t)1O8LNHqcCdhVX;KCKo2ILY@KoffudE z7eGFPaqs3fL7$oh8c)B7YqfFwXPR5bSyDQEG?&2WEiK~`%{VKV^BqI2Gg_g zG1_Oq6YS&1Gh5pYc}^k7GLY{?=sG);>kJT+!x*ooj)B-<4?!1#L%`$@lv|?Y21a~z zKFrEcx8FB#RSjQ*nCQJ*lJsTpOt8m!{*JyV=@1OS)eFBGXq)wik#yNsf#|Ppp%|Y6 z^q$4!ZsXOpu$BA+=cNPcvN~eI0QY?GmGe)j+Hqzk!f5udO*pPg`PA$(p9;H+rq z;0EEU_ap8qYr`oF~>7CdXdXZfTg|IpMq=i!6piR`sMu0>)~}Tnq#ig z!A77l6a5l(%L$vm|BI{Z#<;_g{=MMji%;iKU^0ZF-Jy`c)tWa^f!bA~!q{@)A7Yg! zGNyCEZy>wrpB-?PVH*XHhF*FO=Tgqlp4nTczJ^@@129g|7qBMB8G%#iHMr4IHDrCX9hipO}AV z95sEDAkB5e9UKs&uHQ_Z0l^;lAihJ*9N_GMb137f`1734e)JJ=C%moBUQ@H$b~yVS zC9gU|J8Inv*&tYyOM!1QtMAh4aN<$qRJiAk(2_q5^ej<(q?apqcl@Z>2weJ-fJ}3inB1@+->8gG^)$ z585cL65SRCN%i5*1p7cJx<>ee$;N*|(GFHf;NA}W!>_4xgrFf>E)+OTfQ3bgP?z{b8Yn$hZ(dyVDxr*CXB9E%a5|OIwRvi#;Fz z*RohNxhU8Dt;UV|5wH(@24b=c<8wfN=h2_nqoOfzGguGwu@aL{G4@3ROoWl(*8^>d zDbazWj}g5un=)u!%jORwy5mGKubYS(3&kMWBJd{ghYa0Pav1g-U}FFFU+4)k63MmS zF9BK>17HlKL|dacT5j^mzsAbs^xVRyqIYVfQPmM8x--z7f&a-2 Z{0BJVH_6rLIj#Tz002ovPDHLkV1kF$Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NH3rR#lRA>e5nQMquRTRg)qoqh_ zYT-!5D1kv1iH1oJXF>>~qz^tmszne4ra>kVl%yYpK2&A}1wA052!bp!8g&rgLi7?U zBn#1O$V^A`(e(RY&YD@X&)MgkJ9E2pKg@zZd+oK?jV}jsx@K?y(^-s&Ec^C&wAos2#`wjYC zU;+5e+20AmnxhNBY2bn*I@g$Co9FERptsss)3lI!!I9h<5@W2(jjs>KhqVu}vEYOw zxHu%l*itus9R14BXW06XS>ee2f<8JVgKZ2r>im|6KEu|B)FDUg=@=1w);Yg}F~5e| zA#{%;rS*SOi~wVkL67slJLcOkJ7hj|gtinF#`iVn|3Q&|(RiiE!Nw^%4t+|3Ym!#W z;fuiT`B{}p<$L;3A@7~gKqFg(sq;QJbIy|E`S0viP9?44yFPlJsv$z zwo&xHJO>-2#+T>|tsiN7u_T}6%mHoSs(#PLXsVgi76)0CiGHesxl;Y^}8Q*#zF7ZXCdy|gO!3m2icCzCf&Y-#Lw8YT@cE#OrnM|&Ve1>;KR zAaxs+bM-$+rWlQg`UUcS}7Y1RQ0PX;ffK5O@w~o{{ z38jt%J}YVgGx*UmxK=MN+9&kT|LW|aDgwHgQpXDs!gn0-@6~jx zlQFxHaFzqXnq58pHk+u0@)I7q5%$d=#YPgy8tcW^*hRt zOdFEPF_iixcNb4y?H?woJ;%BBsnyeHUoWoQ>xRDIWkM&`|3f-mQeV@V2dZT+pu>f2 zcY3bMRO**2HIqpqpSvSBdD%e-=!=@pLnduSu{h2K+JsDYpcI~o@M*IETGbp=RI6~D zB=cR;BY6S%O##}u{W-?M^%B1s)2k|#+EEUHSAd`0&*?Vq0PllPbb@^_tb%P3&_OR0<+xt{wAybvfyonLo|^k) zng;wm{T=;JfDUk8^e5gkz&;M7YH2L-k*^o!QcmSI(KFMUN-`J&iJr2ok@h|8rH{|@ z1&Ya&b%Ws=jiHiGojQG*=p8{z<)A;2D;9JCFM45G9VHSB)O2Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91FrWhf1ONa40RR91FaQ7m0NXcg3;+NIDM>^@RA>d|noVe3MHI&qKY}r8 z6(h!)+Ah?lN!_U%TSRoBxKJ^IMN|+sejt8e6QQ(e6t#_AxTpwXr5nXmOKYo8De9tH zYD7fDB2Ue29&@4NTDyhOwU|2Jo5{^y*TxifQTZqm~7AD7l< zwSRJQ@*;fQ04@R2=Le9q9CjE--`(2U`lBlTRiV*6U=ZvDCqYpWS2nWkne$E%bSZcj zG)s2kXlWxIDK&)%vVb7N;2v;F6!35IAA&LP1F&g>TAfasR#$@S!0EutNiv(kYNprs zzM?5QjlTo@0lb_bGYakm=M)VDcV4iOt$cVK|a8C z;6<^9K&cs06AA}sg13N|ugP3fL!My)K9>Rc_OgXcdqb`<)X2w;*J3L?RmujIZxC+| z>O}b`fsv+-m-S@gg~VWfJGOf_C}$#10?6x;;VCp=y7qoFKh|SGwh^Ht?2_c?vA5 z7B)xG@nMLVdt-OR?4|5WaB8UQf%i><2T3PGlhH(F$WYk{jv8qe(j)ksml0d<1Cs&c z_}%4JDrJOzhcS~iR_;?OwN3tPB+`|>KGvy!-*N9!y-guq15L(E$w)^i*GNbf zfd`~CDB27z2)5smzL?|3w{4-g)&X5KOmq>+6dv1ADxYzLRg6mMmmM}5v`3wvL45z^ z@@c9rN+z22)eclLp~jPp9H%c+a4-~hl0F#|3gn-Ib4K8OO8RC28$Y;}K`;FT6oipF zPO{1ce2fw8a{0O{+^Zn0aHPlasfC#*nH|V8Osju`!Y@Ka2Wjd;O*5oy1JI5qd7H+6 zJf-ITnvIB+4%-u`=~wxcNo~^ZC2ZA+GUD4d-DaG30lPvTN7s>| zu)x0*$j;WaF4Zrj#_c)y}h%y=xpnI#yR#Qm7a{8Xg zv``j%f1Iv8GeOXoW|I>rOA2{*By9Yr7@h57(KNl=+&$>LN)YIhogbbBhP zP9ZjD1An@7?kei*b<8uXuzbfkN?a)uMv&r5y{?a?1RsyMU|u~&W??C3kg@TXdOd}{ zx;`Lm0w$$Dt63NP4FJ;ZT0of`)Cf_OpegV;x4 z4Cuqa;Q*4vpILPzz82WQO!blpuo>vjtcO%EYeGl*zz)zXk+1r1*xBd}K^KDo@Ck_5 z2t}Eq^So?Ug7kw~!3LL_LiK#o5Pep;9_YNIYpJe(b_KNQq;wqUpFNW2c?$oR@(;sc VsPU})A$m7svmzFvgOxj==E>P zMg{+88$yNr*Z&R*e755N0IUpaVq_ZvTFUWxDrqOvwp{D0>~5PmeVJOEj%pFnyz3H2 zmx*&HuPLwP%O?orQ>mK>6Pbs-*`Hu43)ZglF$q^pt&9<#z1beh>W_G%VI)Pz+gm#U z_FNZkH|1kamR8_Q=+yhHtX0Y?Wp;N`%YYb6d{N%TmidD}33Rpje}=pX|I4!%&g+SY zh*%cmh1DTx%%Z17bFx5;)yA1=7Kg*hJN7Pt4HR?(DdZ)poy~#T9ILMfPw45GWC^%{ z+b|_G^uTcI`A3AJBC&kfpP+p)|5!$pE$1psJ7ra4wxKIx?W2-cjXmPjj0+HG*dkx( z2N&==6=M!F1ynE_ql)XedEk12-fM5@V^S8BX(JzE(DSG**2_yfMQJcnjyV=jkR^-V zItcfLIMh9y^BrA;IPP}-quwOI9>68mL3YcW`)x9>Ln z^=Nfga+2|NgT)tDv2MTAM2RS_oL`69rV&j`Zv)Z8G5MzQ^ zO=^7Q8Xy?*Clulun^BQb_TOWckvDjX2u9u}+jr=xA5|uxuB6>KN#)0s%;i3{4v}#y z+0Ns!PnSjDJ4YXA=7-h|-jm8y3EWq@z;&lYARV%-ovo7sdhs@bk9~FbR;PkqpYE9F zWu$HEm)?s1baDvC>IoiG5L*0!+Vb-uawd9|?swro9-++<+{w zM`ZTBL-DGQOM2xHT%86ux`xMw-3TPWC&4yWeum{7&$)Nn5wA; z($!B|3fl>l8Lx)lYe*{VKwrzik3^TinoO%-s5g|0sIqgx+M;vq8nfW%Yfw z4Ju}SGd}!UVatjOx(tt8zo}{VXWdni(34M)dzYg6DGIm^R0!6kC=yXWdKqc!K?zL{ zOv)ALr|jr2NJsoul~yc6k2*$&SVSZ-hgCKa9rjD{(QhaP29d#KgGek#E0gD{M9>Z> zPCWbu?&>B!SZVIcMUxHJq$u?bv$6Aub2720e(&FkX!w&OPHJ?TTQH1yZD z5a?O*^}@bWX1c#Q~(@9oZglTS8)X?SQCO_<9aKJ1Et_l4vy-LpHrYj1ucf zq~5f*C#Sqw1GjFRv}6?`S-4F}-2RsLZf2r)77iRMe*U}(CR(KCdxxYwf3dPqB3#JM z*`8dYS$=br-NjMr;S1%n(83Y;q+ttUgjOP{wLTkAM_$iLF8tWtrRpz=tul?)#<+qK zv^S!=nB(}i`{%=#uig?lrgqVzv7MBfzi>EM8pS%_SrtvE2T6r@2wd>k1wv#LT`0Yi zg4?+e&&of5fEuF-9dpbXmm9P${P!@$LQr>bd>-whfY#~cl)Tabc4T@v=SV>Ey8hik z;i#8QuH2KhWKk45ZA!}FuLF0(seip%%SH)S7&s227MpYiH*$jeN4+oCdlon9AH3Hg zad5Z>vy@QoyAFo4j3s_TT6YT029XZcY1ppzK!2U)3-;kJ!CbL-o(nC(RStQ<&XMFs zg(p5HIc1(bhQvWLv zyLH6J|10_Aa;)--LcAEfykw&ZwBsf% z+$ps*9kK-)%^K(`A%ET@7$x+2)fAjfxb5H%B2{Grifv zXkKE)?fVt6CC_&BGVzAe*Gg-HG{DQehk1|pSC|yut>q3#1wEyWXKac)6y27=!mVx}=z?_r$ ZDOZzf2CFdc_xk<4vFC6mbYr(W{{mRs(dYmG literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_feature_paid.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_feature_paid.png new file mode 100644 index 0000000000000000000000000000000000000000..18f3b00bd377cb9ae7399911346ec3fb057d98a3 GIT binary patch literal 2400 zcmV-m37__fP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS@%1J~)RCodHoLz`kMHt6jwJ-Cd zGEK~Gqz~KN3=JdoBA22fipbKvX(0%Nh=uB|gbhVD7WJZw#9~FF3ysK%rpPznP=wAJ7?ysBSs9%a0JRk zprK4MgP< z+g=0ZZ4Sb*1@zonFf&6Bo<(OD*c(7|N;old8%@}nvu+A7i|fT8P4=Mh6!X*A&Ph<1i@v4slO`sbj$$u1V0tG7}4NW!4z9M@xYfc!aw7L5eM@(IW{D{6z^ISE4jTDf}UYx1Wg_&Dg<@(svGD{70M zUpI)f&e6}M74^=L4NllQ@)5X3lG;>$odii2u%aID{#e0z3wQ}U7BCOca=4kulecP@*TJqlS|WU$21_(=-ChAqNTj| z6*8QcgWJItU}ZKL7sXsFeGA`QU|&nr*6gDQH8LEEgQM-&`-!L_4kXQlvAu@P9=l&Nxx z5~NpwpDcOKv^q~GA*BQkZIxI_-{Yo+ddbSdQr-lWA$`XZoAke=e*?C6jau*9W&MFr zdsJ3BQhBLiPam{57$v(9#{{~0hg3oOhTwlH?=J;`>F;A z&1rG%;J&kJrHqAet#)uL>$z$al-~u8Nzusb)#_y_gHPMHR!BKxYdCMI6^~|zu)Hef>L;)glT3gr;33RaMOTJAYT8`}x z!2KZ0!C(SS;cVN+$w4eCgi(M<=@S$yhipcP^sChENvMA>`R{|HIJXTbX!n6D67+p$ zyV^J~7C18#SXt!L6UZO322#>m18MGB3B0`O*!IA?2v~`Z&*LLVA23-tX*0^C`N`fw z{fEKvj69maXM^X#URt4FvFRD>W>s^wF^WYzKv@Y(=@F!NA-E0b{q9UoG6nY=g4CR| zUSaikfT~8aumwj&g46?ADAJF@<6tv50ilr|EiiCL60=tQF>rW?g#Py!S>LXiFh zZ-7U@HRNlac>=5lzk(#0M8TS*!kqb40~B%5BjbUU7b1l^-{8nda5vB+<&v{x`q&#< z<{*!@Q=$<>59(`DJIwnnn(7%N{5c1Yn;fVqiu-ZkF zt(@0*KYe;?61c2iW=aBQ|3RfQLCr@8+BWuES=#{pXdIbEgT|P=+(|gJ_p*_S8liu1 zzXpuTL3A`hSD*3~7JN|KW*LI20orbqwe+8F@`@zqQE5p~QIEzm;4K`yG{{T4=rME2 z0as>@Jhe@2jEjvyUl|V9WWx+zO|~xCPG;44g!1dbF-K*+me?ntuQ3_!sGquFHh_jo z-nU%g)V_a7~!e2xrw^KZEZ&edeo9)}(TX`At= z^!}xPQsgk@cIP2Scq*Ns4zbdaN-v5VHy zL4sT(*G?a@ximL9l{AWWtG1C^eL(f3vGY}Kr&B7x@9zUD*@AEZaLo^kQ+&x?jGc)f zoV@j>uKgl!0-xVc@IucZ?gCa$lQAdM@pXms5-=Z3^~EV3`3mH-m2w|(2IOO5)hDx7 z4v;a$XG3biB_Dx&wW2na{|q3Sl38$J#KPL3$eMOZoRIu8QA7BK;&y}M~-bM1~n%o4P(9B z>V3z{3$C>tPs>JND`}TNKGtT=i96!D$FP3g^mCaca$4I8dV}}&9gw&JPH`s%hN6mZ zet)f8fvA>;6Iv&Afuu){qKfF{vlCoUe-`euSG9@t3dqu9I*r%`x`B?G4gnqahDW~R zxEHPkI+oG#j!qx!20K{;WDhw*=RX#RyT|Cz3$Atn_JewhY|3`_zqyDzU(b6v30i?` zYk_R9A{(+b^aJDS1RU2McL%ViC1e#Aaf!3hWz%P#w>c<_Q#5EAdd)xwb~gjfFOxwv z(P^yaDQ%$XP_M54!BSlVMB@esLDQEM`X(@(n57d=*n|FGbjg`v8IC~l2>cIhG~-!C SL76H50000Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS?oJmAMRCodHn_GxhRTRf(yd@1; znRrRD3#NslghV1eC}bLmVS;ENMS?+=5ZFsnLXaa2@=}?l zmZWK-AxULwUZ&rFI%nLoXYF(SzM1KpZ^56l_qwk&=j?ONKHoGn)T!QqdIt)0prxf{ z7;Ct{@V`JlSj+uy9Q&D^yGzJWI z1^AuECdt0wA+Q!41Z7D+)&TkJ8>$4^IB*6e(e#jg)%j#K38*QjlQJwR+lT!?zZiHU zz%MDjb=lqM+g5c>z}{dk*a@mlzY}1bUl6?O!M7QL>#|LyimgE;G>2#}1NR{;ErAm# zDAA)JUUy^@uSX*p%il0D9Xii=7VFVIhu1(3{g{6UIyBDJi^jVRUgcRNH4*FrSx9;- zzzh&C$f-%8tFuBjvenr++c$#O2;Ko%=_TKXH4m-smTo&9S?KJ09=rgO&cyx5-vS%? ziyz@w6(XNkaJ)2!^JC~P1E&m|Gw^niZ;K&IXOYYpup1=N9)z<&;XgupCjV&=8xPJU z7*%It}t0Yb74w9fv<6 zbG9fC0*3<5w6~Ia_GJx*FFk>46J5_#g2ttHJMnQ3$U=Xiba&Q#Tc1cV_#)tWd1glm ziumpa7J$t_3Hl_SWYWvQ(i3K z=n2a(;_cyht>pv0eP>260bB+yY0oA5tQg4e-~b&6YlTj{u(*TDsz)c&D$- zd{LV|lMg%vJ*4S7b}7P#o{-OazPA>2iXSc9E-B9rXE{9gY>@tnq?pM21Nm}kqio{S ze7xtNnHRS695}qRfk}Z`*`_#qh~}ZFmo%b`FCS)!OVSv@XCJ}W5rdbNW!YLW-o@c1@F{qV24vYf^=oeJN!*~U33jz!rWa%yvW7$laC{J4D3f&D zAboyuDbF5?Yv+%D^%=}>F?d;7+MDvpiqSx0&iRTdbHC=+p2Q9M+B2TYYE^oX#^)(; z*7H5xo9CJOw(@;4j{{3Ls&%R-YwbzgpwB#^7Qe(NRZriX@>jf!V7n*O_pL&=dHl@q zd~GMj6^~bBs#;aEOILeB!Zr1v=vJ9FrglAw=%Lu9_7943g4BA|SezChO^)9#dDTeE zE0&X=UW|Qe|EeKnG-~Q)HEuxCk(%`@5WSGQq!;#%FRM|LYG%75sWDNly0o;F<%l^+f}Y-1!QoFJqQ z{VHV@ut+sppRN-;Mj{MHg2R?qc-{Wfv&taP8^Fdu4XVw0ze{gUtRV*=)TCp`9*A|j z+mYO+1LbH@`R2q0<6Ym%yWP)U;{5z0xi*2-AVjKVsBc$ljza~Fcm1uvKf;_+Do6#4 zl>@N8qQp6?2LHvh0+DfnV=Z<|K)3k`UR)VDO#{s!M9NvYXWDd&{T4bBq+9%mQ^kVW zU=K(l{or&=p<;^~5wH(<5$JnU5@{=8DJZj(*e*@^ihS=CQZ^7!d$Qu zbT%obVpr@5ysEt<(Mez}u(nTNSA^tSG48EaF%`SSLJ5+5Uu60kF$+uq{^u1~xkw-Q zb^v|C-o~YLkz>`U>J9QUr(wh4O$Jr}mrOr)eO~WCy#w_Q)H_h`K)nO?4%9nP??Al+ coz;PV0bPw<76fA9EdT%j07*qoM6N<$f-LLMvH$=8 literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable-xxhdpi/menu_premium_main.png b/TMessagesProj/src/main/res/drawable-xxhdpi/menu_premium_main.png new file mode 100644 index 0000000000000000000000000000000000000000..f4a96b087ac0df6abcc214fd2bad32ebdade18fd GIT binary patch literal 2101 zcmV-52+H?~P)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS?pGibPRCodHn|X*;RUF41HMcZN zTZBwZEtg7L{xL8aNl8UW|0rzF)M%rEc3OxQgeWRZ$yBt(7DA}BEEH$-k0_$Z!YtQv z3n?wd7Bihb-|1aB?>Fb%x%a(yXXeekAAGsz{LXKGcRBam^V+qmP^AKu3REi4d=;o_ zKG*r4>+0&xf-?XprY%)fRh#l9+F*~Ov9m-KN@hP6NshOf z(_d~K!t-5%n)>y!8jPk~0*M3EWcky~NqT2-Hf2ZN&~@!C+5$kiHI=?)Ibmpz~4 z=9q-lof1k+V;=cAA^EcAlW0$H&?LN#euq$E8UvDXNj7?hbV|){UuuIf_GU08^R~ik${)9}FpC=}naRM?+@w}f zoNJZoPfn`e;^j!D#w4hx(U7EpOBP7SrJAJrEn1F@hPzqi+8?J48of_Ec7~_5)8gbw zYN02wiD?7H#zc>ug=y`y7sps_tc{^xfJ?jR=;_reiQIaFAKip${|*|y z=fOP12&(&7tE`NZ6gl4K$@|z@6`H9r-<5U=H+ zLvC7zsR1tiL#LBBeg=GBI^Tfb*{e{-Omfn6VF9qTI+zxcms#mt|8tI~2Nla~fLwIH zU6aer9^%)Tkbd22)(Z>r$aBLQkxjMft+Ogjq@wI-oak z1-ls>0U>%gWG@XL;{(C^fRjD&N2ReBl*Q1gU~Rxr#MVuGHCP1R0a`=e3hZJ#tRQKc zuhYF-Fqnh`*w9XqeZWUw!aO!xPG~Y-41=0Cya3W9jCi9Ydo@63Sl9R{l8s{LF7Pk# z(c3Uf;>Q~e#zz6){vKa2aXj5?o&p2CjtXF6OE=>Mz(=~IA1c6XdKDaX26uq3KpN6C z;axBRxbK1KgYalz^LF5){V*qaRI+E`So?V5rUVN38*n$!dQ4`7{Z+t{cF+#Z%xMzC z1n{PfD%by-W9n~UNqqWb@U_y!aghE=KG`PY5x`UvRMdTzM&6Qc)YbCF94+unp1Cr>3a z$y$%)Y7Z|FtC#|fF&+-lUu$^H(ftgkip8aBx6>qTPFwqizotnXCvZY+HNd4E3DmqI z1)V{7)-`S4hKt$~ht1Q#O<V*^e)yBxKk{7~f5T^r-V|5dK!$(ekM|d6_25Ct; z(oOU?1mtvyS}J%x(@h~3~3Jq;>D3{#(gX}&!apw@{!pQr@mk2Uh5FK zK43983PQAnf#BXijRDDoNVesp!TTS)dpt_9XS&UypgvCZ8OJ^$^pbi5(89hpB|QKR zfDq|1{s?GvQv@=a21wSO2GGqT`MMwU0v*Biz!ziDUrQ1d4d?TKkMxTk@eMdUeO2h2 z$dMx?9U$E}lJC$N`-)>O+a^Kim)Z~)sb_2*ywSx*CL2Ju($GePGhGjFkQWa7fn5or z)QaP6phE*A1u|qSXVbTu$xt5YgXWn9x++c$$Nj+vz;{~foBoSH+i)`>Z0mj}AM#~M zF)af{6(@EskuV546|#JFtrFwAwbDIpJShe8+-eK-=fGftC2zu|ky!R4P!Z fK&1k$wF3VEdMV-Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91NT34%1ONa40RR91NB{r;0FW_8?*IS@?MXyIRCodHoLi_BRT#&QQQ1YU z)UYD)6q+Dn_z;052P`NG3Ze%gdhK^fgdG(+E`M8-_GdlZ+~;$YwvSRKln4VzV+SKH#2M2thM&;=(s?c85opV+lI=% zzP_t)b2S(mL0*qX=p8g@&@nOki`tqXk};NsP6M}t@exLH%%Y=IeidOm$1NmnUzWl- zRYy{$fHmL%2t~3bn^VrIWW>P|_%JXJ>;tVP`H-(+vHt;kB*+n93FrmwreAR=-y=dM zC`cbfP!|!@EU*j=FA(4#$m|Aj-*SlaxMP-j++knOdE8e`;`|C#=Fl(H&j)Mit4`&} zw!WI=Oa$9O9_>M35ts@tEgPA1BHcwCWh;*q)5NlVg31#>H~1qX(%-3D1tte|j4|1a zY|BR)DYluRedpT4>0q!SEh_lOK=A&+3k*MTzbBt*q*w=g`oVcP(i^G=q|rtyt_$us z$Dn)y`A(xADq8!d!@(%96L@q4=G{5%XnD0_0E)#U#XhPfL10*SPFiwyQ`gxN#}$k! z7ND3sQX9hFNf=b0(*r!(40A|@cC=JQF#*NqQ4h>;eGX6i-2r>&*hVW>;5}|?TeZO?iWdQIgsjeQ z!1XycDYmSbf!A@Xjrvq7M<~q!mh}4UEN5xpNGo>W4FTqqiV`Clo&F3g_4_!dR6`tL z8l-WOCAGEqEC9m#Ot7@k@pn$LXqY$RUQ(zrC~H92YqF#7oVI^H6biV4klF(5x0~AB zFo3++vHv(cO{6DjHFGP6lZ>=;oQ$FFrnLGN^82v;BXNG4=R<8F&eg~*NXV+qYWs== zukjQ`J58$Tgxu#7nF(Mo=x^FZ023NpR?k63{j8lT$zBXVK@;qjR@IwsETM22_`xdn zWBF^v)EH?!2bon)jCBhE_Wl28y@^_By~DGtdtuyYnf+KEhwnQI_+RhH)aAe?=zgQH zm*;hd;dwxMa(@-D;kib}*Lkeuh%Wc{a}u)|Xulb;)-l?#$r-ygx8?L3cD9fK|l-FF@Rr(I#g zM6b)y?J{C+5xo_T?pv;~!$en$2$p6yh$6AhNS&aZyebVkNd~gxz~9EkQOdcuIx<~G zPsQ8>Au|S?1W8idHzZ@rI!9KkCDKXKx>zUK2WkB&NxQkt{z@{k&XN6w`n5(z!-E$L zFJy5!#_Zc_C9xbV>LhBT*65OCb5UDHu^(~klNv?Nxda-a+OdVMjurq@iTW;JDf13O z_%?9LC>^AHWvZi;3Ut;RJ4a&C+8sotMv-=xg;qUF5m)0d3a=R@t%km2cp-|fV#Ll# zEhL5$6^z@qj*N2B5kF$yYx+X+wh7Oy|&hJ z5+_J&;g*hDMv*dC@HgaMHnJK{sK02MNX{h~dBrjGEWY)6uR>m1YdHy?peVXE9QT_@ zAEI&EIMpmYfdP%#lJp_T`8dfad7hh~lJ>I2307~v1&)+=+wpGK4)G`GAf?uCaBjjoH=%J{sC9ry|NZ zGG1HjbHWvBdZ6e9fn5#gEbWIeIF{$U%L<5>sQ2cnH2-vYEYr*pJJ|!p$haLWjc>+j ztjiVtobyd_IXaHvXdf3NQdiWV(9(QtpR~-1W!qMpi`u6~Qy;5YfpLY>K4dk5@6g=> zEYP12cMB;O`mk`mGRG>2D}wHQVFoivvucpYTy<4I0B<3e=&faGj7yk?4b zIm%uxYPY)G4Zp`xuFN7Ci-Dy#VRTt0y+~Q4)2YUE;GfkKiToICRGVx?{&H=RiQ!!KAv)d+UD^7F+AN( zc1or~1ZP#Sm%Hq9RysOEsnWt>gP{}#_Vw2I4wR+L4n4(PDjVnJ7Y=0&#d%KT#TNEc z!>O;p{v(c$EY|l_>WvnRu;*>in3wAS#g=bgz=w8x?Epn-dT!t)1aPXU;bX?Rehf3L zkQoHM>~1WiTu=xf_m}|Fil0#tWKbA~hP~?BUt|OWZP`GDpHDy@6g%S)QF57Hhts%oRuw(qINuRb5V5wcaEE1t1fHzK3l0@1oq&b&hlWwtp*^F%4{YbFSkYekO zb^7H<02)ksKbtxeWptvc%|&1;unT~>u}de?UBuB&lfr7CAb%_nWDG-Sn*5&ymI1qV z?lu1W`D=<~>q(Sm0W?+83))TcDc>WKF%8Ibg4CN%Gn01u7FKYQ5BX}(8!B|h30w;S zS}n0lO0X)EY{}-7(D7P(LHZzTG_6d7v@=YLky_2v`j(KlQlb5hS}PZ{;@Q@}=xS?% zXj}u4G_B8T!=(1;#QSu#p-brZKY1gD3zU_C{{WeB(am2@!fF5j002ovPDHLkV1m3H BZUX=S literal 0 HcmV?d00001 diff --git a/TMessagesProj/src/main/res/drawable/story_link.xml b/TMessagesProj/src/main/res/drawable/story_link.xml new file mode 100644 index 0000000000..235661d8c7 --- /dev/null +++ b/TMessagesProj/src/main/res/drawable/story_link.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/TMessagesProj/src/main/res/values/ids.xml b/TMessagesProj/src/main/res/values/ids.xml index 1572fa7b54..a8de4da730 100644 --- a/TMessagesProj/src/main/res/values/ids.xml +++ b/TMessagesProj/src/main/res/values/ids.xml @@ -35,6 +35,7 @@ + diff --git a/TMessagesProj/src/main/res/values/strings.xml b/TMessagesProj/src/main/res/values/strings.xml index 0daae062f1..a9a94aa5c2 100644 --- a/TMessagesProj/src/main/res/values/strings.xml +++ b/TMessagesProj/src/main/res/values/strings.xml @@ -1142,6 +1142,7 @@ VIEW CHANNEL VIEW MESSAGE OPEN BOT + Instant View sponsored recommended Sponsored @@ -1654,6 +1655,16 @@ %1$s sent a sticker to the group %2$s %1$s sent a %3$s sticker to the group %2$s %1$s were charged %2$s + %1$d ⭐️ media + %1$d ⭐️ media + %2$s sent you a paid post for %1$d star + %2$s sent you a paid post for %1$d stars + %2$s posted a paid post for %1$d star + %2$s posted a paid post for %1$d stars + %2$s pinned a paid post for %1$d star + %2$s pinned a paid post for %1$d stars + %2$s posted a paid post in %3$s group for %1$d star + %2$s posted a paid post in %3$s group for %1$d stars %1$s invited you to the group %2$s %1$s renamed the group %2$s %1$s changed the group photo for %2$s @@ -1888,6 +1899,8 @@ Job Birthday Title + URL + Note Create New Contact New Contact Add to Existing Contact @@ -3404,18 +3417,18 @@ This is the main group video now. Photo saved to gallery Video saved to gallery - Photo saved to downloads - Video saved to downloads + Photo saved to **downloads**. + Video saved to **downloads**. Added to saved GIFs - GIF saved to downloads + GIF saved to **downloads**. File saved to music - File saved to downloads - %1$d files saved to downloads - File saved to downloads - %1$d files saved to downloads - %1$d files saved to downloads - %1$d files saved to downloads - %1$d files saved to downloads + File saved to **downloads**. + %1$d files saved to **downloads**. + File saved to **downloads**. + %1$d files saved to **downloads**. + %1$d files saved to **downloads**. + %1$d files saved to **downloads**. + %1$d files saved to **downloads**. %1$d photos saved to gallery Photo saved to gallery %1$d photos saved to gallery @@ -7037,6 +7050,7 @@ %1$s/%2$s Edit Intro Edit Commands + Balance Change Bot Settings Use @BotFather to manage this bot. Set public link @@ -7245,6 +7259,7 @@ Preparing video… Preparing sticker… No saved stories + No stories found No posts yet… Publish photos and videos to display on your profile page Add a post @@ -7592,6 +7607,7 @@ Location Audio Photo + Link Send reaction as a private message Send reaction as a message Remove Audio @@ -8697,8 +8713,18 @@ Ad impressions Ad revenue Withdraw via Fragment - %s in TON - %s in USD + Stars balance + You can withdraw Stars using Fragment, or use Stars to advertise your channel. **Learn more >** + https://telegram.org/tos/stars + Withdraw ⭐️%d + Withdraw All + Withdraw + Buy Ads + Stars Transactions + TON Transactions + %s in TON + %s in Stars + %s in USD Allow biometry Do you want to allow **%s** to use biometry? Download speed limited @@ -8891,6 +8917,7 @@ View Profile > View Group Info > View Channel Info > + Buy on Fragment View Channel Unlock to use Telegram Use PIN @@ -8913,11 +8940,16 @@ In-App Purchase Stars Top-Up Bot In-App Purchase + Ads Purchase from Fragment + Withdraw to Fragment Stars Acquired! **%d Stars** added to your balance. Purchase Completed! You acquired "**%2$s**" in **%3$s** for **%1$d Stars**. + Media Unlocked + **%1$d Star** transferred to **%2$s**. + **%1$d Stars** transferred to **%2$s**. Confirm Your Purchase Do you want to buy "**%2$s**" in **%3$s** for **%1$d star**? Do you want to buy "**%2$s**" in **%3$s** for **%1$d stars**? @@ -8953,10 +8985,84 @@ Transaction ID Transaction ID copied to clipboard Date + TON Transaction Date + View in Blockchain Explorer Review the **Terms of Service** for Stars. Not Available Sorry, no star purchases are available from your country. Refund + Failed + Pending Aw, Snap! Something went wrong while displaying this webpage.\n\nIf it occurs frequently, consider updating **Android WebView**. + Subscribe to **Telegram Premium** to add links to stories. + %d story from this location + %d stories from this location + Create Link + Enter URL + Link Text + Customize Link Text + Add Link + Update Link + Link Preview + You can customize your link + Move Up + Move Down + Open Link > + Proceeds overview + Available balance + Total balance + Total lifetime proceeds + Available balance + Stars from your total balance become available for spending on ads and rewards 21 days after they are earned. + You can withdraw Stars using Fragment, or use Stars to advertise your bot. **Learn more >** + https://telegram.org/privacy + Withdraw via Fragment + Withdraw all via Fragment + Enter amount to withdraw + Withdraw ⭐️%d via Fragment + Withdraw ⭐️%d via Fragment + Revenue + You cannot withdraw less than **%d stars.** + To Front + %d Story found + %d Stories found + View stories with #%s + Limit Reached + You can’t add more than %d links to a story. + Next withdrawal will be available in **%s**. + %1$sd:%2$sh:%3$sm + %2$s & %1$d Other + %2$s & %1$d Others + Web tabs %1$s + Stars revenue + Close All Tabs + Make this content paid + Edit price + %d Star + %d Stars + Media + Paid Content + Enter unlock cost + Users will have to transfer this amount of Stars to your channel in order to view this media. **Learn more >** + https://telegram.org/tos/stars + Make This Media Paid + Save Price + Keep this media free + Media Purchase + ⭐️%d + ⭐️%d + Unlock for ⭐️%d + Unlock for ⭐️%d + ⭐️%d + ⭐️%d + a **photo** + a **video** + **%d photo** + **%d photos** + **%s video** + **%s videos** + Do you want to unlock %1$s in **%2$s** for **%3$s**? + Do you want to unlock %1$s and %2$s in **%3$s** for **%4$s**? + ⭐️ %s \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 788b33e0aa..e5ee80cfe7 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=4845 -APP_VERSION_NAME=10.13.1 +APP_VERSION_CODE=4911 +APP_VERSION_NAME=10.14.2 APP_PACKAGE=org.telegram.messenger RELEASE_KEY_PASSWORD=android RELEASE_KEY_ALIAS=androidkey