From 2273173b0435895e02584c16d640b8f3ccbc198a Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Mon, 15 Jan 2024 23:38:48 +0400
Subject: [PATCH 01/61] Upgraded TDLib to tdlib/td@0f98d76
---
tdlib | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tdlib b/tdlib
index e432e4644e..55041be25f 160000
--- a/tdlib
+++ b/tdlib
@@ -1 +1 @@
-Subproject commit e432e4644eb4d11475e0a1de2dbe0e05f2a9bd99
+Subproject commit 55041be25fa2384dcd6902be08316bb26afaae56
From 267172baf0208d15409c94b9183543c6d0ead20f Mon Sep 17 00:00:00 2001
From: Nick <64551534+null-nick@users.noreply.github.com>
Date: Mon, 15 Jan 2024 21:03:44 +0100
Subject: [PATCH 02/61] WIP: Show `Convert to Broadcast Group` in group
settings
* init
* Update
* Fixes
* Update
* Fixes
* Fixed typo
* Little fixes
* Some changes suggested by @vkryl
* Update app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
Co-authored-by: Vyacheslav <6242627+vkryl@users.noreply.github.com>
* Update app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
Co-authored-by: Vyacheslav <6242627+vkryl@users.noreply.github.com>
* Update app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
Co-authored-by: Vyacheslav <6242627+vkryl@users.noreply.github.com>
* Update app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
Co-authored-by: Vyacheslav <6242627+vkryl@users.noreply.github.com>
* Update app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
Co-authored-by: Vyacheslav <6242627+vkryl@users.noreply.github.com>
* Update app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
* code indentation fixes
* applied suggestion by @vkryl
* Crash fixes
* Update app/src/main/res/values/strings.xml
Co-authored-by: Vyacheslav <6242627+vkryl@users.noreply.github.com>
* Update app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
Co-authored-by: Vyacheslav <6242627+vkryl@users.noreply.github.com>
* Design and logic fixes
* Revert `version.properties`
---------
Co-authored-by: Vyacheslav <6242627+vkryl@users.noreply.github.com>
---
.../thunderdog/challegram/telegram/Tdlib.java | 3 +
.../challegram/ui/ProfileController.java | 60 +++++++++++++++++--
app/src/main/res/values/ids.xml | 7 ++-
app/src/main/res/values/strings.xml | 7 +++
4 files changed, 71 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
index b3f8a59d92..109a75772a 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
@@ -10693,6 +10693,9 @@ public boolean suggestConvertToBroadcastGroup (long chatId) {
if (supergroup == null || supergroup.isChannel || supergroup.isBroadcastGroup || !TD.isCreator(supergroup.status)) {
return false;
}
+ if (isDebugInstance() || BuildConfig.DEBUG) {
+ return true;
+ }
synchronized (dataLock) {
for (TdApi.SuggestedAction action : suggestedActions) {
if (action.getConstructor() == TdApi.SuggestedActionConvertToBroadcastGroup.CONSTRUCTOR) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java b/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
index 05b0dfcf90..f7d4f8ed80 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
@@ -15,14 +15,12 @@
package org.thunderdog.challegram.ui;
import android.annotation.SuppressLint;
-import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
-import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.InputFilter;
@@ -68,7 +66,6 @@
import org.thunderdog.challegram.data.ThreadInfo;
import org.thunderdog.challegram.data.TranslationsManager;
import org.thunderdog.challegram.emoji.EmojiFilter;
-import org.thunderdog.challegram.filegen.SimpleGenerationInfo;
import org.thunderdog.challegram.loader.AvatarReceiver;
import org.thunderdog.challegram.mediaview.MediaViewController;
import org.thunderdog.challegram.mediaview.data.MediaStack;
@@ -99,7 +96,6 @@
import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.theme.ColorState;
import org.thunderdog.challegram.theme.Theme;
-import org.thunderdog.challegram.tool.Intents;
import org.thunderdog.challegram.tool.Keyboard;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
@@ -131,7 +127,6 @@
import org.thunderdog.challegram.widget.ViewPager;
import org.thunderdog.challegram.widget.rtl.RtlViewPager;
-import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -3835,6 +3830,13 @@ private void buildEditCells () {
items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, R.string.AggressiveAntiSpamDesc));
}
+ if (tdlib.suggestConvertToBroadcastGroup(chat.id)) {
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_convertToBroadcastGroup, 0, R.string.ConvertToBroadcastGroup));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.pluralBold(R.string.ConvertToBroadcastGroupDesc, tdlib.supergroupMaxSize())));
+ }
+
if ((supergroupFull != null && supergroupFull.canHideMembers) || (groupFull != null && groupFull.canHideMembers && tdlib.canUpgradeChat(chat.id))) {
boolean membersHidden = supergroupFull != null && supergroupFull.hasHiddenMembers;
items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
@@ -4628,9 +4630,57 @@ public void onClick (View v) {
toggleContentProtection(v);
} else if (viewId == R.id.btn_toggleJoinByRequest) {
toggleJoinByRequests(v);
+ } else if (viewId == R.id.btn_convertToBroadcastGroup) {
+ showOptions(
+ Lang.getMarkdownString(this, R.string.ConvertToBroadcastGroupHint),
+ new int[] {R.id.btn_confirmConvertBroadcast, R.id.btn_cancel},
+ new String[] {
+ Lang.getString(R.string.ConvertToBroadcastGroupButton),
+ Lang.getString(R.string.Cancel)
+ },
+ new int[] {
+ OPTION_COLOR_RED,
+ OPTION_COLOR_NORMAL
+ },
+ new int[] {
+ R.drawable.baseline_bullhorn_24,
+ R.drawable.baseline_cancel_24
+ },
+ (itemView, optionId) -> {
+ if (optionId == R.id.btn_confirmConvertBroadcast) {
+ convertToBroadcastGroup();
+ }
+ return true;
+ });
}
}
+ private void convertToBroadcastGroup () {
+ showOptions(
+ Lang.getMarkdownString(this, R.string.ConvertToBroadcastGroupConfirmHint),
+ new int[] {R.id.btn_convertBroadcastGroup, R.id.btn_cancel},
+ new String[] {
+ Lang.getString(R.string.ConvertToBroadcastGroupConfirm),
+ Lang.getString(R.string.Cancel)
+ },
+ new int[] {
+ OPTION_COLOR_RED,
+ OPTION_COLOR_NORMAL
+ },
+ new int[] {
+ R.drawable.baseline_check_24,
+ R.drawable.baseline_cancel_24
+ },
+ (itemView, optionId) -> {
+ if (optionId == R.id.btn_convertBroadcastGroup) {
+ if (supergroup != null) {
+ tdlib.send(new TdApi.ToggleSupergroupIsBroadcastGroup(supergroup.id), tdlib.typedOkHandler());
+ }
+ }
+ return true;
+ });
+ }
+
private boolean canSetUsername () {
return (supergroup != null && TD.isCreator(supergroup.status)) || (group != null && TD.isCreator(group.status));
}
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
index bf624d7e0e..f68f91940d 100644
--- a/app/src/main/res/values/ids.xml
+++ b/app/src/main/res/values/ids.xml
@@ -1362,6 +1362,11 @@
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d4cfad14e2..2adb85a34f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5007,6 +5007,13 @@
Account deleted
Your account %1$s (%2$s) is now deleted from Telegram servers, and can no longer be restored.\n\nUsing the same phone number will create a new account.
View once
+ Convert to Broadcast Group
+ Broadcast groups can have over %1$s member, but only admins can send messages in them.
+ Broadcast groups can have over %1$s members, but only admins can send messages in them.
+ Convert to Broadcast Group
+ You can convert your current group to a **Broadcast Group**.\n\nBroadcast Groups have no limit on the members but only admins can send messages.\n\n**This action cannot be undone.**
+ Members who are not admins will **permanently** lose their right to send messages in the group.\n\n**This action cannot be undone.**
+ Yes, I am sure, convert.
Congratulations
Unclaimed Prize
From 9d86ad41ececc23031e08c1cefa17cd3b00f147f Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 16 Jan 2024 01:28:27 +0400
Subject: [PATCH 03/61] Sync `strings.xml` with `translations.telegram.org`
---
app/src/main/res/values/strings.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2adb85a34f..d44b013268 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5089,8 +5089,8 @@
This giveaway is sponsored by the admins of %2$s, who acquired %1$s Telegram Premium subscription for %3$s months for its followers.
This giveaway is sponsored by the admins of %2$s, who acquired %1$s Telegram Premium subscriptions for %3$s months for its followers.
- On %2$s, Telegram will automatically selected %1$s random user that joined %3$s.
- On %2$s, Telegram will automatically selected %1$s random users that joined %3$s.
+ On %2$s, Telegram will automatically select %1$s random user that joined %3$s.
+ On %2$s, Telegram will automatically select %1$s random users that joined %3$s.
%2$s also included %1$s %3$s with the prizes. The channel admins are responsible for delivering these prizes.
%2$s also included %1$s %3$s with the prizes. The channel admins are responsible for delivering these prizes.
From c312ee34df48b5c80332cae6ec0978309de0e6ad Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 16 Jan 2024 02:14:16 +0400
Subject: [PATCH 04/61] Hint when tapping on a locked permission group toggle
---
.../challegram/ui/EditRightsController.java | 27 +++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
index d0fb7c5365..bcdee38427 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.text.InputType;
+import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
@@ -49,7 +50,9 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
import me.vkryl.android.widget.FrameLayoutFix;
@@ -555,7 +558,7 @@ protected void setValuedSetting (ListItem item, SettingView view, boolean isUpda
if (i.getId() == R.id.btn_togglePermissionGroup) {
final RightOption option = (RightOption) item.getData();
if (option != null && option.groupRightIds != null) {
- toggleRightsGroup(option.groupRightIds);
+ toggleRightsGroup(view, option.groupRightIds);
}
} else {
view.performClick();
@@ -1783,20 +1786,40 @@ private boolean hasAccessToEditRightsGroup (int[] rights) {
return false;
}
- private void toggleRightsGroup (int[] rights) {
+ private void toggleRightsGroup (SettingView view, int[] rights) {
final int index = adapter.indexOfViewByIdAndValue(R.id.btn_togglePermissionGroup, rights[0]);
final ListItem item = adapter.getItem(index);
if (item == null) {
return;
}
+ Set errorHints = null;
+ int editedCount = 0;
boolean newValue = !item.getBoolValue();
for (int rightId : rights) {
boolean canEdit = hasAccessToEditRight(rightId);
if (canEdit) {
+ if (getValueForId(rightId) != newValue) {
+ editedCount++;
+ }
setValueForRightId(rightId, newValue);
+ } else {
+ CharSequence text = getHintForToggleUnavailability(item);
+ if (text != null) {
+ if (errorHints == null) {
+ errorHints = new HashSet<>();
+ }
+ errorHints.add(text);
+ }
}
}
+ if (editedCount == 0 && errorHints != null) {
+ CharSequence[] hints = errorHints.toArray(new CharSequence[0]);
+ CharSequence hint = TextUtils.join("\n", hints);
+ context().tooltipManager()
+ .builder(((SettingView) view).getToggler())
+ .show(this, tdlib, R.drawable.baseline_info_24, hint);
+ }
}
private int getRightsGroupEnabledCount (int[] rights) {
From 40a8d13cb23817cddf649169bf8865f1ab3a7181 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 16 Jan 2024 02:31:19 +0400
Subject: [PATCH 05/61] Treat service notifications as always offline
---
.../java/org/thunderdog/challegram/telegram/TdlibCache.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCache.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCache.java
index c53ad95ee3..6e9714bc09 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCache.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCache.java
@@ -1373,6 +1373,9 @@ public boolean isOnline (long userId) {
if (userId == myUserId) {
return true;
}
+ if (tdlib.isServiceNotificationsChat(ChatId.fromUserId(userId))) {
+ return false;
+ }
boolean isOnline;
synchronized (dataLock) {
if (userId != myUserId) {
From c549f9792790c4e90b31e85be91757820147ad52 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 16 Jan 2024 03:13:11 +0400
Subject: [PATCH 06/61] Ability to convert group to Broadcast Group (closed
#507)
---
.../challegram/ui/ProfileController.java | 50 ++++++++++++++++---
app/src/main/res/values/strings.xml | 3 ++
2 files changed, 46 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java b/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
index f7d4f8ed80..429a78cb96 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
@@ -332,8 +332,8 @@ private void replaceWithSupergroup (long supergroupId) {
if (isEditing()) {
TdApi.SupergroupFullInfo supergroupFullInfo = tdlib.cache().supergroupFull(supergroupId);
if (supergroupFullInfo == null) {
- tdlib.client().send(new TdApi.GetSupergroupFullInfo(supergroupId), result -> {
- if (result.getConstructor() == TdApi.SupergroupFullInfo.CONSTRUCTOR) {
+ tdlib.send(new TdApi.GetSupergroupFullInfo(supergroupId), (supergroupFull, error) -> {
+ if (error == null) {
tdlib.ui().post(() -> replaceWithSupergroup(supergroupId));
}
});
@@ -342,8 +342,8 @@ private void replaceWithSupergroup (long supergroupId) {
}
TdApi.Chat chat = tdlib.chat(ChatId.fromSupergroupId(supergroupId));
if (chat == null) {
- tdlib.client().send(new TdApi.CreateSupergroupChat(supergroupId, false), result -> {
- if (result.getConstructor() == TdApi.Chat.CONSTRUCTOR) {
+ tdlib.send(new TdApi.CreateSupergroupChat(supergroupId, false), (supergroup, error) -> {
+ if (error == null) {
tdlib.ui().post(() -> replaceWithSupergroup(supergroupId));
}
});
@@ -3141,6 +3141,28 @@ private void toggleSupergroupGroupFeature (FutureBool currentValue, SupergroupFe
}
}
+ private void checkConvertToBroadcastGroup () {
+ boolean needConvertToBroadcastGroup = tdlib.suggestConvertToBroadcastGroup(chat.id);
+ int index = baseAdapter.indexOfViewById(R.id.btn_convertToBroadcastGroup);
+ boolean hasConvertToBroadcastGroup = index != -1;
+ if (needConvertToBroadcastGroup != hasConvertToBroadcastGroup) {
+ if (needConvertToBroadcastGroup) {
+ index = baseAdapter.indexOfView(aggressiveAntiSpamItem);
+ if (index != -1) {
+ index += 3;
+ baseAdapter.addItems(index,
+ new ListItem(ListItem.TYPE_SHADOW_TOP),
+ new ListItem(ListItem.TYPE_SETTING, R.id.btn_convertToBroadcastGroup, 0, R.string.ConvertToBroadcastGroup),
+ new ListItem(ListItem.TYPE_SHADOW_BOTTOM),
+ new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.pluralBold(R.string.ConvertToBroadcastGroupDesc, tdlib.supergroupMaxSize()))
+ );
+ }
+ } else {
+ baseAdapter.removeRange(index - 1, 4);
+ }
+ }
+ }
+
private void toggleAggressiveAntiSpam (View v) {
boolean canToggleAggressiveAntiSpam =
(supergroupFull != null && supergroupFull.canToggleAggressiveAntiSpam) ||
@@ -4648,14 +4670,14 @@ public void onClick (View v) {
},
(itemView, optionId) -> {
if (optionId == R.id.btn_confirmConvertBroadcast) {
- convertToBroadcastGroup();
+ convertToBroadcastGroup(v);
}
return true;
});
}
}
- private void convertToBroadcastGroup () {
+ private void convertToBroadcastGroup (View view) {
showOptions(
Lang.getMarkdownString(this, R.string.ConvertToBroadcastGroupConfirmHint),
new int[] {R.id.btn_convertBroadcastGroup, R.id.btn_cancel},
@@ -4674,7 +4696,20 @@ private void convertToBroadcastGroup () {
(itemView, optionId) -> {
if (optionId == R.id.btn_convertBroadcastGroup) {
if (supergroup != null) {
- tdlib.send(new TdApi.ToggleSupergroupIsBroadcastGroup(supergroup.id), tdlib.typedOkHandler());
+ tdlib.send(new TdApi.ToggleSupergroupIsBroadcastGroup(supergroup.id), (ok, error) -> runOnUiThreadOptional(() -> {
+ if (error != null) {
+ context().tooltipManager().builder(view).icon(R.drawable.baseline_warning_24).show(tdlib, TD.toErrorString(error));
+ } else {
+ openAlert(
+ R.string.ConvertToBroadcastGroupAlertTitle,
+ Lang.getStringBold(R.string.ConvertToBroadcastGroupAlertText, tdlib.chatTitle(chat.id)),
+ Lang.getString(R.string.ConvertToBroadcastGroupAlertClose),
+ (dialog, which) -> dialog.dismiss(),
+ ALERT_NO_CANCELABLE | ALERT_NO_CANCEL
+ );
+ checkConvertToBroadcastGroup();
+ }
+ }));
}
}
return true;
@@ -6082,6 +6117,7 @@ public void onSupergroupUpdated (final TdApi.Supergroup supergroup) {
ProfileController.this.supergroup = supergroup;
checkUsername();
checkEasterEggs();
+ checkConvertToBroadcastGroup();
updateHeader(false);
}
});
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d44b013268..0f2463e47d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5014,6 +5014,9 @@
You can convert your current group to a **Broadcast Group**.\n\nBroadcast Groups have no limit on the members but only admins can send messages.\n\n**This action cannot be undone.**
Members who are not admins will **permanently** lose their right to send messages in the group.\n\n**This action cannot be undone.**
Yes, I am sure, convert.
+ Success
+ Group \"%1$s\" was successfully converted to Broadcast Group.\n\nNow there is no limit on the members count but only admins can send messages.
+ Got it
Congratulations
Unclaimed Prize
From c4a7cb13af2be68fb45c4f1c5e81323d9c2eabfc Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 16 Jan 2024 03:22:29 +0400
Subject: [PATCH 07/61] Fix missing preview header for the first account
---
.../java/org/thunderdog/challegram/widget/ForceTouchView.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/ForceTouchView.java b/app/src/main/java/org/thunderdog/challegram/widget/ForceTouchView.java
index 0b8b388d4a..1560447d3c 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/ForceTouchView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/ForceTouchView.java
@@ -315,7 +315,7 @@ public void initWithContext (ForceTouchContext context) {
headerView.setIgnoreCustomHeight();
headerView.setInnerMargins(Screen.dp(8f), Screen.dp(8f));
headerView.setTextColors(Theme.textAccentColor(), Theme.textDecentColor());
- if (context.boundDataType != 0 && context.boundDataId != 0) {
+ if (context.boundDataType != 0 && (context.boundDataId != 0 || context.boundDataType == DataType.ACCOUNT)) {
switch (context.boundDataType) {
case DataType.CHAT:
setupChat(context.boundDataId, (ThreadInfo) context.boundArg1, headerView);
From 74470fb009101136413822b6dcd2c5a49342b544 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 16 Jan 2024 03:32:30 +0400
Subject: [PATCH 08/61] Add fire icon to blurred secret media preview
---
.../challegram/component/chat/MediaPreview.java | 11 ++++++-----
.../component/chat/MediaPreviewSimple.java | 14 +++++++-------
.../challegram/component/chat/ReplyComponent.java | 13 +++++--------
3 files changed, 18 insertions(+), 20 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreview.java b/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreview.java
index 5cf768383e..beee005fb7 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreview.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreview.java
@@ -207,7 +207,7 @@ public static MediaPreview valueOf (Tdlib tdlib, TdApi.Message message, @Nullabl
TdApi.Photo photo = messagePhoto.photo;
TdApi.PhotoSize thumbnail = Td.findSmallest(photo);
if (thumbnail != null || photo.minithumbnail != null) {
- return new MediaPreviewSimple(tdlib, size, cornerRadius, TD.toThumbnail(thumbnail), photo.minithumbnail, messagePhoto.hasSpoiler);
+ return new MediaPreviewSimple(tdlib, size, cornerRadius, TD.toThumbnail(thumbnail), photo.minithumbnail, messagePhoto.isSecret || messagePhoto.hasSpoiler);
}
break;
}
@@ -230,13 +230,13 @@ public static MediaPreview valueOf (Tdlib tdlib, TdApi.Message message, @Nullabl
}
case TdApi.MessageVideo.CONSTRUCTOR: {
TdApi.MessageVideo messageVideo = (TdApi.MessageVideo) message.content;
- return valueOf(tdlib, messageVideo.video, size, cornerRadius, messageVideo.hasSpoiler);
+ return valueOf(tdlib, messageVideo.video, size, cornerRadius, messageVideo.isSecret || messageVideo.hasSpoiler);
}
case TdApi.MessageAnimation.CONSTRUCTOR: {
TdApi.MessageAnimation messageAnimation = (TdApi.MessageAnimation) message.content;
TdApi.Animation animation = messageAnimation.animation;
if (animation.minithumbnail != null || animation.thumbnail != null) {
- return new MediaPreviewSimple(tdlib, size, cornerRadius, animation.thumbnail, animation.minithumbnail, messageAnimation.hasSpoiler);
+ return new MediaPreviewSimple(tdlib, size, cornerRadius, animation.thumbnail, animation.minithumbnail, messageAnimation.isSecret || messageAnimation.hasSpoiler);
}
break;
}
@@ -262,8 +262,9 @@ public static MediaPreview valueOf (Tdlib tdlib, TdApi.Message message, @Nullabl
return new MediaPreviewSimple(tdlib, size, cornerRadius, sticker);
}
case TdApi.MessageVideoNote.CONSTRUCTOR: {
- TdApi.VideoNote videoNote = ((TdApi.MessageVideoNote) message.content).videoNote;
- return new MediaPreviewSimple(tdlib, size, size / 2, videoNote.thumbnail, videoNote.minithumbnail);
+ TdApi.MessageVideoNote messageVideoNote = (TdApi.MessageVideoNote) message.content;
+ TdApi.VideoNote videoNote = messageVideoNote.videoNote;
+ return new MediaPreviewSimple(tdlib, size, size / 2, videoNote.thumbnail, videoNote.minithumbnail, messageVideoNote.isSecret);
}
case TdApi.MessageVoiceNote.CONSTRUCTOR: {
// TODO voice note preview?
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreviewSimple.java b/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreviewSimple.java
index 7501d30ee0..d4110ece98 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreviewSimple.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreviewSimple.java
@@ -49,7 +49,7 @@ public class MediaPreviewSimple extends MediaPreview {
private TdApi.Sticker sticker;
private Path outline;
private int outlineWidth, outlineHeight;
- private boolean hasSpoiler;
+ private boolean needFireIcon;
private ImageFile previewImage;
private GifFile previewGif;
@@ -168,15 +168,15 @@ public MediaPreviewSimple (Tdlib tdlib, int size, int cornerRadius, TdApi.Thumbn
this(tdlib, size, cornerRadius, thumbnail, minithumbnail, false);
}
- public MediaPreviewSimple (Tdlib tdlib, int size, int cornerRadius, TdApi.Thumbnail thumbnail, TdApi.Minithumbnail minithumbnail, boolean hasSpoiler) {
+ public MediaPreviewSimple (Tdlib tdlib, int size, int cornerRadius, TdApi.Thumbnail thumbnail, TdApi.Minithumbnail minithumbnail, boolean needFireIcon) {
super(size, cornerRadius);
- this.hasSpoiler = hasSpoiler;
+ this.needFireIcon = needFireIcon;
if (minithumbnail != null) {
this.previewImage = new ImageFileLocal(minithumbnail);
this.previewImage.setSize(size);
this.previewImage.setScaleType(ImageFile.CENTER_CROP);
this.previewImage.setDecodeSquare(true);
- if (hasSpoiler) {
+ if (needFireIcon) {
this.previewImage.setIsPrivate();
}
}
@@ -187,11 +187,11 @@ public MediaPreviewSimple (Tdlib tdlib, int size, int cornerRadius, TdApi.Thumbn
this.targetImage.setScaleType(ImageFile.CENTER_CROP);
this.targetImage.setDecodeSquare(true);
this.targetImage.setNoBlur();
- if (hasSpoiler) {
+ if (needFireIcon) {
this.targetImage.setIsPrivate();
}
}
- if (!hasSpoiler) {
+ if (!needFireIcon) {
this.targetGif = TD.toGifFile(tdlib, thumbnail);
if (this.targetGif != null) {
this.targetGif.setOptimizationMode(GifFile.OptimizationMode.STICKER_PREVIEW);
@@ -333,7 +333,7 @@ public void draw (T view, Canvas c, ComplexR
preview.drawPlaceholderContour(c, outline);
}
- if (hasSpoiler) {
+ if (needFireIcon) {
DrawAlgorithms.drawRoundRect(c, cornerRadius, target.getLeft(), target.getTop(), target.getRight(), target.getBottom(), Paints.fillingPaint(Theme.getColor(ColorId.spoilerMediaOverlay)));
DrawAlgorithms.drawParticles(c, cornerRadius, target.getLeft(), target.getTop(), target.getRight(), target.getBottom(), 1f);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/ReplyComponent.java b/app/src/main/java/org/thunderdog/challegram/component/chat/ReplyComponent.java
index 870664bf89..d5ecf8c2f1 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/ReplyComponent.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/ReplyComponent.java
@@ -37,8 +37,6 @@
import org.thunderdog.challegram.data.MediaWrapper;
import org.thunderdog.challegram.data.TD;
import org.thunderdog.challegram.data.TGMessage;
-import org.thunderdog.challegram.data.TGWebPage;
-import org.thunderdog.challegram.helper.InlineSearchContext;
import org.thunderdog.challegram.loader.ComplexReceiver;
import org.thunderdog.challegram.loader.DoubleImageReceiver;
import org.thunderdog.challegram.loader.ImageFile;
@@ -53,7 +51,6 @@
import org.thunderdog.challegram.tool.Drawables;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
-import org.thunderdog.challegram.tool.Strings;
import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.util.text.Text;
import org.thunderdog.challegram.util.text.TextColorSet;
@@ -459,7 +456,7 @@ public void draw (Canvas c, int startX, int startY, int endX, int width, Receive
if (Config.DEBUG_STICKER_OUTLINES && mediaPreview != null) {
receiver.drawPlaceholderContour(c, mediaPreview.contour);
}
- if (mediaPreview != null && mediaPreview.hasSpoiler) {
+ if (mediaPreview != null && mediaPreview.needFireIcon) {
float radius = Theme.getBubbleMergeRadius();
DrawAlgorithms.drawRoundRect(c, radius, receiver.getLeft(), receiver.getTop(), receiver.getRight(), receiver.getBottom(), Paints.fillingPaint(Theme.getColor(ColorId.spoilerMediaOverlay)));
DrawAlgorithms.drawParticles(c, radius, receiver.getLeft(), receiver.getTop(), receiver.getRight(), receiver.getBottom(), 1f);
@@ -719,14 +716,14 @@ private static class MediaPreview {
private final ImageFile miniThumbnail;
private final ImageFile preview;
private final Path contour;
- private final boolean hasSpoiler;
+ private final boolean needFireIcon;
private final boolean previewCircle;
- public MediaPreview (ImageFile miniThumbnail, ImageFile preview, Path contour, boolean hasSpoiler, boolean previewCircle) {
+ public MediaPreview (ImageFile miniThumbnail, ImageFile preview, Path contour, boolean needFireIcon, boolean previewCircle) {
this.miniThumbnail = miniThumbnail;
this.preview = preview;
this.contour = contour;
- this.hasSpoiler = hasSpoiler;
+ this.needFireIcon = needFireIcon;
this.previewCircle = previewCircle;
}
}
@@ -853,7 +850,7 @@ private MediaPreview newMediaPreview (long chatId, TdApi.MessageContent content)
} else {
miniPreview = null;
}
- return new MediaPreview(miniPreview, preview, contour, hasSpoiler, previewCircle);
+ return new MediaPreview(miniPreview, preview, contour, isPrivate || hasSpoiler, previewCircle);
}
private void setMessage (TdApi.Message msg, boolean forceRequestImage, boolean forceLocal) {
From e93eabf99e4cc05c97567cf3cd51e6faffcf90b2 Mon Sep 17 00:00:00 2001
From: tgx-server
Date: Mon, 15 Jan 2024 23:39:47 +0000
Subject: [PATCH 09/61] Version bump to `1679`
---
version.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.properties b/version.properties
index 2daf452895..ab5259b985 100644
--- a/version.properties
+++ b/version.properties
@@ -1,5 +1,5 @@
# App
-version.app=1678
+version.app=1679
version.major=0
# Anchor date point in app versioning
version.creation=873642600564
From cb4e025431b82e665c0790404dea426e63a440fe Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 16 Jan 2024 14:44:11 +0400
Subject: [PATCH 10/61] Support for `Create Topics` permission and restriction
Manage `Create topics` permission & restriction + Force admins dismissal when tapping `Dismiss Admin` + `canManageTopics` in recent actions
---
.../thunderdog/challegram/config/Config.java | 2 +
.../challegram/data/ChatEventUtil.java | 24 ++++-
.../org/thunderdog/challegram/data/TD.java | 75 +++++++++++--
.../challegram/telegram/RightId.java | 6 +-
.../thunderdog/challegram/telegram/Tdlib.java | 4 +-
.../challegram/ui/EditRightsController.java | 102 +++++++++---------
.../challegram/ui/ProfileController.java | 6 +-
app/src/main/res/values/strings.xml | 3 +
vkryl/td | 2 +-
9 files changed, 154 insertions(+), 70 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/config/Config.java b/app/src/main/java/org/thunderdog/challegram/config/Config.java
index 22eea096fa..5c2a8daf59 100644
--- a/app/src/main/java/org/thunderdog/challegram/config/Config.java
+++ b/app/src/main/java/org/thunderdog/challegram/config/Config.java
@@ -315,4 +315,6 @@ public static boolean isThemeDoc (TdApi.Document doc) {
public static final boolean FORCE_REPLY_WHEN_FORWARDING_WITH_COMMENT = false;
public static final boolean DEBUG_VIEW_MESSAGES = false;
public static final boolean ENABLE_TEXT_ANIMATIONS = false;
+
+ public static final boolean COMPILE_CHECK = false /*never set to true*/;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/data/ChatEventUtil.java b/app/src/main/java/org/thunderdog/challegram/data/ChatEventUtil.java
index 42f0d67691..63932e64e4 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/ChatEventUtil.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/ChatEventUtil.java
@@ -22,6 +22,7 @@
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.component.chat.MessagesManager;
+import org.thunderdog.challegram.config.Config;
import org.thunderdog.challegram.core.Lang;
import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.tool.Strings;
@@ -530,7 +531,7 @@ private static TGMessage fullMessage (MessagesManager context, TdApi.Message msg
} else if (isAnonymous) {
appendRight(b, R.string.EventLogPromotedRemainAnonymous, ((TdApi.ChatMemberStatusCreator) oldStatus).isAnonymous, ((TdApi.ChatMemberStatusCreator) newStatus).isAnonymous, false);
} else if (isPromote) {
- if (false) {
+ if (Config.COMPILE_CHECK) {
// Cause compilation error if signature changes
new TdApi.ChatAdministratorRights(
true,
@@ -598,6 +599,7 @@ private static TGMessage fullMessage (MessagesManager context, TdApi.Message msg
appendRight(b, R.string.EventLogRestrictedAddUsers, oldBan != null ? oldBan.permissions.canInviteUsers : oldCanReadMessages, newBan != null ? newBan.permissions.canInviteUsers : newCanReadMessages, false);
appendRight(b, R.string.EventLogRestrictedPinMessages, oldBan != null ? oldBan.permissions.canPinMessages : oldCanReadMessages, newBan != null ? newBan.permissions.canPinMessages : newCanReadMessages, false);
appendRight(b, R.string.EventLogRestrictedChangeInfo, oldBan != null ? oldBan.permissions.canChangeInfo : oldCanReadMessages, newBan != null ? newBan.permissions.canChangeInfo : newCanReadMessages, false);
+ appendRight(b, R.string.EventLogRestrictedTopics, oldBan != null ? oldBan.permissions.canManageTopics : oldCanReadMessages, newBan != null ? newBan.permissions.canManageTopics : newCanReadMessages, false);
}
TdApi.FormattedText formattedText = new TdApi.FormattedText(b.toString().trim(), null);
@@ -711,6 +713,25 @@ private static TdApi.MessageContent convertToNativeMessageContent (TdApi.ChatEve
TdApi.ChatEventPermissionsChanged permissions = (TdApi.ChatEventPermissionsChanged) event.action;
+ if (Config.COMPILE_CHECK) {
+ new TdApi.ChatPermissions(
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false
+ );
+ }
+
appendRight(b, R.string.EventLogPermissionSendMessages, permissions.oldPermissions.canSendBasicMessages, permissions.newPermissions.canSendBasicMessages, true);
appendRight(b, R.string.EventLogPermissionSendPhoto, permissions.oldPermissions.canSendPhotos, permissions.newPermissions.canSendPhotos, true);
appendRight(b, R.string.EventLogPermissionSendVideo, permissions.oldPermissions.canSendVideos, permissions.newPermissions.canSendVideos, true);
@@ -724,6 +745,7 @@ private static TdApi.MessageContent convertToNativeMessageContent (TdApi.ChatEve
appendRight(b, R.string.EventLogPermissionAddUsers, permissions.oldPermissions.canInviteUsers, permissions.newPermissions.canInviteUsers, true);
appendRight(b, R.string.EventLogPermissionPinMessages, permissions.oldPermissions.canPinMessages, permissions.newPermissions.canPinMessages, true);
appendRight(b, R.string.EventLogPermissionChangeInfo, permissions.oldPermissions.canChangeInfo, permissions.newPermissions.canChangeInfo, true);
+ appendRight(b, R.string.EventLogPermissionTopicsCreate, permissions.oldPermissions.canManageTopics, permissions.newPermissions.canManageTopics, true);
TdApi.FormattedText formattedText = new TdApi.FormattedText(b.toString().trim(), new TdApi.TextEntity[] {new TdApi.TextEntity(0, length, new TdApi.TextEntityTypeItalic())});
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TD.java b/app/src/main/java/org/thunderdog/challegram/data/TD.java
index 6614451849..e9031f6764 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TD.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TD.java
@@ -148,7 +148,7 @@ public static boolean isValidRight (@RightId int rightId) {
case RightId.INVITE_USERS:
case RightId.PIN_MESSAGES:
case RightId.MANAGE_VIDEO_CHATS:
- case RightId.MANAGE_TOPICS:
+ case RightId.MANAGE_OR_CREATE_TOPICS:
case RightId.POST_STORIES:
case RightId.EDIT_STORIES:
case RightId.DELETE_STORIES:
@@ -176,6 +176,42 @@ public static MessageId restoreMessageId (Bundle inState, String prefix) {
}
public static boolean checkRight (TdApi.ChatPermissions permissions, @RightId int rightId) {
+ if (false) {
+ // compile check
+ new TdApi.ChatPermissions(
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false
+ );
+ new TdApi.ChatAdministratorRights(
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false
+ );
+ }
switch (rightId) {
case RightId.READ_MESSAGES:
return true;
@@ -189,29 +225,31 @@ public static boolean checkRight (TdApi.ChatPermissions permissions, @RightId in
return permissions.canSendPhotos;
case RightId.SEND_VIDEOS:
return permissions.canSendVideos;
- case RightId.SEND_VOICE_NOTES:
- return permissions.canSendVoiceNotes;
case RightId.SEND_VIDEO_NOTES:
return permissions.canSendVideoNotes;
- case RightId.SEND_OTHER_MESSAGES:
- return permissions.canSendOtherMessages;
+ case RightId.SEND_VOICE_NOTES:
+ return permissions.canSendVoiceNotes;
case RightId.SEND_POLLS:
return permissions.canSendPolls;
+ case RightId.SEND_OTHER_MESSAGES:
+ return permissions.canSendOtherMessages;
case RightId.EMBED_LINKS:
return permissions.canAddWebPagePreviews;
+ case RightId.CHANGE_CHAT_INFO:
+ return permissions.canChangeInfo;
case RightId.INVITE_USERS:
return permissions.canInviteUsers;
case RightId.PIN_MESSAGES:
return permissions.canPinMessages;
- case RightId.CHANGE_CHAT_INFO:
- return permissions.canChangeInfo;
+ // Same right, but different meaning
+ case RightId.MANAGE_OR_CREATE_TOPICS:
+ return permissions.canManageTopics;
// Admin-only
case RightId.ADD_NEW_ADMINS:
case RightId.BAN_USERS:
case RightId.DELETE_MESSAGES:
case RightId.EDIT_MESSAGES:
case RightId.MANAGE_VIDEO_CHATS:
- case RightId.MANAGE_TOPICS:
case RightId.POST_STORIES:
case RightId.EDIT_STORIES:
case RightId.DELETE_STORIES:
@@ -1148,6 +1186,24 @@ public static TdApi.InputFile createInputFile (String path, @Nullable String typ
}
public static boolean hasRestrictions (TdApi.ChatPermissions a, TdApi.ChatPermissions defaultPermissions) {
+ if (Config.COMPILE_CHECK) {
+ new TdApi.ChatPermissions(
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false
+ );
+ }
return
(a.canSendBasicMessages != defaultPermissions.canSendBasicMessages && defaultPermissions.canSendBasicMessages) ||
(a.canSendAudios != defaultPermissions.canSendAudios && defaultPermissions.canSendAudios) ||
@@ -1161,7 +1217,8 @@ public static boolean hasRestrictions (TdApi.ChatPermissions a, TdApi.ChatPermis
(a.canSendPolls != defaultPermissions.canSendPolls && defaultPermissions.canSendPolls) ||
(a.canInviteUsers != defaultPermissions.canInviteUsers && defaultPermissions.canInviteUsers) ||
(a.canPinMessages != defaultPermissions.canPinMessages && defaultPermissions.canPinMessages) ||
- (a.canChangeInfo != defaultPermissions.canChangeInfo && defaultPermissions.canChangeInfo);
+ (a.canChangeInfo != defaultPermissions.canChangeInfo && defaultPermissions.canChangeInfo) ||
+ (a.canManageTopics != defaultPermissions.canManageTopics && defaultPermissions.canManageTopics);
}
public static int getCombineMode (TdApi.Message message) {
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/RightId.java b/app/src/main/java/org/thunderdog/challegram/telegram/RightId.java
index b86e35d8f7..17d86b3574 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/RightId.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/RightId.java
@@ -16,8 +16,6 @@
import androidx.annotation.IntDef;
-import org.thunderdog.challegram.R;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -44,7 +42,7 @@
RightId.POST_STORIES,
RightId.EDIT_STORIES,
RightId.DELETE_STORIES,
- RightId.MANAGE_TOPICS,
+ RightId.MANAGE_OR_CREATE_TOPICS,
RightId.ADD_NEW_ADMINS,
RightId.REMAIN_ANONYMOUS
})
@@ -71,7 +69,7 @@
POST_STORIES = 19,
EDIT_STORIES = 20,
DELETE_STORIES = 21,
- MANAGE_TOPICS = 22,
+ MANAGE_OR_CREATE_TOPICS = 22,
ADD_NEW_ADMINS = 23,
REMAIN_ANONYMOUS = 24;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
index 109a75772a..f32967c8d8 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
@@ -11326,7 +11326,7 @@ public CharSequence buildRestrictionText (@Nullable RestrictionStatus status,
case RightId.EDIT_MESSAGES:
case RightId.INVITE_USERS:
case RightId.MANAGE_VIDEO_CHATS:
- case RightId.MANAGE_TOPICS:
+ case RightId.MANAGE_OR_CREATE_TOPICS:
case RightId.POST_STORIES:
case RightId.EDIT_STORIES:
case RightId.DELETE_STORIES:
@@ -11416,7 +11416,7 @@ public boolean canSendMessage (TdApi.Chat chat, @RightId int kindResId) {
case RightId.EDIT_MESSAGES:
case RightId.INVITE_USERS:
case RightId.MANAGE_VIDEO_CHATS:
- case RightId.MANAGE_TOPICS:
+ case RightId.MANAGE_OR_CREATE_TOPICS:
case RightId.POST_STORIES:
case RightId.EDIT_STORIES:
case RightId.DELETE_STORIES:
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
index bcdee38427..21372f5c6b 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
@@ -315,20 +315,7 @@ public void onClick (View view) {
} else if (viewId == R.id.btn_dismissAdmin) {
showOptions(null, new int[] {R.id.btn_dismissAdmin, R.id.btn_cancel}, new String[] {Lang.getString(R.string.DismissAdmin), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_dismissAdmin && !isDoneInProgress()) {
- targetAdmin.rights.canChangeInfo = false;
- targetAdmin.rights.canManageChat = false;
- targetAdmin.rights.canPostMessages = false;
- targetAdmin.rights.canEditMessages = false;
- targetAdmin.rights.canDeleteMessages = false;
- targetAdmin.rights.canInviteUsers = false;
- targetAdmin.rights.canRestrictMembers = false;
- targetAdmin.rights.canPinMessages = false;
- targetAdmin.rights.canManageVideoChats = false;
- targetAdmin.rights.isAnonymous = false;
- targetAdmin.rights.canPromoteMembers = false;
- targetAdmin.rights.canPostStories = false;
- targetAdmin.rights.canEditStories = false;
- targetAdmin.rights.canDeleteStories = false;
+ Td.setAllAdministratorRights(targetAdmin.rights, false);
updateValues();
setDoneInProgress(true);
setDoneVisible(true);
@@ -400,9 +387,10 @@ private void performRequest (boolean force) {
return;
}
+ TdApi.ChatPermissions chatPermissions = tdlib.chatPermissions(args.chatId);
if (args.mode == MODE_RESTRICTION) {
if (canViewMessages) {
- if (!TD.hasRestrictions(targetRestrict.permissions, tdlib.chatPermissions(args.chatId))) {
+ if (!TD.hasRestrictions(targetRestrict.permissions, chatPermissions)) {
// newStatus = targetRestrict.isMember ? new TdApi.ChatMemberStatusMember() : new TdApi.ChatMemberStatusLeft();
if (args.member == null || !TD.isRestricted(args.member.status)) {
UI.showToast(R.string.NoRestrictionsHint, Toast.LENGTH_SHORT);
@@ -418,6 +406,8 @@ private void performRequest (boolean force) {
} else if (args.member != null && args.member.status.getConstructor() == TdApi.ChatMemberStatusCreator.CONSTRUCTOR) {
TdApi.ChatMemberStatusCreator creator = (TdApi.ChatMemberStatusCreator) args.member.status;
newStatus = new TdApi.ChatMemberStatusCreator(targetAdmin.customTitle, targetAdmin.rights.isAnonymous, creator.isMember);
+ } else if (Td.isEmpty(targetAdmin, chatPermissions)) {
+ newStatus = new TdApi.ChatMemberStatusMember();
} else {
newStatus = targetAdmin;
}
@@ -510,7 +500,7 @@ protected void setValuedSetting (ListItem item, SettingView view, boolean isUpda
view.getToggler().setRadioEnabled(item.getBoolValue(), isUpdate);
view.getToggler().setShowLock(!canEdit);
if (needAddData) {
- view.setData(item.getBoolValue() ? R.string.AllMembers : (rightId == RightId.INVITE_USERS || rightId == RightId.CHANGE_CHAT_INFO || rightId == RightId.PIN_MESSAGES) ? R.string.OnlyAdminsSpecific : R.string.OnlyAdmins);
+ view.setData(item.getBoolValue() ? R.string.AllMembers : (isCommonRight(rightId) || rightId == RightId.MANAGE_OR_CREATE_TOPICS) ? R.string.OnlyAdminsSpecific : R.string.OnlyAdmins);
}
} else if (viewId == R.id.btn_togglePermissionGroup) {
final RightOption option = (RightOption) item.getData();
@@ -702,11 +692,7 @@ private CharSequence getHintForToggleUnavailability (ListItem item) {
break;
}
case MODE_ADMIN_PROMOTION: {
- if (!tdlib.cache().senderBot(args.senderId) && (
- rightId == RightId.INVITE_USERS ||
- rightId == RightId.CHANGE_CHAT_INFO ||
- rightId == RightId.PIN_MESSAGES
- ) && TD.checkRight(tdlib.chatPermissions(args.chatId), rightId) && currentValue) {
+ if (!tdlib.cache().senderBot(args.senderId) && isCommonRight(rightId) && TD.checkRight(tdlib.chatPermissions(args.chatId), rightId) && currentValue) {
int promoteMode = args.member == null ? TD.PROMOTE_MODE_NEW : TD.canPromoteAdmin(args.myStatus, args.member.status);
if (promoteMode != TD.PROMOTE_MODE_NEW && promoteMode != TD.PROMOTE_MODE_EDIT)
return null;
@@ -734,6 +720,17 @@ private CharSequence getHintForToggleUnavailability (ListItem item) {
return null;
}
+ private static boolean isCommonRight (@RightId int rightId) {
+ //noinspection SwitchIntDef
+ switch (rightId) {
+ case RightId.INVITE_USERS:
+ case RightId.CHANGE_CHAT_INFO:
+ case RightId.PIN_MESSAGES:
+ return true;
+ }
+ return false;
+ }
+
private boolean hasAccessToEditRight (ListItem item) {
Args args = getArgumentsStrict();
@RightId final int id;
@@ -830,7 +827,7 @@ private boolean hasAccessToEditRight (int id) {
return me.rights.canPinMessages;
case RightId.MANAGE_VIDEO_CHATS:
return me.rights.canManageVideoChats;
- case RightId.MANAGE_TOPICS:
+ case RightId.MANAGE_OR_CREATE_TOPICS:
return me.rights.canManageTopics;
case RightId.POST_STORIES:
return me.rights.canPostStories;
@@ -1065,38 +1062,28 @@ private void buildCells () {
rightIdOptions.add(new RightOption(RightId.INVITE_USERS));
rightIdOptions.add(new RightOption(RightId.PIN_MESSAGES));
rightIdOptions.add(new RightOption(RightId.CHANGE_CHAT_INFO));
+ if (isForum || getValueForId(RightId.MANAGE_OR_CREATE_TOPICS)) {
+ rightIdOptions.add(new RightOption(RightId.MANAGE_OR_CREATE_TOPICS));
+ }
} else if (args.mode == MODE_RESTRICTION) {
- if (args.senderId.getConstructor() == TdApi.MessageSenderChat.CONSTRUCTOR) {
- rightIdOptions.add(new RightOption(RightId.SEND_BASIC_MESSAGES));
- rightIdOptions.add(new RightOption(R.string.RightSendMedia, SEND_MEDIA_RIGHT_IDS));
- rightIdOptions.add(new RightOption(RightId.INVITE_USERS));
- rightIdOptions.add(new RightOption(RightId.PIN_MESSAGES));
- rightIdOptions.add(new RightOption(RightId.CHANGE_CHAT_INFO));
- } else {
+ if (args.senderId.getConstructor() == TdApi.MessageSenderUser.CONSTRUCTOR) {
rightIdOptions.add(new RightOption(RightId.READ_MESSAGES));
- rightIdOptions.add(new RightOption(RightId.SEND_BASIC_MESSAGES));
- rightIdOptions.add(new RightOption(R.string.RightSendMedia, SEND_MEDIA_RIGHT_IDS));
- rightIdOptions.add(new RightOption(RightId.INVITE_USERS));
- rightIdOptions.add(new RightOption(RightId.PIN_MESSAGES));
- rightIdOptions.add(new RightOption(RightId.CHANGE_CHAT_INFO));
}
- } else if (isChannel) {
+ rightIdOptions.add(new RightOption(RightId.SEND_BASIC_MESSAGES));
+ rightIdOptions.add(new RightOption(R.string.RightSendMedia, SEND_MEDIA_RIGHT_IDS));
+ rightIdOptions.add(new RightOption(RightId.INVITE_USERS));
+ rightIdOptions.add(new RightOption(RightId.PIN_MESSAGES));
+ rightIdOptions.add(new RightOption(RightId.CHANGE_CHAT_INFO));
+ if (isForum || getValueForId(RightId.MANAGE_OR_CREATE_TOPICS)) {
+ rightIdOptions.add(new RightOption(RightId.MANAGE_OR_CREATE_TOPICS));
+ }
+ } else /*args.mode == MODE_ADMIN_PROMOTION*/ if (isChannel) {
rightIdOptions.add(new RightOption(RightId.CHANGE_CHAT_INFO));
rightIdOptions.add(new RightOption(R.string.RightMessages, MANAGE_CHANNEL_POSTS_IDS));
rightIdOptions.add(new RightOption(RightId.INVITE_USERS));
rightIdOptions.add(new RightOption(RightId.MANAGE_VIDEO_CHATS));
rightIdOptions.add(new RightOption(RightId.ADD_NEW_ADMINS));
rightIdOptions.add(new RightOption(R.string.RightStories, MANAGE_STORIES_RIGHT_IDS));
- } else if (isForum) {
- rightIdOptions.add(new RightOption(RightId.CHANGE_CHAT_INFO));
- rightIdOptions.add(new RightOption( RightId.DELETE_MESSAGES));
- rightIdOptions.add(new RightOption(RightId.BAN_USERS));
- rightIdOptions.add(new RightOption(RightId.INVITE_USERS));
- rightIdOptions.add(new RightOption(RightId.PIN_MESSAGES));
- rightIdOptions.add(new RightOption(RightId.MANAGE_VIDEO_CHATS));
- rightIdOptions.add(new RightOption(RightId.MANAGE_TOPICS));
- rightIdOptions.add(new RightOption(RightId.REMAIN_ANONYMOUS));
- rightIdOptions.add(new RightOption(RightId.ADD_NEW_ADMINS));
} else {
rightIdOptions.add(new RightOption(RightId.CHANGE_CHAT_INFO));
rightIdOptions.add(new RightOption(RightId.DELETE_MESSAGES));
@@ -1104,6 +1091,9 @@ private void buildCells () {
rightIdOptions.add(new RightOption(RightId.INVITE_USERS));
rightIdOptions.add(new RightOption(RightId.PIN_MESSAGES));
rightIdOptions.add(new RightOption(RightId.MANAGE_VIDEO_CHATS));
+ if (isForum || getValueForId(RightId.MANAGE_OR_CREATE_TOPICS)) {
+ rightIdOptions.add(new RightOption(RightId.MANAGE_OR_CREATE_TOPICS));
+ }
rightIdOptions.add(new RightOption(RightId.REMAIN_ANONYMOUS));
rightIdOptions.add(new RightOption(RightId.ADD_NEW_ADMINS));
}
@@ -1477,8 +1467,12 @@ private void setValueForRightId (@RightId int id, boolean newValue) {
case RightId.MANAGE_VIDEO_CHATS:
targetAdmin.rights.canManageVideoChats = newValue;
break;
- case RightId.MANAGE_TOPICS:
- targetAdmin.rights.canManageTopics = newValue;
+ case RightId.MANAGE_OR_CREATE_TOPICS:
+ if (getArgumentsStrict().mode == MODE_ADMIN_PROMOTION) {
+ targetAdmin.rights.canManageTopics = newValue;
+ } else {
+ targetRestrict.permissions.canManageTopics = newValue;
+ }
break;
case RightId.POST_STORIES:
targetAdmin.rights.canPostStories = newValue;
@@ -1617,8 +1611,12 @@ private boolean getValueForId (@RightId int id) {
return targetAdmin.rights.canPromoteMembers;
case RightId.MANAGE_VIDEO_CHATS:
return targetAdmin.rights.canManageVideoChats;
- case RightId.MANAGE_TOPICS:
- return targetAdmin.rights.canManageTopics;
+ case RightId.MANAGE_OR_CREATE_TOPICS:
+ if (getArgumentsStrict().mode == MODE_ADMIN_PROMOTION) {
+ return targetAdmin.rights.canManageTopics;
+ } else {
+ return canViewMessages && targetRestrict.permissions.canManageTopics;
+ }
case RightId.POST_STORIES:
return targetAdmin.rights.canPostStories;
case RightId.EDIT_STORIES:
@@ -1673,8 +1671,8 @@ private boolean getValueForId (@RightId int id) {
return R.string.RightEditMessages;
case RightId.MANAGE_VIDEO_CHATS:
return isChannel ? R.string.RightLiveStreams : R.string.RightVoiceChats;
- case RightId.MANAGE_TOPICS:
- return R.string.RightTopics;
+ case RightId.MANAGE_OR_CREATE_TOPICS:
+ return getArgumentsStrict().mode == MODE_ADMIN_PROMOTION ? R.string.RightTopics : R.string.RightTopicsCreate;
case RightId.POST_STORIES:
return R.string.RightStoriesPost;
case RightId.EDIT_STORIES:
@@ -1713,7 +1711,7 @@ private boolean getValueForId (@RightId int id) {
case RightId.MANAGE_VIDEO_CHATS:
return R.drawable.baseline_video_chat_24;
- case RightId.MANAGE_TOPICS:
+ case RightId.MANAGE_OR_CREATE_TOPICS:
return R.drawable.baseline_format_list_bulleted_type_24;
case RightId.POST_STORIES: // todo
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java b/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
index 429a78cb96..4348e24270 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
@@ -1839,7 +1839,11 @@ protected void setValuedSetting (ListItem item, SettingView view, boolean isUpda
break;
}
} else if (itemId == R.id.btn_chatPermissions) {
- view.setData(Lang.plural(R.string.xPermissions, Td.count(chat.permissions), TdConstants.CHAT_PERMISSIONS_COUNT));
+ int totalPermissionsCount = TdConstants.CHAT_PERMISSIONS_COUNT;
+ if (!tdlib.isForum(chat.id)) {
+ totalPermissionsCount--;
+ }
+ view.setData(Lang.plural(R.string.xPermissions, Td.count(chat.permissions), totalPermissionsCount));
} else if (itemId == R.id.btn_enabledReactions) {
TdApi.ChatAvailableReactions availableReactions = chat.availableReactions;
switch (availableReactions.getConstructor()) {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0f2463e47d..eb69f9fbdf 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1837,6 +1837,7 @@
Manage video chats
Manage live streams
Manage topics
+ Create topics
Stories
Messages
Post Stories
@@ -1998,6 +1999,8 @@
Send polls
Change info
Change info
+ Create topics
+ Create topics
Pin messages
Pin messages
Add users
diff --git a/vkryl/td b/vkryl/td
index 3fd09d3347..951d9330d1 160000
--- a/vkryl/td
+++ b/vkryl/td
@@ -1 +1 @@
-Subproject commit 3fd09d3347481de3034070e6077ab25c2b866ebf
+Subproject commit 951d9330d1e40ab9c4724f624f28db0618fc1db7
From 649aad152ef30cbefce8a6d8a498b24bb4f430e3 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 16 Jan 2024 15:02:05 +0400
Subject: [PATCH 11/61] Do not crash if semi built-in sticker is missing
---
.../component/popups/JoinRequestsComponent.java | 9 ++++++---
.../thunderdog/challegram/ui/ChatLinksController.java | 7 +++++--
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/component/popups/JoinRequestsComponent.java b/app/src/main/java/org/thunderdog/challegram/component/popups/JoinRequestsComponent.java
index a48de836ad..62a7543fea 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/popups/JoinRequestsComponent.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/popups/JoinRequestsComponent.java
@@ -289,9 +289,12 @@ private void buildCells () {
ArrayList items = new ArrayList<>();
if (!isBottomSheet && !isSeparateLink && !inSearchMode()) {
- items.add(new ListItem(ListItem.TYPE_EMPTY_OFFSET_SMALL));
- items.add(new ListItem(ListItem.TYPE_EMBED_STICKER).setData(tdlib().findTgxEmoji(UTYAN_EMOJI)));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ TdApi.Sticker tgxEmoji = tdlib().findTgxEmoji(UTYAN_EMOJI);
+ if (tgxEmoji != null) {
+ items.add(new ListItem(ListItem.TYPE_EMPTY_OFFSET_SMALL));
+ items.add(new ListItem(ListItem.TYPE_EMBED_STICKER).setData(tgxEmoji));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ }
}
if (joinRequests != null) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ChatLinksController.java b/app/src/main/java/org/thunderdog/challegram/ui/ChatLinksController.java
index ed01acd28f..57022868fa 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ChatLinksController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ChatLinksController.java
@@ -729,8 +729,11 @@ private void buildCells () {
items.add(new ListItem(ListItem.TYPE_CHAT_SMALL, R.id.btn_openChat).setLongId(adminUserId).setIntValue(inviteLinks.size()));
items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
} else {
- items.add(new ListItem(ListItem.TYPE_EMPTY_OFFSET_SMALL));
- items.add(new ListItem(ListItem.TYPE_EMBED_STICKER).setData(tdlib.findTgxEmoji(UTYAN_EMOJI)));
+ TdApi.Sticker tgxEmoji = tdlib.findTgxEmoji(UTYAN_EMOJI);
+ if (tgxEmoji != null) {
+ items.add(new ListItem(ListItem.TYPE_EMPTY_OFFSET_SMALL));
+ items.add(new ListItem(ListItem.TYPE_EMBED_STICKER).setData(tgxEmoji));
+ }
}
for (TdApi.ChatInviteLink inviteLink : inviteLinks) {
From a2b7dcb8c9823f1024003f0d885e83f1dd49a0aa Mon Sep 17 00:00:00 2001
From: Arseny271 <56611696+Arseny271@users.noreply.github.com>
Date: Tue, 16 Jan 2024 15:08:46 +0300
Subject: [PATCH 12/61] Fixes for table layout logic in Instant Views (#538)
---
.../thunderdog/challegram/data/PageBlockTable.java | 11 +++++------
1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/data/PageBlockTable.java b/app/src/main/java/org/thunderdog/challegram/data/PageBlockTable.java
index 02350b893e..7d01cdf1cf 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/PageBlockTable.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/PageBlockTable.java
@@ -150,15 +150,15 @@ protected int computeHeight (View view, final int maxContentWidth) {
tableCordsX = TableLayout.computeCordsArray(columnsWidth);
for (Cell cell : cellsList) {
- cell.build((int) Math.ceil(tableCordsX[cell.cellPositionEndX()] - tableCordsX[cell.cellPositionStartX()] + 2), false);
+ cell.build((int) Math.ceil(tableCordsX[cell.cellPositionEndX()] - tableCordsX[cell.cellPositionStartX()] + 2));
}
tableCordsY = TableLayout.computeCordsArrayY(cellsList, totalRowsCount, Cell.METRIC_TYPE_CURRENT);
} else {
+ tableCordsX = tableLayoutMinWidth.cellsX;
for (Cell cell : cellsList) {
- cell.build(defaultWidth, true);
+ cell.build((int) Math.ceil(tableCordsX[cell.cellPositionEndX()] - tableCordsX[cell.cellPositionStartX()] + 2));
}
- tableCordsX = tableLayoutMinWidth.cellsX;
- tableCordsY = tableLayoutMinWidth.cellsY;
+ tableCordsY = TableLayout.computeCordsArrayY(cellsList, totalRowsCount, Cell.METRIC_TYPE_CURRENT);
}
customTableWidth = Math.round(tableCordsX[tableCordsX.length - 1]);
@@ -418,9 +418,8 @@ public void prepareToBuild (int maxCellWidth) {
}
}
- public void build (int maxCellWidth, boolean alwaysBreak) {
+ public void build (int maxCellWidth) {
if (text != null) {
- text.setTextFlag(Text.FLAG_ALWAYS_BREAK, alwaysBreak);
text.changeMaxWidth(maxCellWidth - Screen.dp(PADDING_HORIZONTAL) * 2);
}
}
From 49a2971b8b3c7d47f170eccba5b0fc39053d700b Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 16 Jan 2024 20:35:50 +0400
Subject: [PATCH 13/61] Never blur images inside of `TextMedia` (resolved #539)
---
.../java/org/thunderdog/challegram/util/text/TextMedia.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/org/thunderdog/challegram/util/text/TextMedia.java b/app/src/main/java/org/thunderdog/challegram/util/text/TextMedia.java
index 6454dcc0d1..6a3998250b 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/text/TextMedia.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/text/TextMedia.java
@@ -118,6 +118,7 @@ public TextMedia (Text source, Tdlib tdlib, String keyId, long id, TdApi.RichTex
} else {
imageFile = new ImageFile(tdlib, icon.document.document);
imageFile.setSize(Screen.dp(Math.max(icon.width, icon.height)));
+ imageFile.setNoBlur();
}
}
@@ -169,6 +170,7 @@ private void buildCustomEmoji (@NonNull TdlibEmojiManager.Entry customEmoji) {
this.imageFile = new ImageFile(tdlib, sticker.sticker);
this.imageFile.setSize(Math.max(width, height));
this.imageFile.setScaleType(ImageFile.FIT_CENTER);
+ this.imageFile.setNoBlur();
break;
}
}
From 64567828e7ae53e018be3a77ba9add5d69be3933 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Thu, 18 Jan 2024 04:19:00 +0400
Subject: [PATCH 14/61] Strengthen emulator detections
---
app/src/main/AndroidManifest.xml | 5 +++++
.../main/java/org/thunderdog/challegram/MainActivity.java | 7 +++++--
.../challegram/component/attach/MediaLayout.java | 2 +-
vkryl/android | 2 +-
4 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ade7f811a9..07581068c5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -108,6 +108,11 @@
+
+
+
+
+
diff --git a/app/src/main/java/org/thunderdog/challegram/MainActivity.java b/app/src/main/java/org/thunderdog/challegram/MainActivity.java
index fa7e1a8143..9d2735b88d 100644
--- a/app/src/main/java/org/thunderdog/challegram/MainActivity.java
+++ b/app/src/main/java/org/thunderdog/challegram/MainActivity.java
@@ -18,6 +18,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.SystemClock;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
@@ -1450,12 +1451,14 @@ public void onResume () {
UI.startNotificationService();
if (!madeEmulatorChecks && !Settings.instance().isEmulator()) {
madeEmulatorChecks = true;
- Background.instance().post(() -> {
+ new Thread(() -> {
+ long ms = SystemClock.uptimeMillis();
boolean isEmulator = DeviceUtils.detectEmulator(MainActivity.this);
+ Log.v("Ran emulator detections in %dms", ms);
if (isEmulator) {
Settings.instance().markAsEmulator();
}
- });
+ }, "EmulatorDetector").start();
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/attach/MediaLayout.java b/app/src/main/java/org/thunderdog/challegram/component/attach/MediaLayout.java
index c8b9d30dd6..4f185e2f5b 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/attach/MediaLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/attach/MediaLayout.java
@@ -615,7 +615,7 @@ public boolean onBottomPrepareSectionChange (int fromIndex, int toIndex, boolean
boolean googleMapsInstalled;
try {
MapsInitializer.initialize(getContext());
- googleMapsInstalled = DeviceUtils.isAppInstalled(getContext(), U.PACKAGE_GOOGLE_MAPS);
+ googleMapsInstalled = DeviceUtils.isApplicationInstalled(getContext(), U.PACKAGE_GOOGLE_MAPS, false);
} catch (Throwable t) {
googleMapsInstalled = false;
}
diff --git a/vkryl/android b/vkryl/android
index f730b50efb..d6a80bf360 160000
--- a/vkryl/android
+++ b/vkryl/android
@@ -1 +1 @@
-Subproject commit f730b50efbb02f0b48335fbfba6058bf61804fac
+Subproject commit d6a80bf36028b801c73dc997fb3fc90975c22910
From 22f7ba004fd9c0dcffb902bdfc3de4cf540ecdde Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Fri, 19 Jan 2024 21:35:56 +0400
Subject: [PATCH 15/61] Fix rudimentary missing emoji warning for emoji
statuses
---
.../org/thunderdog/challegram/util/EmojiStatusHelper.java | 4 +++-
.../java/org/thunderdog/challegram/util/text/Text.java | 8 +++++++-
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/util/EmojiStatusHelper.java b/app/src/main/java/org/thunderdog/challegram/util/EmojiStatusHelper.java
index 9aee3ce685..4fb8a1c33e 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/EmojiStatusHelper.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/EmojiStatusHelper.java
@@ -485,6 +485,8 @@ public void onAppear () {
long emojiStatusId = user.emojiStatus.customEmojiId;
TdApi.TextEntity emoji = new TdApi.TextEntity(0, 1, new TdApi.TextEntityTypeCustomEmoji(emojiStatusId));
- return new TdApi.FormattedText("*", new TdApi.TextEntity[] {emoji});
+ return new TdApi.FormattedText(EMOJI, new TdApi.TextEntity[] {emoji});
}
+
+ public static final String EMOJI = "*";
}
diff --git a/app/src/main/java/org/thunderdog/challegram/util/text/Text.java b/app/src/main/java/org/thunderdog/challegram/util/text/Text.java
index 250113e62d..439882c130 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/text/Text.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/text/Text.java
@@ -57,6 +57,7 @@
import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.tool.Views;
import org.thunderdog.challegram.unsorted.Settings;
+import org.thunderdog.challegram.util.EmojiStatusHelper;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -1251,7 +1252,12 @@ private void processTextOrEmoji (final String in, final int start, final int end
if (entity != null && entity.isCustomEmoji()) {
String emojiCode = in.substring(start, end);
- EmojiInfo info = Emoji.instance().getEmojiInfo(emojiCode, true);
+ EmojiInfo info;
+ if (!EmojiStatusHelper.EMOJI.equals(emojiCode)) {
+ info = Emoji.instance().getEmojiInfo(emojiCode, true);
+ } else {
+ info = null;
+ }
processEmoji(in, start, end, info, out, entity);
return;
}
From 362d671ca76680c7aac5e0892b734b41194e722b Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 00:32:59 +0400
Subject: [PATCH 16/61] Disable `GL_RENDERER`-based emulator detection for now
---
vkryl/android | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/vkryl/android b/vkryl/android
index d6a80bf360..dde7a1957c 160000
--- a/vkryl/android
+++ b/vkryl/android
@@ -1 +1 @@
-Subproject commit d6a80bf36028b801c73dc997fb3fc90975c22910
+Subproject commit dde7a1957cb145158e6ad30bbe8be7f551ce26a6
From 6f8febf7b4fe0f876aa327f62cc5e2aed999b67b Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 01:01:59 +0400
Subject: [PATCH 17/61] Optimize emulator detection
---
.../thunderdog/challegram/BaseActivity.java | 54 +++++++++++++++++++
.../thunderdog/challegram/MainActivity.java | 17 ++----
.../challegram/ui/PhoneController.java | 4 ++
.../challegram/unsorted/Settings.java | 45 ++++++++++++++--
4 files changed, 102 insertions(+), 18 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/BaseActivity.java b/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
index 6b9bd011b2..65e8e892aa 100644
--- a/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
+++ b/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
@@ -40,6 +40,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemClock;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.Display;
@@ -136,14 +137,18 @@
import java.lang.ref.Reference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import me.vkryl.android.AnimatorUtils;
+import me.vkryl.android.DeviceUtils;
import me.vkryl.android.animator.FactorAnimator;
import me.vkryl.android.widget.FrameLayoutFix;
import me.vkryl.core.BitwiseUtils;
import me.vkryl.core.ColorUtils;
import me.vkryl.core.lambda.CancellableRunnable;
+import me.vkryl.core.lambda.FutureBool;
import me.vkryl.core.lambda.FutureInt;
import me.vkryl.core.lambda.RunnableBool;
import me.vkryl.core.reference.ReferenceList;
@@ -320,9 +325,57 @@ protected final void setTdlib (Tdlib tdlib) {
drawer.onCurrentTdlibChanged(tdlib);
}
onTdlibChanged();
+ runEmulatorChecks(false);
}
}
+ private boolean ranEmulatorChecks;
+
+ public void runEmulatorChecks (boolean force) {
+ if (Settings.instance().isEmulator() || ranEmulatorChecks) {
+ return;
+ }
+ long installationId = Settings.instance().installationId();
+ if (!force) {
+ List conditions = Arrays.asList(
+ () -> !tdlib.context().hasActiveAccounts() || tdlib.isUnauthorized(),
+ () -> {
+ Settings.EmulatorDetectionResult detectionResult = Settings.instance().getLastEmulatorDetectionResult();
+ if (detectionResult != null) {
+ long elapsed = System.currentTimeMillis() - detectionResult.time;
+ return installationId != detectionResult.installationId || elapsed >= TimeUnit.DAYS.toMillis(3);
+ }
+ // every 2 days or after every update
+ return true;
+ }
+ );
+ boolean hasAnyReason = false;
+ for (FutureBool condition : conditions) {
+ if (condition.getBoolValue()) {
+ hasAnyReason = true;
+ break;
+ }
+ }
+ if (!hasAnyReason) {
+ return;
+ }
+ }
+
+ ranEmulatorChecks = true;
+
+ // Impl
+ new Thread(() -> {
+ long ms = SystemClock.uptimeMillis();
+ boolean isEmulator = DeviceUtils.detectEmulator(BaseActivity.this);
+ long elapsed = SystemClock.uptimeMillis() - ms;
+ Log.v("Ran emulator detections in %dms", elapsed);
+ Settings.instance().trackEmulatorDetectionResult(installationId, elapsed, isEmulator);
+ if (isEmulator) {
+ tdlib.context().setIsEmulator(true);
+ }
+ }, "EmulatorDetector").start();
+ }
+
protected void onTdlibChanged () {
// override
}
@@ -1030,6 +1083,7 @@ public void onResume () {
}
}*/
appUpdater.checkForUpdates();
+ runEmulatorChecks(false);
}
protected void setOnline (boolean isOnline) {
diff --git a/app/src/main/java/org/thunderdog/challegram/MainActivity.java b/app/src/main/java/org/thunderdog/challegram/MainActivity.java
index 9d2735b88d..4a9b55ad5d 100644
--- a/app/src/main/java/org/thunderdog/challegram/MainActivity.java
+++ b/app/src/main/java/org/thunderdog/challegram/MainActivity.java
@@ -34,7 +34,6 @@
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.config.Config;
-import org.thunderdog.challegram.core.Background;
import org.thunderdog.challegram.core.Lang;
import org.thunderdog.challegram.data.TD;
import org.thunderdog.challegram.helper.LiveLocationHelper;
@@ -96,8 +95,10 @@
import org.thunderdog.challegram.widget.NoScrollTextView;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -108,6 +109,7 @@
import me.vkryl.core.ArrayUtils;
import me.vkryl.core.StringUtils;
import me.vkryl.core.lambda.CancellableRunnable;
+import me.vkryl.core.lambda.FutureBool;
import me.vkryl.core.lambda.RunnableData;
import me.vkryl.td.MessageId;
@@ -1438,8 +1440,6 @@ public void initDefault (int accountId, boolean allowAsync) {
}
}
- private boolean madeEmulatorChecks;
-
@Override
public void onResume () {
super.onResume();
@@ -1449,17 +1449,6 @@ public void onResume () {
tdlib.context().global().notifyResolvableProblemAvailabilityMightHaveChanged();
tdlib.context().dateManager().checkCurrentDate();
UI.startNotificationService();
- if (!madeEmulatorChecks && !Settings.instance().isEmulator()) {
- madeEmulatorChecks = true;
- new Thread(() -> {
- long ms = SystemClock.uptimeMillis();
- boolean isEmulator = DeviceUtils.detectEmulator(MainActivity.this);
- Log.v("Ran emulator detections in %dms", ms);
- if (isEmulator) {
- Settings.instance().markAsEmulator();
- }
- }, "EmulatorDetector").start();
- }
}
@Override
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
index 999d712aff..028dae0bd0 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
@@ -36,6 +36,7 @@
import androidx.recyclerview.widget.RecyclerView;
import org.drinkless.tdlib.TdApi;
+import org.thunderdog.challegram.MainActivity;
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.component.base.SettingView;
import org.thunderdog.challegram.config.Config;
@@ -1072,6 +1073,9 @@ private void makeTestRequest () {
@Override
public void onFocus () {
super.onFocus();
+ if (mode == MODE_LOGIN && isAccountAdd) {
+ context.runEmulatorChecks(true);
+ }
if (oneShot) {
return;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java b/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
index 717e7d7d01..b54e6d27dc 100644
--- a/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
+++ b/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
@@ -332,7 +332,8 @@ public static String accountInfoPrefix (int accountId) {
private static final String KEY_PUSH_REPORTED_ERROR = "push_reported_error";
private static final String KEY_PUSH_REPORTED_ERROR_DATE = "push_reported_error_date";
private static final String KEY_CRASH_DEVICE_ID = "crash_device_id";
- public static final String KEY_IS_EMULATOR = "is_emulator";
+ private static final String KEY_IS_EMULATOR = "is_emulator";
+ private static final String KEY_EMULATOR_DETECTION_RESULT = "emulator";
private static final @Deprecated String KEY_EMOJI_COUNTERS_OLD = "counters_v2";
private static final @Deprecated String KEY_EMOJI_RECENTS_OLD = "recents_v2";
@@ -6187,10 +6188,46 @@ public boolean isEmulator () {
return pmc.getBoolean(KEY_IS_EMULATOR, false);
}
- public void markAsEmulator () {
- if (!isEmulator()) {
+ public static class EmulatorDetectionResult {
+ public static final int FLAG_EMULATOR_DETECTED = 1;
+
+ public final long time, installationId, elapsed, flags;
+
+ public EmulatorDetectionResult (long time, long installationId, long elapsed, long flags) {
+ this.time = time;
+ this.installationId = installationId;
+ this.elapsed = elapsed;
+ this.flags = flags;
+ }
+
+ public boolean isDetected () {
+ return BitwiseUtils.hasFlag(flags, FLAG_EMULATOR_DETECTED);
+ }
+ }
+
+ @Nullable
+ public EmulatorDetectionResult getLastEmulatorDetectionResult () {
+ long[] emulatorDetectionResult = pmc.getLongArray(KEY_EMULATOR_DETECTION_RESULT);
+ if (emulatorDetectionResult == null) {
+ return null;
+ }
+ return new EmulatorDetectionResult(
+ emulatorDetectionResult[0],
+ emulatorDetectionResult[1],
+ emulatorDetectionResult[2],
+ emulatorDetectionResult[3]
+ );
+ }
+
+ public void trackEmulatorDetectionResult (long installationId, long elapsed, boolean isEmulator) {
+ long[] result = new long[] {
+ System.currentTimeMillis(),
+ installationId,
+ isEmulator ? EmulatorDetectionResult.FLAG_EMULATOR_DETECTED : 0
+ };
+ pmc.putLongArray(KEY_EMULATOR_DETECTION_RESULT, result);
+ if (isEmulator) {
putBoolean(KEY_IS_EMULATOR, true);
- TdlibManager.instance().setIsEmulator(true);
}
}
From b6796e2c5aff028bdad7964517e854a59966e09f Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 01:09:25 +0400
Subject: [PATCH 18/61] Exclude some emulator checks on non-experimental builds
---
app/src/main/java/org/thunderdog/challegram/BaseActivity.java | 2 +-
vkryl/android | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/BaseActivity.java b/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
index 65e8e892aa..6e4c17db6b 100644
--- a/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
+++ b/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
@@ -366,7 +366,7 @@ public void runEmulatorChecks (boolean force) {
// Impl
new Thread(() -> {
long ms = SystemClock.uptimeMillis();
- boolean isEmulator = DeviceUtils.detectEmulator(BaseActivity.this);
+ boolean isEmulator = DeviceUtils.detectEmulator(BaseActivity.this, BuildConfig.EXPERIMENTAL);
long elapsed = SystemClock.uptimeMillis() - ms;
Log.v("Ran emulator detections in %dms", elapsed);
Settings.instance().trackEmulatorDetectionResult(installationId, elapsed, isEmulator);
diff --git a/vkryl/android b/vkryl/android
index dde7a1957c..14e7a1bf06 160000
--- a/vkryl/android
+++ b/vkryl/android
@@ -1 +1 @@
-Subproject commit dde7a1957cb145158e6ad30bbe8be7f551ce26a6
+Subproject commit 14e7a1bf060b597eeb31df2b7aea5c4eab81b2ba
From 99085f9c63ffa63808fecc89bf6523657b74af00 Mon Sep 17 00:00:00 2001
From: tgx-server
Date: Fri, 19 Jan 2024 21:11:50 +0000
Subject: [PATCH 19/61] Version bump to `1680`
---
version.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.properties b/version.properties
index ab5259b985..18a2392d87 100644
--- a/version.properties
+++ b/version.properties
@@ -1,5 +1,5 @@
# App
-version.app=1679
+version.app=1680
version.major=0
# Anchor date point in app versioning
version.creation=873642600564
From a7f0fc5090180a9d754cd8ea4c83f2ca47029a07 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 13:17:50 +0400
Subject: [PATCH 20/61] Optimize imports
Code cleanup
---
app/src/main/java/org/drinkmore/Tracer.java | 3 +--
.../java/org/thunderdog/challegram/FillingDrawable.java | 2 +-
.../main/java/org/thunderdog/challegram/MainActivity.java | 5 -----
.../challegram/component/attach/CustomItemAnimator.java | 2 --
.../component/attach/GridSpacingItemDecoration.java | 2 +-
.../challegram/component/attach/MediaLocationMapView.java | 1 -
.../challegram/component/chat/CircleCounterBadgeView.java | 1 -
.../challegram/component/preview/PreviewLayout.java | 2 --
.../thunderdog/challegram/component/user/BubbleView2.java | 6 ------
.../challegram/component/user/BubbleWrapView2.java | 1 -
.../challegram/component/user/SimpleUsersAdapter.java | 1 -
app/src/main/java/org/thunderdog/challegram/core/Media.java | 3 +--
.../java/org/thunderdog/challegram/filegen/VideoData.java | 2 +-
.../java/org/thunderdog/challegram/loader/ImageActor.java | 1 -
.../org/thunderdog/challegram/mediaview/CheckCircle.java | 2 +-
.../thunderdog/challegram/mediaview/ColorPickerWrap.java | 1 -
.../challegram/mediaview/MediaFilterNameView.java | 2 +-
.../challegram/mediaview/MediaViewThumbLocation.java | 3 +--
.../org/thunderdog/challegram/mediaview/SliderView.java | 3 +--
.../thunderdog/challegram/mediaview/crop/CropAreaView.java | 1 -
.../thunderdog/challegram/navigation/BackHeaderButton.java | 3 +--
.../thunderdog/challegram/navigation/CounterHeaderView.java | 2 +-
.../thunderdog/challegram/navigation/DoubleHeaderView.java | 2 +-
.../thunderdog/challegram/navigation/EditHeaderView.java | 2 --
.../org/thunderdog/challegram/navigation/HeaderButton.java | 2 +-
.../thunderdog/challegram/navigation/OverlayButtonWrap.java | 3 +--
.../navigation/ViewPagerHeaderViewReactionsCompact.java | 1 -
.../thunderdog/challegram/receiver/RefreshRateLimiter.java | 1 -
.../org/thunderdog/challegram/support/FillingDrawable.java | 2 +-
.../org/thunderdog/challegram/support/RippleSupport.java | 2 +-
.../thunderdog/challegram/support/SimpleShapeDrawable.java | 2 +-
.../org/thunderdog/challegram/telegram/EmojiMediaType.java | 2 --
.../org/thunderdog/challegram/telegram/TdlibCounter.java | 3 ++-
.../thunderdog/challegram/telegram/TdlibEmojiManager.java | 1 -
.../org/thunderdog/challegram/theme/ThemeListenerEntry.java | 2 +-
.../org/thunderdog/challegram/theme/ThemeListenerList.java | 2 --
app/src/main/java/org/thunderdog/challegram/tool/Views.java | 1 -
.../thunderdog/challegram/ui/BottomSheetViewController.java | 1 -
.../thunderdog/challegram/ui/ChatFolderIconSelector.java | 1 -
.../thunderdog/challegram/ui/EmojiStatusListController.java | 2 +-
.../java/org/thunderdog/challegram/ui/PhoneController.java | 1 -
.../java/org/thunderdog/challegram/ui/SettingHolder.java | 2 +-
.../java/org/thunderdog/challegram/ui/SettingsAdapter.java | 2 +-
.../thunderdog/challegram/ui/SettingsBlockedController.java | 2 --
.../org/thunderdog/challegram/ui/SettingsController.java | 1 -
.../challegram/ui/SettingsPrivacyKeyController.java | 2 +-
.../challegram/ui/StickersTrendingController.java | 1 -
.../java/org/thunderdog/challegram/ui/ThemeController.java | 2 +-
.../org/thunderdog/challegram/ui/camera/CameraDelegate.java | 2 --
.../org/thunderdog/challegram/ui/camera/CameraQrBridge.java | 4 ----
.../challegram/ui/camera/CameraQrCodeRootLayout.java | 1 -
.../thunderdog/challegram/ui/camera/CameraRootLayout.java | 1 -
.../org/thunderdog/challegram/util/AvatarDrawModifier.java | 4 ----
app/src/main/java/org/thunderdog/challegram/util/Crash.java | 1 -
.../org/thunderdog/challegram/util/LineDrawModifier.java | 2 +-
.../challegram/util/ReactionsCounterDrawable.java | 3 ---
.../java/org/thunderdog/challegram/widget/ClearButton.java | 2 +-
.../org/thunderdog/challegram/widget/FillingDecoration.java | 3 +--
.../thunderdog/challegram/widget/InfiniteRecyclerView.java | 1 -
.../thunderdog/challegram/widget/MaterialEditTextGroup.java | 2 +-
.../org/thunderdog/challegram/widget/RippleRevealView.java | 3 +--
.../org/thunderdog/challegram/widget/SeparatorView.java | 3 +--
.../org/thunderdog/challegram/widget/SettingStupidView.java | 2 +-
.../challegram/widget/SimpleMediaWrapperView.java | 2 +-
.../org/thunderdog/challegram/widget/SliderWrapView.java | 2 +-
65 files changed, 34 insertions(+), 98 deletions(-)
diff --git a/app/src/main/java/org/drinkmore/Tracer.java b/app/src/main/java/org/drinkmore/Tracer.java
index 92656a5e7a..e83d2b9930 100644
--- a/app/src/main/java/org/drinkmore/Tracer.java
+++ b/app/src/main/java/org/drinkmore/Tracer.java
@@ -18,15 +18,14 @@
import androidx.annotation.Keep;
import androidx.annotation.Nullable;
-import org.drinkless.tdlib.Client;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.Log;
import org.thunderdog.challegram.N;
import org.thunderdog.challegram.data.TD;
import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.telegram.TdlibAccount;
-import org.thunderdog.challegram.util.Crash;
import org.thunderdog.challegram.unsorted.Settings;
+import org.thunderdog.challegram.util.Crash;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/app/src/main/java/org/thunderdog/challegram/FillingDrawable.java b/app/src/main/java/org/thunderdog/challegram/FillingDrawable.java
index 89990ec841..7c760b8a39 100644
--- a/app/src/main/java/org/thunderdog/challegram/FillingDrawable.java
+++ b/app/src/main/java/org/thunderdog/challegram/FillingDrawable.java
@@ -24,8 +24,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ThemeDelegate;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
diff --git a/app/src/main/java/org/thunderdog/challegram/MainActivity.java b/app/src/main/java/org/thunderdog/challegram/MainActivity.java
index 4a9b55ad5d..14ed2bbb20 100644
--- a/app/src/main/java/org/thunderdog/challegram/MainActivity.java
+++ b/app/src/main/java/org/thunderdog/challegram/MainActivity.java
@@ -18,7 +18,6 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
-import android.os.SystemClock;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
@@ -95,21 +94,17 @@
import org.thunderdog.challegram.widget.NoScrollTextView;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import me.vkryl.android.AnimatorUtils;
-import me.vkryl.android.DeviceUtils;
import me.vkryl.android.animator.BoolAnimator;
import me.vkryl.android.animator.FactorAnimator;
import me.vkryl.core.ArrayUtils;
import me.vkryl.core.StringUtils;
import me.vkryl.core.lambda.CancellableRunnable;
-import me.vkryl.core.lambda.FutureBool;
import me.vkryl.core.lambda.RunnableData;
import me.vkryl.td.MessageId;
diff --git a/app/src/main/java/org/thunderdog/challegram/component/attach/CustomItemAnimator.java b/app/src/main/java/org/thunderdog/challegram/component/attach/CustomItemAnimator.java
index 61827f51ae..0989cd4dea 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/attach/CustomItemAnimator.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/attach/CustomItemAnimator.java
@@ -24,8 +24,6 @@
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
-import org.thunderdog.challegram.component.chat.MessageView;
-
import java.util.ArrayList;
import java.util.List;
diff --git a/app/src/main/java/org/thunderdog/challegram/component/attach/GridSpacingItemDecoration.java b/app/src/main/java/org/thunderdog/challegram/component/attach/GridSpacingItemDecoration.java
index 00ba51a608..82ce369ac7 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/attach/GridSpacingItemDecoration.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/attach/GridSpacingItemDecoration.java
@@ -24,8 +24,8 @@
import androidx.recyclerview.widget.RecyclerView;
import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import me.vkryl.core.ColorUtils;
diff --git a/app/src/main/java/org/thunderdog/challegram/component/attach/MediaLocationMapView.java b/app/src/main/java/org/thunderdog/challegram/component/attach/MediaLocationMapView.java
index 3fbd9ae67a..bd49987153 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/attach/MediaLocationMapView.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/attach/MediaLocationMapView.java
@@ -48,7 +48,6 @@
import org.thunderdog.challegram.BaseActivity;
import org.thunderdog.challegram.Log;
import org.thunderdog.challegram.R;
-import org.thunderdog.challegram.U;
import org.thunderdog.challegram.core.Background;
import org.thunderdog.challegram.navigation.HeaderView;
import org.thunderdog.challegram.navigation.ViewController;
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/CircleCounterBadgeView.java b/app/src/main/java/org/thunderdog/challegram/component/chat/CircleCounterBadgeView.java
index 0f620eec2c..1985314622 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/CircleCounterBadgeView.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/CircleCounterBadgeView.java
@@ -13,7 +13,6 @@
import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.tool.Screen;
-import org.thunderdog.challegram.ui.MessagesController;
import org.thunderdog.challegram.widget.CircleButton;
import me.vkryl.android.AnimatorUtils;
diff --git a/app/src/main/java/org/thunderdog/challegram/component/preview/PreviewLayout.java b/app/src/main/java/org/thunderdog/challegram/component/preview/PreviewLayout.java
index 071d314124..0fafee98c3 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/preview/PreviewLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/preview/PreviewLayout.java
@@ -27,7 +27,6 @@
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.BaseActivity;
-import org.thunderdog.challegram.BuildConfig;
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.core.Lang;
import org.thunderdog.challegram.data.EmbeddedService;
@@ -42,7 +41,6 @@
import org.thunderdog.challegram.widget.PopupLayout;
import me.vkryl.android.widget.FrameLayoutFix;
-import me.vkryl.core.StringUtils;
public abstract class PreviewLayout extends FrameLayoutFix implements View.OnClickListener, PopupLayout.ShowListener, PopupLayout.DismissListener {
protected EmbeddedService nativeEmbed;
diff --git a/app/src/main/java/org/thunderdog/challegram/component/user/BubbleView2.java b/app/src/main/java/org/thunderdog/challegram/component/user/BubbleView2.java
index 35fcda730b..9c4d7147b1 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/user/BubbleView2.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/user/BubbleView2.java
@@ -15,7 +15,6 @@
package org.thunderdog.challegram.component.user;
import android.graphics.Canvas;
-import android.graphics.Color;
import android.graphics.Path;
import android.graphics.RectF;
import android.text.TextPaint;
@@ -23,8 +22,6 @@
import android.view.MotionEvent;
import android.view.View;
-import androidx.annotation.Nullable;
-
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.U;
import org.thunderdog.challegram.core.Lang;
@@ -32,12 +29,9 @@
import org.thunderdog.challegram.loader.AvatarReceiver;
import org.thunderdog.challegram.loader.ComplexReceiver;
import org.thunderdog.challegram.loader.Receiver;
-import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.support.ViewSupport;
import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.telegram.TdlibSender;
-import org.thunderdog.challegram.theme.ColorId;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
diff --git a/app/src/main/java/org/thunderdog/challegram/component/user/BubbleWrapView2.java b/app/src/main/java/org/thunderdog/challegram/component/user/BubbleWrapView2.java
index 59c5e8a82e..6c2e5495b5 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/user/BubbleWrapView2.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/user/BubbleWrapView2.java
@@ -22,7 +22,6 @@
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.data.TGInlineKeyboard;
import org.thunderdog.challegram.loader.ComplexReceiver;
import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.tool.Fonts;
diff --git a/app/src/main/java/org/thunderdog/challegram/component/user/SimpleUsersAdapter.java b/app/src/main/java/org/thunderdog/challegram/component/user/SimpleUsersAdapter.java
index 65a5e75b71..2b2c52a0a3 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/user/SimpleUsersAdapter.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/user/SimpleUsersAdapter.java
@@ -23,7 +23,6 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import org.thunderdog.challegram.R;
import org.thunderdog.challegram.U;
import org.thunderdog.challegram.component.attach.MeasuredAdapterDelegate;
import org.thunderdog.challegram.data.TGUser;
diff --git a/app/src/main/java/org/thunderdog/challegram/core/Media.java b/app/src/main/java/org/thunderdog/challegram/core/Media.java
index 48a16df413..8a7d7ddf93 100644
--- a/app/src/main/java/org/thunderdog/challegram/core/Media.java
+++ b/app/src/main/java/org/thunderdog/challegram/core/Media.java
@@ -46,7 +46,6 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -54,8 +53,8 @@
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
-import me.vkryl.core.StringUtils;
import me.vkryl.core.BitwiseUtils;
+import me.vkryl.core.StringUtils;
public class Media {
public static final String DATE_COLUMN = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? MediaStore.Images.Media.DATE_MODIFIED : MediaStore.Images.Media.DATE_TAKEN;
diff --git a/app/src/main/java/org/thunderdog/challegram/filegen/VideoData.java b/app/src/main/java/org/thunderdog/challegram/filegen/VideoData.java
index b7d80804f8..88d3070ad8 100644
--- a/app/src/main/java/org/thunderdog/challegram/filegen/VideoData.java
+++ b/app/src/main/java/org/thunderdog/challegram/filegen/VideoData.java
@@ -45,11 +45,11 @@
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import me.vkryl.core.BitwiseUtils;
import me.vkryl.core.MathUtils;
import me.vkryl.core.StringUtils;
import me.vkryl.core.collection.SparseLongArray;
import me.vkryl.core.lambda.RunnableLong;
-import me.vkryl.core.BitwiseUtils;
public class VideoData {
private final String sourcePath;
diff --git a/app/src/main/java/org/thunderdog/challegram/loader/ImageActor.java b/app/src/main/java/org/thunderdog/challegram/loader/ImageActor.java
index 0f4420780e..a381cd9d2a 100644
--- a/app/src/main/java/org/thunderdog/challegram/loader/ImageActor.java
+++ b/app/src/main/java/org/thunderdog/challegram/loader/ImageActor.java
@@ -24,7 +24,6 @@
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.Log;
import org.thunderdog.challegram.config.Config;
-import org.thunderdog.challegram.data.TD;
import org.thunderdog.challegram.player.AudioController;
import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.telegram.TdlibManager;
diff --git a/app/src/main/java/org/thunderdog/challegram/mediaview/CheckCircle.java b/app/src/main/java/org/thunderdog/challegram/mediaview/CheckCircle.java
index 201f7a5ef3..199038ddb0 100644
--- a/app/src/main/java/org/thunderdog/challegram/mediaview/CheckCircle.java
+++ b/app/src/main/java/org/thunderdog/challegram/mediaview/CheckCircle.java
@@ -19,8 +19,8 @@
import android.graphics.Paint;
import android.view.View;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.Views;
diff --git a/app/src/main/java/org/thunderdog/challegram/mediaview/ColorPickerWrap.java b/app/src/main/java/org/thunderdog/challegram/mediaview/ColorPickerWrap.java
index f0352e6def..5fd7c143aa 100644
--- a/app/src/main/java/org/thunderdog/challegram/mediaview/ColorPickerWrap.java
+++ b/app/src/main/java/org/thunderdog/challegram/mediaview/ColorPickerWrap.java
@@ -21,7 +21,6 @@
import androidx.recyclerview.widget.RecyclerView;
-import org.thunderdog.challegram.R;
import org.thunderdog.challegram.mediaview.data.FiltersState;
import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.tool.Screen;
diff --git a/app/src/main/java/org/thunderdog/challegram/mediaview/MediaFilterNameView.java b/app/src/main/java/org/thunderdog/challegram/mediaview/MediaFilterNameView.java
index da126c8dc9..b767c0e5a6 100644
--- a/app/src/main/java/org/thunderdog/challegram/mediaview/MediaFilterNameView.java
+++ b/app/src/main/java/org/thunderdog/challegram/mediaview/MediaFilterNameView.java
@@ -25,8 +25,8 @@
import androidx.annotation.Nullable;
import org.thunderdog.challegram.navigation.ViewController;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Fonts;
import org.thunderdog.challegram.widget.NoScrollTextView;
diff --git a/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewThumbLocation.java b/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewThumbLocation.java
index feb74d31ca..de77e912de 100644
--- a/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewThumbLocation.java
+++ b/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewThumbLocation.java
@@ -19,9 +19,8 @@
import android.graphics.Path;
import android.graphics.RectF;
-import org.thunderdog.challegram.R;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.DrawAlgorithms;
import org.thunderdog.challegram.tool.Paints;
diff --git a/app/src/main/java/org/thunderdog/challegram/mediaview/SliderView.java b/app/src/main/java/org/thunderdog/challegram/mediaview/SliderView.java
index e34df44203..97aa889729 100644
--- a/app/src/main/java/org/thunderdog/challegram/mediaview/SliderView.java
+++ b/app/src/main/java/org/thunderdog/challegram/mediaview/SliderView.java
@@ -21,9 +21,8 @@
import android.view.MotionEvent;
import android.view.View;
-import org.thunderdog.challegram.R;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
diff --git a/app/src/main/java/org/thunderdog/challegram/mediaview/crop/CropAreaView.java b/app/src/main/java/org/thunderdog/challegram/mediaview/crop/CropAreaView.java
index 4d0a52d4b5..8a3f1ca7ff 100644
--- a/app/src/main/java/org/thunderdog/challegram/mediaview/crop/CropAreaView.java
+++ b/app/src/main/java/org/thunderdog/challegram/mediaview/crop/CropAreaView.java
@@ -20,7 +20,6 @@
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
-import android.os.Build;
import android.view.MotionEvent;
import android.view.View;
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/BackHeaderButton.java b/app/src/main/java/org/thunderdog/challegram/navigation/BackHeaderButton.java
index b5fc91dd61..cbfe00be2e 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/BackHeaderButton.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/BackHeaderButton.java
@@ -23,10 +23,9 @@
import androidx.annotation.Nullable;
import org.thunderdog.challegram.BaseActivity;
-import org.thunderdog.challegram.R;
import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ThemeDelegate;
import org.thunderdog.challegram.theme.ThemedColorAnimator;
import org.thunderdog.challegram.tool.Paints;
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/CounterHeaderView.java b/app/src/main/java/org/thunderdog/challegram/navigation/CounterHeaderView.java
index 64dded2871..aa9d02fd9a 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/CounterHeaderView.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/CounterHeaderView.java
@@ -28,8 +28,8 @@
import androidx.annotation.NonNull;
import org.thunderdog.challegram.U;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.Strings;
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/DoubleHeaderView.java b/app/src/main/java/org/thunderdog/challegram/navigation/DoubleHeaderView.java
index 68da87f974..a637c464d2 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/DoubleHeaderView.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/DoubleHeaderView.java
@@ -31,8 +31,8 @@
import org.thunderdog.challegram.U;
import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Drawables;
import org.thunderdog.challegram.tool.Fonts;
import org.thunderdog.challegram.tool.Paints;
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/EditHeaderView.java b/app/src/main/java/org/thunderdog/challegram/navigation/EditHeaderView.java
index 752a887653..dff6f3230b 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/EditHeaderView.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/EditHeaderView.java
@@ -30,8 +30,6 @@
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.loader.ImageFile;
-import org.thunderdog.challegram.loader.ImageFileLocal;
import org.thunderdog.challegram.loader.ImageGalleryFile;
import org.thunderdog.challegram.loader.ImageReceiver;
import org.thunderdog.challegram.tool.Drawables;
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/HeaderButton.java b/app/src/main/java/org/thunderdog/challegram/navigation/HeaderButton.java
index 35078dc42f..3b60d84f9d 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/HeaderButton.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/HeaderButton.java
@@ -23,8 +23,8 @@
import androidx.annotation.DrawableRes;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Drawables;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/OverlayButtonWrap.java b/app/src/main/java/org/thunderdog/challegram/navigation/OverlayButtonWrap.java
index 1f242e68d3..0a4ba49e49 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/OverlayButtonWrap.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/OverlayButtonWrap.java
@@ -34,11 +34,10 @@
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
-import org.thunderdog.challegram.R;
import org.thunderdog.challegram.core.Lang;
import org.thunderdog.challegram.support.RippleSupport;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Anim;
import org.thunderdog.challegram.tool.Fonts;
import org.thunderdog.challegram.tool.Screen;
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerHeaderViewReactionsCompact.java b/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerHeaderViewReactionsCompact.java
index 43e421aedc..05ef3c9882 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerHeaderViewReactionsCompact.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerHeaderViewReactionsCompact.java
@@ -46,7 +46,6 @@
import org.thunderdog.challegram.widget.ReactionsSelectorRecyclerView;
import me.vkryl.android.widget.FrameLayoutFix;
-import me.vkryl.core.ColorUtils;
import me.vkryl.core.MathUtils;
@SuppressLint("ViewConstructor")
diff --git a/app/src/main/java/org/thunderdog/challegram/receiver/RefreshRateLimiter.java b/app/src/main/java/org/thunderdog/challegram/receiver/RefreshRateLimiter.java
index 591db08078..fa1388694a 100644
--- a/app/src/main/java/org/thunderdog/challegram/receiver/RefreshRateLimiter.java
+++ b/app/src/main/java/org/thunderdog/challegram/receiver/RefreshRateLimiter.java
@@ -28,7 +28,6 @@
import org.thunderdog.challegram.tool.Screen;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
public class RefreshRateLimiter implements ComplexReceiverUpdateListener, ReceiverUpdateListener {
diff --git a/app/src/main/java/org/thunderdog/challegram/support/FillingDrawable.java b/app/src/main/java/org/thunderdog/challegram/support/FillingDrawable.java
index d78e56f510..b069f2eaa0 100644
--- a/app/src/main/java/org/thunderdog/challegram/support/FillingDrawable.java
+++ b/app/src/main/java/org/thunderdog/challegram/support/FillingDrawable.java
@@ -24,8 +24,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ThemeDelegate;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
diff --git a/app/src/main/java/org/thunderdog/challegram/support/RippleSupport.java b/app/src/main/java/org/thunderdog/challegram/support/RippleSupport.java
index e246f558a2..ddcac2c9e4 100644
--- a/app/src/main/java/org/thunderdog/challegram/support/RippleSupport.java
+++ b/app/src/main/java/org/thunderdog/challegram/support/RippleSupport.java
@@ -23,8 +23,8 @@
import androidx.annotation.Nullable;
import org.thunderdog.challegram.navigation.ViewController;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Views;
import org.thunderdog.challegram.util.ColorChangeAcceptorDelegate;
import org.thunderdog.challegram.util.CustomStateListDrawable;
diff --git a/app/src/main/java/org/thunderdog/challegram/support/SimpleShapeDrawable.java b/app/src/main/java/org/thunderdog/challegram/support/SimpleShapeDrawable.java
index 09870f19ad..433aac488c 100644
--- a/app/src/main/java/org/thunderdog/challegram/support/SimpleShapeDrawable.java
+++ b/app/src/main/java/org/thunderdog/challegram/support/SimpleShapeDrawable.java
@@ -19,8 +19,8 @@
import android.graphics.drawable.Drawable;
import android.os.Build;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.util.ColorChangeAcceptorDelegate;
import me.vkryl.core.ColorUtils;
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/EmojiMediaType.java b/app/src/main/java/org/thunderdog/challegram/telegram/EmojiMediaType.java
index 90eed612ee..86742c0dff 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/EmojiMediaType.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/EmojiMediaType.java
@@ -16,8 +16,6 @@
import androidx.annotation.IntDef;
-import org.thunderdog.challegram.ui.MessagesController;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.SOURCE)
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCounter.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCounter.java
index da7e4cdae3..5ee89c6c6b 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCounter.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCounter.java
@@ -14,9 +14,10 @@
*/
package org.thunderdog.challegram.telegram;
-import me.vkryl.leveldb.LevelDB;
import org.thunderdog.challegram.unsorted.Settings;
+import me.vkryl.leveldb.LevelDB;
+
public class TdlibCounter {
public int totalChatCount;
public int chatCount, chatUnmutedCount;
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibEmojiManager.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibEmojiManager.java
index b20ce07f19..e6ad382430 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibEmojiManager.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibEmojiManager.java
@@ -18,7 +18,6 @@
import androidx.annotation.Nullable;
import org.drinkless.tdlib.TdApi;
-import org.thunderdog.challegram.Log;
import java.util.Collection;
diff --git a/app/src/main/java/org/thunderdog/challegram/theme/ThemeListenerEntry.java b/app/src/main/java/org/thunderdog/challegram/theme/ThemeListenerEntry.java
index 47e5495cf7..366a9012d1 100644
--- a/app/src/main/java/org/thunderdog/challegram/theme/ThemeListenerEntry.java
+++ b/app/src/main/java/org/thunderdog/challegram/theme/ThemeListenerEntry.java
@@ -34,8 +34,8 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
-import me.vkryl.core.ColorUtils;
import me.vkryl.core.BitwiseUtils;
+import me.vkryl.core.ColorUtils;
public class ThemeListenerEntry {
public static final int MODE_INVALIDATE = 0;
diff --git a/app/src/main/java/org/thunderdog/challegram/theme/ThemeListenerList.java b/app/src/main/java/org/thunderdog/challegram/theme/ThemeListenerList.java
index 9953228ce1..481816cb91 100644
--- a/app/src/main/java/org/thunderdog/challegram/theme/ThemeListenerList.java
+++ b/app/src/main/java/org/thunderdog/challegram/theme/ThemeListenerList.java
@@ -17,8 +17,6 @@
import android.graphics.Paint;
import android.view.View;
-import org.thunderdog.challegram.R;
-
import java.util.ArrayList;
import me.vkryl.android.util.InvalidateDelegate;
diff --git a/app/src/main/java/org/thunderdog/challegram/tool/Views.java b/app/src/main/java/org/thunderdog/challegram/tool/Views.java
index 0823652113..e7008c7d3e 100644
--- a/app/src/main/java/org/thunderdog/challegram/tool/Views.java
+++ b/app/src/main/java/org/thunderdog/challegram/tool/Views.java
@@ -52,7 +52,6 @@
import org.thunderdog.challegram.U;
import org.thunderdog.challegram.core.DiffMatchPatch;
import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.loader.Receiver;
import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.support.ViewTranslator;
import org.thunderdog.challegram.theme.ColorId;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/BottomSheetViewController.java b/app/src/main/java/org/thunderdog/challegram/ui/BottomSheetViewController.java
index 3af2d93235..cbc3bf683d 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/BottomSheetViewController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/BottomSheetViewController.java
@@ -5,7 +5,6 @@
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderIconSelector.java b/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderIconSelector.java
index c78bb75ad5..8e9b0f72a7 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderIconSelector.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderIconSelector.java
@@ -28,7 +28,6 @@
import androidx.recyclerview.widget.RecyclerView;
import org.drinkless.tdlib.TdApi;
-import org.thunderdog.challegram.R;
import org.thunderdog.challegram.data.TD;
import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.support.RippleSupport;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java b/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java
index cf3fe88f43..f51372dca1 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java
@@ -51,11 +51,11 @@
import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.tool.Views;
import org.thunderdog.challegram.unsorted.Settings;
+import org.thunderdog.challegram.util.StickerSetsDataProvider;
import org.thunderdog.challegram.util.StringList;
import org.thunderdog.challegram.v.CustomRecyclerView;
import org.thunderdog.challegram.v.RtlGridLayoutManager;
import org.thunderdog.challegram.widget.EmojiLayout;
-import org.thunderdog.challegram.util.StickerSetsDataProvider;
import org.thunderdog.challegram.widget.ForceTouchView;
import java.util.ArrayList;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
index 028dae0bd0..bd882d8931 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
@@ -36,7 +36,6 @@
import androidx.recyclerview.widget.RecyclerView;
import org.drinkless.tdlib.TdApi;
-import org.thunderdog.challegram.MainActivity;
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.component.base.SettingView;
import org.thunderdog.challegram.config.Config;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingHolder.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingHolder.java
index 81aa97ed93..215524056d 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingHolder.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingHolder.java
@@ -64,8 +64,8 @@
import org.thunderdog.challegram.support.RippleSupport;
import org.thunderdog.challegram.support.ViewSupport;
import org.thunderdog.challegram.telegram.Tdlib;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Drawables;
import org.thunderdog.challegram.tool.Fonts;
import org.thunderdog.challegram.tool.Paints;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsAdapter.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsAdapter.java
index 265585eaf2..4464540aa5 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsAdapter.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsAdapter.java
@@ -63,8 +63,8 @@
import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.telegram.TdlibAccount;
import org.thunderdog.challegram.telegram.TdlibDelegate;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.UI;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsBlockedController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsBlockedController.java
index c5feeca9ca..2e4979750d 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsBlockedController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsBlockedController.java
@@ -31,12 +31,10 @@
import org.thunderdog.challegram.data.TGUser;
import org.thunderdog.challegram.navigation.HeaderView;
import org.thunderdog.challegram.navigation.Menu;
-import org.thunderdog.challegram.support.ViewSupport;
import org.thunderdog.challegram.telegram.ChatListener;
import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.telegram.TdlibCache;
import org.thunderdog.challegram.telegram.TdlibUi;
-import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.Strings;
import org.thunderdog.challegram.util.SenderPickerDelegate;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java
index aa7ca5fb2b..32ffb1c3cf 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java
@@ -14,7 +14,6 @@
*/
package org.thunderdog.challegram.ui;
-import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyKeyController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyKeyController.java
index f1ea711f1e..8e81912580 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyKeyController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyKeyController.java
@@ -41,9 +41,9 @@
import org.thunderdog.challegram.tool.Fonts;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.UI;
-import org.thunderdog.challegram.util.ProfilePhotoDrawModifier;
import org.thunderdog.challegram.util.CustomTypefaceSpan;
import org.thunderdog.challegram.util.NoUnderlineClickableSpan;
+import org.thunderdog.challegram.util.ProfilePhotoDrawModifier;
import org.thunderdog.challegram.util.UserPickerMultiDelegate;
import org.thunderdog.challegram.v.CustomRecyclerView;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/StickersTrendingController.java b/app/src/main/java/org/thunderdog/challegram/ui/StickersTrendingController.java
index 6b7da2457e..147aff3277 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/StickersTrendingController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/StickersTrendingController.java
@@ -49,7 +49,6 @@
import me.vkryl.core.StringUtils;
import me.vkryl.core.collection.LongSet;
import me.vkryl.core.lambda.CancellableRunnable;
-import me.vkryl.core.lambda.RunnableData;
public class StickersTrendingController extends ViewController implements StickerSmallView.StickerMovementCallback, Client.ResultHandler, TGStickerObj.DataProvider, StickersListener, TGStickerSetInfo.ViewCallback {
private final boolean isEmoji;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ThemeController.java b/app/src/main/java/org/thunderdog/challegram/ui/ThemeController.java
index 9ca68ff45f..77528189ac 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ThemeController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ThemeController.java
@@ -36,9 +36,9 @@
import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.navigation.ViewPagerController;
import org.thunderdog.challegram.telegram.Tdlib;
+import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.theme.ColorState;
import org.thunderdog.challegram.theme.Theme;
-import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.theme.ThemeInfo;
import org.thunderdog.challegram.theme.ThemeManager;
import org.thunderdog.challegram.tool.Screen;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraDelegate.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraDelegate.java
index 640a597cbe..fff6dd0d8e 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraDelegate.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraDelegate.java
@@ -12,8 +12,6 @@
*/
package org.thunderdog.challegram.ui.camera;
-import android.graphics.Point;
-import android.graphics.Rect;
import android.graphics.RectF;
import androidx.annotation.Nullable;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraQrBridge.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraQrBridge.java
index 37688d37cb..70a509dbcf 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraQrBridge.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraQrBridge.java
@@ -13,7 +13,6 @@
package org.thunderdog.challegram.ui.camera;
import android.graphics.ImageFormat;
-import android.graphics.Rect;
import android.graphics.RectF;
import android.media.Image;
import android.os.Build;
@@ -38,18 +37,15 @@
import com.google.zxing.ResultPoint;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeReader;
-import com.google.zxing.qrcode.detector.AlignmentPattern;
import com.google.zxing.qrcode.detector.FinderPattern;
import org.thunderdog.challegram.Log;
import org.thunderdog.challegram.U;
-import org.thunderdog.challegram.config.Config;
import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.ui.camera.legacy.CameraApiLegacy;
import org.thunderdog.challegram.unsorted.Settings;
import java.nio.ByteBuffer;
-import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraQrCodeRootLayout.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraQrCodeRootLayout.java
index 8627c78133..99ec81724d 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraQrCodeRootLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraQrCodeRootLayout.java
@@ -22,7 +22,6 @@
import android.view.View;
import androidx.annotation.NonNull;
-import androidx.annotation.StringRes;
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.core.Lang;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraRootLayout.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraRootLayout.java
index 99d58b8301..d00303263e 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraRootLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraRootLayout.java
@@ -15,7 +15,6 @@
package org.thunderdog.challegram.ui.camera;
import android.content.Context;
-import android.graphics.Rect;
import android.graphics.RectF;
import android.view.MotionEvent;
diff --git a/app/src/main/java/org/thunderdog/challegram/util/AvatarDrawModifier.java b/app/src/main/java/org/thunderdog/challegram/util/AvatarDrawModifier.java
index 6e11939b5b..5984fd7978 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/AvatarDrawModifier.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/AvatarDrawModifier.java
@@ -18,8 +18,6 @@
import android.graphics.Canvas;
import android.view.View;
-import androidx.annotation.Px;
-
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.loader.AvatarReceiver;
import org.thunderdog.challegram.loader.ComplexReceiver;
@@ -27,8 +25,6 @@
import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.tool.Screen;
-import me.vkryl.td.Td;
-
public class AvatarDrawModifier implements DrawModifier {
final int size;
diff --git a/app/src/main/java/org/thunderdog/challegram/util/Crash.java b/app/src/main/java/org/thunderdog/challegram/util/Crash.java
index a97889a568..9bfa9964b9 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/Crash.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/Crash.java
@@ -20,7 +20,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.StringDef;
-import org.drinkless.tdlib.Client;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.BuildConfig;
import org.thunderdog.challegram.Log;
diff --git a/app/src/main/java/org/thunderdog/challegram/util/LineDrawModifier.java b/app/src/main/java/org/thunderdog/challegram/util/LineDrawModifier.java
index 69d88b6681..6e9f8bfa8f 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/LineDrawModifier.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/LineDrawModifier.java
@@ -21,8 +21,8 @@
import androidx.annotation.Nullable;
import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ThemeDelegate;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
diff --git a/app/src/main/java/org/thunderdog/challegram/util/ReactionsCounterDrawable.java b/app/src/main/java/org/thunderdog/challegram/util/ReactionsCounterDrawable.java
index c66b20a3b3..42f7036c14 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/ReactionsCounterDrawable.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/ReactionsCounterDrawable.java
@@ -8,11 +8,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import org.thunderdog.challegram.data.TGReactions;
import org.thunderdog.challegram.tool.Screen;
-import me.vkryl.android.animator.ListAnimator;
-
public class ReactionsCounterDrawable extends Drawable {
private final ReactionsListAnimator topReactions;
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/ClearButton.java b/app/src/main/java/org/thunderdog/challegram/widget/ClearButton.java
index 1f316950cc..436dbb3a25 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/ClearButton.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/ClearButton.java
@@ -24,8 +24,8 @@
import org.thunderdog.challegram.core.Lang;
import org.thunderdog.challegram.navigation.HeaderButton;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.DrawAlgorithms;
import org.thunderdog.challegram.tool.Screen;
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/FillingDecoration.java b/app/src/main/java/org/thunderdog/challegram/widget/FillingDecoration.java
index 26ffa5be58..e55d71528c 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/FillingDecoration.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/FillingDecoration.java
@@ -22,10 +22,9 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import org.thunderdog.challegram.R;
import org.thunderdog.challegram.navigation.ViewController;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import java.util.ArrayList;
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/InfiniteRecyclerView.java b/app/src/main/java/org/thunderdog/challegram/widget/InfiniteRecyclerView.java
index 11f628a445..d7af55f79f 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/InfiniteRecyclerView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/InfiniteRecyclerView.java
@@ -29,7 +29,6 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import org.thunderdog.challegram.R;
import org.thunderdog.challegram.U;
import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.theme.ColorId;
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/MaterialEditTextGroup.java b/app/src/main/java/org/thunderdog/challegram/widget/MaterialEditTextGroup.java
index 7595ef17d1..5764b32d37 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/MaterialEditTextGroup.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/MaterialEditTextGroup.java
@@ -37,8 +37,8 @@
import org.thunderdog.challegram.navigation.TooltipOverlayView;
import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.telegram.Tdlib;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Fonts;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.Strings;
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/RippleRevealView.java b/app/src/main/java/org/thunderdog/challegram/widget/RippleRevealView.java
index 0886f1a160..e6a9e2689c 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/RippleRevealView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/RippleRevealView.java
@@ -18,9 +18,8 @@
import android.graphics.Canvas;
import android.view.View;
-import org.thunderdog.challegram.R;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import me.vkryl.core.ColorUtils;
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/SeparatorView.java b/app/src/main/java/org/thunderdog/challegram/widget/SeparatorView.java
index 69d9dc6ddf..7463bb2fed 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/SeparatorView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/SeparatorView.java
@@ -21,10 +21,9 @@
import android.view.View;
import android.view.ViewGroup;
-import org.thunderdog.challegram.R;
import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Screen;
public class SeparatorView extends View {
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/SettingStupidView.java b/app/src/main/java/org/thunderdog/challegram/widget/SettingStupidView.java
index d3216ad451..873a458475 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/SettingStupidView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/SettingStupidView.java
@@ -28,8 +28,8 @@
import org.thunderdog.challegram.navigation.RtlCheckListener;
import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.support.RippleSupport;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Fonts;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.Views;
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/SimpleMediaWrapperView.java b/app/src/main/java/org/thunderdog/challegram/widget/SimpleMediaWrapperView.java
index e8f18127bf..adf40b30cf 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/SimpleMediaWrapperView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/SimpleMediaWrapperView.java
@@ -21,8 +21,8 @@
import org.thunderdog.challegram.data.MediaWrapper;
import org.thunderdog.challegram.loader.DoubleImageReceiver;
import org.thunderdog.challegram.loader.ImageReceiver;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import me.vkryl.android.util.SingleViewProvider;
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/SliderWrapView.java b/app/src/main/java/org/thunderdog/challegram/widget/SliderWrapView.java
index 6803872296..611cb856fb 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/SliderWrapView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/SliderWrapView.java
@@ -32,8 +32,8 @@
import org.thunderdog.challegram.mediaview.MediaFilterNameView;
import org.thunderdog.challegram.mediaview.SliderView;
import org.thunderdog.challegram.navigation.ViewController;
-import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.UI;
From ae32451488cfda55da772a15101d2743e74c05ab Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 15:01:50 +0400
Subject: [PATCH 21/61] Move `ViewController.OPTION_COLOR_*` constants to
`OptionColor` interface
Cherry-pick nikita-toropov/Telegram-X@6c609ba
Co-authored-by: nikita-toropov <4970595+nikita-toropov@users.noreply.github.com>
---
.../attach/MediaBottomFilesController.java | 2 +-
.../component/chat/InlineResultsWrap.java | 4 +-
.../component/chat/MessageView.java | 14 +-
.../popups/JoinRequestsComponent.java | 6 +-
.../component/popups/ModernOptions.java | 2 +-
.../component/preview/PreviewLayout.java | 2 +-
.../org/thunderdog/challegram/data/TD.java | 2 +-
.../data/TGMessageGiveawayBase.java | 20 +--
.../challegram/helper/LiveLocationHelper.java | 2 +-
.../mediaview/MediaViewController.java | 12 +-
.../challegram/navigation/OptionsLayout.java | 12 +-
.../navigation/TelegramViewController.java | 8 +-
.../challegram/navigation/ViewController.java | 35 ++---
.../challegram/telegram/TdlibUi.java | 120 +++++++++---------
.../challegram/ui/CallListController.java | 6 +-
.../challegram/ui/ChatLinksController.java | 6 +-
.../ui/ChatStatisticsController.java | 10 +-
.../challegram/ui/ChatsController.java | 6 +-
.../challegram/ui/CreateGroupController.java | 2 +-
.../challegram/ui/CreatePollController.java | 2 +-
.../ui/EditChatFolderController.java | 4 +-
.../ui/EditDeleteAccountReasonController.java | 2 +-
.../challegram/ui/EditLanguageController.java | 2 +-
.../challegram/ui/EditRightsController.java | 4 +-
.../challegram/ui/EditSessionController.java | 2 +-
.../challegram/ui/EditUsernameController.java | 4 +-
.../ui/EmojiMediaListController.java | 2 +-
.../ui/EmojiStatusListController.java | 10 +-
.../challegram/ui/MainController.java | 24 ++--
.../ui/MessageOptionsController.java | 2 +-
.../challegram/ui/MessagesController.java | 18 +--
.../challegram/ui/PhoneController.java | 2 +-
.../challegram/ui/PlaybackController.java | 2 +-
.../challegram/ui/ProfileController.java | 42 +++---
.../ui/ReactionsPickerController.java | 8 +-
.../challegram/ui/Settings2FAController.java | 4 +-
.../ui/SettingsBlockedController.java | 4 +-
.../challegram/ui/SettingsBugController.java | 10 +-
.../ui/SettingsCacheController.java | 12 +-
.../challegram/ui/SettingsController.java | 28 ++--
.../challegram/ui/SettingsDataController.java | 2 +-
.../ui/SettingsFoldersController.java | 6 +-
.../ui/SettingsLanguageController.java | 12 +-
.../ui/SettingsLogFilesController.java | 2 +-
.../ui/SettingsNotificationController.java | 6 +-
.../ui/SettingsPrivacyController.java | 6 +-
.../ui/SettingsProxyController.java | 10 +-
.../ui/SettingsSessionsController.java | 4 +-
.../ui/SettingsThemeController.java | 16 +--
.../ui/SettingsWebsitesController.java | 4 +-
.../challegram/ui/SharedCommonController.java | 2 +-
.../ui/SharedMembersController.java | 16 +--
.../challegram/ui/ThemeListController.java | 4 +-
.../ui/TranslationControllerV2.java | 2 +-
.../challegram/util/AppUpdater.java | 4 +-
.../challegram/widget/BaseView.java | 2 +-
.../challegram/widget/EmojiLayout.java | 16 +--
57 files changed, 289 insertions(+), 284 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/component/attach/MediaBottomFilesController.java b/app/src/main/java/org/thunderdog/challegram/component/attach/MediaBottomFilesController.java
index cbde304944..569b2944f7 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/attach/MediaBottomFilesController.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/attach/MediaBottomFilesController.java
@@ -205,7 +205,7 @@ private void navigateToPath (final View view, final String currentPath, final St
File external = UI.getAppContext().getExternalFilesDir(null);
String externalPath = external != null ? external.getPath() : null;
if (!isUpper && (path.equals(internalPath) || path.equals(externalPath))) {
- before = after -> showOptions(Lang.getMarkdownString(this, R.string.ApplicationFolderWarning), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ApplicationFolderWarningConfirm), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_warning_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ before = after -> showOptions(Lang.getMarkdownString(this, R.string.ApplicationFolderWarning), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ApplicationFolderWarningConfirm), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_warning_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_done) {
after.run();
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/InlineResultsWrap.java b/app/src/main/java/org/thunderdog/challegram/component/chat/InlineResultsWrap.java
index c622036f88..7ce9c092e4 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/InlineResultsWrap.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/InlineResultsWrap.java
@@ -295,7 +295,7 @@ public boolean onLongClick (View v) {
if (result instanceof InlineResultCommand) {
return c instanceof MessagesController && ((MessagesController) c).canWriteMessages() && ((MessagesController) c).onCommandLongPressed((InlineResultCommand) result);
} else if (result instanceof InlineResultHashtag) {
- c.showOptions(Lang.getString(R.string.HashtagDeleteHint), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getOK(), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ c.showOptions(Lang.getString(R.string.HashtagDeleteHint), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getOK(), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_delete) {
removeItem(result);
delegate.tdlib().client().send(new TdApi.RemoveRecentHashtag(((InlineResultHashtag) result).data().substring(1)), delegate.tdlib().okHandler());
@@ -304,7 +304,7 @@ public boolean onLongClick (View v) {
});
return true;
} else if (result instanceof InlineResultMention && ((InlineResultMention) result).isInlineBot()) {
- c.showOptions(Lang.getString(R.string.BotDeleteHint), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getOK(), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ c.showOptions(Lang.getString(R.string.BotDeleteHint), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getOK(), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_delete) {
removeItem(result);
if (c instanceof MessagesController) {
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/MessageView.java b/app/src/main/java/org/thunderdog/challegram/component/chat/MessageView.java
index 60af9b4f1e..b2520ddd1e 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/MessageView.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/MessageView.java
@@ -1238,21 +1238,21 @@ private void showEventLogOptions (MessagesController m, TGMessage msg) {
ids.append(R.id.btn_reportFalsePositive);
strings.append(R.string.ReportFalsePositive);
icons.append(R.drawable.baseline_report_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
if (TD.canCopyText(msg.getMessage()) || (msg instanceof TGMessageText && ((TGMessageText) msg).getText().text.trim().length() > 0)) {
ids.append(R.id.btn_messageCopy);
strings.append(R.string.Copy);
icons.append(R.drawable.baseline_content_copy_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
if (msg.isTranslatable() && msg.translationStyleMode() != Settings.TRANSLATE_MODE_NONE) {
ids.append(R.id.btn_chatTranslate);
strings.append(R.string.Translate);
icons.append(R.drawable.baseline_translate_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
if (!isChannel) {
@@ -1263,7 +1263,7 @@ private void showEventLogOptions (MessagesController m, TGMessage msg) {
strings.append(Lang.getString(R.string.ViewMessagesFromUser, m.tdlib().senderName(sender, true)));
}
icons.append(R.drawable.baseline_person_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
if (myStatus != null && !(TD.isCreator(member.status) && TD.isCreator(myStatus))) {
@@ -1271,7 +1271,7 @@ private void showEventLogOptions (MessagesController m, TGMessage msg) {
if (promoteMode != TD.PROMOTE_MODE_NONE && promoteMode != TD.PROMOTE_MODE_NEW) {
ids.append(R.id.btn_editRights);
icons.append(R.drawable.baseline_stars_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
switch (promoteMode) {
case TD.PROMOTE_MODE_EDIT:
strings.append(R.string.EditAdminRights);
@@ -1288,7 +1288,7 @@ private void showEventLogOptions (MessagesController m, TGMessage msg) {
if (restrictMode != TD.RESTRICT_MODE_NONE && !(sender.getConstructor() == TdApi.MessageSenderChat.CONSTRUCTOR && Td.getSenderId(sender) == m.getChatId())) {
if (!isChannel || (isChannel && restrictMode == TD.RESTRICT_MODE_EDIT)) {
ids.append(R.id.btn_restrictMember);
- colors.append(restrictMode == TD.RESTRICT_MODE_NEW ? ViewController.OPTION_COLOR_RED : ViewController.OPTION_COLOR_NORMAL);
+ colors.append(restrictMode == TD.RESTRICT_MODE_NEW ? ViewController.OptionColor.RED : ViewController.OptionColor.NORMAL);
icons.append(R.drawable.baseline_block_24);
switch (restrictMode) {
@@ -1310,7 +1310,7 @@ private void showEventLogOptions (MessagesController m, TGMessage msg) {
ids.append(R.id.btn_blockSender);
icons.append(R.drawable.baseline_remove_circle_24);
strings.append(isChannel ? R.string.ChannelRemoveUser : R.string.RemoveFromGroup);
- colors.append(ViewController.OPTION_COLOR_RED);
+ colors.append(ViewController.OptionColor.RED);
}
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/popups/JoinRequestsComponent.java b/app/src/main/java/org/thunderdog/challegram/component/popups/JoinRequestsComponent.java
index 62a7543fea..88acd0340d 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/popups/JoinRequestsComponent.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/popups/JoinRequestsComponent.java
@@ -135,7 +135,7 @@ public void onClick (View v) {
msg.append("\n").append(Lang.wrap(Lang.getString(R.string.InviteLinkRequestSince, Lang.getMessageTimestamp(request.date, TimeUnit.SECONDS)), Lang.italicCreator()));
}
- controller.showOptions(msg, new int[]{R.id.btn_approveChatRequest, R.id.btn_declineChatRequest, R.id.btn_openChat}, new String[]{Lang.getString(isChannel ? R.string.InviteLinkActionAcceptChannel : R.string.InviteLinkActionAccept), Lang.getString(R.string.InviteLinkActionDeclineAction), Lang.getString(R.string.InviteLinkActionWrite)}, new int[] { ViewController.OPTION_COLOR_BLUE, ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL }, new int[]{R.drawable.baseline_person_add_24, R.drawable.baseline_delete_24, R.drawable.baseline_person_24}, (itemView2, id2) -> {
+ controller.showOptions(msg, new int[]{R.id.btn_approveChatRequest, R.id.btn_declineChatRequest, R.id.btn_openChat}, new String[]{Lang.getString(isChannel ? R.string.InviteLinkActionAcceptChannel : R.string.InviteLinkActionAccept), Lang.getString(R.string.InviteLinkActionDeclineAction), Lang.getString(R.string.InviteLinkActionWrite)}, new int[] {ViewController.OptionColor.BLUE, ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[]{R.drawable.baseline_person_add_24, R.drawable.baseline_delete_24, R.drawable.baseline_person_24}, (itemView2, id2) -> {
if (id2 == R.id.btn_approveChatRequest) {
acceptRequest(user);
} else if (id2 == R.id.btn_openChat) {
@@ -245,7 +245,7 @@ private void openProfile (TGUser user) {
}
private void acceptRequest (TGUser user) {
- controller.showOptions(Lang.getStringBold(R.string.AreYouSureAcceptJoinRequest, user.getName(), tdlib().chatTitle(chatId)), new int[]{R.id.btn_approveChatRequest, R.id.btn_cancel}, new String[]{Lang.getString(isChannel ? R.string.InviteLinkActionAcceptChannel : R.string.InviteLinkActionAccept), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_BLUE, ViewController.OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_person_add_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
+ controller.showOptions(Lang.getStringBold(R.string.AreYouSureAcceptJoinRequest, user.getName(), tdlib().chatTitle(chatId)), new int[]{R.id.btn_approveChatRequest, R.id.btn_cancel}, new String[]{Lang.getString(isChannel ? R.string.InviteLinkActionAcceptChannel : R.string.InviteLinkActionAccept), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OptionColor.BLUE, ViewController.OptionColor.NORMAL}, new int[]{R.drawable.baseline_person_add_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
if (id2 == R.id.btn_approveChatRequest) {
tdlib().client().send(new TdApi.ProcessChatJoinRequest(chatId, user.getUserId(), true), obj -> removeSender(user));
}
@@ -255,7 +255,7 @@ private void acceptRequest (TGUser user) {
}
private void declineRequest (TGUser user) {
- controller.showOptions(Lang.getStringBold(R.string.AreYouSureDeclineJoinRequest, user.getName()), new int[]{R.id.btn_declineChatRequest, R.id.btn_cancel}, new String[]{Lang.getString(R.string.InviteLinkActionDeclineAction), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
+ controller.showOptions(Lang.getStringBold(R.string.AreYouSureDeclineJoinRequest, user.getName()), new int[]{R.id.btn_declineChatRequest, R.id.btn_cancel}, new String[]{Lang.getString(R.string.InviteLinkActionDeclineAction), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
if (id2 == R.id.btn_declineChatRequest) {
tdlib().client().send(new TdApi.ProcessChatJoinRequest(chatId, user.getUserId(), false), obj -> removeSender(user));
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/popups/ModernOptions.java b/app/src/main/java/org/thunderdog/challegram/component/popups/ModernOptions.java
index c0a46d3ccb..5b5faa7231 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/popups/ModernOptions.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/popups/ModernOptions.java
@@ -69,7 +69,7 @@ private static void showLocationAlert (ViewController> context, CharSequence f
desc,
new int[]{R.id.btn_done, R.id.btn_privacyPolicy, R.id.btn_cancel},
new String[]{Lang.getString(R.string.Continue), Lang.getString(R.string.PrivacyPolicy), Lang.getString(R.string.Cancel)},
- new int[]{ViewController.OPTION_COLOR_BLUE, ViewController.OPTION_COLOR_NORMAL, ViewController.OPTION_COLOR_NORMAL},
+ new int[]{ViewController.OptionColor.BLUE, ViewController.OptionColor.NORMAL, ViewController.OptionColor.NORMAL},
new int[]{R.drawable.baseline_check_circle_24, R.drawable.baseline_policy_24, R.drawable.baseline_cancel_24},
new OptionDelegate() {
@Override
diff --git a/app/src/main/java/org/thunderdog/challegram/component/preview/PreviewLayout.java b/app/src/main/java/org/thunderdog/challegram/component/preview/PreviewLayout.java
index 0fafee98c3..205e62ba45 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/preview/PreviewLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/preview/PreviewLayout.java
@@ -89,7 +89,7 @@ protected void addFooterItem (int id, @StringRes int stringRes, int icon) {
footerHeight += params.height;
TextView item;
- item = OptionsLayout.genOptionView(getContext(), id, Lang.getString(stringRes), ViewController.OPTION_COLOR_NORMAL, icon, this, themeListeners, null);
+ item = OptionsLayout.genOptionView(getContext(), id, Lang.getString(stringRes), ViewController.OptionColor.NORMAL, icon, this, themeListeners, null);
RippleSupport.setSimpleWhiteBackground(item);
themeListeners.addThemeInvalidateListener(item);
item.setLayoutParams(params);
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TD.java b/app/src/main/java/org/thunderdog/challegram/data/TD.java
index e9031f6764..5096b30918 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TD.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TD.java
@@ -3843,7 +3843,7 @@ public static void deleteFiles (final ViewController> context, final TdApi.Fil
}
final String size = Strings.buildSize(totalSize);
final long totalSizeFinal = totalSize;
- context.showOptions(Lang.getString(files.length == 1 ? R.string.DeleteFileHint : R.string.DeleteMultipleFilesHint), new int[]{R.id.btn_deleteFile, R.id.btn_cancel}, new String[]{Lang.getString(R.string.ClearX, size), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ context.showOptions(Lang.getString(files.length == 1 ? R.string.DeleteFileHint : R.string.DeleteMultipleFilesHint), new int[]{R.id.btn_deleteFile, R.id.btn_cancel}, new String[]{Lang.getString(R.string.ClearX, size), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_deleteFile) {
TdlibManager.instance().player().stopPlaybackIfPlayingAnyOf(files);
context.context().closeFilePip(files);
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayBase.java b/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayBase.java
index bf7549e72b..85d96feaab 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayBase.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayBase.java
@@ -508,15 +508,15 @@ protected void showPremiumGiveawayInfoPopup (int winnerCount, int monthCount, lo
infoSsb.append(Lang.pluralBold(R.string.GiveawayInfoEndedPart3, infoCompleted.activationCount));
if (hasGiftCode) {
- b.item(new ViewController.OptionItem(R.id.btn_openLink, Lang.getString(R.string.GiveawayViewMyPrize), ViewController.OPTION_COLOR_BLUE, R.drawable.baseline_gift_outline_24));
+ b.item(new ViewController.OptionItem(R.id.btn_openLink, Lang.getString(R.string.GiveawayViewMyPrize), ViewController.OptionColor.BLUE, R.drawable.baseline_gift_outline_24));
}
if (wasRefunded) {
- b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayEndedRefunded), ViewController.OPTION_COLOR_RED, R.drawable.baseline_info_24));
+ b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayEndedRefunded), ViewController.OptionColor.RED, R.drawable.baseline_info_24));
} else if (hasGiftCode) {
- b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayEndedYouWon), ViewController.OPTION_COLOR_GREEN, R.drawable.baseline_party_popper_24));
+ b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayEndedYouWon), ViewController.OptionColor.GREEN, R.drawable.baseline_party_popper_24));
} else {
- b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayEndedYouLose), ViewController.OPTION_COLOR_BLUE, R.drawable.baseline_info_24));
+ b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayEndedYouLose), ViewController.OptionColor.BLUE, R.drawable.baseline_info_24));
}
b.info(infoSsb);
@@ -538,11 +538,11 @@ protected void showPremiumGiveawayInfoPopup (int winnerCount, int monthCount, lo
switch (status.getConstructor()) {
case TdApi.PremiumGiveawayParticipantStatusParticipating.CONSTRUCTOR: {
- b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayOngoingYouParticipating), ViewController.OPTION_COLOR_GREEN, R.drawable.baseline_info_24));
+ b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayOngoingYouParticipating), ViewController.OptionColor.GREEN, R.drawable.baseline_info_24));
break;
}
case TdApi.PremiumGiveawayParticipantStatusEligible.CONSTRUCTOR: {
- b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayOngoingYouEligible), ViewController.OPTION_COLOR_BLUE, R.drawable.baseline_info_24));
+ b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayOngoingYouEligible), ViewController.OptionColor.BLUE, R.drawable.baseline_info_24));
infoSsb.append("\n\n");
infoSsb.append(Lang.getStringBold(R.string.GiveawayInfoOngoingPartEligible, sponsors, getDateTime(winnersSelectionDate)));
break;
@@ -551,20 +551,20 @@ protected void showPremiumGiveawayInfoPopup (int winnerCount, int monthCount, lo
TdApi.PremiumGiveawayParticipantStatusAdministrator s = (TdApi.PremiumGiveawayParticipantStatusAdministrator) status;
infoSsb.append("\n\n");
infoSsb.append(Lang.getStringBold(R.string.GiveawayInfoOngoingPartRestrictedAdmin, tdlib.chatTitle(s.chatId)));
- b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayOngoingYouRestricted), ViewController.OPTION_COLOR_RED, R.drawable.baseline_info_24));
+ b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayOngoingYouRestricted), ViewController.OptionColor.RED, R.drawable.baseline_info_24));
break;
}
case TdApi.PremiumGiveawayParticipantStatusAlreadyWasMember.CONSTRUCTOR: {
infoSsb.append("\n\n");
infoSsb.append(Lang.getStringBold(R.string.GiveawayInfoOngoingPartRestrictedMember, tdlib.chatTitle(boostedChatId)));
- b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayOngoingYouRestricted), ViewController.OPTION_COLOR_RED, R.drawable.baseline_info_24));
+ b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayOngoingYouRestricted), ViewController.OptionColor.RED, R.drawable.baseline_info_24));
break;
}
case TdApi.PremiumGiveawayParticipantStatusDisallowedCountry.CONSTRUCTOR: {
TdApi.PremiumGiveawayParticipantStatusDisallowedCountry s = (TdApi.PremiumGiveawayParticipantStatusDisallowedCountry) status;
infoSsb.append("\n\n");
infoSsb.append(Lang.getStringBold(R.string.GiveawayInfoOngoingPartRestrictedCountry, s.userCountryCode));
- b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayOngoingYouRestricted), ViewController.OPTION_COLOR_RED, R.drawable.baseline_info_24));
+ b.subtitle(new ViewController.OptionItem(0, Lang.getString(R.string.GiveawayOngoingYouRestricted), ViewController.OptionColor.RED, R.drawable.baseline_info_24));
break;
}
default: {
@@ -581,7 +581,7 @@ protected void showPremiumGiveawayInfoPopup (int winnerCount, int monthCount, lo
throw Td.unsupported(premiumGiveawayInfo);
}
- b.item(new ViewController.OptionItem(R.id.btn_cancel, Lang.getString(R.string.GiveawayInfoClose), ViewController.OPTION_COLOR_NORMAL, R.drawable.baseline_cancel_24));
+ b.item(new ViewController.OptionItem(R.id.btn_cancel, Lang.getString(R.string.GiveawayInfoClose), ViewController.OptionColor.NORMAL, R.drawable.baseline_cancel_24));
controller().showOptions(b.build(), this::onGiveawayInfoOptionItemPressed);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/helper/LiveLocationHelper.java b/app/src/main/java/org/thunderdog/challegram/helper/LiveLocationHelper.java
index ab27d235b6..57108e0db9 100644
--- a/app/src/main/java/org/thunderdog/challegram/helper/LiveLocationHelper.java
+++ b/app/src/main/java/org/thunderdog/challegram/helper/LiveLocationHelper.java
@@ -565,7 +565,7 @@ public void stopLiveLocations (final long chatId, final Runnable after) {
info = Lang.getString(R.string.StopLiveLocationInfo);
}
- c.showOptions(info, ids.get(), strings.get(), new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, icons.get(), (itemView, id) -> {
+ c.showOptions(info, ids.get(), strings.get(), new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, icons.get(), (itemView, id) -> {
if (id == R.id.btn_stopAllLiveLocations) {
tdlib.cache().stopLiveLocations(chatId);
if (after != null) {
diff --git a/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewController.java b/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewController.java
index 95774c7b98..33bbd963ca 100644
--- a/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewController.java
+++ b/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewController.java
@@ -6362,7 +6362,7 @@ public boolean allowSliderChanges (SliderView view) {
undoButton.setOnClickListener(this);
undoButton.setOnLongClickListener(v -> {
if (paintView != null && !paintView.getContentWrap().isBusy()) {
- showOptions(null, new int[]{R.id.paint_clear, R.id.btn_cancel}, new String[]{Lang.getString(R.string.ClearDrawing), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(null, new int[]{R.id.paint_clear, R.id.btn_cancel}, new String[]{Lang.getString(R.string.ClearDrawing), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.paint_clear) {
undoAllPaintActions();
}
@@ -7315,7 +7315,7 @@ private void resetMediaPaddings (int section) {
private static final int MODE_CANCEL = 2;
private void showYesNo () {
- showOptions(Lang.getString(R.string.DiscardCurrentChanges), new int[]{R.id.btn_discard, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Discard), Lang.getString(R.string.Cancel)}, new int[]{OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.DiscardCurrentChanges), new int[]{R.id.btn_discard, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Discard), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_discard) {
changeSection(SECTION_CAPTION, MODE_CANCEL);
}
@@ -8086,7 +8086,7 @@ public void onClick (View v) {
icons.append(R.drawable.baseline_crop_free_24);
ids.append(R.id.btn_proportion_free);
strings.append(R.string.CropFree);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
}
float originalProportion = cropAreaView.getOriginalProportion();
@@ -8109,7 +8109,7 @@ public void onClick (View v) {
} else {
strings.append(R.string.CropOriginal);
}
- colors.append(originalProportion == proportion ? OPTION_COLOR_BLUE : OPTION_COLOR_NORMAL);
+ colors.append(originalProportion == proportion ? OptionColor.BLUE : OptionColor.NORMAL);
}
if ((float) width / (float) height != 1f) {
@@ -8131,11 +8131,11 @@ public void onClick (View v) {
strings.append(verb1 + ":" + verb2);
}
icons.append(verb3);
- colors.append((float) verb1 / (float) verb2 == proportion ? OPTION_COLOR_BLUE : OPTION_COLOR_NORMAL);
+ colors.append((float) verb1 / (float) verb2 == proportion ? OptionColor.BLUE : OptionColor.NORMAL);
}
if (!currentCropState.isEmpty()) {
- colors.append(OPTION_COLOR_RED);
+ colors.append(OptionColor.RED);
ids.append(R.id.btn_crop_reset);
strings.append(R.string.Reset);
icons.append(R.drawable.baseline_cancel_24);
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/OptionsLayout.java b/app/src/main/java/org/thunderdog/challegram/navigation/OptionsLayout.java
index 08a8681b74..6b6d80d40f 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/OptionsLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/OptionsLayout.java
@@ -136,16 +136,16 @@ protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
public static @ColorId int getOptionColorId (int color) {
switch (color) {
- case ViewController.OPTION_COLOR_NORMAL: {
+ case ViewController.OptionColor.NORMAL: {
return ColorId.text;
}
- case ViewController.OPTION_COLOR_RED: {
+ case ViewController.OptionColor.RED: {
return ColorId.textNegative;
}
- case ViewController.OPTION_COLOR_BLUE: {
+ case ViewController.OptionColor.BLUE: {
return ColorId.textNeutral;
}
- case ViewController.OPTION_COLOR_GREEN: {
+ case ViewController.OptionColor.GREEN: {
return ColorId.iconPositive;
}
}
@@ -176,7 +176,7 @@ public static TextView genOptionView (Context context, int id, CharSequence stri
if (icon != 0) {
Drawable drawable = Drawables.get(context.getResources(), icon);
if (drawable != null) {
- final int drawableColorId = color == ViewController.OPTION_COLOR_NORMAL ? ColorId.icon : colorId;
+ final int drawableColorId = color == ViewController.OptionColor.NORMAL ? ColorId.icon : colorId;
drawable.setColorFilter(Paints.getColorFilter(forcedTheme != null ? forcedTheme.getColor(drawableColorId) : Theme.getColor(drawableColorId)));
if (themeProvider != null) {
themeProvider.addThemeFilterListener(drawable, drawableColorId);
@@ -227,7 +227,7 @@ public void setSubtitle (CharSequence name, @DrawableRes int icon, int color) {
if (icon != 0) {
Drawable drawable = Drawables.get(getContext().getResources(), icon);
if (drawable != null) {
- final int drawableColorId = color == ViewController.OPTION_COLOR_NORMAL ? ColorId.icon : colorId;
+ final int drawableColorId = color == ViewController.OptionColor.NORMAL ? ColorId.icon : colorId;
drawable.setColorFilter(Paints.getColorFilter(forcedTheme != null ? forcedTheme.getColor(drawableColorId) : Theme.getColor(drawableColorId)));
if (parent != null) {
parent.addThemeFilterListener(drawable, drawableColorId);
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/TelegramViewController.java b/app/src/main/java/org/thunderdog/challegram/navigation/TelegramViewController.java
index 7c03d0f5a2..1f6054dd3b 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/TelegramViewController.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/TelegramViewController.java
@@ -126,7 +126,7 @@ protected boolean needChatSearchManagerPreparation () {
private static final boolean DEBUG_CHATS_SEARCH_ADAPTER = false;
private void removeSuggestedChat (final TGFoundChat chat) {
- showOptions(Lang.getStringBold(R.string.ChatHintsDelete, chat.getTitle()), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[]{OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_sweep_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getStringBold(R.string.ChatHintsDelete, chat.getTitle()), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_sweep_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_delete) {
chatSearchManager.removeTopChat(chat.getId());
}
@@ -135,7 +135,7 @@ private void removeSuggestedChat (final TGFoundChat chat) {
}
private void removeRecentChat (final TGFoundChat chat) {
- showOptions(Lang.getStringBold(R.string.DeleteXFromRecents, chat.getTitle()), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[]{OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getStringBold(R.string.DeleteXFromRecents, chat.getTitle()), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_delete) {
chatSearchManager.removeRecentlyFoundChat(chat);
}
@@ -332,7 +332,7 @@ protected void setChatData (ListItem item, VerticalChatView chatView) {
if (!chatSearchManager.areLocalChatsRecent()) {
return;
}
- showOptions(Lang.getString(R.string.ClearRecentsHint), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.Clear), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.ClearRecentsHint), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.Clear), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_delete) {
chatSearchManager.clearRecentlyFoundChats();
}
@@ -427,7 +427,7 @@ protected void setInfo (ListItem item, int position, ListInfoView infoView) {
return false;
}
final TGFoundChat chat = (TGFoundChat) item.getData();
- showOptions(Lang.getStringBold(R.string.DeleteXFromRecents, chat.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getStringBold(R.string.DeleteXFromRecents, chat.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_delete) {
chatSearchManager.removeRecentlyFoundChat(chat);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/ViewController.java b/app/src/main/java/org/thunderdog/challegram/navigation/ViewController.java
index a1542c4767..26038fec1f 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/ViewController.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/ViewController.java
@@ -50,6 +50,7 @@
import androidx.annotation.CallSuper;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@@ -120,6 +121,8 @@
import org.thunderdog.challegram.widget.ShadowView;
import org.thunderdog.challegram.widget.TimerView;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
@@ -2184,15 +2187,17 @@ public final void showCopyUrlOptions (final String url, final @Nullable TdlibUi.
// Options delegate
- public static final int OPTION_COLOR_NORMAL = 0x01;
- public static final int OPTION_COLOR_RED = 0x02;
- public static final int OPTION_COLOR_BLUE = 0x03;
- public static final int OPTION_COLOR_GREEN = 0x04;
-
- public static final float DISABLED_ALPHA = .7f;
-
- private OptionsLayout optionsWrap;
- private View.OnClickListener onOptionClick;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ OptionColor.NORMAL,
+ OptionColor.RED,
+ OptionColor.BLUE,
+ OptionColor.GREEN,
+ OptionColor.INACTIVE
+ })
+ public @interface OptionColor {
+ int NORMAL = 1, RED = 2, BLUE = 3, GREEN = 4, INACTIVE = 5;
+ }
public final void showCallOptions (final String phoneNumber, final long userId) {
if (userId == 0) {
@@ -2226,11 +2231,11 @@ public final PopupLayout showOptions (int[] ids, String[] titles) {
}
public final PopupLayout showConfirm (@Nullable CharSequence info, @Nullable String okString, @NonNull Runnable onConfirm) {
- return showConfirm(info, okString, R.drawable.baseline_check_circle_24, OPTION_COLOR_NORMAL, onConfirm);
+ return showConfirm(info, okString, R.drawable.baseline_check_circle_24, OptionColor.NORMAL, onConfirm);
}
public final PopupLayout showConfirm (@Nullable CharSequence info, @Nullable String okString, int okIcon, int okColor, @NonNull Runnable onConfirm) {
- return showOptions(info, new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {okString != null ? okString : Lang.getString(R.string.OK), Lang.getString(R.string.Cancel)}, new int[] {okColor, OPTION_COLOR_NORMAL}, new int[] {okIcon, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ return showOptions(info, new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {okString != null ? okString : Lang.getString(R.string.OK), Lang.getString(R.string.Cancel)}, new int[] {okColor, OptionColor.NORMAL}, new int[] {okIcon, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_done) {
onConfirm.run();
}
@@ -2259,7 +2264,7 @@ public final void showUnsavedChangesPromptBeforeLeaving (@Nullable Runnable onCo
}
public final void showUnsavedChangesPromptBeforeLeaving (@Nullable CharSequence info, @NonNull String discardText, @Nullable Runnable onConfirm) {
- showOptions(info, new int[]{R.id.btn_done, R.id.btn_cancel}, new String[]{discardText, Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(info, new int[]{R.id.btn_done, R.id.btn_cancel}, new String[]{discardText, Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_done) {
if (onConfirm != null)
onConfirm.run();
@@ -2274,7 +2279,7 @@ public final PopupLayout showOptions (CharSequence info, int[] ids, String[] tit
}
public final PopupLayout showWarning (CharSequence info, RunnableBool callback) {
- return showOptions(info, new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.TdlibLogsWarningConfirm), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_warning_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ return showOptions(info, new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.TdlibLogsWarningConfirm), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_warning_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
callback.runWithBool(id == R.id.btn_done);
return true;
});
@@ -2298,7 +2303,7 @@ public OptionItem (int id, CharSequence name, int color, int icon) {
public static class Builder {
private int id;
private CharSequence name;
- private int color = OPTION_COLOR_NORMAL;
+ private int color = OptionColor.NORMAL;
private int icon;
public Builder () {
@@ -2400,7 +2405,7 @@ public final PopupLayout showOptions (CharSequence info, int[] ids, String[] tit
public final Options getOptions (CharSequence info, int[] ids, String[] titles, int[] colors, int[] icons) {
OptionItem[] items = new OptionItem[ids.length];
for (int i = 0; i < ids.length; i++) {
- items[i] = new OptionItem(ids != null ? ids[i] : i, titles[i], colors != null ? colors[i] : OPTION_COLOR_NORMAL, icons != null ? icons[i] : 0);
+ items[i] = new OptionItem(ids != null ? ids[i] : i, titles[i], colors != null ? colors[i] : OptionColor.NORMAL, icons != null ? icons[i] : 0);
}
return new Options(info, null, null, items);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java
index 6ef39e32b7..40b79c7764 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java
@@ -465,7 +465,7 @@ public void kickMember (ViewController> c, final long chatId, final TdApi.Mess
if (senderId.getConstructor() == TdApi.MessageSenderChat.CONSTRUCTOR)
return;
if (ChatId.getType(chatId) == TdApi.ChatTypeBasicGroup.CONSTRUCTOR) {
- c.showOptions(Lang.getStringBold(R.string.MemberCannotJoinRegularGroup, tdlib.senderName(senderId, true)), new int[] {R.id.btn_blockSender, R.id.btn_cancel}, new String[]{Lang.getString(R.string.RemoveFromGroup), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ c.showOptions(Lang.getStringBold(R.string.MemberCannotJoinRegularGroup, tdlib.senderName(senderId, true)), new int[] {R.id.btn_blockSender, R.id.btn_cancel}, new String[]{Lang.getString(R.string.RemoveFromGroup), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[]{R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_blockSender) {
tdlib.setChatMemberStatus(chatId, senderId, new TdApi.ChatMemberStatusLeft(), currentStatus, null);
}
@@ -636,7 +636,7 @@ public static void showDeleteOptions (final ViewController> context, final TdA
context.showOptions(null,
new int[] {R.id.menu_btn_delete, R.id.btn_cancel},
new String[] {deleteActionMsg, Lang.getString(R.string.Cancel)},
- new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL},
+ new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL},
new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24},
(itemView, id) -> {
if (id == R.id.menu_btn_delete) {
@@ -744,7 +744,7 @@ public void act () {
popupFinal[0].hideWindow(true);
}
return false;
- }), new int[] {R.id.btn_openChat, R.id.btn_cancel}, StringList.asArray(R.string.AskButton, R.string.Cancel), new int[] {ViewController.OPTION_COLOR_BLUE, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_help_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ }), new int[] {R.id.btn_openChat, R.id.btn_cancel}, StringList.asArray(R.string.AskButton, R.string.Cancel), new int[] {ViewController.OptionColor.BLUE, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_help_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_openChat) {
tdlib.client().send(new TdApi.GetSupportUser(), object -> {
switch (object.getConstructor()) {
@@ -959,7 +959,7 @@ public static void fillMuteOptions (IntList ids, IntList icons, StringList strin
icons.append(R.drawable.baseline_notifications_24);
ids.append(R.id.btn_menu_enable);
if (colors != null) {
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
}
@@ -968,7 +968,7 @@ public static void fillMuteOptions (IntList ids, IntList icons, StringList strin
icons.append(R.drawable.baseline_notifications_off_24);
ids.append(R.id.btn_menu_resetToDefault);
if (colors != null) {
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
}
@@ -977,7 +977,7 @@ public static void fillMuteOptions (IntList ids, IntList icons, StringList strin
icons.append(R.drawable.baseline_notifications_off_24);
ids.append(R.id.btn_menu_disable);
if (colors != null) {
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
}
@@ -992,9 +992,9 @@ public static void fillMuteOptions (IntList ids, IntList icons, StringList strin
ids.append(R.id.btn_menu_8hours);
ids.append(R.id.btn_menu_2days);
if (colors != null) {
- colors.append(ViewController.OPTION_COLOR_NORMAL);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
}
@@ -1003,7 +1003,7 @@ public static void fillMuteOptions (IntList ids, IntList icons, StringList strin
strings.append(R.string.NotificationsCustomize);
icons.append(R.drawable.baseline_settings_24);
if (colors != null) {
- colors.append(prioritizeCustomization ? ViewController.OPTION_COLOR_RED : ViewController.OPTION_COLOR_NORMAL);
+ colors.append(prioritizeCustomization ? ViewController.OptionColor.RED : ViewController.OptionColor.NORMAL);
}
}
}
@@ -3743,17 +3743,17 @@ public void openProxyAlert (TdlibDelegate context, String server, int port, TdAp
ids.append(R.id.btn_addProxy);
strings.append(R.string.ProxyEnable);
icons.append(R.drawable.baseline_security_24);
- colors.append(ViewController.OPTION_COLOR_BLUE);
+ colors.append(ViewController.OptionColor.BLUE);
ids.append(R.id.btn_save);
strings.append(R.string.ProxySaveForLater);
icons.append(R.drawable.baseline_playlist_add_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
ids.append(R.id.btn_cancel);
strings.append(R.string.Cancel);
icons.append(R.drawable.baseline_cancel_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
c.showOptions(msg, ids.get(), strings.get(), colors.get(), icons.get(), (itemView, id) -> {
if (id == R.id.btn_addProxy) {
@@ -3773,7 +3773,7 @@ public void permanentlyDeleteAccount (ViewController> context, boolean showAlt
Lang.getMarkdownString(context, needShowAlternatives ? R.string.DeleteAccountConfirmFirst : R.string.DeleteAccountConfirm),
new int[] {R.id.btn_deleteAccount, R.id.btn_cancel},
new String[] {Lang.getString(needShowAlternatives ? R.string.DeleteAccountConfirmFirstBtn : R.string.DeleteAccountConfirmBtn), Lang.getString(R.string.Cancel)},
- new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL},
+ new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL},
new int[] {R.drawable.baseline_delete_alert_24, R.drawable.baseline_cancel_24},
(optionItemView, id) -> {
if (id == R.id.btn_deleteAccount) {
@@ -4218,7 +4218,7 @@ private void leaveJoinChat (ViewController> context, long chatId, boolean join
});
context.showSettings(b);
} else {
- context.showOptions(informationStr, new int[]{checkId, R.id.btn_cancel}, new String[]{Lang.getString(confirmButtonRes), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ context.showOptions(informationStr, new int[]{checkId, R.id.btn_cancel}, new String[]{Lang.getString(confirmButtonRes), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == checkId) {
act.runWithBool(false);
}
@@ -4328,7 +4328,7 @@ private void showClearHistoryConfirm (ViewController> context, final long chat
info,
new int[]{R.id.btn_clearChatHistory, R.id.btn_cancel},
new String[]{confirmButton, Lang.getString(R.string.Cancel)},
- new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL},
+ new int[]{ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL},
new int[]{confirmButtonIcon, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_clearChatHistory) {
if (needSecondaryConfirm && !isSecondaryConfirm) {
@@ -4352,7 +4352,7 @@ private void showDeleteOrClearHistory (final ViewController> context, final lo
onDelete.run();
return;
}
- context.showOptions(chatName, new int[] {R.id.btn_removeChatFromList, R.id.btn_clearChatHistory}, new String[] {Lang.getString(getDeleteChatStringRes(chatId, allowBlock)), Lang.getString(R.string.ClearHistory)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.templarian_baseline_broom_24}, (itemView, id) -> {
+ context.showOptions(chatName, new int[] {R.id.btn_removeChatFromList, R.id.btn_clearChatHistory}, new String[] {Lang.getString(getDeleteChatStringRes(chatId, allowBlock)), Lang.getString(R.string.ClearHistory)}, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.templarian_baseline_broom_24}, (itemView, id) -> {
if (id == R.id.btn_removeChatFromList) {
onDelete.run();
} else if (id == R.id.btn_clearChatHistory) {
@@ -4374,7 +4374,7 @@ private void showHidePsaConfirm (final ViewController> context, final TdApi.Ch
context.showOptions(Lang.getPsaHideConfirm((TdApi.ChatSourcePublicServiceAnnouncement) source, tdlib.chatTitle(chatId)),
new int[] {R.id.btn_delete, R.id.btn_cancel},
new String[] {Lang.getString(R.string.PsaHideDone), Lang.getString(R.string.Cancel)},
- new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL},
+ new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL},
new int[] {R.drawable.baseline_delete_sweep_24, R.drawable.baseline_cancel_24},
(optionItemView, id) -> {
if (id == R.id.btn_delete) {
@@ -4436,7 +4436,7 @@ private void showDeleteChatConfirm (final ViewController> context, final long
})
);
} else {
- context.showOptions(info, new int[] {R.id.btn_removeChatFromList, R.id.btn_cancel}, new String[] {Lang.getString(deleteAndStop ? R.string.DeleteAndStop : R.string.DeleteChat), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, resultId) -> {
+ context.showOptions(info, new int[] {R.id.btn_removeChatFromList, R.id.btn_cancel}, new String[] {Lang.getString(deleteAndStop ? R.string.DeleteAndStop : R.string.DeleteChat), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, resultId) -> {
if (resultId == R.id.btn_removeChatFromList) {
if (blockUser) {
tdlib.blockSender(new TdApi.MessageSenderUser(userId), new TdApi.BlockListMain(), blockResult -> deleter.runWithBool(false));
@@ -4483,7 +4483,7 @@ private void showDeleteChatConfirm (final ViewController> context, final long
})
);
} else {
- context.showOptions(Lang.getStringBold(secretChat.state.getConstructor() == TdApi.SecretChatStatePending.CONSTRUCTOR ? R.string.DeleteSecretChatPendingConfirm : R.string.DeleteSecretChatClosedConfirm, userName), new int[] {R.id.btn_removeChatFromList, R.id.btn_cancel}, new String[]{Lang.getString(R.string.DeleteChat), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ context.showOptions(Lang.getStringBold(secretChat.state.getConstructor() == TdApi.SecretChatStatePending.CONSTRUCTOR ? R.string.DeleteSecretChatPendingConfirm : R.string.DeleteSecretChatClosedConfirm, userName), new int[] {R.id.btn_removeChatFromList, R.id.btn_cancel}, new String[]{Lang.getString(R.string.DeleteChat), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_removeChatFromList) {
if (blockUser) {
tdlib.blockSender(new TdApi.MessageSenderUser(secretChat.userId), new TdApi.BlockListMain(), blockResult -> deleter.runWithBool(false));
@@ -4503,7 +4503,7 @@ private void showDeleteChatConfirm (final ViewController> context, final long
if (status != null && TD.isMember(status, false)) {
leaveJoinChat(context, chatId, false, after);
} else {
- context.showOptions(Lang.getString(R.string.AreYouSureDeleteThisChat), new int[] {R.id.btn_removeChatFromList, R.id.btn_cancel}, new String[] {Lang.getString(R.string.DeleteChat), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ context.showOptions(Lang.getString(R.string.AreYouSureDeleteThisChat), new int[] {R.id.btn_removeChatFromList, R.id.btn_cancel}, new String[] {Lang.getString(R.string.DeleteChat), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_removeChatFromList) {
deleter.runWithBool(false);
}
@@ -4581,21 +4581,21 @@ public void showChatOptions (ViewController> context, final TdApi.ChatList cha
if (canSelect && onSelect != null) {
ids.append(R.id.btn_selectChat);
strings.append(isSelected ? R.string.Unselect : R.string.Select);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
icons.append(R.drawable.baseline_playlist_add_check_24);
}
if (!tdlib.isSelfChat(chatId)) {
ids.append(R.id.btn_notifications);
strings.append(hasNotifications ? R.string.MuteNotifications : R.string.EnableNotifications);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
icons.append(hasNotifications ? R.drawable.baseline_notifications_off_24 : R.drawable.baseline_notifications_24);
}
if (position != null) {
ids.append(position.isPinned ? R.id.btn_unpinChat : R.id.btn_pinChat);
strings.append(position.isPinned ? R.string.UnpinFromTop : R.string.PinToTop);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
icons.append(position.isPinned ? R.drawable.deproko_baseline_pin_undo_24 : R.drawable.deproko_baseline_pin_24);
}
@@ -4603,7 +4603,7 @@ public void showChatOptions (ViewController> context, final TdApi.ChatList cha
boolean isArchived = chatList instanceof TdApi.ChatListArchive;
ids.append(isArchived ? R.id.btn_unarchiveChat : R.id.btn_archiveChat);
strings.append(isArchived ? R.string.UnarchiveChat : R.string.ArchiveChat);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
icons.append(isArchived ? R.drawable.baseline_unarchive_24 : R.drawable.baseline_archive_24);
}
@@ -4611,12 +4611,12 @@ public void showChatOptions (ViewController> context, final TdApi.ChatList cha
if (TD.isChatListMain(chatList) || TD.isChatListArchive(chatList)) {
ids.append(R.id.btn_addChatToFolder);
strings.append(R.string.AddToFolder);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
icons.append(R.drawable.templarian_baseline_folder_plus_24);
} else if (TD.isChatListFolder(chatList)) {
ids.append(R.id.btn_removeChatFromFolder);
strings.append(R.string.RemoveFromFolder);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
icons.append(R.drawable.templarian_baseline_folder_remove_24);
}
}
@@ -4627,18 +4627,18 @@ public void showChatOptions (ViewController> context, final TdApi.ChatList cha
if (!canRead || chat.unreadCount == 0 || !hasPasscode) { // when passcode is set, "mark as read" is unavailable, when there are some unread messages
ids.append(canRead ? R.id.btn_markChatAsRead : R.id.btn_markChatAsUnread);
strings.append(canRead ? R.string.MarkAsRead : R.string.MarkAsUnread);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
icons.append(canRead ? Config.ICON_MARK_AS_READ : Config.ICON_MARK_AS_UNREAD);
}
if (!hasPasscode && tdlib.canClearHistory(chat)) {
ids.append(R.id.btn_clearChatHistory);
strings.append(R.string.ClearHistory);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
icons.append(R.drawable.templarian_baseline_broom_24);
}
- colors.append(ViewController.OPTION_COLOR_RED);
+ colors.append(ViewController.OptionColor.RED);
icons.append(R.drawable.baseline_delete_24);
switch (chat.type.getConstructor()) {
case TdApi.ChatTypePrivate.CONSTRUCTOR: {
@@ -4734,21 +4734,21 @@ public void showInviteLinkOptions (ViewController> context, final TdApi.ChatIn
ids.append(R.id.btn_viewInviteLinkMembers);
strings.append(R.string.InviteLinkViewMembers);
icons.append(R.drawable.baseline_visibility_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
if (!deleted && link.createsJoinRequest && link.pendingJoinRequestCount > 0) {
ids.append(R.id.btn_manageJoinRequests);
strings.append(R.string.InviteLinkViewRequests);
icons.append(R.drawable.baseline_pending_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
if (showNavigatingToLinks && tdlib.canManageInviteLinks(chat)) {
ids.append(R.id.btn_manageInviteLinks);
strings.append(R.string.InviteLinkManage);
icons.append(R.drawable.baseline_add_link_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
if (!deleted && !link.isRevoked) {
@@ -4756,34 +4756,34 @@ public void showInviteLinkOptions (ViewController> context, final TdApi.ChatIn
ids.append(R.id.btn_edit);
strings.append(R.string.InviteLinkEdit);
icons.append(R.drawable.baseline_edit_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
}
ids.append(R.id.btn_copyLink);
strings.append(R.string.InviteLinkCopy);
icons.append(R.drawable.baseline_content_copy_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
ids.append(R.id.btn_shareLink);
strings.append(R.string.ShareLink);
icons.append(R.drawable.baseline_forward_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
icons.append(R.drawable.baseline_link_off_24);
ids.append(R.id.btn_revokeLink);
strings.append(R.string.RevokeLink);
- colors.append(ViewController.OPTION_COLOR_RED);
+ colors.append(ViewController.OptionColor.RED);
} else {
ids.append(R.id.btn_copyLink);
strings.append(R.string.InviteLinkCopy);
icons.append(R.drawable.baseline_content_copy_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
if (!deleted) {
icons.append(R.drawable.baseline_delete_24);
ids.append(R.id.btn_deleteLink);
strings.append(R.string.InviteLinkDelete);
- colors.append(ViewController.OPTION_COLOR_RED);
+ colors.append(ViewController.OptionColor.RED);
}
}
@@ -4824,7 +4824,7 @@ public void showInviteLinkOptions (ViewController> context, final TdApi.ChatIn
sc.setArguments(new ShareController.Args(text).setShare(exportText, null));
sc.show();
} else if (id == R.id.btn_deleteLink) {
- context.showOptions(Lang.getString(R.string.AreYouSureDeleteInviteLink), new int[] {R.id.btn_deleteLink, R.id.btn_cancel}, new String[] {Lang.getString(R.string.InviteLinkDelete), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
+ context.showOptions(Lang.getString(R.string.AreYouSureDeleteInviteLink), new int[] {R.id.btn_deleteLink, R.id.btn_cancel}, new String[] {Lang.getString(R.string.InviteLinkDelete), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
if (id2 == R.id.btn_deleteLink) {
if (onLinkDeleted != null) onLinkDeleted.run();
context.tdlib().client().send(new TdApi.DeleteRevokedChatInviteLink(chatId, link.inviteLink), tdlib.okHandler());
@@ -4833,7 +4833,7 @@ public void showInviteLinkOptions (ViewController> context, final TdApi.ChatIn
return true;
});
} else if (id == R.id.btn_revokeLink) {
- context.showOptions(Lang.getString(context.tdlib().isChannel(chatId) ? R.string.AreYouSureRevokeInviteLinkChannel : R.string.AreYouSureRevokeInviteLinkGroup), new int[] {R.id.btn_revokeLink, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RevokeLink), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_link_off_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
+ context.showOptions(Lang.getString(context.tdlib().isChannel(chatId) ? R.string.AreYouSureRevokeInviteLinkChannel : R.string.AreYouSureRevokeInviteLinkGroup), new int[] {R.id.btn_revokeLink, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RevokeLink), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_link_off_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
if (id2 == R.id.btn_revokeLink) {
context.tdlib().client().send(new TdApi.RevokeChatInviteLink(chatId, link.inviteLink), result -> {
if (result.getConstructor() == TdApi.ChatInviteLinks.CONSTRUCTOR && onLinkRevoked != null) {
@@ -5413,7 +5413,7 @@ public void showLanguageInstallPrompt (TdlibDelegate c, TdApi.LanguagePackInfo i
return;
}
CharSequence text = Strings.buildMarkdown(c, Lang.getString(info.isOfficial ? R.string.LanguageAlert : R.string.LanguageCustomAlert, info.name, (int) Math.floor((float) info.translatedStringCount / (float) info.totalStringCount * 100f), info.translationUrl), null);
- context.showOptions(text, new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.LanguageChange), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_BLUE, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_language_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ context.showOptions(text, new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.LanguageChange), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.BLUE, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_language_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_done) {
c.tdlib().client().send(new TdApi.AddCustomServerLanguagePack(info.id), result -> {
switch (result.getConstructor()) {
@@ -5462,7 +5462,7 @@ public void showLanguageInstallPrompt (ViewController> c, CustomLangPackResult
out.getCompletenessPercentage(),
out.isComplete() ? Lang.plural(R.string.xStrings, out.getStringsCount()) : Lang.plural(R.string.xStrings, out.getStringsCount()) + ", " + Lang.plural(R.string.TranslationsMissing, out.getMissingStringsCount())
),
- new int[] {R.id.btn_messageApplyLocalization, R.id.btn_cancel}, new String[] {Lang.getString(R.string.LanguageInstall), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_BLUE, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_language_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ new int[] {R.id.btn_messageApplyLocalization, R.id.btn_cancel}, new String[] {Lang.getString(R.string.LanguageInstall), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.BLUE, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_language_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_messageApplyLocalization) {
applyLocalisation(c, out);
}
@@ -5496,7 +5496,7 @@ public static void removeAccount (ViewController> context, final TdlibAccount
}
private static void removeAccount (ViewController> context, final TdlibAccount account, boolean isSignOut) {
- context.showOptions(Lang.getStringBold(isSignOut ? R.string.SignOutHint2 : R.string.RemoveAccountHint2, account.getName()), new int[]{R.id.btn_removeAccount, R.id.btn_cancel}, new String[]{Lang.getString(R.string.LogOut), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_logout_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ context.showOptions(Lang.getStringBold(isSignOut ? R.string.SignOutHint2 : R.string.RemoveAccountHint2, account.getName()), new int[]{R.id.btn_removeAccount, R.id.btn_cancel}, new String[]{Lang.getString(R.string.LogOut), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_logout_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_removeAccount) {
account.tdlib().signOut();
}
@@ -5537,7 +5537,7 @@ public void showDeleteThemeConfirm (ViewController> context, ThemeInfo theme,
//noinspection WrongConstant
if (!ThemeManager.isCustomTheme(theme.getId()))
return;
- context.showOptions(Lang.getString(R.string.ThemeRemoveInfo), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ThemeRemoveConfirm), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ context.showOptions(Lang.getString(R.string.ThemeRemoveInfo), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ThemeRemoveConfirm), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_done) {
ThemeManager.instance().removeCustomTheme(tdlib, theme.getId(), theme.parentThemeId(), onDelete);
}
@@ -5923,19 +5923,19 @@ public void readCustomTheme (ViewController> context, TdApi.File doc, @Nullabl
ids.append(R.id.btn_done);
icons.append(R.drawable.baseline_palette_24);
- colors.append(ViewController.OPTION_COLOR_BLUE);
+ colors.append(ViewController.OptionColor.BLUE);
strings.append(R.string.ThemeInstallDone);
if (onError != null) {
ids.append(R.id.btn_open);
icons.append(R.drawable.baseline_open_in_browser_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
strings.append(R.string.Open);
}
ids.append(R.id.btn_cancel);
icons.append(R.drawable.baseline_cancel_24);
- colors.append(ViewController.OPTION_COLOR_NORMAL);
+ colors.append(ViewController.OptionColor.NORMAL);
strings.append(R.string.Cancel);
if (info != null) {
@@ -6453,7 +6453,7 @@ public boolean pickSchedulingState (ViewController> context, RunnableData context, long userId) {
if (tdlib.cache().userContact(userId)) {
- context.showOptions(Lang.getStringBold(R.string.DeleteContactConfirm, tdlib.cache().userName(userId)), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id1) -> {
+ context.showOptions(Lang.getStringBold(R.string.DeleteContactConfirm, tdlib.cache().userName(userId)), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id1) -> {
if (!context.isDestroyed() && id1 == R.id.btn_delete) {
tdlib.client().send(new TdApi.RemoveContacts(new long[] {userId}), tdlib.okHandler());
}
@@ -6488,7 +6488,7 @@ public void requestTransferOwnership (ViewController> context, CharSequence fi
context.addOneShotFocusListener(() ->
context.showOptions(new ViewController.Options.Builder()
.info(Strings.getTitleAndText(Lang.getString(R.string.TransferOwnershipAlert), finalAlertMessageText))
- .item(new ViewController.OptionItem(R.id.btn_next, Lang.getString(R.string.TransferOwnershipConfirm), ViewController.OPTION_COLOR_RED, R.drawable.templarian_baseline_account_switch_24))
+ .item(new ViewController.OptionItem(R.id.btn_next, Lang.getString(R.string.TransferOwnershipConfirm), ViewController.OptionColor.RED, R.drawable.templarian_baseline_account_switch_24))
.cancelItem()
.build(), (optionView, id) -> {
if (id == R.id.btn_next) {
@@ -6506,7 +6506,7 @@ public void requestTransferOwnership (ViewController> context, CharSequence fi
case TdApi.CanTransferOwnershipResultPasswordNeeded.CONSTRUCTOR: {
context.showOptions(new ViewController.Options.Builder()
.info(Strings.getTitleAndText(Lang.getString(R.string.TransferOwnershipSecurityAlert), Lang.getMarkdownString(context, R.string.TransferOwnershipSecurityPasswordNeeded)))
- .item(new ViewController.OptionItem(R.id.btn_next, Lang.getString(R.string.TransferOwnershipSecurityActionSetPassword), ViewController.OPTION_COLOR_BLUE, R.drawable.mrgrigri_baseline_textbox_password_24))
+ .item(new ViewController.OptionItem(R.id.btn_next, Lang.getString(R.string.TransferOwnershipSecurityActionSetPassword), ViewController.OptionColor.BLUE, R.drawable.mrgrigri_baseline_textbox_password_24))
.cancelItem()
.build(), (optionView, id) -> {
if (id == R.id.btn_next) {
@@ -6522,7 +6522,7 @@ public void requestTransferOwnership (ViewController> context, CharSequence fi
case TdApi.CanTransferOwnershipResultPasswordTooFresh.CONSTRUCTOR: {
context.showOptions(new ViewController.Options.Builder()
.info(Strings.getTitleAndText(Lang.getString(R.string.TransferOwnershipSecurityAlert), Lang.getMarkdownString(context, R.string.TransferOwnershipSecurityWaitPassword, Lang.getDuration(((TdApi.CanTransferOwnershipResultPasswordTooFresh) canTransferOwnership).retryAfter))))
- .item(new ViewController.OptionItem(R.id.btn_next, Lang.getString(R.string.OK), ViewController.OPTION_COLOR_NORMAL, R.drawable.baseline_check_circle_24))
+ .item(new ViewController.OptionItem(R.id.btn_next, Lang.getString(R.string.OK), ViewController.OptionColor.NORMAL, R.drawable.baseline_check_circle_24))
.cancelItem()
.build(), (optionView, id) -> true
);
@@ -6531,7 +6531,7 @@ public void requestTransferOwnership (ViewController> context, CharSequence fi
case TdApi.CanTransferOwnershipResultSessionTooFresh.CONSTRUCTOR: {
context.showOptions(new ViewController.Options.Builder()
.info(Strings.getTitleAndText(Lang.getString(R.string.TransferOwnershipSecurityAlert), Lang.getMarkdownString(context, R.string.TransferOwnershipSecurityWaitSession, Lang.getDuration(((TdApi.CanTransferOwnershipResultSessionTooFresh) canTransferOwnership).retryAfter))))
- .item(new ViewController.OptionItem(R.id.btn_next, Lang.getString(R.string.OK), ViewController.OPTION_COLOR_NORMAL, R.drawable.baseline_check_circle_24))
+ .item(new ViewController.OptionItem(R.id.btn_next, Lang.getString(R.string.OK), ViewController.OptionColor.NORMAL, R.drawable.baseline_check_circle_24))
.cancelItem()
.build(), (optionView, id) -> true
);
@@ -7071,16 +7071,16 @@ public void showMenuForProfile (@Nullable MediaCollectorDelegate delegate, boole
if (profilePhotoToDelete != 0 && !isPublic) {
b.item(new ViewController.OptionItem(R.id.btn_open, Lang.getString(R.string.Open),
- ViewController.OPTION_COLOR_NORMAL, R.drawable.baseline_visibility_24));
+ ViewController.OptionColor.NORMAL, R.drawable.baseline_visibility_24));
}
b.item(new ViewController.OptionItem(R.id.btn_changePhotoGallery, Lang.getString(isPublic ? R.string.SetPublicPhoto : R.string.SetProfilePhoto),
- ViewController.OPTION_COLOR_NORMAL, R.drawable.baseline_image_24));
+ ViewController.OptionColor.NORMAL, R.drawable.baseline_image_24));
final Runnable deleteRunnable = () -> showDeletePhotoConfirm(() -> deleteProfilePhoto(profilePhotoToDelete));
if (profilePhotoToDelete != 0 && !isPublic) {
b.item(new ViewController.OptionItem(R.id.btn_changePhotoDelete, Lang.getString(R.string.Delete),
- ViewController.OPTION_COLOR_RED, R.drawable.baseline_delete_24));
+ ViewController.OptionColor.RED, R.drawable.baseline_delete_24));
}
showOptions(b.build(), (itemView, id) -> {
@@ -7107,11 +7107,11 @@ public void showMenuForChat (TdApi.Chat chat, MediaCollectorDelegate delegate, b
if (chat.photo != null && allowOpenPhoto) {
b.item(new ViewController.OptionItem(R.id.btn_open, Lang.getString(R.string.Open),
- ViewController.OPTION_COLOR_NORMAL, R.drawable.baseline_visibility_24));
+ ViewController.OptionColor.NORMAL, R.drawable.baseline_visibility_24));
}
b.item(new ViewController.OptionItem(R.id.btn_changePhotoGallery, Lang.getString(isChannel ? R.string.SetChannelPhoto : R.string.SetGroupPhoto),
- ViewController.OPTION_COLOR_NORMAL, R.drawable.baseline_image_24));
+ ViewController.OptionColor.NORMAL, R.drawable.baseline_image_24));
final boolean canDelete = chat.photo != null;
showOptions(b.build(), (itemView, id) -> {
@@ -7131,7 +7131,7 @@ public void showMenuForNonCreatedChat (EditHeaderView headerView, boolean isChan
ViewController.Options.Builder b = new ViewController.Options.Builder();
b.item(new ViewController.OptionItem(R.id.btn_changePhotoGallery, Lang.getString(isChannel ? R.string.SetChannelPhoto : R.string.SetGroupPhoto),
- ViewController.OPTION_COLOR_NORMAL, R.drawable.baseline_image_24));
+ ViewController.OptionColor.NORMAL, R.drawable.baseline_image_24));
final boolean canDelete = headerView.getImageFile() != null;
showOptions(b.build(), (itemView, id) -> {
@@ -7144,7 +7144,7 @@ public void showMenuForNonCreatedChat (EditHeaderView headerView, boolean isChan
}
private void showDeletePhotoConfirm (Runnable onConfirm) {
- context.showConfirm(Lang.getString(R.string.RemovePhotoConfirm), Lang.getString(R.string.Delete), R.drawable.baseline_delete_24, ViewController.OPTION_COLOR_RED, () -> {
+ context.showConfirm(Lang.getString(R.string.RemovePhotoConfirm), Lang.getString(R.string.Delete), R.drawable.baseline_delete_24, ViewController.OptionColor.RED, () -> {
onConfirm.run();
if (currentMediaLayout != null) {
currentMediaLayout.hide(false);
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/CallListController.java b/app/src/main/java/org/thunderdog/challegram/ui/CallListController.java
index 8e54a5353a..1e07d3fa35 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/CallListController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/CallListController.java
@@ -279,7 +279,7 @@ private ListItem newPeopleItem () {
}
private void removeTopChat (final TGFoundChat chat) {
- showOptions(Lang.getStringBold(R.string.ChatHintsDelete, chat.getTitle()), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[]{OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_sweep_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getStringBold(R.string.ChatHintsDelete, chat.getTitle()), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_sweep_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_delete) {
tdlib.client().send(new TdApi.RemoveTopChat(new TdApi.TopChatCategoryCalls(), chat.getChatId()), tdlib.okHandler());
if (hasTopChats()) {
@@ -617,7 +617,7 @@ public boolean onLongClick (View v) {
final long chatId = call.getChatId();
final long[] messageIdsToDelete = call.getMessageIds();
if (messageIdsToDelete != null) {
- showOptions(null, new int[]{R.id.btn_deleteAll, R.id.btn_openChat, R.id.btn_cancel}, new String[]{Lang.getString(R.string.DeleteEntry), Lang.getString(R.string.OpenChat), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL, ViewController.OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_sweep_24, R.drawable.baseline_chat_bubble_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(null, new int[]{R.id.btn_deleteAll, R.id.btn_openChat, R.id.btn_cancel}, new String[]{Lang.getString(R.string.DeleteEntry), Lang.getString(R.string.OpenChat), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL, OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_sweep_24, R.drawable.baseline_chat_bubble_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_deleteAll) {
tdlib.deleteMessages(chatId, messageIdsToDelete, false);
} else if (id == R.id.btn_openChat) {
@@ -688,7 +688,7 @@ public void onForceTouchAction (ForceTouchView.ForceTouchContext context, int ac
}).setSaveStr(R.string.Delete).setSaveColorId(ColorId.textNegative)
);
} else {
- showOptions(null, new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.DeleteEntry), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_sweep_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(null, new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.DeleteEntry), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_sweep_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_delete) {
tdlib.deleteMessages(chatId, call.getMessageIds(), false);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ChatLinksController.java b/app/src/main/java/org/thunderdog/challegram/ui/ChatLinksController.java
index 57022868fa..5438db3473 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ChatLinksController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ChatLinksController.java
@@ -294,7 +294,7 @@ public void onRemove (RecyclerView.ViewHolder viewHolder) {
TdApi.ChatInviteLink link = (TdApi.ChatInviteLink) item.getData();
if (link.isRevoked) {
- showOptions(Lang.getString(R.string.AreYouSureDeleteInviteLink), new int[]{R.id.btn_deleteLink, R.id.btn_cancel}, new String[]{Lang.getString(R.string.InviteLinkDelete), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
+ showOptions(Lang.getString(R.string.AreYouSureDeleteInviteLink), new int[]{R.id.btn_deleteLink, R.id.btn_cancel}, new String[]{Lang.getString(R.string.InviteLinkDelete), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
if (id2 == R.id.btn_deleteLink) {
inviteLinksRevoked.remove(link);
smOnRevokedLinkDeleted(link);
@@ -305,7 +305,7 @@ public void onRemove (RecyclerView.ViewHolder viewHolder) {
return true;
});
} else {
- showOptions(Lang.getString(tdlib.isChannel(chatId) ? R.string.AreYouSureRevokeInviteLinkChannel : R.string.AreYouSureRevokeInviteLinkGroup), new int[]{R.id.btn_revokeLink, R.id.btn_cancel}, new String[]{Lang.getString(R.string.RevokeLink), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_link_off_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
+ showOptions(Lang.getString(tdlib.isChannel(chatId) ? R.string.AreYouSureRevokeInviteLinkChannel : R.string.AreYouSureRevokeInviteLinkGroup), new int[]{R.id.btn_revokeLink, R.id.btn_cancel}, new String[]{Lang.getString(R.string.RevokeLink), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL}, new int[]{R.drawable.baseline_link_off_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
if (id2 == R.id.btn_revokeLink) {
tdlib.client().send(new TdApi.RevokeChatInviteLink(chatId, link.inviteLink), result -> {
if (result.getConstructor() == TdApi.ChatInviteLinks.CONSTRUCTOR) {
@@ -351,7 +351,7 @@ public void onClick (View v) {
notifyParentIfPossible();
}, (links) -> onLinkRevoked(link, links));
} else if (viewId == R.id.btn_deleteAllRevokedLinks) {
- showOptions(Lang.getString(R.string.AreYouSureDeleteAllInviteLinks), new int[] {R.id.btn_deleteAllRevokedLinks, R.id.btn_cancel}, new String[] {Lang.getString(R.string.DeleteAllRevokedLinks), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.AreYouSureDeleteAllInviteLinks), new int[] {R.id.btn_deleteAllRevokedLinks, R.id.btn_cancel}, new String[] {Lang.getString(R.string.DeleteAllRevokedLinks), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_deleteAllRevokedLinks) {
TdApi.ChatInviteLink firstLink = inviteLinksRevoked.get(0);
TdApi.ChatInviteLink lastLink = inviteLinksRevoked.get(inviteLinksRevoked.size() - 1);
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ChatStatisticsController.java b/app/src/main/java/org/thunderdog/challegram/ui/ChatStatisticsController.java
index d7391d02c3..a4c48e7989 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ChatStatisticsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ChatStatisticsController.java
@@ -664,7 +664,7 @@ private void openMemberMenu (DoubleTextWrapper content) {
if (TD.isCreator(member.status)) {
if (TD.isCreator(myStatus)) {
ids.append(R.id.btn_editRights);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_edit_24);
strings.append(R.string.EditAdminTitle);
}
@@ -672,7 +672,7 @@ private void openMemberMenu (DoubleTextWrapper content) {
int promoteMode = TD.canPromoteAdmin(myStatus, member.status);
if (promoteMode != TD.PROMOTE_MODE_NONE) {
ids.append(R.id.btn_editRights);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_stars_24);
switch (promoteMode) {
case TD.PROMOTE_MODE_EDIT:
@@ -693,7 +693,7 @@ private void openMemberMenu (DoubleTextWrapper content) {
int restrictMode = TD.canRestrictMember(myStatus, member.status);
if (restrictMode != TD.RESTRICT_MODE_NONE) {
ids.append(R.id.btn_restrictMember);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_block_24);
switch (restrictMode) {
@@ -712,7 +712,7 @@ private void openMemberMenu (DoubleTextWrapper content) {
if (restrictMode != TD.RESTRICT_MODE_VIEW && TD.isMember(member.status)) {
ids.append(R.id.btn_blockSender);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_remove_circle_24);
strings.append(R.string.RemoveFromGroup);
}
@@ -726,7 +726,7 @@ private void openMemberMenu (DoubleTextWrapper content) {
strings.append(Lang.getString(R.string.ViewMessagesFromUser, tdlib.cache().userFirstName(content.getUserId())));
}
icons.append(R.drawable.baseline_person_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
runOnUiThreadOptional(() -> showOptions("", ids.get(), strings.get(), colors.get(), icons.get(), (itemView, id) -> {
if (id == R.id.btn_messageViewList) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ChatsController.java b/app/src/main/java/org/thunderdog/challegram/ui/ChatsController.java
index 2002ac1fc0..66f96248ab 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ChatsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ChatsController.java
@@ -1512,7 +1512,7 @@ private void bulkDeleteChat (boolean clearHistory) {
String actionStr = Lang.plural(clearHistory ? R.string.ClearXHistories : R.string.DeleteXChats, selectedChats.size());
RunnableBool deleter = needRevoke -> {
- showOptions(Lang.getString(R.string.NoUndoWarn), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {actionStr, Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {clearHistory ? R.drawable.templarian_baseline_broom_24 : R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (v, optionId) -> {
+ showOptions(Lang.getString(R.string.NoUndoWarn), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {actionStr, Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {clearHistory ? R.drawable.templarian_baseline_broom_24 : R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (v, optionId) -> {
if (optionId == R.id.btn_delete) {
final int size = selectedChats.size();
AtomicInteger remaining = new AtomicInteger(size);
@@ -1548,7 +1548,7 @@ private void bulkDeleteChat (boolean clearHistory) {
showOptions(info,
new int[]{R.id.btn_delete, R.id.btn_cancel},
new String[]{actionStr, Lang.getString(R.string.Cancel)},
- new int[]{OPTION_COLOR_RED, OPTION_COLOR_NORMAL},
+ new int[]{OptionColor.RED, OptionColor.NORMAL},
new int[]{R.drawable.templarian_baseline_broom_24, R.drawable.baseline_cancel_24}, (v, optionId) -> {
if (optionId == R.id.btn_delete) {
deleter.runWithBool(false);
@@ -1676,7 +1676,7 @@ public void onMoreItemPressed (int id) {
Lang.pluralBold(botCount == count ? R.string.BlockXBots : R.string.BlockXUsers, count),
new int[] {R.id.btn_blockSender, R.id.btn_cancel},
new String[] {Lang.getString(R.string.BlockContact), Lang.getString(R.string.Cancel)},
- new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL},
+ new int[] {OptionColor.RED, OptionColor.NORMAL},
new int[] {R.drawable.baseline_block_24, R.drawable.baseline_cancel_24},
(v, optionId) -> {
if (optionId == R.id.btn_unblockSender || optionId == R.id.btn_blockSender) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/CreateGroupController.java b/app/src/main/java/org/thunderdog/challegram/ui/CreateGroupController.java
index 26a814bf7d..8e0bc315bb 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/CreateGroupController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/CreateGroupController.java
@@ -340,7 +340,7 @@ public void detachReceiver () {
private void onUserClick (TGUser user) {
pickedUser = user;
- showOptions(null, new int[] {R.id.btn_deleteMember, R.id.btn_cancel}, new String[] {Lang.getString(R.string.GroupDontAdd), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, this::onOptionItemPressed);
+ showOptions(null, new int[] {R.id.btn_deleteMember, R.id.btn_cancel}, new String[] {Lang.getString(R.string.GroupDontAdd), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, this::onOptionItemPressed);
}
public boolean onOptionItemPressed (View optionItemView, int id) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/CreatePollController.java b/app/src/main/java/org/thunderdog/challegram/ui/CreatePollController.java
index bff3bc5cbf..f2289fa64d 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/CreatePollController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/CreatePollController.java
@@ -321,7 +321,7 @@ private boolean canChangePollType () {
@Override
public boolean onBackPressed (boolean fromTop) {
if (hasUnsavedPoll()) {
- showOptions(Lang.getString(isQuiz ? R.string.QuizDiscardPrompt : R.string.PollDiscardPrompt), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(isQuiz ? R.string.QuizDiscard : R.string.PollDiscard), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(isQuiz ? R.string.QuizDiscardPrompt : R.string.PollDiscardPrompt), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(isQuiz ? R.string.QuizDiscard : R.string.PollDiscard), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_done) {
navigateBack();
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderController.java
index 33c5221a08..a4c5ff24da 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderController.java
@@ -514,7 +514,7 @@ private void showRemoveConditionConfirm (int position, ListItem item) {
stringRes = inclusion ? R.string.FolderRemoveInclusionConfirmType : R.string.FolderRemoveExclusionConfirmType;
}
CharSequence info = Lang.getStringBold(stringRes, title);
- showConfirm(info, Lang.getString(R.string.Remove), R.drawable.baseline_delete_24, OPTION_COLOR_RED, () -> {
+ showConfirm(info, Lang.getString(R.string.Remove), R.drawable.baseline_delete_24, OptionColor.RED, () -> {
int index = adapter.getItem(position) == item ? position : adapter.indexOfView(item);
if (index != RecyclerView.NO_POSITION) {
adapter.removeRange(index - 1, 2); /* separator, condition */
@@ -550,7 +550,7 @@ private void showRemoveConditionConfirm (int position, ListItem item) {
}
private void showRemoveFolderConfirm () {
- showConfirm(Lang.getString(R.string.RemoveFolderConfirm), Lang.getString(R.string.Remove), R.drawable.baseline_delete_24, OPTION_COLOR_RED, () -> {
+ showConfirm(Lang.getString(R.string.RemoveFolderConfirm), Lang.getString(R.string.Remove), R.drawable.baseline_delete_24, OptionColor.RED, () -> {
deleteChatFolder(chatFolderId);
});
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditDeleteAccountReasonController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditDeleteAccountReasonController.java
index f5802f5703..7af34dc235 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EditDeleteAccountReasonController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditDeleteAccountReasonController.java
@@ -76,7 +76,7 @@ public boolean onDonePressed (EditTextController> controller, DoneButton butto
Lang.getMarkdownString(controller, R.string.DeleteAccountConfirmFinal),
new int[] {R.id.btn_deleteAccount, R.id.btn_cancel},
new String[] {Lang.getString(R.string.DeleteAccountConfirmFinalBtn), Lang.getString(R.string.Cancel)},
- new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL},
+ new int[] {OptionColor.RED, OptionColor.NORMAL},
new int[] {R.drawable.baseline_delete_alert_24, R.drawable.baseline_cancel_24},
(optionItemView, id) -> {
if (id == R.id.btn_deleteAccount) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditLanguageController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditLanguageController.java
index 25466bf253..40fd29ba7d 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EditLanguageController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditLanguageController.java
@@ -467,7 +467,7 @@ private boolean navigateToPreviousString () {
}
private void exit (boolean forceBack) {
- showOptions(Lang.getStringBold(R.string.LocalizationEditConfirmPrompt, getArgumentsStrict().string.getKey()), new int[] {R.id.btn_save, R.id.btn_discard, R.id.btn_cancel}, new String[] {Lang.getString(R.string.LocalizationEditConfirmSave), Lang.getString(R.string.LocalizationEditConfirmDiscard), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_BLUE, OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_check_24, R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getStringBold(R.string.LocalizationEditConfirmPrompt, getArgumentsStrict().string.getKey()), new int[] {R.id.btn_save, R.id.btn_discard, R.id.btn_cancel}, new String[] {Lang.getString(R.string.LocalizationEditConfirmSave), Lang.getString(R.string.LocalizationEditConfirmDiscard), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.BLUE, OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_check_24, R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_save || id == R.id.btn_discard) {
if (id == R.id.btn_save) {
if (!saveString()) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
index 21372f5c6b..b752ef2ee6 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
@@ -289,7 +289,7 @@ public void onClick (View view) {
targetRestrict.isMember = TD.isMember(args.member.status);
if (targetRestrict.isMember || args.senderId.getConstructor() == TdApi.MessageSenderChat.CONSTRUCTOR) {
- showOptions(Lang.getStringBold(R.string.QUnblockX, tdlib.senderName(args.senderId)), new int[] {R.id.btn_blockSender, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RemoveRestrictions), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getStringBold(R.string.QUnblockX, tdlib.senderName(args.senderId)), new int[] {R.id.btn_blockSender, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RemoveRestrictions), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_blockSender) {
unblockRunnable.run();
}
@@ -313,7 +313,7 @@ public void onClick (View view) {
);
}
} else if (viewId == R.id.btn_dismissAdmin) {
- showOptions(null, new int[] {R.id.btn_dismissAdmin, R.id.btn_cancel}, new String[] {Lang.getString(R.string.DismissAdmin), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(null, new int[] {R.id.btn_dismissAdmin, R.id.btn_cancel}, new String[] {Lang.getString(R.string.DismissAdmin), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_dismissAdmin && !isDoneInProgress()) {
Td.setAllAdministratorRights(targetAdmin.rights, false);
updateValues();
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditSessionController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditSessionController.java
index 0b8dd93695..018027425c 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EditSessionController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditSessionController.java
@@ -106,7 +106,7 @@ public void onClick (View v) {
if (session.isCurrent) {
navigateTo(new SettingsLogOutController(context, tdlib));
} else {
- showOptions(null, new int[] {R.id.btn_terminateSession, R.id.btn_cancel}, new String[] {Lang.getString(session.isPasswordPending ? R.string.TerminateIncompleteSession : R.string.TerminateSession), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_dangerous_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(null, new int[] {R.id.btn_terminateSession, R.id.btn_cancel}, new String[] {Lang.getString(session.isPasswordPending ? R.string.TerminateIncompleteSession : R.string.TerminateSession), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_dangerous_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_terminateSession) {
navigateBack();
getArgumentsStrict().sessionTerminationListener.run();
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditUsernameController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditUsernameController.java
index fc72e09d8c..f2a833b55b 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EditUsernameController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditUsernameController.java
@@ -192,11 +192,11 @@ public void onClick (View v) {
return;
}
String publicLink = tdlib.tMeHost() + tdlib.chatUsername(chat.getChatId());
- showOptions(publicLink, new int[] {R.id.btn_delete, R.id.btn_openChat}, new String[] {Lang.getString(R.string.ChatLinkRemove), Lang.getString(R.string.ChatLinkView)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_visibility_24}, (itemView, id) -> {
+ showOptions(publicLink, new int[] {R.id.btn_delete, R.id.btn_openChat}, new String[] {Lang.getString(R.string.ChatLinkRemove), Lang.getString(R.string.ChatLinkView)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_visibility_24}, (itemView, id) -> {
if (id == R.id.btn_openChat) {
tdlib.ui().openChat(this, chat.getChatId(), new TdlibUi.ChatOpenParameters().keepStack());
} else if (id == R.id.btn_delete) {
- showOptions(Lang.getStringBold(R.string.ChatLinkRemoveAlert, tdlib.chatTitle(chat.getChatId()), publicLink), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ChatLinkRemove), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (resultItemView, confirmId) -> {
+ showOptions(Lang.getStringBold(R.string.ChatLinkRemoveAlert, tdlib.chatTitle(chat.getChatId()), publicLink), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ChatLinkRemove), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (resultItemView, confirmId) -> {
if (confirmId == R.id.btn_delete && !isInProgress()) {
setInProgress(true);
tdlib.client().send(new TdApi.SetSupergroupUsername(ChatId.toSupergroupId(chat.getChatId()), null), result -> {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EmojiMediaListController.java b/app/src/main/java/org/thunderdog/challegram/ui/EmojiMediaListController.java
index 50ee8d423a..bd1a663f00 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EmojiMediaListController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EmojiMediaListController.java
@@ -383,7 +383,7 @@ public void onGifLongPressed (View view, TdApi.Animation animation) {
private void removeGif (final TdApi.Animation animation) {
ViewController> c = context().navigation().getCurrentStackItem();
if (c != null) {
- c.showOptions(Lang.getString(R.string.RemoveGifConfirm), new int[]{R.id.btn_deleteGif, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[]{ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ c.showOptions(Lang.getString(R.string.RemoveGifConfirm), new int[]{R.id.btn_deleteGif, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Delete), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_deleteGif) {
gifsAdapter.removeSavedGif(animation.animation.id);
if (gifsAdapter.getItemCount() == 0) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java b/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java
index f51372dca1..e6b8e2acbe 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java
@@ -1386,11 +1386,11 @@ public void onMenuStickerPreviewClick (View v, ViewController> context, @NonNu
Lang.getString(R.string.SetEmojiAsStatusTimed2Days),
Lang.getString(R.string.SetEmojiAsStatusTimedCustom)
}, new int[] {
- ViewController.OPTION_COLOR_NORMAL,
- ViewController.OPTION_COLOR_NORMAL,
- ViewController.OPTION_COLOR_NORMAL,
- ViewController.OPTION_COLOR_NORMAL,
- ViewController.OPTION_COLOR_NORMAL,
+ OptionColor.NORMAL,
+ OptionColor.NORMAL,
+ OptionColor.NORMAL,
+ OptionColor.NORMAL,
+ OptionColor.NORMAL,
}, new int[] {
R.drawable.baseline_access_time_24,
R.drawable.baseline_access_time_24,
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/MainController.java b/app/src/main/java/org/thunderdog/challegram/ui/MainController.java
index 356e768d1f..2adb7d3b60 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/MainController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/MainController.java
@@ -1068,17 +1068,17 @@ private boolean showLanguageSuggestion () {
ids.append(R.id.btn_done);
strings.append(TD.findOrdinary(cloudStrings, Lang.getResourceEntryName(R.string.language_continueInLanguage), () -> Lang.getString(R.string.language_continueInLanguage)));
- colors.append(OPTION_COLOR_BLUE);
+ colors.append(OptionColor.BLUE);
icons.append(R.drawable.baseline_check_24);
ids.append(R.id.btn_cancel);
icons.append(R.drawable.baseline_cancel_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
strings.append(R.string.Cancel);
ids.append(R.id.btn_languageSettings);
strings.append(R.string.MoreLanguages);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_language_24);
CharSequence text = Strings.buildMarkdown(this, TD.findOrdinary(cloudStrings, Lang.getResourceEntryName(R.string.language_continueInLanguagePopupText), () -> Lang.getString(R.string.language_continueInLanguagePopupText)), null);
@@ -1116,7 +1116,7 @@ private boolean showEmojiUpdateSuggestion () {
tdlib.ui().postDelayed(() -> {
if (!isFocused() || isDestroyed() || context.isPasscodeShowing())
return;
- showOptions(Lang.getStringBold(R.string.EmojiSetUpdated, emojiPack.displayName), new int[] {R.id.btn_downloadFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.EmojiSetUpdate), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_BLUE, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_sync_24, R.drawable.baseline_cancel_24}, (v, id) -> {
+ showOptions(Lang.getStringBold(R.string.EmojiSetUpdated, emojiPack.displayName), new int[] {R.id.btn_downloadFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.EmojiSetUpdate), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.BLUE, OptionColor.NORMAL}, new int[] {R.drawable.baseline_sync_24, R.drawable.baseline_cancel_24}, (v, id) -> {
if (id == R.id.btn_downloadFile) {
SettingsCloudEmojiController c = new SettingsCloudEmojiController(context, tdlib);
c.setArguments(new SettingsCloudController.Args<>(emojiPack));
@@ -2047,27 +2047,27 @@ private void showChatListOptions (@Nullable CharSequence title, TdApi.ChatList c
options.info(title);
}
if (isFolder) {
- options.item(new OptionItem(R.id.btn_editFolder, Lang.getString(R.string.EditFolder), OPTION_COLOR_NORMAL, R.drawable.baseline_edit_24));
+ options.item(new OptionItem(R.id.btn_editFolder, Lang.getString(R.string.EditFolder), OptionColor.NORMAL, R.drawable.baseline_edit_24));
int chatFolderStyle = tdlib.settings().chatFolderStyle();
if (chatFolderStyle == ChatFolderStyle.ICON_ONLY || chatFolderStyle == ChatFolderStyle.LABEL_AND_ICON) {
- options.item(new OptionItem(R.id.btn_changeFolderIcon, Lang.getString(R.string.ChatFolderChangeIcon), OPTION_COLOR_NORMAL, R.drawable.baseline_image_24));
+ options.item(new OptionItem(R.id.btn_changeFolderIcon, Lang.getString(R.string.ChatFolderChangeIcon), OptionColor.NORMAL, R.drawable.baseline_image_24));
}
- options.item(new OptionItem(R.id.btn_folderIncludeChats, Lang.getString(R.string.ChatFolderAddChats), OPTION_COLOR_NORMAL, R.drawable.baseline_add_24));
+ options.item(new OptionItem(R.id.btn_folderIncludeChats, Lang.getString(R.string.ChatFolderAddChats), OptionColor.NORMAL, R.drawable.baseline_add_24));
}
if (isMain) {
if (!Config.RESTRICT_HIDING_MAIN_LIST) {
- options.item(new OptionItem(R.id.btn_hideFolder, Lang.getString(R.string.HideAllChats), OPTION_COLOR_NORMAL, R.drawable.baseline_eye_off_24));
+ options.item(new OptionItem(R.id.btn_hideFolder, Lang.getString(R.string.HideAllChats), OptionColor.NORMAL, R.drawable.baseline_eye_off_24));
}
} else if (isFolder || isArchive) {
- options.item(new OptionItem(R.id.btn_hideFolder, Lang.getString(R.string.HideFolder), OPTION_COLOR_NORMAL, R.drawable.baseline_eye_off_24));
+ options.item(new OptionItem(R.id.btn_hideFolder, Lang.getString(R.string.HideFolder), OptionColor.NORMAL, R.drawable.baseline_eye_off_24));
}
if (isFolder) {
- options.item(new OptionItem(R.id.btn_removeFolder, Lang.getString(R.string.RemoveFolder), OPTION_COLOR_RED, R.drawable.baseline_delete_24));
+ options.item(new OptionItem(R.id.btn_removeFolder, Lang.getString(R.string.RemoveFolder), OptionColor.RED, R.drawable.baseline_delete_24));
}
if (options.itemCount() > 0) {
options.item(OptionItem.SEPARATOR);
}
- options.item(new OptionItem(R.id.btn_chatFolders, Lang.getString(R.string.EditFolders), OPTION_COLOR_NORMAL, R.drawable.baseline_edit_folders_24));
+ options.item(new OptionItem(R.id.btn_chatFolders, Lang.getString(R.string.EditFolders), OptionColor.NORMAL, R.drawable.baseline_edit_folders_24));
showOptions(options.build(), (v, id) -> {
if (id == R.id.btn_editFolder) {
tdlib.send(new TdApi.GetChatFolder(chatFolderId), (chatFolder, error) -> runOnUiThreadOptional(() -> {
@@ -2118,7 +2118,7 @@ private void showChatListOptions (@Nullable CharSequence title, TdApi.ChatList c
});
});
} else if (id == R.id.btn_removeFolder) {
- showConfirm(Lang.getString(R.string.RemoveFolderConfirm), Lang.getString(R.string.Remove), R.drawable.baseline_delete_24, OPTION_COLOR_RED, () -> {
+ showConfirm(Lang.getString(R.string.RemoveFolderConfirm), Lang.getString(R.string.Remove), R.drawable.baseline_delete_24, OptionColor.RED, () -> {
tdlib.send(new TdApi.DeleteChatFolder(chatFolderId, null), tdlib.typedOkHandler());
});
} else if (id == R.id.btn_chatFolders) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsController.java b/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsController.java
index ad66af7025..e5b85b19e3 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsController.java
@@ -205,7 +205,7 @@ public void onBindViewHolder (@NonNull OptionHolder holder, int position) {
if (item.icon != 0) {
Drawable drawable = Drawables.get(context.getResources(), item.icon);
if (drawable != null) {
- final int drawableColorId = item.color == ViewController.OPTION_COLOR_NORMAL ? ColorId.icon : colorId;
+ final int drawableColorId = item.color == OptionColor.NORMAL ? ColorId.icon : colorId;
drawable.setColorFilter(Paints.getColorFilter(Theme.getColor(drawableColorId)));
if (themeProvider != null) {
themeProvider.addThemeFilterListener(drawable, drawableColorId);
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java b/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java
index 56b3564d54..b3a9df5fdd 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java
@@ -2105,7 +2105,7 @@ public void onMoreItemPressed (int id) {
if (user == null) {
return;
}
- showOptions(TD.getUserName(user) + ", " + Strings.formatPhone(user.phoneNumber), new int[] {R.id.btn_shareMyContact, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ShareMyContactInfo), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_BLUE, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_contact_phone_24, R.drawable.baseline_cancel_24}, (itemView, id1) -> {
+ showOptions(TD.getUserName(user) + ", " + Strings.formatPhone(user.phoneNumber), new int[] {R.id.btn_shareMyContact, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ShareMyContactInfo), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.BLUE, OptionColor.NORMAL}, new int[] {R.drawable.baseline_contact_phone_24, R.drawable.baseline_cancel_24}, (itemView, id1) -> {
if (id1 == R.id.btn_shareMyContact) {
sendContact(tdlib.myUser(), true, Td.newSendOptions());
}
@@ -3683,7 +3683,7 @@ public void onMenuItemPressed (int id, View view) {
}
} else if (id == R.id.menu_btn_unpinAll) {
if (selectedMessageIds != null && selectedMessageIds.size() > 0) {
- showOptions(Lang.pluralBold(R.string.UnpinXMessages, selectedMessageIds.size()), new int[] {R.id.btn_unpinAll, R.id.btn_cancel}, new String[] {Lang.getString(R.string.Unpin), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.deproko_baseline_pin_undo_24, R.drawable.baseline_cancel_24}, (itemView, viewId) -> {
+ showOptions(Lang.pluralBold(R.string.UnpinXMessages, selectedMessageIds.size()), new int[] {R.id.btn_unpinAll, R.id.btn_cancel}, new String[] {Lang.getString(R.string.Unpin), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.deproko_baseline_pin_undo_24, R.drawable.baseline_cancel_24}, (itemView, viewId) -> {
if (viewId == R.id.btn_unpinAll) {
final int size = selectedMessageIds.size();
for (int i = 0; i < size; i++) {
@@ -3728,7 +3728,7 @@ public void onMenuItemPressed (int id, View view) {
}
}
if (count > 0) {
- showOptions(new int[] {R.id.btn_messageResend, R.id.btn_cancel}, new String[] {Lang.plural(R.string.ResendXMessages, count), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_BLUE, OPTION_COLOR_NORMAL}, (v, optionId) -> {
+ showOptions(new int[] {R.id.btn_messageResend, R.id.btn_cancel}, new String[] {Lang.plural(R.string.ResendXMessages, count), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.BLUE, OptionColor.NORMAL}, (v, optionId) -> {
if (optionId == R.id.btn_messageResend) {
resendSelectedMessages();
finishSelectMode(-1);
@@ -4560,7 +4560,7 @@ private void patchReadReceiptsOptions (PopupLayout layout, MessageContext messag
FrameLayout frameLayout = new FrameLayoutFix(layout.getContext());
frameLayout.setLayoutParams(new LinearLayout.LayoutParams(0, Screen.dp(54f), 1f));
- TextView receiptText = OptionsLayout.genOptionView(layout.getContext(), R.id.more_btn_openReadReceipts, Lang.getString(R.string.LoadingMessageSeen), ViewController.OPTION_COLOR_NORMAL, 0, null, getThemeListeners(), null);
+ TextView receiptText = OptionsLayout.genOptionView(layout.getContext(), R.id.more_btn_openReadReceipts, Lang.getString(R.string.LoadingMessageSeen), OptionColor.NORMAL, 0, null, getThemeListeners(), null);
TripleAvatarView tav = new TripleAvatarView(layout.getContext());
@@ -4591,7 +4591,7 @@ private void patchReadReceiptsOptions (PopupLayout layout, MessageContext messag
optionsLayout.addView(receiptWrap, 2);
- TextView subtitleView = OptionsLayout.genOptionView(layout.getContext(), 0, null, OPTION_COLOR_NORMAL, 0, null, getThemeListeners(), null);
+ TextView subtitleView = OptionsLayout.genOptionView(layout.getContext(), 0, null, OptionColor.NORMAL, 0, null, getThemeListeners(), null);
BoolAnimator isSubtitleVisible = new BoolAnimator(0, (id, factor, fraction, callee) -> {
receiptText.setTranslationY(-Screen.dp(10f) * factor);
@@ -5313,7 +5313,7 @@ private OptionDelegate newMessageOptionDelegate (final TGMessage selectedMessage
TdApi.Message message = selectedMessage.getMessage();
TdApi.Poll poll = ((TdApi.MessagePoll) message.content).poll;
boolean isQuiz = poll.type.getConstructor() == TdApi.PollTypeQuiz.CONSTRUCTOR;
- showOptions(Lang.getStringBold(isQuiz ? R.string.StopQuizWarn : R.string.StopPollWarn, poll.question), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(isQuiz ? R.string.StopQuiz : R.string.StopPoll), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_poll_24, R.drawable.baseline_cancel_24}, (optionItemView, optionId) -> {
+ showOptions(Lang.getStringBold(isQuiz ? R.string.StopQuizWarn : R.string.StopPollWarn, poll.question), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(isQuiz ? R.string.StopQuiz : R.string.StopPoll), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_poll_24, R.drawable.baseline_cancel_24}, (optionItemView, optionId) -> {
if (optionId == R.id.btn_done) {
tdlib.client().send(new TdApi.StopPoll(message.chatId, message.id, message.replyMarkup), tdlib.okHandler());
}
@@ -7520,7 +7520,7 @@ private void dismissPinnedMessage () {
.item(canPinAnyMessage(false) ? new OptionItem.Builder()
.id(R.id.btn_unpinMessage)
.name(Lang.getString(pinnedCount == 1 ? R.string.UnpinMessage : R.string.UnpinMessagesConfirm))
- .color(OPTION_COLOR_RED)
+ .color(OptionColor.RED)
.icon(R.drawable.deproko_baseline_pin_undo_24)
.build() : null
)
@@ -7724,7 +7724,7 @@ private void checkActionBar () {
Lang.getString(R.string.ReportLocationTitle),
Lang.getStringBold(R.string.ReportLocationDesc, location.address)
);
- showOptions(title, new int[] {R.id.btn_reportChat, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ReportLocationAction), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_report_24, R.drawable.baseline_cancel_24}, (v, optionId) -> {
+ showOptions(title, new int[] {R.id.btn_reportChat, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ReportLocationAction), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_report_24, R.drawable.baseline_cancel_24}, (v, optionId) -> {
if (optionId == R.id.btn_reportChat) {
tdlib.client().send(new TdApi.ReportChat(chatId, null, new TdApi.ReportReasonUnrelatedLocation(), null), tdlib.okHandler());
dismissActionBar();
@@ -7798,7 +7798,7 @@ public boolean onSenderPick (ContactsController context, View view, TdApi.Messag
items.add(new TopBarView.Item(R.id.btn_shareMyContact, R.string.SharePhoneNumber, v -> {
TdApi.User user = tdlib.myUser();
if (user != null) {
- showOptions(TD.getUserName(user) + ", " + Strings.formatPhone(user.phoneNumber), new int[] {R.id.btn_shareMyContact, R.id.btn_cancel}, new String[] {Lang.getString(R.string.SharePhoneNumberAction), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_BLUE, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_contact_phone_24, R.drawable.baseline_cancel_24}, (itemView, id1) -> {
+ showOptions(TD.getUserName(user) + ", " + Strings.formatPhone(user.phoneNumber), new int[] {R.id.btn_shareMyContact, R.id.btn_cancel}, new String[] {Lang.getString(R.string.SharePhoneNumberAction), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.BLUE, OptionColor.NORMAL}, new int[] {R.drawable.baseline_contact_phone_24, R.drawable.baseline_cancel_24}, (itemView, id1) -> {
if (id1 == R.id.btn_shareMyContact) {
tdlib.client().send(new TdApi.SharePhoneNumber(tdlib.chatUserId(chatId)), tdlib.okHandler());
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
index bd882d8931..88e90da7ed 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
@@ -1031,7 +1031,7 @@ private void suggestInvitingUser (long userId, int importerCount) {
} else {
msg = Lang.getStringBold(R.string.SuggestInvitingUser, getFirstName());
}
- showOptions(msg, new int[] {R.id.btn_invite, R.id.btn_cancel}, new String[] {Lang.getString(R.string.Invite), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_BLUE, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_person_add_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(msg, new int[] {R.id.btn_invite, R.id.btn_cancel}, new String[] {Lang.getString(R.string.Invite), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.BLUE, OptionColor.NORMAL}, new int[] {R.drawable.baseline_person_add_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_invite) {
tdlib.cache().getInviteText(text -> {
Intents.sendSms(getPhoneNumber(), text.text);
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/PlaybackController.java b/app/src/main/java/org/thunderdog/challegram/ui/PlaybackController.java
index 352ef1dcda..e6c85e93c3 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/PlaybackController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/PlaybackController.java
@@ -930,7 +930,7 @@ public void onTrackListClose (Tdlib tdlib, long playListChatId, long playListMax
private void removeTrack (final InlineResultCommon common) {
if (currentItem != null) {
- showOptions(Lang.getStringBold(R.string.PlayListRemoveTrack, common.getTrackTitle() + " – " + common.getTrackSubtitle()), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.PlayListRemove), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getStringBold(R.string.PlayListRemoveTrack, common.getTrackTitle() + " – " + common.getTrackSubtitle()), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.PlayListRemove), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_delete) {
tdlib.context().player().removeTrack(common.getMessage(), true);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java b/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
index 4348e24270..522455732b 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
@@ -702,7 +702,7 @@ public void onMoreItemPressed (int id) {
final boolean needBlock = !tdlib.chatFullyBlocked(chat.id);
final boolean isBot = tdlib.isBotChat(chat.id);
if (needBlock) {
- showOptions(Lang.getStringBold(isBot ? R.string.BlockBotConfirm : R.string.BlockUserConfirm, tdlib.chatTitle(chat.id)), new int[] {R.id.btn_blockSender, R.id.btn_cancel}, new String[] {Lang.getString(isBot ? R.string.BlockBot : R.string.BlockContact), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_block_24, R.drawable.baseline_cancel_24}, (itemView, id1) -> {
+ showOptions(Lang.getStringBold(isBot ? R.string.BlockBotConfirm : R.string.BlockUserConfirm, tdlib.chatTitle(chat.id)), new int[] {R.id.btn_blockSender, R.id.btn_cancel}, new String[] {Lang.getString(isBot ? R.string.BlockBot : R.string.BlockContact), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_block_24, R.drawable.baseline_cancel_24}, (itemView, id1) -> {
if (!isDestroyed() && id1 == R.id.btn_blockSender) {
tdlib.blockSender(tdlib.sender(chat.id), new TdApi.BlockListMain(), result -> {
if (TD.isOk(result)) {
@@ -3341,10 +3341,10 @@ public void onClick (@NonNull View widget) {
icons.append(R.drawable.baseline_group_add_24);
strings.append(R.string.ChannelGroupNew);
- showOptions(info, ids.get(), strings.get(), size == 3 ? new int[]{OPTION_COLOR_RED, OPTION_COLOR_NORMAL, OPTION_COLOR_NORMAL} : null, icons.get(), (v, id) -> {
+ showOptions(info, ids.get(), strings.get(), size == 3 ? new int[]{OptionColor.RED, OptionColor.NORMAL, OptionColor.NORMAL} : null, icons.get(), (v, id) -> {
if (id == R.id.btn_delete) {
if (linkedChat != null) {
- showConfirm(Lang.getString(R.string.UnlinkGroupConfirm, linkedChatCreator, tdlib.chatTitle(linkedChat)), Lang.getString(R.string.UnlinkGroupDone), R.drawable.baseline_remove_circle_24, OPTION_COLOR_RED, () ->
+ showConfirm(Lang.getString(R.string.UnlinkGroupConfirm, linkedChatCreator, tdlib.chatTitle(linkedChat)), Lang.getString(R.string.UnlinkGroupDone), R.drawable.baseline_remove_circle_24, OptionColor.RED, () ->
tdlib.client().send(new TdApi.SetChatDiscussionGroup(chat.id, 0), tdlib.okHandler())
);
}
@@ -3386,9 +3386,9 @@ public boolean forceSupergroupChat () {
if (linkedChat == null)
return;
CharSequence info = Lang.getString(R.string.GroupChannelInfo, linkedChatCreator, tdlib.chatTitle(linkedChat));
- showOptions(info, new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.GroupChannelUnlink), Lang.getString(R.string.Cancel)}, new int[]{OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (v, id) -> {
+ showOptions(info, new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(R.string.GroupChannelUnlink), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL}, new int[]{R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (v, id) -> {
if (id == R.id.btn_delete) {
- showConfirm(Lang.getString(R.string.UnlinkChannelConfirm, linkedChatCreator, tdlib.chatTitle(linkedChat)), Lang.getString(R.string.UnlinkChannelDone), R.drawable.baseline_remove_circle_24, OPTION_COLOR_RED, () ->
+ showConfirm(Lang.getString(R.string.UnlinkChannelConfirm, linkedChatCreator, tdlib.chatTitle(linkedChat)), Lang.getString(R.string.UnlinkChannelDone), R.drawable.baseline_remove_circle_24, OptionColor.RED, () ->
tdlib.client().send(new TdApi.SetChatDiscussionGroup(0, chat.id), tdlib.okHandler())
);
}
@@ -3431,7 +3431,7 @@ private void linkGroup (ViewController> context, long selectedChatId, boolean
public void onClick (@NonNull View widget) {
tdlib.ui().openChat(ProfileController.this, argIndex == 0 ? selectedChatId : currentChatId, new TdlibUi.ChatOpenParameters().keepStack().removeDuplicates());
}
- }, tdlib.chatTitle(selectedChatId), tdlib.chatTitle(currentChatId)), Lang.getString(R.string.LinkGroupConfirmOverrideDone), R.drawable.baseline_remove_circle_24, OPTION_COLOR_RED, () -> {
+ }, tdlib.chatTitle(selectedChatId), tdlib.chatTitle(currentChatId)), Lang.getString(R.string.LinkGroupConfirmOverrideDone), R.drawable.baseline_remove_circle_24, OptionColor.RED, () -> {
act.runWithLong(selectedChatId);
});
} else {
@@ -3465,7 +3465,7 @@ public void onClick (@NonNull View widget) {
if (selectedFullInfo != null && !selectedFullInfo.isAllHistoryAvailable) {
b.append("\n\n").append(Lang.getMarkdownString(this, R.string.LinkGroupConfirmWarnPreHistory));
}
- showOptions(b, new int[]{R.id.btn_done, R.id.btn_cancel}, new String[]{Lang.getString(R.string.LinkGroupConfirmDone), Lang.getString(R.string.Cancel)}, new int[]{OPTION_COLOR_BLUE, OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_link_24, R.drawable.baseline_cancel_24}, (optionView, optionId) -> {
+ showOptions(b, new int[]{R.id.btn_done, R.id.btn_cancel}, new String[]{Lang.getString(R.string.LinkGroupConfirmDone), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.BLUE, OptionColor.NORMAL}, new int[]{R.drawable.baseline_link_24, R.drawable.baseline_cancel_24}, (optionView, optionId) -> {
if (optionId == R.id.btn_done) {
doneAct.run();
}
@@ -4315,7 +4315,7 @@ private void addMember (final int mode, final ContactsController context, final
if (needConfirm) {
showOptions(new Options.Builder()
.info(Lang.getStringBold(isChannel() ? R.string.QAddXToChannel : R.string.AddToTheGroup, memberName))
- .item(new OptionItem(R.id.btn_addMember, Lang.getString(R.string.AddMember), OPTION_COLOR_NORMAL, R.drawable.baseline_person_add_24))
+ .item(new OptionItem(R.id.btn_addMember, Lang.getString(R.string.AddMember), OptionColor.NORMAL, R.drawable.baseline_person_add_24))
.cancelItem()
.build(),
(itemView, id) -> {
@@ -4372,7 +4372,7 @@ private void addMember (final int mode, final ContactsController context, final
return;
}
if (isBasicGroup() || isChannel()) {
- showOptions(Lang.getStringBold(isBasicGroup() ? R.string.MemberCannotJoinGroup : R.string.MemberCannotJoinChannel, memberName), new int[] {R.id.btn_blockSender, R.id.btn_cancel}, new String[]{Lang.getString(R.string.BlockUser), Lang.getString(R.string.Cancel)}, new int[]{OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getStringBold(isBasicGroup() ? R.string.MemberCannotJoinGroup : R.string.MemberCannotJoinChannel, memberName), new int[] {R.id.btn_blockSender, R.id.btn_cancel}, new String[]{Lang.getString(R.string.BlockUser), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL}, new int[]{R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_blockSender) {
tdlib.setChatMemberStatus(chat.id, member.memberId, new TdApi.ChatMemberStatusBanned(), member.status, (success, error) -> {
if (success) {
@@ -4493,14 +4493,14 @@ private void destroyChat () {
showOptions(msg,
new int[] {R.id.btn_destroyChat, R.id.btn_cancel},
new String[] {Lang.getString(isChannel ? R.string.DestroyChannel : R.string.DestroyGroup), Lang.getString(R.string.Cancel)},
- new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL},
+ new int[] {OptionColor.RED, OptionColor.NORMAL},
new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24},
(itemView, id) -> {
if (id == R.id.btn_destroyChat) {
showOptions(Lang.getString(isChannel ? R.string.DestroyChannelHint : R.string.DestroyGroupHint),
new int[] {R.id.btn_destroyChat, R.id.btn_cancel},
new String[] {Lang.getString(isChannel ? R.string.DestroyChannel : R.string.DestroyGroup), Lang.getString(R.string.Cancel)},
- new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL},
+ new int[] {OptionColor.RED, OptionColor.NORMAL},
new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (resultItemView, resultId) -> {
if (resultId == R.id.btn_destroyChat) {
tdlib.client().send(new TdApi.DeleteChat(getChatId()), tdlib.okHandler());
@@ -4665,8 +4665,8 @@ public void onClick (View v) {
Lang.getString(R.string.Cancel)
},
new int[] {
- OPTION_COLOR_RED,
- OPTION_COLOR_NORMAL
+ OptionColor.RED,
+ OptionColor.NORMAL
},
new int[] {
R.drawable.baseline_bullhorn_24,
@@ -4690,8 +4690,8 @@ private void convertToBroadcastGroup (View view) {
Lang.getString(R.string.Cancel)
},
new int[] {
- OPTION_COLOR_RED,
- OPTION_COLOR_NORMAL
+ OptionColor.RED,
+ OptionColor.NORMAL
},
new int[] {
R.drawable.baseline_check_24,
@@ -4827,29 +4827,29 @@ private void openInviteLinkMenu () {
ids.append(R.id.btn_manageInviteLinks);
strings.append(R.string.InviteLinkManage);
icons.append(R.drawable.baseline_add_link_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
if (chat.pendingJoinRequests != null && chat.pendingJoinRequests.totalCount > 0) {
ids.append(R.id.btn_manageJoinRequests);
strings.append(R.string.InviteLinkRequests);
icons.append(R.drawable.baseline_pending_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
}
ids.append(R.id.btn_copyLink);
strings.append(R.string.InviteLinkCopy);
icons.append(R.drawable.baseline_content_copy_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
ids.append(R.id.btn_shareLink);
strings.append(R.string.ShareLink);
icons.append(R.drawable.baseline_forward_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_link_off_24);
ids.append(R.id.btn_revokeLink);
strings.append(R.string.RevokeLink);
- colors.append(OPTION_COLOR_RED);
+ colors.append(OptionColor.RED);
CharSequence info = TD.makeClickable(Lang.getString(R.string.CreatedByXOnDate, ((target, argStart, argEnd, spanIndex, needFakeBold) -> spanIndex == 0 ? Lang.newUserSpan(new TdlibContext(context, tdlib), link.creatorUserId) : null), tdlib.cache().userName(link.creatorUserId), Lang.getRelativeTimestamp(link.date, TimeUnit.SECONDS)));
Lang.SpanCreator firstBoldCreator = (target, argStart, argEnd, spanIndex, needFakeBold) -> spanIndex == 0 ? Lang.newBoldSpan(needFakeBold) : null;
@@ -4866,7 +4866,7 @@ private void openInviteLinkMenu () {
c.setArguments(new ShareController.Args(text).setShare(exportText, null));
c.show();
} else if (id == R.id.btn_revokeLink) {
- showOptions(Lang.getString(tdlib.isChannel(chat.id) ? R.string.AreYouSureRevokeInviteLinkChannel : R.string.AreYouSureRevokeInviteLinkGroup), new int[] {R.id.btn_revokeLink, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RevokeLink), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_link_off_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
+ showOptions(Lang.getString(tdlib.isChannel(chat.id) ? R.string.AreYouSureRevokeInviteLinkChannel : R.string.AreYouSureRevokeInviteLinkGroup), new int[] {R.id.btn_revokeLink, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RevokeLink), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_link_off_24, R.drawable.baseline_cancel_24}, (itemView2, id2) -> {
if (id2 == R.id.btn_revokeLink) {
tdlib.client().send(new TdApi.RevokeChatInviteLink(chat.id, link.inviteLink), result -> {
if (result.getConstructor() == TdApi.ChatInviteLinks.CONSTRUCTOR) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ReactionsPickerController.java b/app/src/main/java/org/thunderdog/challegram/ui/ReactionsPickerController.java
index 493369c4b2..494be902e4 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ReactionsPickerController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ReactionsPickerController.java
@@ -818,7 +818,7 @@ private boolean onEmojiHeaderLongClick (View v) {
showOptions(null, new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {
Lang.getString(R.string.ClearRecentReactionsAction),
Lang.getString(R.string.Cancel)
- }, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_auto_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ }, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_auto_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_done) {
TGStickerSetInfo info = reactionsController.getStickerSetBySectionIndex(1);
if (info != null) {
@@ -835,9 +835,9 @@ private boolean onEmojiHeaderLongClick (View v) {
}
private void removeStickerSet (final TGStickerSetInfo info) {
- showOptions(null, new int[] {R.id.btn_copyLink, R.id.btn_archive, R.id.more_btn_delete}, new String[] {Lang.getString(R.string.CopyLink), Lang.getString(R.string.ArchivePack), Lang.getString(R.string.DeletePack)}, new int[] {ViewController.OPTION_COLOR_NORMAL, ViewController.OPTION_COLOR_NORMAL, ViewController.OPTION_COLOR_RED}, new int[] {R.drawable.baseline_link_24, R.drawable.baseline_archive_24, R.drawable.baseline_delete_24}, (itemView, id) -> {
+ showOptions(null, new int[] {R.id.btn_copyLink, R.id.btn_archive, R.id.more_btn_delete}, new String[] {Lang.getString(R.string.CopyLink), Lang.getString(R.string.ArchivePack), Lang.getString(R.string.DeletePack)}, new int[] {OptionColor.NORMAL, OptionColor.NORMAL, OptionColor.RED}, new int[] {R.drawable.baseline_link_24, R.drawable.baseline_archive_24, R.drawable.baseline_delete_24}, (itemView, id) -> {
if (id == R.id.more_btn_delete) {
- showOptions(Lang.getStringBold(R.string.RemoveEmojiSet, info.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RemoveStickerSetAction), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (resultItemView, resultId) -> {
+ showOptions(Lang.getStringBold(R.string.RemoveEmojiSet, info.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RemoveStickerSetAction), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (resultItemView, resultId) -> {
if (resultId == R.id.btn_delete) {
reactionsController.removeStickerSet(info);
tdlib().client().send(new TdApi.ChangeStickerSet(info.getId(), false, false), tdlib().okHandler());
@@ -845,7 +845,7 @@ private void removeStickerSet (final TGStickerSetInfo info) {
return true;
});
} else if (id == R.id.btn_archive) {
- showOptions(Lang.getStringBold(R.string.ArchiveEmojiSet, info.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ArchiveStickerSetAction), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_archive_24, R.drawable.baseline_cancel_24}, (resultItemView, resultId) -> {
+ showOptions(Lang.getStringBold(R.string.ArchiveEmojiSet, info.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ArchiveStickerSetAction), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_archive_24, R.drawable.baseline_cancel_24}, (resultItemView, resultId) -> {
if (resultId == R.id.btn_delete) {
reactionsController.removeStickerSet(info);
tdlib().client().send(new TdApi.ChangeStickerSet(info.getId(), false, true), tdlib().okHandler());
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/Settings2FAController.java b/app/src/main/java/org/thunderdog/challegram/ui/Settings2FAController.java
index 80a46d4252..67fbe8a644 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/Settings2FAController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/Settings2FAController.java
@@ -191,7 +191,7 @@ public void onClick (View v) {
});
} else if (viewId == R.id.btn_abort2FA) {
hideSoftwareKeyboard();
- showOptions(Lang.getString(R.string.AbortPasswordConfirm), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.AbortPassword), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.AbortPasswordConfirm), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.AbortPassword), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_done) {
hideSoftwareKeyboard();
abort2FA();
@@ -214,7 +214,7 @@ public void onClick (View v) {
}
});
} else if (viewId == R.id.btn_disablePassword) {
- showOptions(Strings.buildMarkdown(this, Lang.getString(state.hasPassportData ? R.string.TurnPasswordOffQuestion2 : R.string.TurnPasswordOffQuestion), null), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.DisablePassword), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Strings.buildMarkdown(this, Lang.getString(state.hasPassportData ? R.string.TurnPasswordOffQuestion2 : R.string.TurnPasswordOffQuestion), null), new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {Lang.getString(R.string.DisablePassword), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_remove_circle_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_done) {
state.hasRecoveryEmailAddress = false;
state.recoveryEmailAddressCodeInfo = null;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsBlockedController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsBlockedController.java
index 2e4979750d..6e60a54976 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsBlockedController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsBlockedController.java
@@ -108,7 +108,7 @@ public String getUserPickTitle () {
@Override
public boolean onSenderPick (ContactsController context, View view, TdApi.MessageSender senderId) {
- showOptions(Lang.getStringBold(senderId.getConstructor() == TdApi.MessageSenderUser.CONSTRUCTOR ? R.string.QBlockUser : R.string.QBlockChat, Strings.wrapRtlLtr(tdlib.senderName(senderId))), new int[] {R.id.btn_blockSender, R.id.btn_cancel}, new String[] {Lang.getString(senderId.getConstructor() == TdApi.MessageSenderUser.CONSTRUCTOR ? R.string.BlockUserBtn : R.string.BlockChatBtn), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_block_24, R.drawable.baseline_cancel_24});
+ showOptions(Lang.getStringBold(senderId.getConstructor() == TdApi.MessageSenderUser.CONSTRUCTOR ? R.string.QBlockUser : R.string.QBlockChat, Strings.wrapRtlLtr(tdlib.senderName(senderId))), new int[] {R.id.btn_blockSender, R.id.btn_cancel}, new String[] {Lang.getString(senderId.getConstructor() == TdApi.MessageSenderUser.CONSTRUCTOR ? R.string.BlockUserBtn : R.string.BlockChatBtn), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_block_24, R.drawable.baseline_cancel_24});
return false;
}
@@ -129,7 +129,7 @@ public void onFocus () {
}
public void unblockSender (TGUser user) {
- showOptions(Lang.getStringBold(R.string.QUnblockX, tdlib.senderName(user.getSenderId())), new int[]{R.id.btn_unblockSender, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Unblock), Lang.getString(R.string.Cancel)}, new int[]{OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_block_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getStringBold(R.string.QUnblockX, tdlib.senderName(user.getSenderId())), new int[]{R.id.btn_unblockSender, R.id.btn_cancel}, new String[]{Lang.getString(R.string.Unblock), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_block_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_unblockSender) {
tdlib.blockSender(user.getSenderId(), null, tdlib.okHandler(() -> {
runOnUiThreadOptional(() -> {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsBugController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsBugController.java
index a99a8dac01..0597a337ed 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsBugController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsBugController.java
@@ -1525,22 +1525,22 @@ private void viewTdlibLog (View view, final boolean old) {
ids.append(R.id.btn_tdlib_viewLogs);
icons.append(R.drawable.baseline_visibility_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
strings.append(R.string.Open);
ids.append(R.id.btn_tdlib_shareLogs);
icons.append(tdlib == null || tdlib.context().inRecoveryMode() ? R.drawable.baseline_share_24 : R.drawable.baseline_forward_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
strings.append(R.string.Share);
ids.append(R.id.btn_saveFile);
icons.append(R.drawable.baseline_file_download_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
strings.append(R.string.SaveToDownloads);
ids.append(R.id.btn_tdlib_clearLogs);
icons.append(R.drawable.baseline_delete_24);
- colors.append(OPTION_COLOR_RED);
+ colors.append(OptionColor.RED);
strings.append(R.string.Delete);
showOptions(tdlibLogFile.getName() + " (" + Strings.buildSize(logSize[i]) + ")", ids.get(), strings.get(), colors.get(), icons.get(), (itemView, id) -> {
@@ -1572,7 +1572,7 @@ public boolean onLongClick (View v) {
setLogFiles(Log.getLogFiles());
}
if (logFiles != null && !logFiles.isEmpty()) {
- showOptions("Clear " + Strings.buildSize(logFiles.totalSize) + "?", new int[] {R.id.btn_deleteAll, R.id.btn_cancel}, new String[] {"Delete all logs", "Cancel"}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions("Clear " + Strings.buildSize(logFiles.totalSize) + "?", new int[] {R.id.btn_deleteAll, R.id.btn_cancel}, new String[] {"Delete all logs", "Cancel"}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_deleteAll) {
deleteAllFiles();
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsCacheController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsCacheController.java
index b132542ebd..fbad5fa5d1 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsCacheController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsCacheController.java
@@ -413,7 +413,7 @@ public void onClick (View v) {
showAllChats();
} else if (viewId == R.id.btn_paint) {
if (fastStats != null) {
- showOptions(Lang.getString(R.string.PaintsInfo), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getPaintsSize())), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.PaintsInfo), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getPaintsSize())), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_deleteFile) {
Background.instance().post(() -> {
FileUtils.delete(PaintState.getPaintsDir(), true);
@@ -425,7 +425,7 @@ public void onClick (View v) {
}
} else if (viewId == R.id.btn_junk) {
if (fastStats != null) {
- showOptions(Lang.getString(R.string.JunkFilesInfo), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getJunkSize())), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.JunkFilesInfo), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getJunkSize())), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_deleteFile) {
if (!fastStats.deleteJunk())
Log.w("Failed to delete some junk");
@@ -436,7 +436,7 @@ public void onClick (View v) {
}
} else if (viewId == R.id.btn_camera) {
if (fastStats != null) {
- showOptions(Lang.getString(R.string.InAppCameraCacheDeleteConfirm), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getPrivateCameraMediaSize())), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.InAppCameraCacheDeleteConfirm), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getPrivateCameraMediaSize())), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_deleteFile) {
if (!fastStats.deletePrivateCameraMedia())
Log.w("Failed to delete some emoji sets");
@@ -447,7 +447,7 @@ public void onClick (View v) {
}
} else if (viewId == R.id.btn_emoji) {
if (fastStats != null) {
- showOptions(Lang.getString(R.string.EmojiSetsInfo), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getEmojiUnusedSize())), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.EmojiSetsInfo), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getEmojiUnusedSize())), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_deleteFile) {
if (!fastStats.deleteEmoji())
Log.w("Failed to delete some emoji sets");
@@ -458,7 +458,7 @@ public void onClick (View v) {
}
} else if (viewId == R.id.btn_lottie) {
if (fastStats != null) {
- showOptions(Lang.getString(R.string.AnimatedStickersInfo), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getLottieSize())), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.AnimatedStickersInfo), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getLottieSize())), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_deleteFile) {
if (!fastStats.deleteLottieFiles())
Log.w("Failed to delete some emoji sets");
@@ -479,7 +479,7 @@ public void onClick (View v) {
Settings.instance().deleteAllLogs(true, this::reloadFastStats);
}));
} else {
- showOptions(Lang.getString(R.string.AppLogsClear), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getLogsSize())), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.AppLogsClear), new int[] {R.id.btn_deleteFile, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearX, Strings.buildSize(fastStats.getLogsSize())), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_deleteFile) {
Settings.instance().deleteAllLogs(true, this::reloadFastStats);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java
index 32ffb1c3cf..755c8faae5 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java
@@ -1010,13 +1010,13 @@ public void onClick (View v) {
if (!appBuildInfo.getPullRequests().isEmpty()) {
b.info(Lang.plural(R.string.PullRequestsInfo, appBuildInfo.getPullRequests().size()));
}
- b.item(new OptionItem(R.id.btn_sourceCode, Lang.getString(R.string.format_commit, Lang.getString(R.string.ViewSourceCode), appBuildInfo.getCommit()), OPTION_COLOR_NORMAL, R.drawable.baseline_github_24));
+ b.item(new OptionItem(R.id.btn_sourceCode, Lang.getString(R.string.format_commit, Lang.getString(R.string.ViewSourceCode), appBuildInfo.getCommit()), OptionColor.NORMAL, R.drawable.baseline_github_24));
if (appBuildInfo.getTdlibCommitFull() != null) {
- b.item(new OptionItem(R.id.btn_tdlib, Lang.getCharSequence(R.string.format_commit, "TDLib " + Td.tdlibVersion(), Td.tdlibCommitHash()), OPTION_COLOR_NORMAL, R.drawable.baseline_tdlib_24));
+ b.item(new OptionItem(R.id.btn_tdlib, Lang.getCharSequence(R.string.format_commit, "TDLib " + Td.tdlibVersion(), Td.tdlibCommitHash()), OptionColor.NORMAL, R.drawable.baseline_tdlib_24));
}
int i = 0;
for (PullRequest pullRequest : appBuildInfo.getPullRequests()) {
- b.item(new OptionItem(i++, Lang.getString(R.string.format_commit, Lang.getString(R.string.PullRequestCommit, pullRequest.getId()), pullRequest.getCommit()), OPTION_COLOR_NORMAL, R.drawable.templarian_baseline_source_merge_24));
+ b.item(new OptionItem(i++, Lang.getString(R.string.format_commit, Lang.getString(R.string.PullRequestCommit, pullRequest.getId()), pullRequest.getCommit()), OptionColor.NORMAL, R.drawable.templarian_baseline_source_merge_24));
}
showOptions(b.build(), (view, id) -> {
if (id == R.id.btn_sourceCode || id == R.id.btn_tdlib) {
@@ -1080,17 +1080,17 @@ private void showSuggestionPopup (TdApi.SuggestedAction suggestedAction) {
ids.append(R.id.btn_changePhoneNumber);
titles.append(R.string.ReminderActionChangePhoneNumber);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_edit_24);
ids.append(R.id.btn_cancel);
titles.append(Lang.getString(R.string.ReminderCheckPhoneNumberHide, originalPhoneNumber));
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_check_24);
ids.append(R.id.btn_info);
titles.append(R.string.ReminderActionLearnMore);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_info_24);
break;
@@ -1100,12 +1100,12 @@ private void showSuggestionPopup (TdApi.SuggestedAction suggestedAction) {
ids.append(R.id.btn_2fa);
titles.append(R.string.ReminderActionVerifyPassword);
- colors.append(OPTION_COLOR_BLUE);
+ colors.append(OptionColor.BLUE);
icons.append(R.drawable.mrgrigri_baseline_textbox_password_24);
ids.append(R.id.btn_cancel);
titles.append(R.string.ReminderCheckTfaPasswordHide);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_cancel_24);
break;
@@ -1197,13 +1197,13 @@ private void showBuildOptions (boolean allowDebug) {
ids.append(R.id.btn_copyText);
strings.append(R.string.CopyVersion);
icons.append(R.drawable.baseline_content_copy_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
if (!Config.SHOW_COPY_REPORT_DETAILS_IN_SETTINGS) {
ids.append(R.id.btn_copyDebug);
strings.append(R.string.CopyReportData);
icons.append(R.drawable.baseline_bug_report_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
}
boolean notificationError = tdlib.context().getTokenState() == TdlibManager.TokenState.ERROR;
@@ -1211,24 +1211,24 @@ private void showBuildOptions (boolean allowDebug) {
ids.append(R.id.btn_pushService);
strings.append(R.string.PushServices);
icons.append(notificationError ? R.drawable.baseline_sync_problem_24 : R.drawable.baseline_sync_24);
- colors.append(notificationError ? OPTION_COLOR_RED : OPTION_COLOR_NORMAL);
+ colors.append(notificationError ? OptionColor.RED : OptionColor.NORMAL);
}
if (allowDebug) {
ids.append(R.id.btn_tdlib);
strings.append(R.string.TdlibLogs);
icons.append(R.drawable.baseline_build_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
ids.append(R.id.btn_build);
strings.append(R.string.AppLogs);
icons.append(R.drawable.baseline_build_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
ids.append(R.id.btn_experiment);
strings.append(R.string.ExperimentalSettings);
icons.append(R.drawable.templarian_baseline_flask_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
}
SpannableStringBuilder b = new SpannableStringBuilder();
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsDataController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsDataController.java
index b8c1905a4e..f8085f3515 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsDataController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsDataController.java
@@ -452,7 +452,7 @@ public void onClick (View v) {
final boolean toggleResult = adapter.toggleView(v);
if (id == R.id.btn_resetNetworkStats) {
- showOptions(Lang.getString(R.string.ResetStatsHint), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.Reset), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, optionId) -> {
+ showOptions(Lang.getString(R.string.ResetStatsHint), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.Reset), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, optionId) -> {
if (optionId == R.id.btn_delete) {
tdlib.client().send(new TdApi.ResetNetworkStatistics(), object -> {
switch (object.getConstructor()) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsFoldersController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsFoldersController.java
index 829dc8a855..cabb696b83 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsFoldersController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsFoldersController.java
@@ -484,8 +484,8 @@ private void showTooltip (View view, CharSequence text) {
private void showChatFolderOptions (TdApi.ChatFolderInfo chatFolderInfo) {
Options options = new Options.Builder()
.info(chatFolderInfo.title)
- .item(new OptionItem(R.id.btn_edit, Lang.getString(R.string.EditFolder), OPTION_COLOR_NORMAL, R.drawable.baseline_edit_24))
- .item(new OptionItem(R.id.btn_delete, Lang.getString(R.string.RemoveFolder), OPTION_COLOR_RED, R.drawable.baseline_delete_24))
+ .item(new OptionItem(R.id.btn_edit, Lang.getString(R.string.EditFolder), OptionColor.NORMAL, R.drawable.baseline_edit_24))
+ .item(new OptionItem(R.id.btn_delete, Lang.getString(R.string.RemoveFolder), OptionColor.RED, R.drawable.baseline_delete_24))
.build();
showOptions(options, (optionItemView, id) -> {
if (id == R.id.btn_edit) {
@@ -498,7 +498,7 @@ private void showChatFolderOptions (TdApi.ChatFolderInfo chatFolderInfo) {
}
private void showRemoveFolderConfirm (int chatFolderId) {
- showConfirm(Lang.getString(R.string.RemoveFolderConfirm), Lang.getString(R.string.Remove), R.drawable.baseline_delete_24, OPTION_COLOR_RED, () -> {
+ showConfirm(Lang.getString(R.string.RemoveFolderConfirm), Lang.getString(R.string.Remove), R.drawable.baseline_delete_24, OptionColor.RED, () -> {
deleteChatFolder(chatFolderId);
});
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsLanguageController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsLanguageController.java
index 0f88bd6b7f..df17e50dda 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsLanguageController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsLanguageController.java
@@ -298,7 +298,7 @@ private void showRemoveLanguagePrompt (ListItem item) {
if (languageInfo == null || languageInfo.isOfficial)
return;
boolean isCustom = Td.isLocal(languageInfo);
- showOptions(Lang.getStringBold(isCustom ? R.string.DeleteLanguageConfirm : R.string.LanguageDeleteConfirm, languageInfo.nativeName, languageInfo.name, tdlib.tMeLanguageUrl(languageInfo.id)), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(isCustom ? R.string.RemoveLanguage : R.string.LanguageDelete), Lang.getString(R.string.Cancel)}, new int[]{OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getStringBold(isCustom ? R.string.DeleteLanguageConfirm : R.string.LanguageDeleteConfirm, languageInfo.nativeName, languageInfo.name, tdlib.tMeLanguageUrl(languageInfo.id)), new int[]{R.id.btn_delete, R.id.btn_cancel}, new String[]{Lang.getString(isCustom ? R.string.RemoveLanguage : R.string.LanguageDelete), Lang.getString(R.string.Cancel)}, new int[]{OptionColor.RED, OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_delete) {
removeLanguage(item, languageInfo);
}
@@ -548,28 +548,28 @@ private boolean showLanguageOptions (ListItem item) {
ids.append(R.id.btn_view);
icons.append(R.drawable.baseline_edit_24);
strings.append(R.string.LocalizationEdit);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
} else if (languageInfo.id.equals(Lang.packId())) {
ids.append(R.id.btn_view);
icons.append(R.drawable.baseline_visibility_24);
strings.append(R.string.LocalizationView);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
}
ids.append(R.id.btn_shareLink);
icons.append(R.drawable.baseline_forward_24);
strings.append(R.string.Share);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
ids.append(R.id.btn_share);
icons.append(R.drawable.baseline_code_24);
strings.append(R.string.LocalisationShare);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
if (Td.isInstalled(languageInfo)) {
ids.append(R.id.btn_delete);
icons.append(R.drawable.baseline_delete_forever_24);
strings.append(Td.isLocal(languageInfo) ? R.string.RemoveLanguage : R.string.LanguageDelete);
- colors.append(OPTION_COLOR_RED);
+ colors.append(OptionColor.RED);
}
} else if (!Td.isLocal(languageInfo)) {
ids = new IntList(1);
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsLogFilesController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsLogFilesController.java
index 9de0a4939c..1d8e296105 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsLogFilesController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsLogFilesController.java
@@ -126,7 +126,7 @@ public void onClick (View v) {
if (viewId == R.id.btn_file) {
final ListItem item = (ListItem) v.getTag();
final File file = (File) item.getData();
- showOptions(file.getName() + " (" + Strings.buildSize(file.length()) + ")", new int[] {R.id.btn_open, R.id.btn_share, R.id.btn_delete}, new String[] {"View", "Share", "Delete"}, new int[] {OPTION_COLOR_NORMAL, OPTION_COLOR_NORMAL, OPTION_COLOR_RED}, new int[] {R.drawable.baseline_visibility_24, R.drawable.baseline_forward_24, R.drawable.baseline_delete_24}, (itemView, id) -> {
+ showOptions(file.getName() + " (" + Strings.buildSize(file.length()) + ")", new int[] {R.id.btn_open, R.id.btn_share, R.id.btn_delete}, new String[] {"View", "Share", "Delete"}, new int[] {OptionColor.NORMAL, OptionColor.NORMAL, OptionColor.RED}, new int[] {R.drawable.baseline_visibility_24, R.drawable.baseline_forward_24, R.drawable.baseline_delete_24}, (itemView, id) -> {
if (id == R.id.btn_open) {
TextController c = new TextController(context, tdlib);
c.setArguments(TextController.Arguments.fromFile(file.getName(), file.getPath(), "text/plain"));
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsNotificationController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsNotificationController.java
index f58a227cd6..3996d7d06c 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsNotificationController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsNotificationController.java
@@ -1474,8 +1474,8 @@ public void onClick (View v) {
}
case TdlibNotificationManager.Status.FIREBASE_ERROR: {
showOptions(new Options.Builder()
- .item(new OptionItem(R.id.btn_retry, Lang.getString(R.string.FirebaseErrorResolveTryAgain), OPTION_COLOR_BLUE, R.drawable.baseline_sync_problem_24))
- .item(new OptionItem(R.id.btn_share, Lang.getString(R.string.FirebaseErrorResolveShareError), OPTION_COLOR_NORMAL, R.drawable.baseline_forward_24))
+ .item(new OptionItem(R.id.btn_retry, Lang.getString(R.string.FirebaseErrorResolveTryAgain), OptionColor.BLUE, R.drawable.baseline_sync_problem_24))
+ .item(new OptionItem(R.id.btn_share, Lang.getString(R.string.FirebaseErrorResolveShareError), OptionColor.NORMAL, R.drawable.baseline_forward_24))
.build(), (optionView, optionId) -> {
if (optionId == R.id.btn_retry) {
tdlib.context().checkDeviceToken(0, null);
@@ -1980,7 +1980,7 @@ private static boolean isRingtone (int id) {
}
private void showResetNotificationsConfirm () {
- showOptions(Lang.getString(R.string.ResetNotificationsConfirm), new int[] {R.id.btn_resetNotifications, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ResetNotifications), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.ResetNotificationsConfirm), new int[] {R.id.btn_resetNotifications, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ResetNotifications), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_resetNotifications) {
resetNotificationSettings();
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyController.java
index e6559ded4d..0a982806d4 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyController.java
@@ -391,7 +391,7 @@ public void onClick (View v) {
} else if (id == R.id.btn_suggestContacts) {
boolean value = ((SettingView) v).getToggler().isEnabled();
if (value) {
- showOptions(Lang.getString(R.string.SuggestContactsAlert), new int[] {R.id.btn_suggestContacts, R.id.btn_cancel}, new String[] {Lang.getString(R.string.SuggestContactsDone), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, resultId) -> {
+ showOptions(Lang.getString(R.string.SuggestContactsAlert), new int[] {R.id.btn_suggestContacts, R.id.btn_cancel}, new String[] {Lang.getString(R.string.SuggestContactsDone), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, resultId) -> {
if (resultId == R.id.btn_suggestContacts) {
tdlib.setDisableTopChats(true);
adapter.updateValuedSettingById(R.id.btn_suggestContacts);
@@ -402,7 +402,7 @@ public void onClick (View v) {
tdlib.setDisableTopChats(!((SettingView) v).getToggler().toggle(true));
}
} else if (id == R.id.btn_resetContacts) {
- showOptions(Lang.getString(R.string.SyncContactsDeleteInfo), new int[] {R.id.btn_resetContacts, R.id.btn_cancel}, new String[] {Lang.getString(R.string.SyncContactsDeleteButton), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, resultId) -> {
+ showOptions(Lang.getString(R.string.SyncContactsDeleteInfo), new int[] {R.id.btn_resetContacts, R.id.btn_cancel}, new String[] {Lang.getString(R.string.SyncContactsDeleteButton), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, resultId) -> {
if (resultId == R.id.btn_resetContacts) {
tdlib.contacts().deleteContacts();
}
@@ -462,7 +462,7 @@ public void onClick (View v) {
} else if (id == R.id.btn_deleteAccount) {
tdlib.ui().permanentlyDeleteAccount(this, true);
} else if (id == R.id.btn_clearAllDrafts) {
- showOptions(Lang.getString(R.string.AreYouSureClearDrafts), new int[] {R.id.btn_clearAllDrafts, R.id.btn_cancel}, new String[] {Lang.getString(R.string.PrivacyDeleteCloudDrafts), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, actionId) -> {
+ showOptions(Lang.getString(R.string.AreYouSureClearDrafts), new int[] {R.id.btn_clearAllDrafts, R.id.btn_cancel}, new String[] {Lang.getString(R.string.PrivacyDeleteCloudDrafts), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, actionId) -> {
if (actionId == R.id.btn_clearAllDrafts) {
tdlib.client().send(new TdApi.ClearAllDraftMessages(true), tdlib.doneHandler());
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsProxyController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsProxyController.java
index 47de1a8c83..a423b38ab3 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsProxyController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsProxyController.java
@@ -555,7 +555,7 @@ private void removeProxy (Settings.Proxy proxy) {
if (proxyId == Settings.PROXY_ID_NONE) {
return;
}
- showOptions(Lang.getString(R.string.ProxyRemoveInfo), new int[] {R.id.btn_removeProxy, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ProxyRemove), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.ProxyRemoveInfo), new int[] {R.id.btn_removeProxy, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ProxyRemove), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_removeProxy) {
removeProxyImpl(proxyId);
}
@@ -667,24 +667,24 @@ private void showProxyOptions (Settings.Proxy proxy) {
ids.append(R.id.btn_editProxy);
strings.append(R.string.ProxyEdit);
icons.append(R.drawable.baseline_edit_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
if (proxy.proxy.type.getConstructor() != TdApi.ProxyTypeHttp.CONSTRUCTOR) {
ids.append(R.id.btn_share);
strings.append(R.string.Share);
icons.append(R.drawable.baseline_forward_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
ids.append(R.id.btn_copyLink);
strings.append(R.string.CopyLink);
icons.append(R.drawable.baseline_link_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
}
ids.append(R.id.btn_removeProxy);
strings.append(R.string.ProxyRemove);
icons.append(R.drawable.baseline_delete_24);
- colors.append(OPTION_COLOR_RED);
+ colors.append(OptionColor.RED);
showOptions(proxy.getName().toString(), ids.get(), strings.get(), colors.get(), icons.get(), (itemView, id) -> {
if (id == R.id.btn_share) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsSessionsController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsSessionsController.java
index 9cfcc806cc..39384125b6 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsSessionsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsSessionsController.java
@@ -536,7 +536,7 @@ private void removeSessionFromList (TdApi.Session session) {
private TdApi.Session sessionToTerminate;
private void killSession (final TdApi.Session session, boolean alert) {
- showOptions(Strings.concat("\n\n", Lang.boldify(Lang.getString(session.isPasswordPending ? R.string.TerminateIncompleteSessionQuestion : R.string.TerminateSessionQuestion)), getSubtext(session, true)), new int[]{R.id.btn_terminateSession, R.id.btn_cancel, R.id.btn_copyText}, new String[]{Lang.getString(session.isPasswordPending ? R.string.TerminateIncompleteSession : R.string.TerminateSession), Lang.getString(R.string.Cancel), Lang.getString(R.string.Copy)}, new int[]{OPTION_COLOR_RED, OPTION_COLOR_NORMAL, OPTION_COLOR_NORMAL}, new int[]{R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24, R.drawable.baseline_content_copy_24}, (itemView, id) -> {
+ showOptions(Strings.concat("\n\n", Lang.boldify(Lang.getString(session.isPasswordPending ? R.string.TerminateIncompleteSessionQuestion : R.string.TerminateSessionQuestion)), getSubtext(session, true)), new int[]{R.id.btn_terminateSession, R.id.btn_cancel, R.id.btn_copyText}, new String[]{Lang.getString(session.isPasswordPending ? R.string.TerminateIncompleteSession : R.string.TerminateSession), Lang.getString(R.string.Cancel), Lang.getString(R.string.Copy)}, new int[]{OptionColor.RED, OptionColor.NORMAL, OptionColor.NORMAL}, new int[]{R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24, R.drawable.baseline_content_copy_24}, (itemView, id) -> {
if (id == R.id.btn_terminateSession) {
terminateSession(session);
} else if (id == R.id.btn_copyText) {
@@ -565,7 +565,7 @@ public boolean onOptionItemPressed (View optionItemView, int id) {
public void onClick (View v) {
final int viewId = v.getId();
if (viewId == R.id.btn_terminateAllSessions) {
- showOptions(Lang.getString(R.string.AreYouSureSessions), new int[] {R.id.btn_terminateAllSessions, R.id.btn_cancel}, new String[] {Lang.getString(R.string.TerminateAllSessions), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.AreYouSureSessions), new int[] {R.id.btn_terminateAllSessions, R.id.btn_cancel}, new String[] {Lang.getString(R.string.TerminateAllSessions), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_terminateAllSessions) {
terminateOtherSessions();
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsThemeController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsThemeController.java
index 49fea57821..fcabfba3e0 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsThemeController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsThemeController.java
@@ -1403,18 +1403,18 @@ private void showThemeOptions (ListItem item) {
ids.append(R.id.btn_edit);
icons.append(R.drawable.baseline_edit_24);
strings.append(R.string.ThemeEdit);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
ids.append(R.id.btn_share);
icons.append(R.drawable.baseline_forward_24);
strings.append(Settings.instance().canEditAuthor(customThemeId) ? R.string.ThemeExport : R.string.Share);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
if (!isCurrent) {
ids.append(R.id.btn_new);
icons.append(R.drawable.baseline_content_copy_24);
strings.append(R.string.ThemeCopy);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
}
} else {
info = Lang.getStringBold(R.string.ThemeCreateInfo, item.getString());
@@ -1422,30 +1422,30 @@ private void showThemeOptions (ListItem item) {
ids.append(R.id.btn_new);
icons.append(R.drawable.baseline_edit_24);
strings.append(R.string.ThemeCreate);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
ids.append(R.id.btn_share);
icons.append(R.drawable.baseline_forward_24);
strings.append(R.string.Share);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
}
ids.append(R.id.btn_delete);
icons.append(R.drawable.baseline_delete_forever_24);
strings.append(R.string.ThemeRemove);
- colors.append(OPTION_COLOR_RED);
+ colors.append(OptionColor.RED);
} else {
info = Lang.getStringBold(R.string.ThemeCreateInfo, item.getString());
ids.append(R.id.btn_new);
icons.append(R.drawable.baseline_create_24);
strings.append(R.string.ThemeCreate);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
if (BuildConfig.DEBUG) {
ids.append(R.id.btn_share);
icons.append(R.drawable.baseline_forward_24);
strings.append(R.string.Share);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsWebsitesController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsWebsitesController.java
index 2955f168a8..359673dacc 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsWebsitesController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsWebsitesController.java
@@ -276,7 +276,7 @@ private void terminateSession (final TdApi.ConnectedWebsite website, boolean ban
public void onClick (View v) {
final int viewId = v.getId();
if (viewId == R.id.btn_terminateAllSessions) {
- showOptions(Lang.getString(R.string.DisconnectAllWebsitesHint), new int[] {R.id.btn_terminateAllSessions, R.id.btn_cancel}, new String[] {Lang.getString(R.string.TerminateAllWebSessions), Lang.getString(R.string.Cancel)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ showOptions(Lang.getString(R.string.DisconnectAllWebsitesHint), new int[] {R.id.btn_terminateAllSessions, R.id.btn_cancel}, new String[] {Lang.getString(R.string.TerminateAllWebSessions), Lang.getString(R.string.Cancel)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_terminateAllSessions) {
disconnectAllWebsites();
}
@@ -285,7 +285,7 @@ public void onClick (View v) {
} else if (viewId == R.id.btn_session) {
ListItem item = (ListItem) v.getTag();
final TdApi.ConnectedWebsite website = (TdApi.ConnectedWebsite) item.getData();
- showOptions(website.domainName, new int[] {R.id.btn_terminateSession, R.id.btn_openChat}, new String[] {Lang.getString(R.string.DisconnectWebsiteAction), Lang.getString(R.string.OpenChat)}, new int[] {OPTION_COLOR_RED, OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_chat_bubble_24}, (itemView, id) -> {
+ showOptions(website.domainName, new int[] {R.id.btn_terminateSession, R.id.btn_openChat}, new String[] {Lang.getString(R.string.DisconnectWebsiteAction), Lang.getString(R.string.OpenChat)}, new int[] {OptionColor.RED, OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_forever_24, R.drawable.baseline_chat_bubble_24}, (itemView, id) -> {
if (id == R.id.btn_openChat) {
tdlib.ui().openPrivateChat(this, website.botUserId, new TdlibUi.ChatOpenParameters().keepStack());
} else if (id == R.id.btn_terminateSession) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SharedCommonController.java b/app/src/main/java/org/thunderdog/challegram/ui/SharedCommonController.java
index d76d5f03f4..11e9490069 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SharedCommonController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SharedCommonController.java
@@ -297,7 +297,7 @@ protected boolean needsCustomLongClickListener () {
protected boolean onLongClick (View v, ListItem item) {
final InlineResult> c = (InlineResult>) item.getData();
- alternateParent.showOptions(null, new int[]{R.id.btn_showInChat, R.id.btn_share, R.id.btn_delete}, new String[]{Lang.getString(R.string.ShowInChat), Lang.getString(R.string.Share), Lang.getString(R.string.Delete)}, new int[]{OPTION_COLOR_NORMAL, OPTION_COLOR_NORMAL, OPTION_COLOR_RED}, new int[] {R.drawable.baseline_visibility_24, R.drawable.baseline_forward_24, R.drawable.baseline_delete_24}, (itemView, id) -> {
+ alternateParent.showOptions(null, new int[]{R.id.btn_showInChat, R.id.btn_share, R.id.btn_delete}, new String[]{Lang.getString(R.string.ShowInChat), Lang.getString(R.string.Share), Lang.getString(R.string.Delete)}, new int[]{OptionColor.NORMAL, OptionColor.NORMAL, OptionColor.RED}, new int[] {R.drawable.baseline_visibility_24, R.drawable.baseline_forward_24, R.drawable.baseline_delete_24}, (itemView, id) -> {
if (id == R.id.btn_showInChat) {
alternateParent.closeSearchModeByBackPress(false);
tdlib.ui().openMessage(SharedCommonController.this, c.getMessage(), new TdlibUi.UrlOpenParameters().tooltip(context().tooltipManager().builder(v)));
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SharedMembersController.java b/app/src/main/java/org/thunderdog/challegram/ui/SharedMembersController.java
index cf7dc34fa6..b7935b976e 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SharedMembersController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SharedMembersController.java
@@ -271,14 +271,14 @@ protected boolean onLongClick (View v, ListItem item) {
if (TD.isCreator(member.status)) {
if (TD.isCreator(myStatus)) {
ids.append(R.id.btn_editRights);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_edit_24);
strings.append(R.string.EditAdminTitle);
boolean isAnonymous = ((TdApi.ChatMemberStatusCreator) member.status).isAnonymous;
if (!isChannel() || isAnonymous) {
ids.append(isAnonymous ? R.id.btn_makePublic : R.id.btn_makePrivate);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(isAnonymous ? R.drawable.nilsfast_baseline_incognito_off_24 : R.drawable.infanf_baseline_incognito_24);
strings.append(isAnonymous ? R.string.EditOwnerPublic : R.string.EditOwnerAnonymous);
}
@@ -287,7 +287,7 @@ protected boolean onLongClick (View v, ListItem item) {
int promoteMode = TD.canPromoteAdmin(myStatus, member.status);
if (promoteMode != TD.PROMOTE_MODE_NONE) {
ids.append(R.id.btn_editRights);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_stars_24);
switch (promoteMode) {
case TD.PROMOTE_MODE_EDIT:
@@ -311,7 +311,7 @@ protected boolean onLongClick (View v, ListItem item) {
)) {
boolean isAnonymous = ((TdApi.ChatMemberStatusAdministrator) member.status).rights.isAnonymous;
ids.append(isAnonymous ? R.id.btn_makePublic : R.id.btn_makePrivate);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(isAnonymous ? R.drawable.nilsfast_baseline_incognito_off_24 : R.drawable.infanf_baseline_incognito_24);
strings.append(isAnonymous ? R.string.EditAdminPublic : R.string.EditAdminAnonymous);
}
@@ -321,7 +321,7 @@ protected boolean onLongClick (View v, ListItem item) {
if (restrictMode != TD.RESTRICT_MODE_NONE) {
if (!isChannel() && !(restrictMode == TD.RESTRICT_MODE_EDIT && member.memberId.getConstructor() == TdApi.MessageSenderChat.CONSTRUCTOR)) {
ids.append(R.id.btn_restrictMember);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_block_24);
switch (restrictMode) {
case TD.RESTRICT_MODE_EDIT:
@@ -341,7 +341,7 @@ protected boolean onLongClick (View v, ListItem item) {
if (restrictMode != TD.RESTRICT_MODE_VIEW) {
if (TD.isMember(member.status)) {
ids.append(R.id.btn_blockSender);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_remove_circle_24);
strings.append(isChannel() ? R.string.ChannelRemoveUser : R.string.RemoveFromGroup);
} else {
@@ -361,7 +361,7 @@ protected boolean onLongClick (View v, ListItem item) {
R.string.UnbanMember
);
ids.append(R.id.btn_unblockSender);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
icons.append(R.drawable.baseline_remove_circle_24);
}
}
@@ -377,7 +377,7 @@ protected boolean onLongClick (View v, ListItem item) {
strings.append(Lang.getString(content.getSenderId().getConstructor() == TdApi.MessageSenderUser.CONSTRUCTOR ? R.string.ViewMessagesFromUser : R.string.ViewMessagesFromChat, tdlib.senderName(content.getSenderId(), true)));
}
icons.append(R.drawable.baseline_person_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
}
if (!ids.isEmpty()) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ThemeListController.java b/app/src/main/java/org/thunderdog/challegram/ui/ThemeListController.java
index 5787811423..892384ab62 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ThemeListController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ThemeListController.java
@@ -2798,7 +2798,7 @@ private void removeColor (View v, boolean clearAll) {
Lang.plural(R.string.ColorClearDone, state.getVersionCount(false)),
Lang.getString(R.string.Cancel)
}, new int[]{
- OPTION_COLOR_RED, OPTION_COLOR_NORMAL
+ OptionColor.RED, OptionColor.NORMAL
}, new int[]{
R.drawable.baseline_delete_forever_24, R.drawable.baseline_cancel_24
}, (itemView, optionId) -> {
@@ -2818,7 +2818,7 @@ private void removeColor (View v, boolean clearAll) {
Lang.getString(R.string.ColorRemoveDone),
Lang.getString(R.string.Cancel)
}, new int[] {
- OPTION_COLOR_RED, OPTION_COLOR_NORMAL
+ OptionColor.RED, OptionColor.NORMAL
}, new int[] {
R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24
}, (itemView, optionId) -> {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/TranslationControllerV2.java b/app/src/main/java/org/thunderdog/challegram/ui/TranslationControllerV2.java
index 990a4795ce..3b98f300ac 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/TranslationControllerV2.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/TranslationControllerV2.java
@@ -515,7 +515,7 @@ public void showOptions () {
ids.append(R.id.btn_copyTranslation);
strings.append(R.string.TranslationCopy);
icons.append(R.drawable.baseline_content_copy_24);
- colors.append(OPTION_COLOR_NORMAL);
+ colors.append(OptionColor.NORMAL);
}
if (ids.isEmpty()) return;
diff --git a/app/src/main/java/org/thunderdog/challegram/util/AppUpdater.java b/app/src/main/java/org/thunderdog/challegram/util/AppUpdater.java
index 2cd11a13c6..52b60f6833 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/AppUpdater.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/AppUpdater.java
@@ -352,10 +352,10 @@ private boolean offerTelegramChannelUpdate () {
(target, argStart, argEnd, argIndex, needFakeBold) -> argIndex != 1 ? Lang.boldCreator().onCreateSpan(target, argStart, argEnd, argIndex, needFakeBold) : null,
Strings.buildSize(bytesToDownload), displayVersion
))
- .item(new ViewController.OptionItem(R.id.btn_update, Lang.getString(R.string.DownloadUpdate), ViewController.OPTION_COLOR_BLUE, R.drawable.baseline_system_update_24));
+ .item(new ViewController.OptionItem(R.id.btn_update, Lang.getString(R.string.DownloadUpdate), ViewController.OptionColor.BLUE, R.drawable.baseline_system_update_24));
final String changesUrl = commit != null && !BuildConfig.COMMIT.equals(commit) ? BuildConfig.REMOTE_URL + "/compare/" + BuildConfig.COMMIT + "..." + commit : null;
if (changesUrl != null) {
- b.item(new ViewController.OptionItem(R.id.btn_sourceCode, Lang.getString(R.string.UpdateSourceChanges), ViewController.OPTION_COLOR_NORMAL, R.drawable.baseline_code_24));
+ b.item(new ViewController.OptionItem(R.id.btn_sourceCode, Lang.getString(R.string.UpdateSourceChanges), ViewController.OptionColor.NORMAL, R.drawable.baseline_code_24));
}
b.cancelItem();
c.showOptions(b.build(), (optionItemView, id) -> {
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/BaseView.java b/app/src/main/java/org/thunderdog/challegram/widget/BaseView.java
index 03f1169d90..68bb46ba4d 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/BaseView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/BaseView.java
@@ -593,7 +593,7 @@ private void openPreview (ViewController> controller, float x, float y) {
@Override
public void onForceTouchAction (ForceTouchView.ForceTouchContext context, int actionId, Object arg) {
if (actionId == R.id.btn_messageUnpin) {
- ancestor.showOptions(new ViewController.Options.Builder().item(new ViewController.OptionItem(R.id.btn_messageUnpin, Lang.getString(R.string.UnpinMessage), ViewController.OPTION_COLOR_RED, R.drawable.deproko_baseline_pin_undo_24)).cancelItem().build(), (optionItemView, id) -> {
+ ancestor.showOptions(new ViewController.Options.Builder().item(new ViewController.OptionItem(R.id.btn_messageUnpin, Lang.getString(R.string.UnpinMessage), ViewController.OptionColor.RED, R.drawable.deproko_baseline_pin_undo_24)).cancelItem().build(), (optionItemView, id) -> {
if (id == R.id.btn_messageUnpin) {
tdlib.client().send(new TdApi.UnpinChatMessage(messageId.getChatId(), messageId.getMessageId()), tdlib.okHandler());
}
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/EmojiLayout.java b/app/src/main/java/org/thunderdog/challegram/widget/EmojiLayout.java
index 6d4efcf7e9..01695d65b5 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/EmojiLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/EmojiLayout.java
@@ -126,7 +126,7 @@ public void clearRecentStickers () {
themeProvider.showOptions(null, new int[] {R.id.btn_done, R.id.btn_cancel}, new String[] {
Lang.getString(animatedEmojiOnly ? R.string.ClearRecentEmojiStatuses : R.string.ClearRecentStickers),
Lang.getString(R.string.Cancel)
- }, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_auto_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ }, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_auto_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_done) {
setShowRecents(false);
if (animatedEmojiOnly) {
@@ -150,7 +150,7 @@ public void clearRecentStickers () {
private void clearRecentEmoji () {
if (themeProvider != null) {
- themeProvider.showOptions(null, new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearRecentEmojiAction), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_auto_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
+ themeProvider.showOptions(null, new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ClearRecentEmojiAction), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_auto_delete_24, R.drawable.baseline_cancel_24}, (itemView, id) -> {
if (id == R.id.btn_delete) {
Emoji.instance().clearRecents();
ViewController> c = adapter.getCachedItem(0);
@@ -174,15 +174,15 @@ public void openEmojiSetOptions (final TGStickerSetInfo info) {
Lang.getString(R.string.CopyLink),
Lang.getString(isTrending ? R.string.AddPack : R.string.DeletePack)
}, new int[] {
- ViewController.OPTION_COLOR_NORMAL,
- isTrending ? ViewController.OPTION_COLOR_NORMAL : ViewController.OPTION_COLOR_RED
+ ViewController.OptionColor.NORMAL,
+ isTrending ? ViewController.OptionColor.NORMAL : ViewController.OptionColor.RED
}, new int[] {
R.drawable.baseline_link_24,
isTrending ? R.drawable.deproko_baseline_insert_sticker_24 : R.drawable.baseline_delete_24
}, (itemView, id) -> {
if (id == R.id.more_btn_delete) {
if (themeProvider != null) {
- themeProvider.showOptions(Lang.getStringBold(R.string.RemoveEmojiSet, info.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RemoveStickerSetAction), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (resultItemView, resultId) -> {
+ themeProvider.showOptions(Lang.getStringBold(R.string.RemoveEmojiSet, info.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RemoveStickerSetAction), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (resultItemView, resultId) -> {
if (resultId == R.id.btn_delete) {
ViewController> c = adapter.getCachedItem(0);
if (c != null) {
@@ -211,10 +211,10 @@ public void removeStickerSet (final TGStickerSetInfo info) {
if (animatedEmojiOnly) return;
if (themeProvider != null) {
- themeProvider.showOptions(null, new int[] {R.id.btn_copyLink, R.id.btn_archive, R.id.more_btn_delete}, new String[] {Lang.getString(R.string.CopyLink), Lang.getString(R.string.ArchivePack), Lang.getString(R.string.DeletePack)}, new int[] {ViewController.OPTION_COLOR_NORMAL, ViewController.OPTION_COLOR_NORMAL, ViewController.OPTION_COLOR_RED}, new int[] {R.drawable.baseline_link_24, R.drawable.baseline_archive_24, R.drawable.baseline_delete_24}, (itemView, id) -> {
+ themeProvider.showOptions(null, new int[] {R.id.btn_copyLink, R.id.btn_archive, R.id.more_btn_delete}, new String[] {Lang.getString(R.string.CopyLink), Lang.getString(R.string.ArchivePack), Lang.getString(R.string.DeletePack)}, new int[] {ViewController.OptionColor.NORMAL, ViewController.OptionColor.NORMAL, ViewController.OptionColor.RED}, new int[] {R.drawable.baseline_link_24, R.drawable.baseline_archive_24, R.drawable.baseline_delete_24}, (itemView, id) -> {
if (id == R.id.more_btn_delete) {
if (themeProvider != null) {
- themeProvider.showOptions(Lang.getStringBold(R.string.RemoveStickerSet, info.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RemoveStickerSetAction), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (resultItemView, resultId) -> {
+ themeProvider.showOptions(Lang.getStringBold(R.string.RemoveStickerSet, info.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.RemoveStickerSetAction), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_delete_24, R.drawable.baseline_cancel_24}, (resultItemView, resultId) -> {
if (resultId == R.id.btn_delete) {
parentController.tdlib().client().send(new TdApi.ChangeStickerSet(info.getId(), false, false), parentController.tdlib().okHandler());
}
@@ -223,7 +223,7 @@ public void removeStickerSet (final TGStickerSetInfo info) {
}
} else if (id == R.id.btn_archive) {
if (themeProvider != null) {
- themeProvider.showOptions(Lang.getStringBold(R.string.ArchiveStickerSet, info.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ArchiveStickerSetAction), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OPTION_COLOR_RED, ViewController.OPTION_COLOR_NORMAL}, new int[] {R.drawable.baseline_archive_24, R.drawable.baseline_cancel_24}, (resultItemView, resultId) -> {
+ themeProvider.showOptions(Lang.getStringBold(R.string.ArchiveStickerSet, info.getTitle()), new int[] {R.id.btn_delete, R.id.btn_cancel}, new String[] {Lang.getString(R.string.ArchiveStickerSetAction), Lang.getString(R.string.Cancel)}, new int[] {ViewController.OptionColor.RED, ViewController.OptionColor.NORMAL}, new int[] {R.drawable.baseline_archive_24, R.drawable.baseline_cancel_24}, (resultItemView, resultId) -> {
if (resultId == R.id.btn_delete) {
parentController.tdlib().client().send(new TdApi.ChangeStickerSet(info.getId(), false, true), parentController.tdlib().okHandler());
}
From 41f9cbfc3e458a3bafb30c3360032f30af7f99f9 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 15:13:07 +0400
Subject: [PATCH 22/61] Upgraded TDLib to tdlib/td@09c6bad
---
app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java | 2 ++
tdlib | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
index f32967c8d8..8c6c7f70aa 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
@@ -5903,6 +5903,7 @@ private void updateParameters (Client client) {
client.send(new TdApi.SetOption("storage_max_files_size", new TdApi.OptionValueInteger(Integer.MAX_VALUE)), okHandler);
client.send(new TdApi.SetOption("ignore_default_disable_notification", new TdApi.OptionValueBoolean(true)), okHandler);
client.send(new TdApi.SetOption("ignore_platform_restrictions", new TdApi.OptionValueBoolean(AppInstallationUtil.isAppSideLoaded())), okHandler);
+ client.send(new TdApi.SetOption("process_pinned_messages_as_mentions", new TdApi.OptionValueBoolean(true)), okHandler);
}
checkConnectionParams(client, true);
@@ -9306,6 +9307,7 @@ private void updateOption (ClientHolder context, TdApi.UpdateOption update) {
case "use_storage_optimizer":
case "storage_max_files_size":
case "use_pfs":
+ case "process_pinned_messages_as_mentions":
case "use_quick_ack":
case "connection_parameters":
case "notification_group_count_max":
diff --git a/tdlib b/tdlib
index 55041be25f..28e2d7e6cd 160000
--- a/tdlib
+++ b/tdlib
@@ -1 +1 @@
-Subproject commit 55041be25fa2384dcd6902be08316bb26afaae56
+Subproject commit 28e2d7e6cd59c5fffb71ed0451afef55d75a4d0d
From 4ca4dc7a510257d36666c3ebf9edd151bc5c67ea Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 16:20:10 +0400
Subject: [PATCH 23/61] Move emulator detection cache logic to
`EmulatorDetectionResult`
---
.../challegram/unsorted/Settings.java | 36 ++++++++++++++-----
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java b/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
index b54e6d27dc..5d00bacec6 100644
--- a/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
+++ b/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
@@ -6203,6 +6203,27 @@ public EmulatorDetectionResult (long time, long installationId, long elapsed, lo
public boolean isDetected () {
return BitwiseUtils.hasFlag(flags, FLAG_EMULATOR_DETECTED);
}
+
+ public long[] toLongArray () {
+ return new long[] {
+ time,
+ installationId,
+ elapsed,
+ flags
+ };
+ }
+
+ public static EmulatorDetectionResult restore (long[] array) {
+ if (array == null || array.length != 4) {
+ return null;
+ }
+ return new EmulatorDetectionResult(
+ array[0],
+ array[1],
+ array[2],
+ array[3]
+ );
+ }
}
@Nullable
@@ -6211,21 +6232,18 @@ public EmulatorDetectionResult getLastEmulatorDetectionResult () {
if (emulatorDetectionResult == null) {
return null;
}
- return new EmulatorDetectionResult(
- emulatorDetectionResult[0],
- emulatorDetectionResult[1],
- emulatorDetectionResult[2],
- emulatorDetectionResult[3]
- );
+ return EmulatorDetectionResult.restore(emulatorDetectionResult);
}
public void trackEmulatorDetectionResult (long installationId, long elapsed, boolean isEmulator) {
- long[] result = new long[] {
+ EmulatorDetectionResult result = new EmulatorDetectionResult(
System.currentTimeMillis(),
installationId,
+ elapsed,
isEmulator ? EmulatorDetectionResult.FLAG_EMULATOR_DETECTED : 0
- };
- pmc.putLongArray(KEY_EMULATOR_DETECTION_RESULT, result);
+ );
+ long[] data = result.toLongArray();
+ pmc.putLongArray(KEY_EMULATOR_DETECTION_RESULT, data);
if (isEmulator) {
putBoolean(KEY_IS_EMULATOR, true);
}
From b4311914f37d2535058a6f6a24fa1d90aeab472f Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 17:37:53 +0400
Subject: [PATCH 24/61] Animate avatar in the chat header
---
.../java/org/thunderdog/challegram/loader/AvatarReceiver.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/loader/AvatarReceiver.java b/app/src/main/java/org/thunderdog/challegram/loader/AvatarReceiver.java
index 0e4595aa7c..933efb4b2a 100644
--- a/app/src/main/java/org/thunderdog/challegram/loader/AvatarReceiver.java
+++ b/app/src/main/java/org/thunderdog/challegram/loader/AvatarReceiver.java
@@ -766,7 +766,7 @@ private void loadAnimation (@Nullable TdApi.ChatPhoto photo, boolean allowAnimat
boolean fullSize = BitwiseUtils.hasFlag(options, Options.FULL_SIZE);
TdApi.AnimatedChatPhoto smallAnimation = photo.smallAnimation == null ? photo.animation : photo.smallAnimation;
TdApi.AnimatedChatPhoto fullAnimation = photo.smallAnimation != null ? photo.animation : null;
- loadPreviewAnimation(!fullSize || fullAnimation == null ? smallAnimation : null);
+ loadPreviewAnimation(!fullSize || fullAnimation == null || displayFullSizeOnlyInFullScreen ? smallAnimation : null);
loadFullAnimation(fullSize ? fullAnimation : null);
} else {
loadPreviewAnimation(null);
From 462cde9621bf475443366eedb01fa192e0b84625 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 18:33:30 +0400
Subject: [PATCH 25/61] Fix giveaway localizations
---
.../challegram/data/TGMessageGiveaway.java | 15 +++++++-
.../data/TGMessageGiveawayWinners.java | 37 ++++++++++++++-----
.../thunderdog/challegram/tool/Strings.java | 23 +++++++++---
app/src/main/res/values/strings.xml | 10 +++--
4 files changed, 64 insertions(+), 21 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveaway.java b/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveaway.java
index 82b2e14032..9b60c2036f 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveaway.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveaway.java
@@ -32,6 +32,7 @@
import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
+import org.thunderdog.challegram.tool.Strings;
import org.thunderdog.challegram.tool.TGCountry;
import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.tool.Views;
@@ -74,7 +75,16 @@ public TGMessageGiveaway (MessagesManager manager, TdApi.Message msg, @NonNull T
content.add(Lang.getString(R.string.GiveawayPrizesWith), () -> Theme.getColor(ColorId.textLight), currentViews);
content.padding(Screen.dp(6));
}
- content.add(Lang.pluralBold(R.string.xGiveawayPrizePremiumInfo, premiumGiveaway.winnerCount, premiumGiveaway.monthCount), getTextColorSet(), currentViews);
+ CharSequence text = Lang.plural(
+ R.string.xGiveawayPrizePremium,
+ premiumGiveaway.winnerCount,
+ (target, argStart, argEnd, argIndex, needFakeBold) -> argIndex == 0 ? Lang.newBoldSpan(needFakeBold) : null,
+ Lang.pluralBold(
+ R.string.xGiveawayPrizePremiumMonths, premiumGiveaway.monthCount
+ )
+ );
+ text = Strings.replaceBoldTokens(text);
+ content.add(text, getTextColorSet(), currentViews);
content.padding(Screen.dp(BLOCK_MARGIN));
content.add(Lang.boldify(Lang.getString(R.string.GiveawayParticipants)), getTextColorSet(), currentViews);
@@ -114,7 +124,8 @@ public TGMessageGiveaway (MessagesManager manager, TdApi.Message msg, @NonNull T
return content.getHeight();
}
- @Override protected void onBuildButton (int maxWidth) {
+ @Override
+ protected void onBuildButton (int maxWidth) {
final boolean isParticipating = TD.isParticipating(premiumGiveawayInfo);
rippleButton.setCustom(isParticipating ? R.drawable.baseline_check_18 : 0, Lang.getString(isParticipating ? R.string.GiveawayParticipating : R.string.GiveawayLearnMore), maxWidth, false, this);
rippleButton.firstButton().setCustomIconReverse(true);
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayWinners.java b/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayWinners.java
index 6e1ae1b34d..7ecd8c77c1 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayWinners.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayWinners.java
@@ -16,6 +16,8 @@
import android.graphics.Canvas;
import android.graphics.RectF;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
import android.text.style.ClickableSpan;
import android.view.Gravity;
import android.view.View;
@@ -41,6 +43,7 @@
import org.thunderdog.challegram.util.text.TextEntity;
import org.thunderdog.challegram.util.text.TextEntityCustom;
+import me.vkryl.core.StringUtils;
import me.vkryl.td.MessageId;
import me.vkryl.td.Td;
@@ -68,17 +71,33 @@ public TGMessageGiveawayWinners (MessagesManager manager, TdApi.Message msg, @No
participantsCounterY = content.getHeight() + Screen.dp(1.5f);
content.padding(Screen.dp(35));
- String gvw = Lang.getString(R.string.Giveaway);
- TextEntityCustom custom = new TextEntityCustom(controller(), tdlib, gvw, 0, gvw.length(), 0, openParameters());
- custom.setCustomColorSet(getLinkColorSet());
- custom.setOnClickListener(new ClickableSpan() {
- @Override public void onClick (@NonNull View widget) {
- tdlib.ui().openMessage(controller(), giveawayWinners.boostedChatId, new MessageId(giveawayWinners.boostedChatId, giveawayWinners.giveawayMessageId), openParameters());
+
+ CharSequence text = Lang.pluralBold(R.string.xGiveawayWinnersSelectedText, giveawayWinners.winnerCount);
+ int startIndex = StringUtils.indexOf(text, "**");
+ if (startIndex != -1) {
+ int endIndex = StringUtils.indexOf(text, "**", startIndex + 2);
+ if (endIndex != -1) {
+ SpannableStringBuilder b = new SpannableStringBuilder(text);
+
+ String linkText = b.subSequence(startIndex + 2, endIndex).toString();
+ TextEntityCustom custom = new TextEntityCustom(controller(), tdlib, linkText, 0, linkText.length(), 0, openParameters());
+ custom.setCustomColorSet(getLinkColorSet());
+ custom.setOnClickListener(new ClickableSpan() {
+ @Override public void onClick (@NonNull View widget) {
+ tdlib.ui().openMessage(controller(), giveawayWinners.boostedChatId, new MessageId(giveawayWinners.boostedChatId, giveawayWinners.giveawayMessageId), openParameters());
+ }
+ });
+ FormattedText linkArgument = new FormattedText(linkText, new TextEntity[] {custom});
+
+ b.delete(endIndex, endIndex + 2);
+ b.delete(startIndex, startIndex + 2);
+ endIndex -= 2;
+ b.setSpan(linkArgument, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text = b;
}
- });
- FormattedText gvwf = new FormattedText(gvw, new TextEntity[] {custom});
+ }
- FormattedText formattedText = FormattedText.getPlural(tdlib, null, R.string.xGiveawayWinnersSelectedInfo, giveawayWinners.winnerCount, gvwf);
+ FormattedText formattedText = FormattedText.valueOf(text, tdlib, openParameters());
Text.Builder b = new Text.Builder(formattedText, maxWidth - Screen.dp(CONTENT_PADDING_DP * 2), getGiveawayTextStyleProvider(), getTextColorSet(), null).viewProvider(currentViews).textFlags(Text.FLAG_ALIGN_CENTER);
content.add(Lang.boldify(Lang.getString(R.string.GiveawayWinnersSelected)), getTextColorSet(), currentViews);
diff --git a/app/src/main/java/org/thunderdog/challegram/tool/Strings.java b/app/src/main/java/org/thunderdog/challegram/tool/Strings.java
index 3fd039352e..8567a9ae41 100644
--- a/app/src/main/java/org/thunderdog/challegram/tool/Strings.java
+++ b/app/src/main/java/org/thunderdog/challegram/tool/Strings.java
@@ -705,11 +705,17 @@ public static String getNumber (String input) {
return b.toString();
}
- public static CharSequence replaceBoldTokens (final String input) {
- return replaceBoldTokens(input, ColorId.NONE);
+ public static CharSequence replaceBoldTokens (final CharSequence input) {
+ return replaceBoldTokens(input, Lang.boldCreator());
}
- public static CharSequence replaceBoldTokens (final String input, @ColorId int colorId) {
+ public static CharSequence replaceBoldTokens (final CharSequence input, @ColorId int colorId) {
+ return replaceBoldTokens(input, (target, argStart, argEnd, argIndex, needFakeBold) ->
+ new CustomTypefaceSpan(needFakeBold ? Fonts.getRobotoRegular() : Fonts.getRobotoMedium(), colorId).setFakeBold(needFakeBold)
+ );
+ }
+
+ public static CharSequence replaceBoldTokens (final CharSequence input, Lang.SpanCreator spanCreator) {
String token = "**";
int tokenLen = token.length();
@@ -722,12 +728,17 @@ public static CharSequence replaceBoldTokens (final String input, @ColorId int c
end = ssb.toString().indexOf(token, start);
if (start > -1 && end > -1) {
- boolean fakeBold = Text.needFakeBold(ssb, start, end);
- CustomTypefaceSpan span = new CustomTypefaceSpan(fakeBold ? Fonts.getRobotoRegular() : Fonts.getRobotoMedium(), colorId).setFakeBold(fakeBold);
- ssb.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
// Delete the tokens before and after the span
ssb.delete(end, end + tokenLen);
ssb.delete(start - tokenLen, start);
+ start -= tokenLen;
+ end -= tokenLen;
+
+ // Set span
+ boolean fakeBold = Text.needFakeBold(ssb, start, end);
+ Object span = spanCreator.onCreateSpan(ssb, start, end, count, fakeBold);
+ ssb.setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+
count++;
}
} while (start > -1 && end > -1);
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index eb69f9fbdf..304c127a60 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5042,10 +5042,12 @@
Winners Selection Date
%1$s at %2$s
- %1$s winner of the %2$s were randomly selected by Telegram.
- %1$s winners of the %2$s were randomly selected by Telegram.
- %1$s Telegram Premium Subscription for %2$s months.
- %1$s Telegram Premium Subscriptions for %2$s months.
+ %1$s winner of the **Giveaway** were randomly selected by Telegram.
+ %1$s winners of the **Giveaway** were randomly selected by Telegram.
+ %1$s **Telegram Premium** subscription %2$s.
+ %1$s **Telegram Premium** subscriptions %2$s.
+ for %1$s month
+ for %1$s months
Your prize is a **Telegram Premium** subscription for **%1$s** month.
Your prize is a **Telegram Premium** subscription for **%1$s** months.
From cf3ca341c6f52e4fff8a8184dbe206d8bd2eb02c Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 18:35:50 +0400
Subject: [PATCH 26/61] Sync `strings.xml` with `translations.telegram.org`
---
app/src/main/res/values/strings.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 304c127a60..a2ac615bb9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -955,7 +955,7 @@
Who can add you to groups and channels?
You can restrict who can add you to groups and channels with granular precision.
Who can see your Last Seen time?
- You won\'t see Last Seen or Online statuses for people with whom you don\'t share yours. Approximate times will be shown instead (recently, within a week, within a month).
+ Unless you are a Premium user, you won\'t see Last Seen or Online statuses for people with whom you don\'t share yours. Approximate times will be shown instead (recently, within a week, within a month).
Always Share With
Never Share With
These settings will override the values above.
From e1ee0b8586d82756ecfff3c8c00ac801f29a88ab Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 18:43:49 +0400
Subject: [PATCH 27/61] Update `vkryl/core` submodule
---
vkryl/core | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/vkryl/core b/vkryl/core
index 2c06947759..ecafdc141a 160000
--- a/vkryl/core
+++ b/vkryl/core
@@ -1 +1 @@
-Subproject commit 2c06947759e986a9ad24d805ad19d39aa0a30023
+Subproject commit ecafdc141ae902956ebca4d6f19a2135e311ec0f
From 3f4d812182a25afbfd188726337b0cee987cafb2 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 21:21:34 +0400
Subject: [PATCH 28/61] Add emulator detector prompt
---
.../thunderdog/challegram/BaseActivity.java | 74 +++++++++++++++----
.../challegram/ui/PhoneController.java | 44 ++++++++++-
.../challegram/unsorted/Settings.java | 33 ++++++---
app/src/main/res/values/local_strings.xml | 3 +
app/src/main/res/values/strings.xml | 8 +-
vkryl/android | 2 +-
6 files changed, 132 insertions(+), 32 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/BaseActivity.java b/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
index 6e4c17db6b..d556589509 100644
--- a/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
+++ b/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
@@ -151,6 +151,7 @@
import me.vkryl.core.lambda.FutureBool;
import me.vkryl.core.lambda.FutureInt;
import me.vkryl.core.lambda.RunnableBool;
+import me.vkryl.core.lambda.RunnableData;
import me.vkryl.core.reference.ReferenceList;
import me.vkryl.core.reference.ReferenceUtils;
import nl.dionsegijn.konfetti.xml.KonfettiView;
@@ -325,27 +326,58 @@ protected final void setTdlib (Tdlib tdlib) {
drawer.onCurrentTdlibChanged(tdlib);
}
onTdlibChanged();
- runEmulatorChecks(false);
+ runEmulatorChecks();
}
}
- private boolean ranEmulatorChecks;
+ private boolean ranEmulatorChecks, emulatorChecksFinished;
+ private final List> emulatorCheckFinishCallbacks = new ArrayList<>();
- public void runEmulatorChecks (boolean force) {
- if (Settings.instance().isEmulator() || ranEmulatorChecks) {
+ public void runEmulatorChecks () {
+ runEmulatorChecksImpl(false, null);
+ }
+
+ public void forceRunEmulatorChecks (@Nullable RunnableData after) {
+ runEmulatorChecksImpl(true, after);
+ }
+
+ private void addEmulatorChecksCallback (@Nullable RunnableData after) {
+ if (after == null) {
+ return;
+ }
+ boolean postponed;
+ synchronized (emulatorCheckFinishCallbacks) {
+ postponed = !emulatorChecksFinished;
+ if (postponed) {
+ emulatorCheckFinishCallbacks.add(after);
+ }
+ }
+ if (!postponed) {
+ after.runWithData(Settings.instance().getLastEmulatorDetectionResult());
+ }
+ }
+
+ private void runEmulatorChecksImpl (boolean force, @Nullable RunnableData after) {
+ if (ranEmulatorChecks) {
+ addEmulatorChecksCallback(after);
return;
}
+
long installationId = Settings.instance().installationId();
if (!force) {
+ Settings.EmulatorDetectionResult previousResult = Settings.instance().getLastEmulatorDetectionResult();
List conditions = Arrays.asList(
+ // every app launch without authorization
() -> !tdlib.context().hasActiveAccounts() || tdlib.isUnauthorized(),
() -> {
- Settings.EmulatorDetectionResult detectionResult = Settings.instance().getLastEmulatorDetectionResult();
- if (detectionResult != null) {
- long elapsed = System.currentTimeMillis() - detectionResult.time;
- return installationId != detectionResult.installationId || elapsed >= TimeUnit.DAYS.toMillis(3);
+ if (previousResult != null) {
+ if (previousResult.isEmulatorDetected()) {
+ return false;
+ }
+ long elapsed = System.currentTimeMillis() - previousResult.time;
+ // every 3 days or after every update
+ return installationId != previousResult.installationId || elapsed >= TimeUnit.DAYS.toMillis(3);
}
- // every 2 days or after every update
return true;
}
);
@@ -357,22 +389,34 @@ public void runEmulatorChecks (boolean force) {
}
}
if (!hasAnyReason) {
+ if (after != null) {
+ after.runWithData(previousResult);
+ }
return;
}
}
ranEmulatorChecks = true;
+ addEmulatorChecksCallback(after);
// Impl
new Thread(() -> {
long ms = SystemClock.uptimeMillis();
- boolean isEmulator = DeviceUtils.detectEmulator(BaseActivity.this, BuildConfig.EXPERIMENTAL);
+ long detectionResult = DeviceUtils.detectEmulator(BaseActivity.this, BuildConfig.EXPERIMENTAL);
long elapsed = SystemClock.uptimeMillis() - ms;
Log.v("Ran emulator detections in %dms", elapsed);
- Settings.instance().trackEmulatorDetectionResult(installationId, elapsed, isEmulator);
- if (isEmulator) {
- tdlib.context().setIsEmulator(true);
- }
+ Settings.EmulatorDetectionResult result = Settings.instance().trackEmulatorDetectionResult(installationId, elapsed, detectionResult);
+ tdlib.context().setIsEmulator(result.isEmulatorDetected());
+ UI.post(() -> {
+ emulatorChecksFinished = true;
+ if (activityState != UI.State.DESTROYED) {
+ for (int i = emulatorCheckFinishCallbacks.size() - 1; i >= 0; i--) {
+ RunnableData act = emulatorCheckFinishCallbacks.remove(i);
+ act.runWithData(result);
+ }
+ }
+ emulatorCheckFinishCallbacks.clear();
+ });
}, "EmulatorDetector").start();
}
@@ -1083,7 +1127,7 @@ public void onResume () {
}
}*/
appUpdater.checkForUpdates();
- runEmulatorChecks(false);
+ runEmulatorChecks();
}
protected void setOnline (boolean isOnline) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
index 88e90da7ed..2687306829 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
@@ -14,7 +14,10 @@
*/
package org.thunderdog.challegram.ui;
+import android.app.AlertDialog;
import android.content.Context;
+import android.net.Uri;
+import android.os.Build;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Spannable;
@@ -36,7 +39,10 @@
import androidx.recyclerview.widget.RecyclerView;
import org.drinkless.tdlib.TdApi;
+import org.thunderdog.challegram.BuildConfig;
+import org.thunderdog.challegram.Log;
import org.thunderdog.challegram.R;
+import org.thunderdog.challegram.U;
import org.thunderdog.challegram.component.base.SettingView;
import org.thunderdog.challegram.config.Config;
import org.thunderdog.challegram.core.Background;
@@ -59,6 +65,7 @@
import org.thunderdog.challegram.tool.TGPhoneFormat;
import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.tool.Views;
+import org.thunderdog.challegram.unsorted.Settings;
import org.thunderdog.challegram.util.CustomTypefaceSpan;
import org.thunderdog.challegram.util.NoUnderlineClickableSpan;
import org.thunderdog.challegram.widget.MaterialEditTextGroup;
@@ -1072,8 +1079,41 @@ private void makeTestRequest () {
@Override
public void onFocus () {
super.onFocus();
- if (mode == MODE_LOGIN && isAccountAdd) {
- context.runEmulatorChecks(true);
+ if (mode == MODE_LOGIN) {
+ context.forceRunEmulatorChecks(detectionResult -> executeOnUiThreadOptional(() -> {
+ if (detectionResult != null && (detectionResult.isEmulatorDetected() || BuildConfig.DEBUG)) {
+ AlertDialog.Builder b = new AlertDialog.Builder(context, Theme.dialogTheme());
+ b.setTitle(Lang.getString(R.string.EmulatorWarningTitle));
+ b.setMessage(Lang.getMarkdownStringSecure(this, R.string.EmulatorWarning));
+ b.setPositiveButton(Lang.getString(R.string.EmulatorWarningBtnOk), (dialog, which) -> dialog.dismiss());
+ b.setNeutralButton(Lang.getString(R.string.EmulatorWarningBtnReport), (dialog, which) -> {
+ try {
+ Uri uri = Uri.parse(BuildConfig.REMOTE_URL);
+ String title = Lang.getString(R.string.EmulatorDetectorReport_title, Build.BRAND, Build.MODEL);
+ String metadata = U.getUsefulMetadata(tdlib);
+ String body = Lang.getString(R.string.EmulatorDetectorReport_text,
+ Build.PRODUCT,
+ Build.DEVICE,
+ Build.HARDWARE,
+ metadata,
+ detectionResult.toHumanReadableFormat()
+ );
+ Uri reportUri = uri
+ .buildUpon()
+ .appendEncodedPath("issues/new")
+ .appendQueryParameter("title", title)
+ .appendQueryParameter("body", body)
+ .build();
+ Intents.openUriInBrowser(reportUri);
+ } catch (Throwable t) {
+ Log.e(t);
+ UI.showToast("Unable to create report: " + Log.toString(t), Toast.LENGTH_SHORT);
+ }
+ });
+ b.setCancelable(false);
+ showAlert(b);
+ }
+ }));
}
if (oneShot) {
return;
diff --git a/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java b/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
index 5d00bacec6..95461d6d31 100644
--- a/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
+++ b/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
@@ -6189,19 +6189,21 @@ public boolean isEmulator () {
}
public static class EmulatorDetectionResult {
- public static final int FLAG_EMULATOR_DETECTED = 1;
+ public final long time, installationId, elapsed, result;
- public final long time, installationId, elapsed, flags;
-
- public EmulatorDetectionResult (long time, long installationId, long elapsed, long flags) {
+ public EmulatorDetectionResult (long time, long installationId, long elapsed, long result) {
this.time = time;
this.installationId = installationId;
this.elapsed = elapsed;
- this.flags = flags;
+ this.result = result;
+ }
+
+ public boolean isEmulatorDetected () {
+ return result != 0;
}
- public boolean isDetected () {
- return BitwiseUtils.hasFlag(flags, FLAG_EMULATOR_DETECTED);
+ public String toHumanReadableFormat () {
+ return Long.toString(result, 16);
}
public long[] toLongArray () {
@@ -6209,7 +6211,7 @@ public long[] toLongArray () {
time,
installationId,
elapsed,
- flags
+ result
};
}
@@ -6235,18 +6237,25 @@ public EmulatorDetectionResult getLastEmulatorDetectionResult () {
return EmulatorDetectionResult.restore(emulatorDetectionResult);
}
- public void trackEmulatorDetectionResult (long installationId, long elapsed, boolean isEmulator) {
+ @NonNull
+ public EmulatorDetectionResult trackEmulatorDetectionResult (long installationId, long elapsed, long emulatorCheckResult) {
EmulatorDetectionResult result = new EmulatorDetectionResult(
System.currentTimeMillis(),
installationId,
elapsed,
- isEmulator ? EmulatorDetectionResult.FLAG_EMULATOR_DETECTED : 0
+ emulatorCheckResult
);
long[] data = result.toLongArray();
pmc.putLongArray(KEY_EMULATOR_DETECTION_RESULT, data);
- if (isEmulator) {
- putBoolean(KEY_IS_EMULATOR, true);
+ boolean wasEmulator = isEmulator();
+ if (wasEmulator != result.isEmulatorDetected()) {
+ if (result.isEmulatorDetected()) {
+ putBoolean(KEY_IS_EMULATOR, true);
+ } else {
+ pmc.remove(KEY_IS_EMULATOR);
+ }
}
+ return result;
}
private List authenticationTokens;
diff --git a/app/src/main/res/values/local_strings.xml b/app/src/main/res/values/local_strings.xml
index 502f99b21e..05525fa9ab 100644
--- a/app/src/main/res/values/local_strings.xml
+++ b/app/src/main/res/values/local_strings.xml
@@ -8,4 +8,7 @@
%1$02d.%2$02d.%3$d, %4$d:%5$02d %6$s
+
+ False-positive emulator detection warning on $1%s `%2$s`
+ Hello. I use **Telegram X** on a real device, but it says I am running on an emulator.\n\n**My real device details:**\n\nProduct: `%1$s`\nDevice: `%2$s`\nHardware: `%3$s`\n\n**The app version I am using:**\n\n%4$s\n\n**Emulator detection result:** `%5$s`\n\nPlease take a look if this warning can be resolved for my device.\nThank you!
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index a2ac615bb9..4308211a66 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5042,7 +5042,7 @@
Winners Selection Date
%1$s at %2$s
- %1$s winner of the **Giveaway** were randomly selected by Telegram.
+ %1$s winner of the **Giveaway** was randomly selected by Telegram.
%1$s winners of the **Giveaway** were randomly selected by Telegram.
%1$s **Telegram Premium** subscription %2$s.
%1$s **Telegram Premium** subscriptions %2$s.
@@ -5115,8 +5115,12 @@
%1$s of the winners already used their gift link.
%1$s of the winners already used their gift links.
-
Close
View my prize
+ You are running **Telegram X** on an **emulated device**.\n\n**Note**: if you don\'t have an **existing** Telegram account, you may need a **real device** to create it.\n\nIn case you see this warning on a real device (such as phone, tablet, etc), press **report issue**.
+ Emulator detected
+ Report issue
+ Got it
+
diff --git a/vkryl/android b/vkryl/android
index 14e7a1bf06..7a054bcb83 160000
--- a/vkryl/android
+++ b/vkryl/android
@@ -1 +1 @@
-Subproject commit 14e7a1bf060b597eeb31df2b7aea5c4eab81b2ba
+Subproject commit 7a054bcb83c652233fbab0397d3884cd96a99d8c
From 9f8de228b955ae1a757fcfeb9c4c312015337f15 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 20 Jan 2024 21:47:59 +0400
Subject: [PATCH 29/61] Improve false-positive emulator detection reporting
---
.../challegram/ui/PhoneController.java | 117 ++++++++++++------
app/src/main/res/values/local_strings.xml | 4 +-
app/src/main/res/values/strings.xml | 2 +
3 files changed, 85 insertions(+), 38 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
index 2687306829..1a868f95ce 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
@@ -68,6 +68,8 @@
import org.thunderdog.challegram.unsorted.Settings;
import org.thunderdog.challegram.util.CustomTypefaceSpan;
import org.thunderdog.challegram.util.NoUnderlineClickableSpan;
+import org.thunderdog.challegram.util.OptionDelegate;
+import org.thunderdog.challegram.util.StringList;
import org.thunderdog.challegram.widget.MaterialEditTextGroup;
import org.thunderdog.challegram.widget.NoScrollTextView;
@@ -78,6 +80,7 @@
import me.vkryl.android.widget.FrameLayoutFix;
import me.vkryl.core.MathUtils;
import me.vkryl.core.StringUtils;
+import me.vkryl.core.collection.IntList;
import me.vkryl.core.lambda.RunnableBool;
import me.vkryl.td.Td;
@@ -1076,45 +1079,87 @@ private void makeTestRequest () {
}
}
+ private void showEmulatorPrompt () {
+ if (mode != MODE_LOGIN) {
+ return;
+ }
+ context.forceRunEmulatorChecks(detectionResult -> executeOnUiThreadOptional(() -> {
+ if (detectionResult != null && (detectionResult.isEmulatorDetected() || BuildConfig.DEBUG)) {
+ AlertDialog.Builder b = new AlertDialog.Builder(context, Theme.dialogTheme());
+ b.setTitle(Lang.getString(R.string.EmulatorWarningTitle));
+ b.setMessage(Lang.getMarkdownStringSecure(this, R.string.EmulatorWarning));
+ b.setPositiveButton(Lang.getString(R.string.EmulatorWarningBtnOk), (dialog, which) -> dialog.dismiss());
+ b.setNeutralButton(Lang.getString(R.string.EmulatorWarningBtnReport), (dialog, which) -> {
+ try {
+ Uri uri = Uri.parse(BuildConfig.REMOTE_URL);
+ String title = Lang.getString(R.string.EmulatorDetectorReport_title, Build.BRAND, Build.MODEL);
+ String metadata = U.getUsefulMetadata(tdlib);
+ String body = Lang.getString(R.string.EmulatorDetectorReport_text,
+ Build.BRAND,
+ Build.MODEL,
+ Build.PRODUCT,
+ Build.DEVICE,
+ Build.HARDWARE,
+ metadata,
+ detectionResult.toHumanReadableFormat()
+ );
+ Uri reportUri = uri
+ .buildUpon()
+ .appendEncodedPath("issues/new")
+ .appendQueryParameter("title", title)
+ .appendQueryParameter("body", body)
+ .build();
+
+ IntList ids = new IntList(3);
+ StringList strings = new StringList(3);
+ IntList icons = new IntList(3);
+ ids.append(R.id.btn_openIn);
+ strings.append(R.string.EmulatorWarningReportBtn);
+ icons.append(R.drawable.baseline_github_24);
+
+ ids.append(R.id.btn_copyLink);
+ strings.append(R.string.CopyLink);
+ icons.append(R.drawable.baseline_link_24);
+
+ if (tdlib.context().hasActiveAccounts()) {
+ ids.append(R.id.btn_share);
+ strings.append(R.string.Share);
+ icons.append(R.drawable.baseline_forward_24);
+ }
+ showOptions(Lang.getMarkdownStringSecure(this, R.string.EmulatorWarningReport), ids.get(), strings.get(), null, icons.get(), (optionItemView, id) -> {
+ if (id == R.id.btn_openIn) {
+ Intents.openUriInBrowser(reportUri);
+ } else if (id == R.id.btn_copyLink) {
+ UI.copyText(reportUri.toString(), R.string.CopiedLink);
+ } else if (id == R.id.btn_share) {
+ String text = reportUri.toString();
+ ShareController c = new ShareController(context, context.currentTdlib());
+ c.setArguments(new ShareController.Args(text).setExport(text));
+ c.show();
+ }
+ return true;
+ });
+ } catch (Throwable t) {
+ Log.e(t);
+ UI.showToast("Unable to create report: " + Log.toString(t), Toast.LENGTH_SHORT);
+ }
+ });
+ b.setCancelable(false);
+ showAlert(b);
+ }
+ }));
+ }
+
+ @Override
+ public void onActivityResume () {
+ super.onActivityResume();
+ showEmulatorPrompt();
+ }
+
@Override
public void onFocus () {
super.onFocus();
- if (mode == MODE_LOGIN) {
- context.forceRunEmulatorChecks(detectionResult -> executeOnUiThreadOptional(() -> {
- if (detectionResult != null && (detectionResult.isEmulatorDetected() || BuildConfig.DEBUG)) {
- AlertDialog.Builder b = new AlertDialog.Builder(context, Theme.dialogTheme());
- b.setTitle(Lang.getString(R.string.EmulatorWarningTitle));
- b.setMessage(Lang.getMarkdownStringSecure(this, R.string.EmulatorWarning));
- b.setPositiveButton(Lang.getString(R.string.EmulatorWarningBtnOk), (dialog, which) -> dialog.dismiss());
- b.setNeutralButton(Lang.getString(R.string.EmulatorWarningBtnReport), (dialog, which) -> {
- try {
- Uri uri = Uri.parse(BuildConfig.REMOTE_URL);
- String title = Lang.getString(R.string.EmulatorDetectorReport_title, Build.BRAND, Build.MODEL);
- String metadata = U.getUsefulMetadata(tdlib);
- String body = Lang.getString(R.string.EmulatorDetectorReport_text,
- Build.PRODUCT,
- Build.DEVICE,
- Build.HARDWARE,
- metadata,
- detectionResult.toHumanReadableFormat()
- );
- Uri reportUri = uri
- .buildUpon()
- .appendEncodedPath("issues/new")
- .appendQueryParameter("title", title)
- .appendQueryParameter("body", body)
- .build();
- Intents.openUriInBrowser(reportUri);
- } catch (Throwable t) {
- Log.e(t);
- UI.showToast("Unable to create report: " + Log.toString(t), Toast.LENGTH_SHORT);
- }
- });
- b.setCancelable(false);
- showAlert(b);
- }
- }));
- }
+ showEmulatorPrompt();
if (oneShot) {
return;
}
diff --git a/app/src/main/res/values/local_strings.xml b/app/src/main/res/values/local_strings.xml
index 05525fa9ab..086abcb693 100644
--- a/app/src/main/res/values/local_strings.xml
+++ b/app/src/main/res/values/local_strings.xml
@@ -9,6 +9,6 @@
%1$02d.%2$02d.%3$d, %4$d:%5$02d %6$s
- False-positive emulator detection warning on $1%s `%2$s`
- Hello. I use **Telegram X** on a real device, but it says I am running on an emulator.\n\n**My real device details:**\n\nProduct: `%1$s`\nDevice: `%2$s`\nHardware: `%3$s`\n\n**The app version I am using:**\n\n%4$s\n\n**Emulator detection result:** `%5$s`\n\nPlease take a look if this warning can be resolved for my device.\nThank you!
+ False-positive emulator detection warning on %1$s \`%2$s\`
+ Hello. I use **Telegram X** on a real device, but it says I am running on an emulator.\n\n**My real device details:**\n\nName: %1$s \`%2$s\`\nProduct: \`%3$s\`\nDevice: \`%4$s\`\nHardware: \`%5$s\`\n\n**The app version I am using:**\n\n%6$s\n\n**Emulator detection result:** `%7$s`\n\nPlease take a look if this warning can be resolved for my device.\nThank you!
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4308211a66..3a750fcbc9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5122,5 +5122,7 @@
Emulator detected
Report issue
Got it
+ **Note**: you will need **GitHub account** to report this issue.\n\nAlternatively you can copy the link and send it to tgx-android@pm.me
+ Report on GitHub
From f090fdb873527aaad41b418905ad0cf1090b28ec Mon Sep 17 00:00:00 2001
From: tgx-server
Date: Sat, 20 Jan 2024 18:14:18 +0000
Subject: [PATCH 30/61] Version bump to `1681`
---
version.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.properties b/version.properties
index 18a2392d87..5cf8d21264 100644
--- a/version.properties
+++ b/version.properties
@@ -1,5 +1,5 @@
# App
-version.app=1680
+version.app=1681
version.major=0
# Anchor date point in app versioning
version.creation=873642600564
From ca1eaad487c462f612c98ce335fe8d24ffe65e93 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Thu, 25 Jan 2024 20:54:04 +0400
Subject: [PATCH 31/61] API for custom bubbles in `BubbleHeaderView`
---
.../component/user/BubbleHeaderView.java | 66 ++--
.../challegram/component/user/BubbleView.java | 329 +++++++++---------
.../component/user/BubbleWrapView.java | 35 +-
.../org/thunderdog/challegram/data/TD.java | 12 +
.../thunderdog/challegram/data/TGUser.java | 8 +
.../challegram/loader/AvatarReceiver.java | 12 +-
.../thunderdog/challegram/telegram/Tdlib.java | 21 ++
.../challegram/ui/ContactsController.java | 133 ++++---
.../ui/SettingsPrivacyKeyController.java | 29 +-
.../util/UserPickerMultiDelegate.java | 4 +-
10 files changed, 369 insertions(+), 280 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/component/user/BubbleHeaderView.java b/app/src/main/java/org/thunderdog/challegram/component/user/BubbleHeaderView.java
index 468ff7af00..5cb3951c82 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/user/BubbleHeaderView.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/user/BubbleHeaderView.java
@@ -25,6 +25,7 @@
import android.widget.FrameLayout;
import android.widget.ScrollView;
+import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import org.thunderdog.challegram.core.Lang;
@@ -32,6 +33,7 @@
import org.thunderdog.challegram.navigation.HeaderView;
import org.thunderdog.challegram.navigation.RtlCheckListener;
import org.thunderdog.challegram.navigation.StretchyHeaderView;
+import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.Views;
import org.thunderdog.challegram.unsorted.Size;
@@ -43,27 +45,32 @@
import me.vkryl.android.widget.FrameLayoutFix;
public class BubbleHeaderView extends FrameLayoutFix implements RtlCheckListener, StretchyHeaderView, TextWatcher, HeaderView.OffsetChangeListener {
- private HeaderEditText editText;
- private ScrollView scrollView;
- private BubbleWrapView bubbleWrap;
+ private final HeaderEditText editText;
+ private final ScrollView scrollView;
+ private final BubbleWrapView bubbleWrap;
+ private final Tdlib tdlib;
Callback callback;
- private ArrayList users;
- private int maxBubbleHeight;
+ private final ArrayList bubbles;
+ private final int maxBubbleHeight;
- public BubbleHeaderView (Context context) {
+ public BubbleHeaderView (Context context, @NonNull Tdlib tdlib) {
super(context);
+ this.tdlib = tdlib;
- users = new ArrayList<>(10);
+ bubbles = new ArrayList<>(10);
FrameLayoutFix.LayoutParams params;
params = FrameLayoutFix.newParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- bubbleWrap = new BubbleWrapView(context);
+ bubbleWrap = new BubbleWrapView(context, tdlib);
bubbleWrap.setHeaderView(this);
bubbleWrap.setLayoutParams(params);
- params = FrameLayoutFix.newParams(ViewGroup.LayoutParams.MATCH_PARENT, maxBubbleHeight = Screen.dp(BubbleWrapView.START_Y) + Screen.dp(BubbleWrapView.SPACING) + Screen.dp(16f) * 4);
+ int bubbleHeight = Screen.dp(BubbleView.RADIUS) * 2;
+ // TODO: expand maxBubbleHeight if users starts scrolling manually, and shrink back with delay once finished
+ this.maxBubbleHeight = Screen.dp(BubbleWrapView.START_Y) + Screen.dp(BubbleWrapView.SPACING) + bubbleHeight * 2;
+ params = FrameLayoutFix.newParams(ViewGroup.LayoutParams.MATCH_PARENT, maxBubbleHeight);
if (Lang.rtl()) {
params.rightMargin = Screen.dp(60f);
} else {
@@ -122,9 +129,17 @@ public void checkRtl () {
}
public void forceUsers (List users) {
+ List entries = new ArrayList<>(users.size());
for (TGUser user : users) {
- this.users.add(user);
- bubbleWrap.addBubbleForce(user);
+ entries.add(BubbleView.Entry.valueOf(tdlib, user));
+ }
+ forceBubbles(entries);
+ }
+
+ public void forceBubbles (List entries) {
+ for (BubbleView.Entry entry : entries) {
+ this.bubbles.add(entry);
+ bubbleWrap.addBubbleForce(entry);
}
bubbleWrap.buildLayout();
@@ -165,25 +180,24 @@ public boolean areBubblesAnimating () {
// public changers
- public void addUser (TGUser user) {
- users.add(user);
- bubbleWrap.addBubble(user);
+ public void addBubble (BubbleView.Entry entry) {
+ bubbles.add(entry);
+ bubbleWrap.addBubble(entry);
}
- private void removeUser (int index) {
- TGUser removed = users.remove(index);
+ private void removeBubbleAt (int index) {
+ BubbleView.Entry removed = bubbles.remove(index);
bubbleWrap.removeBubble(removed);
}
- public void removeUser (TGUser user) {
- long userId = user.getUserId();
- int i = 0;
- for (TGUser u : users) {
- if (u.getUserId() == userId) {
- removeUser(i);
- break;
- }
- i++;
+ private int indexOfEntry (BubbleView.Entry entry) {
+ return bubbles.indexOf(entry);
+ }
+
+ public void removeBubble (BubbleView.Entry entry) {
+ int index = indexOfEntry(entry);
+ if (index != -1) {
+ removeBubbleAt(index);
}
}
@@ -314,7 +328,7 @@ public void setScaleFactor (float scaleFactor, float fromFactor, float toScaleFa
public interface Callback {
View getTranslationView ();
void searchUser (String q);
- void onBubbleRemoved (long chatId);
+ void onBubbleRemoved (@NonNull BubbleView.Entry entry);
void setHeaderOffset (int offset);
void applyHeaderOffset ();
void prepareHeaderOffset (int height);
diff --git a/app/src/main/java/org/thunderdog/challegram/component/user/BubbleView.java b/app/src/main/java/org/thunderdog/challegram/component/user/BubbleView.java
index cdf959304d..05153e96cb 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/user/BubbleView.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/user/BubbleView.java
@@ -14,98 +14,147 @@
*/
package org.thunderdog.challegram.component.user;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.graphics.Canvas;
import android.graphics.RectF;
-import android.text.TextUtils;
import android.view.View;
-import org.thunderdog.challegram.U;
+import androidx.annotation.Nullable;
+
+import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.data.AvatarPlaceholder;
+import org.thunderdog.challegram.data.TD;
import org.thunderdog.challegram.data.TGUser;
-import org.thunderdog.challegram.loader.ImageFile;
-import org.thunderdog.challegram.loader.ImageReceiver;
+import org.thunderdog.challegram.loader.AvatarReceiver;
+import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
+import org.thunderdog.challegram.tool.Views;
+import org.thunderdog.challegram.util.text.Text;
+import org.thunderdog.challegram.util.text.TextColorSet;
+import org.thunderdog.challegram.util.text.TextStyleProvider;
+import org.thunderdog.challegram.widget.AttachDelegate;
import me.vkryl.android.AnimatorUtils;
+import me.vkryl.android.animator.BoolAnimator;
import me.vkryl.core.ColorUtils;
+import me.vkryl.core.StringUtils;
+import me.vkryl.core.lambda.Destroyable;
+import me.vkryl.core.lambda.RunnableData;
+import me.vkryl.td.Td;
-public class BubbleView {
+public class BubbleView implements AttachDelegate, Destroyable {
private static final int FLAG_HIDING = 0x01;
private static final int FLAG_SHOWING = 0x02;
private static final int FLAG_MOVING = 0x04;
- private static final int FLAG_DELETING = 0x08;
private int flags;
- private BubbleWrapView view;
- private TGUser user;
+ private final BubbleWrapView context;
+ private final Entry entry;
+ private final int width, avatarSize, paddingLeft, avatarRadius;
+
+ private final BoolAnimator isDeleting;
- private int width, avatarSize;
+ private final AvatarReceiver receiver;
private int x, y;
- private int paddingLeft;
- private int avatarRadius;
- private int textOffset;
+ private Text displayName;
+
+ public static class Entry {
+ public final Tdlib tdlib;
+ public final String id;
+ public final @Nullable TdApi.MessageSender senderId;
+ public final String name;
+ public final @Nullable String shortName;
+ private final RunnableData avatarLoader;
+
+ public Entry (Tdlib tdlib, String id, @Nullable TdApi.MessageSender senderId, String name, @Nullable String shortName, RunnableData avatarLoader) {
+ this.tdlib = tdlib;
+ this.id = id;
+ this.senderId = senderId;
+ this.name = name;
+ this.shortName = shortName;
+ this.avatarLoader = avatarLoader;
+ }
- private String name;
- private int nameWidth;
+ public static Entry valueOf (Tdlib tdlib, TGUser user) {
+ TdApi.MessageSender sender = user.getSenderId();
+ return new Entry(
+ tdlib, "sender_" + user.getChatId(),
+ sender,
+ user.getName(),
+ user.getShorterName(),
+ receiver ->
+ receiver.requestMessageSender(tdlib, sender, AvatarReceiver.Options.FORCE_IGNORE_FORUM)
+ );
+ }
- private AvatarPlaceholder avatarPlaceholder;
- private ImageFile avatar;
- private ImageReceiver receiver;
+ public static Entry valueOf (Tdlib tdlib, TdApi.MessageSender senderId) {
+ final String name = tdlib.senderName(senderId);
+ final String shortName;
+ TdApi.User user = tdlib.senderUser(senderId);
+ if (user != null) {
+ shortName = TD.getShorterUserNameOrNull(user.firstName, user.lastName);
+ } else {
+ shortName = null;
+ }
+ RunnableData loader = receiver ->
+ receiver.requestMessageSender(tdlib, senderId, AvatarReceiver.Options.FORCE_IGNORE_FORUM);
+ return new Entry(
+ tdlib, "sender_" + Td.getSenderId(senderId), senderId,
+ name, shortName, loader
+ );
+ }
+ }
+
+ public static final float RADIUS = 16f;
- public BubbleView (BubbleWrapView view, TGUser user, int maxTextWidth) {
- this.view = view;
- this.user = user;
+ public BubbleView (BubbleWrapView context, Entry entry, int maxTextWidth) {
+ this.context = context;
+ this.entry = entry;
+ this.isDeleting = new BoolAnimator(context, AnimatorUtils.DECELERATE_INTERPOLATOR, 120l);
paddingLeft = Screen.dp(7f);
int paddingRight = Screen.dp(11f);
- avatarRadius = Screen.dp(16f);
- textOffset = Screen.dp(21f);
+ avatarRadius = Screen.dp(RADIUS);
avatarSize = avatarRadius * 2;
- if ((avatar = user.getAvatar()) == null) {
- avatarPlaceholder = new AvatarPlaceholder(16f, user.getAvatarPlaceholderMetadata(), null);
- }
+ receiver = new AvatarReceiver(context);
- name = user.getName();
- buildName(maxTextWidth);
+ setName(entry.name, entry.shortName, maxTextWidth);
- width = nameWidth + paddingLeft + paddingRight + avatarSize;
+ width = getNameWidth() + paddingLeft + paddingRight + avatarSize;
+ }
- if (avatar != null) {
- receiver = new ImageReceiver(view, avatarRadius);
- }
+ public Entry getEntry () {
+ return entry;
}
- private boolean shortNameAttempt;
-
- private void buildName (int maxWidth) {
- nameWidth = (int) U.measureText(name, view.paint);
- if (nameWidth > maxWidth) {
- if (!shortNameAttempt) {
- String firstName = this.user.getFirstName();
- String lastName = this.user.getLastName();
- if (firstName.length() > 0 && lastName.length() > 0) {
- shortNameAttempt = true;
- name = firstName.charAt(0) + ". " + lastName;
- buildName(maxWidth);
- }
- }
- name = (String) TextUtils.ellipsize(name, view.paint, maxWidth, TextUtils.TruncateAt.END);
- nameWidth = (int) U.measureText(name, view.paint);
+ private int getNameWidth () {
+ return displayName != null ? displayName.getWidth() : 0;
+ }
+
+ private void setName (String name, @Nullable String shorterName, int maxWidth) {
+ TextStyleProvider textStyleProvider = Paints.robotoStyleProvider(14f);
+ TextColorSet textColorSet = () ->
+ Theme.getColor(ColorId.headerText);
+ displayName = new Text.Builder(name, maxWidth, textStyleProvider, textColorSet)
+ .singleLine()
+ .build();
+
+ if (displayName.isEllipsized() && !StringUtils.isEmpty(shorterName)) {
+ displayName = new Text.Builder(shorterName, maxWidth, textStyleProvider, textColorSet)
+ .singleLine()
+ .build();
}
}
public void requestFile () {
- if (receiver != null) {
- receiver.requestFile(avatar);
+ if (entry.avatarLoader != null) {
+ entry.avatarLoader.runWithData(receiver);
+ } else {
+ receiver.clear();
}
}
@@ -125,10 +174,6 @@ public int getY () {
return (flags & FLAG_MOVING) != 0 ? toY : y;
}
- public long getChatId () {
- return user.getChatId();
- }
-
private int toX, diffX, toY, diffY;
public void setXY (int x, int y) {
@@ -149,7 +194,7 @@ private void layoutReceiver () {
int cx = x + (int) ((float) diffX * factor);
int cy = y + (int) ((float) diffY * factor);
if (Lang.rtl()) {
- cx = view.getMeasuredWidth() - cx - avatarSize;
+ cx = context.getMeasuredWidth() - cx - avatarSize;
}
receiver.setBounds(cx, cy, cx + avatarSize, cy + avatarSize);
}
@@ -204,8 +249,6 @@ public void draw (Canvas c, View parentView) {
int cx, cy;
float scale;
-
-
if ((flags & FLAG_MOVING) != 0) {
cx = x + (int) ((float) diffX * factor);
cy = y + (int) ((float) diffY * factor);
@@ -227,139 +270,95 @@ public void draw (Canvas c, View parentView) {
}
final boolean savedScale = scale != 1f;
+ final int scaleSaveCount;
if (savedScale) {
- c.save();
+ scaleSaveCount = Views.save(c);
float vScale = 1f - (1f - scale) * .65f;
c.scale(vScale, vScale, cx + (float) width * .5f, cy + avatarRadius);
+ } else {
+ scaleSaveCount = -1;
}
- // int alpha = (int) (255f * scale);
- boolean deleting = (deleteFactor != 0f && (flags & FLAG_DELETING) != 0);
-
- // view.paint.setColor(deleting ? changer.getColor(deleteFactor) : TGTheme.headerPlaceholderColor());
- view.paint.setColor(ColorUtils.alphaColor(scale, ColorUtils.fromToArgb(ColorUtils.compositeColor(Theme.headerColor(), Theme.headerPlaceholderColor()), Theme.getColor(ColorId.headerRemoveBackground), deleting ? deleteFactor : 0f)));
- // view.paint.setAlpha(alpha);
+ final int headerColor = Theme.headerColor();
+ final int headerPlaceholderColor = Theme.headerPlaceholderColor();
+ final int headerDeleteColor = Theme.getColor(ColorId.headerRemoveBackground);
+ final int headerOverlayColor = ColorUtils.compositeColor(headerColor, headerPlaceholderColor);
+ final int headerDeleteHighlightColor = Theme.getColor(ColorId.headerRemoveBackgroundHighlight);
+ final float deleteFactor = this.isDeleting.getFloatValue();
+ final boolean isDeleting = deleteFactor != 0f;
+ final int avatarBackgroundColor = ColorUtils.alphaColor(scale,
+ ColorUtils.fromToArgb(
+ headerOverlayColor,
+ headerDeleteHighlightColor,
+ deleteFactor
+ )
+ );
+ final int bubbleBackgroundColor = ColorUtils.alphaColor(
+ scale, ColorUtils.fromToArgb(
+ headerOverlayColor,
+ headerDeleteColor, deleteFactor
+ )
+ );
+ context.paint.setColor(bubbleBackgroundColor);
RectF rectF = Paints.getRectF();
rectF.set(cx, cy, cx + width, cy + avatarSize);
- c.drawRoundRect(rectF, avatarRadius, avatarRadius, view.paint);
- //c.drawRect(cx + avatarRadius, cy, cx + width - avatarRadius, cy + avatarSize, view.paint);
- //c.drawCircle(cx + width - avatarRadius, cy + avatarRadius, avatarRadius, view.paint);
+ c.drawRoundRect(rectF, avatarRadius, avatarRadius, context.paint);
- view.paint.setColor(ColorUtils.color((int) (255f * scale), 0xffffffff));
- if (name != null) {
- c.drawText(name, Lang.rtl() ? cx + width - avatarSize - paddingLeft - nameWidth : cx + avatarSize + paddingLeft, cy + textOffset, view.paint);
+ if (displayName != null) {
+ int startX = Lang.rtl() ? cx + width - avatarSize - paddingLeft - displayName.getWidth() : cx + avatarSize + paddingLeft;
+ int startY = cy + avatarSize / 2 - displayName.getLineHeight(false) / 2;
+ displayName.draw(c, startX, startY, null, scale);
}
int circleX = Lang.rtl() ? cx + width - avatarRadius : cx + avatarRadius;
- if (receiver != null) {
- layoutReceiver();
- if (receiver.needPlaceholder()) {
- view.paint.setColor(ColorUtils.alphaColor(scale, ColorUtils.fromToArgb(ColorUtils.compositeColor(Theme.headerColor(), Theme.headerPlaceholderColor()), Theme.getColor(ColorId.headerRemoveBackgroundHighlight), deleting ? deleteFactor : 0f)));
- c.drawCircle(receiver.centerX(), receiver.centerY(), avatarRadius, view.paint);
- } else if (deleting) {
- view.paint.setColor(ColorUtils.alphaColor(scale, Theme.getColor(ColorId.headerRemoveBackgroundHighlight)));
- c.drawCircle(receiver.centerX(), receiver.centerY(), avatarRadius, view.paint);
- }
- receiver.setPaintAlpha(deleting ? scale * (1f - deleteFactor) : scale);
- if (deleting) {
- c.save();
- c.rotate(45f * (Lang.rtl() ? 1f : -1f) * deleteFactor, receiver.centerX(), receiver.centerY());
- }
- receiver.draw(c);
- if (deleting) {
- c.restore();
- }
- receiver.restorePaintAlpha();
- } else if (avatarPlaceholder != null) {
- if (deleting) {
- c.save();
- c.rotate(45f * (Lang.rtl() ? 1f : -1f) * deleteFactor, circleX, cy + avatarRadius);
-
- view.paint.setColor(ColorUtils.alphaColor(scale, Theme.getColor(ColorId.headerRemoveBackgroundHighlight)));
- c.drawCircle(circleX, cy + avatarRadius, avatarRadius, view.paint);
- }
- avatarPlaceholder.draw(c, circleX, cy + avatarRadius, scale * (1f - deleteFactor));
- if (deleting) {
- c.restore();
- }
+ layoutReceiver();
+ if (receiver.needPlaceholder() || isDeleting) {
+ receiver.drawPlaceholderRounded(c, avatarRadius, avatarBackgroundColor);
}
+ int saveCount = isDeleting ? Views.save(c) : -1;
+ if (isDeleting) {
+ c.rotate(45f * (Lang.rtl() ? 1f : -1f) * deleteFactor, receiver.centerX(), receiver.centerY());
+ }
+ receiver.setPaintAlpha(isDeleting ? scale * (1f - deleteFactor) : scale);
+ receiver.draw(c);
+ if (isDeleting) {
+ Views.restore(c, saveCount);
+ }
+ receiver.restorePaintAlpha();
- if (deleting) {
- c.save();
+ if (isDeleting) {
+ saveCount = Views.save(c);
c.rotate(90f + 45f * (Lang.rtl() ? 1f : -1f) * deleteFactor, circleX, cy + avatarRadius);
- view.paint.setColor(ColorUtils.color((int) (255f * scale * deleteFactor), 0xffffffff));
- c.drawRect(circleX - view.deleteIconWidth, cy + avatarRadius - view.deleteIconStroke, circleX + view.deleteIconWidth, cy + avatarRadius + view.deleteIconStroke, view.paint);
- c.drawRect(circleX - view.deleteIconStroke, cy + avatarRadius - view.deleteIconWidth, circleX + view.deleteIconStroke, cy + avatarRadius + view.deleteIconWidth, view.paint);
+ context.paint.setColor(ColorUtils.color((int) (255f * scale * deleteFactor), 0xffffffff));
+ c.drawRect(circleX - context.deleteIconWidth, cy + avatarRadius - context.deleteIconStroke, circleX + context.deleteIconWidth, cy + avatarRadius + context.deleteIconStroke, context.paint);
+ c.drawRect(circleX - context.deleteIconStroke, cy + avatarRadius - context.deleteIconWidth, circleX + context.deleteIconStroke, cy + avatarRadius + context.deleteIconWidth, context.paint);
- c.restore();
+ Views.restore(c, saveCount);
}
if (savedScale) {
- c.restore();
- }
- }
-
- // Deletion
-
- private float deleteFactor;
-
- public void setDeleteFactor (float deleteFactor) {
- if (this.deleteFactor != deleteFactor) {
- this.deleteFactor = deleteFactor;
- view.invalidate(x, y, x + width, y + avatarSize);
+ Views.restore(c, scaleSaveCount);
}
}
- public float getDeleteFactor () {
- return deleteFactor;
- }
-
- public void startDeletion () {
- flags |= FLAG_DELETING;
-
- final float startFactor = getDeleteFactor();
- final float diffFactor = 1f - startFactor;
- ValueAnimator obj;
- obj = AnimatorUtils.simpleValueAnimator();
- obj.addUpdateListener(animation -> setDeleteFactor(startFactor + diffFactor * AnimatorUtils.getFraction(animation)));
- obj.setInterpolator(AnimatorUtils.DECELERATE_INTERPOLATOR);
- obj.setDuration(120l);
- obj.start();
- }
-
- public void cancelDeletion () {
- final float startFactor = getDeleteFactor();
- ValueAnimator obj;
- obj = AnimatorUtils.simpleValueAnimator();
- obj.addUpdateListener(animation -> setDeleteFactor(startFactor - startFactor * AnimatorUtils.getFraction(animation)));
- obj.setInterpolator(AnimatorUtils.DECELERATE_INTERPOLATOR);
- obj.setDuration(120l);
- obj.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd (Animator animation) {
- flags &= ~FLAG_DELETING;
- }
- });
- obj.start();
+ public void setIsDeleting (boolean isDeleting, boolean animated) {
+ this.isDeleting.setValue(isDeleting, animated);
}
- public void destroy () {
- if (receiver != null) {
- receiver.requestFile(null);
- }
+ @Override
+ public void performDestroy () {
+ receiver.destroy();
}
- public void onAttachedToWindow () {
- if (receiver != null) {
- receiver.attach();
- }
+ @Override
+ public void attach () {
+ receiver.attach();
}
- public void onDetachedFromWindow () {
- if (receiver != null) {
- receiver.detach();
- }
+ @Override
+ public void detach () {
+ receiver.detach();
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/thunderdog/challegram/component/user/BubbleWrapView.java b/app/src/main/java/org/thunderdog/challegram/component/user/BubbleWrapView.java
index 6d54e7c1fe..f08dae6429 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/user/BubbleWrapView.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/user/BubbleWrapView.java
@@ -24,9 +24,11 @@
import android.view.MotionEvent;
import android.view.View;
+import androidx.annotation.NonNull;
+
import org.thunderdog.challegram.core.Background;
import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.data.TGUser;
+import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.tool.Fonts;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.UI;
@@ -44,10 +46,10 @@ public class BubbleWrapView extends View {
static final float START_Y = 12f;
static final float SPACING = 8f;
- private ArrayList bubbles;
+ private final ArrayList bubbles;
private BubbleHeaderView headerView;
- public BubbleWrapView (Context context) {
+ public BubbleWrapView (Context context, @NonNull Tdlib tdlib) {
super(context);
bubbles = new ArrayList<>(10);
paint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.FILTER_BITMAP_FLAG);
@@ -60,7 +62,7 @@ public void setHeaderView (BubbleHeaderView headerView) {
this.headerView = headerView;
}
- public void addBubbleForce (final TGUser user) {
+ public void addBubbleForce (final BubbleView.Entry entry) {
int defaultWidth = Screen.dp(100f);
int maxWidth = (int) ((float) (Screen.smallestSide() - Screen.dp(60f)) * .5f) - Screen.dp(SPACING) - Screen.dp(44f);
@@ -73,7 +75,7 @@ public void addBubbleForce (final TGUser user) {
maxTextWidth = maxWidth;
}
- BubbleView view = new BubbleView(this, user, maxTextWidth);
+ BubbleView view = new BubbleView(this, entry, maxTextWidth);
if (bubbles.size() == 0) {
view.setXY(Screen.dp(START_X), Screen.dp(START_Y));
@@ -95,7 +97,7 @@ public void addBubbleForce (final TGUser user) {
// final BubbleView view = new BubbleView(BubbleWrapView.this, user, Scree);
}
- public void addBubble (final TGUser user) {
+ public void addBubble (final BubbleView.Entry entry) {
isAnimating = true;
changingHeight = false;
@@ -112,7 +114,7 @@ public void addBubble (final TGUser user) {
maxTextWidth = maxWidth;
}
- final BubbleView view = new BubbleView(BubbleWrapView.this, user, maxTextWidth);
+ final BubbleView view = new BubbleView(BubbleWrapView.this, entry, maxTextWidth);
UI.post(() -> {
view.requestFile();
@@ -174,11 +176,10 @@ public void onAnimationEnd (Animator animation) {
});
}
- public void removeBubble (TGUser user) {
- long chatId = user.getChatId();
+ public void removeBubble (@NonNull BubbleView.Entry entry) {
int i = 0;
for (BubbleView view : bubbles) {
- if (view.getChatId() == chatId) {
+ if (entry.equals(view.getEntry())) {
hideAnimated(i, false);
break;
}
@@ -241,7 +242,7 @@ public void onAnimationEnd (Animator animation) {
protected void onAttachedToWindow () {
super.onAttachedToWindow();
for (BubbleView view : bubbles) {
- view.onAttachedToWindow();
+ view.attach();
}
}
@@ -249,7 +250,7 @@ protected void onAttachedToWindow () {
protected void onDetachedFromWindow () {
super.onDetachedFromWindow();
for (BubbleView view : bubbles) {
- view.onDetachedFromWindow();
+ view.detach();
}
}
@@ -268,13 +269,13 @@ public void deleteBubble () {
bubbles.get(i).completeMove();
}
BubbleView view = bubbles.remove(rangeStart);
- view.destroy();
+ view.performDestroy();
rangeStart = rangeEnd = 0;
}
public void destroy () {
for (BubbleView bubble : bubbles) {
- bubble.destroy();
+ bubble.performDestroy();
}
}
@@ -370,7 +371,7 @@ public float getFactor () {
private int startX, startY;
private void clearTouch () {
- bubbles.get(caughtIndex).cancelDeletion();
+ bubbles.get(caughtIndex).setIsDeleting(false, true);
caughtIndex = -1;
}
@@ -379,7 +380,7 @@ private void completeTouch () {
BubbleView view = bubbles.get(caughtIndex);
hideAnimated(caughtIndex, true);
if (headerView.callback != null) {
- headerView.callback.onBubbleRemoved(view.getChatId());
+ headerView.callback.onBubbleRemoved(view.getEntry());
}
}
@@ -417,7 +418,7 @@ public boolean onTouchEvent (MotionEvent e) {
caughtIndex = i;
deleteIconStroke = Screen.dp(1f);
deleteIconWidth = Screen.dp(7f);
- view.startDeletion();
+ view.setIsDeleting(true, true);
break;
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TD.java b/app/src/main/java/org/thunderdog/challegram/data/TD.java
index 5096b30918..747fc0bf7e 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TD.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TD.java
@@ -1614,6 +1614,18 @@ public static String getUserSingleName (long userId, @Nullable TdApi.User user)
return getFirstName(user);
}
+ @Nullable
+ public static String getShorterUserNameOrNull (String firstName, String lastName) {
+ if (!StringUtils.isEmpty(firstName) && !StringUtils.isEmpty(lastName) && firstName.codePointCount(0, firstName.length()) > 1) {
+ return new StringBuilder()
+ .appendCodePoint(firstName.codePointAt(0))
+ .append(". ")
+ .append(lastName)
+ .toString();
+ }
+ return null;
+ }
+
public static Letters getLetters () {
return getLetters(null, null, "?");
}
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGUser.java b/app/src/main/java/org/thunderdog/challegram/data/TGUser.java
index 13b95ad361..088ab8b467 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGUser.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGUser.java
@@ -242,6 +242,10 @@ public TdApi.MessageSender getSenderId () {
}
}
+ public boolean belongsToSenderId (@NonNull TdApi.MessageSender senderId) {
+ return getChatId() == Td.getSenderId(senderId);
+ }
+
public void setChat (long chatId, @Nullable TdApi.Chat chat) {
this.user = null;
this.chatId = chatId;
@@ -347,6 +351,10 @@ public String getName () {
return nameText;
}
+ public String getShorterName () {
+ return TD.getShorterUserNameOrNull(getFirstName(), getLastName());
+ }
+
public float getNameWidth () {
return nameWidth;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/loader/AvatarReceiver.java b/app/src/main/java/org/thunderdog/challegram/loader/AvatarReceiver.java
index 933efb4b2a..527871c9cc 100644
--- a/app/src/main/java/org/thunderdog/challegram/loader/AvatarReceiver.java
+++ b/app/src/main/java/org/thunderdog/challegram/loader/AvatarReceiver.java
@@ -94,7 +94,8 @@ public FullChatPhoto (@NonNull TdApi.ChatPhoto chatPhoto, long chatId) {
Options.FORCE_ANIMATION,
Options.FORCE_FORUM,
Options.NO_UPDATES,
- Options.SHOW_ONLINE
+ Options.SHOW_ONLINE,
+ Options.FORCE_IGNORE_FORUM
}, flag = true)
public @interface Options {
int
@@ -103,7 +104,8 @@ public FullChatPhoto (@NonNull TdApi.ChatPhoto chatPhoto, long chatId) {
FORCE_ANIMATION = 1 << 1,
FORCE_FORUM = 1 << 2,
NO_UPDATES = 1 << 3,
- SHOW_ONLINE = 1 << 4
+ SHOW_ONLINE = 1 << 4,
+ FORCE_IGNORE_FORUM = 1 << 5
;
}
@@ -520,17 +522,17 @@ private void updateForumState (boolean isUpdate) {
break;
}
case DataType.SPECIFIC_PHOTO: {
- setIsForum(BitwiseUtils.hasFlag(options, Options.FORCE_FORUM) || (specificPhoto != null && tdlib.isForum(specificPhoto.chatId)), isUpdate);
+ setIsForum(!BitwiseUtils.hasFlag(options, Options.FORCE_IGNORE_FORUM) && (BitwiseUtils.hasFlag(options, Options.FORCE_FORUM) || (specificPhoto != null && tdlib.isForum(specificPhoto.chatId))), isUpdate);
break;
}
case DataType.SPECIFIC_FILE:
case DataType.PLACEHOLDER:
case DataType.USER: {
- setIsForum(BitwiseUtils.hasFlag(options, Options.FORCE_FORUM), isUpdate);
+ setIsForum(!BitwiseUtils.hasFlag(options, Options.FORCE_IGNORE_FORUM) && BitwiseUtils.hasFlag(options, Options.FORCE_FORUM), isUpdate);
break;
}
case DataType.CHAT: {
- setIsForum(BitwiseUtils.hasFlag(options, Options.FORCE_FORUM) || tdlib.isForum(dataId), isUpdate);
+ setIsForum(!BitwiseUtils.hasFlag(options, Options.FORCE_IGNORE_FORUM) && (BitwiseUtils.hasFlag(options, Options.FORCE_FORUM) || tdlib.isForum(dataId)), isUpdate);
break;
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
index 8c6c7f70aa..3f3c21dbb1 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
@@ -3717,6 +3717,27 @@ public String sponsorName (TdApi.MessageSponsor sponsor) {
}
}
+ public long senderUserId (@NonNull TdApi.MessageSender senderId) {
+ switch (senderId.getConstructor()) {
+ case TdApi.MessageSenderChat.CONSTRUCTOR: {
+ long chatId = ((TdApi.MessageSenderChat) senderId).chatId;
+ return chatUserId(chatId);
+ }
+ case TdApi.MessageSenderUser.CONSTRUCTOR: {
+ return ((TdApi.MessageSenderUser) senderId).userId;
+ }
+ default: {
+ Td.assertMessageSender_439d4c9c();
+ throw Td.unsupported(senderId);
+ }
+ }
+ }
+
+ public @Nullable TdApi.User senderUser (@NonNull TdApi.MessageSender senderId) {
+ long userId = senderUserId(senderId);
+ return userId != 0 ? cache().user(userId) : null;
+ }
+
public String senderName (TdApi.Message msg, boolean allowForward, boolean shorten) {
long authorId = Td.getMessageAuthorId(msg, allowForward);
if (authorId == 0 && allowForward) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ContactsController.java b/app/src/main/java/org/thunderdog/challegram/ui/ContactsController.java
index bf4b62c274..2693c4116b 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ContactsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ContactsController.java
@@ -28,6 +28,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.collection.SparseArrayCompat;
@@ -39,6 +40,7 @@
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.component.dialogs.SearchManager;
import org.thunderdog.challegram.component.user.BubbleHeaderView;
+import org.thunderdog.challegram.component.user.BubbleView;
import org.thunderdog.challegram.component.user.UserView;
import org.thunderdog.challegram.core.Background;
import org.thunderdog.challegram.core.Lang;
@@ -78,7 +80,7 @@
import me.vkryl.android.widget.FrameLayoutFix;
import me.vkryl.core.StringUtils;
-import me.vkryl.td.ChatId;
+import me.vkryl.core.collection.LongList;
import me.vkryl.td.Td;
public class ContactsController extends TelegramViewController implements OptionDelegate, BubbleHeaderView.Callback, TextWatcher, Runnable, Menu, Unlockable,
@@ -137,7 +139,7 @@ public ContactsController (Context context, Tdlib tdlib) {
private DoubleHeaderView addMemberHeaderView;
private TdApi.MessageSender pickedSenderId;
- private List pickedChats;
+ private List pickedBubbles;
private int mode;
private int sourceType;
@@ -159,25 +161,19 @@ public void initWithMode (int mode) {
case MODE_IMPORT: {
if (mode == MODE_MULTI_PICK) {
long[] chatIds = multiDelegate.getAlreadySelectedChatIds();
- pickedChats = new ArrayList<>(chatIds != null ? chatIds.length : 10);
+ pickedBubbles = new ArrayList<>(chatIds != null ? chatIds.length : 10);
if (chatIds != null) {
for (long chatId : chatIds) {
- long userId = ChatId.toUserId(chatId);
+ long userId = tdlib.chatUserId(chatId);
if (userId != 0) {
- TdApi.User user = tdlib.cache().user(userId);
- if (user != null) {
- pickedChats.add(new TGUser(tdlib, user));
- }
+ pickedBubbles.add(BubbleView.Entry.valueOf(tdlib, new TdApi.MessageSenderUser(userId)));
} else {
- TdApi.Chat chat = tdlib.chat(chatId);
- if (chat != null) {
- pickedChats.add(new TGUser(tdlib, chat));
- }
+ pickedBubbles.add(BubbleView.Entry.valueOf(tdlib, new TdApi.MessageSenderChat(chatId)));
}
}
}
} else {
- pickedChats = new ArrayList<>(10);
+ pickedBubbles = new ArrayList<>(10);
}
break;
}
@@ -302,11 +298,11 @@ public void onScrollStateChanged (RecyclerView recyclerView, int newState) {
addMemberHeaderView.setTitle(titleRes);
addMemberHeaderView.setSubtitle(chatTitle);
} else if (hasBubbles()) {
- headerCell = new BubbleHeaderView(context);
+ headerCell = new BubbleHeaderView(context, tdlib);
headerCell.setHint(bindLocaleChanger(mode == MODE_MULTI_PICK ? multiDelegate.provideMultiUserPickerHint() : R.string.SendMessageTo, headerCell.getInput(), true, false));
headerCell.setCallback(this);
- if (pickedChats != null && pickedChats.size() > 0) {
- headerCell.forceUsers(pickedChats);
+ if (pickedBubbles != null && !pickedBubbles.isEmpty()) {
+ headerCell.forceBubbles(pickedBubbles);
headerOffset = headerCell.getCurrentWrapHeight();
recyclerView.setTranslationY(headerOffset);
((FrameLayoutFix.LayoutParams) recyclerView.getLayoutParams()).bottomMargin = headerOffset;
@@ -333,7 +329,7 @@ public void onScrollStateChanged (RecyclerView recyclerView, int newState) {
if (needChatSearch()) {
RecyclerView recyclerView = generateChatSearchView(contentView);
- if (pickedChats != null && pickedChats.size() > 0) {
+ if (pickedBubbles != null && !pickedBubbles.isEmpty()) {
recyclerView.setTranslationY(headerOffset);
((FrameLayoutFix.LayoutParams) recyclerView.getLayoutParams()).bottomMargin = headerOffset;
}
@@ -573,7 +569,7 @@ protected int getBackButton () {
@Override
protected int getFloatingButtonId () {
- return mode == MODE_ADD_MEMBER || mode == MODE_MULTI_PICK ? 0 : mode == MODE_PICK || mode == MODE_NEW_CHAT || mode == MODE_CALL || mode == MODE_NEW_SECRET_CHAT || pickedChats.size() == 0 ? 0 : mode == MODE_CHANNEL_MEMBERS || mode == MODE_MULTI_PICK ? R.drawable.baseline_check_24 : R.drawable.baseline_arrow_forward_24;
+ return mode == MODE_ADD_MEMBER || mode == MODE_MULTI_PICK ? 0 : mode == MODE_PICK || mode == MODE_NEW_CHAT || mode == MODE_CALL || mode == MODE_NEW_SECRET_CHAT || pickedBubbles.isEmpty() ? 0 : mode == MODE_CHANNEL_MEMBERS || mode == MODE_MULTI_PICK ? R.drawable.baseline_check_24 : R.drawable.baseline_arrow_forward_24;
}
@Override
@@ -591,7 +587,7 @@ protected void onFloatingButtonPressed () {
}
private void createChannel () {
- int size = pickedChats.size();
+ int size = pickedBubbles.size();
if (size == 0 || creatingChat) {
return;
@@ -600,10 +596,15 @@ private void createChannel () {
setStackLocked(true);
creatingChat = true;
- long[] userIds = new long[size];
+ LongList userIdsList = new LongList(size);
for (int i = 0; i < size; i++) {
- userIds[i] = pickedChats.get(i).getUserId();
+ TdApi.MessageSender senderId = pickedBubbles.get(i).senderId;
+ long userId = tdlib.senderUserId(senderId);
+ if (userId != 0) {
+ userIdsList.append(userId);
+ }
}
+ long[] userIds = userIdsList.get();
tdlib.send(new TdApi.AddChatMembers(chat.id, userIds), (ok, error) -> {
if (error != null) {
@@ -625,7 +626,7 @@ public void unlock () {
private boolean creatingChat;
private void createGroup () {
- int size = pickedChats.size();
+ int size = pickedBubbles.size();
if (size == 0 || creatingChat) {
return;
@@ -635,8 +636,10 @@ private void createGroup () {
creatingChat = true;
final ArrayList users = new ArrayList<>(size);
- for (int i = 0; i < size; i++) {
- users.add(pickedChats.get(i));
+ for (TGUser user : this.users) {
+ if (isSelected(user)) {
+ users.add(user);
+ }
}
Background.instance().post(() -> {
@@ -665,7 +668,7 @@ private void createGroup () {
// Multi-user
private boolean isSelected (TGUser u) {
- return canSelectContacts() && indexOfSelectedChat(u.getChatId()) >= 0;
+ return canSelectContacts() && indexOfSelectedChat(u.getChatId()) != -1;
}
private boolean selectUser (TGUser u, UserView v) {
@@ -683,32 +686,33 @@ private boolean selectUser (TGUser u, UserView v) {
}
int selectedIndex = indexOfSelectedChat(u.getChatId());
if (canSelectContacts() && selectedIndex >= 0) {
- pickedChats.remove(selectedIndex);
+ BubbleView.Entry removedEntry = pickedBubbles.remove(selectedIndex);
if (v != null)
v.setChecked(false, true);
if (hasBubbles()) {
- headerCell.removeUser(u);
+ headerCell.removeBubble(removedEntry);
}
- if (pickedChats.size() == 0) {
+ if (pickedBubbles.isEmpty()) {
if (floatingButton != null) {
floatingButton.hide();
}
}
} else {
- int nextSize = pickedChats.size() + 1;
+ int nextSize = pickedBubbles.size() + 1;
if (mode == MODE_NEW_GROUP) {
if (nextSize >= tdlib.supergroupMaxSize()) {
context.tooltipManager().builder(v).show(this, tdlib, R.drawable.baseline_error_24, Lang.pluralBold(R.string.ParticipantXLimitReached, tdlib.supergroupMaxSize()));
return false;
}
}
- pickedChats.add(u);
+ BubbleView.Entry entry = BubbleView.Entry.valueOf(tdlib, u);
+ pickedBubbles.add(entry);
if (v != null)
v.setChecked(true, true);
if (hasBubbles()) {
- headerCell.addUser(u);
+ headerCell.addBubble(entry);
}
- if (pickedChats.size() == 1) {
+ if (pickedBubbles.size() == 1) {
if (floatingButton != null && getFloatingButtonId() != 0) {
floatingButton.show(this);
}
@@ -719,7 +723,7 @@ private boolean selectUser (TGUser u, UserView v) {
// hideSoftwareKeyboard();
}
if (mode == MODE_MULTI_PICK) {
- multiDelegate.onAlreadyPickedChatsChanged(pickedChats);
+ multiDelegate.onAlreadyPickedChatsChanged(pickedBubbles);
}
if (viewIndex != -1) {
adapter.notifyItemChanged(viewIndex);
@@ -963,45 +967,66 @@ private void searchInternal (final String q, boolean optimize) {
}
}
+ private int indexOfSelectedChat (BubbleView.Entry entry) {
+ return pickedBubbles != null ? pickedBubbles.indexOf(entry) : -1;
+ }
+
private int indexOfSelectedChat (long chatId) {
- for (int index = 0; index < pickedChats.size(); index++) {
- if (pickedChats.get(index).getChatId() == chatId) {
+ if (pickedBubbles != null) {
+ for (int index = 0; index < pickedBubbles.size(); index++) {
+ BubbleView.Entry entry = pickedBubbles.get(index);
+ if (entry.senderId != null && Td.getSenderId(entry.senderId) == chatId) {
+ return index;
+ }
+ }
+ }
+ return -1;
+ }
+
+ private int indexOfSenderId (TdApi.MessageSender senderId) {
+ if (senderId == null) {
+ return -1;
+ }
+ int index = 0;
+ for (TGUser user : users) {
+ if (user.belongsToSenderId(senderId)) {
return index;
}
+ index++;
}
return -1;
}
@Override
- public void onBubbleRemoved (long chatId) {
- int index = indexOfSelectedChat(chatId);
+ public void onBubbleRemoved (@NonNull BubbleView.Entry entry) {
+ if (entry.senderId == null) {
+ // ContactsController doesn't support custom bubbles, because there was no need
+ return;
+ }
+ int index = indexOfSelectedChat(entry);
if (index != -1) {
- pickedChats.remove(index);
- if (pickedChats.size() == 0 && floatingButton != null) {
+ pickedBubbles.remove(index);
+ if (pickedBubbles.isEmpty() && floatingButton != null) {
floatingButton.hide();
}
- int i = 0;
- for (TGUser user : users) {
- if (user.getChatId() == chatId) {
- View v = recyclerView.getLayoutManager().findViewByPosition(i);
+ int userIndex = indexOfSenderId(entry.senderId);
+ if (userIndex != -1) {
+ View v = recyclerView.getLayoutManager().findViewByPosition(userIndex);
- if (v != null && v instanceof UserView) {
- UserView u = (UserView) v;
- if (u.getUser().getChatId() == chatId) {
- u.setChecked(false, true);
- break;
- }
+ if (v instanceof UserView) {
+ UserView u = (UserView) v;
+ TGUser user = u.getUser();
+ if (user != null && user.belongsToSenderId(entry.senderId)) {
+ u.setChecked(false, true);
}
-
- adapter.notifyItemChanged(i);
- break;
}
- i++;
+
+ adapter.notifyItemChanged(userIndex);
}
if (mode == MODE_MULTI_PICK) {
- multiDelegate.onAlreadyPickedChatsChanged(pickedChats);
+ multiDelegate.onAlreadyPickedChatsChanged(pickedBubbles);
}
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyKeyController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyKeyController.java
index 8e81912580..1b89d222ce 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyKeyController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsPrivacyKeyController.java
@@ -29,8 +29,8 @@
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.component.base.SettingView;
import org.thunderdog.challegram.component.dialogs.SearchManager;
+import org.thunderdog.challegram.component.user.BubbleView;
import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.data.TGUser;
import org.thunderdog.challegram.navigation.ActivityResultHandler;
import org.thunderdog.challegram.telegram.PrivacySettings;
import org.thunderdog.challegram.telegram.PrivacySettingsListener;
@@ -52,7 +52,6 @@
import me.vkryl.core.StringUtils;
import me.vkryl.core.collection.LongList;
-import me.vkryl.td.ChatId;
import me.vkryl.td.Td;
public class SettingsPrivacyKeyController extends RecyclerViewController implements View.OnClickListener, UserPickerMultiDelegate, PrivacySettingsListener, ActivityResultHandler,
@@ -593,15 +592,23 @@ public int provideMultiUserPickerHint () {
}
@Override
- public void onAlreadyPickedChatsChanged (List users) {
- LongList userIds = new LongList(users.size());
- LongList chatIds = new LongList(users.size());
- for (TGUser user : users) {
- long chatId = user.getChatId();
- if (ChatId.isPrivate(chatId)) {
- userIds.append(ChatId.toUserId(chatId));
- } else {
- chatIds.append(chatId);
+ public void onAlreadyPickedChatsChanged (List bubbles) {
+ LongList userIds = new LongList(bubbles.size());
+ LongList chatIds = new LongList(bubbles.size());
+ for (BubbleView.Entry entry : bubbles) {
+ if (entry.senderId == null) {
+ continue;
+ }
+ switch (entry.senderId.getConstructor()) {
+ case TdApi.MessageSenderChat.CONSTRUCTOR:
+ chatIds.append(((TdApi.MessageSenderChat) entry.senderId).chatId);
+ break;
+ case TdApi.MessageSenderUser.CONSTRUCTOR:
+ userIds.append(((TdApi.MessageSenderUser) entry.senderId).userId);
+ break;
+ default:
+ Td.assertMessageSender_439d4c9c();
+ throw Td.unsupported(entry.senderId);
}
}
if (userPickMode == R.id.btn_alwaysAllow) {
diff --git a/app/src/main/java/org/thunderdog/challegram/util/UserPickerMultiDelegate.java b/app/src/main/java/org/thunderdog/challegram/util/UserPickerMultiDelegate.java
index c068e65a43..3b3b106387 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/UserPickerMultiDelegate.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/UserPickerMultiDelegate.java
@@ -14,12 +14,12 @@
*/
package org.thunderdog.challegram.util;
-import org.thunderdog.challegram.data.TGUser;
+import org.thunderdog.challegram.component.user.BubbleView;
import java.util.List;
public interface UserPickerMultiDelegate {
long[] getAlreadySelectedChatIds ();
- void onAlreadyPickedChatsChanged (List chats);
+ void onAlreadyPickedChatsChanged (List entries);
int provideMultiUserPickerHint ();
}
From 7c403e026b8a09d5008504201b14351390e74394 Mon Sep 17 00:00:00 2001
From: Kira Roubin
Date: Sat, 27 Jan 2024 00:36:53 +0300
Subject: [PATCH 32/61] refactor: Client settings structure
---
.../mgx/ui/ChatsSettingsMoexController.java | 106 -----
.../mgx/ui/GeneralSettingsMoexController.java | 132 ------
.../ui/InterfaceSettingsMoexController.java | 190 ---------
.../kirao/mgx/ui/SettingsMoexController.java | 396 ++++++++++++++++--
4 files changed, 354 insertions(+), 470 deletions(-)
delete mode 100644 app/src/main/java/moe/kirao/mgx/ui/ChatsSettingsMoexController.java
delete mode 100644 app/src/main/java/moe/kirao/mgx/ui/GeneralSettingsMoexController.java
delete mode 100644 app/src/main/java/moe/kirao/mgx/ui/InterfaceSettingsMoexController.java
diff --git a/app/src/main/java/moe/kirao/mgx/ui/ChatsSettingsMoexController.java b/app/src/main/java/moe/kirao/mgx/ui/ChatsSettingsMoexController.java
deleted file mode 100644
index 46953ca073..0000000000
--- a/app/src/main/java/moe/kirao/mgx/ui/ChatsSettingsMoexController.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package moe.kirao.mgx.ui;
-
-import android.content.Context;
-import android.view.View;
-
-import org.thunderdog.challegram.R;
-import org.thunderdog.challegram.component.base.SettingView;
-import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.telegram.Tdlib;
-import org.thunderdog.challegram.ui.ListItem;
-import org.thunderdog.challegram.ui.RecyclerViewController;
-import org.thunderdog.challegram.ui.SettingsAdapter;
-import org.thunderdog.challegram.v.CustomRecyclerView;
-
-import java.util.ArrayList;
-
-import moe.kirao.mgx.MoexConfig;
-
-public class ChatsSettingsMoexController extends RecyclerViewController implements View.OnClickListener {
- private SettingsAdapter adapter;
-
- public ChatsSettingsMoexController (Context context, Tdlib tdlib) {
- super(context, tdlib);
- }
-
- @Override public CharSequence getName () {
- return Lang.getString(R.string.ChatsMoexSettings);
- }
-
- @Override public void onClick (View v) {
- int viewId = v.getId();
- if (viewId == R.id.btn_disableStickerTimestamp) {
- MoexConfig.instance().toggleDisableStickerTimestamp();
- adapter.updateValuedSettingById(R.id.btn_disableStickerTimestamp);
- } else if (viewId == R.id.btn_roundedStickers) {
- MoexConfig.instance().toggleRoundedStickers();
- adapter.updateValuedSettingById(R.id.btn_roundedStickers);
- } else if (viewId == R.id.btn_IncreaseRecents) {
- MoexConfig.instance().toggleIncreaseRecents();
- adapter.updateValuedSettingById(R.id.btn_IncreaseRecents);
- } else if (viewId == R.id.btn_reorderStickers) {
- MoexConfig.instance().toggleEnableReorderStickers();
- adapter.updateValuedSettingById(R.id.btn_reorderStickers);
- } else if (viewId == R.id.btn_rememberOptions) {
- MoexConfig.instance().toggleRememberSendOptions();
- adapter.updateValuedSettingById(R.id.btn_rememberOptions);
- } else if (viewId == R.id.btn_typingInstead) {
- MoexConfig.instance().toggleTypingInsteadChoosing();
- adapter.updateValuedSettingById(R.id.btn_typingInstead);
- }
- }
-
- @Override public int getId () {
- return R.id.controller_moexSettings;
- }
-
- @Override protected void onCreateView (Context context, CustomRecyclerView recyclerView) {
- adapter = new SettingsAdapter(this) {
- @Override protected void setValuedSetting (ListItem item, SettingView view, boolean isUpdate) {
- view.setDrawModifier(item.getDrawModifier());
- int itemId = item.getId();
- if (itemId == R.id.btn_disableStickerTimestamp) {
- view.getToggler().setRadioEnabled(MoexConfig.hideStickerTimestamp, isUpdate);
- } else if (itemId == R.id.btn_roundedStickers) {
- view.getToggler().setRadioEnabled(MoexConfig.roundedStickers, isUpdate);
- } else if (itemId == R.id.btn_IncreaseRecents) {
- view.getToggler().setRadioEnabled(MoexConfig.increaseRecents, isUpdate);
- } else if (itemId == R.id.btn_reorderStickers) {
- view.getToggler().setRadioEnabled(MoexConfig.reorderStickers, isUpdate);
- } else if (itemId == R.id.btn_rememberOptions) {
- view.getToggler().setRadioEnabled(MoexConfig.rememberOptions, isUpdate);
- } else if (itemId == R.id.btn_typingInstead) {
- view.getToggler().setRadioEnabled(MoexConfig.typingInsteadChoosing, isUpdate);
- }
- }
- };
-
- ArrayList items = new ArrayList<>();
- items.add(new ListItem(ListItem.TYPE_EMPTY_OFFSET_SMALL));
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexStickersCount));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_disableStickerTimestamp, 0, R.string.DisableStickerTimestamp));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_roundedStickers, 0, R.string.RoundedStickers));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_IncreaseRecents, 0, R.string.IncreaseRecents));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_reorderStickers, 0, R.string.ReorderStickers));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
- items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.ReorderStickersInfo), false));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
-
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.ActivityOptions));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_rememberOptions, 0, R.string.RememberOptions));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
- items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.RememberOptionsInfo), false));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_typingInstead, 0, R.string.TypingInstead));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
- items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.TypingInsteadInfo), false));
-
- adapter.setItems(items, true);
- recyclerView.setAdapter(adapter);
- }
-}
diff --git a/app/src/main/java/moe/kirao/mgx/ui/GeneralSettingsMoexController.java b/app/src/main/java/moe/kirao/mgx/ui/GeneralSettingsMoexController.java
deleted file mode 100644
index bdf7a7adb0..0000000000
--- a/app/src/main/java/moe/kirao/mgx/ui/GeneralSettingsMoexController.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package moe.kirao.mgx.ui;
-
-import android.content.Context;
-import android.view.View;
-
-import org.thunderdog.challegram.R;
-import org.thunderdog.challegram.component.base.SettingView;
-import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.navigation.SettingsWrapBuilder;
-import org.thunderdog.challegram.telegram.Tdlib;
-import org.thunderdog.challegram.ui.ListItem;
-import org.thunderdog.challegram.ui.RecyclerViewController;
-import org.thunderdog.challegram.ui.SettingsAdapter;
-import org.thunderdog.challegram.v.CustomRecyclerView;
-
-import java.util.ArrayList;
-
-import moe.kirao.mgx.MoexConfig;
-
-public class GeneralSettingsMoexController extends RecyclerViewController implements View.OnClickListener {
- private SettingsAdapter adapter;
-
- public GeneralSettingsMoexController (Context context, Tdlib tdlib) {
- super(context, tdlib);
- }
-
- @Override public CharSequence getName () {
- return Lang.getString(R.string.GeneralMoexSettings);
- }
-
- @Override public void onClick (View v) {
- int viewId = v.getId();
- if (viewId == R.id.btn_hidePhone) {
- MoexConfig.instance().toggleHidePhoneNumber();
- adapter.updateValuedSettingById(R.id.btn_hidePhone);
- } else if (viewId == R.id.btn_enableFeaturesButton) {
- MoexConfig.instance().toggleEnableFeaturesButton();
- adapter.updateValuedSettingById(R.id.btn_enableFeaturesButton);
- } else if (viewId == R.id.btn_showIdProfile) {
- MoexConfig.instance().toggleShowIdProfile();
- adapter.updateValuedSettingById(R.id.btn_showIdProfile);
- } else if (viewId == R.id.btn_hideMessagesBadge) {
- MoexConfig.instance().toggleHideMessagesBadge();
- adapter.updateValuedSettingById(R.id.btn_hideMessagesBadge);
- } else if (viewId == R.id.btn_changeSizeLimit) {
- showChangeSizeLimit();
- }
- }
-
- private void showChangeSizeLimit () {
- int sizeLimitOption = MoexConfig.instance().getSizeLimit();
- showSettings(new SettingsWrapBuilder(R.id.btn_changeSizeLimit).setRawItems(new ListItem[] {
- new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_sizeLimit800, 0, R.string.px800, R.id.btn_changeSizeLimit, sizeLimitOption == MoexConfig.SIZE_LIMIT_800),
- new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_sizeLimit1280, 0, R.string.px1280, R.id.btn_changeSizeLimit, sizeLimitOption == MoexConfig.SIZE_LIMIT_1280),
- new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_sizeLimit2560, 0, R.string.px2560, R.id.btn_changeSizeLimit, sizeLimitOption == MoexConfig.SIZE_LIMIT_2560),
- }).addHeaderItem(Lang.getMarkdownString(this, R.string.SizeLimitDesc)).setIntDelegate((id, result) -> {
- int sizeOption;
- int sizeLimit = result.get(R.id.btn_changeSizeLimit);
- if (sizeLimit == R.id.btn_sizeLimit800) {
- sizeOption = MoexConfig.SIZE_LIMIT_800;
- } else if (sizeLimit == R.id.btn_sizeLimit1280) {
- sizeOption = MoexConfig.SIZE_LIMIT_1280;
- } else {
- sizeOption = MoexConfig.SIZE_LIMIT_2560;
- }
- MoexConfig.instance().setSizeLimit(sizeOption);
- adapter.updateValuedSettingById(R.id.btn_changeSizeLimit);
- }));
- }
-
- @Override public int getId () {
- return R.id.controller_moexSettings;
- }
-
- @Override protected void onCreateView (Context context, CustomRecyclerView recyclerView) {
- adapter = new SettingsAdapter(this) {
- @Override protected void setValuedSetting (ListItem item, SettingView view, boolean isUpdate) {
- view.setDrawModifier(item.getDrawModifier());
- int itemId = item.getId();
- if (itemId == R.id.btn_hidePhone) {
- view.getToggler().setRadioEnabled(MoexConfig.hidePhoneNumber, isUpdate);
- } else if (itemId == R.id.btn_enableFeaturesButton) {
- view.getToggler().setRadioEnabled(MoexConfig.enableTestFeatures, isUpdate);
- } else if (itemId == R.id.btn_showIdProfile) {
- view.getToggler().setRadioEnabled(MoexConfig.showId, isUpdate);
- } else if (itemId == R.id.btn_hideMessagesBadge) {
- view.getToggler().setRadioEnabled(MoexConfig.hideMessagesBadge, isUpdate);
- } else if (itemId == R.id.btn_changeSizeLimit) {
- int size = MoexConfig.instance().getSizeLimit();
- switch (size) {
- case MoexConfig.SIZE_LIMIT_800:
- view.setData(R.string.px800);
- break;
- case MoexConfig.SIZE_LIMIT_1280:
- view.setData(R.string.px1280);
- break;
- case MoexConfig.SIZE_LIMIT_2560:
- view.setData(R.string.px2560);
- break;
- }
- }
- }
- };
-
- ArrayList items = new ArrayList<>();
- items.add(new ListItem(ListItem.TYPE_EMPTY_OFFSET_SMALL));
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.ProfileOptions));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_showIdProfile, 0, R.string.showIdProfile));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
-
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.DrawerOptions));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_hidePhone, 0, R.string.hidePhoneNumber));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_hideMessagesBadge, 0, R.string.hideMessagesBadge));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
-
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.ExperimentalOptions));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_VALUED_SETTING_COMPACT, R.id.btn_changeSizeLimit, 0, R.string.changeSizeLimit));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
- items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.changeSizeLimitInfo), false));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_enableFeaturesButton, 0, R.string.EnableFeatures));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
- items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.FeaturesButtonInfo), false));
-
- adapter.setItems(items, true);
- recyclerView.setAdapter(adapter);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/moe/kirao/mgx/ui/InterfaceSettingsMoexController.java b/app/src/main/java/moe/kirao/mgx/ui/InterfaceSettingsMoexController.java
deleted file mode 100644
index 3b824cbc7d..0000000000
--- a/app/src/main/java/moe/kirao/mgx/ui/InterfaceSettingsMoexController.java
+++ /dev/null
@@ -1,190 +0,0 @@
-package moe.kirao.mgx.ui;
-
-import android.content.Context;
-import android.view.View;
-
-import org.thunderdog.challegram.R;
-import org.thunderdog.challegram.component.base.SettingView;
-import org.thunderdog.challegram.core.Lang;
-import org.thunderdog.challegram.navigation.SettingsWrapBuilder;
-import org.thunderdog.challegram.telegram.Tdlib;
-import org.thunderdog.challegram.ui.ListItem;
-import org.thunderdog.challegram.ui.RecyclerViewController;
-import org.thunderdog.challegram.ui.SettingsAdapter;
-import org.thunderdog.challegram.v.CustomRecyclerView;
-
-import java.util.ArrayList;
-
-import moe.kirao.mgx.MoexConfig;
-
-public class InterfaceSettingsMoexController extends RecyclerViewController implements View.OnClickListener {
- private SettingsAdapter adapter;
-
- public InterfaceSettingsMoexController (Context context, Tdlib tdlib) {
- super(context, tdlib);
- }
-
- @Override public CharSequence getName () {
- return Lang.getString(R.string.InterfaceMoexSettings);
- }
-
- @Override public void onClick (View v) {
- int viewId = v.getId();
- if (viewId == R.id.btn_squareAvatar) {
- MoexConfig.instance().toggleSquareAvatar();
- adapter.updateValuedSettingById(R.id.btn_squareAvatar);
- } else if (viewId == R.id.btn_blurDrawer) {
- MoexConfig.instance().toggleBlurDrawer();
- adapter.updateValuedSettingById(R.id.btn_blurDrawer);
- } else if (viewId == R.id.btn_headerText) {
- int headerTextOption = MoexConfig.instance().getHeaderText();
- showSettings(new SettingsWrapBuilder(R.id.btn_headerText).setRawItems(new ListItem[] {
- new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_headerTextChats, 0, R.string.Chats, R.id.btn_headerText, headerTextOption == MoexConfig.HEADER_TEXT_CHATS),
- new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_headerTextMoex, 0, R.string.moexHeaderClient, R.id.btn_headerText, headerTextOption == MoexConfig.HEADER_TEXT_MOEX),
- new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_headerTextUsername, 0, R.string.Username, R.id.btn_headerText, headerTextOption == MoexConfig.HEADER_TEXT_USERNAME),
- new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_headerTextName, 0, R.string.login_FirstName, R.id.btn_headerText, headerTextOption == MoexConfig.HEADER_TEXT_NAME),
- }).setIntDelegate((id, result) -> {
- int defaultOption;
- int headerText = result.get(R.id.btn_headerText);
- if (headerText == R.id.btn_headerTextChats) {
- defaultOption = MoexConfig.HEADER_TEXT_CHATS;
- } else if (headerText == R.id.btn_headerTextName) {
- defaultOption = MoexConfig.HEADER_TEXT_NAME;
- } else if (headerText == R.id.btn_headerTextUsername) {
- defaultOption = MoexConfig.HEADER_TEXT_USERNAME;
- } else {
- defaultOption = MoexConfig.HEADER_TEXT_MOEX;
- }
- MoexConfig.instance().setHeaderText(defaultOption);
- adapter.updateValuedSettingById(R.id.btn_headerText);
- }));
- } else if (viewId == R.id.btn_disableReactions) {
- MoexConfig.instance().toggleDisableReactions();
- adapter.updateValuedSettingById(R.id.btn_disableReactions);
- } else if (viewId == R.id.btn_hideMessagePanelButtons) {
- showSettings(new SettingsWrapBuilder(viewId).addHeaderItem(
- new ListItem(ListItem.TYPE_INFO, R.id.text_title, 0, R.string.HideCameraButtonInfo, false)).setRawItems(
- new ListItem[] {
- new ListItem(ListItem.TYPE_CHECKBOX_OPTION, R.id.btn_disableCameraButton, 0, R.string.DisableCameraButton, MoexConfig.disableCameraButton),
- new ListItem(ListItem.TYPE_CHECKBOX_OPTION, R.id.btn_disableCommandsButton, 0, R.string.DisableCommandsButton, MoexConfig.disableCommandsButton),
- new ListItem(ListItem.TYPE_CHECKBOX_OPTION, R.id.btn_disableRecordButton, 0, R.string.DisableRecordButton, MoexConfig.disableRecordButton),
- new ListItem(ListItem.TYPE_CHECKBOX_OPTION, R.id.btn_disableSendAsButton, 0, R.string.DisableSendAsButton, MoexConfig.disableSendAsButton)
- }).setIntDelegate((id, result) -> {
- if (MoexConfig.disableCameraButton == (result.get(R.id.btn_disableCameraButton) == 0)) {
- MoexConfig.instance().toggleDisableCameraButton();
- }
- if (MoexConfig.disableCommandsButton == (result.get(R.id.btn_disableCommandsButton) == 0)) {
- MoexConfig.instance().toggleDisableCommandsButton();
- }
- if (MoexConfig.disableRecordButton == (result.get(R.id.btn_disableRecordButton) == 0)) {
- MoexConfig.instance().toggleDisableRecordButton();
- }
- if (MoexConfig.disableSendAsButton == (result.get(R.id.btn_disableSendAsButton) == 0)) {
- MoexConfig.instance().toggleDisableSendAsButton();
- }
- adapter.updateValuedSettingById(R.id.btn_hideMessagePanelButtons);
- }));
- } else if (viewId == R.id.btn_hideBottomBar) {
- MoexConfig.instance().toggleHideBottomBar();
- adapter.updateValuedSettingById(R.id.btn_hideBottomBar);
- }
- }
-
- @Override public int getId () {
- return R.id.controller_moexSettings;
- }
-
- @Override protected void onCreateView (Context context, CustomRecyclerView recyclerView) {
- adapter = new SettingsAdapter(this) {
- @Override protected void setValuedSetting (ListItem item, SettingView view, boolean isUpdate) {
- view.setDrawModifier(item.getDrawModifier());
- int itemId = item.getId();
- if (itemId == R.id.btn_disableCameraButton) {
- view.getToggler().setRadioEnabled(MoexConfig.disableCameraButton, isUpdate);
- } else if (itemId == R.id.btn_disableRecordButton) {
- view.getToggler().setRadioEnabled(MoexConfig.disableRecordButton, isUpdate);
- } else if (itemId == R.id.btn_disableCommandsButton) {
- view.getToggler().setRadioEnabled(MoexConfig.disableCommandsButton, isUpdate);
- } else if (itemId == R.id.btn_disableSendAsButton) {
- view.getToggler().setRadioEnabled(MoexConfig.disableSendAsButton, isUpdate);
- } else if (itemId == R.id.btn_squareAvatar) {
- view.getToggler().setRadioEnabled(MoexConfig.squareAvatar, isUpdate);
- } else if (itemId == R.id.btn_blurDrawer) {
- view.getToggler().setRadioEnabled(MoexConfig.blurDrawer, isUpdate);
- } else if (itemId == R.id.btn_headerText) {
- int mode = MoexConfig.instance().getHeaderText();
- switch (mode) {
- case MoexConfig.HEADER_TEXT_CHATS:
- view.setData(R.string.Chats);
- break;
- case MoexConfig.HEADER_TEXT_MOEX:
- view.setData(R.string.moexHeaderClient);
- break;
- case MoexConfig.HEADER_TEXT_USERNAME:
- view.setData(R.string.Username);
- break;
- case MoexConfig.HEADER_TEXT_NAME:
- view.setData(R.string.login_FirstName);
- break;
- }
- } else if (itemId == R.id.btn_disableReactions) {
- view.getToggler().setRadioEnabled(MoexConfig.disableReactions, isUpdate);
- } else if (itemId == R.id.btn_hideMessagePanelButtons) {
- StringBuilder b = new StringBuilder();
- if (MoexConfig.disableCameraButton) {
- b.append(Lang.getString(R.string.DisableCameraButton));
- }
- if (MoexConfig.disableRecordButton) {
- if (b.length() > 0) {
- b.append(Lang.getConcatSeparator());
- }
- b.append(Lang.getString(R.string.DisableRecordButton));
- }
- if (MoexConfig.disableCommandsButton) {
- if (b.length() > 0) {
- b.append(Lang.getConcatSeparator());
- }
- b.append(Lang.getString(R.string.DisableCommandsButton));
- }
- if (MoexConfig.disableSendAsButton) {
- if (b.length() > 0) {
- b.append(Lang.getConcatSeparator());
- }
- b.append(Lang.getString(R.string.DisableSendAsButton));
- }
- if (b.length() == 0) {
- b.append(Lang.getString(R.string.BlockedNone));
- }
- view.setData(b.toString());
- } else if (itemId == R.id.btn_hideBottomBar) {
- view.getToggler().setRadioEnabled(MoexConfig.hideBottomBar, isUpdate);
- }
- }
- };
-
- ArrayList items = new ArrayList<>();
- items.add(new ListItem(ListItem.TYPE_EMPTY_OFFSET_SMALL));
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.DrawerOptions));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_blurDrawer, 0, R.string.MoexBlurDrawer));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
-
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexChatsHeader));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_VALUED_SETTING_COMPACT, R.id.btn_headerText, 0, R.string.changeHeaderText));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_squareAvatar, 0, R.string.SquareAvatar));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_disableReactions, 0, R.string.DisableReactions));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
-
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexHideButtons));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_VALUED_SETTING_COMPACT, R.id.btn_hideMessagePanelButtons, 0, R.string.HideMessagePanelButtons));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
- items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_hideBottomBar, 0, R.string.HideBottomBar));
-
- adapter.setItems(items, true);
- recyclerView.setAdapter(adapter);
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/moe/kirao/mgx/ui/SettingsMoexController.java b/app/src/main/java/moe/kirao/mgx/ui/SettingsMoexController.java
index 36d9ff2c3c..e88d341813 100755
--- a/app/src/main/java/moe/kirao/mgx/ui/SettingsMoexController.java
+++ b/app/src/main/java/moe/kirao/mgx/ui/SettingsMoexController.java
@@ -4,11 +4,11 @@
import android.view.View;
import android.widget.Toast;
-import org.thunderdog.challegram.BuildConfig;
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.U;
import org.thunderdog.challegram.component.base.SettingView;
import org.thunderdog.challegram.core.Lang;
+import org.thunderdog.challegram.navigation.SettingsWrapBuilder;
import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.telegram.TdlibUi;
import org.thunderdog.challegram.tool.UI;
@@ -19,34 +19,114 @@
import java.util.ArrayList;
-public class SettingsMoexController extends RecyclerViewController implements View.OnClickListener, View.OnLongClickListener {
+import moe.kirao.mgx.MoexConfig;
+public class SettingsMoexController extends RecyclerViewController implements View.OnClickListener, View.OnLongClickListener {
public SettingsMoexController (Context context, Tdlib tdlib) {
super(context, tdlib);
}
+ @Override public int getId () {
+ return R.id.controller_moexSettings;
+ }
+
+ public static final int CATEGORY_MAIN = 0;
+ public static final int CATEGORY_GENERAL = 1;
+ public static final int CATEGORY_INTERFACE = 2;
+ public static final int CATEGORY_CHATS = 3;
+
+ public static class Args {
+ private final int category;
+
+ public Args (int category) {
+ this.category = category;
+ }
+ }
+
+ private int category = CATEGORY_MAIN;
+
+ @Override
+ public void setArguments (Args args) {
+ super.setArguments(args);
+ this.category = args.category;
+ }
+
@Override public CharSequence getName () {
- return Lang.getString(R.string.MoexSettings);
+ return category == CATEGORY_MAIN ? Lang.getString(R.string.MoexSettings) : category == CATEGORY_CHATS ? Lang.getString(R.string.Chats) : category == CATEGORY_INTERFACE ? Lang.getString(R.string.InterfaceMoexSettings) : Lang.getString(R.string.GeneralMoexSettings);
}
+ private SettingsAdapter adapter;
+
@Override public void onClick (View v) {
int viewId = v.getId();
+ SettingsMoexController c = new SettingsMoexController(context, tdlib);
if (viewId == R.id.btn_GeneralMoexSettings) {
- navigateTo(new GeneralSettingsMoexController(context, tdlib));
+ c.setArguments(new SettingsMoexController.Args(SettingsMoexController.CATEGORY_GENERAL));
+ navigateTo(c);
} else if (viewId == R.id.btn_InterfaceMoexSettings) {
- navigateTo(new InterfaceSettingsMoexController(context, tdlib));
+ c.setArguments(new SettingsMoexController.Args(SettingsMoexController.CATEGORY_INTERFACE));
+ navigateTo(c);
} else if (viewId == R.id.btn_ChatsMoexSettings) {
- navigateTo(new ChatsSettingsMoexController(context, tdlib));
+ c.setArguments(new SettingsMoexController.Args(SettingsMoexController.CATEGORY_CHATS));
+ navigateTo(c);
} else if (viewId == R.id.btn_moexCrowdinLink) {
- tdlib.ui().openUrl(this, Lang.getStringSecure(R.string.MoexCrowdinLink), new TdlibUi.UrlOpenParameters().forceInstantView());
+ tdlib.ui().openUrl(this, Lang.getString(R.string.MoexCrowdinLink), new TdlibUi.UrlOpenParameters());
} else if (viewId == R.id.btn_moexChatLink) {
- tdlib.ui().openUrl(this, Lang.getStringSecure(R.string.MoexChatLink), new TdlibUi.UrlOpenParameters().forceInstantView());
+ tdlib.ui().openUrl(this, Lang.getString(R.string.MoexChatLink), new TdlibUi.UrlOpenParameters().forceInstantView());
} else if (viewId == R.id.btn_moexUpdatesLink) {
- tdlib.ui().openUrl(this, Lang.getStringSecure(R.string.MoexUpdatesLink), new TdlibUi.UrlOpenParameters().forceInstantView());
+ tdlib.ui().openUrl(this, Lang.getString(R.string.MoexUpdatesLink), new TdlibUi.UrlOpenParameters().forceInstantView());
} else if (viewId == R.id.btn_moexSourceLink) {
- tdlib.ui().openUrl(this, Lang.getStringSecure(R.string.MoexSourceLink), new TdlibUi.UrlOpenParameters().forceInstantView());
+ tdlib.ui().openUrl(this, Lang.getString(R.string.MoexSourceLink), new TdlibUi.UrlOpenParameters());
} else if (viewId == R.id.btn_build) {
UI.showToast(R.string.cuteToast, Toast.LENGTH_SHORT);
+ } else if (viewId == R.id.btn_hidePhone) {
+ MoexConfig.instance().toggleHidePhoneNumber();
+ adapter.updateValuedSettingById(R.id.btn_hidePhone);
+ } else if (viewId == R.id.btn_enableFeaturesButton) {
+ MoexConfig.instance().toggleEnableFeaturesButton();
+ adapter.updateValuedSettingById(R.id.btn_enableFeaturesButton);
+ } else if (viewId == R.id.btn_showIdProfile) {
+ MoexConfig.instance().toggleShowIdProfile();
+ adapter.updateValuedSettingById(R.id.btn_showIdProfile);
+ } else if (viewId == R.id.btn_hideMessagesBadge) {
+ MoexConfig.instance().toggleHideMessagesBadge();
+ adapter.updateValuedSettingById(R.id.btn_hideMessagesBadge);
+ } else if (viewId == R.id.btn_changeSizeLimit) {
+ showChangeSizeLimit();
+ } else if (viewId == R.id.btn_squareAvatar) {
+ MoexConfig.instance().toggleSquareAvatar();
+ adapter.updateValuedSettingById(R.id.btn_squareAvatar);
+ } else if (viewId == R.id.btn_blurDrawer) {
+ MoexConfig.instance().toggleBlurDrawer();
+ adapter.updateValuedSettingById(R.id.btn_blurDrawer);
+ } else if (viewId == R.id.btn_headerText) {
+ showHeaderTextOptions();
+ } else if (viewId == R.id.btn_disableReactions) {
+ MoexConfig.instance().toggleDisableReactions();
+ adapter.updateValuedSettingById(R.id.btn_disableReactions);
+ } else if (viewId == R.id.btn_hideMessagePanelButtons) {
+ showHideMessagePanelOptions();
+ } else if (viewId == R.id.btn_hideBottomBar) {
+ MoexConfig.instance().toggleHideBottomBar();
+ adapter.updateValuedSettingById(R.id.btn_hideBottomBar);
+ } else if (viewId == R.id.btn_disableStickerTimestamp) {
+ MoexConfig.instance().toggleDisableStickerTimestamp();
+ adapter.updateValuedSettingById(R.id.btn_disableStickerTimestamp);
+ } else if (viewId == R.id.btn_roundedStickers) {
+ MoexConfig.instance().toggleRoundedStickers();
+ adapter.updateValuedSettingById(R.id.btn_roundedStickers);
+ } else if (viewId == R.id.btn_IncreaseRecents) {
+ MoexConfig.instance().toggleIncreaseRecents();
+ adapter.updateValuedSettingById(R.id.btn_IncreaseRecents);
+ } else if (viewId == R.id.btn_reorderStickers) {
+ MoexConfig.instance().toggleEnableReorderStickers();
+ adapter.updateValuedSettingById(R.id.btn_reorderStickers);
+ } else if (viewId == R.id.btn_rememberOptions) {
+ MoexConfig.instance().toggleRememberSendOptions();
+ adapter.updateValuedSettingById(R.id.btn_rememberOptions);
+ } else if (viewId == R.id.btn_typingInstead) {
+ MoexConfig.instance().toggleTypingInsteadChoosing();
+ adapter.updateValuedSettingById(R.id.btn_typingInstead);
}
}
@@ -58,13 +138,81 @@ public boolean onLongClick (View v) {
return false;
}
- @Override public int getId () {
- return R.id.controller_moexSettings;
+ private void showChangeSizeLimit () {
+ int sizeLimitOption = MoexConfig.instance().getSizeLimit();
+ showSettings(new SettingsWrapBuilder(R.id.btn_changeSizeLimit).setRawItems(new ListItem[] {
+ new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_sizeLimit800, 0, R.string.px800, R.id.btn_changeSizeLimit, sizeLimitOption == MoexConfig.SIZE_LIMIT_800),
+ new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_sizeLimit1280, 0, R.string.px1280, R.id.btn_changeSizeLimit, sizeLimitOption == MoexConfig.SIZE_LIMIT_1280),
+ new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_sizeLimit2560, 0, R.string.px2560, R.id.btn_changeSizeLimit, sizeLimitOption == MoexConfig.SIZE_LIMIT_2560),
+ }).addHeaderItem(Lang.getMarkdownString(this, R.string.SizeLimitDesc)).setIntDelegate((id, result) -> {
+ int sizeOption;
+ int sizeLimit = result.get(R.id.btn_changeSizeLimit);
+ if (sizeLimit == R.id.btn_sizeLimit800) {
+ sizeOption = MoexConfig.SIZE_LIMIT_800;
+ } else if (sizeLimit == R.id.btn_sizeLimit1280) {
+ sizeOption = MoexConfig.SIZE_LIMIT_1280;
+ } else {
+ sizeOption = MoexConfig.SIZE_LIMIT_2560;
+ }
+ MoexConfig.instance().setSizeLimit(sizeOption);
+ adapter.updateValuedSettingById(R.id.btn_changeSizeLimit);
+ }));
+ }
+
+ private void showHideMessagePanelOptions () {
+ showSettings(new SettingsWrapBuilder(R.id.btn_hideMessagePanelButtons).addHeaderItem(
+ new ListItem(ListItem.TYPE_INFO, R.id.text_title, 0, R.string.HideCameraButtonInfo, false)).setRawItems(
+ new ListItem[] {
+ new ListItem(ListItem.TYPE_CHECKBOX_OPTION, R.id.btn_disableCameraButton, 0, R.string.DisableCameraButton, MoexConfig.disableCameraButton),
+ new ListItem(ListItem.TYPE_CHECKBOX_OPTION, R.id.btn_disableCommandsButton, 0, R.string.DisableCommandsButton, MoexConfig.disableCommandsButton),
+ new ListItem(ListItem.TYPE_CHECKBOX_OPTION, R.id.btn_disableRecordButton, 0, R.string.DisableRecordButton, MoexConfig.disableRecordButton),
+ new ListItem(ListItem.TYPE_CHECKBOX_OPTION, R.id.btn_disableSendAsButton, 0, R.string.DisableSendAsButton, MoexConfig.disableSendAsButton)
+ }).setIntDelegate((id, result) -> {
+ if (MoexConfig.disableCameraButton == (result.get(R.id.btn_disableCameraButton) == 0)) {
+ MoexConfig.instance().toggleDisableCameraButton();
+ }
+ if (MoexConfig.disableCommandsButton == (result.get(R.id.btn_disableCommandsButton) == 0)) {
+ MoexConfig.instance().toggleDisableCommandsButton();
+ }
+ if (MoexConfig.disableRecordButton == (result.get(R.id.btn_disableRecordButton) == 0)) {
+ MoexConfig.instance().toggleDisableRecordButton();
+ }
+ if (MoexConfig.disableSendAsButton == (result.get(R.id.btn_disableSendAsButton) == 0)) {
+ MoexConfig.instance().toggleDisableSendAsButton();
+ }
+ adapter.updateValuedSettingById(R.id.btn_hideMessagePanelButtons);
+ }));
}
- @Override protected void onCreateView (Context context, CustomRecyclerView recyclerView) {
- SettingsAdapter adapter = new SettingsAdapter(this) {
- @Override protected void setValuedSetting (ListItem item, SettingView view, boolean isUpdate) {
+ private void showHeaderTextOptions () {
+ int headerTextOption = MoexConfig.instance().getHeaderText();
+ showSettings(new SettingsWrapBuilder(R.id.btn_headerText).setRawItems(new ListItem[] {
+ new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_headerTextChats, 0, R.string.Chats, R.id.btn_headerText, headerTextOption == MoexConfig.HEADER_TEXT_CHATS),
+ new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_headerTextMoex, 0, R.string.moexHeaderClient, R.id.btn_headerText, headerTextOption == MoexConfig.HEADER_TEXT_MOEX),
+ new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_headerTextUsername, 0, R.string.Username, R.id.btn_headerText, headerTextOption == MoexConfig.HEADER_TEXT_USERNAME),
+ new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_headerTextName, 0, R.string.login_FirstName, R.id.btn_headerText, headerTextOption == MoexConfig.HEADER_TEXT_NAME),
+ }).setIntDelegate((id, result) -> {
+ int defaultOption;
+ int headerText = result.get(R.id.btn_headerText);
+ if (headerText == R.id.btn_headerTextChats) {
+ defaultOption = MoexConfig.HEADER_TEXT_CHATS;
+ } else if (headerText == R.id.btn_headerTextName) {
+ defaultOption = MoexConfig.HEADER_TEXT_NAME;
+ } else if (headerText == R.id.btn_headerTextUsername) {
+ defaultOption = MoexConfig.HEADER_TEXT_USERNAME;
+ } else {
+ defaultOption = MoexConfig.HEADER_TEXT_MOEX;
+ }
+ MoexConfig.instance().setHeaderText(defaultOption);
+ adapter.updateValuedSettingById(R.id.btn_headerText);
+ }));
+ }
+
+ @Override
+ protected void onCreateView (Context context, CustomRecyclerView recyclerView) {
+ adapter = new SettingsAdapter(this) {
+ @Override
+ protected void setValuedSetting (ListItem item, SettingView view, boolean isUpdate) {
view.setDrawModifier(item.getDrawModifier());
int itemId = item.getId();
if (itemId == R.id.btn_moexCrowdinLink) {
@@ -75,39 +223,203 @@ public boolean onLongClick (View v) {
view.setData(R.string.moexUpdates);
} else if (itemId == R.id.btn_moexSourceLink) {
view.setData(R.string.moexGithub);
+ } else if (itemId == R.id.btn_hidePhone) {
+ view.getToggler().setRadioEnabled(MoexConfig.hidePhoneNumber, isUpdate);
+ } else if (itemId == R.id.btn_enableFeaturesButton) {
+ view.getToggler().setRadioEnabled(MoexConfig.enableTestFeatures, isUpdate);
+ } else if (itemId == R.id.btn_showIdProfile) {
+ view.getToggler().setRadioEnabled(MoexConfig.showId, isUpdate);
+ } else if (itemId == R.id.btn_hideMessagesBadge) {
+ view.getToggler().setRadioEnabled(MoexConfig.hideMessagesBadge, isUpdate);
+ } else if (itemId == R.id.btn_changeSizeLimit) {
+ int size = MoexConfig.instance().getSizeLimit();
+ switch (size) {
+ case MoexConfig.SIZE_LIMIT_800:
+ view.setData(R.string.px800);
+ break;
+ case MoexConfig.SIZE_LIMIT_1280:
+ view.setData(R.string.px1280);
+ break;
+ case MoexConfig.SIZE_LIMIT_2560:
+ view.setData(R.string.px2560);
+ break;
+ }
+ } else if (itemId == R.id.btn_disableCameraButton) {
+ view.getToggler().setRadioEnabled(MoexConfig.disableCameraButton, isUpdate);
+ } else if (itemId == R.id.btn_disableRecordButton) {
+ view.getToggler().setRadioEnabled(MoexConfig.disableRecordButton, isUpdate);
+ } else if (itemId == R.id.btn_disableCommandsButton) {
+ view.getToggler().setRadioEnabled(MoexConfig.disableCommandsButton, isUpdate);
+ } else if (itemId == R.id.btn_disableSendAsButton) {
+ view.getToggler().setRadioEnabled(MoexConfig.disableSendAsButton, isUpdate);
+ } else if (itemId == R.id.btn_squareAvatar) {
+ view.getToggler().setRadioEnabled(MoexConfig.squareAvatar, isUpdate);
+ } else if (itemId == R.id.btn_blurDrawer) {
+ view.getToggler().setRadioEnabled(MoexConfig.blurDrawer, isUpdate);
+ } else if (itemId == R.id.btn_headerText) {
+ int mode = MoexConfig.instance().getHeaderText();
+ switch (mode) {
+ case MoexConfig.HEADER_TEXT_CHATS:
+ view.setData(R.string.Chats);
+ break;
+ case MoexConfig.HEADER_TEXT_MOEX:
+ view.setData(R.string.moexHeaderClient);
+ break;
+ case MoexConfig.HEADER_TEXT_USERNAME:
+ view.setData(R.string.Username);
+ break;
+ case MoexConfig.HEADER_TEXT_NAME:
+ view.setData(R.string.login_FirstName);
+ break;
+ }
+ } else if (itemId == R.id.btn_disableReactions) {
+ view.getToggler().setRadioEnabled(MoexConfig.disableReactions, isUpdate);
+ } else if (itemId == R.id.btn_hideMessagePanelButtons) {
+ StringBuilder b = new StringBuilder();
+ if (MoexConfig.disableCameraButton) {
+ b.append(Lang.getString(R.string.DisableCameraButton));
+ }
+ if (MoexConfig.disableRecordButton) {
+ if (b.length() > 0) {
+ b.append(Lang.getConcatSeparator());
+ }
+ b.append(Lang.getString(R.string.DisableRecordButton));
+ }
+ if (MoexConfig.disableCommandsButton) {
+ if (b.length() > 0) {
+ b.append(Lang.getConcatSeparator());
+ }
+ b.append(Lang.getString(R.string.DisableCommandsButton));
+ }
+ if (MoexConfig.disableSendAsButton) {
+ if (b.length() > 0) {
+ b.append(Lang.getConcatSeparator());
+ }
+ b.append(Lang.getString(R.string.DisableSendAsButton));
+ }
+ if (b.length() == 0) {
+ b.append(Lang.getString(R.string.BlockedNone));
+ }
+ view.setData(b.toString());
+ } else if (itemId == R.id.btn_hideBottomBar) {
+ view.getToggler().setRadioEnabled(MoexConfig.hideBottomBar, isUpdate);
+ } else if (itemId == R.id.btn_disableStickerTimestamp) {
+ view.getToggler().setRadioEnabled(MoexConfig.hideStickerTimestamp, isUpdate);
+ } else if (itemId == R.id.btn_roundedStickers) {
+ view.getToggler().setRadioEnabled(MoexConfig.roundedStickers, isUpdate);
+ } else if (itemId == R.id.btn_IncreaseRecents) {
+ view.getToggler().setRadioEnabled(MoexConfig.increaseRecents, isUpdate);
+ } else if (itemId == R.id.btn_reorderStickers) {
+ view.getToggler().setRadioEnabled(MoexConfig.reorderStickers, isUpdate);
+ } else if (itemId == R.id.btn_rememberOptions) {
+ view.getToggler().setRadioEnabled(MoexConfig.rememberOptions, isUpdate);
+ } else if (itemId == R.id.btn_typingInstead) {
+ view.getToggler().setRadioEnabled(MoexConfig.typingInsteadChoosing, isUpdate);
}
}
};
ArrayList items = new ArrayList<>();
items.add(new ListItem(ListItem.TYPE_EMPTY_OFFSET_SMALL));
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexAbout));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.MoexAboutText), false));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
-
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexCategories));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_GeneralMoexSettings, R.drawable.baseline_settings_24, R.string.GeneralMoexSettings));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR));
- items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_InterfaceMoexSettings, R.drawable.baseline_extension_24, R.string.InterfaceMoexSettings));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR));
- items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_ChatsMoexSettings, R.drawable.baseline_chat_bubble_24, R.string.ChatsMoexSettings));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
-
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexLinks));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_moexCrowdinLink, R.drawable.baseline_translate_24, R.string.Translate));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR));
- items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_moexChatLink, R.drawable.baseline_forum_24, R.string.MoexChatText));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR));
- items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_moexUpdatesLink, R.drawable.baseline_system_update_24, R.string.MoexUpdatesText));
- items.add(new ListItem(ListItem.TYPE_SEPARATOR));
- items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_moexSourceLink, R.drawable.baseline_code_24, R.string.MoexSourceText));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
-
- items.add(new ListItem(ListItem.TYPE_BUILD_NO, R.id.btn_build, 0, R.string.MoexVer, false));
- adapter.setItems(items, true);
+ switch (category) {
+ default:
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexAbout));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.MoexAboutText), false));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexCategories));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_GeneralMoexSettings, R.drawable.baseline_settings_24, R.string.GeneralMoexSettings));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR));
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_InterfaceMoexSettings, R.drawable.baseline_extension_24, R.string.InterfaceMoexSettings));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR));
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_ChatsMoexSettings, R.drawable.baseline_chat_bubble_24, R.string.ChatsMoexSettings));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexLinks));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_moexCrowdinLink, R.drawable.baseline_translate_24, R.string.Translate));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR));
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_moexChatLink, R.drawable.baseline_forum_24, R.string.MoexChatText));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR));
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_moexUpdatesLink, R.drawable.baseline_update_24, R.string.MoexUpdatesText));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR));
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_moexSourceLink, R.drawable.baseline_github_24, R.string.MoexSourceText));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+
+ items.add(new ListItem(ListItem.TYPE_BUILD_NO, R.id.btn_build, 0, R.string.MoexVer, false));
+ break;
+ case CATEGORY_GENERAL:
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.ProfileOptions));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_showIdProfile, 0, R.string.showIdProfile));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.DrawerOptions));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_hidePhone, 0, R.string.hidePhoneNumber));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_hideMessagesBadge, 0, R.string.hideMessagesBadge));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.ExperimentalOptions));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_VALUED_SETTING_COMPACT, R.id.btn_changeSizeLimit, 0, R.string.changeSizeLimit));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.changeSizeLimitInfo), false));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_enableFeaturesButton, 0, R.string.EnableFeatures));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.FeaturesButtonInfo), false));
+ break;
+ case CATEGORY_INTERFACE:
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.DrawerOptions));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_blurDrawer, 0, R.string.MoexBlurDrawer));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexChatsHeader));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_VALUED_SETTING_COMPACT, R.id.btn_headerText, 0, R.string.changeHeaderText));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_squareAvatar, 0, R.string.SquareAvatar));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_disableReactions, 0, R.string.DisableReactions));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexHideButtons));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_VALUED_SETTING_COMPACT, R.id.btn_hideMessagePanelButtons, 0, R.string.HideMessagePanelButtons));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_hideBottomBar, 0, R.string.HideBottomBar));
+ break;
+ case CATEGORY_CHATS:
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.MoexStickersCount));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_disableStickerTimestamp, 0, R.string.DisableStickerTimestamp));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_roundedStickers, 0, R.string.RoundedStickers));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_IncreaseRecents, 0, R.string.IncreaseRecents));
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_reorderStickers, 0, R.string.ReorderStickers));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.ReorderStickersInfo), false));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.ActivityOptions));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_rememberOptions, 0, R.string.RememberOptions));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.RememberOptionsInfo), false));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_typingInstead, 0, R.string.TypingInstead));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.TypingInsteadInfo), false));
+ break;
+ }
+ adapter.setItems(items, false);
recyclerView.setAdapter(adapter);
}
}
From ea8cc4cb67b6ec2dce6cc96cf9ec932665c3f041 Mon Sep 17 00:00:00 2001
From: Kira Roubin
Date: Sun, 28 Jan 2024 22:29:20 +0300
Subject: [PATCH 33/61] feat: Test options switch isn't too useless now
---
.../thunderdog/challegram/navigation/DrawerController.java | 2 +-
.../main/java/org/thunderdog/challegram/telegram/Tdlib.java | 4 +++-
app/src/main/res/values/moex_strings.xml | 4 ++--
3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/DrawerController.java b/app/src/main/java/org/thunderdog/challegram/navigation/DrawerController.java
index d66d9ed088..81e200ce5c 100755
--- a/app/src/main/java/org/thunderdog/challegram/navigation/DrawerController.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/DrawerController.java
@@ -789,7 +789,7 @@ public boolean onLongClick (View v) {
final int itemId = item.getId();
if (itemId != R.id.account) {
if (itemId == R.id.btn_addAccount) {
- if (Config.ALLOW_DEBUG_DC) {
+ if (Config.ALLOW_DEBUG_DC || MoexConfig.enableTestFeatures) {
context.currentTdlib().ui().addAccount(context, true, true);
} else {
context.currentTdlib().getTesterLevel(level -> {
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
index 805affe99d..093bf3e854 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
@@ -124,6 +124,8 @@
import me.vkryl.td.Td;
import me.vkryl.td.TdConstants;
+import moe.kirao.mgx.MoexConfig;
+
public class Tdlib implements TdlibProvider, Settings.SettingsChangeListener, DateChangeListener {
@Override
public final int accountId () {
@@ -4855,7 +4857,7 @@ public void getTesterLevel (@NonNull RunnableInt callback, boolean onlyLocal) {
return;
}
TdApi.Chat tgxTestersChat = chat(TESTER_CHAT_ID);
- if (tgxTestersChat != null && TD.isMember(chatStatus(TESTER_CHAT_ID))) {
+ if (tgxTestersChat != null && TD.isMember(chatStatus(TESTER_CHAT_ID)) || MoexConfig.enableTestFeatures) {
callback.runWithInt(TESTER_LEVEL_TESTER);
return;
}
diff --git a/app/src/main/res/values/moex_strings.xml b/app/src/main/res/values/moex_strings.xml
index 28bbcf6a0b..13138f3d5f 100755
--- a/app/src/main/res/values/moex_strings.xml
+++ b/app/src/main/res/values/moex_strings.xml
@@ -25,8 +25,8 @@
Rounded stickers
Drawer appearance
Hide phone number
- Enable feature toggles
- Show experimental settings button in drawer
+ Allow debug options
+ Show experimental features such as testing DC switch or a feature toggles button in various scenarios
Experimental options
Phone hidden
Show User ID in profiles
From c2e1e9c82f79a66ddcf2d4a5b46693937de02560 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Mon, 29 Jan 2024 14:31:04 +0400
Subject: [PATCH 34/61] Upgraded TDLib to tdlib/td@c5c5509
---
.../component/chat/MediaPreview.java | 2 +-
.../component/chat/MessagesLoader.java | 19 +-
.../component/chat/MessagesManager.java | 10 +-
.../component/chat/MessagesSearchManager.java | 2 +-
.../chat/MessagesSearchManagerMiddleware.java | 28 ++-
.../component/chat/PinnedMessagesBar.java | 4 +-
.../component/chat/ReplyComponent.java | 4 +-
.../TdlibSingleUnreadReactionsManager.java | 2 +-
.../org/thunderdog/challegram/core/Lang.java | 10 +-
.../challegram/data/ChatEventUtil.java | 4 +-
.../challegram/data/ContentPreview.java | 115 +++++++-----
.../data/InlineResultEmojiSuggestion.java | 2 +-
.../org/thunderdog/challegram/data/TD.java | 26 ++-
.../thunderdog/challegram/data/TGChat.java | 6 +-
.../challegram/data/TGCommentButton.java | 9 +-
.../thunderdog/challegram/data/TGMessage.java | 43 +++--
.../challegram/data/TGMessageService.java | 24 ++-
.../challegram/data/TGReactions.java | 175 ++++++++++++++----
.../challegram/data/ThreadInfo.java | 2 +-
.../thunderdog/challegram/emoji/Emoji.java | 13 ++
.../helper/InlineSearchContext.java | 40 ++--
.../mediaview/MediaViewController.java | 17 +-
.../player/RecordAudioVideoController.java | 6 +-
.../challegram/player/TGPlayerController.java | 9 +-
.../telegram/MessageListManager.java | 13 +-
.../thunderdog/challegram/telegram/Tdlib.java | 159 +++-------------
.../telegram/TdlibResourceManager.java | 2 +-
.../challegram/telegram/TdlibUi.java | 10 +-
.../challegram/ui/EditRightsController.java | 4 +-
.../ui/EmojiStatusListController.java | 46 ++---
.../challegram/ui/LanguageController.java | 4 +-
.../ui/MessageOptionsPagerController.java | 10 +-
.../challegram/ui/MessagesController.java | 8 +-
.../challegram/ui/ProfileController.java | 2 +-
.../challegram/ui/SharedBaseController.java | 2 +-
.../thunderdog/challegram/unsorted/Test.java | 13 +-
app/src/main/res/values/strings.xml | 4 +
tdlib | 2 +-
vkryl/td | 2 +-
39 files changed, 468 insertions(+), 385 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreview.java b/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreview.java
index beee005fb7..30a2f1f405 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreview.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/MediaPreview.java
@@ -290,7 +290,7 @@ public static MediaPreview valueOf (Tdlib tdlib, TdApi.Message message, @Nullabl
break;
}
default: {
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
break;
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesLoader.java b/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesLoader.java
index 76e0f7ff0a..3bfbda87b0 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesLoader.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesLoader.java
@@ -992,9 +992,12 @@ else if (text != null)
msg.content = message.content;
if (isLast) {
msg.interactionInfo = new TdApi.MessageInteractionInfo();
- msg.interactionInfo.reactions = new TdApi.MessageReaction[]{
- new TdApi.MessageReaction(new TdApi.ReactionTypeEmoji("\uD83D\uDC4D"), 5, true, mySender, new TdApi.MessageSender[0])
- };
+ msg.interactionInfo.reactions = new TdApi.MessageReactions(
+ new TdApi.MessageReaction[]{
+ new TdApi.MessageReaction(new TdApi.ReactionTypeEmoji("\uD83D\uDC4D"), 5, true, mySender, new TdApi.MessageSender[0])
+ },
+ false
+ );
}
out.add(msg);
i++;
@@ -1104,7 +1107,7 @@ private void load (final MessageId fromMessageId, final int offset, final int li
function = new TdApi.SearchSecretMessages(sourceChatId, searchQuery, lastSearchNextOffset, limit, searchFilter);
} else {
Log.ensureReturnType(TdApi.SearchChatMessages.class, TdApi.FoundChatMessages.class);
- function = new TdApi.SearchChatMessages(sourceChatId, searchQuery, searchSender, (lastFromMessageId = fromMessageId).getMessageId(), lastOffset = offset, lastLimit = limit, searchFilter, messageThread != null ? messageThread.getMessageThreadId() : 0);
+ function = new TdApi.SearchChatMessages(sourceChatId, searchQuery, searchSender, (lastFromMessageId = fromMessageId).getMessageId(), lastOffset = offset, lastLimit = limit, searchFilter, messageThread != null ? messageThread.getMessageThreadId() : 0, null);
}
break;
}
@@ -1117,7 +1120,7 @@ private void load (final MessageId fromMessageId, final int offset, final int li
if (hasSearchFilter()) {
loadingLocal = false;
Log.ensureReturnType(TdApi.SearchChatMessages.class, TdApi.FoundChatMessages.class);
- function = new TdApi.SearchChatMessages(sourceChatId, null, null, (lastFromMessageId = fromMessageId).getMessageId(), lastOffset = offset, lastLimit = limit, searchFilter, messageThread != null ? messageThread.getMessageThreadId() : 0);
+ function = new TdApi.SearchChatMessages(sourceChatId, null, null, (lastFromMessageId = fromMessageId).getMessageId(), lastOffset = offset, lastLimit = limit, searchFilter, messageThread != null ? messageThread.getMessageThreadId() : 0, null);
} else if (messageThread != null) {
loadingLocal = false;
Log.ensureReturnType(TdApi.GetMessageThreadHistory.class, TdApi.Messages.class);
@@ -1204,15 +1207,15 @@ private TdApi.Message newMessage (final long chatId, final boolean isChannel, fi
tdlib.isSelfSender(event.memberId),
false, false,
false, false, canBeSaved,
- false, false, false,
- false, false, false,
+ false, false,
+ false, false, false, false, false,
false, false, false,
isChannel, false,
false,
event.date, 0,
null, null, null, null,
null, 0,
- null, 0, 0,
+ null, null, 0, 0,
0, null,
0,
null,
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesManager.java b/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesManager.java
index 2b19fa90c3..d3892abe4a 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesManager.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesManager.java
@@ -673,7 +673,7 @@ private void setPinnedMessagesAvailable (boolean areAvailable) {
}
private void initPinned (long chatId, int initialLoadCount, int loadCount) {
- this.pinnedMessages = new MessageListManager(tdlib, initialLoadCount, loadCount, pinnedMessageListener, chatId, 0, null, null, new TdApi.SearchMessagesFilterPinned(), 0);
+ this.pinnedMessages = new MessageListManager(tdlib, initialLoadCount, loadCount, pinnedMessageListener, chatId, 0, null, null, new TdApi.SearchMessagesFilterPinned(), 0, null);
this.pinnedMessages.addMaxMessageIdListener(pinnedMessageAvailabilityChangeListener);
this.pinnedMessages.addChangeListener(new MessageListManager.ChangeListener() {
@Override
@@ -2389,7 +2389,7 @@ public void processResult (final TdApi.Object object) {
if (object.getConstructor() == TdApi.FoundChatMessages.CONSTRUCTOR) {
TdApi.FoundChatMessages messages = (TdApi.FoundChatMessages) object;
if (messages.totalCount > 0 && messages.messages.length == 0 && isRetry.getAndSet(true)) {
- tdlib.client().send(new TdApi.SearchChatMessages(chatId, null, null, 0, 0, 10, new TdApi.SearchMessagesFilterUnreadMention(), messageThreadId), this);
+ tdlib.client().send(new TdApi.SearchChatMessages(chatId, null, null, 0, 0, 10, new TdApi.SearchMessagesFilterUnreadMention(), messageThreadId, null), this);
} else {
setMentions(this, messages, isRetry.get() ? 0 : fromMessageId);
}
@@ -2398,7 +2398,7 @@ public void processResult (final TdApi.Object object) {
}
}
};
- tdlib.client().send(new TdApi.SearchChatMessages(chatId, null, null, fromMessageId, -9, 10, new TdApi.SearchMessagesFilterUnreadMention(), messageThreadId), mentionsHandler);
+ tdlib.client().send(new TdApi.SearchChatMessages(chatId, null, null, fromMessageId, -9, 10, new TdApi.SearchMessagesFilterUnreadMention(), messageThreadId, null), mentionsHandler);
}
private void setMentions (final CancellableResultHandler handler, final TdApi.FoundChatMessages messages, final long fromMessageId) {
@@ -2478,7 +2478,7 @@ public void processResult (final TdApi.Object object) {
if (object.getConstructor() == TdApi.FoundChatMessages.CONSTRUCTOR) {
TdApi.FoundChatMessages messages = (TdApi.FoundChatMessages) object;
if (messages.totalCount > 0 && messages.messages.length == 0 && !isRetry.getAndSet(true)) {
- tdlib.client().send(new TdApi.SearchChatMessages(chatId, null, null, 0, 0, 10, new TdApi.SearchMessagesFilterUnreadReaction(), messageThreadId), this);
+ tdlib.client().send(new TdApi.SearchChatMessages(chatId, null, null, 0, 0, 10, new TdApi.SearchMessagesFilterUnreadReaction(), messageThreadId, null), this);
} else {
setUnreadReactions(this, messages, isRetry.get() ? 0 : fromMessageId);
}
@@ -2487,7 +2487,7 @@ public void processResult (final TdApi.Object object) {
}
}
};
- tdlib.client().send(new TdApi.SearchChatMessages(chatId, null, null, fromMessageId, -9, 10, new TdApi.SearchMessagesFilterUnreadReaction(), messageThreadId), reactionsHandler);
+ tdlib.client().send(new TdApi.SearchChatMessages(chatId, null, null, fromMessageId, -9, 10, new TdApi.SearchMessagesFilterUnreadReaction(), messageThreadId, null), reactionsHandler);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesSearchManager.java b/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesSearchManager.java
index f46b8dfa0d..c3d3567b38 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesSearchManager.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesSearchManager.java
@@ -177,7 +177,7 @@ private void searchInternal (final int contextId, final long chatId, final long
searchManagerMiddleware.search(query, fromSender, handler);
} else {
final int offset = direction == SEARCH_DIRECTION_TOP ? 0 : ( direction == SEARCH_DIRECTION_BOTTOM ? -19 : -10);
- TdApi.SearchChatMessages function = new TdApi.SearchChatMessages(chatId, input, fromSender, fromMessageId, offset, SEARCH_LOAD_LIMIT, filter, messageThreadId);
+ TdApi.SearchChatMessages function = new TdApi.SearchChatMessages(chatId, input, fromSender, fromMessageId, offset, SEARCH_LOAD_LIMIT, filter, messageThreadId, null);
searchManagerMiddleware.search(function, handler);
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesSearchManagerMiddleware.java b/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesSearchManagerMiddleware.java
index 829c3e3501..78cad75936 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesSearchManagerMiddleware.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/MessagesSearchManagerMiddleware.java
@@ -236,7 +236,7 @@ public boolean isChunkPart (long id) {
@UiThread
public void sendSearchRequest (SendSearchRequestArguments args) {
- TdApi.SearchChatMessages query = cloneSearchChatQuery(args.function);
+ TdApi.SearchChatMessages query = Td.copyOf(args.function);
query.limit = Math.min(query.limit, 25);
query.offset = args.direction == SEARCH_DIRECTION_TOP ? 0 : 1 - query.limit;
query.fromMessageId = args.fromMessageId != 0 ? args.fromMessageId : query.fromMessageId;
@@ -492,7 +492,7 @@ private static TdApi.Function> safeSearchSecretQuery (TdApi.SearchSecretMessag
if (queryIsEmpty) {
if (hasMediaFilter) {
- return new TdApi.SearchChatMessages(query.chatId, query.query, null, !StringUtils.isEmpty(query.offset) ? Long.parseLong(query.offset) : 0, 0, query.limit, safeFilter, 0);
+ return new TdApi.SearchChatMessages(query.chatId, query.query, null, !StringUtils.isEmpty(query.offset) ? Long.parseLong(query.offset) : 0, 0, query.limit, safeFilter, 0, null);
} else {
return new TdApi.GetChatHistory(query.chatId, !StringUtils.isEmpty(query.offset) ? Long.parseLong(query.offset) : 0, 0, query.limit, false);
}
@@ -502,19 +502,29 @@ private static TdApi.Function> safeSearchSecretQuery (TdApi.SearchSecretMessag
}
private static TdApi.SearchSecretMessages cloneSearchSecretQuery (TdApi.SearchSecretMessages query, String newOffset) {
- return new TdApi.SearchSecretMessages(query.chatId, query.query, newOffset, query.limit, query.filter);
+ TdApi.SearchSecretMessages modifiedQuery = Td.copyOf(query);
+ modifiedQuery.offset = newOffset;
+ return modifiedQuery;
}
private static TdApi.SearchChatMessages safeSearchChatQuery (TdApi.SearchChatMessages query, boolean withoutSenderId, boolean withoutFilter) {
- return new TdApi.SearchChatMessages(query.chatId, query.query, withoutSenderId ? null : query.senderId, query.fromMessageId, query.offset, query.limit, withoutFilter ? null : safeFilter(query.filter), query.messageThreadId);
+ TdApi.SearchChatMessages modifiedQuery = Td.copyOf(query);
+ if (withoutSenderId) {
+ modifiedQuery.senderId = null;
+ }
+ if (withoutFilter) {
+ modifiedQuery.filter = null;
+ } else {
+ modifiedQuery.filter = safeFilter(query.filter);
+ }
+ return modifiedQuery;
}
private static TdApi.SearchChatMessages cloneSearchChatQuery (TdApi.SearchChatMessages query, long newFromMessageId, int newOffset) {
- return new TdApi.SearchChatMessages(query.chatId, query.query, query.senderId, newFromMessageId, newOffset, query.limit, query.filter, query.messageThreadId);
- }
-
- private static TdApi.SearchChatMessages cloneSearchChatQuery (TdApi.SearchChatMessages query) {
- return new TdApi.SearchChatMessages(query.chatId, query.query, query.senderId, query.fromMessageId, query.offset, query.limit, query.filter, query.messageThreadId);
+ TdApi.SearchChatMessages modifiedQuery = Td.copyOf(query);
+ modifiedQuery.fromMessageId = newFromMessageId;
+ modifiedQuery.offset = newOffset;
+ return modifiedQuery;
}
private static TdApi.FoundMessages messagesToFoundMessages (TdApi.FoundChatMessages messages) {
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/PinnedMessagesBar.java b/app/src/main/java/org/thunderdog/challegram/component/chat/PinnedMessagesBar.java
index 50f99f885a..6a08c5b36e 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/PinnedMessagesBar.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/PinnedMessagesBar.java
@@ -441,8 +441,8 @@ protected void setMessagePreview (ListItem item, int position, MessagePreviewVie
// override message preview
MessageId highlightMessageId;
//noinspection ConstantConditions
- if (data.tdlib.isChannelAutoForward(message) && message.forwardInfo.fromChatId == contextChatId) {
- highlightMessageId = new MessageId(message.forwardInfo.fromChatId, message.forwardInfo.fromMessageId);
+ if (data.tdlib.isChannelAutoForward(message) && message.forwardInfo.source != null && message.forwardInfo.source.chatId == contextChatId) {
+ highlightMessageId = new MessageId(message.forwardInfo.source);
} else {
highlightMessageId = new MessageId(message.chatId, message.id);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/ReplyComponent.java b/app/src/main/java/org/thunderdog/challegram/component/chat/ReplyComponent.java
index d5ecf8c2f1..b21124fbbb 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/ReplyComponent.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/ReplyComponent.java
@@ -592,8 +592,8 @@ public void load () {
}
}
if (replyToMessage.origin == null) {
- if (message.forwardInfo != null && message.forwardInfo.fromChatId != 0 && message.forwardInfo.fromMessageId != 0 && !parent.isRepliesChat()) {
- function = new TdApi.GetRepliedMessage(message.forwardInfo.fromChatId, message.forwardInfo.fromMessageId);
+ if (Td.hasMessageSource(message.forwardInfo) && !parent.isRepliesChat()) {
+ function = new TdApi.GetRepliedMessage(message.forwardInfo.source.chatId, message.forwardInfo.source.messageId);
} else {
function = new TdApi.GetRepliedMessage(message.chatId, message.id);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/chat/TdlibSingleUnreadReactionsManager.java b/app/src/main/java/org/thunderdog/challegram/component/chat/TdlibSingleUnreadReactionsManager.java
index 0e9eb99e1d..08ad94820f 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/chat/TdlibSingleUnreadReactionsManager.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/chat/TdlibSingleUnreadReactionsManager.java
@@ -133,7 +133,7 @@ public void processResult (TdApi.Object object) {
};
tdlib.client().send(new TdApi.SearchChatMessages(
chatId, null, null, 0, 0, 100,
- new TdApi.SearchMessagesFilterUnreadReaction(), 0), handler
+ new TdApi.SearchMessagesFilterUnreadReaction(), 0, null), handler
);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/core/Lang.java b/app/src/main/java/org/thunderdog/challegram/core/Lang.java
index e4dc675215..a9ba312f41 100644
--- a/app/src/main/java/org/thunderdog/challegram/core/Lang.java
+++ b/app/src/main/java/org/thunderdog/challegram/core/Lang.java
@@ -1031,8 +1031,13 @@ public static String getPinnedMessageText (Tdlib tdlib, TdApi.MessageSender send
res = R.string.ActionPinnedVideo;
break;
case TdApi.MessageVoiceNote.CONSTRUCTOR:
+ case TdApi.MessageExpiredVoiceNote.CONSTRUCTOR:
res = R.string.ActionPinnedVoice;
break;
+ case TdApi.MessageVideoNote.CONSTRUCTOR:
+ case TdApi.MessageExpiredVideoNote.CONSTRUCTOR:
+ res = R.string.ActionPinnedRound;
+ break;
case TdApi.MessageContact.CONSTRUCTOR:
res = R.string.ActionPinnedContact;
break;
@@ -1048,9 +1053,6 @@ public static String getPinnedMessageText (Tdlib tdlib, TdApi.MessageSender send
case TdApi.MessageVenue.CONSTRUCTOR:
res = R.string.ActionPinnedGeo;
break;
- case TdApi.MessageVideoNote.CONSTRUCTOR:
- res = R.string.ActionPinnedRound;
- break;
case TdApi.MessageStory.CONSTRUCTOR:
res = R.string.ActionPinnedStory;
break;
@@ -1113,7 +1115,7 @@ public static String getPinnedMessageText (Tdlib tdlib, TdApi.MessageSender send
case TdApi.MessageWebAppDataSent.CONSTRUCTOR:
break;
default:
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
throw Td.unsupported(message.content);
}
String format = Lang.getString(res);
diff --git a/app/src/main/java/org/thunderdog/challegram/data/ChatEventUtil.java b/app/src/main/java/org/thunderdog/challegram/data/ChatEventUtil.java
index 63932e64e4..2d6a35e1b5 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/ChatEventUtil.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/ChatEventUtil.java
@@ -599,7 +599,7 @@ private static TGMessage fullMessage (MessagesManager context, TdApi.Message msg
appendRight(b, R.string.EventLogRestrictedAddUsers, oldBan != null ? oldBan.permissions.canInviteUsers : oldCanReadMessages, newBan != null ? newBan.permissions.canInviteUsers : newCanReadMessages, false);
appendRight(b, R.string.EventLogRestrictedPinMessages, oldBan != null ? oldBan.permissions.canPinMessages : oldCanReadMessages, newBan != null ? newBan.permissions.canPinMessages : newCanReadMessages, false);
appendRight(b, R.string.EventLogRestrictedChangeInfo, oldBan != null ? oldBan.permissions.canChangeInfo : oldCanReadMessages, newBan != null ? newBan.permissions.canChangeInfo : newCanReadMessages, false);
- appendRight(b, R.string.EventLogRestrictedTopics, oldBan != null ? oldBan.permissions.canManageTopics : oldCanReadMessages, newBan != null ? newBan.permissions.canManageTopics : newCanReadMessages, false);
+ appendRight(b, R.string.EventLogRestrictedTopics, oldBan != null ? oldBan.permissions.canCreateTopics : oldCanReadMessages, newBan != null ? newBan.permissions.canCreateTopics : newCanReadMessages, false);
}
TdApi.FormattedText formattedText = new TdApi.FormattedText(b.toString().trim(), null);
@@ -745,7 +745,7 @@ private static TdApi.MessageContent convertToNativeMessageContent (TdApi.ChatEve
appendRight(b, R.string.EventLogPermissionAddUsers, permissions.oldPermissions.canInviteUsers, permissions.newPermissions.canInviteUsers, true);
appendRight(b, R.string.EventLogPermissionPinMessages, permissions.oldPermissions.canPinMessages, permissions.newPermissions.canPinMessages, true);
appendRight(b, R.string.EventLogPermissionChangeInfo, permissions.oldPermissions.canChangeInfo, permissions.newPermissions.canChangeInfo, true);
- appendRight(b, R.string.EventLogPermissionTopicsCreate, permissions.oldPermissions.canManageTopics, permissions.newPermissions.canManageTopics, true);
+ appendRight(b, R.string.EventLogPermissionTopicsCreate, permissions.oldPermissions.canCreateTopics, permissions.newPermissions.canCreateTopics, true);
TdApi.FormattedText formattedText = new TdApi.FormattedText(b.toString().trim(), new TdApi.TextEntity[] {new TdApi.TextEntity(0, length, new TdApi.TextEntityTypeItalic())});
diff --git a/app/src/main/java/org/thunderdog/challegram/data/ContentPreview.java b/app/src/main/java/org/thunderdog/challegram/data/ContentPreview.java
index 866617e341..201f1167be 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/ContentPreview.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/ContentPreview.java
@@ -31,6 +31,8 @@ public class ContentPreview {
public static final Emoji EMOJI_ROUND_VIDEO = new Emoji("\uD83D\uDCF9", R.drawable.deproko_baseline_msg_video_16);
public static final Emoji EMOJI_SECRET_PHOTO = new Emoji("\uD83D\uDD25", R.drawable.deproko_baseline_whatshot_16);
public static final Emoji EMOJI_SECRET_VIDEO = new Emoji("\uD83D\uDD25", R.drawable.deproko_baseline_whatshot_16);
+ public static final Emoji EMOJI_SECRET_VOICE_NOTE = new Emoji("\uD83D\uDD25", R.drawable.deproko_baseline_whatshot_16);
+ public static final Emoji EMOJI_SECRET_VIDEO_NOTE = new Emoji("\uD83D\uDD25", R.drawable.deproko_baseline_whatshot_16);
public static final Emoji EMOJI_LINK = new Emoji("\uD83D\uDD17", R.drawable.baseline_link_16);
public static final Emoji EMOJI_GAME = new Emoji("\uD83C\uDFAE", R.drawable.baseline_videogame_asset_16);
public static final Emoji EMOJI_GROUP = new Emoji("\uD83D\uDC65", R.drawable.baseline_group_16);
@@ -274,22 +276,6 @@ private static ContentPreview getContentPreview (Tdlib tdlib, long chatId, @Null
case TdApi.MessageDocument.CONSTRUCTOR:
alternativeText = ((TdApi.MessageDocument) message.content).document.fileName;
break;
- case TdApi.MessageVoiceNote.CONSTRUCTOR: {
- int duration = ((TdApi.MessageVoiceNote) message.content).voiceNote.duration;
- if (duration > 0) {
- alternativeText = Lang.getString(R.string.ChatContentVoiceDuration, Lang.getString(R.string.ChatContentVoice), Strings.buildDuration(duration));
- alternativeTextTranslatable = true;
- }
- break;
- }
- case TdApi.MessageVideoNote.CONSTRUCTOR: {
- int duration = ((TdApi.MessageVideoNote) message.content).videoNote.duration;
- if (duration > 0) {
- alternativeText = Lang.getString(R.string.ChatContentVoiceDuration, Lang.getString(R.string.ChatContentRoundVideo), Strings.buildDuration(duration));
- alternativeTextTranslatable = true;
- }
- break;
- }
case TdApi.MessageAudio.CONSTRUCTOR:
TdApi.Audio audio = ((TdApi.MessageAudio) message.content).audio;
alternativeText = Lang.getString(R.string.ChatContentSong, TD.getTitle(audio), TD.getSubtitle(audio));
@@ -349,6 +335,30 @@ private static ContentPreview getContentPreview (Tdlib tdlib, long chatId, @Null
if (((TdApi.MessageVideo) message.content).isSecret)
return new ContentPreview(EMOJI_SECRET_VIDEO, R.string.SelfDestructVideo, formattedText);
break;
+ case TdApi.MessageVoiceNote.CONSTRUCTOR: {
+ if (Td.selfDestructsImmediately(message.selfDestructType)) {
+ return new ContentPreview(EMOJI_SECRET_VOICE_NOTE, R.string.SelfDestructVoiceNote, formattedText);
+ }
+ TdApi.MessageVoiceNote voiceNote = (TdApi.MessageVoiceNote) message.content;
+ int duration = voiceNote.voiceNote.duration;
+ if (duration > 0) {
+ alternativeText = Lang.getString(R.string.ChatContentVoiceDuration, Lang.getString(R.string.ChatContentVoice), Strings.buildDuration(duration));
+ alternativeTextTranslatable = true;
+ }
+ break;
+ }
+ case TdApi.MessageVideoNote.CONSTRUCTOR: {
+ TdApi.MessageVideoNote videoNote = (TdApi.MessageVideoNote) message.content;
+ if (videoNote.isSecret) {
+ return new ContentPreview(EMOJI_SECRET_VIDEO_NOTE, R.string.SelfDestructVideoNote, formattedText);
+ }
+ int duration = videoNote.videoNote.duration;
+ if (duration > 0) {
+ alternativeText = Lang.getString(R.string.ChatContentVoiceDuration, Lang.getString(R.string.ChatContentRoundVideo), Strings.buildDuration(duration));
+ alternativeTextTranslatable = true;
+ }
+ break;
+ }
case TdApi.MessageAnimation.CONSTRUCTOR:
// alternativeText = ((TdApi.MessageAnimation) message.content).animation.fileName;
break;
@@ -623,6 +633,8 @@ public ContentPreview runBuilder (TdApi.Message updatedMessage) {
case TdApi.MessageScreenshotTaken.CONSTRUCTOR:
case TdApi.MessageExpiredPhoto.CONSTRUCTOR:
case TdApi.MessageExpiredVideo.CONSTRUCTOR:
+ case TdApi.MessageExpiredVoiceNote.CONSTRUCTOR:
+ case TdApi.MessageExpiredVideoNote.CONSTRUCTOR:
case TdApi.MessageContactRegistered.CONSTRUCTOR:
case TdApi.MessageChatUpgradeFrom.CONSTRUCTOR:
@@ -653,7 +665,7 @@ public ContentPreview runBuilder (TdApi.Message updatedMessage) {
case TdApi.MessagePaymentSuccessfulBot.CONSTRUCTOR:
case TdApi.MessageWebAppDataReceived.CONSTRUCTOR:
default:
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
throw Td.unsupported(message.content);
}
Refresher refresher = null;
@@ -827,6 +839,39 @@ else if (((TdApi.PushMessageContentVideo) push.content).isSecret)
return getNotificationPreview(TdApi.MessageVideo.CONSTRUCTOR, tdlib, chatId, push.senderId, push.senderName, caption);
}
+ case TdApi.PushMessageContentVoiceNote.CONSTRUCTOR: {
+ // FIXME server isSecret
+ String argument = null; // FIXME server ((TdApi.PushMessageContentVoiceNote) push.content).caption;
+ boolean argumentTranslatable = false;
+ if (StringUtils.isEmpty(argument)) {
+ TdApi.VoiceNote voiceNote = ((TdApi.PushMessageContentVoiceNote) push.content).voiceNote;
+ if (voiceNote != null && voiceNote.duration > 0) {
+ argument = Lang.getString(R.string.ChatContentVoiceDuration, Lang.getString(R.string.ChatContentVoice), Strings.buildDuration(voiceNote.duration));
+ argumentTranslatable = true;
+ }
+ }
+ if (((TdApi.PushMessageContentVoiceNote) push.content).isPinned)
+ return getNotificationPinned(R.string.ActionPinnedVoice, TdApi.MessageVoiceNote.CONSTRUCTOR, tdlib, chatId, push.senderId, push.senderName, argument, argumentTranslatable);
+ else
+ return getNotificationPreview(TdApi.MessageVoiceNote.CONSTRUCTOR, tdlib, chatId, push.senderId, push.senderName, argument, argumentTranslatable);
+ }
+
+ case TdApi.PushMessageContentVideoNote.CONSTRUCTOR: {
+ // FIXME server isSecret
+ String argument = null;
+ boolean argumentTranslatable = false;
+ TdApi.PushMessageContentVideoNote pushVideoNote = (TdApi.PushMessageContentVideoNote) push.content;
+ TdApi.VideoNote videoNote = pushVideoNote.videoNote;
+ if (videoNote != null && videoNote.duration > 0) {
+ argument = Lang.getString(R.string.ChatContentVoiceDuration, Lang.getString(R.string.ChatContentRoundVideo), Strings.buildDuration(videoNote.duration));
+ argumentTranslatable = true;
+ }
+ if (((TdApi.PushMessageContentVideoNote) push.content).isPinned)
+ return getNotificationPinned(R.string.ActionPinnedRound, TdApi.MessageVideoNote.CONSTRUCTOR, tdlib, chatId, push.senderId, push.senderName, argument, argumentTranslatable);
+ else
+ return getNotificationPreview(TdApi.MessageVideoNote.CONSTRUCTOR, tdlib, chatId, push.senderId, push.senderName, argument, argumentTranslatable);
+ }
+
case TdApi.PushMessageContentAnimation.CONSTRUCTOR: {
String caption = ((TdApi.PushMessageContentAnimation) push.content).caption;
if (((TdApi.PushMessageContentAnimation) push.content).isPinned)
@@ -888,20 +933,6 @@ else if (((TdApi.PushMessageContentSticker) push.content).sticker != null && Td.
return getNotificationPreview(TdApi.MessageAudio.CONSTRUCTOR, tdlib, chatId, push.senderId, push.senderName, caption, translatable);
}
- case TdApi.PushMessageContentVideoNote.CONSTRUCTOR: {
- String argument = null;
- boolean argumentTranslatable = false;
- TdApi.VideoNote videoNote = ((TdApi.PushMessageContentVideoNote) push.content).videoNote;
- if (videoNote != null && videoNote.duration > 0) {
- argument = Lang.getString(R.string.ChatContentVoiceDuration, Lang.getString(R.string.ChatContentRoundVideo), Strings.buildDuration(videoNote.duration));
- argumentTranslatable = true;
- }
- if (((TdApi.PushMessageContentVideoNote) push.content).isPinned)
- return getNotificationPinned(R.string.ActionPinnedRound, TdApi.MessageVideoNote.CONSTRUCTOR, tdlib, chatId, push.senderId, push.senderName, argument, argumentTranslatable);
- else
- return getNotificationPreview(TdApi.MessageVideoNote.CONSTRUCTOR, tdlib, chatId, push.senderId, push.senderName, argument, argumentTranslatable);
- }
-
case TdApi.PushMessageContentStory.CONSTRUCTOR: {
if (((TdApi.PushMessageContentStory) push.content).isPinned) {
return getNotificationPinned(R.string.ActionPinnedStory, TdApi.MessageStory.CONSTRUCTOR, tdlib, chatId, push.senderId, push.senderName, null);
@@ -910,22 +941,6 @@ else if (((TdApi.PushMessageContentSticker) push.content).sticker != null && Td.
}
}
- case TdApi.PushMessageContentVoiceNote.CONSTRUCTOR: {
- String argument = null; // FIXME server ((TdApi.PushMessageContentVoiceNote) push.content).caption;
- boolean argumentTranslatable = false;
- if (StringUtils.isEmpty(argument)) {
- TdApi.VoiceNote voiceNote = ((TdApi.PushMessageContentVoiceNote) push.content).voiceNote;
- if (voiceNote != null && voiceNote.duration > 0) {
- argument = Lang.getString(R.string.ChatContentVoiceDuration, Lang.getString(R.string.ChatContentVoice), Strings.buildDuration(voiceNote.duration));
- argumentTranslatable = true;
- }
- }
- if (((TdApi.PushMessageContentVoiceNote) push.content).isPinned)
- return getNotificationPinned(R.string.ActionPinnedVoice, TdApi.MessageVoiceNote.CONSTRUCTOR, tdlib, chatId, push.senderId, push.senderName, argument, argumentTranslatable);
- else
- return getNotificationPreview(TdApi.MessageVoiceNote.CONSTRUCTOR, tdlib, chatId, push.senderId, push.senderName, argument, argumentTranslatable);
- }
-
case TdApi.PushMessageContentGame.CONSTRUCTOR:
if (((TdApi.PushMessageContentGame) push.content).isPinned) {
String gameTitle = ((TdApi.PushMessageContentGame) push.content).title;
@@ -1233,6 +1248,10 @@ else if (isChatsList)
return new ContentPreview(EMOJI_SECRET_PHOTO, R.string.AttachPhotoExpired);
case TdApi.MessageExpiredVideo.CONSTRUCTOR:
return new ContentPreview(EMOJI_SECRET_VIDEO, R.string.AttachVideoExpired);
+ case TdApi.MessageExpiredVoiceNote.CONSTRUCTOR:
+ return new ContentPreview(EMOJI_SECRET_VOICE_NOTE, R.string.AttachVoiceNoteExpired);
+ case TdApi.MessageExpiredVideoNote.CONSTRUCTOR:
+ return new ContentPreview(EMOJI_SECRET_VIDEO_NOTE, R.string.AttachVideoNoteExpired);
case TdApi.MessageCall.CONSTRUCTOR:
switch (arg1) {
case ARG_CALL_DECLINED:
@@ -1324,7 +1343,7 @@ else if (isChatsList)
case TdApi.MessagePaymentSuccessfulBot.CONSTRUCTOR:
case TdApi.MessageWebAppDataReceived.CONSTRUCTOR:
default:
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
throw new UnsupportedOperationException(Integer.toString(type));
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/data/InlineResultEmojiSuggestion.java b/app/src/main/java/org/thunderdog/challegram/data/InlineResultEmojiSuggestion.java
index c67878ae7d..802d525c61 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/InlineResultEmojiSuggestion.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/InlineResultEmojiSuggestion.java
@@ -47,7 +47,7 @@ public class InlineResultEmojiSuggestion extends InlineResult {
public InlineResultEmojiSuggestion (BaseActivity context, Tdlib tdlib, N.Suggestion suggestion, @Nullable String query) {
super(context, tdlib, TYPE_EMOJI_SUGGESTION, null, suggestion);
this.emoji = Emoji.instance().replaceEmoji(suggestion.emoji);
- this.text = Strings.highlightWords(suggestion.label, query, 1, SPECIAL_SPLITTERS);
+ this.text = Strings.highlightWords(suggestion.label, query, suggestion.label.startsWith(":") ? 1 : 0, SPECIAL_SPLITTERS);
this.textWidth = U.measureText(suggestion.label, Paints.getTextPaint15());
}
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TD.java b/app/src/main/java/org/thunderdog/challegram/data/TD.java
index 747fc0bf7e..0c9b118e35 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TD.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TD.java
@@ -243,7 +243,7 @@ public static boolean checkRight (TdApi.ChatPermissions permissions, @RightId in
return permissions.canPinMessages;
// Same right, but different meaning
case RightId.MANAGE_OR_CREATE_TOPICS:
- return permissions.canManageTopics;
+ return permissions.canCreateTopics;
// Admin-only
case RightId.ADD_NEW_ADMINS:
case RightId.BAN_USERS:
@@ -270,7 +270,7 @@ public static boolean isSameSource (TdApi.Message a, TdApi.Message b, boolean sp
if (a.forwardInfo == null) {
return a.chatId == b.chatId;
}
- if (a.forwardInfo.origin.getConstructor() != b.forwardInfo.origin.getConstructor() || a.forwardInfo.fromChatId != b.forwardInfo.fromChatId)
+ if (a.forwardInfo.origin.getConstructor() != b.forwardInfo.origin.getConstructor() || !Td.equalsTo(a.forwardInfo.source, b.forwardInfo.source, false))
return false;
if (splitAuthors) {
switch (a.forwardInfo.origin.getConstructor()) {
@@ -1094,7 +1094,7 @@ public static boolean canSendToSecretChat (TdApi.MessageContent content) {
return false;
}
default: {
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
}
}
return true;
@@ -1218,7 +1218,7 @@ public static boolean hasRestrictions (TdApi.ChatPermissions a, TdApi.ChatPermis
(a.canInviteUsers != defaultPermissions.canInviteUsers && defaultPermissions.canInviteUsers) ||
(a.canPinMessages != defaultPermissions.canPinMessages && defaultPermissions.canPinMessages) ||
(a.canChangeInfo != defaultPermissions.canChangeInfo && defaultPermissions.canChangeInfo) ||
- (a.canManageTopics != defaultPermissions.canManageTopics && defaultPermissions.canManageTopics);
+ (a.canCreateTopics != defaultPermissions.canCreateTopics && defaultPermissions.canCreateTopics);
}
public static int getCombineMode (TdApi.Message message) {
@@ -2028,17 +2028,11 @@ public static TdApi.User newFakeUser (long userId, String firstName, String last
TdlibAccentColor.defaultAccentColorIdForUserId(userId), 0,
0, 0,
null,
- false,
- false,
- false,
- false,
- false,
- false,
- null,
- false,
- false,
+ false, false, false,
+ false, false,
+ false, null, false, false,
false, false,
- true,
+ false, true,
new TdApi.UserTypeRegular(),
null,
false
@@ -5047,9 +5041,11 @@ public static boolean isScreenshotSensitive (TdApi.Message message) {
switch (message.content.getConstructor()) {
case TdApi.MessageExpiredPhoto.CONSTRUCTOR:
case TdApi.MessageExpiredVideo.CONSTRUCTOR:
+ case TdApi.MessageExpiredVoiceNote.CONSTRUCTOR:
+ case TdApi.MessageExpiredVideoNote.CONSTRUCTOR:
return true;
default:
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
break;
}
return false;
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGChat.java b/app/src/main/java/org/thunderdog/challegram/data/TGChat.java
index 0ab6507420..1421253531 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGChat.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGChat.java
@@ -1610,11 +1610,11 @@ private boolean setReactions (boolean animated) {
chat.lastMessage.interactionInfo.reactions : null, animated);
}
- private boolean setReactions (TdApi.MessageReaction[] reactions, boolean animated) {
+ private boolean setReactions (@Nullable TdApi.MessageReactions reactions, boolean animated) {
this.reactionsListEntry.clear();
- if (reactions != null && !isDestroyed) {
- for (TdApi.MessageReaction reaction : reactions) {
+ if (!Td.isEmpty(reactions) && !isDestroyed) {
+ for (TdApi.MessageReaction reaction : reactions.reactions) {
String reactionKey = TD.makeReactionKey(reaction.type);
TGReaction reactionObj = tdlib.getReaction(reaction.type);
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGCommentButton.java b/app/src/main/java/org/thunderdog/challegram/data/TGCommentButton.java
index 705ff79360..97b44b1708 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGCommentButton.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGCommentButton.java
@@ -284,8 +284,8 @@ public boolean onTouchEvent (View view, MotionEvent event) {
if (context.isRepliesChat()) {
TdApi.MessageForwardInfo forwardInfo = context.msg.forwardInfo;
MessageId replyToMessageId = MessageId.valueOf(context.msg.replyTo);
- if (forwardInfo != null && forwardInfo.fromChatId != 0 && forwardInfo.fromMessageId != 0) {
- MessageId replyMessageId = new MessageId(forwardInfo.fromChatId, forwardInfo.fromMessageId);
+ if (forwardInfo != null && forwardInfo.source != null && forwardInfo.source.chatId != 0 && forwardInfo.source.messageId != 0) {
+ MessageId replyMessageId = new MessageId(forwardInfo.source);
context.openMessageThread(replyMessageId, replyToMessageId);
} else if (replyToMessageId != null) {
context.openMessageThread(replyToMessageId);
@@ -645,8 +645,9 @@ private void openCommentsPreviewAsync (int x, int y) {
MessageId messageId, fallbackMessageId;
if (context.isRepliesChat()) {
MessageId replyToMessageId = MessageId.valueOf(context.msg.replyTo);
- if (context.msg.forwardInfo != null) {
- messageId = new MessageId(context.msg.forwardInfo.fromChatId, context.msg.forwardInfo.fromMessageId);
+ TdApi.MessageForwardInfo forwardInfo = context.msg.forwardInfo;
+ if (forwardInfo != null && forwardInfo.source != null && forwardInfo.source.chatId != 0 && forwardInfo.source.messageId != 0) {
+ messageId = new MessageId(forwardInfo.source);
fallbackMessageId = replyToMessageId;
} else {
messageId = replyToMessageId;
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGMessage.java b/app/src/main/java/org/thunderdog/challegram/data/TGMessage.java
index 914252995f..70342ecf16 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGMessage.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGMessage.java
@@ -410,10 +410,10 @@ public void onInvalidateReceiversRequested () {
break;
case TdApi.MessageOriginChannel.CONSTRUCTOR:
TdApi.MessageOriginChannel info = (TdApi.MessageOriginChannel) msg.forwardInfo.origin;
- if ((msg.forwardInfo.fromChatId == 0 && msg.forwardInfo.fromMessageId == 0)) {
- msg.forwardInfo.fromChatId = info.chatId;
- msg.forwardInfo.fromMessageId = info.messageId;
- }
+ /*FIXME?
+ if (!Td.hasMessageSource(msg.forwardInfo)) {
+ msg.forwardInfo.source = new TdApi.ForwardSource(info.chatId, info.messageId, null, "", 0, false);
+ }*/
break;
case TdApi.MessageOriginHiddenUser.CONSTRUCTOR:
break;
@@ -460,7 +460,7 @@ public void onInvalidateReceiversRequested () {
if (msg.isChannelPost || (msg.forwardInfo != null && (
msg.forwardInfo.origin.getConstructor() == TdApi.MessageOriginChannel.CONSTRUCTOR ||
TD.getViewCount(msg.interactionInfo) > 1 ||
- tdlib.isChannel(msg.forwardInfo.fromChatId) ||
+ tdlib.isChannel(msg.forwardInfo.source) ||
this.sender.isChannel()
))) {
this.viewCounter = new Counter.Builder()
@@ -997,7 +997,7 @@ protected final boolean needCommentButton () {
return messageWithThread != null && TD.getReplyInfo(messageWithThread.interactionInfo) != null;
}
if (isRepliesChat()) {
- return FeatureToggles.SHOW_VIEW_IN_CHAT_BUTTON_IN_REPLIES && msg.forwardInfo != null && msg.forwardInfo.fromChatId != 0 && msg.forwardInfo.fromMessageId != 0 && msg.forwardInfo.fromChatId != msg.chatId;
+ return FeatureToggles.SHOW_VIEW_IN_CHAT_BUTTON_IN_REPLIES && msg.forwardInfo != null && Td.hasMessageSource(msg.forwardInfo) && msg.forwardInfo.source.chatId != msg.chatId;
}
return false;
}
@@ -2155,7 +2155,7 @@ public final void draw (MessageView view, Canvas c, @NonNull AvatarReceiver avat
if (shouldShowMessageRestrictedWarning()) {
if (isRestrictedByTelegram()) {
- isRestricted.draw(c, right, top, Gravity.RIGHT, 1f, view, 0, isRestrictedCounterLastDrawRect);
+ isRestricted.draw(c, right, top, Gravity.RIGHT, 1f, view, ColorId.NONE, isRestrictedCounterLastDrawRect);
right -= isRestricted.getScaledWidth(Screen.dp(COUNTER_ICON_MARGIN));
} else {
isUnsupported.draw(c, right, top, Gravity.RIGHT, 1f, view, ColorId.iconLight, isRestrictedCounterLastDrawRect);
@@ -3143,10 +3143,11 @@ public final boolean forceForwardOrImportInfo () {
if (msg.importInfo != null) {
return true;
}
- return msg.forwardInfo != null && !isOutgoing() && (
+ TdApi.MessageForwardInfo forwardInfo = msg.forwardInfo;
+ return forwardInfo != null && !isOutgoing() && (
BitwiseUtils.hasFlag(flags, FLAG_SELF_CHAT) ||
- (isChannelAutoForward() && msg.forwardInfo.origin.getConstructor() == TdApi.MessageOriginChannel.CONSTRUCTOR &&
- msg.forwardInfo.fromChatId == ((TdApi.MessageOriginChannel) msg.forwardInfo.origin).chatId) ||
+ (isChannelAutoForward() && forwardInfo.origin.getConstructor() == TdApi.MessageOriginChannel.CONSTRUCTOR &&
+ forwardInfo.source != null && forwardInfo.source.chatId == ((TdApi.MessageOriginChannel) forwardInfo.origin).chatId) ||
(isPsa() && !sender.isUser() && useBubbles()) ||
isRepliesChat());
}
@@ -3999,7 +4000,7 @@ protected void drawBubbleTimePart (Canvas c, MessageView view) {
if (shouldShowMessageRestrictedWarning()) {
if (isRestrictedByTelegram()) {
- isRestricted.draw(c, startX, counterY, Gravity.LEFT, 1f, view, 0, isRestrictedCounterLastDrawRect);
+ isRestricted.draw(c, startX, counterY, Gravity.LEFT, 1f, view, ColorId.NONE, isRestrictedCounterLastDrawRect);
startX += isRestricted.getScaledWidth(Screen.dp(COUNTER_ICON_MARGIN));
} else {
isUnsupported.draw(c, startX, counterY, Gravity.LEFT, 1f, view, iconColorId, isRestrictedCounterLastDrawRect);
@@ -5309,17 +5310,17 @@ private boolean isSelfChat () {
}
public final boolean needMessageButton () {
- return ((flags & FLAG_SELF_CHAT) != 0 || isChannelAutoForward() || isRepliesChat()) && msg.forwardInfo != null && msg.forwardInfo.fromChatId != 0 && msg.forwardInfo.fromMessageId != 0 && msg.forwardInfo.fromChatId != msg.chatId;
+ return ((flags & FLAG_SELF_CHAT) != 0 || isChannelAutoForward() || isRepliesChat()) && Td.hasMessageSource(msg.forwardInfo) && msg.forwardInfo.source.chatId != msg.chatId;
}
public final void openSourceMessage () {
- if (msg.forwardInfo != null) {
+ if (Td.hasMessageSource(msg.forwardInfo)) {
if (isRepliesChat()) {
- MessageId replyMessageId = new MessageId(msg.forwardInfo.fromChatId, msg.forwardInfo.fromMessageId);
+ MessageId replyMessageId = new MessageId(msg.forwardInfo.source);
MessageId replyToMessageId = Td.toMessageId(msg.replyTo);
openMessageThread(replyMessageId, replyToMessageId);
} else {
- tdlib.ui().openMessage(controller(), msg.forwardInfo.fromChatId, new MessageId(msg.forwardInfo.fromChatId, msg.forwardInfo.fromMessageId), openParameters());
+ tdlib.ui().openMessage(controller(), msg.forwardInfo.source.chatId, new MessageId(msg.forwardInfo.source), openParameters());
}
}
}
@@ -5768,7 +5769,7 @@ private void updateInteractionInfo (boolean allowAnimation) {
}
if (reactionsCounter != null) {
int count = messageReactions.getTotalCount();
- if (tdlib.isUserChat(msg.chatId) && messageReactions.getReactions() != null && (count == 1 || messageReactions.getReactions().length > 1)) {
+ if (tdlib.isUserChat(msg.chatId) && messageReactions.getReactions() != null && (count == 1 || Td.reactionTypesCount(messageReactions.getReactions()) > 1)) {
count = 0;
}
reactionsCounter.setCount(count, !messageReactions.hasChosen(), animated);
@@ -8145,6 +8146,12 @@ public static TGMessage valueOf (MessagesManager context, TdApi.Message msg, TdA
case TdApi.MessageExpiredVideo.CONSTRUCTOR: {
return new TGMessageService(context, msg, (TdApi.MessageExpiredVideo) content);
}
+ case TdApi.MessageExpiredVoiceNote.CONSTRUCTOR: {
+ return new TGMessageService(context, msg, (TdApi.MessageExpiredVoiceNote) content);
+ }
+ case TdApi.MessageExpiredVideoNote.CONSTRUCTOR: {
+ return new TGMessageService(context, msg, (TdApi.MessageExpiredVideoNote) content);
+ }
case TdApi.MessagePinMessage.CONSTRUCTOR: {
return new TGMessageService(context, msg, (TdApi.MessagePinMessage) content);
}
@@ -8273,7 +8280,7 @@ public static TGMessage valueOf (MessagesManager context, TdApi.Message msg, TdA
break;
}
default: {
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
throw Td.unsupported(msg.content);
}
}
@@ -8750,7 +8757,7 @@ private Counter getReactionsCounter () {
.build();
int count = messageReactions.getTotalCount();
- if (tdlib.isUserChat(msg.chatId) && messageReactions.getReactions() != null && (count == 1 || messageReactions.getReactions().length > 1)) {
+ if (tdlib.isUserChat(msg.chatId) && messageReactions.getReactions() != null && (count == 1 || Td.reactionTypesCount(messageReactions.getReactions()) > 1)) {
count = 0;
}
reactionsCounter.setCount(count, !messageReactions.hasChosen(), false);
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGMessageService.java b/app/src/main/java/org/thunderdog/challegram/data/TGMessageService.java
index 78189ae4f9..6f27a5a531 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGMessageService.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGMessageService.java
@@ -175,6 +175,20 @@ public TGMessageService (MessagesManager context, TdApi.Message msg, TdApi.Messa
);
}
+ public TGMessageService (MessagesManager context, TdApi.Message msg, TdApi.MessageExpiredVoiceNote expiredVoiceNote) {
+ super(context, msg);
+ setTextCreator(() ->
+ getText(R.string.AttachVoiceNoteExpired)
+ );
+ }
+
+ public TGMessageService (MessagesManager context, TdApi.Message msg, TdApi.MessageExpiredVideoNote expiredVideoNote) {
+ super(context, msg);
+ setTextCreator(() ->
+ getText(R.string.AttachVideoNoteExpired)
+ );
+ }
+
public TGMessageService (MessagesManager context, TdApi.Message msg, TdApi.MessageProximityAlertTriggered proximityAlertTriggered) {
super(context, msg);
TdlibSender travelerSender = new TdlibSender(tdlib(), msg.chatId, proximityAlertTriggered.travelerId);
@@ -280,8 +294,13 @@ public FormattedText createText () {
staticResId = R.string.ActionPinnedVideo;
break;
case TdApi.MessageVoiceNote.CONSTRUCTOR:
+ case TdApi.MessageExpiredVoiceNote.CONSTRUCTOR:
staticResId = R.string.ActionPinnedVoice;
break;
+ case TdApi.MessageVideoNote.CONSTRUCTOR:
+ case TdApi.MessageExpiredVideoNote.CONSTRUCTOR:
+ staticResId = R.string.ActionPinnedRound;
+ break;
case TdApi.MessageSticker.CONSTRUCTOR:
staticResId = R.string.ActionPinnedSticker;
break;
@@ -298,9 +317,6 @@ public FormattedText createText () {
case TdApi.MessageVenue.CONSTRUCTOR:
staticResId = R.string.ActionPinnedGeo;
break;
- case TdApi.MessageVideoNote.CONSTRUCTOR:
- staticResId = R.string.ActionPinnedRound;
- break;
case TdApi.MessageContact.CONSTRUCTOR:
staticResId = R.string.ActionPinnedContact;
break;
@@ -360,7 +376,7 @@ public FormattedText createText () {
staticResId = R.string.ActionPinnedNoText;
break;
default:
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
throw Td.unsupported(message.content);
}
String format = Lang.getString(staticResId);
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGReactions.java b/app/src/main/java/org/thunderdog/challegram/data/TGReactions.java
index 20d77923a1..39b5c253ca 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGReactions.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGReactions.java
@@ -8,6 +8,7 @@
import android.view.MotionEvent;
import android.view.View;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.drinkless.tdlib.Client;
@@ -37,7 +38,9 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -54,7 +57,7 @@
public class TGReactions implements Destroyable, ReactionLoadListener {
private final Tdlib tdlib;
- private TdApi.MessageReaction[] reactions;
+ private @Nullable TdApi.MessageReactions reactions;
private final TGMessage parent;
@@ -72,7 +75,7 @@ public class TGReactions implements Destroyable, ReactionLoadListener {
private int height = 0;
private int lastLineWidth = 0;
- TGReactions (TGMessage parent, Tdlib tdlib, TdApi.MessageReaction[] reactions, MessageReactionsDelegate delegate) {
+ TGReactions (TGMessage parent, Tdlib tdlib, TdApi.MessageReactions reactions, MessageReactionsDelegate delegate) {
this.parent = parent;
this.delegate = delegate;
@@ -96,18 +99,18 @@ public void requestReactionFiles (ComplexReceiver complexReceiver) {
}
}
- public void setReactions (TdApi.MessageReaction[] reactions) {
+ public void setReactions (@Nullable TdApi.MessageReactions reactions) {
this.reactionsListEntry.clear();
this.tdReactionsMap.clear();
this.reactions = reactions;
this.chosenReactions.clear();
this.totalCount = 0;
- if (reactions == null || isDestroyed) {
+ if (isEmpty() || isDestroyed) {
return;
}
- for (TdApi.MessageReaction reaction : reactions) {
+ for (TdApi.MessageReaction reaction : reactions.reactions) {
String reactionKey = TD.makeReactionKey(reaction.type);
tdReactionsMap.put(reactionKey, reaction);
totalCount += reaction.totalCount;
@@ -134,52 +137,138 @@ public void setReactions (TdApi.MessageReaction[] reactions) {
}
}
- public void setReactions (ArrayList combinedMessages) {
- this.reactionsListEntry.clear();
- this.chosenReactions.clear();
- this.totalCount = 0;
+ private static @Nullable CombineResult combineReactions (List messages) {
+ if (messages == null || messages.isEmpty()) {
+ return null;
+ }
- HashMap reactionsHashMap = new HashMap<>();
+ List nonEmptyReactions = null;
- for (TdApi.Message message : combinedMessages) {
- if (message.interactionInfo == null) {
- continue;
- }
- if (message.interactionInfo.reactions == null) {
- continue;
+ for (TdApi.Message message : messages) {
+ if (message.interactionInfo != null && !Td.isEmpty(message.interactionInfo.reactions)) {
+ if (nonEmptyReactions == null) {
+ nonEmptyReactions = new ArrayList<>();
+ }
+ nonEmptyReactions.add(message.interactionInfo.reactions);
}
+ }
- for (TdApi.MessageReaction reaction : message.interactionInfo.reactions) {
- final String reactionKey = TD.makeReactionKey(reaction.type);
- TdApi.MessageReaction fakeReaction = reactionsHashMap.get(reactionKey);
- if (fakeReaction == null) {
- fakeReaction = new TdApi.MessageReaction(reaction.type, 0, false, null, new TdApi.MessageSender[0]);
- reactionsHashMap.put(reactionKey, fakeReaction);
- }
- fakeReaction.totalCount += reaction.totalCount;
- if (reaction.recentSenderIds != null && reaction.recentSenderIds.length > 0) {
- fakeReaction.recentSenderIds = reaction.recentSenderIds; // todo conact arrays ?
- }
- fakeReaction.isChosen = reaction.isChosen;
+ if (nonEmptyReactions != null) {
+ return combineReactions(nonEmptyReactions.toArray(new TdApi.MessageReactions[0]));
+ }
+
+ return null;
+ }
+
+ private static class CombineResult {
+ public final TdApi.MessageReactions reactions;
+ public final int totalCount;
+ public final String[] chosenReactions;
+
+ public CombineResult (TdApi.MessageReactions reactions, int totalCount, String[] chosenReactions) {
+ this.reactions = reactions;
+ this.totalCount = totalCount;
+ this.chosenReactions = chosenReactions;
+ }
+ }
+
+ private static @NonNull CombineResult combineReactions (@NonNull TdApi.MessageReactions[] allReactions) {
+ int totalCount = 0;
+ Set chosenReactions = new LinkedHashSet<>();
+ TdApi.MessageReactions result;
+
+ if (allReactions.length == 1) {
+ result = allReactions[0];
+ for (TdApi.MessageReaction reaction : result.reactions) {
totalCount += reaction.totalCount;
if (reaction.isChosen) {
- chosenReactions.add(reactionKey);
+ chosenReactions.add(TD.makeReactionKey(reaction.type));
}
}
+ return new CombineResult(
+ result,
+ totalCount,
+ chosenReactions.toArray(new String[0])
+ );
+ } else {
+ Map map = new LinkedHashMap<>();
+ for (TdApi.MessageReactions reactions : allReactions) {
+ for (TdApi.MessageReaction reaction : reactions.reactions) {
+ final String reactionKey = TD.makeReactionKey(reaction.type);
+
+ TdApi.MessageReaction combinedReaction = map.get(reactionKey);
+ if (combinedReaction == null) {
+ combinedReaction = new TdApi.MessageReaction(
+ reaction.type,
+ 0,
+ false,
+ null,
+ new TdApi.MessageSender[0]
+ );
+ map.put(reactionKey, combinedReaction);
+ }
+
+ combinedReaction.totalCount += reaction.totalCount;
+ combinedReaction.recentSenderIds = concatSenders(combinedReaction.recentSenderIds, reaction.recentSenderIds);
+ if (reaction.isChosen) {
+ combinedReaction.isChosen = true;
+ chosenReactions.add(reactionKey);
+ }
+ totalCount += reaction.totalCount;
+ }
+ }
+ result = new TdApi.MessageReactions(
+ map.values().toArray(new TdApi.MessageReaction[0]),
+ allReactions[0].areTags
+ );
+ Arrays.sort(result.reactions, (a, b) -> Integer.compare(b.totalCount, a.totalCount));
}
- TdApi.MessageReaction[] combinedReactionsArray = new TdApi.MessageReaction[reactionsHashMap.size()];
- int i = 0;
- for (Map.Entry pair : reactionsHashMap.entrySet()) {
- combinedReactionsArray[i++] = pair.getValue();
+ return new CombineResult(
+ result,
+ totalCount,
+ chosenReactions.toArray(new String[0])
+ );
+ }
+
+ private static TdApi.MessageSender[] concatSenders (TdApi.MessageSender[] a, TdApi.MessageSender[] b) {
+ if (a == null || a.length == 0) {
+ return b;
+ }
+ if (b == null || b.length == 0) {
+ return a;
+ }
+ List result = new ArrayList<>(a.length + b.length);
+ Set existingSenderIds = new LinkedHashSet<>();
+ for (TdApi.MessageSender sender : a) {
+ long senderId = Td.getSenderId(sender);
+ existingSenderIds.add(senderId);
}
+ Collections.addAll(result, a);
+ for (TdApi.MessageSender sender : b) {
+ long senderId = Td.getSenderId(sender);
+ if (!existingSenderIds.contains(senderId)) {
+ result.add(sender);
+ }
+ }
+ return result.toArray(new TdApi.MessageSender[0]);
+ }
- Arrays.sort(combinedReactionsArray, (a, b) -> b.totalCount - a.totalCount);
- setReactions(combinedReactionsArray);
+ public void setReactions (ArrayList combinedMessages) {
+ this.reactionsListEntry.clear();
+ this.chosenReactions.clear();
+ this.totalCount = 0;
+
+ CombineResult result = combineReactions(combinedMessages);
+ if (result != null) {
+ this.totalCount = result.totalCount;
+ Collections.addAll(this.chosenReactions, result.chosenReactions);
+ setReactions(result.reactions);
+ }
}
@Nullable
- public TdApi.MessageReaction[] getReactions () {
+ public TdApi.MessageReactions getReactions () {
return reactions;
}
@@ -217,12 +306,12 @@ public void onUpdateTextSize () {
}
public void updateCounterAnimators (boolean animated) {
- if (reactions == null) {
+ if (isEmpty()) {
return;
}
final int mode = Settings.instance().getReactionAvatarsMode();
- for (TdApi.MessageReaction reaction : reactions) {
+ for (TdApi.MessageReaction reaction : reactions.reactions) {
String reactionKey = TD.makeReactionKey(reaction.type);
TGReactions.MessageReactionEntry entry = reactionsMapEntry.get(reactionKey);
if (entry != null) {
@@ -256,14 +345,18 @@ private TdApi.MessageSender[] getRecentSenderIds (TdApi.MessageReaction reaction
return ArrayUtils.filter(sendersPreFiltered, (item) -> parent.matchesReactionSenderAvatarFilter(msgText, reaction, item)).toArray(new TdApi.MessageSender[0]);
}
+ public boolean isEmpty () {
+ return reactions == null || Td.isEmpty(reactions);
+ }
+
public void requestAvatarFiles (ComplexReceiver complexReceiver, boolean isUpdate) {
- if (reactions == null) {
+ if (isEmpty()) {
return;
}
if (!isUpdate) {
complexReceiver.clear();
}
- for (TdApi.MessageReaction reaction : reactions) {
+ for (TdApi.MessageReaction reaction : reactions.reactions) {
String reactionKey = TD.makeReactionKey(reaction.type);
TGReactions.MessageReactionEntry entry = reactionsMapEntry.get(reactionKey);
if (entry != null) {
@@ -273,7 +366,7 @@ public void requestAvatarFiles (ComplexReceiver complexReceiver, boolean isUpdat
}
public void resetReactionsAnimator (boolean animated) {
- if (reactions == null) {
+ if (isEmpty()) {
reactionsAnimator.clear(animated);
return;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/data/ThreadInfo.java b/app/src/main/java/org/thunderdog/challegram/data/ThreadInfo.java
index 5a754b9fd9..f96f7da280 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/ThreadInfo.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/ThreadInfo.java
@@ -62,7 +62,7 @@ private ThreadInfo (@Nullable Tdlib tdlib, @NonNull TdApi.MessageThreadInfo thre
boolean areComments = tdlib.isChannelAutoForward(oldestMessage);
if (contextChatId == 0 && areComments && oldestMessage != null && chatId != oldestMessage.chatId) {
//noinspection ConstantConditions
- contextChatId = oldestMessage.forwardInfo.fromChatId;
+ contextChatId = oldestMessage.forwardInfo.source.chatId;
}
return new ThreadInfo(tdlib, threadInfo, contextChatId, areComments);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/emoji/Emoji.java b/app/src/main/java/org/thunderdog/challegram/emoji/Emoji.java
index b220621fe1..e7df00c1c1 100644
--- a/app/src/main/java/org/thunderdog/challegram/emoji/Emoji.java
+++ b/app/src/main/java/org/thunderdog/challegram/emoji/Emoji.java
@@ -80,6 +80,19 @@ public static Emoji instance () {
private final CountLimiter singleLimiter = newSingleLimiter();
+ public static String cleanupEmoji (String emoji) {
+ if (StringUtils.isEmpty(emoji)) {
+ return emoji;
+ }
+ StringBuilder b = new StringBuilder(emoji);
+ int end = b.length();
+ while (b.charAt(end - 1) == '\uFE0F') {
+ b.delete(end - 1, end);
+ end--;
+ }
+ return b.toString();
+ }
+
public static boolean equals (String a, String b) {
int end1 = a.length();
while (end1 > 0 && a.charAt(end1 - 1) == '\uFE0F') {
diff --git a/app/src/main/java/org/thunderdog/challegram/helper/InlineSearchContext.java b/app/src/main/java/org/thunderdog/challegram/helper/InlineSearchContext.java
index 6d60c2cff7..d9af78a993 100644
--- a/app/src/main/java/org/thunderdog/challegram/helper/InlineSearchContext.java
+++ b/app/src/main/java/org/thunderdog/challegram/helper/InlineSearchContext.java
@@ -55,7 +55,9 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Set;
import me.vkryl.core.StringUtils;
import me.vkryl.core.lambda.CancellableRunnable;
@@ -1270,31 +1272,31 @@ public void act () {
}
if (!StringUtils.isEmpty(query)) {
- tdlib.client().send(new TdApi.SearchEmojis(query, false, U.getInputLanguages()), result -> {
- if (result.getConstructor() == TdApi.Emojis.CONSTRUCTOR) {
- TdApi.Emojis emojis = (TdApi.Emojis) result;
- ArrayList> addedResults = new ArrayList<>(emojis.emojis.length);
- for (String emoji : emojis.emojis) {
- boolean found = false;
- if (inlineResults != null) {
- for (InlineResult> existingResult : inlineResults) {
- if (existingResult instanceof InlineResultEmojiSuggestion && Emoji.equals(emoji, ((InlineResultEmojiSuggestion) existingResult).getEmoji())) {
- found = true;
- break;
- }
- }
- }
- if (!found) {
- addedResults.add(new InlineResultEmojiSuggestion(context, tdlib, new N.Suggestion(emoji, null, null), null).setTarget(startIndex, endIndex));
+ final String searchQuery = query;
+ Set addedEmojis = new LinkedHashSet<>();
+ if (inlineResults != null) {
+ for (InlineResult> existingResult : inlineResults) {
+ if (existingResult instanceof InlineResultEmojiSuggestion) {
+ String emoji = ((InlineResultEmojiSuggestion) existingResult).getEmoji();
+ addedEmojis.add(Emoji.cleanupEmoji(emoji));
+ }
+ }
+ }
+ tdlib.send(new TdApi.SearchEmojis(searchQuery, U.getInputLanguages()), (keywords, error) -> {
+ if (keywords != null) {
+ ArrayList> extraResults = new ArrayList<>(keywords.emojiKeywords.length);
+ for (TdApi.EmojiKeyword keyword : keywords.emojiKeywords) {
+ if (addedEmojis.add(Emoji.cleanupEmoji(keyword.emoji))) {
+ extraResults.add(new InlineResultEmojiSuggestion(context, tdlib, new N.Suggestion(keyword.emoji, !StringUtils.isEmpty(keyword.keyword) ? keyword.keyword : null, null), searchQuery).setTarget(startIndex, endIndex));
}
}
- if (!addedResults.isEmpty()) {
+ if (!extraResults.isEmpty()) {
tdlib.ui().post(() -> {
if (isPending() && StringUtils.equalsOrBothEmpty(currentInput, currentText)) {
if (inlineResults == null || inlineResults.isEmpty()) {
- showEmojiSuggestions(addedResults);
+ showEmojiSuggestions(extraResults);
} else {
- inlineResults.addAll(addedResults);
+ inlineResults.addAll(extraResults);
showEmojiSuggestions(inlineResults);
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewController.java b/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewController.java
index 33bbd963ca..5099f32193 100644
--- a/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewController.java
+++ b/app/src/main/java/org/thunderdog/challegram/mediaview/MediaViewController.java
@@ -224,6 +224,7 @@ public static class Args {
private boolean reverseMode;
private long receiverChatId, messageThreadId;
+ private @Nullable TdApi.SavedMessagesTopic savedMessagesTopic;
private boolean areOnlyScheduled, deleteOnExit;
@@ -280,6 +281,11 @@ public Args setMessageThreadId (long messageThreadId) {
return this;
}
+ public Args setSavedMessagesTopic (@Nullable TdApi.SavedMessagesTopic savedMessagesTopic) {
+ this.savedMessagesTopic = savedMessagesTopic;
+ return this;
+ }
+
public @Nullable TdApi.SearchMessagesFilter filter;
public void setFilter (@Nullable TdApi.SearchMessagesFilter filter) {
@@ -307,6 +313,7 @@ public int getId () {
private MediaStack stack;
private @Nullable TdApi.SearchMessagesFilter filter;
private long messageThreadId;
+ private @Nullable TdApi.SavedMessagesTopic savedMessagesTopic;
@Override
public void setArguments (Args args) {
@@ -319,6 +326,7 @@ public void setArguments (Args args) {
this.reverseMode = args.reverseMode;
this.filter = args.filter;
this.messageThreadId = args.messageThreadId;
+ this.savedMessagesTopic = args.savedMessagesTopic;
}
public MediaViewThumbLocation getCurrentTargetLocation () {
@@ -2068,7 +2076,8 @@ private void loadMoreIfNeeded (boolean edgeReached, boolean isEnd) {
chatId, null, null,
initialFromMessageId, 0,
LOAD_COUNT, searchFilter(),
- messageThreadId
+ messageThreadId,
+ savedMessagesTopic
);
tdlib.client().send(searchFunction, foundChatMessagesHandler(chatId, initialFromMessageId, LOAD_COUNT));
}
@@ -2084,7 +2093,8 @@ LOAD_COUNT, searchFilter(),
chatId, null, null,
initialFromMessageId, 0,
LOAD_COUNT_PROFILE, searchFilter(),
- messageThreadId
+ messageThreadId,
+ savedMessagesTopic
);
tdlib.client().send(searchFunction, foundChatMessagesHandler(chatId, initialFromMessageId, LOAD_COUNT_PROFILE));
}
@@ -2191,7 +2201,8 @@ private void addItems (long chatId, long fromMessageId, int loadCount, TdApi.Fou
chatId, null, null,
messages.nextFromMessageId, 0,
loadCount, searchFilter(),
- messageThreadId
+ messageThreadId,
+ savedMessagesTopic
);
tdlib.client().send(retryFunction, foundChatMessagesHandler(chatId, messages.nextFromMessageId, loadCount));
return;
diff --git a/app/src/main/java/org/thunderdog/challegram/player/RecordAudioVideoController.java b/app/src/main/java/org/thunderdog/challegram/player/RecordAudioVideoController.java
index 9ffb8156b8..62ac74a731 100644
--- a/app/src/main/java/org/thunderdog/challegram/player/RecordAudioVideoController.java
+++ b/app/src/main/java/org/thunderdog/challegram/player/RecordAudioVideoController.java
@@ -1543,7 +1543,7 @@ public void onVideoRecordingFinished (String key, long resultFileSize, long resu
finishFileGeneration(resultFileSize);
} else {
finishFileGeneration(resultFileSize);
- sendVideoNote(new TdApi.InputMessageVideoNote(new TdApi.InputFileId(roundFile.id), null, savedRoundDurationSeconds, VIDEO_NOTE_LENGTH), Td.newSendOptions(), roundFile);
+ sendVideoNote(new TdApi.InputMessageVideoNote(new TdApi.InputFileId(roundFile.id), null, savedRoundDurationSeconds, VIDEO_NOTE_LENGTH, null), Td.newSendOptions(), roundFile);
}
} else {
finishFileGeneration(-1);
@@ -1670,9 +1670,9 @@ private void closeVideoEditMode (@NonNull TdApi.MessageSendOptions initialSendOp
double endTimeSeconds = videoPreviewView.getEndTime();
String conversion = VideoGenerationInfo.makeConversion(roundFile.id, false, 0, (long) (startTimeSeconds * 1_000_000), (long) (endTimeSeconds * 1_000_000), true, 0);
TdApi.InputFileGenerated trimmedFile = new TdApi.InputFileGenerated(roundFile.local.path, conversion, 0);
- sendVideoNote(new TdApi.InputMessageVideoNote(trimmedFile, null, (int) Math.round(endTimeSeconds - startTimeSeconds), VIDEO_NOTE_LENGTH), initialSendOptions, null);
+ sendVideoNote(new TdApi.InputMessageVideoNote(trimmedFile, null, (int) Math.round(endTimeSeconds - startTimeSeconds), VIDEO_NOTE_LENGTH, null), initialSendOptions, null);
} else {
- sendVideoNote(new TdApi.InputMessageVideoNote(new TdApi.InputFileId(roundFile.id), null, savedRoundDurationSeconds, VIDEO_NOTE_LENGTH), initialSendOptions, roundFile);
+ sendVideoNote(new TdApi.InputMessageVideoNote(new TdApi.InputFileId(roundFile.id), null, savedRoundDurationSeconds, VIDEO_NOTE_LENGTH, null), initialSendOptions, roundFile);
}
} else {
tdlib.client().send(new TdApi.DeleteFile(roundFile.id), tdlib.silentHandler());
diff --git a/app/src/main/java/org/thunderdog/challegram/player/TGPlayerController.java b/app/src/main/java/org/thunderdog/challegram/player/TGPlayerController.java
index f32d7c43e5..15b67407ad 100644
--- a/app/src/main/java/org/thunderdog/challegram/player/TGPlayerController.java
+++ b/app/src/main/java/org/thunderdog/challegram/player/TGPlayerController.java
@@ -155,6 +155,7 @@ public static boolean compareTrackFiles (TdApi.File a, TdApi.File b) {
private long playlistChatId;
private long playlistMessageThreadId;
+ private TdApi.SavedMessagesTopic savedMessagesTopic;
private long playlistMaxMessageId, playlistMinMessageId;
private String playlistSearchQuery;
private TdApi.GetInlineQueryResults playlistInlineQuery;
@@ -1522,7 +1523,8 @@ private void prepareStack (boolean allowNewer, boolean allowOlder, int forceReas
chatId, playlistSearchQuery, null,
playlistSearchNextFromMessageId != 0 ? Math.min(minMessageId, playlistSearchNextFromMessageId) : minMessageId,
0, 100, filter,
- playlistMessageThreadId
+ playlistMessageThreadId,
+ null
) : null;
requestNew = allowNewer ? playlistInlineQuery != null ? makeNextInlineQuery() : new TdApi.SearchChatMessages(
chatId,
@@ -1530,7 +1532,8 @@ private void prepareStack (boolean allowNewer, boolean allowOlder, int forceReas
maxMessageId,
-99, 100,
filter,
- playlistMessageThreadId
+ playlistMessageThreadId,
+ null
) : null;
}
@@ -1894,7 +1897,7 @@ public PlayList setSearchQuery (String query) {
}
/**
- * Message thread identifier to be passed in {@link org.drinkless.tdlib.telegram.TdApi.SearchChatMessages} query
+ * Message thread identifier to be passed in {@link org.drinkless.tdlib.TdApi.SearchChatMessages} query
*/
public PlayList setMessageThreadId (long messageThreadId) {
this.messageThreadId = messageThreadId;
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/MessageListManager.java b/app/src/main/java/org/thunderdog/challegram/telegram/MessageListManager.java
index 9f4829123b..f6151ed13d 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/MessageListManager.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/MessageListManager.java
@@ -39,6 +39,7 @@ public interface ChangeListener extends ListManager.ListChangeListener {
+ tdlib.send(new TdApi.GetChatMessageCount(chatId, filter, savedMessagesTopic, local), (chatMessageCount, error) -> {
final int count;
if (error != null) {
Log.e("GetChatMessageCount: %s, filter:%s, chatId:%s", TD.toErrorString(error), filter, chatId);
@@ -139,7 +142,7 @@ private void fetchMessageCount (boolean local, @Nullable RunnableInt callback) {
}
return;
}
- tdlib.send(new TdApi.SearchChatMessages(chatId, query, sender, 0, 0, 1, filter, messageThreadId), (foundChatMessages, error) -> {
+ tdlib.send(new TdApi.SearchChatMessages(chatId, query, sender, 0, 0, 1, filter, messageThreadId, savedMessagesTopic), (foundChatMessages, error) -> {
final int count;
if (error != null) {
Log.e("SearchChatMessages: %s, chatId: %d", TD.toErrorString(error), chatId);
@@ -216,9 +219,9 @@ protected TdApi.Function> nextLoadFunction (boolean reverse, int itemCount, in
long fromMessageId = this.items.isEmpty() ? startFromMessageId : this.items.get(reverse ? 0 : this.items.size() - 1).id;
if (hasFilter()) {
if (reverse) {
- return new TdApi.SearchChatMessages(chatId, query, sender, fromMessageId, -loadCount, loadCount + 1, filter, messageThreadId);
+ return new TdApi.SearchChatMessages(chatId, query, sender, fromMessageId, -loadCount, loadCount + 1, filter, messageThreadId, savedMessagesTopic);
} else {
- return new TdApi.SearchChatMessages(chatId, query, sender, nextSearchFromMessageId != null && nextSearchFromMessageId != 0 ? nextSearchFromMessageId : fromMessageId, 0, loadCount, filter, messageThreadId);
+ return new TdApi.SearchChatMessages(chatId, query, sender, nextSearchFromMessageId != null && nextSearchFromMessageId != 0 ? nextSearchFromMessageId : fromMessageId, 0, loadCount, filter, messageThreadId, savedMessagesTopic);
}
} else {
if (reverse) {
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
index 3f3c21dbb1..0354a2474d 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
@@ -357,12 +357,8 @@ public void init () {
client.send(new TdApi.GetApplicationConfig(), tdlib.configHandler);
}
- public void sendFakeUpdate (TdApi.Update update, boolean forceUi) {
- if (forceUi) {
- tdlib.processUpdate(this, update);
- } else {
- runOnTdlibThread(() -> tdlib.processUpdate(this, update));
- }
+ public void sendFakeUpdate (TdApi.Update update) {
+ runOnTdlibThread(() -> tdlib.processUpdate(this, update));
}
@Override
@@ -569,9 +565,7 @@ public TdlibCounter getCounter (@NonNull TdApi.ChatList chatList) {
BuildConfig.TELEGRAM_API_ID,
BuildConfig.TELEGRAM_API_HASH,
null, null, null, // updateParameters
- BuildConfig.VERSION_NAME,
- false,
- false
+ BuildConfig.VERSION_NAME
);
} else {
this.parameters = new TdApi.SetTdlibParameters(
@@ -584,9 +578,7 @@ public TdlibCounter getCounter (@NonNull TdApi.ChatList chatList) {
BuildConfig.TELEGRAM_API_ID,
BuildConfig.TELEGRAM_API_HASH,
null, null, null, // updateParameters
- BuildConfig.VERSION_NAME,
- false,
- false
+ BuildConfig.VERSION_NAME
);
}
@@ -3999,6 +3991,10 @@ public boolean isUser (TdApi.MessageSender senderId) {
return chatUserId(chatId) != 0;
}
+ public boolean isChannel (@Nullable TdApi.ForwardSource source) {
+ return source != null && source.chatId != 0 && isChannel(source.chatId);
+ }
+
public boolean isChannel (long chatId) {
TdApi.Supergroup supergroup = chatToSupergroup(chatId);
return supergroup != null && supergroup.isChannel;
@@ -4011,11 +4007,11 @@ public boolean isSupergroup (long chatId) {
public boolean isChannelAutoForward (@Nullable TdApi.Message message) {
return message != null && message.chatId != 0 && message.forwardInfo != null &&
- message.forwardInfo.fromChatId != 0 && message.forwardInfo.fromMessageId != 0 &&
- message.forwardInfo.fromChatId != message.chatId &&
+ Td.hasMessageSource(message.forwardInfo) &&
+ message.forwardInfo.source.chatId != message.chatId &&
message.senderId.getConstructor() == TdApi.MessageSenderChat.CONSTRUCTOR &&
- ((TdApi.MessageSenderChat) message.senderId).chatId == message.forwardInfo.fromChatId &&
- isSupergroup(message.chatId) && isChannel(message.forwardInfo.fromChatId);
+ ((TdApi.MessageSenderChat) message.senderId).chatId == message.forwardInfo.source.chatId &&
+ isSupergroup(message.chatId) && isChannel(message.forwardInfo.source.chatId);
}
public boolean canDisablePinnedMessageNotifications (long chatId) {
@@ -4754,7 +4750,7 @@ public TdApi.FormattedText getPendingFormattedText (long chatId, long messageId)
case TdApi.MessageAnimatedEmoji.CONSTRUCTOR:
return Td.textOrCaption(messageText);
}
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
throw Td.unsupported(messageText);
}
return getPendingMessageCaption(chatId, messageId);
@@ -9458,31 +9454,6 @@ private void updateDefaultReactionType (TdApi.UpdateDefaultReactionType update)
}
}
- /*private void updateReactions (TdApi.UpdateReactions update) {
- synchronized (dataLock) {
- HashMap supportedTGReactionsMap = new HashMap<>();
- ArrayList notPremiumReactions = new ArrayList<>();
- ArrayList onlyPremiumReactions = new ArrayList<>();
-
- for (int a = 0; a < update.reactions.length; a++) {
- TdApi.Reaction reaction = update.reactions[a];
- TGReaction tgReaction = new TGReaction(this, reaction);
- supportedTGReactionsMap.put(reaction.reaction, tgReaction);
- if (reaction.isActive) {
- if (reaction.isPremium) {
- onlyPremiumReactions.add(tgReaction);
- } else {
- notPremiumReactions.add(tgReaction);
- }
- }
- }
-
- this.supportedTGReactionsMap = supportedTGReactionsMap;
- this.notPremiumReactions = notPremiumReactions;
- this.onlyPremiumReactions = onlyPremiumReactions;
- }
- }*/
-
private void updateStickerSet (TdApi.StickerSet stickerSet) {
animatedTgxEmoji.update(this, stickerSet);
animatedDiceExplicit.update(this, stickerSet);
@@ -9519,90 +9490,8 @@ private void updateFileGenerationStop (TdApi.UpdateFileGenerationStop update) {
filegen().updateFileGenerationStop(update);
}
- // Updates: ENTRY
-
- /*public void doFake () {
- // Captain Raptor =
- // Lucky = 472280754
-
- TdApi.Chat captainRaptor = chat(73083932);
- TdApi.Chat lucky = chat(472280754);
- TdApi.Chat kepler = chat(636750);
- TdApi.Chat sajil = chat(47518346);
- TdApi.Chat nepho = chat(213963390);
-
-
- if (captainRaptor == null || lucky == null || kepler == null || sajil == null || nepho == null) {
- return;
- }
-
- sendFakeUpdate(new TdApi.UpdateUserChatAction(captainRaptor.id, (int) captainRaptor.id, new TdApi.ChatActionTyping()), false);
- sendFakeUpdate(new TdApi.UpdateUserChatAction(lucky.id, (int) lucky.id, new TdApi.ChatActionRecordingVoiceNote()), false);
- sendFakeUpdate(new TdApi.UpdateUserChatAction(kepler.id, (int) kepler.id, new TdApi.ChatActionRecordingVideoNote()), false);
- sendFakeUpdate(new TdApi.UpdateUserChatAction(nepho.id, (int) nepho.id, new TdApi.ChatActionStartPlayingGame()), false);
- int stepsCount = 8;
- int factorPerStep = 100 / stepsCount;
- long delay = 300;
- for (int i = 0; i <= stepsCount; i++) {
- final int progress = i * factorPerStep;
- if (i == 0) {
- sendFakeUpdate(new TdApi.UpdateUserChatAction(sajil.id, (int) sajil.id, new TdApi.ChatActionUploadingPhoto()), false);
- } else {
- UI.post(new Runnable() {
- @Override
- public void run () {
- sendFakeUpdate(new TdApi.UpdateUserChatAction(sajil.id, (int) sajil.id, new TdApi.ChatActionUploadingPhoto(progress)), false);
- }
- }, delay * i);
- if (i == stepsCount) {
- UI.post(new Runnable() {
- @Override
- public void run () {
- sendFakeMessage(captainRaptor, new TdApi.MessageText(new TdApi.FormattedText("Hello!", null), null));
- sendFakeMessage(lucky, new TdApi.MessageVoiceNote(null, new TdApi.FormattedText(), false));
- sendFakeMessage(kepler, new TdApi.MessageVideoNote(null, false, false));
- sendFakeMessage(sajil, new TdApi.MessagePhoto(null, new TdApi.FormattedText(), false));
- sendFakeMessage(nepho, new TdApi.MessageGameScore(0, 0, 9138));
-
- sendFakeUpdate(new TdApi.UpdateUserChatAction(captainRaptor.id, (int) captainRaptor.id, new TdApi.ChatActionCancel()), false);
- sendFakeUpdate(new TdApi.UpdateUserChatAction(lucky.id, (int) lucky.id, new TdApi.ChatActionCancel()), false);
- sendFakeUpdate(new TdApi.UpdateUserChatAction(kepler.id, (int) kepler.id, new TdApi.ChatActionCancel()), false);
- sendFakeUpdate(new TdApi.UpdateUserChatAction(sajil.id, (int) sajil.id, new TdApi.ChatActionCancel()), false);
- sendFakeUpdate(new TdApi.UpdateUserChatAction(nepho.id, (int) nepho.id, new TdApi.ChatActionCancel()), false);
- }
- }, delay * (i + 1));
- }
- }
- }
- }
-
- private void sendFakeMessage (TdApi.Chat chat, TdApi.MessageContent content) {
- TdApi.Message message = TD.newFakeMessage(content);
- message.chatId = chat.id;
- message.senderUserId = (int) chat.id;
- message.date = (int) (System.currentTimeMillis() / 1000l);
- sendFakeUpdate(new TdApi.UpdateChatReadInbox(chat.id, chat.lastReadInboxMessageId, chat.unreadCount + 1), false);
- // sendFakeUpdate(new TdApi.UpdateNewMessage(message, false, false), false);
- sendFakeUpdate(new TdApi.UpdateChatLastMessage(chat.id, message, chat.order), false);
- }*/
-
- public void sendFakeUpdate (TdApi.Update update, boolean forceUi) {
- clientHolder().sendFakeUpdate(update, forceUi);
- }
-
- public void __sendFakeAction (TdApi.ChatAction action) {
- ArrayList chats;
- synchronized (dataLock) {
- chats = new ArrayList<>(this.chats.size());
- for (Map.Entry chat : this.chats.entrySet()) {
- chats.add(chat.getValue());
- }
- }
- for (TdApi.Chat chat : chats) {
- if (chat.lastMessage != null) {
- sendFakeUpdate(new TdApi.UpdateChatAction(chat.id, 0, chat.lastMessage.senderId, action), false);
- }
- }
+ public void sendFakeUpdate (TdApi.Update update) {
+ clientHolder().sendFakeUpdate(update);
}
private void processUpdate (ClientHolder context, TdApi.Update update) {
@@ -9730,6 +9619,16 @@ private void processUpdate (ClientHolder context, TdApi.Update update) {
break;
}
+ // Saved messages
+ case TdApi.UpdatePinnedSavedMessagesTopics.CONSTRUCTOR: {
+ // TODO
+ break;
+ }
+ case TdApi.UpdateSavedMessagesTags.CONSTRUCTOR: {
+ // TODO
+ break;
+ }
+
// Reactions
case TdApi.UpdateActiveEmojiReactions.CONSTRUCTOR: {
updateActiveEmojiReactions((TdApi.UpdateActiveEmojiReactions) update);
@@ -10119,7 +10018,7 @@ private void processUpdate (ClientHolder context, TdApi.Update update) {
throw Td.unsupported(update);
}
default: {
- Td.assertUpdate_618db8c7();
+ Td.assertUpdate_f1964de1();
throw Td.unsupported(update);
}
}
@@ -10389,7 +10288,7 @@ void fetchAllMessages (long chatId, @Nullable String query, @Nullable TdApi.Sear
boolean needFilter = !StringUtils.isEmpty(query) || filter != null;
TdApi.Function> function;
if (needFilter) {
- function = new TdApi.SearchChatMessages(chatId, query, null, 0, 0, 100, filter, 0);
+ function = new TdApi.SearchChatMessages(chatId, query, null, 0, 0, 100, filter, 0, null);
} else {
function = new TdApi.GetChatHistory(chatId, 0, 0, 0, false);
}
@@ -11040,8 +10939,10 @@ public CharSequence getRestrictionText (TdApi.Chat chat, TdApi.Message message)
case TdApi.MessageStory.CONSTRUCTOR:
return getStoryRestrictionText(chat);
case TdApi.MessageVideoNote.CONSTRUCTOR:
+ case TdApi.MessageExpiredVideoNote.CONSTRUCTOR:
return getDefaultRestrictionText(chat, RightId.SEND_VIDEO_NOTES);
case TdApi.MessageVoiceNote.CONSTRUCTOR:
+ case TdApi.MessageExpiredVoiceNote.CONSTRUCTOR:
return getDefaultRestrictionText(chat, RightId.SEND_VOICE_NOTES);
// RightId.SEND_BASIC_MESSAGES
case TdApi.MessageText.CONSTRUCTOR:
@@ -11106,7 +11007,7 @@ public CharSequence getRestrictionText (TdApi.Chat chat, TdApi.Message message)
// assuming we want to check RightId.SEND_BASIC_MESSAGES
return getBasicMessageRestrictionText(chat);
default:
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
throw Td.unsupported(message.content);
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibResourceManager.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibResourceManager.java
index 697badd2ff..904dd86eb7 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibResourceManager.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibResourceManager.java
@@ -86,7 +86,7 @@ public void findResource (RunnableData onDone, String query, long
}
tdlib.incrementJobReferenceCount();
tdlib.openChat(chatId, null, () -> {
- tdlib.client().send(new TdApi.SearchChatMessages(chatId, query, null, 0, 0, 1, new TdApi.SearchMessagesFilterDocument(), 0), result -> {
+ tdlib.client().send(new TdApi.SearchChatMessages(chatId, query, null, 0, 0, 1, new TdApi.SearchMessagesFilterDocument(), 0, null), result -> {
switch (result.getConstructor()) {
case TdApi.FoundChatMessages.CONSTRUCTOR: {
TdApi.FoundChatMessages messages = (TdApi.FoundChatMessages) result;
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java
index 40b79c7764..8da681a2c9 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java
@@ -411,7 +411,7 @@ private static boolean deleteSuperGroupMessages (final ViewController> context
}
});
// TODO TDLib / server: ability to get totalCount with limit=0
- tdlib.client().send(new TdApi.SearchChatMessages(chatId, null, senderId, 0, 0, 1, null, 0), result -> {
+ tdlib.client().send(new TdApi.SearchChatMessages(chatId, null, senderId, 0, 0, 1, null, 0, null), result -> {
if (result.getConstructor() == TdApi.FoundChatMessages.CONSTRUCTOR) {
int moreCount = ((TdApi.FoundChatMessages) result).totalCount - deletingMessages.length;
if (moreCount > 0) {
@@ -2405,7 +2405,7 @@ public void openMessage (final TdlibDelegate context, final TdApi.MessageLinkInf
if (Config.SHOW_CHANNEL_POST_REPLY_INFO_IN_COMMENTS) {
TdApi.Message message = messageThread.getOldestMessage();
if (message != null && message.replyTo == null && message.forwardInfo != null && tdlib.isChannelAutoForward(message)) {
- tdlib.send(new TdApi.GetRepliedMessage(message.forwardInfo.fromChatId, message.forwardInfo.fromMessageId), (repliedMessage, repliedMessageError) -> {
+ tdlib.send(new TdApi.GetRepliedMessage(message.forwardInfo.source.chatId, message.forwardInfo.source.messageId), (repliedMessage, repliedMessageError) -> {
if (repliedMessage != null) {
message.replyTo = new TdApi.MessageReplyToMessage(repliedMessage.chatId, repliedMessage.id, null, null, repliedMessage.date, repliedMessage.content);
}
@@ -6692,9 +6692,9 @@ public EmojiStickers (Tdlib tdlib, TdApi.StickerType stickerType, String query,
setStickers(object, StickersType.INSTALLED)
);
if (isComplexQuery) {
- tdlib.client().send(new TdApi.SearchEmojis(query, false, U.getInputLanguages()), result -> {
- String[] emojis = result.getConstructor() == TdApi.Emojis.CONSTRUCTOR ? ((TdApi.Emojis) result).emojis : null;
- if (emojis != null && emojis.length > 0) {
+ tdlib.send(new TdApi.SearchEmojis(query, U.getInputLanguages()), (keywords, error) -> {
+ if (keywords != null && keywords.emojiKeywords.length > 0) {
+ String[] emojis = Td.findUniqueEmojis(keywords.emojiKeywords);
String emojisQuery = TextUtils.join(" ", emojis);
// Request 2x more than limit for the case all of the stickers returned by GetStickers
tdlib.client().send(new TdApi.GetStickers(stickerType, emojisQuery, limit * 2, chatId), object ->
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
index b752ef2ee6..1b885f2989 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditRightsController.java
@@ -1471,7 +1471,7 @@ private void setValueForRightId (@RightId int id, boolean newValue) {
if (getArgumentsStrict().mode == MODE_ADMIN_PROMOTION) {
targetAdmin.rights.canManageTopics = newValue;
} else {
- targetRestrict.permissions.canManageTopics = newValue;
+ targetRestrict.permissions.canCreateTopics = newValue;
}
break;
case RightId.POST_STORIES:
@@ -1615,7 +1615,7 @@ private boolean getValueForId (@RightId int id) {
if (getArgumentsStrict().mode == MODE_ADMIN_PROMOTION) {
return targetAdmin.rights.canManageTopics;
} else {
- return canViewMessages && targetRestrict.permissions.canManageTopics;
+ return canViewMessages && targetRestrict.permissions.canCreateTopics;
}
case RightId.POST_STORIES:
return targetAdmin.rights.canPostStories;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java b/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java
index e6b8e2acbe..348eae741c 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EmojiStatusListController.java
@@ -16,6 +16,7 @@
import android.content.Context;
import android.os.Build;
+import android.text.TextUtils;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -30,6 +31,7 @@
import org.drinkless.tdlib.Client;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.R;
+import org.thunderdog.challegram.U;
import org.thunderdog.challegram.component.attach.CustomItemAnimator;
import org.thunderdog.challegram.component.emoji.GifView;
import org.thunderdog.challegram.component.emoji.MediaStickersAdapter;
@@ -59,6 +61,8 @@
import org.thunderdog.challegram.widget.ForceTouchView;
import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.Set;
import me.vkryl.android.AnimatorUtils;
import me.vkryl.android.animator.FactorAnimator;
@@ -68,6 +72,7 @@
import me.vkryl.core.StringUtils;
import me.vkryl.core.collection.IntList;
import me.vkryl.core.collection.LongList;
+import me.vkryl.td.Td;
public class EmojiStatusListController extends ViewController implements
StickerSmallView.StickerMovementCallback, StickerPreviewView.MenuStickerPreviewCallback,
@@ -938,34 +943,23 @@ private void loadStickers () {
getCustomEmojiStickers(recentEmojiList.get(), serviceStickersHandler(null))))))
);
} else {
- if (StringUtils.isEmpty(currentEmojiSearchRequest)) {
- tdlib.client().send(new TdApi.SearchEmojis(currentTextSearchRequest, false, new String[] {"en"}), obj -> {
- switch (obj.getConstructor()) {
- case TdApi.Emojis.CONSTRUCTOR: {
- TdApi.Emojis emojis = (TdApi.Emojis) obj;
- if (emojis.emojis.length > 0) {
- StringBuilder b = new StringBuilder();
- for (String emoji : emojis.emojis) {
- if (b.length() > 0) {
- b.append(" ");
- }
- b.append(emoji);
- }
-
- tdlib.client().send(new TdApi.SearchStickers(new TdApi.StickerTypeCustomEmoji(), b.toString(), 200), serviceStickersHandler(currentTextSearchRequest));
- } else {
- tdlib.client().send(new TdApi.SearchInstalledStickerSets(new TdApi.StickerTypeCustomEmoji(), currentTextSearchRequest, 200), stickerSetsHandler(false));
- }
- break;
- }
- case TdApi.Error.CONSTRUCTOR: {
- UI.showError(obj);
- break;
- }
+ if (!StringUtils.isEmpty(currentEmojiSearchRequest)) {
+ tdlib.client().send(new TdApi.SearchStickers(new TdApi.StickerTypeCustomEmoji(), currentEmojiSearchRequest, 200), serviceStickersHandler(currentTextSearchRequest));
+ } else {
+ final String textQuery = currentTextSearchRequest;
+ tdlib.send(new TdApi.SearchEmojis(textQuery, U.getInputLanguages()), (keywords, error) -> {
+ if (error != null) {
+ UI.showError(error);
+ return;
+ }
+ String[] uniqueEmojis = Td.findUniqueEmojis(keywords.emojiKeywords);
+ if (uniqueEmojis.length > 0) {
+ String uniqueEmojisQuery = TextUtils.join(" ", uniqueEmojis);
+ tdlib.client().send(new TdApi.SearchStickers(new TdApi.StickerTypeCustomEmoji(), uniqueEmojisQuery, 200), serviceStickersHandler(currentTextSearchRequest));
+ } else {
+ tdlib.client().send(new TdApi.SearchInstalledStickerSets(new TdApi.StickerTypeCustomEmoji(), textQuery, 200), stickerSetsHandler(false));
}
});
- } else {
- tdlib.client().send(new TdApi.SearchStickers(new TdApi.StickerTypeCustomEmoji(), currentEmojiSearchRequest, 200), serviceStickersHandler(currentTextSearchRequest));
}
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/LanguageController.java b/app/src/main/java/org/thunderdog/challegram/ui/LanguageController.java
index 5e3d0b768d..29ac570cc7 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/LanguageController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/LanguageController.java
@@ -97,7 +97,7 @@ public View getCustomHeaderCell () {
public void destroy () {
super.destroy();
if (hadChanges) {
- tdlib.client().send(new TdApi.SetAlarm(), ignored -> tdlib.sendFakeUpdate(new TdApi.UpdateLanguagePackStrings(BuildConfig.LANGUAGE_PACK, langPack.languageInfo.id, null), false));
+ tdlib.client().send(new TdApi.SetAlarm(), ignored -> tdlib.sendFakeUpdate(new TdApi.UpdateLanguagePackStrings(BuildConfig.LANGUAGE_PACK, langPack.languageInfo.id, null)));
hadChanges = false;
}
}
@@ -126,7 +126,7 @@ public void onLanguageStringChanged (Lang.Pack langPack, Lang.PackString string)
} else if (Lang.packId().equals(langPack.languageInfo.id)) {
tdlib.sendFakeUpdate(new TdApi.UpdateLanguagePackStrings(BuildConfig.LANGUAGE_PACK, langPack.languageInfo.id, new TdApi.LanguagePackString[] {
string.translated ? string.string : new TdApi.LanguagePackString(string.getKey(), Lang.STRING_DELETED())
- }), false);
+ }));
}
// TODO move from old language_code to the new one, when it changes
// TODO update language list when language_name or language_nameInEnglish updates
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsPagerController.java b/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsPagerController.java
index 0e7c2a2a06..cefde2dbd8 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsPagerController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsPagerController.java
@@ -137,7 +137,7 @@ public MessageOptionsPagerController (Context context, Tdlib tdlib, Options opti
if (state.needShowMessageReactionSenders) {
REACTED_START_POSITION = i;
- for (TdApi.MessageReaction reaction : state.messageReactions) {
+ for (TdApi.MessageReaction reaction : state.messageReactions.reactions) {
TGReaction tgReaction = tdlib.getReaction(reaction.type);
counters[i] = new ViewPagerTopView.Item(tgReaction, new Counter.Builder()
.noBackground().allBold(true).textSize(13f).colorSet(this).callback(this)
@@ -483,7 +483,7 @@ protected ViewController> onCreatePagerItemForPosition (Context context, int p
}
if (position >= REACTED_START_POSITION && REACTED_START_POSITION != -1) {
- MessageOptionsReactedController c = new MessageOptionsReactedController(context, this.tdlib, getPopupLayout(), state.message, state.messageReactions[position - REACTED_START_POSITION].type);
+ MessageOptionsReactedController c = new MessageOptionsReactedController(context, this.tdlib, getPopupLayout(), state.message, state.messageReactions.reactions[position - REACTED_START_POSITION].type);
c.getValue();
if (isFirstCreation && !state.needShowMessageOptions) {
setHeaderPosition(getContentOffset() + HeaderView.getTopOffset());
@@ -999,7 +999,7 @@ public static class State {
public final Tdlib tdlib;
public final Options options;
public final TGMessage message;
- public final TdApi.MessageReaction[] messageReactions;
+ public final TdApi.MessageReactions messageReactions;
public final TdApi.AvailableReaction[] availableReactions;
public final Set chosenReactions;
public final long[] emojiPackIds;
@@ -1036,7 +1036,7 @@ public State (TGMessage message, Options options, OnReactionClickListener onReac
this.needShowMessageOptions = options != null;
this.needShowReactionsPopupPicker = needShowMessageOptions && message.needShowReactionPopupPicker();
- this.needShowMessageReactionSenders = messageReactions != null && message.canGetAddedReactions() && message.getMessageReactions().getTotalCount() > 0;
+ this.needShowMessageReactionSenders = !Td.isEmpty(messageReactions) && message.canGetAddedReactions() && message.getMessageReactions().getTotalCount() > 0;
this.headerButtonsVisibleWidth = needShowReactionsPopupPicker ? Screen.dp(56): 0;
this.needShowCustomEmojiInsidePicker = isPremium && message.isCustomEmojiReactionsAvailable();
@@ -1056,7 +1056,7 @@ public State (TGMessage message, Options options, OnReactionClickListener onReac
public int getPagesCount () {
return (needShowMessageOptions ? 1 : 0)
+ (needShowMessageViews ? 1 : 0)
- + (needShowMessageReactionSenders ? messageReactions.length + 1 : 0);
+ + (needShowMessageReactionSenders ? Td.reactionTypesCount(messageReactions) + 1 : 0);
}
public int getRightViewsWidth () {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java b/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java
index b3a9df5fdd..1a025678f1 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java
@@ -6499,7 +6499,7 @@ public boolean hasEditedChanges () {
return !Td.equalsTo(oldText, newText);
}
default: {
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
break;
}
}
@@ -6862,7 +6862,7 @@ private void saveMessage (boolean applyMarkdown) {
break;
}
default: {
- Td.assertMessageContent_d40af239();
+ Td.assertMessageContent_cfe6660a();
throw Td.unsupported(editContext.message.content);
}
}
@@ -9336,10 +9336,10 @@ public boolean sendRecord (View view, final TGRecord record, boolean allowReply,
if (record.getWaveform() == null) {
Background.instance().post(() -> {
byte[] waveform = N.getWaveform(record.getPath());
- tdlib.sendMessage(chatId, getMessageThreadId(), replyTo, finalSendOptions, new TdApi.InputMessageVoiceNote(record.toInputFile(), record.getDuration(), waveform, null), null);
+ tdlib.sendMessage(chatId, getMessageThreadId(), replyTo, finalSendOptions, new TdApi.InputMessageVoiceNote(record.toInputFile(), record.getDuration(), waveform, null, null), null);
});
} else {
- tdlib.sendMessage(chatId, getMessageThreadId(), replyTo, finalSendOptions, new TdApi.InputMessageVoiceNote(record.toInputFile(), record.getDuration(), record.getWaveform(), null), null);
+ tdlib.sendMessage(chatId, getMessageThreadId(), replyTo, finalSendOptions, new TdApi.InputMessageVoiceNote(record.toInputFile(), record.getDuration(), record.getWaveform(), null, null), null);
}
return true;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java b/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
index 522455732b..707c0026d5 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ProfileController.java
@@ -5621,7 +5621,7 @@ public boolean needAsynchronousAnimation () {
}
private void getMessageCount (TdApi.SearchMessagesFilter filter, boolean returnLocal) {
- tdlib.send(new TdApi.GetChatMessageCount(getChatId(), filter, returnLocal), (messageCount, error) -> {
+ tdlib.send(new TdApi.GetChatMessageCount(getChatId(), filter, /*TODO*/ null, returnLocal), (messageCount, error) -> {
int count;
if (error != null) {
Log.e("TDLib error getMessageCount chatId:%d, filter:%s, returnLocal:%b: %s", getChatId(), filter, returnLocal, TD.toErrorString(error));
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SharedBaseController.java b/app/src/main/java/org/thunderdog/challegram/ui/SharedBaseController.java
index 6a11d918e7..0cb06337f1 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SharedBaseController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SharedBaseController.java
@@ -510,7 +510,7 @@ private void setCanLoadMore (boolean canLoadMore, boolean notify) {
protected TdApi.Function> buildRequest (final long chatId, final long messageThreadId, final String query, final long offset, final String secretOffset, final int limit) {
if (StringUtils.isEmpty(query) || !ChatId.isSecret(chatId)) {
- return new TdApi.SearchChatMessages(chatId, query, null, offset, 0, limit, provideSearchFilter(), messageThreadId);
+ return new TdApi.SearchChatMessages(chatId, query, null, offset, 0, limit, provideSearchFilter(), messageThreadId, /*TODO*/ null);
} else {
return new TdApi.SearchSecretMessages(chatId, query, secretOffset, limit, provideSearchFilter());
}
diff --git a/app/src/main/java/org/thunderdog/challegram/unsorted/Test.java b/app/src/main/java/org/thunderdog/challegram/unsorted/Test.java
index 35df8c5424..8f35237cb1 100644
--- a/app/src/main/java/org/thunderdog/challegram/unsorted/Test.java
+++ b/app/src/main/java/org/thunderdog/challegram/unsorted/Test.java
@@ -110,9 +110,14 @@ public static void onClick (final BaseActivity context) {
c.showOptions(info, ids.get(), strings.get(), null, null, new OptionDelegate() {
@Override
public boolean onOptionItemPressed (View optionItemView, int id) {
- TdApi.ChatAction action = actions[id];
- testAction = action.getConstructor() == TdApi.ChatActionCancel.CONSTRUCTOR ? null : action;
- context.currentTdlib().__sendFakeAction(action);
+ TdApi.ChatAction action = actions[id] == null || actions[id].getConstructor() == TdApi.ChatActionCancel.CONSTRUCTOR ? null : actions[id];
+ testAction = action;
+ final Tdlib tdlib = context.currentTdlib();
+ tdlib.getAllChats(new TdApi.ChatListMain(), chat -> {
+ if (chat.lastMessage != null) {
+ tdlib.sendFakeUpdate(new TdApi.UpdateChatAction(chat.id, 0, chat.lastMessage.senderId, action));
+ }
+ }, null, false);
return true;
}
});
@@ -134,7 +139,7 @@ public static boolean onChatClick (Tdlib tdlib, TdApi.Chat chat) {
userId = Td.getSenderUserId(chat.lastMessage);
}
if (chat.lastMessage != null) {
- tdlib.sendFakeUpdate(new TdApi.UpdateChatAction(chat.id, 0, chat.lastMessage.senderId, tdlib.status().hasStatus(chat.id, 0) ? new TdApi.ChatActionCancel() : testAction != null ? testAction : new TdApi.ChatActionTyping()), false);
+ tdlib.sendFakeUpdate(new TdApi.UpdateChatAction(chat.id, 0, chat.lastMessage.senderId, tdlib.status().hasStatus(chat.id, 0) ? new TdApi.ChatActionCancel() : testAction != null ? testAction : new TdApi.ChatActionTyping()));
}
return true;
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3a750fcbc9..dc1703ecff 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1646,6 +1646,8 @@
Any details such as age, occupation or city.\nExample: 23 y.o. designer from San Francisco.
Expired photo
Expired video
+ Expired voice message
+ Expired video message
Send with Enter
Hide keyboard on chat scroll
Hashtag has been copied to the clipboard
@@ -3396,6 +3398,8 @@
Self-Destruct Photo
Self-Destruct Video
+ Self-Destruct Voice Message
+ Self-Destruct Video Message
Invoice for %1$s
Invoice
diff --git a/tdlib b/tdlib
index 28e2d7e6cd..85090039ad 160000
--- a/tdlib
+++ b/tdlib
@@ -1 +1 @@
-Subproject commit 28e2d7e6cd59c5fffb71ed0451afef55d75a4d0d
+Subproject commit 85090039adab7bfffefe1aac106fcbe5fe36de94
diff --git a/vkryl/td b/vkryl/td
index 951d9330d1..ef06f2c410 160000
--- a/vkryl/td
+++ b/vkryl/td
@@ -1 +1 @@
-Subproject commit 951d9330d1e40ab9c4724f624f28db0618fc1db7
+Subproject commit ef06f2c410aaecd06d8663f82405066662ab5a25
From 10be7ee5db70cfa5c06d520472cb9fd00dab44e4 Mon Sep 17 00:00:00 2001
From: Arseny271 <56611696+Arseny271@users.noreply.github.com>
Date: Tue, 30 Jan 2024 01:02:19 +0400
Subject: [PATCH 35/61] Giveaway improvements and fixes (#547)
* New particle generation algorithm
* Gift Code Link Popup Fix
* Add country flags
---
.../component/popups/GiftCodeController.java | 16 ++-
.../popups/ModernActionedLayout.java | 7 +
.../challegram/data/TGMessageGiveaway.java | 6 +
.../data/TGMessageGiveawayBase.java | 4 +-
.../thunderdog/challegram/emoji/Emoji.java | 9 ++
.../util/GiftParticlesDrawable.java | 134 ++++++++++++++----
.../challegram/widget/GiftHeaderView.java | 2 +-
7 files changed, 147 insertions(+), 31 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/component/popups/GiftCodeController.java b/app/src/main/java/org/thunderdog/challegram/component/popups/GiftCodeController.java
index 902a3670c4..9f4f850f49 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/popups/GiftCodeController.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/popups/GiftCodeController.java
@@ -16,6 +16,7 @@
import android.content.Context;
import android.text.style.ClickableSpan;
+import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -38,6 +39,7 @@
import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Screen;
+import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.ui.ListItem;
import org.thunderdog.challegram.ui.SettingHolder;
import org.thunderdog.challegram.ui.SettingsAdapter;
@@ -220,12 +222,22 @@ protected int getInitialContentHeight () {
@Override
public boolean ignoreStartHeightLimits () {
- return true;
+ if (UI.isLandscape()) {
+ return super.ignoreStartHeightLimits();
+ }
+
+ final int h = Screen.currentActualHeight() - HeaderView.getSize(false);
+ return measuredRecyclerHeight < h;
}
@Override
protected boolean canExpandHeight () {
- return false;
+ if (UI.isLandscape()) {
+ return super.canExpandHeight();
+ }
+
+ final int h = Screen.currentActualHeight() - HeaderView.getSize(false);
+ return measuredRecyclerHeight >= h;
}
@Override
diff --git a/app/src/main/java/org/thunderdog/challegram/component/popups/ModernActionedLayout.java b/app/src/main/java/org/thunderdog/challegram/component/popups/ModernActionedLayout.java
index 121d253a99..a3bdbd4baa 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/popups/ModernActionedLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/popups/ModernActionedLayout.java
@@ -20,11 +20,18 @@
import org.thunderdog.challegram.component.attach.MediaLayout;
import org.thunderdog.challegram.data.TGMessage;
import org.thunderdog.challegram.navigation.ViewController;
+import org.thunderdog.challegram.tool.Keyboard;
+import org.thunderdog.challegram.tool.UI;
public class ModernActionedLayout extends MediaLayout {
private MediaBottomBaseController> curController;
public static void showGiftCode (ViewController> context, String code, @Nullable TdApi.MessagePremiumGiftCode giftCodeContent, @NonNull TdApi.PremiumGiftCodeInfo giftCodeInfo) {
+ if (context.getKeyboardState()) {
+ context.hideSoftwareKeyboard();
+ UI.post(() -> showGiftCode(context, code, giftCodeContent, giftCodeInfo), 100);
+ return;
+ }
showMal(context, (mal) -> new GiftCodeController(mal, code, giftCodeContent, giftCodeInfo));
}
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveaway.java b/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveaway.java
index 9b60c2036f..4ea60e1bd5 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveaway.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveaway.java
@@ -27,6 +27,7 @@
import org.thunderdog.challegram.component.chat.MessageView;
import org.thunderdog.challegram.component.chat.MessagesManager;
import org.thunderdog.challegram.core.Lang;
+import org.thunderdog.challegram.emoji.Emoji;
import org.thunderdog.challegram.telegram.TdlibUi;
import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.theme.Theme;
@@ -100,6 +101,11 @@ public TGMessageGiveaway (MessagesManager manager, TdApi.Message msg, @NonNull T
sb.append(Lang.getConcatSeparator());
}
String[] info = TGCountry.instance().find(countryCode);
+ String flag = Emoji.getEmojiFlagFromCountry(countryCode);
+ if (!StringUtils.isEmpty(flag)) {
+ sb.append(flag);
+ sb.append(' ');
+ }
sb.append(info != null ? info[2] : countryCode);
}
content.padding(Screen.dp(6));
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayBase.java b/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayBase.java
index 85d96feaab..0b51f8a7a0 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayBase.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGMessageGiveawayBase.java
@@ -100,7 +100,7 @@ protected final void buildContent (int maxWidth) {
}
@Override
- public boolean isValidPosition (int x, int y) {
+ public boolean isValidPosition (float x, float y) {
return y < content.getHeight() && content.isValidPosition(x - (useFullWidthParticles ? getContentX() : 0) - Screen.dp(CONTENT_PADDING_DP), y);
}
@@ -246,7 +246,7 @@ public boolean performLongPress (View view, float x, float y) {
private final RectF tmpRectF = new RectF();
@Override
- public boolean isValidPosition (int x, int y) {
+ public boolean isValidPosition (float x, float y) {
for (ContentPart p : parts) {
int w = p.getWidth();
int h = p.getHeight();
diff --git a/app/src/main/java/org/thunderdog/challegram/emoji/Emoji.java b/app/src/main/java/org/thunderdog/challegram/emoji/Emoji.java
index e7df00c1c1..f431be50c3 100644
--- a/app/src/main/java/org/thunderdog/challegram/emoji/Emoji.java
+++ b/app/src/main/java/org/thunderdog/challegram/emoji/Emoji.java
@@ -28,6 +28,7 @@
import com.coremedia.iso.Hex;
+import org.drinkless.tdlib.Client;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.Log;
import org.thunderdog.challegram.U;
@@ -1136,4 +1137,12 @@ public String cleanEmojiCode (String code) {
return code.substring(0, code.length() - 1);
return code;
}
+
+ public static String getEmojiFlagFromCountry (String countryCode) {
+ try {
+ return Client.execute(new TdApi.GetCountryFlagEmoji(countryCode)).text;
+ } catch (Client.ExecutionException e) {
+ return null;
+ }
+ }
}
diff --git a/app/src/main/java/org/thunderdog/challegram/util/GiftParticlesDrawable.java b/app/src/main/java/org/thunderdog/challegram/util/GiftParticlesDrawable.java
index 08eeb4e10d..8d4b0f114f 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/GiftParticlesDrawable.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/GiftParticlesDrawable.java
@@ -17,6 +17,7 @@
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.PixelFormat;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -31,14 +32,16 @@
import org.thunderdog.challegram.tool.Screen;
import java.util.ArrayList;
+import java.util.List;
import kotlin.random.Random;
+import me.vkryl.core.ArrayUtils;
import me.vkryl.core.ColorUtils;
import me.vkryl.core.MathUtils;
public class GiftParticlesDrawable extends Drawable {
public interface ParticleValidator {
- boolean isValidPosition (int x, int y);
+ boolean isValidPosition (float x, float y);
}
private @Nullable ParticleValidator particleValidator;
@@ -73,37 +76,25 @@ protected void onBoundsChange (@NonNull Rect bounds) {
setSize(bounds.width(), bounds.height());
}
- private static final int GRID_SIZE = 35;
+ private static final int GRID_SIZE = 25;
private void setSize (int width, int height) {
if (this.width == width && this.height == height) {
return;
}
-
- final int gridSection = Screen.dp(GRID_SIZE);
- int gridSectionsX = (int) Math.floor((float) width / gridSection);
- int gridSectionsY = (int) Math.floor((float) height / gridSection);
- int gridStartX = (width - gridSection * gridSectionsX) / 2;
- int gridStartY = (height - gridSection * gridSectionsY) / 2;
-
this.width = width;
this.height = height;
- ArrayList particles = new ArrayList<>(gridSectionsX * gridSectionsY);
- for (int x = 0; x < gridSectionsX; x++) {
- for (int y = 0; y < gridSectionsY; y++) {
- int sx = gridStartX + gridSection * x;
- int sy = gridStartY + gridSection * y;
- int ex = sx + gridSection;
- int ey = sy + gridSection;
- int px = MathUtils.random(sx, ex);
- int py = MathUtils.random(sy, ey);
-
- if (particleValidator == null || particleValidator.isValidPosition(px, py)) {
- particles.add(new Particle(MathUtils.random(0, 3), particleColors[MathUtils.random(0, 5)], px, py, 0.75f + Random.Default.nextFloat() * 0.5f, Random.Default.nextFloat() * 360f));
- }
- }
+ List points = poissonDiskSampling(Screen.dp(GRID_SIZE), width, height, 10);
+ if (particleValidator != null) {
+ points = ArrayUtils.filter(points, p -> particleValidator.isValidPosition(p.x, p.y));
+ }
+
+ ArrayList particles = new ArrayList<>(points.size());
+ for (PointF pointF: points) {
+ particles.add(new Particle(MathUtils.random(0, 3), particleColors[MathUtils.random(0, 5)], pointF.x, pointF.y, 0.75f + Random.Default.nextFloat() * 0.5f, Random.Default.nextFloat() * 360f));
}
+
this.particles = particles.toArray(new Particle[0]);
}
@@ -151,10 +142,10 @@ private static class Particle {
public final int color;
public final float scale;
public final float angle;
- public final int x;
- public final int y;
+ public final float x;
+ public final float y;
- public Particle (int type, int color, int x, int y, float scale, float angle) {
+ public Particle (int type, int color, float x, float y, float scale, float angle) {
this.type = type;
this.color = color;
this.x = x;
@@ -163,4 +154,95 @@ public Particle (int type, int color, int x, int y, float scale, float angle) {
this.angle = angle;
}
}
+
+ /* * */
+
+ static boolean isValidPoint(PointF[][] grid, int width, int height, float cellsize,
+ int gwidth, int gheight,
+ PointF p, float radius) {
+ /* Make sure the point is on the screen */
+ final int gp = Screen.dp(15) / 2;
+ if ((p.x < gp) || (p.x >= (width - gp)) || (p.y < gp) || (p.y >= (height - gp)))
+ return false;
+
+ /* Check neighboring eight cells */
+ int xindex = (int)Math.floor(p.x / cellsize);
+ int yindex = (int)Math.floor(p.y / cellsize);
+ int i0 = Math.max(xindex - 1, 0);
+ int i1 = Math.min(xindex + 1, gwidth - 1);
+ int j0 = Math.max(yindex - 1, 0);
+ int j1 = Math.min(yindex + 1, gheight - 1);
+
+ for (int i = i0; i <= i1; i++)
+ for (int j = j0; j <= j1; j++)
+ if (grid[i][j] != null)
+ if (MathUtils.distance(grid[i][j].x, grid[i][j].y, p.x, p.y) < radius)
+ return false;
+
+ /* If we get here, return true */
+ return true;
+ }
+
+ static void insertPoint(PointF[][] grid, float cellsize, PointF point) {
+ int xindex = (int)Math.floor(point.x / cellsize);
+ int yindex = (int)Math.floor(point.y / cellsize);
+ grid[xindex][yindex] = point;
+ }
+
+ static ArrayList poissonDiskSampling(float radius, int width, int height, int k) {
+ int N = 2;
+ /* The final set of points to return */
+ ArrayList points = new ArrayList();
+ /* The currently "active" set of points */
+ ArrayList active = new ArrayList();
+ /* Initial point p0 */
+ PointF p0 = new PointF(MathUtils.random(0, width), MathUtils.random(0, height));
+ PointF[][] grid;
+ float cellsize = (float) Math.floor(radius/Math.sqrt(N));
+
+ /* Figure out no. of cells in the grid for our canvas */
+ int ncells_width = (int)Math.ceil(width/cellsize) + 1;
+ int ncells_height = (int)Math.ceil(height/cellsize) + 1;
+
+ /* Allocate the grid an initialize all elements to null */
+ grid = new PointF[ncells_width][ncells_height];
+ for (int i = 0; i < ncells_width; i++)
+ for (int j = 0; j < ncells_height; j++)
+ grid[i][j] = null;
+
+ insertPoint(grid, cellsize, p0);
+ points.add(p0);
+ active.add(p0);
+
+ while (active.size() > 0) {
+ int random_index = MathUtils.random(0, active.size() - 1);
+ PointF p = active.get(random_index);
+
+ boolean found = false;
+ for (int tries = 0; tries < k; tries++) {
+ float theta = MathUtils.random(0, 360);
+ float new_radius = MathUtils.random((int)radius, (int)(2*radius));
+ float pnewx = (float) (p.x + new_radius * Math.cos(Math.toRadians(theta)));
+ float pnewy = (float) (p.y + new_radius * Math.sin(Math.toRadians(theta)));
+ PointF pnew = new PointF(pnewx, pnewy);
+
+ if (!isValidPoint(grid, width, height, cellsize,
+ ncells_width, ncells_height,
+ pnew, radius))
+ continue;
+
+ points.add(pnew);
+ insertPoint(grid, cellsize, pnew);
+ active.add(pnew);
+ found = true;
+ break;
+ }
+
+ /* If no point was found after k tries, remove p */
+ if (!found)
+ active.remove(random_index);
+ }
+
+ return points;
+ }
}
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/GiftHeaderView.java b/app/src/main/java/org/thunderdog/challegram/widget/GiftHeaderView.java
index 630a0c3eb9..f0f15081d3 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/GiftHeaderView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/GiftHeaderView.java
@@ -79,7 +79,7 @@ private void buildContent () {
}
@Override
- public boolean isValidPosition (int x, int y) {
+ public boolean isValidPosition (float x, float y) {
return content == null || content.isValidPosition(x - Screen.dp(60), y - contentY);
}
From bade013f3d253830b73f952d4c30b133cea12859 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 30 Jan 2024 14:35:25 +0400
Subject: [PATCH 36/61] Upgraded TDLib to tdlib/td@437c2d0
---
tdlib | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tdlib b/tdlib
index 85090039ad..800b09a4bd 160000
--- a/tdlib
+++ b/tdlib
@@ -1 +1 @@
-Subproject commit 85090039adab7bfffefe1aac106fcbe5fe36de94
+Subproject commit 800b09a4bd2ed28ac03bb8de2f09d4131d71e63b
From b0b57884537d13f46b9812c5b21ce7c31250b1a4 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 30 Jan 2024 16:50:04 +0400
Subject: [PATCH 37/61] Improve emulator detection + Fix duplicate emulator
prompt + Improve `Copy report details` and false-positive emulator report
---
app/build.gradle.kts | 1 -
.../java/org/thunderdog/challegram/U.java | 10 +-
.../thunderdog/challegram/telegram/Tdlib.java | 15 +-
.../challegram/telegram/TdlibCache.java | 11 +-
.../challegram/ui/PhoneController.java | 11 +-
.../challegram/ui/SettingsBugController.java | 4 +-
.../challegram/ui/SettingsController.java | 8 +-
.../ui/SettingsThemeController.java | 6 +-
.../challegram/unsorted/Settings.java | 2 +-
.../challegram/util/AppInstallationUtil.java | 225 ------------------
.../challegram/util/AppUpdater.java | 25 +-
buildSrc/src/main/kotlin/Config.kt | 2 +-
vkryl/android | 2 +-
13 files changed, 63 insertions(+), 259 deletions(-)
delete mode 100644 app/src/main/java/org/thunderdog/challegram/util/AppInstallationUtil.java
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a97c124046..f76ef4d74f 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -115,7 +115,6 @@ android {
ndkPath = File(sdkDirectory, "ndk/$ndkVersion").absolutePath
buildConfigString("NDK_VERSION", ndkVersion)
buildConfigBool("WEBP_ENABLED", true) // variant.minSdkVersion < 19
- buildConfigBool("SIDE_LOAD_ONLY", variant.sideLoadOnly)
ndk.abiFilters.clear()
ndk.abiFilters.addAll(variant.filters)
externalNativeBuild.ndkBuild.abiFilters(*variant.filters)
diff --git a/app/src/main/java/org/thunderdog/challegram/U.java b/app/src/main/java/org/thunderdog/challegram/U.java
index f7ec2319ee..698debc930 100644
--- a/app/src/main/java/org/thunderdog/challegram/U.java
+++ b/app/src/main/java/org/thunderdog/challegram/U.java
@@ -126,7 +126,6 @@
import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.ui.TextController;
import org.thunderdog.challegram.util.AppBuildInfo;
-import org.thunderdog.challegram.util.AppInstallationUtil;
import org.thunderdog.challegram.util.Permissions;
import org.thunderdog.challegram.widget.NoScrollTextView;
@@ -166,6 +165,7 @@
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
+import me.vkryl.android.AppInstallationUtil;
import me.vkryl.android.LocaleUtils;
import me.vkryl.android.SdkVersion;
import me.vkryl.core.ArrayUtils;
@@ -2397,9 +2397,13 @@ public static String getUsefulMetadata (@Nullable Tdlib tdlib) {
"Build: `" + Build.FINGERPRINT + "`\n" +
"Package: " + UI.getAppContext().getPackageName() + "\n" +
"Locale: " + locale + (!locale.equals(appLocale) ? " (app: " + appLocale + ")" : "");
- String installerName = AppInstallationUtil.getInstallerPrettyName();
+ String installerName = AppInstallationUtil.getInstallerPackageName(UI.getAppContext());
if (!StringUtils.isEmpty(installerName)) {
- metadata += "\nInstaller: " + installerName;
+ metadata += "\nInstaller: " + AppInstallationUtil.prettifyPackageName(installerName);
+ }
+ String initiatorName = AppInstallationUtil.getInitiatorPackageName(UI.getAppContext());
+ if (!StringUtils.isEmpty(initiatorName)) {
+ metadata += "\nInitiator: " + AppInstallationUtil.prettifyPackageName(initiatorName);
}
String fingerprint = U.getApkFingerprint("SHA1");
if (!StringUtils.isEmpty(fingerprint)) {
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
index 0354a2474d..6e25d6d124 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
@@ -66,7 +66,6 @@
import org.thunderdog.challegram.ui.EditRightsController;
import org.thunderdog.challegram.unsorted.Passcode;
import org.thunderdog.challegram.unsorted.Settings;
-import org.thunderdog.challegram.util.AppInstallationUtil;
import org.thunderdog.challegram.util.DrawableProvider;
import org.thunderdog.challegram.util.UserProvider;
import org.thunderdog.challegram.util.WrapperProvider;
@@ -100,6 +99,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import me.vkryl.android.AppInstallationUtil;
import me.vkryl.core.ArrayUtils;
import me.vkryl.core.BitwiseUtils;
import me.vkryl.core.FileUtils;
@@ -5824,13 +5824,14 @@ private Map newConnectionParams () {
throw new IllegalStateException(Integer.toString(state));
}
}
- long timeZoneOffset = timeZoneOffset();
- params.put("package_id", UI.getAppContext().getPackageName());
- String installerName = AppInstallationUtil.getInstallerPackageName();
+ final long timeZoneOffset = timeZoneOffset();
+ final Context context = UI.getAppContext();
+ params.put("package_id", context.getPackageName());
+ String installerName = AppInstallationUtil.getInstallerPackageName(context);
if (!StringUtils.isEmpty(installerName)) {
params.put("installer", installerName);
}
- String initiatorName = AppInstallationUtil.getInitiatorPackageName();
+ String initiatorName = AppInstallationUtil.getInitiatorPackageName(context);
if (!StringUtils.isEmpty(initiatorName) && !initiatorName.equals(installerName)) {
params.put("initiator", initiatorName);
}
@@ -5919,7 +5920,7 @@ private void updateParameters (Client client) {
updateNotificationParameters(client);
client.send(new TdApi.SetOption("storage_max_files_size", new TdApi.OptionValueInteger(Integer.MAX_VALUE)), okHandler);
client.send(new TdApi.SetOption("ignore_default_disable_notification", new TdApi.OptionValueBoolean(true)), okHandler);
- client.send(new TdApi.SetOption("ignore_platform_restrictions", new TdApi.OptionValueBoolean(AppInstallationUtil.isAppSideLoaded())), okHandler);
+ client.send(new TdApi.SetOption("ignore_platform_restrictions", new TdApi.OptionValueBoolean(AppInstallationUtil.isAppSideLoaded(UI.getAppContext()))), okHandler);
client.send(new TdApi.SetOption("process_pinned_messages_as_mentions", new TdApi.OptionValueBoolean(true)), okHandler);
}
checkConnectionParams(client, true);
@@ -6746,7 +6747,7 @@ public double emojiesAnimatedZoom () {
}
public boolean youtubePipEnabled () {
- return !youtubePipDisabled || AppInstallationUtil.isAppSideLoaded();
+ return !youtubePipDisabled || AppInstallationUtil.isAppSideLoaded(UI.getAppContext());
}
public RtcServer[] rtcServers () {
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCache.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCache.java
index 6e9714bc09..192993a793 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCache.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibCache.java
@@ -40,7 +40,7 @@
import org.thunderdog.challegram.loader.ImageFile;
import org.thunderdog.challegram.tool.Strings;
import org.thunderdog.challegram.tool.UI;
-import org.thunderdog.challegram.util.AppInstallationUtil;
+import org.thunderdog.challegram.util.AppUpdater;
import org.thunderdog.challegram.util.DrawableProvider;
import org.thunderdog.challegram.util.text.Letters;
import org.thunderdog.challegram.voip.annotation.CallState;
@@ -53,7 +53,9 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import me.vkryl.android.AppInstallationUtil;
import me.vkryl.core.ArrayUtils;
+import me.vkryl.core.StringUtils;
import me.vkryl.core.collection.LongSparseIntArray;
import me.vkryl.core.collection.LongSparseLongArray;
import me.vkryl.core.lambda.CancellableRunnable;
@@ -275,10 +277,11 @@ public void getInviteText (@Nullable final RunnableData callback) {
}
private @NonNull AppInstallationUtil.DownloadUrl toDownloadUrl (@Nullable TdApi.HttpUrl url) {
- if (url != null && tdlib.hasUrgentInAppUpdate()) {
- return new AppInstallationUtil.DownloadUrl(AppInstallationUtil.InstallerId.UNKNOWN, url.url);
+ final String httpUrl = url != null ? url.url : null;
+ if (!StringUtils.isEmpty(httpUrl) && tdlib.hasUrgentInAppUpdate()) {
+ return new AppInstallationUtil.DownloadUrl(httpUrl);
}
- return AppInstallationUtil.getDownloadUrl(url != null ? url.url : null);
+ return AppUpdater.getDownloadUrl(httpUrl);
}
public void getDownloadUrl (@Nullable final RunnableData callback) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
index 1a868f95ce..788e9ee173 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/PhoneController.java
@@ -65,10 +65,8 @@
import org.thunderdog.challegram.tool.TGPhoneFormat;
import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.tool.Views;
-import org.thunderdog.challegram.unsorted.Settings;
import org.thunderdog.challegram.util.CustomTypefaceSpan;
import org.thunderdog.challegram.util.NoUnderlineClickableSpan;
-import org.thunderdog.challegram.util.OptionDelegate;
import org.thunderdog.challegram.util.StringList;
import org.thunderdog.challegram.widget.MaterialEditTextGroup;
import org.thunderdog.challegram.widget.NoScrollTextView;
@@ -1079,12 +1077,17 @@ private void makeTestRequest () {
}
}
+ private AlertDialog emulatorPrompt;
+
private void showEmulatorPrompt () {
if (mode != MODE_LOGIN) {
return;
}
context.forceRunEmulatorChecks(detectionResult -> executeOnUiThreadOptional(() -> {
- if (detectionResult != null && (detectionResult.isEmulatorDetected() || BuildConfig.DEBUG)) {
+ if (detectionResult != null && detectionResult.isEmulatorDetected()) {
+ if (emulatorPrompt != null && emulatorPrompt.isShowing()) {
+ return;
+ }
AlertDialog.Builder b = new AlertDialog.Builder(context, Theme.dialogTheme());
b.setTitle(Lang.getString(R.string.EmulatorWarningTitle));
b.setMessage(Lang.getMarkdownStringSecure(this, R.string.EmulatorWarning));
@@ -1145,7 +1148,7 @@ private void showEmulatorPrompt () {
}
});
b.setCancelable(false);
- showAlert(b);
+ emulatorPrompt = showAlert(b);
}
}));
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsBugController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsBugController.java
index 0597a337ed..ac3a9f862a 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsBugController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsBugController.java
@@ -58,7 +58,7 @@
import org.thunderdog.challegram.ui.camera.CameraController;
import org.thunderdog.challegram.unsorted.Settings;
import org.thunderdog.challegram.unsorted.Test;
-import org.thunderdog.challegram.util.AppInstallationUtil;
+import org.thunderdog.challegram.util.AppUpdater;
import org.thunderdog.challegram.util.Crash;
import org.thunderdog.challegram.util.StringList;
import org.thunderdog.challegram.v.CustomRecyclerView;
@@ -395,7 +395,7 @@ private CharSequence getCrashGuide () {
default:
return null;
}
- return Lang.getMarkdownStringSecure(this, resId, getDiskAvailableInfo(), AppInstallationUtil.getDownloadUrl(null).url);
+ return Lang.getMarkdownStringSecure(this, resId, getDiskAvailableInfo(), AppUpdater.getDownloadUrl(null).url);
}
@Override
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java
index 755c8faae5..d55ea55660 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsController.java
@@ -71,7 +71,7 @@
import org.thunderdog.challegram.unsorted.Settings;
import org.thunderdog.challegram.unsorted.Size;
import org.thunderdog.challegram.util.AppBuildInfo;
-import org.thunderdog.challegram.util.AppInstallationUtil;
+import org.thunderdog.challegram.util.AppUpdater;
import org.thunderdog.challegram.util.OptionDelegate;
import org.thunderdog.challegram.util.PullRequest;
import org.thunderdog.challegram.util.StringList;
@@ -83,6 +83,7 @@
import java.util.List;
import java.util.concurrent.TimeUnit;
+import me.vkryl.android.AppInstallationUtil;
import me.vkryl.android.widget.FrameLayoutFix;
import me.vkryl.core.ArrayUtils;
import me.vkryl.core.ColorUtils;
@@ -621,7 +622,7 @@ public void setValuedSetting (ListItem item, SettingView view, boolean isUpdate)
items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- AppInstallationUtil.DownloadUrl downloadUrl = AppInstallationUtil.getDownloadUrl(null);
+ AppInstallationUtil.DownloadUrl downloadUrl = AppUpdater.getDownloadUrl(null);
@DrawableRes int downloadIconRes;
@StringRes int downloadStringRes = R.string.CheckForUpdates;
if (tdlib.hasUrgentInAppUpdate() && tdlib.isProduction()) {
@@ -629,7 +630,8 @@ public void setValuedSetting (ListItem item, SettingView view, boolean isUpdate)
downloadUrl = new AppInstallationUtil.DownloadUrl(downloadUrl.installerId, tdlib.tMeUrl(BuildConfig.TELEGRAM_UPDATES_CHANNEL));
} else {
switch (downloadUrl.installerId) {
- case AppInstallationUtil.InstallerId.UNKNOWN: {
+ case AppInstallationUtil.InstallerId.UNKNOWN:
+ case AppInstallationUtil.InstallerId.MEMU_EMULATOR: {
if (!StringUtils.isEmpty(BuildConfig.GOOGLE_PLAY_URL)) {
downloadUrl = new AppInstallationUtil.DownloadUrl(AppInstallationUtil.InstallerId.GOOGLE_PLAY, BuildConfig.GOOGLE_PLAY_URL);
downloadIconRes = R.drawable.baseline_google_play_24;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsThemeController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsThemeController.java
index fcabfba3e0..c6bc12cd64 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsThemeController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsThemeController.java
@@ -55,7 +55,6 @@
import org.thunderdog.challegram.tool.Strings;
import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.unsorted.Settings;
-import org.thunderdog.challegram.util.AppInstallationUtil;
import org.thunderdog.challegram.util.AppUpdater;
import org.thunderdog.challegram.util.DrawableModifier;
import org.thunderdog.challegram.util.Permissions;
@@ -72,6 +71,7 @@
import java.util.List;
import java.util.TimeZone;
+import me.vkryl.android.AppInstallationUtil;
import me.vkryl.core.BitwiseUtils;
import me.vkryl.core.DateUtils;
import me.vkryl.core.MathUtils;
@@ -529,7 +529,7 @@ public void onRemove (RecyclerView.ViewHolder viewHolder) {
}*/
items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
- if (AppInstallationUtil.isAppSideLoaded()) {
+ if (AppInstallationUtil.isAppSideLoaded(UI.getAppContext())) {
items.addAll(Arrays.asList(
new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.InAppUpdates),
new ListItem(ListItem.TYPE_SHADOW_TOP),
@@ -553,7 +553,7 @@ public void onRemove (RecyclerView.ViewHolder viewHolder) {
items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.Chats));
items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- boolean sideLoaded = AppInstallationUtil.isAppSideLoaded();
+ boolean sideLoaded = AppInstallationUtil.isAppSideLoaded(UI.getAppContext());
if (tdlib.canIgnoreSensitiveContentRestriction() && (sideLoaded || tdlib.ignoreSensitiveContentRestrictions())) {
items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_restrictSensitiveContent, 0, R.string.DisplaySensitiveContent));
diff --git a/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java b/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
index 95461d6d31..ccf58b37ed 100644
--- a/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
+++ b/app/src/main/java/org/thunderdog/challegram/unsorted/Settings.java
@@ -6203,7 +6203,7 @@ public boolean isEmulatorDetected () {
}
public String toHumanReadableFormat () {
- return Long.toString(result, 16);
+ return "0x" + Long.toString(result, 16);
}
public long[] toLongArray () {
diff --git a/app/src/main/java/org/thunderdog/challegram/util/AppInstallationUtil.java b/app/src/main/java/org/thunderdog/challegram/util/AppInstallationUtil.java
deleted file mode 100644
index ba885c2236..0000000000
--- a/app/src/main/java/org/thunderdog/challegram/util/AppInstallationUtil.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * This file is a part of Telegram X
- * Copyright © 2014 (tgx-android@pm.me)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- * File created on 13/11/2023
- */
-package org.thunderdog.challegram.util;
-
-import android.os.Build;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.thunderdog.challegram.BuildConfig;
-import org.thunderdog.challegram.Log;
-import org.thunderdog.challegram.tool.UI;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import me.vkryl.core.StringUtils;
-
-public class AppInstallationUtil {
- public static final String VENDOR_GOOGLE_PLAY = "com.android.vending";
- public static final String VENDOR_GALAXY_STORE = "com.sec.android.app.samsungapps";
- public static final String VENDOR_HUAWEI_APPGALLERY = "com.huawei.appmarket";
- public static final String VENDOR_AMAZON_APPSTORE = "com.amazon.venezia";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({
- InstallerId.UNKNOWN,
- InstallerId.GOOGLE_PLAY,
- InstallerId.GALAXY_STORE,
- InstallerId.HUAWEI_APPGALLERY,
- InstallerId.AMAZON_APPSTORE
- })
- public @interface InstallerId {
- int
- UNKNOWN = 0,
- GOOGLE_PLAY = 1,
- GALAXY_STORE = 2,
- HUAWEI_APPGALLERY = 3,
- AMAZON_APPSTORE = 4;
- }
-
- private static Integer installerId;
-
- public static synchronized @InstallerId int getInstallerId () {
- if (installerId == null) {
- installerId = getInstallerIdImpl();
- }
- return installerId;
- }
-
- private static @InstallerId int getInstallerIdImpl () {
- final String installerPackageName = getInstallerPackageName();
- if (!StringUtils.isEmpty(installerPackageName)) {
- //noinspection ConstantConditions
- switch (installerPackageName) {
- case VENDOR_GOOGLE_PLAY:
- return InstallerId.GOOGLE_PLAY;
- case VENDOR_GALAXY_STORE:
- return InstallerId.GALAXY_STORE;
- case VENDOR_HUAWEI_APPGALLERY:
- return InstallerId.HUAWEI_APPGALLERY;
- case VENDOR_AMAZON_APPSTORE:
- return InstallerId.AMAZON_APPSTORE;
- }
- }
- return InstallerId.UNKNOWN;
- }
-
- // Checks initiator and installer id for current app installation
-
- @Nullable
- public static String getInitiatorPackageName () {
- final String packageName = UI.getAppContext().getPackageName();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- try {
- android.content.pm.InstallSourceInfo sourceInfo = UI.getAppContext().getPackageManager().getInstallSourceInfo(packageName);
- String initiatingId = sourceInfo.getInitiatingPackageName();
- if (!StringUtils.isEmpty(initiatingId)) {
- return initiatingId;
- }
- } catch (Throwable t) {
- Log.v("Unable to determine initiator package name", t);
- }
- }
- return null;
- }
-
- @Nullable
- public static String getInstallerPackageName () {
- final String packageName = UI.getAppContext().getPackageName();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- try {
- android.content.pm.InstallSourceInfo sourceInfo = UI.getAppContext().getPackageManager().getInstallSourceInfo(packageName);
- String installerId = sourceInfo.getInstallingPackageName();
- if (!StringUtils.isEmpty(installerId)) {
- return installerId;
- }
- String initiatingId = sourceInfo.getInitiatingPackageName();
- if (!StringUtils.isEmpty(initiatingId)) {
- return initiatingId;
- }
- } catch (Throwable t) {
- Log.v("Unable to determine installer package via modern API", t);
- }
- }
- try {
- String installerPackageName = UI.getAppContext().getPackageManager().getInstallerPackageName(packageName);
- if (StringUtils.isEmpty(installerPackageName)) {
- return null;
- }
- return installerPackageName;
- } catch (Throwable t) {
- Log.v("Unable to determine installer package", t);
- return null;
- }
- }
-
- public static @Nullable String getInstallerPrettyName () {
- switch (getInstallerId()) {
- case InstallerId.UNKNOWN:
- return getInstallerPackageName();
- case InstallerId.GOOGLE_PLAY:
- return "Google Play";
- case InstallerId.GALAXY_STORE:
- return "Galaxy Store";
- case InstallerId.HUAWEI_APPGALLERY:
- return "Huawei AppGallery";
- case InstallerId.AMAZON_APPSTORE:
- return "Amazon AppStore";
- }
- throw new UnsupportedOperationException();
- }
-
- // Checks whether app is installed from unofficial source (e.g. directly via an APK)
-
- public static boolean isAppSideLoaded () {
- return getInstallerId() == InstallerId.UNKNOWN;
- }
-
- // Do not allow in-app updates from Google Play, if we are installed from market that doesn't allow it
-
- public static boolean allowInAppGooglePlayUpdates () {
- switch (getInstallerId()) {
- case InstallerId.UNKNOWN:
- case InstallerId.GOOGLE_PLAY: {
- //noinspection ObsoleteSdkInt
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && !BuildConfig.SIDE_LOAD_ONLY;
- }
- case InstallerId.GALAXY_STORE:
- case InstallerId.HUAWEI_APPGALLERY:
- case InstallerId.AMAZON_APPSTORE:
- return false;
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- // Do not allow in-app updates via Telegram channel, unless it's a direct APK installation
-
- public static boolean allowInAppTelegramUpdates () {
- return !BuildConfig.EXPERIMENTAL && getInstallerId() == InstallerId.UNKNOWN;
- }
-
- // Do not allow non-store URLs for compliance
-
- public static class DownloadUrl {
- public final @InstallerId int installerId;
- public final String url;
-
- public DownloadUrl (int installerId, String url) {
- this.installerId = installerId;
- this.url = url;
- }
- }
-
- @SuppressWarnings("ConstantConditions")
- public static @NonNull DownloadUrl getDownloadUrl (@Nullable String remoteDownloadUrl) {
- @InstallerId int installerId = getInstallerId();
- switch (installerId) {
- case InstallerId.UNKNOWN: // primary distribution channel, no need to force URL.
- break;
- case InstallerId.GOOGLE_PLAY:
- if (!StringUtils.isEmpty(BuildConfig.GOOGLE_PLAY_URL)) {
- return new DownloadUrl(installerId, BuildConfig.GOOGLE_PLAY_URL);
- }
- break;
-
- case InstallerId.GALAXY_STORE:
- if (!StringUtils.isEmpty(BuildConfig.GALAXY_STORE_URL)) {
- return new DownloadUrl(installerId, BuildConfig.GALAXY_STORE_URL);
- }
- break;
- case InstallerId.HUAWEI_APPGALLERY:
- if (!StringUtils.isEmpty(BuildConfig.HUAWEI_APPGALLERY_URL)) {
- return new DownloadUrl(installerId, BuildConfig.HUAWEI_APPGALLERY_URL);
- }
- break;
- case InstallerId.AMAZON_APPSTORE:
- if (!StringUtils.isEmpty(BuildConfig.AMAZON_APPSTORE_URL)) {
- return new DownloadUrl(installerId, BuildConfig.AMAZON_APPSTORE_URL);
- }
- break;
- }
- if (remoteDownloadUrl != null) {
- return new DownloadUrl(InstallerId.UNKNOWN, remoteDownloadUrl);
- }
- if (StringUtils.isEmpty(BuildConfig.DOWNLOAD_URL)) {
- throw new UnsupportedOperationException();
- }
- return new DownloadUrl(InstallerId.UNKNOWN, BuildConfig.DOWNLOAD_URL);
- }
-}
diff --git a/app/src/main/java/org/thunderdog/challegram/util/AppUpdater.java b/app/src/main/java/org/thunderdog/challegram/util/AppUpdater.java
index 52b60f6833..2603547f60 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/AppUpdater.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/AppUpdater.java
@@ -52,6 +52,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import me.vkryl.android.AppInstallationUtil;
import me.vkryl.core.StringUtils;
import me.vkryl.core.lambda.RunnableBool;
import me.vkryl.core.reference.ReferenceList;
@@ -120,7 +121,7 @@ public AppUpdater (BaseActivity context) {
this.context = context;
this.listeners = new ReferenceList<>();
AppUpdateManager appUpdateManager = null;
- if (AppInstallationUtil.allowInAppGooglePlayUpdates()) {
+ if (AppInstallationUtil.allowInAppGooglePlayUpdates(context)) {
try {
appUpdateManager = AppUpdateManagerFactory.create(context);
} catch (Throwable t) {
@@ -183,7 +184,23 @@ private boolean preferTelegramChannelFlow () {
// TODO: add server config to force
return googlePlayUpdateManager == null ||
forceTelegramChannelFlow ||
- (googlePlayFlowError && AppInstallationUtil.isAppSideLoaded());
+ (googlePlayFlowError && AppInstallationUtil.isAppSideLoaded(UI.getAppContext()));
+ }
+
+ public static AppInstallationUtil.PublicMarketUrls publicMarketUrls () {
+ return new AppInstallationUtil.PublicMarketUrls(
+ BuildConfig.DOWNLOAD_URL,
+ BuildConfig.GOOGLE_PLAY_URL,
+ BuildConfig.GALAXY_STORE_URL,
+ BuildConfig.HUAWEI_APPGALLERY_URL,
+ BuildConfig.AMAZON_APPSTORE_URL
+ );
+ }
+
+ public static AppInstallationUtil.DownloadUrl getDownloadUrl (@Nullable String serverSuggestedDownloadUrl) {
+ @AppInstallationUtil.InstallerId int installerId = AppInstallationUtil.getInstallerId(UI.getAppContext());
+ AppInstallationUtil.PublicMarketUrls publicMarketUrls = publicMarketUrls();
+ return publicMarketUrls.toDownloadUrl(installerId, serverSuggestedDownloadUrl);
}
private void setState (@State int state) {
@@ -219,7 +236,7 @@ private void checkForGooglePlayUpdates () {
}
case UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS:
case UpdateAvailability.UPDATE_NOT_AVAILABLE: {
- if (AppInstallationUtil.isAppSideLoaded()) {
+ if (AppInstallationUtil.isAppSideLoaded(UI.getAppContext())) {
onGooglePlayFlowError();
} else {
onUpdateUnavailable();
@@ -308,7 +325,7 @@ private void checkForTelegramChannelUpdates () {
onUpdateUnavailable();
return;
}
- if (!AppInstallationUtil.allowInAppTelegramUpdates() && !tdlib.hasUrgentInAppUpdate()) {
+ if (BuildConfig.EXPERIMENTAL || (!AppInstallationUtil.allowInAppTelegramUpdates(UI.getAppContext()) && !tdlib.hasUrgentInAppUpdate())) {
onUpdateUnavailable();
return;
}
diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt
index cbf74a8fbc..8294e21cca 100644
--- a/buildSrc/src/main/kotlin/Config.kt
+++ b/buildSrc/src/main/kotlin/Config.kt
@@ -28,7 +28,7 @@ object LibraryVersions {
const val ANNOTATIONS = "1.3.0"
}
-class AbiVariant (val flavor: String, vararg val filters: String = arrayOf(), val displayName: String = filters[0], val sideLoadOnly: Boolean = false) {
+class AbiVariant (val flavor: String, vararg val filters: String = arrayOf(), val displayName: String = filters[0]) {
init {
if (filters.isEmpty())
error("Empty filters passed")
diff --git a/vkryl/android b/vkryl/android
index 7a054bcb83..2505fb09e9 160000
--- a/vkryl/android
+++ b/vkryl/android
@@ -1 +1 @@
-Subproject commit 7a054bcb83c652233fbab0397d3884cd96a99d8c
+Subproject commit 2505fb09e9490f661a33165ecf81e090c7ab70ac
From 756c33af57243a0cba2e973d8d3c51ae75d4c49a Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 30 Jan 2024 23:34:29 +0400
Subject: [PATCH 38/61] Do not force show update prompt in debug builds
---
app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
index 6e25d6d124..b9ff9c34dc 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
@@ -10377,7 +10377,7 @@ public void findUpdateFile (@NonNull RunnableData onDone) {
version = document.fileName.substring(prefix.length(), i == -1 ? document.fileName.length() : i);
if (version.matches("^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$")) {
buildNo = StringUtils.parseInt(version.substring(version.lastIndexOf('.') + 1));
- if (buildNo > BuildConfig.ORIGINAL_VERSION_CODE || BuildConfig.DEBUG) {
+ if (buildNo > BuildConfig.ORIGINAL_VERSION_CODE) {
ok = true;
}
}
From abf75bcc8557f452b7087634e103507fdcfd0ef0 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Wed, 31 Jan 2024 01:13:26 +0400
Subject: [PATCH 39/61] Improve in-app orientation locking + In-app camera
state fixes + Properly handle fold event on foldable devices
---
app/src/main/AndroidManifest.xml | 8 +-
.../thunderdog/challegram/BaseActivity.java | 101 ++++++++++--------
.../challegram/player/RoundVideoRecorder.java | 4 +-
.../ui/camera/CameraController.java | 3 +-
.../challegram/ui/camera/CameraLayout.java | 17 +--
.../ui/camera/CameraTextureView.java | 4 +-
.../ui/camera/legacy/CameraApi.java | 8 ++
7 files changed, 77 insertions(+), 68 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 07581068c5..526d02f9ed 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -242,14 +242,14 @@
diff --git a/app/src/main/java/org/thunderdog/challegram/BaseActivity.java b/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
index d556589509..8c84c74fd6 100644
--- a/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
+++ b/app/src/main/java/org/thunderdog/challegram/BaseActivity.java
@@ -1160,6 +1160,7 @@ public void onDestroy () {
Tracer.onUiError(t);
throw t;
}
+ setOrientationLockFlags(0);
if (navigation != null) {
navigation.destroy();
}
@@ -1413,17 +1414,6 @@ public int getCurrentOrientation () {
return currentOrientation;
}
- public void lockOrientation (int newOrientation) {
- if (currentOrientation != newOrientation) {
- currentOrientation = newOrientation;
- if (mIsOrientationBlocked) {
- requestAndroidOrientation(newOrientation);
- } else {
- setIsOrientationBlocked(true);
- }
- }
- }
-
private void setIsOrientationBlocked (boolean blocked) {
if (mIsOrientationBlocked == blocked || mIsOrientationRequested)
return;
@@ -1431,21 +1421,24 @@ private void setIsOrientationBlocked (boolean blocked) {
mIsOrientationBlocked = blocked;
if (blocked) {
- int rotation = getWindowManager().getDefaultDisplay().getRotation();
-
- if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE &&
- (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90)) {
- requestAndroidOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
- } else if (currentOrientation == Configuration.ORIENTATION_PORTRAIT &&
- (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90)) {
- requestAndroidOrientationPortrait();
- } else if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE &&
- (rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270)) {
- requestAndroidOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ requestAndroidOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
} else {
- if (currentOrientation == Configuration.ORIENTATION_PORTRAIT &&
- (rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270)) {
+ int rotation = getWindowManager().getDefaultDisplay().getRotation();
+ if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE &&
+ (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90)) {
+ requestAndroidOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
+ } else if (currentOrientation == Configuration.ORIENTATION_PORTRAIT &&
+ (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_90)) {
requestAndroidOrientationPortrait();
+ } else if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE &&
+ (rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270)) {
+ requestAndroidOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ } else {
+ if (currentOrientation == Configuration.ORIENTATION_PORTRAIT &&
+ (rotation == Surface.ROTATION_180 || rotation == Surface.ROTATION_270)) {
+ requestAndroidOrientationPortrait();
+ }
}
}
} else {
@@ -2914,36 +2907,47 @@ private void setCameraDragging (boolean isDragging) {
}
}
- private boolean cameraOrientationBlocked;
- private int savedAnimation;
+ private int savedRotationAnimation = -1;
+
+ private void requestWindowRotationAnimation (int requestedAnimation) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ Window window = getWindow();
+ WindowManager.LayoutParams attrs = window.getAttributes();
+ int pendingRotationAnimation;
+ if (requestedAnimation == -1) {
+ pendingRotationAnimation = savedRotationAnimation;
+ savedRotationAnimation = -1;
+ } else {
+ pendingRotationAnimation = requestedAnimation;
+ if (savedRotationAnimation == -1) {
+ savedRotationAnimation = attrs.rotationAnimation;
+ }
+ }
+ if (pendingRotationAnimation != -1 && attrs.rotationAnimation != pendingRotationAnimation) {
+ attrs.rotationAnimation = pendingRotationAnimation;
+ window.setAttributes(attrs);
+ }
+ }
+ }
private void checkCameraOrientationBlocked () {
boolean isBlocked = ((cameraFactor < 1f && isCameraOpen) || (cameraFactor != 0f && cameraFactor != 1f) || isCameraDragging || (cameraFactor == 1f && camera != null && camera.supportsCustomRotations())) && !(camera != null && camera.hasOpenEditor());
- if (cameraOrientationBlocked != isBlocked) {
- cameraOrientationBlocked = isBlocked;
- setIsOrientationBlocked(isBlocked);
- }
+ setOrientationLockFlagEnabled(ORIENTATION_FLAG_CAMERA, isBlocked);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
boolean needCrossFadeAnimation = cameraFactor == 1f;
- int desiredRotation;
- Window window = getWindow();
- WindowManager.LayoutParams attrs = window.getAttributes();
+ int rotationAnimation;
if (needCrossFadeAnimation) {
- if (attrs.rotationAnimation != WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT) {
- savedAnimation = attrs.rotationAnimation;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && false) {
+ // looks bad on AOSP (Pixel Fold), and exactly like JUMPCUT on Samsung devices
+ rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+ } else {
+ rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
}
- desiredRotation = WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
} else {
- desiredRotation = savedAnimation;
- }
- if (attrs.rotationAnimation != desiredRotation) {
- attrs.rotationAnimation = desiredRotation;
- window.setAttributes(attrs);
+ rotationAnimation = -1;
}
+ requestWindowRotationAnimation(rotationAnimation);
}
- /*if (isBlocked && camera != null && !camera.supportsCustomRotations()) {
- lockOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- }*/
}
private void setCameraOpen (ViewController.CameraOpenOptions options, boolean isOpen, boolean byDrag) {
@@ -3544,18 +3548,23 @@ public final void onTrackChanged (Tdlib tdlib, @Nullable TdApi.Message newTrack,
public static final int ORIENTATION_FLAG_RECORDING = 1 << 5;
public static final int ORIENTATION_FLAG_CROP = 1 << 6;
public static final int ORIENTATION_FLAG_PROXIMITY = 1 << 7;
+ public static final int ORIENTATION_FLAG_CAMERA = 1 << 8;
private int orientationFlags;
- public void setOrientationLockFlagEnabled (int flag, boolean enabled) {
+ private void setOrientationLockFlags (int flags) {
boolean oldLocked = this.orientationFlags != 0;
- this.orientationFlags = BitwiseUtils.setFlag(this.orientationFlags, flag, enabled);
+ this.orientationFlags = flags;
boolean newLocked = this.orientationFlags != 0;
if (oldLocked != newLocked) {
setIsOrientationBlocked(newLocked);
}
}
+ public void setOrientationLockFlagEnabled (int flag, boolean enabled) {
+ setOrientationLockFlags(BitwiseUtils.setFlag(this.orientationFlags, flag, enabled));
+ }
+
// Language
@Override
diff --git a/app/src/main/java/org/thunderdog/challegram/player/RoundVideoRecorder.java b/app/src/main/java/org/thunderdog/challegram/player/RoundVideoRecorder.java
index 09cfbf6199..5b4dd8a483 100644
--- a/app/src/main/java/org/thunderdog/challegram/player/RoundVideoRecorder.java
+++ b/app/src/main/java/org/thunderdog/challegram/player/RoundVideoRecorder.java
@@ -747,7 +747,7 @@ private void doDestroy () {
}
}
- private boolean doCapture () {
+ public boolean isCapturing () {
return isCapturing || finishCapture || recording;
}
@@ -764,7 +764,7 @@ private void onDraw(Integer cameraId) {
}
cameraSurface.updateTexImage();
- if (doCapture()) {
+ if (isCapturing()) {
if (!recording) {
int resolution;
int bitrate;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraController.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraController.java
index a56ef659d6..64d1acda85 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraController.java
@@ -1256,7 +1256,6 @@ private boolean applyFakeRotation () {
}
int prevOrientation = context().getCurrentOrientation();
- context().lockOrientation(requestedOrientation);
checkDisplayRotation();
return (prevOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) != (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
@@ -1276,7 +1275,7 @@ public void onFactorChanged (int id, float factor, float fraction, FactorAnimato
public void onFactorChangeFinished (int id, float finalFactor, FactorAnimator callee) {
switch (id) {
case ANIMATOR_ROTATION:
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2 && finalFactor % 90.0f == 0.0f) {
applyFakeRotation();
}
break;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraLayout.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraLayout.java
index eb85715a64..8799667ac2 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraLayout.java
@@ -41,16 +41,6 @@ public void setDisallowRatioChanges (boolean disallowRatioChanges) {
}
}
- private int originalWidth, originalHeight;
-
- public int getOriginalWidth () {
- return originalWidth;
- }
-
- public int getOriginalHeight () {
- return originalHeight;
- }
-
@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
int viewWidth = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
@@ -62,7 +52,7 @@ protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
final float maxAspectRatio = Settings.instance().getCameraAspectRatio();
if (maxAspectRatio != 0f) {
float aspectRatio = MathUtils.aspectRatio(viewWidth, viewHeight);
- if (aspectRatio != maxAspectRatio) {
+ if (aspectRatio > maxAspectRatio) {
if (viewWidth > viewHeight) {
viewWidth = (int) ((float) viewWidth / aspectRatio * maxAspectRatio);
} else {
@@ -72,7 +62,10 @@ protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
}
}
- super.onMeasure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.getMode(widthMeasureSpec)), MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.getMode(heightMeasureSpec)));
+ super.onMeasure(
+ MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.getMode(widthMeasureSpec)),
+ MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.getMode(heightMeasureSpec))
+ );
int width = getMeasuredWidth();
int height = getMeasuredHeight();
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraTextureView.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraTextureView.java
index ef82e25a74..2d5336c421 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraTextureView.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraTextureView.java
@@ -64,7 +64,7 @@ protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
if (!ignoreAspectRatio && ratioWidth > 0 && ratioHeight > 0) {
int width, height;
- if (viewWidth < viewHeight * ratioWidth / ratioHeight) {
+ if (viewWidth < viewHeight * ((float) ratioWidth / (float) ratioHeight)) {
width = viewWidth;
height = (int) ((float) viewWidth * ((float) ratioHeight / (float) ratioWidth));
} else {
@@ -73,7 +73,7 @@ protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
}
float ratio = Math.max((float) viewWidth / (float) width, (float) viewHeight / (float) height);
- if (ratio > 1f) {
+ if (ratio != 1f) {
width *= ratio;
height *= ratio;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/legacy/CameraApi.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/legacy/CameraApi.java
index e0249cc371..186b254cdb 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/legacy/CameraApi.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/legacy/CameraApi.java
@@ -88,10 +88,18 @@ public final void setPreviewSize (int viewWidth, int viewHeight) {
mPreviewWidth = viewWidth;
mPreviewHeight = viewHeight;
onPreviewSizeChanged(viewWidth, viewHeight);
+ if (isCameraActive && !isCameraBusy()) {
+ setCameraActive(false);
+ setCameraActive(true);
+ }
}
}
protected abstract void onPreviewSizeChanged (int newWidth, int newHeight);
+ private boolean isCameraBusy () {
+ return isVideoCapturing || (roundRecorder != null && roundRecorder.isCapturing());
+ }
+
/**
* Open camera preview
*/
From 6fbb7d7e3acd2ec733985bfd8f6fcb55fa5d1b41 Mon Sep 17 00:00:00 2001
From: tgx-server
Date: Tue, 30 Jan 2024 21:41:35 +0000
Subject: [PATCH 40/61] Version bump to `1682`
---
version.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.properties b/version.properties
index 5cf8d21264..97e0af4578 100644
--- a/version.properties
+++ b/version.properties
@@ -1,5 +1,5 @@
# App
-version.app=1681
+version.app=1682
version.major=0
# Anchor date point in app versioning
version.creation=873642600564
From f19ffbcdada424f07fa105068b833ac56af10f46 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Wed, 31 Jan 2024 20:13:14 +0400
Subject: [PATCH 41/61] Remove debug toasts for `needRestrictScreenshots`
changes
---
.../org/thunderdog/challegram/telegram/TdlibMessageViewer.java | 3 ---
1 file changed, 3 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibMessageViewer.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibMessageViewer.java
index 8a16801ff8..82de0f15ff 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibMessageViewer.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibMessageViewer.java
@@ -869,9 +869,6 @@ private void checkNeedRestrictScreenshots () {
}
if (this.needRestrictScreenshots != needRestrictScreenshots) {
this.needRestrictScreenshots = needRestrictScreenshots;
- if (BuildConfig.DEBUG) {
- UI.showToast("update restrictScreenshots to " + needRestrictScreenshots, Toast.LENGTH_SHORT);
- }
for (Listener listener : listeners) {
listener.onNeedRestrictScreenshots(this, needRestrictScreenshots);
}
From df3e65763d96e75e138ce1a101e71c158b19ad10 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Wed, 31 Jan 2024 20:13:35 +0400
Subject: [PATCH 42/61] Enable application logging by default in debug builds
---
app/src/main/java/org/thunderdog/challegram/Log.java | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/Log.java b/app/src/main/java/org/thunderdog/challegram/Log.java
index 1f7031b0a6..3e0901a6f6 100644
--- a/app/src/main/java/org/thunderdog/challegram/Log.java
+++ b/app/src/main/java/org/thunderdog/challegram/Log.java
@@ -453,13 +453,15 @@ public static void load (SharedPreferences prefs) {
level = Log.LEVEL_VERBOSE;
tags = TAG_NDK | TAG_CRASH;
} else {
- settings = prefs.getInt(Settings.KEY_LOG_SETTINGS, 0);
- level = prefs.getInt(Settings.KEY_LOG_LEVEL, Log.LEVEL_ASSERT);
- long defaultTags = Log.TAG_CRASH | Log.TAG_FCM | Log.TAG_ACCOUNTS;
+ int defaultLogSettings = BuildConfig.DEBUG ? Log.SETTING_ANDROID_LOG : 0;
+ int defaultLogLevel = BuildConfig.DEBUG ? Log.LEVEL_VERBOSE : Log.LEVEL_ASSERT;
+ long defaultLogTags = Log.TAG_CRASH | Log.TAG_FCM | Log.TAG_ACCOUNTS;
if (Config.DEBUG_GALAXY_TAB_2) {
- defaultTags |= Log.TAG_INTRO;
+ defaultLogTags |= Log.TAG_INTRO;
}
- tags = prefs.getLong(Settings.KEY_LOG_TAGS, defaultTags);
+ settings = prefs.getInt(Settings.KEY_LOG_SETTINGS, defaultLogSettings);
+ level = prefs.getInt(Settings.KEY_LOG_LEVEL, defaultLogLevel);
+ tags = prefs.getLong(Settings.KEY_LOG_TAGS, defaultLogTags);
}
setLogLevelImpl(level);
From 7b2c8c95a9b86f625ca1b5378e25d0404f51a825 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Wed, 31 Jan 2024 20:17:52 +0400
Subject: [PATCH 43/61] Upgrade CameraX to `1.3.1`
---
app/build.gradle.kts | 7 +-
.../challegram/ui/camera/CameraButton.java | 6 +-
.../ui/camera/CameraController.java | 15 +
.../challegram/ui/camera/CameraDelegate.java | 5 +
.../challegram/ui/camera/CameraManager.java | 25 +-
.../ui/camera/x/CameraManagerX.java | 261 ++++++++++--------
6 files changed, 187 insertions(+), 132 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index f76ef4d74f..a559cb4473 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -205,9 +205,10 @@ dependencies {
implementation("androidx.interpolator:interpolator:1.0.0")
implementation("androidx.gridlayout:gridlayout:1.0.0")
// CameraX: https://developer.android.com/jetpack/androidx/releases/camera
- implementation("androidx.camera:camera-camera2:1.2.3")
- implementation("androidx.camera:camera-lifecycle:1.2.3")
- implementation("androidx.camera:camera-view:1.2.3")
+ implementation("androidx.camera:camera-camera2:1.3.1")
+ implementation("androidx.camera:camera-video:1.3.1")
+ implementation("androidx.camera:camera-lifecycle:1.3.1")
+ implementation("androidx.camera:camera-view:1.3.1")
// Google Play Services: https://developers.google.com/android/guides/releases
implementation("com.google.android.gms:play-services-base:17.6.0")
implementation("com.google.android.gms:play-services-basement:17.6.0")
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraButton.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraButton.java
index bad90c4966..ab90308750 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraButton.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraButton.java
@@ -124,7 +124,10 @@ public boolean takePhoto () {
return false;
}
+ private boolean takeVideoRequested;
+
public void takeVideo () {
+ takeVideoRequested = true;
if (listener != null) {
inVideoCapture = listener.onStartVideoCapture(this);
} else {
@@ -248,8 +251,9 @@ public boolean onTouchEvent (MotionEvent e) {
if (isActive) {
super.onTouchEvent(e);
}
- finishVideoCapture(!isInRecordMode && e.getAction() == MotionEvent.ACTION_UP && isActive);
+ finishVideoCapture(!isInRecordMode && e.getAction() == MotionEvent.ACTION_UP && isActive && !takeVideoRequested);
isActive = false;
+ takeVideoRequested = false;
break;
default: {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraController.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraController.java
index 64d1acda85..f4e997868a 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraController.java
@@ -571,6 +571,16 @@ public void resolveExpectedError (@CameraError.Code int code) {
}
}
+ @Override
+ public void displayHint (String hint) {
+ if (!UI.inUiThread()) {
+ handler.sendMessage(Message.obtain(handler, ACTION_DISPLAY_HINT, hint));
+ return;
+ }
+
+ context().tooltipManager().builder(button).controller(this).show(tdlib, hint).hideDelayed();
+ }
+
private int availableCameraCount = -1;
@AnyThread
@@ -1823,6 +1833,7 @@ public boolean onKeyUp (int keyCode, KeyEvent event) {
private static final int ACTION_ZOOM_CHANGED = 9;
private static final int ACTION_PERFORM_SUCCESS_HINT = 10;
private static final int ACTION_UPDATE_DURATION = 11;
+ private static final int ACTION_DISPLAY_HINT = 12;
private static class CameraUiHandler extends Handler {
private final CameraController context;
@@ -1839,6 +1850,10 @@ public void handleMessage (Message msg) {
context.displayFatalErrorMessage((String) msg.obj);
break;
}
+ case ACTION_DISPLAY_HINT: {
+ context.displayHint((String) msg.obj);
+ break;
+ }
case ACTION_CHANGE_CAMERA_COUNT: {
context.onAvailableCamerasCountChanged(msg.arg1);
break;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraDelegate.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraDelegate.java
index fff6dd0d8e..32629e20a9 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraDelegate.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraDelegate.java
@@ -24,6 +24,11 @@ public interface CameraDelegate {
* */
void displayFatalErrorMessage (String msg);
+ /**
+ * Show hint over record button
+ * */
+ void displayHint (String hint);
+
/**
* Start error resolution, e.g.
*
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraManager.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraManager.java
index 12e2fb315b..b808c8ba6b 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraManager.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/CameraManager.java
@@ -18,7 +18,6 @@
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
-import android.widget.Toast;
import androidx.annotation.AnyThread;
import androidx.annotation.UiThread;
@@ -28,6 +27,7 @@
import org.thunderdog.challegram.Log;
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.U;
+import org.thunderdog.challegram.core.Lang;
import org.thunderdog.challegram.loader.ImageGalleryFile;
import org.thunderdog.challegram.loader.ImageReader;
import org.thunderdog.challegram.loader.ImageStrictCache;
@@ -215,9 +215,9 @@ public final void setTakingPhoto (boolean isTakingPhoto) {
}
}
- public final void setFinishingVideo (boolean isVideo) {
- if (this.finishingVideo != isVideo) {
- this.finishingVideo = isVideo;
+ public final void setFinishingVideo (boolean isFinishingVideo) {
+ if (this.finishingVideo != isFinishingVideo) {
+ this.finishingVideo = isFinishingVideo;
checkUiBlocked();
}
}
@@ -262,10 +262,15 @@ public final void onTakeMediaError (boolean isVideo) {
if (isVideo) {
setTakingVideo(false, -1);
- UI.showToast(R.string.TakeVideoError, Toast.LENGTH_SHORT);
+ UI.post(() -> {
+ setFinishingVideo(false);
+ delegate.displayHint(Lang.getString(R.string.TakeVideoError));
+ }, 20);
} else {
setTakingPhoto(false);
- UI.showToast(R.string.TakePhotoError, Toast.LENGTH_SHORT);
+ UI.post(() -> {
+ delegate.displayHint(Lang.getString(R.string.TakePhotoError));
+ });
}
}
@@ -282,7 +287,7 @@ public void onTakeMediaResult (final ImageGalleryFile resultFile, boolean isVide
if (!delegate.usePrivateFolder()) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- U.copyToGallery(UI.getContext(context), resultFile.getFilePath(), U.TYPE_PHOTO, false, resultFile::trackCopy);
+ U.copyToGallery(UI.getContext(context), resultFile.getFilePath(), isVideo ? U.TYPE_VIDEO : U.TYPE_PHOTO, false, resultFile::trackCopy);
} else {
U.addToGallery(new File(resultFile.getFilePath()));
}
@@ -301,8 +306,12 @@ public void onTakeMediaResult (final ImageGalleryFile resultFile, boolean isVide
}
}
+ private boolean inPrivateMode () {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q || delegate.usePrivateFolder();
+ }
+
public final File getOutputFile (boolean isVideo) {
- final boolean isPrivate = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q || delegate.usePrivateFolder();
+ final boolean isPrivate = inPrivateMode();
if (isVideo) {
return U.generateVideoPath(isPrivate);
} else {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/camera/x/CameraManagerX.java b/app/src/main/java/org/thunderdog/challegram/ui/camera/x/CameraManagerX.java
index b5806c373e..d1a6af6c3f 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/camera/x/CameraManagerX.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/camera/x/CameraManagerX.java
@@ -22,7 +22,6 @@
import android.view.View;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.Camera;
import androidx.camera.core.CameraInfoUnavailableException;
@@ -31,9 +30,19 @@
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.Preview;
-import androidx.camera.core.VideoCapture;
import androidx.camera.core.ZoomState;
+import androidx.camera.core.impl.UseCaseConfigFactory;
+import androidx.camera.core.resolutionselector.AspectRatioStrategy;
+import androidx.camera.core.resolutionselector.ResolutionSelector;
import androidx.camera.lifecycle.ProcessCameraProvider;
+import androidx.camera.video.FallbackStrategy;
+import androidx.camera.video.FileOutputOptions;
+import androidx.camera.video.Quality;
+import androidx.camera.video.QualitySelector;
+import androidx.camera.video.Recorder;
+import androidx.camera.video.Recording;
+import androidx.camera.video.VideoCapture;
+import androidx.camera.video.VideoRecordEvent;
import androidx.camera.view.PreviewView;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
@@ -50,13 +59,12 @@
import org.thunderdog.challegram.unsorted.Settings;
import java.io.File;
+import java.util.Arrays;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class CameraManagerX extends CameraManager {
- private static final boolean REUSE_PREVIEW_DISABLED = true;
- private static final boolean REUSE_CAPTURE_DISABLED = true;
-
public CameraManagerX (Context context, CameraDelegate delegate) {
super(context, delegate);
}
@@ -74,14 +82,14 @@ protected PreviewView onCreateView () {
private boolean isOpen;
private ProcessCameraProvider cameraProvider;
private ImageCapture imageCapture;
- private VideoCapture videoCapture;
+ private VideoCapture videoCapture;
+ private Recording videoRecording;
+ private VideoRecordEvent videoRecordStatus;
private Preview preview;
private int previewRotation;
private Camera camera;
private int flashMode = ImageCapture.FLASH_MODE_OFF;
private boolean originalFacing;
- private int lastAspectRatio;
- private Rational lastAspectRatioCustom;
private CameraQrBridge cameraQrBridge;
@Override
@@ -122,9 +130,7 @@ protected void onParentSizeChanged (int width, int height) {
} else {
aspectRatioCustom = new Rational(Math.min(width, height), Math.max(width, height));
}
- if (lastAspectRatio != aspectRatio || (aspectRatioCustom == null) != (lastAspectRatioCustom == null) || (aspectRatioCustom != null && !aspectRatioCustom.equals(lastAspectRatioCustom))) {
- bindPreview();
- }
+ bindPreview();
}
}
@@ -139,42 +145,26 @@ private void bindPreview () {
if (!isOpen || isPaused || cameraProvider == null)
return;
+ // Must unbind the use-cases before rebinding them
+ cameraProvider.unbindAll();
+
+ AspectRatioStrategy aspectRatioStrategy;
int aspectRatioMode = Settings.instance().getCameraAspectRatioMode();
- Rational aspectRatioCustom = null;
- int aspectRatio = AspectRatio.RATIO_16_9;
switch (aspectRatioMode) {
- case Settings.CAMERA_RATIO_1_1:
- aspectRatioCustom = new Rational(1, 1);
+ case Settings.CAMERA_RATIO_FULL_SCREEN:
+ case Settings.CAMERA_RATIO_16_9:
+ aspectRatioStrategy = AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY;
break;
+ case Settings.CAMERA_RATIO_1_1:
case Settings.CAMERA_RATIO_4_3:
- aspectRatio = AspectRatio.RATIO_4_3;
- break;
- case Settings.CAMERA_RATIO_FULL_SCREEN: {
- int width = getParentWidth();
- int height = getParentHeight();
- if (width == 0 || height == 0)
- return;
- float ratio = (float) Math.max(width, height) / (float) Math.min(width, height);
- if (ratio == 16f / 9f) {
- aspectRatio = AspectRatio.RATIO_16_9;
- } else if (ratio == 4f / 3f) {
- aspectRatio = AspectRatio.RATIO_4_3;
- } else if (ratio == 1f) {
- aspectRatioCustom = new Rational(1, 1);
- } else {
- aspectRatioCustom = new Rational(Math.min(width, height), Math.max(width, height));
- }
- break;
- }
- case Settings.CAMERA_RATIO_16_9:
default:
- aspectRatio = AspectRatio.RATIO_16_9;
+ aspectRatioStrategy = AspectRatioStrategy.RATIO_4_3_FALLBACK_AUTO_STRATEGY;
break;
}
-
- // Must unbind the use-cases before rebinding them
- cameraProvider.unbindAll();
+ ResolutionSelector resolutionSelector = new ResolutionSelector.Builder()
+ .setAspectRatioStrategy(aspectRatioStrategy)
+ .build();
final int lensFacing = preferFrontFacingCamera() ? CameraSelector.LENS_FACING_FRONT : CameraSelector.LENS_FACING_BACK;
@@ -190,65 +180,51 @@ private void bindPreview () {
Log.e(Log.TAG_CAMERA, "Unable to camera %d", lensFacing);
}
- if (REUSE_PREVIEW_DISABLED || preview == null || previewRotation != getSurfaceRotation()) {
- Preview.Builder previewBuilder = new Preview.Builder()
- .setTargetRotation(previewRotation = getSurfaceRotation());
- if (aspectRatioCustom != null) {
- previewBuilder.setTargetResolution(toSize(aspectRatioCustom, getSurfaceRotation()));
- } else {
- previewBuilder.setTargetAspectRatio(aspectRatio);
- }
- preview = previewBuilder.build();
- }
+ Preview.Builder previewBuilder = new Preview.Builder()
+ .setTargetRotation(previewRotation = getSurfaceRotation())
+ .setResolutionSelector(resolutionSelector);
+ preview = previewBuilder.build();
- if (REUSE_CAPTURE_DISABLED || imageCapture == null) {
- ImageCapture.Builder imageCaptureBuilder = new ImageCapture.Builder()
- .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
- .setFlashMode(flashMode)
- .setTargetRotation(getSurfaceRotation());
- if (aspectRatioCustom != null) {
- imageCaptureBuilder.setTargetResolution(toSize(aspectRatioCustom, getSurfaceRotation()));
- } else {
- imageCaptureBuilder.setTargetAspectRatio(aspectRatio);
- }
- imageCapture = imageCaptureBuilder.build();
- } else {
- imageCapture.setFlashMode(flashMode);
- imageCapture.setTargetRotation(getSurfaceRotation());
- }
- if (REUSE_CAPTURE_DISABLED || videoCapture == null) {
- VideoCapture.Builder b = new VideoCapture.Builder()
- .setTargetRotation(getSurfaceRotation());
- if (aspectRatioCustom != null) {
- b.setTargetResolution(toSize(aspectRatioCustom, getSurfaceRotation()));
- } else {
- b.setTargetAspectRatio(aspectRatio);
- }
- videoCapture = b.build();
- } else {
- videoCapture.setTargetRotation(getSurfaceRotation());
- }
+ ImageCapture.Builder imageCaptureBuilder = new ImageCapture.Builder()
+ .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
+ .setFlashMode(flashMode)
+ .setTargetRotation(getSurfaceRotation())
+ .setResolutionSelector(resolutionSelector);
+ imageCapture = imageCaptureBuilder.build();
- ImageAnalysis imageAnalyzer = new ImageAnalysis.Builder()
+ QualitySelector qualitySelector = QualitySelector.fromOrderedList(
+ Arrays.asList(Quality.FHD, Quality.HD, Quality.SD),
+ FallbackStrategy.lowerQualityOrHigherThan(Quality.SD)
+ );
+ Recorder recorder = new Recorder.Builder()
+ .setQualitySelector(qualitySelector)
+ .build();
+ videoCapture = new VideoCapture.Builder<>(recorder)
+ .setCaptureType(UseCaseConfigFactory.CaptureType.VIDEO_CAPTURE)
.setTargetRotation(getSurfaceRotation())
- .setTargetAspectRatio(aspectRatio)
+ .setResolutionSelector(resolutionSelector)
.build();
- if (delegate.useQrScanner()) {
+ ImageAnalysis imageAnalyzer;
+
+ boolean needQrScanner = delegate.useQrScanner();
+ if (needQrScanner) {
+ imageAnalyzer = new ImageAnalysis.Builder()
+ .setTargetRotation(getSurfaceRotation())
+ .setResolutionSelector(resolutionSelector)
+ .build();
if (cameraQrBridge == null) {
cameraQrBridge = new CameraQrBridge(this);
}
-
imageAnalyzer.setAnalyzer(cameraQrBridge.backgroundExecutor, proxy -> cameraQrBridge.processImage(proxy));
+ } else {
+ imageAnalyzer = null;
}
- lastAspectRatio = aspectRatio;
- lastAspectRatioCustom = aspectRatioCustom;
-
try {
// A variable number of use-cases can be passed here -
// camera provides access to CameraControl & CameraInfo
- if (delegate.useQrScanner()) {
+ if (needQrScanner) {
// We probably don't want to take photos or videos while scanning QR codes. (Also, there are 3 use case limit in CameraX)
this.camera = cameraProvider.bindToLifecycle((LifecycleOwner) context, cameraSelector, preview, imageAnalyzer);
} else {
@@ -271,13 +247,14 @@ public void closeCamera () {
if (this.isOpen) {
if (cameraProvider != null) {
cameraProvider.unbindAll();
- if (REUSE_PREVIEW_DISABLED) {
- preview = null;
- }
- if (REUSE_CAPTURE_DISABLED) {
- videoCapture = null;
- imageCapture = null;
+ preview = null;
+ videoRecordStatus = null;
+ if (videoRecording != null) {
+ videoRecording.close();
+ videoRecording = null;
}
+ videoCapture = null;
+ imageCapture = null;
cameraProvider = null;
camera = null;
}
@@ -478,41 +455,67 @@ public void onError (@NonNull ImageCaptureException e) {
}
}
- @SuppressWarnings("RestrictedApi")
@Override
protected boolean onStartVideoCapture (int outRotation) {
if (videoCapture != null) {
boolean success;
+ File outFile = getOutputFile(true);
+ boolean forceEnableTorch = flashMode == ImageCapture.FLASH_MODE_ON && camera.getCameraInfo().hasFlashUnit();
try {
- File outFile = getOutputFile(true);
- videoCapture.startRecording(new VideoCapture.OutputFileOptions.Builder(outFile).build(), ContextCompat.getMainExecutor(context), new VideoCapture.OnVideoSavedCallback() {
- @Override
- public void onVideoSaved (@NonNull VideoCapture.OutputFileResults ignored) {
- U.toGalleryFile(outFile, true, file -> {
- setTakingVideo(false, -1);
- onTakeMediaResult(file, true);
- });
- }
-
- @Override
- public void onError (int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
- setTakingVideo(false, -1);
- Log.e(Log.TAG_CAMERA, "Failed to capture video: %d, message: %s", cause, videoCaptureError, message);
- onTakeMediaError(true);
- }
- });
+ if (!outFile.createNewFile()) {
+ return false;
+ }
+ } catch (Throwable t) {
+ Log.w(Log.TAG_CAMERA, "Unable to create output file for video", t);
+ return false;
+ }
+ if (forceEnableTorch) {
+ forceEnableTorch();
+ }
+ try {
+ AtomicBoolean isFinished = new AtomicBoolean(false);
+ videoRecording = videoCapture.getOutput()
+ .prepareRecording(context, new FileOutputOptions.Builder(outFile).build())
+ .withAudioEnabled()
+ .start(ContextCompat.getMainExecutor(context), event -> {
+ if (!(event instanceof VideoRecordEvent.Status)) {
+ videoRecordStatus = event;
+ }
+ if (event instanceof VideoRecordEvent.Start) {
+ VideoRecordEvent.Start start = (VideoRecordEvent.Start) event;
+ setTakingVideo(true, SystemClock.uptimeMillis());
+ } else if (event instanceof VideoRecordEvent.Finalize) {
+ if (isFinished.getAndSet(true)) {
+ return;
+ }
+ VideoRecordEvent.Finalize finalize = (VideoRecordEvent.Finalize) event;
+
+ if (finalize.hasError()) {
+ if (outFile.exists()) {
+ if (!outFile.delete()) {
+ Log.e(Log.TAG_CAMERA, "Unable to delete video output file");
+ }
+ }
+ setTakingVideo(false, -1);
+ Log.e(Log.TAG_CAMERA, "Failed to capture video: %d", finalize.getCause(), finalize.getError());
+ onTakeMediaError(true);
+ } else {
+ U.toGalleryFile(outFile, true, file -> {
+ setTakingVideo(false, -1);
+ onTakeMediaResult(file, true);
+ });
+ }
+ }
+ });
success = true;
} catch (Throwable t) {
Log.e("Cannot start recording video", t);
success = false;
}
- if (success) {
- if (flashMode == ImageCapture.FLASH_MODE_ON && camera.getCameraInfo().hasFlashUnit()) {
- camera.getCameraControl().enableTorch(true);
- }
- UI.post(() -> setTakingVideo(true, SystemClock.uptimeMillis()));
- return true;
+ if (!success) {
+ disableTorch();
}
+ return success;
}
return false;
}
@@ -520,15 +523,33 @@ public void onError (int videoCaptureError, @NonNull String message, @Nullable T
@SuppressWarnings("RestrictedApi")
@Override
protected void onFinishOrCancelVideoCapture () {
- if (videoCapture != null) {
- videoCapture.stopRecording();
- if (flashMode == ImageCapture.FLASH_MODE_ON && camera.getCameraInfo().hasFlashUnit()) {
- camera.getCameraControl().enableTorch(false);
- }
- delegate.onVideoCaptureEnded();
+ if (videoRecording != null) {
+ videoRecording.stop();
+ onVideoRecordingFinished();
}
}
+ private boolean torchForceEnabled;
+
+ private void forceEnableTorch () {
+ if (!torchForceEnabled) {
+ torchForceEnabled = true;
+ camera.getCameraControl().enableTorch(true);
+ }
+ }
+
+ private void disableTorch () {
+ if (torchForceEnabled) {
+ torchForceEnabled = false;
+ camera.getCameraControl().enableTorch(false);
+ }
+ }
+
+ private void onVideoRecordingFinished () {
+ disableTorch();
+ delegate.onVideoCaptureEnded();
+ }
+
@Override
public void destroy () {
if (cameraQrBridge != null) {
From 945b809f7875109474d181450b9b3a7079031cb0 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Wed, 31 Jan 2024 20:31:13 +0400
Subject: [PATCH 44/61] Update `androidx.collection:collection` to `1.4.0`
---
app/build.gradle.kts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a559cb4473..346d131e2d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -201,7 +201,7 @@ dependencies {
implementation("androidx.work:work-runtime:2.9.0")
implementation("androidx.browser:browser:1.5.0") // 1.7.0+ requires minSdkVersion 19
implementation("androidx.exifinterface:exifinterface:1.3.7")
- implementation("androidx.collection:collection:1.3.0")
+ implementation("androidx.collection:collection:1.4.0")
implementation("androidx.interpolator:interpolator:1.0.0")
implementation("androidx.gridlayout:gridlayout:1.0.0")
// CameraX: https://developer.android.com/jetpack/androidx/releases/camera
From 3427d347164d7de05c96a82f0b08c025d5f7222a Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Thu, 1 Feb 2024 15:04:28 +0400
Subject: [PATCH 45/61] Add `spellchecker.dic` with project-specific dictionary
---
spellchecker.dic | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 spellchecker.dic
diff --git a/spellchecker.dic b/spellchecker.dic
new file mode 100644
index 0000000000..575eec41ff
--- /dev/null
+++ b/spellchecker.dic
@@ -0,0 +1,2 @@
+minithumbnail
+tdlib
\ No newline at end of file
From de6d9702541f3d849627801438bb90859aac0a2f Mon Sep 17 00:00:00 2001
From: Kira Roubin
Date: Fri, 2 Feb 2024 22:10:27 +0300
Subject: [PATCH 46/61] fix: Apply sticker round radius globally
---
.../challegram/component/sticker/StickerPreviewView.java | 6 ++++++
.../challegram/component/sticker/StickerSmallView.java | 5 +++++
.../org/thunderdog/challegram/data/TGMessageSticker.java | 7 +++----
3 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/component/sticker/StickerPreviewView.java b/app/src/main/java/org/thunderdog/challegram/component/sticker/StickerPreviewView.java
index 1804b8d69a..e12480235e 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/sticker/StickerPreviewView.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/sticker/StickerPreviewView.java
@@ -79,6 +79,8 @@
import me.vkryl.core.lambda.Destroyable;
import me.vkryl.td.Td;
+import moe.kirao.mgx.MoexConfig;
+
public class StickerPreviewView extends FrameLayoutFix implements FactorAnimator.Target, PopupLayout.AnimatedPopupProvider, BackListener, Destroyable, ThemeChangeListener {
private static final OvershootInterpolator OVERSHOOT_INTERPOLATOR = new OvershootInterpolator(1f);
@@ -325,14 +327,18 @@ private void setSticker (TGStickerObj sticker, @Nullable TGStickerObj effectStic
GifActor.addFreezeReason(currentSticker.getFullAnimation(), false);
}
this.currentSticker = sticker;
+ float radius = MoexConfig.roundedStickers ? Screen.dp(Theme.getBubbleMergeRadius()) : 0;
if (!sticker.isMasks() && !disableEmojis) {
this.emojiString = new EmojiString(sticker.getAllEmoji(), -1, paint);
} else {
this.emojiString = null;
}
if (effectSticker == null) layoutReceivers();
+ preview.setRadius(radius);
preview.requestFile(sticker.getImage());
+ imageReceiver.setRadius(radius);
imageReceiver.requestFile(sticker.getFullImage());
+ gifReceiver.setRadius(radius);
gifReceiver.requestFile(sticker.getFullAnimation());
if (sticker.isDefaultPremiumStar()) {
defaultPremiumStarDrawable = Drawables.get(R.drawable.baseline_premium_star_28);
diff --git a/app/src/main/java/org/thunderdog/challegram/component/sticker/StickerSmallView.java b/app/src/main/java/org/thunderdog/challegram/component/sticker/StickerSmallView.java
index 9eab67527b..e1088ad454 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/sticker/StickerSmallView.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/sticker/StickerSmallView.java
@@ -52,6 +52,8 @@
import me.vkryl.core.lambda.Destroyable;
import me.vkryl.td.Td;
+import moe.kirao.mgx.MoexConfig;
+
public class StickerSmallView extends View implements FactorAnimator.Target, Destroyable {
public static final float PADDING = 8f;
private static final Interpolator OVERSHOOT_INTERPOLATOR = new OvershootInterpolator(3.2f);
@@ -96,6 +98,7 @@ public void setForceHeight (int forceHeight) {
public void setSticker (@Nullable TGStickerObj sticker) {
this.sticker = sticker;
this.isAnimation = sticker != null && sticker.isAnimated();
+ float corners = MoexConfig.roundedStickers ? Screen.dp(Theme.getImageRadius()) : 0;
resetStickerState();
ImageFile imageFile = sticker != null && !sticker.isEmpty() ? sticker.getImage() : null;
GifFile gifFile = sticker != null && !sticker.isEmpty() ? sticker.getPreviewAnimation() : null;
@@ -103,7 +106,9 @@ public void setSticker (@Nullable TGStickerObj sticker) {
throw new RuntimeException("");
}
contour = sticker != null ? sticker.getContour(Math.min(imageReceiver.getWidth(), imageReceiver.getHeight())) : null;
+ imageReceiver.setRadius(corners);
imageReceiver.requestFile(imageFile);
+ gifReceiver.setRadius(corners);
gifReceiver.requestFile(gifFile);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGMessageSticker.java b/app/src/main/java/org/thunderdog/challegram/data/TGMessageSticker.java
index 7622bea8f6..9995107377 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGMessageSticker.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGMessageSticker.java
@@ -61,6 +61,7 @@
import me.vkryl.core.collection.LongSet;
import me.vkryl.td.Td;
import me.vkryl.td.TdConstants;
+
import moe.kirao.mgx.MoexConfig;
public class TGMessageSticker extends TGMessage implements AnimatedEmojiListener, TdlibEmojiManager.Watcher {
@@ -175,6 +176,7 @@ public void requestFiles (int key, ComplexReceiver receiver, boolean invalidate)
receiver.getPreviewReceiver(key).clear();
return;
}
+ float corners = MoexConfig.roundedStickers ? Screen.dp(Theme.getBubbleMergeRadius()) : 0;
//if (!invalidate) {
DoubleImageReceiver previewReceiver = receiver.getPreviewReceiver(key);
if (preview == null || hasAnimationEnded()) {
@@ -183,14 +185,11 @@ public void requestFiles (int key, ComplexReceiver receiver, boolean invalidate)
previewReceiver.clear();
preview = null;
} else {
+ previewReceiver.setRadius(corners);
previewReceiver.requestFile(null, preview);
}
//}
GifFile file = receiver.getGifReceiver(key).getCurrentFile();
- float corners = 0;
- if (MoexConfig.roundedStickers) {
- corners = Screen.dp(Theme.getBubbleMergeRadius());
- }
if (file != animatedFile) {
receiver.getGifReceiver(key).setRadius(corners);
receiver.getGifReceiver(key).requestFile(null); // The new file may have the same id as
From 1917e75a191020c435fdd1ef78d28b3bfc2b388a Mon Sep 17 00:00:00 2001
From: Kira Roubin
Date: Fri, 2 Feb 2024 22:33:53 +0300
Subject: [PATCH 47/61] fix: Remember send options needed a restart
---
app/src/main/java/moe/kirao/mgx/MoexConfig.java | 15 ++++++++++++---
.../thunderdog/challegram/ui/ShareController.java | 6 +++---
2 files changed, 15 insertions(+), 6 deletions(-)
diff --git a/app/src/main/java/moe/kirao/mgx/MoexConfig.java b/app/src/main/java/moe/kirao/mgx/MoexConfig.java
index afc168e12e..06d5fc4cf7 100644
--- a/app/src/main/java/moe/kirao/mgx/MoexConfig.java
+++ b/app/src/main/java/moe/kirao/mgx/MoexConfig.java
@@ -71,9 +71,6 @@ public class MoexConfig {
public static boolean hideMessagesBadge = instance().getBoolean(KEY_HIDE_MESSAGES_BADGE, false);
public static boolean reorderStickers = instance().getBoolean(KEY_ENABLE_REORDER_STICKERS, false);
public static boolean rememberOptions = instance().getBoolean(KEY_REMEMBER_SEND_OPTIONS, false);
- public static boolean rememberOptions_author = instance().getBoolean(KEY_REMEMBER_SEND_OPTIONS_AUTHOR, false);
- public static boolean rememberOptions_captions = instance().getBoolean(KEY_REMEMBER_SEND_OPTIONS_CAPTIONS, false);
- public static boolean rememberOptions_silent = instance().getBoolean(KEY_REMEMBER_SEND_OPTIONS_SOUND, false);
public static boolean squareAvatar = instance().getBoolean(KEY_SQUARE_AVATAR, false);
public static boolean blurDrawer = instance().getBoolean(KEY_BLUR_DRAWER, false);
public static boolean typingInsteadChoosing = instance().getBoolean(KEY_TYPING_INSTEAD_CHOOSING, true);
@@ -283,14 +280,26 @@ public void SendAsCopy (boolean state) {
putBoolean(KEY_REMEMBER_SEND_OPTIONS_AUTHOR, state);
}
+ public Boolean getAuthorState () {
+ return getBoolean(KEY_REMEMBER_SEND_OPTIONS_AUTHOR, false);
+ }
+
public void SendWithoutCaption (boolean state) {
putBoolean(KEY_REMEMBER_SEND_OPTIONS_CAPTIONS, state);
}
+ public Boolean getCaptionState () {
+ return getBoolean(KEY_REMEMBER_SEND_OPTIONS_CAPTIONS, false);
+ }
+
public void SendSilent (boolean state) {
putBoolean(KEY_REMEMBER_SEND_OPTIONS_SOUND, state);
}
+ public Boolean getSilentState () {
+ return getBoolean(KEY_REMEMBER_SEND_OPTIONS_SOUND, false);
+ }
+
public void toggleSquareAvatar () {
putBoolean(KEY_SQUARE_AVATAR, squareAvatar ^= true);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ShareController.java b/app/src/main/java/org/thunderdog/challegram/ui/ShareController.java
index e32baa69a3..a8c1fd1684 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ShareController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ShareController.java
@@ -3175,9 +3175,9 @@ private void showShareSettings () {
private boolean isSent;
private final boolean rememberOptions = MoexConfig.rememberOptions;
- private boolean needHideAuthor = rememberOptions && MoexConfig.rememberOptions_author;
- private boolean needRemoveCaptions = rememberOptions && MoexConfig.rememberOptions_captions;
- private boolean forceSendWithoutSound = rememberOptions && MoexConfig.rememberOptions_silent;
+ private boolean needHideAuthor = rememberOptions && MoexConfig.instance().getAuthorState();
+ private boolean needRemoveCaptions = rememberOptions && MoexConfig.instance().getCaptionState();
+ private boolean forceSendWithoutSound = rememberOptions && MoexConfig.instance().getSilentState();
private void sendMessages (boolean forceGoToChat, boolean isSingleTap, @Nullable TdApi.MessageSendOptions finalSendOptions) {
if (selectedChats.size() == 0 || isSent) {
From 2f15a208fbc8205133f664e6b4545dc1be8a1ea7 Mon Sep 17 00:00:00 2001
From: Kira Roubin
Date: Fri, 2 Feb 2024 22:50:35 +0300
Subject: [PATCH 48/61] fix: Reply and pin counters are shown when Hide time on
stickers toggled
---
.../java/org/thunderdog/challegram/data/TGMessage.java | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGMessage.java b/app/src/main/java/org/thunderdog/challegram/data/TGMessage.java
index 21b439cec4..fad407cfe1 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGMessage.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGMessage.java
@@ -3999,12 +3999,14 @@ protected void drawBubbleTimePart (Canvas c, MessageView view) {
startX += shareCounter.getScaledWidth(Screen.dp(COUNTER_ICON_MARGIN + COUNTER_ADD_MARGIN));
}
}
- if (replyCounter.getVisibility() > 0f) {
+ if (replyCounter.getVisibility() > 0f && !(isMsgSticker && hideStickerTimestamp)) {
replyCounter.draw(c, startX, counterY, Gravity.LEFT, 1f, view, iconColorId);
startX += replyCounter.getScaledWidth(Screen.dp(COUNTER_ICON_MARGIN + COUNTER_ADD_MARGIN));
}
- isPinned.draw(c, startX, counterY, Gravity.LEFT, 1f, view, iconColorId);
- startX += isPinned.getScaledWidth(Screen.dp(COUNTER_ICON_MARGIN));
+ if (!(isMsgSticker && hideStickerTimestamp)) {
+ isPinned.draw(c, startX, counterY, Gravity.LEFT, 1f, view, iconColorId);
+ startX += isPinned.getScaledWidth(Screen.dp(COUNTER_ICON_MARGIN));
+ }
if (shouldShowMessageRestrictedWarning()) {
if (isRestrictedByTelegram()) {
From c3fee173b0d21f27ab13eb9902e020249be3f159 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Sat, 3 Feb 2024 21:46:48 +0400
Subject: [PATCH 49/61] Optimize memory usage + Migrate from `ExoPlayer` to
`androidx:media3` + Upgrade `FFmpeg` to `6.0` + Memory optimizations
---
.gitmodules | 10 +-
app/build.gradle.kts | 16 +-
app/jni/BuildFlac.cmake | 4 +-
app/jni/CMakeLists.txt | 14 +-
app/jni/cmake/ReadVariables.cmake | 3 +
app/jni/gif.cpp | 354 ++++++++++--------
.../ffmpeg_jni.cc | 108 +++---
.../{exoplayer => androidx-media}/flac_jni.cc | 23 +-
.../flac_parser.cc | 0
.../include/data_source.h | 0
.../include/flac_parser.h | 0
.../{exoplayer => androidx-media}/opus_jni.cc | 38 +-
.../{exoplayer => androidx-media}/vpx_jni.cc | 38 +-
app/jni/third_party/ffmpeg | 2 +-
.../java/org/thunderdog/challegram/N.java | 1 -
.../java/org/thunderdog/challegram/U.java | 32 +-
.../challegram/loader/ImageActor.java | 3 +-
.../challegram/loader/ImageApicFile.java | 2 +-
.../challegram/loader/gif/GifActor.java | 6 +-
.../challegram/loader/gif/GifBridge.java | 1 -
.../challegram/mediaview/VideoPlayerView.java | 19 +-
.../challegram/player/AudioController.java | 25 +-
.../player/BasePlaybackController.java | 5 +-
.../challegram/player/ProximityManager.java | 7 +-
.../player/RoundVideoController.java | 13 +-
.../challegram/player/TGPlayerController.java | 3 +-
.../challegram/service/AudioService.java | 5 +-
.../challegram/telegram/TdlibDataSource.java | 13 +-
.../challegram/ui/MessagesController.java | 3 +-
.../challegram/unsorted/NLoader.java | 10 +-
.../challegram/widget/SimpleVideoPlayer.java | 9 +-
buildSrc/src/main/kotlin/Config.kt | 6 +-
lint.xml | 6 +
scripts/force-clean.sh | 2 +-
...h-exoplayer.sh => patch-androidx-media.sh} | 2 +-
scripts/private/build-ffmpeg-impl.sh | 28 +-
...r-impl.sh => patch-androidx-media-impl.sh} | 42 ++-
scripts/reset.sh | 4 +-
scripts/setup.sh | 4 +-
scripts/update-dependencies.sh | 6 +-
thirdparty/ExoPlayer | 1 -
thirdparty/androidx-media | 1 +
42 files changed, 450 insertions(+), 419 deletions(-)
rename app/jni/third_party/{exoplayer => androidx-media}/ffmpeg_jni.cc (79%)
rename app/jni/third_party/{exoplayer => androidx-media}/flac_jni.cc (93%)
rename app/jni/third_party/{exoplayer => androidx-media}/flac_parser.cc (100%)
rename app/jni/third_party/{exoplayer => androidx-media}/include/data_source.h (100%)
rename app/jni/third_party/{exoplayer => androidx-media}/include/flac_parser.h (100%)
rename app/jni/third_party/{exoplayer => androidx-media}/opus_jni.cc (85%)
rename app/jni/third_party/{exoplayer => androidx-media}/vpx_jni.cc (96%)
create mode 100644 lint.xml
rename scripts/{patch-exoplayer.sh => patch-androidx-media.sh} (52%)
rename scripts/private/{patch-exoplayer-impl.sh => patch-androidx-media-impl.sh} (50%)
delete mode 160000 thirdparty/ExoPlayer
create mode 160000 thirdparty/androidx-media
diff --git a/.gitmodules b/.gitmodules
index 07b3b7d30d..798a468018 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -16,16 +16,16 @@
path = app/jni/third_party/ffmpeg
url = https://github.com/FFmpeg/FFmpeg
shallow = true
- branch = release/4.4
+ branch = release/6.0
[submodule "lz4"]
path = app/jni/third_party/lz4
url = https://github.com/lz4/lz4
shallow = true
-[submodule "ExoPlayer"]
- path = thirdparty/ExoPlayer
- url = https://github.com/google/ExoPlayer
+[submodule "androidx-media"]
+ path = thirdparty/androidx-media
+ url = https://github.com/androidx/media
shallow = true
- branch = release-v2
+ branch = release
[submodule "flac"]
path = app/jni/third_party/flac
url = https://github.com/xiph/flac
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 346d131e2d..a4c7ab0d54 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -77,8 +77,8 @@ android {
"./jni/third_party/webrtc/sdk/android/src/java",
"../thirdparty/WebRTC/src/java"
)
- Config.EXOPLAYER_EXTENSIONS.forEach { module ->
- java.srcDirs("../thirdparty/ExoPlayer/extensions/${module}/src/main/java")
+ Config.ANDROIDX_MEDIA_EXTENSIONS.forEach { extension ->
+ java.srcDirs("../thirdparty/androidx-media/libraries/${extension}/src/main/java")
}
}
@@ -89,10 +89,12 @@ android {
buildTypes {
getByName("release") {
- Config.EXOPLAYER_EXTENSIONS.forEach { module ->
- val proguardFile = file("../thirdparty/ExoPlayer/extensions/${module}/proguard-rules.txt")
+ Config.ANDROIDX_MEDIA_EXTENSIONS.forEach { extension ->
+ val proguardFile = file(
+ "../thirdparty/androidx-media/libraries/${extension}/proguard-rules.txt"
+ )
if (proguardFile.exists()) {
- project.logger.lifecycle("Applying thirdparty/ExoPlayer/extensions/${module}/proguard-rules.pro")
+ project.logger.lifecycle("Applying ${proguardFile.path}")
proguardFile(proguardFile)
}
}
@@ -224,8 +226,8 @@ dependencies {
implementation("com.google.firebase:firebase-appcheck-safetynet:16.1.2")
// Play In-App Updates: https://developer.android.com/reference/com/google/android/play/core/release-notes-in_app_updates
implementation("com.google.android.play:app-update:2.1.0")
- // ExoPlayer: https://github.com/google/ExoPlayer/blob/release-v2/RELEASENOTES.md
- implementation("com.google.android.exoplayer:exoplayer-core:2.19.1")
+ // AndroidX/media: https://github.com/androidx/media/blob/release/RELEASENOTES.md
+ implementation("androidx.media3:media3-exoplayer:1.2.1")
// 17.x version requires minSdk 19 or higher
implementation("com.google.mlkit:language-id:16.1.1")
// The Checker Framework: https://checkerframework.org/CHANGELOG.md
diff --git a/app/jni/BuildFlac.cmake b/app/jni/BuildFlac.cmake
index a1686561d1..439a783a40 100644
--- a/app/jni/BuildFlac.cmake
+++ b/app/jni/BuildFlac.cmake
@@ -1,9 +1,9 @@
# flac
set(FLAC_DIR "${THIRDPARTY_DIR}/flac")
-set(EXO_FLAC_DIR "${CMAKE_HOME_DIRECTORY}/../../thirdparty/ExoPlayer/extensions/flac/src/main/jni")
+set(ANDROIDX_MEDIA_FLAC_DIR "${CMAKE_HOME_DIRECTORY}/../../thirdparty/androidx-media/libraries/decoder_flac/src/main/jni")
-ReadVariables("${EXO_FLAC_DIR}/flac_sources.mk")
+ReadVariables("${ANDROIDX_MEDIA_FLAC_DIR}/flac_sources.mk")
list(FILTER FLAC_SOURCES INCLUDE REGEX "^flac/.+$")
Transform(FLAC_SOURCES "^flac/" "${FLAC_DIR}/")
diff --git a/app/jni/CMakeLists.txt b/app/jni/CMakeLists.txt
index f1c6a5ce8c..aa39be0f79 100644
--- a/app/jni/CMakeLists.txt
+++ b/app/jni/CMakeLists.txt
@@ -210,17 +210,17 @@ add_library(${NATIVE_LIB} SHARED
gif.cpp
views.c
- "${THIRDPARTY_DIR}/exoplayer/opus_jni.cc"
- "${THIRDPARTY_DIR}/exoplayer/flac_jni.cc"
- "${THIRDPARTY_DIR}/exoplayer/flac_parser.cc"
- "${THIRDPARTY_DIR}/exoplayer/ffmpeg_jni.cc"
- "${THIRDPARTY_DIR}/exoplayer/vpx_jni.cc"
+ "${THIRDPARTY_DIR}/androidx-media/opus_jni.cc"
+ "${THIRDPARTY_DIR}/androidx-media/flac_jni.cc"
+ "${THIRDPARTY_DIR}/androidx-media/flac_parser.cc"
+ "${THIRDPARTY_DIR}/androidx-media/ffmpeg_jni.cc"
+ "${THIRDPARTY_DIR}/androidx-media/vpx_jni.cc"
bridge.cpp
)
target_include_directories(${NATIVE_LIB} PRIVATE
"${THIRDPARTY_DIR}/telegram_intro"
- "${THIRDPARTY_DIR}/exoplayer"
+ "${THIRDPARTY_DIR}/androidx-media"
"${THIRDPARTY_DIR}"
.
@@ -247,7 +247,7 @@ target_compile_options(${NATIVE_LIB} PUBLIC
-fno-rtti
)
# TODO: remove -Wno-macro-redefined -Wno-unused-variable
-# only "${THIRDPARTY_DIR}/exoplayer/ffmpeg_jni.cc" needs them.
+# only "${THIRDPARTY_DIR}/androidx-media/ffmpeg_jni.cc" needs them.
# == Linking dependencies ==
diff --git a/app/jni/cmake/ReadVariables.cmake b/app/jni/cmake/ReadVariables.cmake
index 418b16bb38..5232b58dcb 100644
--- a/app/jni/cmake/ReadVariables.cmake
+++ b/app/jni/cmake/ReadVariables.cmake
@@ -8,6 +8,9 @@ if(POLICY CMP0007)
endif()
function(ReadVariables MKFile)
+ if(NOT EXISTS "${MKFile}")
+ message(FATAL_ERROR "File does not exist: ${MKFile}")
+ endif()
file(READ "${MKFile}" FileContents)
string(REGEX REPLACE "\\\\\n *" " " FileContents ${FileContents})
string(REGEX REPLACE "#[^\n]*" "" FileContents ${FileContents})
diff --git a/app/jni/gif.cpp b/app/jni/gif.cpp
index b466d7d915..9b969c1231 100644
--- a/app/jni/gif.cpp
+++ b/app/jni/gif.cpp
@@ -23,7 +23,9 @@
extern "C" {
#include
#include
+#include
#include
+#include
}
@@ -88,9 +90,7 @@ struct VideoInfo {
const std::string path;
- VideoInfo (std::string path) : path(std::move(path)), pkt(), orig_pkt() {
-
- }
+ VideoInfo (const std::string &path) : path(path) { }
~VideoInfo () {
if (video_dec_ctx) {
@@ -109,8 +109,10 @@ struct VideoInfo {
sws_freeContext(scale_ctx);
scale_ctx = nullptr;
}
- av_free_packet(&orig_pkt);
-
+ if (packet != nullptr) {
+ av_packet_free(&packet);
+ packet = nullptr;
+ }
video_stream_idx = -1;
video_stream = nullptr;
}
@@ -124,21 +126,17 @@ struct VideoInfo {
AVFrame *frame = nullptr;
int srcWidth = -1, srcHeight = -1;
int dstWidth = -1, dstHeight = -1;
+ int frameRate = -1;
bool has_decoded_frames = false;
bool is_broken = false;
- AVPacket pkt;
- AVPacket orig_pkt;
-};
+ bool had_invalid_frames = false;
-JNI_FUNC(void, gifInit) {
- av_register_all();
- avcodec_register_all();
-}
+ AVPacket *packet = nullptr;
+};
int open_codec_context (int *stream_idx, AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type) {
int ret;
AVStream *st;
- AVCodec *dec = nullptr;
AVDictionary *opts = nullptr;
ret = av_find_best_stream(fmt_ctx, type, -1, -1, nullptr, 0);
@@ -149,7 +147,7 @@ int open_codec_context (int *stream_idx, AVCodecContext **dec_ctx, AVFormatConte
*stream_idx = ret;
st = fmt_ctx->streams[*stream_idx];
- dec = avcodec_find_decoder(st->codecpar->codec_id);
+ const AVCodec *dec = avcodec_find_decoder(st->codecpar->codec_id);
if (!dec) {
loge(TAG_GIF_LOADER, "failed to find %s decoder for %s", av_get_media_type_string(type), avcodec_get_name(st->codecpar->codec_id));
return -1;
@@ -166,6 +164,16 @@ int open_codec_context (int *stream_idx, AVCodecContext **dec_ctx, AVFormatConte
return ret;
}
+ auto context = *dec_ctx;
+ context->thread_count = 0;
+ if (dec->capabilities & AV_CODEC_CAP_FRAME_THREADS) {
+ context->thread_type = FF_THREAD_FRAME;
+ } else if (dec->capabilities & AV_CODEC_CAP_SLICE_THREADS) {
+ context->thread_type = FF_THREAD_SLICE;
+ } else {
+ context->thread_count = 1;
+ }
+
av_dict_set(&opts, "refcounted_frames", "1", 0);
if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0) {
loge(TAG_GIF_LOADER, "failed to open %s decoder for %s", av_get_media_type_string(type), avcodec_get_name(st->codecpar->codec_id));
@@ -176,36 +184,6 @@ int open_codec_context (int *stream_idx, AVCodecContext **dec_ctx, AVFormatConte
return 0;
}
-int decode_packet (VideoInfo *info, int *got_frame) {
- int ret = 0;
- int decoded = info->pkt.size;
-
- *got_frame = 0;
- if (info->pkt.stream_index == info->video_stream_idx) {
- ret = avcodec_decode_video2(info->video_dec_ctx, info->frame, got_frame, &info->pkt);
- if (ret != 0) {
- return ret;
- }
- }
-
- return decoded;
-}
-
-AVFrame *alloc_picture (AVPixelFormat pix_fmt, int width, int height) {
- AVFrame *f = av_frame_alloc();
- if (!f) {
- return nullptr;
- }
- int size = avpicture_get_size(pix_fmt, width, height);
- uint8_t *buffer = (uint8_t *) av_malloc(size);
- if (!buffer) {
- av_free(f);
- return nullptr;
- }
- avpicture_fill((AVPicture *) f, buffer, pix_fmt, width, height);
- return f;
-}
-
JNI_FUNC(jlong, createDecoder, jstring src, jintArray data, jdouble startMediaTimestamp) {
VideoInfo *info = new VideoInfo(jni::from_jstring(env, src));
@@ -241,9 +219,11 @@ JNI_FUNC(jlong, createDecoder, jstring src, jintArray data, jdouble startMediaTi
return 0;
}
- av_init_packet(&info->pkt);
- info->pkt.data = nullptr;
- info->pkt.size = 0;
+ info->packet = av_packet_alloc();
+ if (info->packet == nullptr) {
+ loge(TAG_GIF_LOADER, "can't allocate packet %s", info->path.c_str());
+ return 0;
+ }
const int srcWidth = info->srcWidth = info->video_dec_ctx->width;
const int srcHeight = info->srcHeight = info->video_dec_ctx->height;
@@ -282,8 +262,11 @@ JNI_FUNC(jlong, createDecoder, jstring src, jintArray data, jdouble startMediaTi
}
}
+ int frameRate = (int) (1000 * av_q2d(info->video_stream->avg_frame_rate));
+
info->dstWidth = dstWidth;
info->dstHeight = dstHeight;
+ info->frameRate = frameRate;
if (startMediaTimestamp != 0) {
int ret = 0;
@@ -299,7 +282,7 @@ JNI_FUNC(jlong, createDecoder, jstring src, jintArray data, jdouble startMediaTi
if (dataArr != nullptr) {
dataArr[0] = dstWidth;
dataArr[1] = dstHeight;
- dataArr[2] = (int) (1000 * av_q2d(info->video_stream->avg_frame_rate));
+ dataArr[2] = frameRate;
/*AVDictionaryEntry *rotate_tag = av_dict_get(info->video_stream->metadata, "rotate", nullptr, 0);
if (rotate_tag && *rotate_tag->value && strcmp(rotate_tag->value, "0") != 0) {
char *tail;
@@ -366,6 +349,75 @@ JNI_FUNC(jboolean, isVideoBroken, jlong ptr) {
}
}
+void to_android_bitmap (JNIEnv *env, VideoInfo *info, jobject bitmap, jintArray data) {
+ auto fmt = (AVPixelFormat) info->frame->format;
+
+ AndroidBitmapInfo bitmapInfo;
+ AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);
+
+ if (bitmapInfo.width == info->dstWidth && bitmapInfo.height == info->dstHeight) {
+ jint *dataArr = env->GetIntArrayElements(data, 0);
+ if (dataArr != nullptr) {
+ dataArr[3] = (int) (1000 * info->frame->pts * av_q2d(info->video_stream->time_base));
+ env->ReleaseIntArrayElements(data, dataArr, 0);
+ }
+
+ AVFrame *frame = info->frame;
+ int frameWidth = frame->width;
+ int frameHeight = frame->height;
+
+ void *pixels;
+ if (AndroidBitmap_lockPixels(env, bitmap, &pixels) == ANDROID_BITMAP_RESULT_SUCCESS) {
+ if (info->scale_ctx != nullptr) {
+ uint8_t *dst_data[1];
+ dst_data[0] = (uint8_t *) pixels;
+ info->dst_linesize[0] = bitmapInfo.stride;
+ // TODO: find out why sws_scale doesn't support transparency (AV_PIX_FMT_YUVA420P) properly
+ // note: for now, updated libyuv + kYvuI601Constants in I420AlphaToARGBMatrix fixes AV_PIX_FMT_YUVA420P issue - but still needs to be researched
+ /*int res =*/ sws_scale(info->scale_ctx, frame->data, frame->linesize, 0, frame->height, dst_data, info->dst_linesize);
+ } else {
+ // TODO: find out why libyuv damages the color palette
+ switch (fmt) {
+ case BITMAP_TARGET_FORMAT: {
+ int size = av_image_get_buffer_size(fmt, frameWidth, frameHeight, 1);
+ memcpy((uint8_t *) pixels, frame->data[0], size);
+ break;
+ }
+ case AV_PIX_FMT_YUV420P:
+ case AV_PIX_FMT_YUVJ420P:
+ if (frame->colorspace == AVColorSpace::AVCOL_SPC_BT709) {
+ libyuv::H420ToARGB(frame->data[0], frame->linesize[0], frame->data[2],
+ frame->linesize[2], frame->data[1], frame->linesize[1],
+ (uint8_t *) pixels, frameWidth * 4, frameWidth, frameHeight);
+ } else {
+ libyuv::I420ToARGB(frame->data[0], frame->linesize[0], frame->data[2],
+ frame->linesize[2], frame->data[1], frame->linesize[1],
+ (uint8_t *) pixels, frameWidth * 4, frameWidth, frameHeight);
+ }
+ break;
+ case AV_PIX_FMT_YUVA420P:
+ libyuv::I420AlphaToARGBMatrix(frame->data[0], frame->linesize[0], frame->data[2],
+ frame->linesize[2], frame->data[1], frame->linesize[1],
+ frame->data[3], frame->linesize[3],
+ (uint8_t *) pixels, frameWidth * 4,
+ &libyuv::kYvuI601Constants, frameWidth, frameHeight,
+ 50);
+ break;
+ case AV_PIX_FMT_BGRA:
+ libyuv::ABGRToARGB(frame->data[0], frame->linesize[0], (uint8_t *) pixels,
+ frameWidth * 4, frameWidth, frameHeight);
+ break;
+ default:
+ // TODO more libyuv cases?
+ logw(TAG_GIF_LOADER, "unsupported pixel format: %d", fmt);
+ break;
+ }
+ }
+ AndroidBitmap_unlockPixels(env, bitmap);
+ }
+ }
+}
+
JNI_FUNC(jint, getVideoFrame, jlong ptr, jobject bitmap, jintArray data) {
if (ptr == 0 || bitmap == nullptr) {
return 0;
@@ -375,135 +427,105 @@ JNI_FUNC(jint, getVideoFrame, jlong ptr, jobject bitmap, jintArray data) {
return 0;
}
- int ret = 0;
- int got_frame = 0;
- bool looped = false;
-
- while (true) {
- if (info->pkt.size == 0) {
- ret = av_read_frame(info->fmt_ctx, &info->pkt);
- if (ret >= 0) {
- info->orig_pkt = info->pkt;
- } else if (!info->has_decoded_frames) {
- info->is_broken = true;
- loge(TAG_GIF_LOADER, "gif file is broken, abort: %s", av_err2str(ret));
- return 0;
+ bool gotFrame = false;
+ bool hasLooped = false;
+ bool eofReached = false;
+ bool fatalError = false;
+ size_t errorCount = 0;
+ size_t maxErrorCount = round(std::min(120.0, (double) info->frameRate / 1000.0) * 2.5);
+
+ do {
+ int ret;
+
+ if (eofReached) {
+ ret = avformat_seek_file(
+ info->fmt_ctx, -1,
+ std::numeric_limits::min(), 0,
+ std::numeric_limits::max(), 0
+ );
+ if (ret != 0) {
+ loge(TAG_GIF_LOADER, "can't seek %s to start, %s",info->path.c_str(), av_err2str(ret));
+ fatalError = true;
+ break;
}
+ info->has_decoded_frames = false;
+ eofReached = false;
+ hasLooped = true;
+ avcodec_flush_buffers(info->video_dec_ctx);
}
-
- if (info->pkt.size > 0) {
- ret = decode_packet(info, &got_frame);
- if (ret < 0) {
- if (info->has_decoded_frames) {
- ret = 0;
- }
- info->pkt.size = 0;
- } else {
- // logd(TAG_GIF_LOADER, "read size %d from packet", ret);
- info->pkt.data += ret;
- info->pkt.size -= ret;
+ ret = av_read_frame(info->fmt_ctx, info->packet);
+ if (ret != 0) {
+ if (!info->has_decoded_frames) {
+ loge(TAG_GIF_LOADER, "av_read_frame fatal error for %s: %s", info->path.c_str(), av_err2str(ret));
+ fatalError = true;
+ break;
}
-
- if (info->pkt.size == 0) {
- av_free_packet(&info->orig_pkt);
+ if (ret == AVERROR_EOF) {
+ eofReached = true;
+ continue;
}
- } else {
- info->pkt.data = nullptr;
- info->pkt.size = 0;
- ret = decode_packet(info, &got_frame);
- if (ret < 0) {
- loge(TAG_GIF_LOADER, "can't decode packet flushed %s", info->path.c_str());
- return 0;
- }
- if (got_frame == 0) {
- if (info->has_decoded_frames) {
- // logd(TAG_GIF_LOADER, "file end reached %s", info->src);
- if ((ret = avformat_seek_file(info->fmt_ctx, -1, std::numeric_limits::min(), 0,
- std::numeric_limits::max(), 0)) < 0) {
- loge(TAG_GIF_LOADER, "can't seek to begin of file %s, %s", info->path.c_str(),
- av_err2str(ret));
- return 0;
- } else {
- avcodec_flush_buffers(info->video_dec_ctx);
- }
- looped = true;
+ loge(TAG_GIF_LOADER, "av_read_frame fatal error for %s: %s", info->path.c_str(), av_err2str(ret));
+ fatalError = true;
+ break;
+ }
+ ret = avcodec_send_packet(info->video_dec_ctx, info->packet);
+ if (ret != 0) {
+ if (ret == AVERROR_INVALIDDATA && info->has_decoded_frames && errorCount < maxErrorCount) {
+ errorCount++;
+ if (errorCount == 1 && !info->had_invalid_frames) {
+ logv(TAG_GIF_LOADER, "avcodec_send_packet error #%zu for %s: %s maxErrorCount: %zu size: %d pts: %lld flags: %d",
+ errorCount,
+ info->path.c_str(),
+ av_err2str(ret),
+ maxErrorCount,
+ info->packet->size,
+ info->packet->pts,
+ info->packet->flags);
}
+ } else if (ret != AVERROR(EAGAIN)) {
+ loge(TAG_GIF_LOADER, "avcodec_send_packet fatal error for %s: %s", info->path.c_str(),
+ av_err2str(ret));
+ fatalError = true;
}
- }
- if (ret < 0) {
- return 0;
- }
- if (got_frame) {
- auto fmt = (AVPixelFormat) info->frame->format;
-
- AndroidBitmapInfo bitmapInfo;
- AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);
-
- if (bitmapInfo.width == info->dstWidth && bitmapInfo.height == info->dstHeight) {
- jint *dataArr = env->GetIntArrayElements(data, 0);
- if (dataArr != nullptr) {
- dataArr[3] = (int) (1000 * info->frame->pts * av_q2d(info->video_stream->time_base));
- env->ReleaseIntArrayElements(data, dataArr, 0);
+ } else {
+ ret = avcodec_receive_frame(info->video_dec_ctx, info->frame);
+ if (ret != 0) {
+ if (ret != AVERROR(EAGAIN)) {
+ loge(TAG_GIF_LOADER, "avcodec_receive_frame fatal error for %s: %s",
+ info->path.c_str(),
+ av_err2str(ret));
+ fatalError = true;
}
-
- AVFrame *frame = info->frame;
- int frameWidth = frame->width;
- int frameHeight = frame->height;
-
- void *pixels;
- if (AndroidBitmap_lockPixels(env, bitmap, &pixels) == ANDROID_BITMAP_RESULT_SUCCESS) {
- if (info->scale_ctx != nullptr) {
- uint8_t *dst_data[1];
- dst_data[0] = (uint8_t *) pixels;
- info->dst_linesize[0] = bitmapInfo.stride;
- // TODO: find out why sws_scale doesn't support transparency (AV_PIX_FMT_YUVA420P) properly
- // note: for now, updated libyuv + kYvuI601Constants in I420AlphaToARGBMatrix fixes AV_PIX_FMT_YUVA420P issue - but still needs to be researched
- /*int res =*/ sws_scale(info->scale_ctx, frame->data, frame->linesize, 0, frame->height, dst_data, info->dst_linesize);
- } else {
- // TODO: find out why libyuv damages the color palette
- switch (fmt) {
- case BITMAP_TARGET_FORMAT:
- memcpy((uint8_t *) pixels, frame->data[0], avpicture_get_size(fmt, frameWidth, frameHeight));
- break;
- case AV_PIX_FMT_YUV420P:
- case AV_PIX_FMT_YUVJ420P:
- if (frame->colorspace == AVColorSpace::AVCOL_SPC_BT709) {
- libyuv::H420ToARGB(frame->data[0], frame->linesize[0], frame->data[2],
- frame->linesize[2], frame->data[1], frame->linesize[1],
- (uint8_t *) pixels, frameWidth * 4, frameWidth, frameHeight);
- } else {
- libyuv::I420ToARGB(frame->data[0], frame->linesize[0], frame->data[2],
- frame->linesize[2], frame->data[1], frame->linesize[1],
- (uint8_t *) pixels, frameWidth * 4, frameWidth, frameHeight);
- }
- break;
- case AV_PIX_FMT_YUVA420P:
- libyuv::I420AlphaToARGBMatrix(frame->data[0], frame->linesize[0], frame->data[2],
- frame->linesize[2], frame->data[1], frame->linesize[1],
- frame->data[3], frame->linesize[3],
- (uint8_t *) pixels, frameWidth * 4,
- &libyuv::kYvuI601Constants, frameWidth, frameHeight,
- 50);
- break;
- case AV_PIX_FMT_BGRA:
- libyuv::ABGRToARGB(frame->data[0], frame->linesize[0], (uint8_t *) pixels,
- frameWidth * 4, frameWidth, frameHeight);
- break;
- default:
- // TODO more libyuv cases?
- logw(TAG_GIF_LOADER, "unsupported pixel format: %d", fmt);
- break;
- }
- }
- AndroidBitmap_unlockPixels(env, bitmap);
+ } else {
+ if (errorCount > 0 && !info->had_invalid_frames) {
+ info->had_invalid_frames = true;
+ logv(TAG_GIF_LOADER, "avcodec_send_packet got #%zu errors for file %s maxErrorCount: %zu size: %d pts: %lld flags: %d",
+ errorCount,
+ info->path.c_str(),
+ maxErrorCount,
+ info->packet->size,
+ info->packet->pts,
+ info->packet->flags);
}
+ // Done.
+ gotFrame = true;
}
-
- info->has_decoded_frames = true;
- av_frame_unref(info->frame);
- return looped ? 2 : 1;
}
+ av_packet_unref(info->packet);
+ } while (!info->is_broken && !fatalError && !gotFrame);
+
+ if (fatalError) {
+ info->is_broken = true;
}
+
+ if (gotFrame) {
+ to_android_bitmap(env, info, bitmap, data);
+ info->has_decoded_frames = true;
+ av_frame_unref(info->frame);
+ return hasLooped ? 2 : 1;
+ }
+
+ return 0;
}
JNI_FUNC(void, cancelLottieDecoder, jlong ptr) {
diff --git a/app/jni/third_party/exoplayer/ffmpeg_jni.cc b/app/jni/third_party/androidx-media/ffmpeg_jni.cc
similarity index 79%
rename from app/jni/third_party/exoplayer/ffmpeg_jni.cc
rename to app/jni/third_party/androidx-media/ffmpeg_jni.cc
index 597897dc27..46e9427657 100644
--- a/app/jni/third_party/exoplayer/ffmpeg_jni.cc
+++ b/app/jni/third_party/androidx-media/ffmpeg_jni.cc
@@ -35,24 +35,25 @@ extern "C" {
#define LOGE(...) \
((void)loge(TAG_NDK, __VA_ARGS__))
-#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
- extern "C" { \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegLibrary_##NAME( \
- JNIEnv *env, jobject thiz, ##__VA_ARGS__); \
- } \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegLibrary_##NAME( \
- JNIEnv *env, jobject thiz, ##__VA_ARGS__)
-
-#define AUDIO_DECODER_FUNC(RETURN_TYPE, NAME, ...) \
+#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
extern "C" { \
JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegAudioDecoder_##NAME( \
- JNIEnv *env, jobject thiz, ##__VA_ARGS__); \
+ Java_androidx_media3_decoder_ffmpeg_FfmpegLibrary_##NAME(JNIEnv *env, \
+ jobject thiz, \
+ ##__VA_ARGS__); \
} \
JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegAudioDecoder_##NAME( \
+ Java_androidx_media3_decoder_ffmpeg_FfmpegLibrary_##NAME( \
+ JNIEnv *env, jobject thiz, ##__VA_ARGS__)
+
+#define AUDIO_DECODER_FUNC(RETURN_TYPE, NAME, ...) \
+ extern "C" { \
+ JNIEXPORT RETURN_TYPE \
+ Java_androidx_media3_decoder_ffmpeg_FfmpegAudioDecoder_##NAME( \
+ JNIEnv *env, jobject thiz, ##__VA_ARGS__); \
+ } \
+ JNIEXPORT RETURN_TYPE \
+ Java_androidx_media3_decoder_ffmpeg_FfmpegAudioDecoder_##NAME( \
JNIEnv *env, jobject thiz, ##__VA_ARGS__)
#define ERROR_STRING_BUFFER_LENGTH 256
@@ -68,16 +69,16 @@ static const int AUDIO_DECODER_ERROR_OTHER = -2;
/**
* Returns the AVCodec with the specified name, or NULL if it is not available.
*/
-AVCodec *getCodecByName(JNIEnv *env, jstring codecName);
+const AVCodec *getCodecByName(JNIEnv *env, jstring codecName);
/**
* Allocates and opens a new AVCodecContext for the specified codec, passing the
* provided extraData as initialization data for the decoder if it is non-NULL.
* Returns the created context.
*/
-AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
- jboolean outputFloat, jint rawSampleRate,
- jint rawChannelCount);
+AVCodecContext *createContext(JNIEnv *env, const AVCodec *codec,
+ jbyteArray extraData, jboolean outputFloat,
+ jint rawSampleRate, jint rawChannelCount);
/**
* Decodes the packet into the output buffer, returning the number of bytes
@@ -107,7 +108,6 @@ void releaseContext(AVCodecContext *context);
if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
- avcodec_register_all();
return JNI_VERSION_1_6;
}*/
@@ -126,7 +126,7 @@ LIBRARY_FUNC(jboolean, ffmpegHasDecoder, jstring codecName) {
AUDIO_DECODER_FUNC(jlong, ffmpegInitialize, jstring codecName,
jbyteArray extraData, jboolean outputFloat,
jint rawSampleRate, jint rawChannelCount) {
- AVCodec *codec = getCodecByName(env, codecName);
+ const AVCodec *codec = getCodecByName(env, codecName);
if (!codec) {
LOGE("Codec not found.");
return 0L;
@@ -155,12 +155,17 @@ AUDIO_DECODER_FUNC(jint, ffmpegDecode, jlong context, jobject inputData,
}
uint8_t *inputBuffer = (uint8_t *)env->GetDirectBufferAddress(inputData);
uint8_t *outputBuffer = (uint8_t *)env->GetDirectBufferAddress(outputData);
- AVPacket packet;
- av_init_packet(&packet);
- packet.data = inputBuffer;
- packet.size = inputSize;
- return decodePacket((AVCodecContext *)context, &packet, outputBuffer,
- outputSize);
+ AVPacket *packet = av_packet_alloc();
+ if (!packet) {
+ LOGE("Failed to allocate packet.");
+ return -1;
+ }
+ packet->data = inputBuffer;
+ packet->size = inputSize;
+ const int ret =
+ decodePacket((AVCodecContext *)context, packet, outputBuffer, outputSize);
+ av_packet_free(&packet);
+ return ret;
}
AUDIO_DECODER_FUNC(jint, ffmpegGetChannelCount, jlong context) {
@@ -168,7 +173,7 @@ AUDIO_DECODER_FUNC(jint, ffmpegGetChannelCount, jlong context) {
LOGE("Context must be non-NULL.");
return -1;
}
- return ((AVCodecContext *)context)->channels;
+ return ((AVCodecContext *)context)->ch_layout.nb_channels;
}
AUDIO_DECODER_FUNC(jint, ffmpegGetSampleRate, jlong context) {
@@ -191,7 +196,7 @@ AUDIO_DECODER_FUNC(jlong, ffmpegReset, jlong jContext, jbyteArray extraData) {
// Release and recreate the context if the codec is TrueHD.
// TODO: Figure out why flushing doesn't work for this codec.
releaseContext(context);
- AVCodec *codec = avcodec_find_decoder(codecId);
+ const AVCodec *codec = avcodec_find_decoder(codecId);
if (!codec) {
LOGE("Unexpected error finding codec %d.", codecId);
return 0L;
@@ -213,19 +218,19 @@ AUDIO_DECODER_FUNC(void, ffmpegRelease, jlong context) {
}
}
-AVCodec *getCodecByName(JNIEnv *env, jstring codecName) {
+const AVCodec *getCodecByName(JNIEnv *env, jstring codecName) {
if (!codecName) {
return NULL;
}
const char *codecNameChars = env->GetStringUTFChars(codecName, NULL);
- AVCodec *codec = avcodec_find_decoder_by_name(codecNameChars);
+ const AVCodec *codec = avcodec_find_decoder_by_name(codecNameChars);
env->ReleaseStringUTFChars(codecName, codecNameChars);
return codec;
}
-AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
- jboolean outputFloat, jint rawSampleRate,
- jint rawChannelCount) {
+AVCodecContext *createContext(JNIEnv *env, const AVCodec *codec,
+ jbyteArray extraData, jboolean outputFloat,
+ jint rawSampleRate, jint rawChannelCount) {
AVCodecContext *context = avcodec_alloc_context3(codec);
if (!context) {
LOGE("Failed to allocate context.");
@@ -248,8 +253,7 @@ AVCodecContext *createContext(JNIEnv *env, AVCodec *codec, jbyteArray extraData,
if (context->codec_id == AV_CODEC_ID_PCM_MULAW ||
context->codec_id == AV_CODEC_ID_PCM_ALAW) {
context->sample_rate = rawSampleRate;
- context->channels = rawChannelCount;
- context->channel_layout = av_get_default_channel_layout(rawChannelCount);
+ av_channel_layout_default(&context->ch_layout, rawChannelCount);
}
context->err_recognition = AV_EF_IGNORE_ERR;
int result = avcodec_open2(context, codec, NULL);
@@ -291,25 +295,29 @@ int decodePacket(AVCodecContext *context, AVPacket *packet,
// Resample output.
AVSampleFormat sampleFormat = context->sample_fmt;
- int channelCount = context->channels;
- int channelLayout = context->channel_layout;
+ int channelCount = context->ch_layout.nb_channels;
int sampleRate = context->sample_rate;
int sampleCount = frame->nb_samples;
int dataSize = av_samples_get_buffer_size(NULL, channelCount, sampleCount,
sampleFormat, 1);
- SwrContext *resampleContext;
- if (context->opaque) {
- resampleContext = (SwrContext *)context->opaque;
- } else {
- resampleContext = swr_alloc();
- av_opt_set_int(resampleContext, "in_channel_layout", channelLayout, 0);
- av_opt_set_int(resampleContext, "out_channel_layout", channelLayout, 0);
- av_opt_set_int(resampleContext, "in_sample_rate", sampleRate, 0);
- av_opt_set_int(resampleContext, "out_sample_rate", sampleRate, 0);
- av_opt_set_int(resampleContext, "in_sample_fmt", sampleFormat, 0);
- // The output format is always the requested format.
- av_opt_set_int(resampleContext, "out_sample_fmt",
- context->request_sample_fmt, 0);
+ SwrContext *resampleContext = static_cast(context->opaque);
+ if (!resampleContext) {
+ result =
+ swr_alloc_set_opts2(&resampleContext, // ps
+ &context->ch_layout, // out_ch_layout
+ context->request_sample_fmt, // out_sample_fmt
+ sampleRate, // out_sample_rate
+ &context->ch_layout, // in_ch_layout
+ sampleFormat, // in_sample_fmt
+ sampleRate, // in_sample_rate
+ 0, // log_offset
+ NULL // log_ctx
+ );
+ if (result < 0) {
+ logError("swr_alloc_set_opts2", result);
+ av_frame_free(&frame);
+ return transformError(result);
+ }
result = swr_init(resampleContext);
if (result < 0) {
logError("swr_init", result);
diff --git a/app/jni/third_party/exoplayer/flac_jni.cc b/app/jni/third_party/androidx-media/flac_jni.cc
similarity index 93%
rename from app/jni/third_party/exoplayer/flac_jni.cc
rename to app/jni/third_party/androidx-media/flac_jni.cc
index 935bed38e6..418e80afb8 100644
--- a/app/jni/third_party/exoplayer/flac_jni.cc
+++ b/app/jni/third_party/androidx-media/flac_jni.cc
@@ -28,14 +28,15 @@
#define ALOGV(...) \
((void)logv(TAG_NDK, __VA_ARGS__))
-#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
- extern "C" { \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_flac_FlacDecoderJni_##NAME( \
- JNIEnv *env, jobject thiz, ##__VA_ARGS__); \
- } \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_flac_FlacDecoderJni_##NAME( \
+#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
+ extern "C" { \
+ JNIEXPORT RETURN_TYPE \
+ Java_androidx_media3_decoder_flac_FlacDecoderJni_##NAME(JNIEnv *env, \
+ jobject thiz, \
+ ##__VA_ARGS__); \
+ } \
+ JNIEXPORT RETURN_TYPE \
+ Java_androidx_media3_decoder_flac_FlacDecoderJni_##NAME( \
JNIEnv *env, jobject thiz, ##__VA_ARGS__)
class JavaDataSource : public DataSource {
@@ -121,8 +122,8 @@ DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) {
bool picturesValid = context->parser->arePicturesValid();
if (picturesValid) {
std::vector pictures = context->parser->getPictures();
- jclass pictureFrameClass = env->FindClass(
- "com/google/android/exoplayer2/metadata/flac/PictureFrame");
+ jclass pictureFrameClass =
+ env->FindClass("androidx/media3/extractor/metadata/flac/PictureFrame");
jmethodID pictureFrameConstructor =
env->GetMethodID(pictureFrameClass, "",
"(ILjava/lang/String;Ljava/lang/String;IIII[B)V");
@@ -148,7 +149,7 @@ DECODER_FUNC(jobject, flacDecodeMetadata, jlong jContext) {
context->parser->getStreamInfo();
jclass flacStreamMetadataClass = env->FindClass(
- "com/google/android/exoplayer2/extractor/"
+ "androidx/media3/extractor/"
"FlacStreamMetadata");
jmethodID flacStreamMetadataConstructor =
env->GetMethodID(flacStreamMetadataClass, "",
diff --git a/app/jni/third_party/exoplayer/flac_parser.cc b/app/jni/third_party/androidx-media/flac_parser.cc
similarity index 100%
rename from app/jni/third_party/exoplayer/flac_parser.cc
rename to app/jni/third_party/androidx-media/flac_parser.cc
diff --git a/app/jni/third_party/exoplayer/include/data_source.h b/app/jni/third_party/androidx-media/include/data_source.h
similarity index 100%
rename from app/jni/third_party/exoplayer/include/data_source.h
rename to app/jni/third_party/androidx-media/include/data_source.h
diff --git a/app/jni/third_party/exoplayer/include/flac_parser.h b/app/jni/third_party/androidx-media/include/flac_parser.h
similarity index 100%
rename from app/jni/third_party/exoplayer/include/flac_parser.h
rename to app/jni/third_party/androidx-media/include/flac_parser.h
diff --git a/app/jni/third_party/exoplayer/opus_jni.cc b/app/jni/third_party/androidx-media/opus_jni.cc
similarity index 85%
rename from app/jni/third_party/exoplayer/opus_jni.cc
rename to app/jni/third_party/androidx-media/opus_jni.cc
index cf82e52aa6..7b690e642e 100644
--- a/app/jni/third_party/exoplayer/opus_jni.cc
+++ b/app/jni/third_party/androidx-media/opus_jni.cc
@@ -34,25 +34,21 @@
} while (0)
#endif // __ANDROID__
-#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
- extern "C" { \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_opus_OpusDecoder_##NAME( \
- JNIEnv* env, jobject thiz, ##__VA_ARGS__); \
- } \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_opus_OpusDecoder_##NAME( \
- JNIEnv* env, jobject thiz, ##__VA_ARGS__)
-
-#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
- extern "C" { \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_opus_OpusLibrary_##NAME( \
- JNIEnv* env, jobject thiz, ##__VA_ARGS__); \
- } \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_opus_OpusLibrary_##NAME( \
- JNIEnv* env, jobject thiz, ##__VA_ARGS__)
+#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
+ extern "C" { \
+ JNIEXPORT RETURN_TYPE Java_androidx_media3_decoder_opus_OpusDecoder_##NAME( \
+ JNIEnv* env, jobject thiz, ##__VA_ARGS__); \
+ } \
+ JNIEXPORT RETURN_TYPE Java_androidx_media3_decoder_opus_OpusDecoder_##NAME( \
+ JNIEnv* env, jobject thiz, ##__VA_ARGS__)
+
+#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
+ extern "C" { \
+ JNIEXPORT RETURN_TYPE Java_androidx_media3_decoder_opus_OpusLibrary_##NAME( \
+ JNIEnv* env, jobject thiz, ##__VA_ARGS__); \
+ } \
+ JNIEXPORT RETURN_TYPE Java_androidx_media3_decoder_opus_OpusLibrary_##NAME( \
+ JNIEnv* env, jobject thiz, ##__VA_ARGS__)
// JNI references for SimpleOutputBuffer class.
static jmethodID outputBufferInit;
@@ -94,8 +90,8 @@ DECODER_FUNC(jlong, opusInit, jint sampleRate, jint channelCount,
}
// Populate JNI References.
- const jclass outputBufferClass = env->FindClass(
- "com/google/android/exoplayer2/decoder/SimpleDecoderOutputBuffer");
+ const jclass outputBufferClass =
+ env->FindClass("androidx/media3/decoder/SimpleDecoderOutputBuffer");
outputBufferInit =
env->GetMethodID(outputBufferClass, "init", "(JI)Ljava/nio/ByteBuffer;");
diff --git a/app/jni/third_party/exoplayer/vpx_jni.cc b/app/jni/third_party/androidx-media/vpx_jni.cc
similarity index 96%
rename from app/jni/third_party/exoplayer/vpx_jni.cc
rename to app/jni/third_party/androidx-media/vpx_jni.cc
index fb6e774020..6f2dee8d38 100644
--- a/app/jni/third_party/exoplayer/vpx_jni.cc
+++ b/app/jni/third_party/androidx-media/vpx_jni.cc
@@ -37,25 +37,21 @@
#define LOGE(...) \
((void)loge(TAG_NDK, __VA_ARGS__))
-#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
- extern "C" { \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_vp9_VpxDecoder_##NAME( \
- JNIEnv* env, jobject thiz, ##__VA_ARGS__); \
- } \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_vp9_VpxDecoder_##NAME( \
- JNIEnv* env, jobject thiz, ##__VA_ARGS__)
-
-#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
- extern "C" { \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_vp9_VpxLibrary_##NAME( \
- JNIEnv* env, jobject thiz, ##__VA_ARGS__); \
- } \
- JNIEXPORT RETURN_TYPE \
- Java_com_google_android_exoplayer2_ext_vp9_VpxLibrary_##NAME( \
- JNIEnv* env, jobject thiz, ##__VA_ARGS__)
+#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
+ extern "C" { \
+ JNIEXPORT RETURN_TYPE Java_androidx_media3_decoder_vp9_VpxDecoder_##NAME( \
+ JNIEnv* env, jobject thiz, ##__VA_ARGS__); \
+ } \
+ JNIEXPORT RETURN_TYPE Java_androidx_media3_decoder_vp9_VpxDecoder_##NAME( \
+ JNIEnv* env, jobject thiz, ##__VA_ARGS__)
+
+#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
+ extern "C" { \
+ JNIEXPORT RETURN_TYPE Java_androidx_media3_decoder_vp9_VpxLibrary_##NAME( \
+ JNIEnv* env, jobject thiz, ##__VA_ARGS__); \
+ } \
+ JNIEXPORT RETURN_TYPE Java_androidx_media3_decoder_vp9_VpxLibrary_##NAME( \
+ JNIEnv* env, jobject thiz, ##__VA_ARGS__)
// JNI references for VideoDecoderOutputBuffer class.
static jmethodID initForYuvFrame;
@@ -477,8 +473,8 @@ DECODER_FUNC(jlong, vpxInit, jboolean disableLoopFilter,
}
// Populate JNI References.
- const jclass outputBufferClass = env->FindClass(
- "com/google/android/exoplayer2/decoder/VideoDecoderOutputBuffer");
+ const jclass outputBufferClass =
+ env->FindClass("androidx/media3/decoder/VideoDecoderOutputBuffer");
initForYuvFrame =
env->GetMethodID(outputBufferClass, "initForYuvFrame", "(IIIII)Z");
initForPrivateFrame =
diff --git a/app/jni/third_party/ffmpeg b/app/jni/third_party/ffmpeg
index 25841e4f90..133069b434 160000
--- a/app/jni/third_party/ffmpeg
+++ b/app/jni/third_party/ffmpeg
@@ -1 +1 @@
-Subproject commit 25841e4f90ebf8357a1b4662ee00e32978fce11b
+Subproject commit 133069b4340961a1b3e8562a0dabfc61b35af84c
diff --git a/app/src/main/java/org/thunderdog/challegram/N.java b/app/src/main/java/org/thunderdog/challegram/N.java
index 1d99bb7792..0eb3bab21a 100644
--- a/app/src/main/java/org/thunderdog/challegram/N.java
+++ b/app/src/main/java/org/thunderdog/challegram/N.java
@@ -66,7 +66,6 @@ public static int pinBitmapIfNeeded (Bitmap bitmap) {
public static native void onSurfaceChanged (int a_width_px, int a_height_px, float a_scale_factor, int a1);
// gif.c
- public static native void gifInit ();
public static native long createDecoder (String path, int[] metadata, double startMediaTimestamp);
public static native long createLottieDecoder (String path, String jsonData, double[] metadata, int fitzpatrickType);
public static native void getLottieSize (long ptr, int[] size);
diff --git a/app/src/main/java/org/thunderdog/challegram/U.java b/app/src/main/java/org/thunderdog/challegram/U.java
index 698debc930..c120c2bb56 100644
--- a/app/src/main/java/org/thunderdog/challegram/U.java
+++ b/app/src/main/java/org/thunderdog/challegram/U.java
@@ -82,22 +82,22 @@
import androidx.annotation.Nullable;
import androidx.core.os.EnvironmentCompat;
import androidx.exifinterface.media.ExifInterface;
+import androidx.media3.common.PlaybackException;
+import androidx.media3.datasource.FileDataSource;
+import androidx.media3.exoplayer.DefaultLoadControl;
+import androidx.media3.exoplayer.DefaultRenderersFactory;
+import androidx.media3.exoplayer.ExoPlaybackException;
+import androidx.media3.exoplayer.ExoPlayer;
+import androidx.media3.exoplayer.RenderersFactory;
+import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
+import androidx.media3.exoplayer.source.MediaSource;
+import androidx.media3.exoplayer.source.MediaSourceFactory;
+import androidx.media3.exoplayer.source.ProgressiveMediaSource;
+import androidx.media3.exoplayer.source.UnrecognizedInputFormatException;
+import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
+import androidx.media3.extractor.DefaultExtractorsFactory;
import androidx.recyclerview.widget.RecyclerView;
-import com.google.android.exoplayer2.DefaultLoadControl;
-import com.google.android.exoplayer2.DefaultRenderersFactory;
-import com.google.android.exoplayer2.ExoPlaybackException;
-import com.google.android.exoplayer2.ExoPlayer;
-import com.google.android.exoplayer2.PlaybackException;
-import com.google.android.exoplayer2.RenderersFactory;
-import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
-import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
-import com.google.android.exoplayer2.source.MediaSource;
-import com.google.android.exoplayer2.source.MediaSourceFactory;
-import com.google.android.exoplayer2.source.ProgressiveMediaSource;
-import com.google.android.exoplayer2.source.UnrecognizedInputFormatException;
-import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
-import com.google.android.exoplayer2.upstream.FileDataSource;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
@@ -733,8 +733,8 @@ public static MediaSource newMediaSource (File file) {
return new ProgressiveMediaSource.Factory(new FileDataSource.Factory()).createMediaSource(newMediaItem(Uri.fromFile(file)));
}
- public static com.google.android.exoplayer2.MediaItem newMediaItem (Uri uri) {
- return new com.google.android.exoplayer2.MediaItem.Builder().setUri(uri).build();
+ public static androidx.media3.common.MediaItem newMediaItem (Uri uri) {
+ return new androidx.media3.common.MediaItem.Builder().setUri(uri).build();
}
public static MediaSource newMediaSource (int accountId, TdApi.Message message) {
diff --git a/app/src/main/java/org/thunderdog/challegram/loader/ImageActor.java b/app/src/main/java/org/thunderdog/challegram/loader/ImageActor.java
index a381cd9d2a..456872e00b 100644
--- a/app/src/main/java/org/thunderdog/challegram/loader/ImageActor.java
+++ b/app/src/main/java/org/thunderdog/challegram/loader/ImageActor.java
@@ -17,10 +17,9 @@
import android.graphics.Bitmap;
import android.os.CancellationSignal;
+import androidx.media3.extractor.metadata.id3.ApicFrame;
import androidx.palette.graphics.Palette;
-import com.google.android.exoplayer2.metadata.id3.ApicFrame;
-
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.Log;
import org.thunderdog.challegram.config.Config;
diff --git a/app/src/main/java/org/thunderdog/challegram/loader/ImageApicFile.java b/app/src/main/java/org/thunderdog/challegram/loader/ImageApicFile.java
index 40a7a2ecf7..7d131ba386 100644
--- a/app/src/main/java/org/thunderdog/challegram/loader/ImageApicFile.java
+++ b/app/src/main/java/org/thunderdog/challegram/loader/ImageApicFile.java
@@ -14,7 +14,7 @@
*/
package org.thunderdog.challegram.loader;
-import com.google.android.exoplayer2.metadata.id3.ApicFrame;
+import androidx.media3.extractor.metadata.id3.ApicFrame;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.data.TD;
diff --git a/app/src/main/java/org/thunderdog/challegram/loader/gif/GifActor.java b/app/src/main/java/org/thunderdog/challegram/loader/gif/GifActor.java
index 9d3fb77ae3..a7b9646152 100644
--- a/app/src/main/java/org/thunderdog/challegram/loader/gif/GifActor.java
+++ b/app/src/main/java/org/thunderdog/challegram/loader/gif/GifActor.java
@@ -597,8 +597,10 @@ public void prepareNextFrame () {
}
} else {
int ret = N.getVideoFrame(nativePtr, free.bitmap, metadata);
- free.no = metadata[3];
- success = true;
+ if (ret != 0) {
+ free.no = metadata[3];
+ success = true;
+ }
if (ret == 2) {
if (isPlayOnce) {
file.setLooped(true);
diff --git a/app/src/main/java/org/thunderdog/challegram/loader/gif/GifBridge.java b/app/src/main/java/org/thunderdog/challegram/loader/gif/GifBridge.java
index 7dc4e55d0e..658f199cc4 100644
--- a/app/src/main/java/org/thunderdog/challegram/loader/gif/GifBridge.java
+++ b/app/src/main/java/org/thunderdog/challegram/loader/gif/GifBridge.java
@@ -55,7 +55,6 @@ public static GifBridge instance () {
private final GifThread[] lottieThreads;
private GifBridge () {
- N.gifInit();
thread = new GifBridgeThread();
// TODO: rework to executors
threads = new GifThread[THREAD_POOL_SIZE];
diff --git a/app/src/main/java/org/thunderdog/challegram/mediaview/VideoPlayerView.java b/app/src/main/java/org/thunderdog/challegram/mediaview/VideoPlayerView.java
index fc3d55bfb3..d8b356304b 100644
--- a/app/src/main/java/org/thunderdog/challegram/mediaview/VideoPlayerView.java
+++ b/app/src/main/java/org/thunderdog/challegram/mediaview/VideoPlayerView.java
@@ -27,16 +27,15 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
-import com.google.android.exoplayer2.PlaybackException;
-import com.google.android.exoplayer2.Player;
-import com.google.android.exoplayer2.analytics.AnalyticsListener;
-import com.google.android.exoplayer2.source.ClippingMediaSource;
-import com.google.android.exoplayer2.source.LoopingMediaSource;
-import com.google.android.exoplayer2.source.MediaSource;
-import com.google.android.exoplayer2.video.VideoSize;
+import androidx.media3.common.C;
+import androidx.media3.common.PlaybackException;
+import androidx.media3.common.Player;
+import androidx.media3.common.VideoSize;
+import androidx.media3.exoplayer.ExoPlayer;
+import androidx.media3.exoplayer.analytics.AnalyticsListener;
+import androidx.media3.exoplayer.source.ClippingMediaSource;
+import androidx.media3.exoplayer.source.LoopingMediaSource;
+import androidx.media3.exoplayer.source.MediaSource;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.BaseActivity;
diff --git a/app/src/main/java/org/thunderdog/challegram/player/AudioController.java b/app/src/main/java/org/thunderdog/challegram/player/AudioController.java
index 85fe8ebda4..a91953017c 100644
--- a/app/src/main/java/org/thunderdog/challegram/player/AudioController.java
+++ b/app/src/main/java/org/thunderdog/challegram/player/AudioController.java
@@ -26,19 +26,18 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
-import com.google.android.exoplayer2.IllegalSeekPositionException;
-import com.google.android.exoplayer2.PlaybackException;
-import com.google.android.exoplayer2.PlaybackParameters;
-import com.google.android.exoplayer2.Player;
-import com.google.android.exoplayer2.Timeline;
-import com.google.android.exoplayer2.Tracks;
-import com.google.android.exoplayer2.metadata.Metadata;
-import com.google.android.exoplayer2.metadata.id3.ApicFrame;
-import com.google.android.exoplayer2.source.MediaSource;
-import com.google.android.exoplayer2.source.TrackGroup;
+import androidx.media3.common.C;
+import androidx.media3.common.IllegalSeekPositionException;
+import androidx.media3.common.Metadata;
+import androidx.media3.common.PlaybackException;
+import androidx.media3.common.PlaybackParameters;
+import androidx.media3.common.Player;
+import androidx.media3.common.Timeline;
+import androidx.media3.common.TrackGroup;
+import androidx.media3.common.Tracks;
+import androidx.media3.exoplayer.ExoPlayer;
+import androidx.media3.exoplayer.source.MediaSource;
+import androidx.media3.extractor.metadata.id3.ApicFrame;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.Log;
diff --git a/app/src/main/java/org/thunderdog/challegram/player/BasePlaybackController.java b/app/src/main/java/org/thunderdog/challegram/player/BasePlaybackController.java
index eb0b0a7f7e..b3461f5062 100644
--- a/app/src/main/java/org/thunderdog/challegram/player/BasePlaybackController.java
+++ b/app/src/main/java/org/thunderdog/challegram/player/BasePlaybackController.java
@@ -16,9 +16,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
-import com.google.android.exoplayer2.PlaybackException;
-import com.google.android.exoplayer2.Player;
+import androidx.media3.common.PlaybackException;
+import androidx.media3.common.Player;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.Log;
diff --git a/app/src/main/java/org/thunderdog/challegram/player/ProximityManager.java b/app/src/main/java/org/thunderdog/challegram/player/ProximityManager.java
index c64e99352f..243e609c00 100644
--- a/app/src/main/java/org/thunderdog/challegram/player/ProximityManager.java
+++ b/app/src/main/java/org/thunderdog/challegram/player/ProximityManager.java
@@ -30,10 +30,9 @@
import android.os.PowerManager;
import androidx.annotation.Nullable;
-
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
-import com.google.android.exoplayer2.audio.AudioAttributes;
+import androidx.media3.common.AudioAttributes;
+import androidx.media3.common.C;
+import androidx.media3.exoplayer.ExoPlayer;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.BaseActivity;
diff --git a/app/src/main/java/org/thunderdog/challegram/player/RoundVideoController.java b/app/src/main/java/org/thunderdog/challegram/player/RoundVideoController.java
index a4452e28bf..a87c0314cc 100644
--- a/app/src/main/java/org/thunderdog/challegram/player/RoundVideoController.java
+++ b/app/src/main/java/org/thunderdog/challegram/player/RoundVideoController.java
@@ -34,12 +34,11 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ExoPlayer;
-import com.google.android.exoplayer2.PlaybackException;
-import com.google.android.exoplayer2.Player;
-import com.google.android.exoplayer2.source.MediaSource;
+import androidx.media3.common.C;
+import androidx.media3.common.PlaybackException;
+import androidx.media3.common.Player;
+import androidx.media3.exoplayer.ExoPlayer;
+import androidx.media3.exoplayer.source.MediaSource;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.BaseActivity;
@@ -1739,7 +1738,7 @@ public boolean onTouchEvent (MotionEvent event) {
private void setExoPlayerParameters () {
if (exoPlayer != null) {
- TdlibManager.instance().player().proximityManager().modifyExoPlayer(exoPlayer, C.CONTENT_TYPE_MOVIE);
+ TdlibManager.instance().player().proximityManager().modifyExoPlayer(exoPlayer, C.AUDIO_CONTENT_TYPE_MOVIE);
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/player/TGPlayerController.java b/app/src/main/java/org/thunderdog/challegram/player/TGPlayerController.java
index 15b67407ad..21566b2f80 100644
--- a/app/src/main/java/org/thunderdog/challegram/player/TGPlayerController.java
+++ b/app/src/main/java/org/thunderdog/challegram/player/TGPlayerController.java
@@ -20,8 +20,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
-import com.google.android.exoplayer2.PlaybackParameters;
+import androidx.media3.common.PlaybackParameters;
import org.drinkless.tdlib.Client;
import org.drinkless.tdlib.TdApi;
diff --git a/app/src/main/java/org/thunderdog/challegram/service/AudioService.java b/app/src/main/java/org/thunderdog/challegram/service/AudioService.java
index 5c88ef91bf..52f13f4d2c 100644
--- a/app/src/main/java/org/thunderdog/challegram/service/AudioService.java
+++ b/app/src/main/java/org/thunderdog/challegram/service/AudioService.java
@@ -36,9 +36,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.metadata.id3.ApicFrame;
+import androidx.media3.common.C;
+import androidx.media3.extractor.metadata.id3.ApicFrame;
import org.drinkless.tdlib.TdApi;
import org.drinkmore.Tracer;
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibDataSource.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibDataSource.java
index 32fed708a0..ccb9e77057 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibDataSource.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibDataSource.java
@@ -14,12 +14,12 @@
import android.net.Uri;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.upstream.BaseDataSource;
-import com.google.android.exoplayer2.upstream.DataSource;
-import com.google.android.exoplayer2.upstream.DataSpec;
+import androidx.media3.common.C;
+import androidx.media3.datasource.BaseDataSource;
+import androidx.media3.datasource.DataSource;
+import androidx.media3.datasource.DataSpec;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.U;
@@ -85,6 +85,7 @@ public TdlibDataSourceException (String message, Throwable cause) {
public static final class Factory implements DataSource.Factory {
@Override
+ @NonNull
public DataSource createDataSource () {
return new TdlibDataSource();
}
@@ -216,7 +217,7 @@ private void releaseReference (TdApi.File file) {
}
@Override
- public int read (byte[] buffer, int bufferOffset, int readLength) throws TdlibDataSourceException {
+ public int read (@NonNull byte[] buffer, int bufferOffset, int readLength) throws TdlibDataSourceException {
if (readLength == 0) {
return 0;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java b/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java
index 1a025678f1..5ec9ab5eb6 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/MessagesController.java
@@ -4177,6 +4177,8 @@ public void destroy () {
headerCell.pause();
}
+ Views.destroyRecyclerView(messagesView);
+
// TODO chat = null;
if (destroyInstance || !reuseEnabled) {
@@ -4205,7 +4207,6 @@ public void destroy () {
botHelper.destroy();
}
removeStaticListeners();
- Views.destroyRecyclerView(messagesView);
Views.destroyRecyclerView(wallpapersList);
if (wallpapersList != null) {
((WallpaperAdapter) wallpapersList.getAdapter()).destroy();
diff --git a/app/src/main/java/org/thunderdog/challegram/unsorted/NLoader.java b/app/src/main/java/org/thunderdog/challegram/unsorted/NLoader.java
index b34ed9c662..1bb35656a8 100644
--- a/app/src/main/java/org/thunderdog/challegram/unsorted/NLoader.java
+++ b/app/src/main/java/org/thunderdog/challegram/unsorted/NLoader.java
@@ -19,14 +19,14 @@
import android.text.TextUtils;
import androidx.annotation.Nullable;
+import androidx.media3.common.C;
+import androidx.media3.decoder.ffmpeg.FfmpegLibrary;
+import androidx.media3.decoder.flac.FlacLibrary;
+import androidx.media3.decoder.opus.OpusLibrary;
+import androidx.media3.decoder.vp9.VpxLibrary;
import com.getkeepsafe.relinker.ReLinker;
import com.getkeepsafe.relinker.ReLinkerInstance;
-import com.google.android.exoplayer2.C;
-import com.google.android.exoplayer2.ext.ffmpeg.FfmpegLibrary;
-import com.google.android.exoplayer2.ext.flac.FlacLibrary;
-import com.google.android.exoplayer2.ext.opus.OpusLibrary;
-import com.google.android.exoplayer2.ext.vp9.VpxLibrary;
import org.thunderdog.challegram.BuildConfig;
import org.thunderdog.challegram.N;
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/SimpleVideoPlayer.java b/app/src/main/java/org/thunderdog/challegram/widget/SimpleVideoPlayer.java
index d36cbd9ac5..7d5a1a2301 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/SimpleVideoPlayer.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/SimpleVideoPlayer.java
@@ -18,11 +18,10 @@
import android.view.TextureView;
import androidx.annotation.Nullable;
-
-import com.google.android.exoplayer2.ExoPlayer;
-import com.google.android.exoplayer2.Player;
-import com.google.android.exoplayer2.source.ClippingMediaSource;
-import com.google.android.exoplayer2.source.MediaSource;
+import androidx.media3.common.Player;
+import androidx.media3.exoplayer.ExoPlayer;
+import androidx.media3.exoplayer.source.ClippingMediaSource;
+import androidx.media3.exoplayer.source.MediaSource;
import org.thunderdog.challegram.U;
diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt
index 8294e21cca..e1eb6e4a76 100644
--- a/buildSrc/src/main/kotlin/Config.kt
+++ b/buildSrc/src/main/kotlin/Config.kt
@@ -17,8 +17,8 @@ object Config {
const val PRIMARY_SDK_VERSION = 21
const val MIN_SDK_VERSION = 16
val JAVA_VERSION = org.gradle.api.JavaVersion.VERSION_11
- val EXOPLAYER_EXTENSIONS = arrayOf("ffmpeg", "flac", "opus", "vp9")
- val SUPPORTED_ABI = arrayOf("armeabi-v7a", "arm64-v8a"/*, "x86"*/, "x86_64")
+ val ANDROIDX_MEDIA_EXTENSIONS = arrayOf("decoder_ffmpeg", "decoder_flac", "decoder_opus", "decoder_vp9")
+ val SUPPORTED_ABI = arrayOf("armeabi-v7a", "arm64-v8a", "x86_64")
}
object LibraryVersions {
@@ -60,14 +60,12 @@ object Abi {
const val UNIVERSAL = 0
const val ARMEABI_V7A = 1
const val ARM64_V8A = 2
- // const val X86 = 3
const val X64 = 4
val VARIANTS = mapOf(
Pair(UNIVERSAL, AbiVariant("universal", displayName = "universal", filters = arrayOf("arm64-v8a", "armeabi-v7a"))),
Pair(ARMEABI_V7A, AbiVariant("arm32", "armeabi-v7a")),
Pair(ARM64_V8A, AbiVariant("arm64", "arm64-v8a")),
- // Pair(X86, AbiVariant("x86", "x86")),
Pair(X64, AbiVariant("x64", "x86_64", displayName = "x64"))
)
}
diff --git a/lint.xml b/lint.xml
new file mode 100644
index 0000000000..9de0fcefdc
--- /dev/null
+++ b/lint.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/scripts/force-clean.sh b/scripts/force-clean.sh
index 9b1f63ec12..3490b15e67 100755
--- a/scripts/force-clean.sh
+++ b/scripts/force-clean.sh
@@ -10,7 +10,7 @@ validate_dir "$THIRDPARTY_LIBRARIES"
rm -rf .gradle buildSrc/.gradle
rm -rf build buildSrc/build app/build vkryl/core/build vkryl/android/build vkryl/td/build vkryl/leveldb/build tdlib/build
-rm -rf "$THIRDPARTY_LIBRARIES/exoplayer"
+rm -rf "$THIRDPARTY_LIBRARIES/androidx-media"
rm -rf vkryl/leveldb/jni/leveldb/out
rm -rf app/.cxx vkryl/leveldb/.cxx
rm -rf app/.externalNativeBuild vkryl/leveldb/.externalNativeBuild
diff --git a/scripts/patch-exoplayer.sh b/scripts/patch-androidx-media.sh
similarity index 52%
rename from scripts/patch-exoplayer.sh
rename to scripts/patch-androidx-media.sh
index 41c78c42a2..69358a9619 100755
--- a/scripts/patch-exoplayer.sh
+++ b/scripts/patch-androidx-media.sh
@@ -3,4 +3,4 @@ set -e
# shellcheck source=set-env.sh
source "$(pwd)/scripts/set-env.sh"
-patch-exoplayer-impl.sh || (echo "ExoPlayer patch failed" && exit 1)
\ No newline at end of file
+patch-androidx-media-impl.sh || (echo "androidx-media patch failed" && exit 1)
\ No newline at end of file
diff --git a/scripts/private/build-ffmpeg-impl.sh b/scripts/private/build-ffmpeg-impl.sh
index 63c3651963..8078fa5972 100755
--- a/scripts/private/build-ffmpeg-impl.sh
+++ b/scripts/private/build-ffmpeg-impl.sh
@@ -8,8 +8,8 @@ function build_one {
fi
validate_dir "$ANDROID_NDK_ROOT"
- LIBVPX_INCLUDE_DIR="$THIRDPARTY_LIBRARIES/libvpx/build/$CPU/include"
- LIBVPX_LIB_DIR="$THIRDPARTY_LIBRARIES/libvpx/build/$CPU/lib"
+ LIBVPX_INCLUDE_DIR="$THIRDPARTY_LIBRARIES/libvpx/build/$FLAVOR/include"
+ LIBVPX_LIB_DIR="$THIRDPARTY_LIBRARIES/libvpx/build/$FLAVOR/lib"
validate_dir "$LIBVPX_INCLUDE_DIR"
validate_dir "$LIBVPX_LIB_DIR"
@@ -150,10 +150,11 @@ CC=${CROSS_PREFIX}${ANDROID_API}-clang
CXX=${CROSS_PREFIX}${ANDROID_API}-clang++
LD=$CC
AS=$CC
-ARCH=arm64
-CPU=arm64-v8a
-PREFIX=./build/$CPU
-ADDITIONAL_CONFIGURE_FLAG="--disable-asm --enable-optimizations"
+ARCH=aarch64
+CPU=armv8-a
+FLAVOR=arm64-v8a
+PREFIX=./build/$FLAVOR
+ADDITIONAL_CONFIGURE_FLAG="--enable-optimizations --disable-x86asm"
OPTIMIZE_CFLAGS=""
EXTRA_LIBS="-lunwind"
EXTRA_LDFLAGS=""
@@ -170,7 +171,8 @@ LD=$CC
AS=$CC
ARCH=x86_64
CPU=x86_64
-PREFIX=./build/$CPU
+FLAVOR=x86_64
+PREFIX=./build/$FLAVOR
ADDITIONAL_CONFIGURE_FLAG="--disable-asm"
OPTIMIZE_CFLAGS=""
EXTRA_LIBS="-lunwind"
@@ -195,14 +197,15 @@ CXX=$PREBUILT/bin/armv7a-linux-androideabi${ANDROID_API}-clang++
AS=$CC
ARCH=arm
CPU=armv7-a
-PREFIX=./build/$CPU
-ADDITIONAL_CONFIGURE_FLAG="--enable-neon"
+FLAVOR=armv7-a
+PREFIX=./build/$FLAVOR
+ADDITIONAL_CONFIGURE_FLAG="--enable-neon --disable-x86asm"
OPTIMIZE_CFLAGS="-marm -march=$CPU -mfloat-abi=softfp"
if [[ ${ANDROID_NDK_VERSION%%.*} -ge 23 ]]; then
LD=$CC
LIBS_DIR="${PREBUILT}/lib64/clang/12.0.9/lib/linux"
validate_dir "$LIBS_DIR"
- EXTRA_LDFLAGS="-L${LIBS_DIR}"
+ EXTRA_LDFLAGS="-L${LIBS_DIR} -Wl,--fix-cortex-a8"
EXTRA_LIBS="-lunwind -lclang_rt.builtins-arm-android"
else
LD="${PREBUILT}/arm-linux-androideabi/bin/ld.gold"
@@ -220,8 +223,9 @@ CXX=${CROSS_PREFIX}${ANDROID_API}-clang++
AS=$CC
ARCH=x86
CPU=i686
-PREFIX=./build/$CPU
-ADDITIONAL_CONFIGURE_FLAG="--disable-x86asm --disable-inline-asm --disable-asm"
+FLAVOR=i686
+PREFIX=./build/$FLAVOR
+ADDITIONAL_CONFIGURE_FLAG="--disable-asm"
OPTIMIZE_CFLAGS="-march=$CPU"
if [[ ${ANDROID_NDK_VERSION%%.*} -ge 23 ]]; then
LD=$CC
diff --git a/scripts/private/patch-exoplayer-impl.sh b/scripts/private/patch-androidx-media-impl.sh
similarity index 50%
rename from scripts/private/patch-exoplayer-impl.sh
rename to scripts/private/patch-androidx-media-impl.sh
index 513dd4250f..974a0e6bf1 100755
--- a/scripts/private/patch-exoplayer-impl.sh
+++ b/scripts/private/patch-androidx-media-impl.sh
@@ -1,24 +1,32 @@
#!/bin/bash
set -e
-EXO_DST="$THIRDPARTY_LIBRARIES/exoplayer"
-EXO_SRC=thirdparty/ExoPlayer/extensions
+test "$SED" || (echo "\$SED is not set!" && exit 1)
-test -d "$EXO_DST" || mkdir "$EXO_DST"
+DESTINATION_DIR="$THIRDPARTY_LIBRARIES/androidx-media"
+SOURCE_DIR=thirdparty/androidx-media/libraries
+SOURCE_FILES=(
+ "${SOURCE_DIR}/decoder_flac/src/main/jni/include"
+ "${SOURCE_DIR}/decoder_ffmpeg/src/main/jni/ffmpeg_jni.cc"
+ "${SOURCE_DIR}/decoder_flac/src/main/jni/flac_jni.cc"
+ "${SOURCE_DIR}/decoder_flac/src/main/jni/flac_parser.cc"
+ "${SOURCE_DIR}/decoder_opus/src/main/jni/opus_jni.cc"
+ "${SOURCE_DIR}/decoder_vp9/src/main/jni/vpx_jni.cc"
+)
+
+for SOURCE_FILE in ${SOURCE_FILES[@]}; do
+ test -f "$SOURCE_FILE" || test -d "$SOURCE_FILE" || (echo "$SOURCE_FILE not found!" && exit 1)
+done
-echo "Copying ExoPlayer files to $EXO_DST..."
+test -d "$DESTINATION_DIR" || mkdir "$DESTINATION_DIR"
-cp -pf "$EXO_SRC/ffmpeg/src/main/jni/ffmpeg_jni.cc" "$EXO_DST"
-cp -pf "$EXO_SRC/flac/src/main/jni/flac_jni.cc" "$EXO_DST"
-cp -pf "$EXO_SRC/flac/src/main/jni/flac_parser.cc" "$EXO_DST"
-# cp -pf "$EXO_SRC/flac/src/main/jni/flac_sources.mk" "$EXO_DST"
-cp -pfr "$EXO_SRC/flac/src/main/jni/include" "$EXO_DST"
-cp -pf "$EXO_SRC/opus/src/main/jni/opus_jni.cc" "$EXO_DST"
-cp -pf "$EXO_SRC/vp9/src/main/jni/vpx_jni.cc" "$EXO_DST"
+echo "Copying androidx-media files to ${DESTINATION_DIR}..."
-pushd "$EXO_DST" > /dev/null
+for SOURCE_FILE in ${SOURCE_FILES[@]}; do
+ cp -pfr "$SOURCE_FILE" "$DESTINATION_DIR"
+done
-test "$SED" || (echo "\$SED is not set!" && exit 1)
+pushd "$DESTINATION_DIR" > /dev/null
sed_rules=\
'$!N;s/^(#include <)android\/(log\.h>)/\1\2/g;'\
@@ -35,12 +43,6 @@ do
$SED -i".bak" -E "$sed_rules" "$file" && rm "$file.bak"
done
-#echo "Patching flac_sources.mk..."
-#$SED -i".bak" -E \
-#'$!N;s/^( *flac_(jni|parser)\.cc *\\\n)+//g;'\
-#'$!N;s/^( *)(flac\/src\/libFLAC)/\1thirdparty\/\2/g;P;D' \
-#flac_sources.mk && rm flac_sources.mk.bak
-
popd > /dev/null
-echo "ExoPlayer successfully patched!"
\ No newline at end of file
+echo "androidx-media successfully patched!"
\ No newline at end of file
diff --git a/scripts/reset.sh b/scripts/reset.sh
index 87ca92aa22..eb923f6eb0 100755
--- a/scripts/reset.sh
+++ b/scripts/reset.sh
@@ -43,5 +43,5 @@ git clean -f -d
git reset --hard
popd > /dev/null
-echo "Resetting ExoPlayer..."
-(test -d "$THIRDPARTY_LIBRARIES/exoplayer" && rm -rf "$THIRDPARTY_LIBRARIES/exoplayer") || true
\ No newline at end of file
+echo "Resetting androidx-media..."
+(test -d "$THIRDPARTY_LIBRARIES/androidx-media" && rm -rf "$THIRDPARTY_LIBRARIES/androidx-media") || true
\ No newline at end of file
diff --git a/scripts/setup.sh b/scripts/setup.sh
index b877fdd6f7..cc0aa1f82a 100755
--- a/scripts/setup.sh
+++ b/scripts/setup.sh
@@ -24,8 +24,8 @@ fi
# Patch opus
patch-opus-impl.sh
-# Patch ExoPlayer sources
-patch-exoplayer-impl.sh
+# Patch androidx-media sources
+patch-androidx-media-impl.sh
# Build and configure libvpx
build-vpx-impl.sh
diff --git a/scripts/update-dependencies.sh b/scripts/update-dependencies.sh
index 0ac9566947..5e869ff4c9 100755
--- a/scripts/update-dependencies.sh
+++ b/scripts/update-dependencies.sh
@@ -6,7 +6,7 @@ source "$(pwd)/scripts/set-env.sh"
reset.sh
simple_modules=( \
- thirdparty/ExoPlayer \
+ thirdparty/androidx-media \
app/jni/thirdparty/jni-utils \
app/jni/thirdparty/libtgvoip \
app/jni/thirdparty/rlottie \
@@ -22,8 +22,8 @@ for module in "${simple_modules[@]}"; do
popd > /dev/null
done
-echo "Patching ExoPlayer..."
-patch-exoplayer-impl.sh
+echo "Patching androidx-media..."
+patch-androidx-media-impl.sh
remote_modules=( webp libyuv ffmpeg lz4 flac opus opusfile ogg libvpx )
for module in "${remote_modules[@]}"; do
diff --git a/thirdparty/ExoPlayer b/thirdparty/ExoPlayer
deleted file mode 160000
index 5df25aefd9..0000000000
--- a/thirdparty/ExoPlayer
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5df25aefd9234f91716960430a4540baa8971315
diff --git a/thirdparty/androidx-media b/thirdparty/androidx-media
new file mode 160000
index 0000000000..b930b40a16
--- /dev/null
+++ b/thirdparty/androidx-media
@@ -0,0 +1 @@
+Subproject commit b930b40a16c06318e43c81771fa2b1024bdb3f29
From 5f42093abc304a3cd466b73ae7cfd4cabcab3316 Mon Sep 17 00:00:00 2001
From: tgx-server
Date: Sat, 3 Feb 2024 17:54:34 +0000
Subject: [PATCH 50/61] Version bump to `1683`
---
version.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.properties b/version.properties
index 97e0af4578..46939a9b19 100644
--- a/version.properties
+++ b/version.properties
@@ -1,5 +1,5 @@
# App
-version.app=1682
+version.app=1683
version.major=0
# Anchor date point in app versioning
version.creation=873642600564
From 2e5f3c0cc64b3b32b980531623bf5686dd2ff0bd Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Mon, 5 Feb 2024 14:19:44 +0400
Subject: [PATCH 51/61] Disable x86asm in FFMpeg for `x86_64` builds
---
scripts/private/build-ffmpeg-impl.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/private/build-ffmpeg-impl.sh b/scripts/private/build-ffmpeg-impl.sh
index 8078fa5972..69c40144c5 100755
--- a/scripts/private/build-ffmpeg-impl.sh
+++ b/scripts/private/build-ffmpeg-impl.sh
@@ -173,7 +173,7 @@ ARCH=x86_64
CPU=x86_64
FLAVOR=x86_64
PREFIX=./build/$FLAVOR
-ADDITIONAL_CONFIGURE_FLAG="--disable-asm"
+ADDITIONAL_CONFIGURE_FLAG="--disable-asm --disable-x86asm"
OPTIMIZE_CFLAGS=""
EXTRA_LIBS="-lunwind"
EXTRA_LDFLAGS=""
From 363954f1b2800c5e29f4cc62f45c6fca37f02f40 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Mon, 5 Feb 2024 15:51:57 +0400
Subject: [PATCH 52/61] Properly handle EOF while rendering GIFs
Some frames were not rendered at the end of mp4 files after changes made in c3fee17, which resulted in noticeable glitch while playing some GIFs. This commit resolves the issue by properly handling end-of-stream.
---
app/jni/gif.cpp | 54 ++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 44 insertions(+), 10 deletions(-)
diff --git a/app/jni/gif.cpp b/app/jni/gif.cpp
index 9b969c1231..cecf12175e 100644
--- a/app/jni/gif.cpp
+++ b/app/jni/gif.cpp
@@ -130,6 +130,10 @@ struct VideoInfo {
bool has_decoded_frames = false;
bool is_broken = false;
bool had_invalid_frames = false;
+ bool eof_reached = false;
+ bool draining = false;
+ size_t drained_count = 0;
+ size_t loop_count = 0;
AVPacket *packet = nullptr;
};
@@ -256,7 +260,7 @@ JNI_FUNC(jlong, createDecoder, jstring src, jintArray data, jdouble startMediaTi
if (info->scale_ctx != nullptr) {
dstWidth = newWidth;
dstHeight = newHeight;
- logi(TAG_GIF_LOADER, "Created scale context %dx%d -> %dx%d, format: %d", srcWidth, srcHeight, dstWidth, dstHeight, BITMAP_TARGET_FORMAT);
+ logi(TAG_GIF_LOADER, "Created scale context %dx%d -> %dx%d, format: %d -> %d", srcWidth, srcHeight, dstWidth, dstHeight, fmt, BITMAP_TARGET_FORMAT);
}
}
}
@@ -271,8 +275,10 @@ JNI_FUNC(jlong, createDecoder, jstring src, jintArray data, jdouble startMediaTi
if (startMediaTimestamp != 0) {
int ret = 0;
int64_t ts = (int64_t) (startMediaTimestamp * (double) AV_TIME_BASE);
- ret = avformat_seek_file(info->fmt_ctx, -1, std::numeric_limits::min(), ts,
- std::numeric_limits::max(), 0);
+ ret = avformat_seek_file(
+ info->fmt_ctx, -1,
+ std::numeric_limits::min(), ts,
+ std::numeric_limits::max(), 0);
if (ret < 0) {
loge(TAG_GIF_LOADER, "can't seek to startMediaTimestamp %s, %s", info->path.c_str(), av_err2str(ret));
}
@@ -326,8 +332,10 @@ JNI_FUNC(jboolean, seekVideoToStart, jlong ptr) {
VideoInfo *info = jni::jlong_to_ptr(ptr);
int ret = 0;
- ret = avformat_seek_file(info->fmt_ctx, -1, std::numeric_limits::min(), 0,
- std::numeric_limits::max(), 0);
+ ret = avformat_seek_file(
+ info->fmt_ctx, -1,
+ std::numeric_limits::min(), 0,
+ std::numeric_limits::max(), 0);
if (ret < 0) {
loge(TAG_GIF_LOADER, "can't forcely seek to beginning of file %s, %s", info->path.c_str(),
av_err2str(ret));
@@ -376,7 +384,6 @@ void to_android_bitmap (JNIEnv *env, VideoInfo *info, jobject bitmap, jintArray
// note: for now, updated libyuv + kYvuI601Constants in I420AlphaToARGBMatrix fixes AV_PIX_FMT_YUVA420P issue - but still needs to be researched
/*int res =*/ sws_scale(info->scale_ctx, frame->data, frame->linesize, 0, frame->height, dst_data, info->dst_linesize);
} else {
- // TODO: find out why libyuv damages the color palette
switch (fmt) {
case BITMAP_TARGET_FORMAT: {
int size = av_image_get_buffer_size(fmt, frameWidth, frameHeight, 1);
@@ -429,7 +436,6 @@ JNI_FUNC(jint, getVideoFrame, jlong ptr, jobject bitmap, jintArray data) {
bool gotFrame = false;
bool hasLooped = false;
- bool eofReached = false;
bool fatalError = false;
size_t errorCount = 0;
size_t maxErrorCount = round(std::min(120.0, (double) info->frameRate / 1000.0) * 2.5);
@@ -437,7 +443,28 @@ JNI_FUNC(jint, getVideoFrame, jlong ptr, jobject bitmap, jintArray data) {
do {
int ret;
- if (eofReached) {
+ if (info->draining) {
+ ret = avcodec_receive_frame(info->video_dec_ctx, info->frame);
+ if (ret == 0) {
+ info->drained_count++;
+ gotFrame = true;
+ break;
+ }
+ info->draining = false;
+ if (info->loop_count == 0) {
+ logv(TAG_GIF_LOADER, "avcodec_receive_frame drain mode finished for %s: %s (%zu frames, eof: %b)", info->path.c_str(),
+ av_err2str(ret),
+ info->drained_count,
+ info->eof_reached);
+ info->drained_count = 0;
+ }
+ }
+
+ if (info->eof_reached) {
+ if (info->loop_count == SIZE_T_MAX) {
+ info->loop_count = 0;
+ }
+ info->loop_count++;
ret = avformat_seek_file(
info->fmt_ctx, -1,
std::numeric_limits::min(), 0,
@@ -449,7 +476,7 @@ JNI_FUNC(jint, getVideoFrame, jlong ptr, jobject bitmap, jintArray data) {
break;
}
info->has_decoded_frames = false;
- eofReached = false;
+ info->eof_reached = false;
hasLooped = true;
avcodec_flush_buffers(info->video_dec_ctx);
}
@@ -461,7 +488,14 @@ JNI_FUNC(jint, getVideoFrame, jlong ptr, jobject bitmap, jintArray data) {
break;
}
if (ret == AVERROR_EOF) {
- eofReached = true;
+ info->eof_reached = true;
+ ret = avcodec_send_packet(info->video_dec_ctx, NULL);
+ if (ret == 0) {
+ info->draining = true;
+ } else if (info->loop_count == 0) {
+ logv(TAG_GIF_LOADER, "avcodec_send_packet can't enter drain mode for %s: %s", info->path.c_str(),
+ av_err2str(ret));
+ }
continue;
}
loge(TAG_GIF_LOADER, "av_read_frame fatal error for %s: %s", info->path.c_str(), av_err2str(ret));
From 1cd72691531ba404861393ebd7e6edf46a354b05 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Mon, 5 Feb 2024 18:38:59 +0400
Subject: [PATCH 53/61] Notification improvements on Android Auto
1. Mute/Unmute on Android Auto should work properly
2. Got rid of `NotificationCompat.CarExtender.UnreadConversation`
3. Fixed duplicate notification issue, which was caused by Android Auto playing sound for summary notification (resolved #553)
4. Conversations should group properly now (only one alert per chat on a screen)
5. Messages now properly get read after playing them aloud.
---
app/build.gradle.kts | 1 +
.../receiver/TGMessageReceiver.java | 7 +-
.../receiver/TGRemoveAllReceiver.java | 2 +-
.../challegram/receiver/TGRemoveReceiver.java | 2 +-
.../challegram/telegram/TdlibManager.java | 30 ++++--
.../telegram/TdlibNotificationExtras.java | 31 ++++--
.../telegram/TdlibNotificationStyle.java | 97 +++++++++++--------
.../thunderdog/challegram/tool/Intents.java | 1 +
.../drawable/baseline_volume_up_24_white.xml | 9 ++
app/src/main/res/values/strings.xml | 19 ++--
10 files changed, 131 insertions(+), 68 deletions(-)
create mode 100755 app/src/main/res/drawable/baseline_volume_up_24_white.xml
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a4c7ab0d54..1848b4912d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -196,6 +196,7 @@ dependencies {
implementation(project(":vkryl:android"))
implementation(project(":vkryl:td"))
// AndroidX: https://developer.android.com/jetpack/androidx/versions
+ implementation("androidx.core:core:1.12.0")
implementation("androidx.activity:activity:1.8.2")
implementation("androidx.palette:palette:1.0.0")
implementation("androidx.recyclerview:recyclerview:1.3.2")
diff --git a/app/src/main/java/org/thunderdog/challegram/receiver/TGMessageReceiver.java b/app/src/main/java/org/thunderdog/challegram/receiver/TGMessageReceiver.java
index b3944b7f38..c868b42bd9 100644
--- a/app/src/main/java/org/thunderdog/challegram/receiver/TGMessageReceiver.java
+++ b/app/src/main/java/org/thunderdog/challegram/receiver/TGMessageReceiver.java
@@ -33,10 +33,13 @@ public void onReceive (Context context, Intent intent) {
int externalActionId;
switch (action) {
case Intents.ACTION_MESSAGE_READ:
- externalActionId = TdlibManager.EXTERNAL_ACTION_MARK_AS_READ;
+ externalActionId = TdlibManager.ExternalAction.MARK_AS_READ;
break;
case Intents.ACTION_MESSAGE_MUTE:
- externalActionId = TdlibManager.EXTERNAL_ACTION_MUTE;
+ externalActionId = TdlibManager.ExternalAction.MUTE;
+ break;
+ case Intents.ACTION_MESSAGE_UNMUTE:
+ externalActionId = TdlibManager.ExternalAction.UNMUTE;
break;
default:
return;
diff --git a/app/src/main/java/org/thunderdog/challegram/receiver/TGRemoveAllReceiver.java b/app/src/main/java/org/thunderdog/challegram/receiver/TGRemoveAllReceiver.java
index 4d6827177a..044f7fa155 100644
--- a/app/src/main/java/org/thunderdog/challegram/receiver/TGRemoveAllReceiver.java
+++ b/app/src/main/java/org/thunderdog/challegram/receiver/TGRemoveAllReceiver.java
@@ -25,6 +25,6 @@ public class TGRemoveAllReceiver extends BroadcastReceiver {
@Override
public void onReceive (Context context, Intent intent) {
TdlibNotificationExtras extras = TdlibNotificationExtras.parseCategory(intent.getExtras());
- TdlibManager.performExternalAction(context, TdlibManager.EXTERNAL_ACTION_MARK_ALL_AS_HIDDEN, extras);
+ TdlibManager.performExternalAction(context, TdlibManager.ExternalAction.MARK_ALL_AS_HIDDEN, extras);
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/receiver/TGRemoveReceiver.java b/app/src/main/java/org/thunderdog/challegram/receiver/TGRemoveReceiver.java
index e77642c750..7d0d4aac52 100644
--- a/app/src/main/java/org/thunderdog/challegram/receiver/TGRemoveReceiver.java
+++ b/app/src/main/java/org/thunderdog/challegram/receiver/TGRemoveReceiver.java
@@ -25,6 +25,6 @@ public class TGRemoveReceiver extends BroadcastReceiver {
@Override
public void onReceive (Context context, Intent intent) {
TdlibNotificationExtras extras = TdlibNotificationExtras.parse(intent.getExtras());
- TdlibManager.performExternalAction(context, TdlibManager.EXTERNAL_ACTION_MARK_AS_HIDDEN, extras);
+ TdlibManager.performExternalAction(context, TdlibManager.ExternalAction.MARK_AS_HIDDEN, extras);
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibManager.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibManager.java
index 19efd3c124..dcece7faf1 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibManager.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibManager.java
@@ -134,10 +134,21 @@ public static boolean makeSync (Context context, int accountId, int cause, long
}
}
- public static final int EXTERNAL_ACTION_MARK_AS_HIDDEN = 0;
- public static final int EXTERNAL_ACTION_MARK_ALL_AS_HIDDEN = 1;
- public static final int EXTERNAL_ACTION_MARK_AS_READ = 2;
- public static final int EXTERNAL_ACTION_MUTE = 3;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ ExternalAction.MARK_AS_HIDDEN,
+ ExternalAction.MARK_ALL_AS_HIDDEN,
+ ExternalAction.MARK_AS_READ,
+ ExternalAction.MUTE,
+ ExternalAction.UNMUTE
+ })
+ public @interface ExternalAction {
+ int MARK_AS_HIDDEN = 0,
+ MARK_ALL_AS_HIDDEN = 1,
+ MARK_AS_READ = 2,
+ MUTE = 3,
+ UNMUTE = 4;
+ }
private interface NotificationTask {
void onPerformTask (Tdlib tdlib, Runnable onDone);
@@ -174,18 +185,21 @@ public static void performExternalAction (Context context, int action, TdlibNoti
performSyncTask(context, extras.accountId, "external:" + action, (tdlib, onDone) -> {
tdlib.incrementNotificationReferenceCount();
switch (action) {
- case EXTERNAL_ACTION_MARK_AS_HIDDEN:
+ case ExternalAction.MARK_AS_HIDDEN:
tdlib.notifications().onHide(extras);
break;
- case EXTERNAL_ACTION_MARK_ALL_AS_HIDDEN:
+ case ExternalAction.MARK_ALL_AS_HIDDEN:
tdlib.notifications().onHideAll(extras.category);
break;
- case EXTERNAL_ACTION_MARK_AS_READ:
+ case ExternalAction.MARK_AS_READ:
extras.read(tdlib);
break;
- case EXTERNAL_ACTION_MUTE:
+ case ExternalAction.MUTE:
extras.mute(tdlib);
break;
+ case ExternalAction.UNMUTE:
+ extras.unmute(tdlib);
+ break;
}
tdlib.notifications().releaseTdlibReference(onDone);
}, null);
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationExtras.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationExtras.java
index 2413f3ef46..76cae2c228 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationExtras.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationExtras.java
@@ -19,6 +19,7 @@
import android.widget.Toast;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import org.drinkless.tdlib.TdApi;
import org.thunderdog.challegram.Log;
@@ -148,26 +149,33 @@ public static void put (Intent intent, Tdlib tdlib, TdlibNotificationGroup group
intent.putExtra("user_ids", userIds);
}
- public void mute (Tdlib tdlib) {
+ public void setMuteFor (Tdlib tdlib, int muteForSeconds) {
boolean needToast = tdlib.notifications().isUnknownGroup(notificationGroupId);
String text = null;
- int muteFor = (int) TimeUnit.HOURS.toSeconds(1);
if (areMentions) {
if (userIds != null) {
if (needToast) {
if (userIds.length == 1) {
- text = Lang.getString(R.string.NotificationMutedPerson, tdlib.cache().userName(userIds[0]));
+ @StringRes int stringRes = muteForSeconds == 0 ? R.string.NotificationUnmutedPerson : R.string.NotificationMutedPerson;
+ text = Lang.getString(stringRes, tdlib.cache().userName(userIds[0]));
} else {
- text = Lang.plural(R.string.NotificationMutedPersons, userIds.length);
+ @StringRes int stringRes = muteForSeconds == 0 ? R.string.NotificationUnmutedPeople : R.string.NotificationMutedPersons;
+ text = Lang.plural(stringRes, userIds.length);
}
}
for (long userId : userIds) {
- tdlib.setMuteForSync(userId, muteFor);
+ tdlib.setMuteForSync(userId, muteForSeconds);
}
}
} else {
- tdlib.setMuteForSync(chatId, muteFor);
- text = needToast ? Lang.getString(ChatId.isUserChat(chatId) ? R.string.NotificationMutedPerson : R.string.NotificationMutedChat, tdlib.chatTitle(chatId)) : null;
+ tdlib.setMuteForSync(chatId, muteForSeconds);
+ @StringRes int stringRes;
+ if (muteForSeconds == 0) {
+ stringRes = ChatId.isUserChat(chatId) ? R.string.NotificationUnmutedPerson : R.string.NotificationUnmutedChat;
+ } else {
+ stringRes = ChatId.isUserChat(chatId) ? R.string.NotificationMutedPerson : R.string.NotificationMutedChat;
+ }
+ text = needToast ? Lang.getString(stringRes, tdlib.chatTitle(chatId)) : null;
}
hide(tdlib);
if (needToast) {
@@ -175,6 +183,15 @@ public void mute (Tdlib tdlib) {
}
}
+ public void unmute (Tdlib tdlib) {
+ setMuteFor(tdlib, 0);
+ }
+
+ public void mute (Tdlib tdlib) {
+ int muteFor = (int) TimeUnit.HOURS.toSeconds(1);
+ setMuteFor(tdlib, muteFor);
+ }
+
public void read (Tdlib tdlib) {
boolean needToast = tdlib.notifications().isUnknownGroup(notificationGroupId);
if (areMentions) {
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationStyle.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationStyle.java
index 39714c25d4..4e16d07cf5 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationStyle.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationStyle.java
@@ -337,31 +337,28 @@ protected final int displayChildNotification (NotificationManagerCompat manager,
final long[] allUserIds = group.isMention() ? group.getAllUserIds() : null;
final boolean[] hasCustomText = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? new boolean[1] : null;
- NotificationCompat.CarExtender.UnreadConversation.Builder conversationBuilder = new NotificationCompat.CarExtender.UnreadConversation.Builder(visualChatTitle != null ? visualChatTitle.toString() : null).setLatestTimestamp(TimeUnit.SECONDS.toMillis(lastNotification.getDate()));
-
Intent msgHeardIntent = new Intent();
styleIntent(Intents.ACTION_MESSAGE_HEARD, msgHeardIntent, tdlib, group, needReplyToMessage, allMessageIds, allUserIds);
- PendingIntent msgHeardPendingIntent = PendingIntent.getBroadcast(UI.getAppContext(), notificationId, msgHeardIntent, Intents.mutabilityFlags(true));
- conversationBuilder.setReadPendingIntent(msgHeardPendingIntent);
+ NotificationCompat.Action replyAction = null, muteAction = null, unmuteAction = null, readAction = null;
- NotificationCompat.Action replyAction = null, muteAction = null, readAction = null;
+ // Hack to avoid duplicate notification for summary notification on Android Auto
+ boolean hideOnAndroidAuto = !isSummary;
if (needReply) {
// reply
- Intent msgReplyIntent = new Intent();
- styleIntent(Intents.ACTION_MESSAGE_REPLY, msgReplyIntent, tdlib, group, needReplyToMessage, allMessageIds, allUserIds);
- PendingIntent msgReplyPendingIntent = PendingIntent.getBroadcast(UI.getAppContext(), notificationId, msgReplyIntent, Intents.mutabilityFlags(true));
- RemoteInput remoteInputAuto = new RemoteInput.Builder(TGBaseReplyReceiver.EXTRA_VOICE_REPLY).setLabel(Lang.getString(R.string.Reply)).build();
- conversationBuilder.setReplyAction(msgReplyPendingIntent, remoteInputAuto);
-
Intent replyIntent = new Intent(UI.getAppContext(), TGWearReplyReceiver.class);
Intents.secureIntent(replyIntent, true);
TdlibNotificationExtras.put(replyIntent, tdlib, group, needReplyToMessage, allMessageIds, allUserIds);
PendingIntent replyPendingIntent = PendingIntent.getBroadcast(UI.getAppContext(), notificationId, replyIntent, Intents.mutabilityFlags(true));
RemoteInput remoteInput = new RemoteInput.Builder(TGBaseReplyReceiver.EXTRA_VOICE_REPLY).setLabel(Lang.getString(R.string.Reply)).build();
String replyToString = Lang.getString(R.string.Reply);
- replyAction = new NotificationCompat.Action.Builder(R.drawable.baseline_reply_24_white, replyToString, replyPendingIntent).setAllowGeneratedReplies(true).addRemoteInput(remoteInput).setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY).build();
+ replyAction = new NotificationCompat.Action.Builder(R.drawable.baseline_reply_24_white, replyToString, replyPendingIntent)
+ .setAllowGeneratedReplies(true)
+ .addRemoteInput(remoteInput)
+ .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
+ .setShowsUserInterface(hideOnAndroidAuto)
+ .build();
}
if (needPreview) {
@@ -370,36 +367,57 @@ protected final int displayChildNotification (NotificationManagerCompat manager,
styleIntent(Intents.ACTION_MESSAGE_READ, intent, tdlib, group, needReplyToMessage, allMessageIds, allUserIds);
try {
PendingIntent pendingIntent = PendingIntent.getBroadcast(UI.getAppContext(), notificationId, intent, Intents.mutabilityFlags(true));
- readAction = new NotificationCompat.Action.Builder(R.drawable.baseline_done_all_24_white, Lang.getString(R.string.ActionRead), pendingIntent).setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ).build();
+ readAction = new NotificationCompat.Action.Builder(R.drawable.baseline_done_all_24_white, Lang.getString(R.string.ActionRead), pendingIntent)
+ .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ)
+ .setShowsUserInterface(hideOnAndroidAuto)
+ .build();
} catch (Throwable t) {
Log.e("Unable to add read intent", t);
}
}
if (true) {
- // mute for 1h
- Intent intent = new Intent(UI.getAppContext(), TGMessageReceiver.class);
- styleIntent(Intents.ACTION_MESSAGE_MUTE, intent, tdlib, group, needReplyToMessage, allMessageIds, allUserIds);
- try {
- PendingIntent pendingIntent = PendingIntent.getBroadcast(UI.getAppContext(), notificationId, intent, Intents.mutabilityFlags(true));
- String text;
- if (group.isMention()) {
- long singleSenderId = group.singleSenderId();
- if (singleSenderId != 0) {
- String firstName = tdlib.chatTitleShort(singleSenderId);
- // text = Lang.plural(R.string.ActionMutePersonHours, 1, firstName);
- text = Lang.getString(R.string.ActionMutePerson, firstName);
- } else {
- // text = Lang.plural(R.string.ActionMuteAll, 1);
- text = Lang.getString(R.string.ActionMuteEveryone);
- }
+ String muteText, unmuteText;
+ if (group.isMention()) {
+ long singleSenderId = group.singleSenderId();
+ if (singleSenderId != 0) {
+ String firstName = tdlib.chatTitleShort(singleSenderId);
+ muteText = Lang.getString(R.string.ActionMutePerson, firstName);
+ unmuteText = Lang.getString(R.string.ActionUnmutePerson, firstName);
} else {
- // text = Lang.plural(R.string.ActionMuteHours, 1);
- text = Lang.getString(R.string.ActionMute);
+ muteText = Lang.getString(R.string.ActionMuteEveryone);
+ unmuteText = Lang.getString(R.string.ActionUnmuteEveryone);
}
- muteAction = new NotificationCompat.Action.Builder(R.drawable.baseline_volume_off_24_white, text, pendingIntent).setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MUTE).build();
+ } else {
+ muteText = Lang.getString(R.string.ActionMute);
+ unmuteText = Lang.getString(R.string.ActionUnmute);
+ }
+
+ // mute for 1h
+ Intent muteIntent = new Intent(UI.getAppContext(), TGMessageReceiver.class);
+ styleIntent(Intents.ACTION_MESSAGE_MUTE, muteIntent, tdlib, group, needReplyToMessage, allMessageIds, allUserIds);
+ try {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(UI.getAppContext(), notificationId, muteIntent, Intents.mutabilityFlags(true));
+ muteAction = new NotificationCompat.Action.Builder(R.drawable.baseline_volume_off_24_white, muteText, pendingIntent)
+ .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MUTE)
+ .setShowsUserInterface(hideOnAndroidAuto)
+ .build();
} catch (Throwable t) {
- Log.e("Unable to add read intent", t);
+ Log.e("Unable to create mute intent", t);
+ }
+
+ // Unmute
+ Intent unmuteIntent = new Intent(UI.getAppContext(), TGMessageReceiver.class);
+ styleIntent(Intents.ACTION_MESSAGE_UNMUTE, unmuteIntent, tdlib, group, needReplyToMessage, allMessageIds, allUserIds);
+
+ try {
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(UI.getAppContext(), notificationId, unmuteIntent, Intents.mutabilityFlags(true));
+ unmuteAction = new NotificationCompat.Action.Builder(R.drawable.baseline_volume_up_24_white, unmuteText, pendingIntent)
+ .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_UNMUTE)
+ .setShowsUserInterface(hideOnAndroidAuto)
+ .build();
+ } catch (Throwable t) {
+ Log.e("Unable to create unmute intent", t);
}
}
@@ -447,7 +465,6 @@ protected final int displayChildNotification (NotificationManagerCompat manager,
messageText = Lang.getString(R.string.YouHaveNewMessage);
}
textBuilder.append(messageText);
- conversationBuilder.addMessage(messageText != null ? messageText.toString() : null);
addMessage(messagingStyle, messageText, person, chat, notification, isSummary ? SUMMARY_MEDIA_LOAD_TIMEOUT : MEDIA_LOAD_TIMEOUT, isRebuild, !onlyScheduled && notification.isScheduled(), !onlySilent && notification.isVisuallySilent(), onlyPinned);
} else {
final CharSequence messageText;
@@ -476,7 +493,6 @@ protected final int displayChildNotification (NotificationManagerCompat manager,
messageText = Lang.plural(R.string.xNewMessages, mergedList.size());
}
textBuilder.append(messageText);
- conversationBuilder.addMessage(messageText != null ? messageText.toString() : null);
addMessage(messagingStyle, messageText, person, tdlib, chat, mergedList, isSummary ? SUMMARY_MEDIA_LOAD_TIMEOUT : MEDIA_LOAD_TIMEOUT, isRebuild, !onlyScheduled && isScheduled, !onlySilent && isVisuallySilent);
}
}
@@ -565,8 +581,6 @@ protected final int displayChildNotification (NotificationManagerCompat manager,
final PendingIntent contentIntent = TdlibNotificationUtils.newIntent(tdlib.id(), tdlib.settings().getLocalChatId(chatId), group.findTargetMessageId());
- NotificationCompat.CarExtender carExtender = new NotificationCompat.CarExtender().setUnreadConversation(conversationBuilder.build());
-
boolean needGroupLogic = true; // !isSummary || (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && settings == null);
NotificationCompat.Builder builder;
@@ -620,14 +634,16 @@ protected final int displayChildNotification (NotificationManagerCompat manager,
}
if (!Passcode.instance().isLocked()) {
- /*if (muteAction != null)
- builder.addAction(muteAction);*/
+ if (muteAction != null)
+ builder.addInvisibleAction(muteAction);
+ if (unmuteAction != null)
+ builder.addInvisibleAction(unmuteAction);
if (replyAction != null)
builder.addAction(replyAction);
if (readAction != null)
builder.addAction(readAction);
}
- try { builder.extend(carExtender); } catch (Throwable t) { Log.w(t); }
+ builder.extend(new NotificationCompat.CarExtender());
styleNotification(tdlib, builder, chatId, chat, allowPreview);
@@ -689,6 +705,7 @@ protected final int displayChildNotification (NotificationManagerCompat manager,
try {
if (Config.TEST_NOTIFICATION_PROBLEM_RESOLUTION)
throw new RuntimeException();
+ Log.v("manager.notify(...) (1) isRebuild:%b isSummary:%b", isRebuild, isSummary);
manager.notify(notificationId, notification);
state = DISPLAY_STATE_OK;
} catch (Throwable t) {
diff --git a/app/src/main/java/org/thunderdog/challegram/tool/Intents.java b/app/src/main/java/org/thunderdog/challegram/tool/Intents.java
index b523d47811..d7ab7e6ace 100644
--- a/app/src/main/java/org/thunderdog/challegram/tool/Intents.java
+++ b/app/src/main/java/org/thunderdog/challegram/tool/Intents.java
@@ -101,6 +101,7 @@ public class Intents {
public static final String ACTION_MESSAGE_REPLY = PACKAGE_NAME + ".ACTION_MESSAGE_REPLY";
public static final String ACTION_MESSAGE_MUTE = PACKAGE_NAME + ".ACTION_MESSAGE_MUTE";
+ public static final String ACTION_MESSAGE_UNMUTE = PACKAGE_NAME + ".ACTION_MESSAGE_UNMUTE";
public static final String ACTION_MESSAGE_READ = PACKAGE_NAME + ".ACTION_MESSAGE_READ";
/// public static final String ACTION_MESSAGE_HIDE = PACKAGE_NAME + ".ACTION_MESSAGE_HIDE";
public static final String ACTION_MESSAGE_HEARD = PACKAGE_NAME + ".ACTION_MESSAGE_HEARD"; // chat_id, last_message_id
diff --git a/app/src/main/res/drawable/baseline_volume_up_24_white.xml b/app/src/main/res/drawable/baseline_volume_up_24_white.xml
new file mode 100755
index 0000000000..a89234b520
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_volume_up_24_white.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index dc1703ecff..2ddac38833 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -3122,21 +3122,22 @@
Mute %1$s
Mute all
+ Unute
+ Unmute %1$s
+ Unmute all
+
Muted %1$s for 1 hour
Muted %1$s for 1 hour
Muted %1$s person for 1 hour
Muted %1$s people for 1 hour
- Marked messages as read
- Marked mentions as read
-
+ Marked messages as read
+ Marked mentions as read
Default
Enabled
From cd40024153830764d56ff4eff0abbdf019aac372 Mon Sep 17 00:00:00 2001
From: tgx-server
Date: Mon, 5 Feb 2024 14:42:28 +0000
Subject: [PATCH 54/61] Version bump to `1684`
---
version.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.properties b/version.properties
index 46939a9b19..5bff1bfd14 100644
--- a/version.properties
+++ b/version.properties
@@ -1,5 +1,5 @@
# App
-version.app=1683
+version.app=1684
version.major=0
# Anchor date point in app versioning
version.creation=873642600564
From 09826852a803f5cef4db5971672c8be5920a843f Mon Sep 17 00:00:00 2001
From: Maksym Moroz
Date: Tue, 6 Feb 2024 10:45:53 +0200
Subject: [PATCH 55/61] Update gradle build files to modern standards (#554)
* refactor(gradle): remove deprecated buildscript and allprojects
* refactor(gradle): remove deprecated gradle.properties buildconfig declaration
* chore(buildSrc): get extension by type instead of by name
* refactor(gradle): use task configuration avoidance APIs
Replace usage of deprecated eager task creation by type-safe lazy counterpart
* refactor(gradle): use type-safe release type configuration
* refactor(gradle): use lazy configureEach for variants
* Use `named` instead of `getByName` in `CMakePlugin.kt`
---------
Signed-off-by: Maksym Moroz
---
app/build.gradle.kts | 59 +++++-----
build.gradle.kts | 16 ---
.../kotlin/me/vkryl/plugin/CMakePlugin.kt | 109 +++++++++---------
gradle.properties | 1 -
settings.gradle.kts | 17 +++
5 files changed, 102 insertions(+), 100 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 1848b4912d..0a635e7917 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -7,27 +7,27 @@ plugins {
id("cmake-plugin")
}
-task("generateResourcesAndThemes") {
+val generateResourcesAndThemes by tasks.registering(me.vkryl.task.GenerateResourcesAndThemesTask::class) {
group = "Setup"
description = "Generates fresh strings, ids, theme resources and utility methods based on current static files"
}
-task("updateLanguages") {
+val updateLanguages by tasks.registering(me.vkryl.task.FetchLanguagesTask::class) {
group = "Setup"
description = "Generates and updates all strings.xml resources based on translations.telegram.org"
}
-task("validateApiTokens") {
+val validateApiTokens by tasks.registering(me.vkryl.task.ValidateApiTokensTask::class) {
group = "Setup"
description = "Validates some API tokens to make sure they work properly and won't cause problems"
}
-task("updateExceptions") {
+val updateExceptions by tasks.registering(me.vkryl.task.UpdateExceptionsTask::class) {
group = "Setup"
description = "Updates exception class names with the app or TDLib version number in order to have separate group on Google Play Developer Console"
}
-task("generatePhoneFormat") {
+val generatePhoneFormat by tasks.registering(me.vkryl.task.GeneratePhoneFormatTask::class) {
group = "Setup"
description = "Generates utility methods for phone formatting, e.g. +12345678901 -> +1 (234) 567 89-01"
}
-task("checkEmojiKeyboard") {
+val checkEmojiKeyboard by tasks.registering(me.vkryl.task.CheckEmojiKeyboardTask::class) {
group = "Setup"
description = "Checks that all supported emoji can be entered from the keyboard"
}
@@ -87,8 +87,12 @@ android {
checkDependencies = true
}
+ buildFeatures {
+ buildConfig = true
+ }
+
buildTypes {
- getByName("release") {
+ release {
Config.ANDROIDX_MEDIA_EXTENSIONS.forEach { extension ->
val proguardFile = file(
"../thirdparty/androidx-media/libraries/${extension}/proguard-rules.txt"
@@ -124,32 +128,31 @@ android {
}
}
}
- applicationVariants.all {
- val variant = this
- val abi = (variant.productFlavors[0].versionCode ?: error("null")) - 1
+ applicationVariants.configureEach {
+ val abi = (productFlavors[0].versionCode ?: error("null")) - 1
val abiVariant = Abi.VARIANTS[abi] ?: error("null")
val versionCode = defaultConfig.versionCode ?: error("null")
val versionCodeOverride = versionCode * 1000 + abi * 10
- val versionNameOverride = "${variant.versionName}.${defaultConfig.versionCode}${if (extra.has("app_version_suffix")) extra["app_version_suffix"] else ""}-${abiVariant.displayName}${if (extra.has("app_name_suffix")) "-" + extra["app_name_suffix"] else ""}${if (variant.buildType.isDebuggable) "-debug" else ""}"
+ val versionNameOverride = "${versionName}.${defaultConfig.versionCode}${if (extra.has("app_version_suffix")) extra["app_version_suffix"] else ""}-${abiVariant.displayName}${if (extra.has("app_name_suffix")) "-" + extra["app_name_suffix"] else ""}${if (buildType.isDebuggable) "-debug" else ""}"
val outputFileNamePrefix = properties.getProperty("app.file", projectName.replace(" ", "-").replace("#", ""))
val fileName = "${outputFileNamePrefix}-${versionNameOverride.replace("-universal(?=-|\$)", "")}"
- variant.buildConfigField("int", "ORIGINAL_VERSION_CODE", versionCode.toString())
- variant.buildConfigField("int", "ABI", abi.toString())
- variant.buildConfigField("String", "ORIGINAL_VERSION_NAME", "\"${variant.versionName}.${defaultConfig.versionCode}\"")
+ buildConfigField("int", "ORIGINAL_VERSION_CODE", versionCode.toString())
+ buildConfigField("int", "ABI", abi.toString())
+ buildConfigField("String", "ORIGINAL_VERSION_NAME", "\"${versionName}.${defaultConfig.versionCode}\"")
- variant.outputs.map { it as ApkVariantOutputImpl }.forEach { output ->
+ outputs.map { it as ApkVariantOutputImpl }.forEach { output ->
output.versionCodeOverride = versionCodeOverride
output.versionNameOverride = versionNameOverride
output.outputFileName = "${fileName}.apk"
}
- if (variant.buildType.isMinifyEnabled) {
- variant.assembleProvider!!.configure {
+ if (buildType.isMinifyEnabled) {
+ assembleProvider!!.configure {
doLast {
- variant.mappingFileProvider.get().files.forEach { mappingFile ->
+ mappingFileProvider.get().files.forEach { mappingFile ->
mappingFile.renameTo(File(mappingFile.parentFile, "${fileName}.txt"))
}
}
@@ -172,17 +175,19 @@ android {
}
gradle.projectsEvaluated {
- tasks.getByName("preBuild").dependsOn(
- "generateResourcesAndThemes",
- "checkEmojiKeyboard",
- "generatePhoneFormat",
- "updateExceptions"
- )
+ tasks.named("preBuild").configure {
+ dependsOn(
+ generateResourcesAndThemes,
+ checkEmojiKeyboard,
+ generatePhoneFormat,
+ updateExceptions,
+ )
+ }
Abi.VARIANTS.forEach { (_, variant) ->
- tasks.getByName("pre${variant.flavor[0].uppercaseChar() + variant.flavor.substring(1)}ReleaseBuild").let { task ->
- task.dependsOn("updateLanguages")
+ tasks.named("pre${variant.flavor[0].uppercaseChar() + variant.flavor.substring(1)}ReleaseBuild") {
+ dependsOn(updateLanguages)
if (!isExperimentalBuild) {
- task.dependsOn("validateApiTokens")
+ dependsOn(validateApiTokens)
}
}
}
diff --git a/build.gradle.kts b/build.gradle.kts
index bae8f36671..495c5038ea 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,17 +1 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
-
-buildscript {
- repositories {
- google()
- mavenCentral()
- maven(url = "https://jitpack.io")
- }
-}
-
-allprojects {
- repositories {
- google()
- mavenCentral()
- maven(url = "https://jitpack.io")
- }
-}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/me/vkryl/plugin/CMakePlugin.kt b/buildSrc/src/main/kotlin/me/vkryl/plugin/CMakePlugin.kt
index 31ca06ef4f..c170c087fa 100644
--- a/buildSrc/src/main/kotlin/me/vkryl/plugin/CMakePlugin.kt
+++ b/buildSrc/src/main/kotlin/me/vkryl/plugin/CMakePlugin.kt
@@ -16,71 +16,68 @@ import Config
import com.android.build.gradle.BaseExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
+import org.gradle.kotlin.dsl.getByType
open class CMakePlugin : Plugin {
- override fun apply (project: Project) {
- val androidExt = project.extensions.getByName("android")
-
- if (androidExt is BaseExtension) {
- androidExt.apply {
- externalNativeBuild {
- cmake {
- path("jni/CMakeLists.txt")
- }
+ override fun apply(project: Project) {
+ project.extensions.getByType().apply {
+ externalNativeBuild {
+ cmake {
+ path("jni/CMakeLists.txt")
}
- buildTypes {
- getByName("debug") {
- externalNativeBuild {
- cmake {
- val flags = arrayOf(
- "-w",
- "-Werror=return-type",
- "-ferror-limit=0",
- "-fno-exceptions",
+ }
+ buildTypes {
+ named("debug") {
+ externalNativeBuild {
+ cmake {
+ val flags = arrayOf(
+ "-w",
+ "-Werror=return-type",
+ "-ferror-limit=0",
+ "-fno-exceptions",
- "-O2",
- "-fno-omit-frame-pointer"
- )
- arguments(
- "-DANDROID_STL=c++_shared",
- "-DANDROID_PLATFORM=android-${Config.MIN_SDK_VERSION}",
- "-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON",
- "-DCMAKE_SKIP_RPATH=ON",
- "-DCMAKE_C_VISIBILITY_PRESET=hidden",
- "-DCMAKE_CXX_VISIBILITY_PRESET=hidden",
- "-DCMAKE_SHARED_LINKER_FLAGS=-Wl,--gc-sections,--icf=safe -Wl,--build-id=sha1",
- "-DCMAKE_C_FLAGS=-D_LARGEFILE_SOURCE=1 ${flags.joinToString(" ")}",
- "-DCMAKE_CXX_FLAGS=-std=c++17 ${flags.joinToString(" ")}"
- )
- }
+ "-O2",
+ "-fno-omit-frame-pointer",
+ )
+ arguments(
+ "-DANDROID_STL=c++_shared",
+ "-DANDROID_PLATFORM=android-${Config.MIN_SDK_VERSION}",
+ "-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON",
+ "-DCMAKE_SKIP_RPATH=ON",
+ "-DCMAKE_C_VISIBILITY_PRESET=hidden",
+ "-DCMAKE_CXX_VISIBILITY_PRESET=hidden",
+ "-DCMAKE_SHARED_LINKER_FLAGS=-Wl,--gc-sections,--icf=safe -Wl,--build-id=sha1",
+ "-DCMAKE_C_FLAGS=-D_LARGEFILE_SOURCE=1 ${flags.joinToString(" ")}",
+ "-DCMAKE_CXX_FLAGS=-std=c++17 ${flags.joinToString(" ")}",
+ )
}
}
+ }
- getByName("release") {
- externalNativeBuild {
- cmake {
- val flags = listOf(
- "-w",
- "-Werror=return-type",
- "-ferror-limit=0",
- "-fno-exceptions",
+ named("release") {
+ externalNativeBuild {
+ cmake {
+ val flags = listOf(
+ "-w",
+ "-Werror=return-type",
+ "-ferror-limit=0",
+ "-fno-exceptions",
- "-O3",
- "-finline-functions"
- )
+ "-O3",
+ "-finline-functions"
+ )
- arguments(
- "-DANDROID_STL=c++_shared",
- "-DANDROID_PLATFORM=android-${Config.MIN_SDK_VERSION}",
- "-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON",
- "-DCMAKE_SKIP_RPATH=ON",
- "-DCMAKE_C_VISIBILITY_PRESET=hidden",
- "-DCMAKE_CXX_VISIBILITY_PRESET=hidden",
- "-DCMAKE_SHARED_LINKER_FLAGS=-Wl,--gc-sections,--icf=safe -Wl,--build-id=sha1",
- "-DCMAKE_C_FLAGS=-D_LARGEFILE_SOURCE=1 ${flags.joinToString(" ")}",
- "-DCMAKE_CXX_FLAGS=-std=c++17 ${flags.joinToString(" ")}"
- )
- }
+ arguments(
+ "-DANDROID_STL=c++_shared",
+ "-DANDROID_PLATFORM=android-${Config.MIN_SDK_VERSION}",
+ "-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON",
+ "-DCMAKE_SKIP_RPATH=ON",
+ "-DCMAKE_C_VISIBILITY_PRESET=hidden",
+ "-DCMAKE_CXX_VISIBILITY_PRESET=hidden",
+ "-DCMAKE_SHARED_LINKER_FLAGS=-Wl,--gc-sections,--icf=safe -Wl,--build-id=sha1",
+ "-DCMAKE_C_FLAGS=-D_LARGEFILE_SOURCE=1 ${flags.joinToString(" ")}",
+ "-DCMAKE_CXX_FLAGS=-std=c++17 ${flags.joinToString(" ")}"
+ )
}
}
}
diff --git a/gradle.properties b/gradle.properties
index 8cd5a4cd97..9d1806ad65 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -17,5 +17,4 @@ android.nonFinalResIds=true
android.nonTransitiveRClass=true
org.gradle.jvmargs=-Xms1024m -Xmx8192m -Dkotlin.daemon.jvm.options\="-Xms1024m -Xmx8192M" -Dfile.encoding\=UTF-8
org.gradle.parallel=true
-android.defaults.buildfeatures.buildconfig=true
org.gradle.unsafe.configuration-cache=true
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 224131f7e5..b6e44f2402 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,3 +1,20 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ maven(url = "https://jitpack.io")
+ }
+}
+
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ maven(url = "https://jitpack.io")
+ }
+}
+
rootProject.name = "tgx"
include(
":tdlib",
From cdb052447eab2c2a00070ada331acde42e6b4e19 Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 6 Feb 2024 12:56:34 +0400
Subject: [PATCH 56/61] Remove rudimentary logging in `TdlibNotificationStyle`
---
.../thunderdog/challegram/telegram/TdlibNotificationStyle.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationStyle.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationStyle.java
index 4e16d07cf5..700ec65394 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationStyle.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibNotificationStyle.java
@@ -705,7 +705,6 @@ protected final int displayChildNotification (NotificationManagerCompat manager,
try {
if (Config.TEST_NOTIFICATION_PROBLEM_RESOLUTION)
throw new RuntimeException();
- Log.v("manager.notify(...) (1) isRebuild:%b isSummary:%b", isRebuild, isSummary);
manager.notify(notificationId, notification);
state = DISPLAY_STATE_OK;
} catch (Throwable t) {
From a3c4f6b025cc06aec8f6fd594094545b4074f25d Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 6 Feb 2024 14:48:57 +0400
Subject: [PATCH 57/61] Build system fixes and improvements
1. Updated AndroidX dependencies
2.Rely on `LibraryVersions.ANDROIDX_CORE` in application gradle file
3. Fixed some build warnings in various gradle files
4. Fixed missing `BuildConfig` in `leveldb` module
---
app/build.gradle.kts | 4 +++-
buildSrc/src/main/kotlin/Config.kt | 5 +++--
vkryl/leveldb | 2 +-
3 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 0a635e7917..1f462b996e 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,3 +1,5 @@
+@file:Suppress("UnstableApiUsage")
+
import com.android.build.gradle.internal.api.ApkVariantOutputImpl
import java.util.*
@@ -201,7 +203,7 @@ dependencies {
implementation(project(":vkryl:android"))
implementation(project(":vkryl:td"))
// AndroidX: https://developer.android.com/jetpack/androidx/versions
- implementation("androidx.core:core:1.12.0")
+ implementation("androidx.core:core:${LibraryVersions.ANDROIDX_CORE}")
implementation("androidx.activity:activity:1.8.2")
implementation("androidx.palette:palette:1.0.0")
implementation("androidx.recyclerview:recyclerview:1.3.2")
diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt
index e1eb6e4a76..54fd1e1e75 100644
--- a/buildSrc/src/main/kotlin/Config.kt
+++ b/buildSrc/src/main/kotlin/Config.kt
@@ -24,8 +24,8 @@ object Config {
object LibraryVersions {
const val MULTIDEX = "2.0.1"
const val DESUGAR = "1.1.5"
- const val ANDROIDX_CORE = "1.7.0"
- const val ANNOTATIONS = "1.3.0"
+ const val ANDROIDX_CORE = "1.12.0"
+ const val ANNOTATIONS = "1.7.1"
}
class AbiVariant (val flavor: String, vararg val filters: String = arrayOf(), val displayName: String = filters[0]) {
@@ -56,6 +56,7 @@ class AbiVariant (val flavor: String, vararg val filters: String = arrayOf(), va
}
}
+@Suppress("MemberVisibilityCanBePrivate")
object Abi {
const val UNIVERSAL = 0
const val ARMEABI_V7A = 1
diff --git a/vkryl/leveldb b/vkryl/leveldb
index 97bda2b641..c3f9266a96 160000
--- a/vkryl/leveldb
+++ b/vkryl/leveldb
@@ -1 +1 @@
-Subproject commit 97bda2b64138afe33fd4588e82de4e47fea108c4
+Subproject commit c3f9266a969bf82a9141e0447f94df0be767817c
From a34db3eb8df595ddd8421c5007cbbe34db9e620b Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 6 Feb 2024 14:52:48 +0400
Subject: [PATCH 58/61] Fix incorrect `drained_count` value in `VideoInfo` +
Remove rudimentary logging
---
app/jni/gif.cpp | 26 ++++++++++++--------------
version.properties | 2 +-
2 files changed, 13 insertions(+), 15 deletions(-)
diff --git a/app/jni/gif.cpp b/app/jni/gif.cpp
index cecf12175e..8c184e0535 100644
--- a/app/jni/gif.cpp
+++ b/app/jni/gif.cpp
@@ -132,8 +132,8 @@ struct VideoInfo {
bool had_invalid_frames = false;
bool eof_reached = false;
bool draining = false;
- size_t drained_count = 0;
size_t loop_count = 0;
+ size_t drained_count = 0;
AVPacket *packet = nullptr;
};
@@ -446,35 +446,33 @@ JNI_FUNC(jint, getVideoFrame, jlong ptr, jobject bitmap, jintArray data) {
if (info->draining) {
ret = avcodec_receive_frame(info->video_dec_ctx, info->frame);
if (ret == 0) {
- info->drained_count++;
+ if (info->loop_count == 0) { // First loop
+ info->drained_count++;
+ }
gotFrame = true;
break;
}
info->draining = false;
- if (info->loop_count == 0) {
- logv(TAG_GIF_LOADER, "avcodec_receive_frame drain mode finished for %s: %s (%zu frames, eof: %b)", info->path.c_str(),
- av_err2str(ret),
- info->drained_count,
- info->eof_reached);
- info->drained_count = 0;
- }
}
if (info->eof_reached) {
- if (info->loop_count == SIZE_T_MAX) {
- info->loop_count = 0;
- }
- info->loop_count++;
ret = avformat_seek_file(
info->fmt_ctx, -1,
std::numeric_limits::min(), 0,
std::numeric_limits::max(), 0
);
if (ret != 0) {
- loge(TAG_GIF_LOADER, "can't seek %s to start, %s",info->path.c_str(), av_err2str(ret));
+ loge(TAG_GIF_LOADER, "can't seek %s to start, %s, drained_count: %zu", info->path.c_str(),
+ av_err2str(ret),
+ info->drained_count);
fatalError = true;
break;
}
+ if (info->loop_count == SIZE_T_MAX) {
+ info->loop_count = 1;
+ } else {
+ info->loop_count++;
+ }
info->has_decoded_frames = false;
info->eof_reached = false;
hasLooped = true;
diff --git a/version.properties b/version.properties
index 5bff1bfd14..7154b7a5e6 100644
--- a/version.properties
+++ b/version.properties
@@ -4,7 +4,7 @@ version.major=0
# Anchor date point in app versioning
version.creation=873642600564
# Native bundle (/app/jni)
-version.jni=225.0.0
+version.jni=226.0.0
# LevelDB (/vkryl/leveldb)
version.leveldb=6.0.0
# Emoji (/app/src/main/assets/emoji)
From a318acf9c5c537171f0c6b1c80c5758012bf170a Mon Sep 17 00:00:00 2001
From: nikita-toropov <4970595+nikita-toropov@users.noreply.github.com>
Date: Tue, 6 Feb 2024 16:45:35 +0500
Subject: [PATCH 59/61] Chat Folders: Shareable folders & Redesign (#502)
(#543)
* Chat Folders: Shareable folders & Redesign
* Move all `ViewController.OPTION_COLOR_*` constants to `OptionColor` interface
* Code style fixes
* Cleaning up
* Disallow slide back gesture with unsaved changes on "Select Chats" screen
* Add checkbox animation in DoubleTextWrapper
* Change header type on "Edit Chat Folder Invite Link" screen
* Change header type on "Invite Link" popup
* Use circle done button on "Edit Chat Folder" screen
* Fix compose button blinking
* Use folder icon as default on icon selector panel
* Add offset before first header on "Edit Chat Folder" screen
* Fix ViewPagerTopView measuring
* Show progress & tooltips on done button on "Edit Chat Folder Invite Link" screen. Set input type for invite link name.
* Do not show premium lock icon for premium users
* Add dynamic bottom offset on "Edit Chat Folder" screen
* Add MainController.showFolderTooltip
* Do not use `import static` statements
* Add copyright comments
---
.../thunderdog/challegram/FileProvider.java | 8 +-
.../thunderdog/challegram/MainActivity.java | 3 +
.../java/org/thunderdog/challegram/U.java | 12 +-
.../component/base/SettingView.java | 29 +-
.../component/dialogs/ChatsAdapter.java | 258 +++++--
.../component/dialogs/ChatsViewHolder.java | 40 +-
.../challegram/component/user/UserView.java | 2 +-
.../thunderdog/challegram/config/Config.java | 1 +
.../org/thunderdog/challegram/core/Lang.java | 4 +
.../challegram/data/DoubleTextWrapper.java | 75 +-
.../org/thunderdog/challegram/data/TD.java | 20 +-
.../thunderdog/challegram/data/TGAvatars.java | 9 +-
.../challegram/data/TGCommentButton.java | 4 +-
.../challegram/data/TGReactions.java | 2 +-
.../challegram/emoji/EmojiSpan.java | 6 +-
.../challegram/navigation/EditHeaderView.java | 17 +-
.../challegram/navigation/OptionsLayout.java | 16 +-
.../navigation/OverlayButtonWrap.java | 6 +
.../navigation/TooltipOverlayView.java | 8 +-
.../challegram/navigation/ViewController.java | 7 +-
.../navigation/ViewPagerController.java | 6 +-
.../navigation/ViewPagerTopView.java | 236 ++++--
.../telegram/ChatFolderListener.java | 34 +
.../challegram/telegram/ChatFolderStyle.java | 4 +-
.../thunderdog/challegram/telegram/Tdlib.java | 83 ++-
.../challegram/telegram/TdlibListeners.java | 43 ++
.../telegram/TdlibSettingsManager.java | 46 +-
.../challegram/telegram/TdlibUi.java | 119 ++-
.../org/thunderdog/challegram/tool/Views.java | 10 +
.../challegram/ui/ChatFolderIconSelector.java | 51 +-
.../ui/ChatFolderInviteLinkController.java | 181 +++++
.../ChatFolderInviteLinkControllerPage.java | 553 ++++++++++++++
.../challegram/ui/ChatsController.java | 287 +++++---
.../challegram/ui/EditBaseController.java | 10 +
.../ui/EditChatFolderController.java | 689 +++++++++++++++---
.../EditChatFolderInviteLinkController.java | 555 ++++++++++++++
.../challegram/ui/FeatureToggles.java | 6 +-
.../thunderdog/challegram/ui/ListItem.java | 14 +-
.../challegram/ui/MainController.java | 397 ++++++++--
.../ui/MessageOptionsPagerController.java | 4 +-
.../challegram/ui/SelectChatsController.java | 48 +-
.../ui/SetSenderControllerPage.java | 4 +-
.../challegram/ui/SettingHolder.java | 84 ++-
.../challegram/ui/SettingsAdapter.java | 68 +-
.../ui/SettingsFoldersController.java | 371 +++++++---
.../challegram/ui/ShareController.java | 2 +-
.../challegram/util/EndIconModifier.java | 78 ++
.../thunderdog/challegram/util/Poller.java | 102 +++
.../challegram/util/PremiumLockModifier.java | 24 +
.../util/text/FormattedCounterAnimator.java | 16 +-
.../challegram/util/text/IconSpan.java | 65 ++
.../util/text/TextReplacementSpan.java | 21 +
.../challegram/v/ChatsRecyclerView.java | 8 +-
.../challegram/widget/BetterChatView.java | 2 +-
.../challegram/widget/ChartLayout.java | 8 +-
.../challegram/widget/PopupLayout.java | 21 +
.../challegram/widget/ScalableTextView.java | 8 +-
.../widget/SimplestCheckBoxHelper.java | 33 +-
.../challegram/widget/SuggestedChatsView.java | 149 ++++
.../challegram/widget/VerticalChatView.java | 2 +-
.../res/drawable/baseline_airplane_24.xml | 9 +
.../main/res/drawable/baseline_crown_24.xml | 9 +
.../res/drawable/baseline_filter_variant.xml | 9 +
.../res/drawable/baseline_folder_open_96.xml | 9 +
.../main/res/drawable/baseline_info_16.xml | 9 +
app/src/main/res/values/ids.xml | 9 +
app/src/main/res/values/strings.xml | 61 +-
67 files changed, 4406 insertions(+), 678 deletions(-)
create mode 100644 app/src/main/java/org/thunderdog/challegram/telegram/ChatFolderListener.java
create mode 100644 app/src/main/java/org/thunderdog/challegram/ui/ChatFolderInviteLinkController.java
create mode 100644 app/src/main/java/org/thunderdog/challegram/ui/ChatFolderInviteLinkControllerPage.java
create mode 100644 app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderInviteLinkController.java
create mode 100644 app/src/main/java/org/thunderdog/challegram/util/EndIconModifier.java
create mode 100644 app/src/main/java/org/thunderdog/challegram/util/Poller.java
create mode 100644 app/src/main/java/org/thunderdog/challegram/util/PremiumLockModifier.java
create mode 100644 app/src/main/java/org/thunderdog/challegram/util/text/IconSpan.java
create mode 100644 app/src/main/java/org/thunderdog/challegram/util/text/TextReplacementSpan.java
create mode 100644 app/src/main/java/org/thunderdog/challegram/widget/SuggestedChatsView.java
create mode 100644 app/src/main/res/drawable/baseline_airplane_24.xml
create mode 100644 app/src/main/res/drawable/baseline_crown_24.xml
create mode 100644 app/src/main/res/drawable/baseline_filter_variant.xml
create mode 100644 app/src/main/res/drawable/baseline_folder_open_96.xml
create mode 100755 app/src/main/res/drawable/baseline_info_16.xml
diff --git a/app/src/main/java/org/thunderdog/challegram/FileProvider.java b/app/src/main/java/org/thunderdog/challegram/FileProvider.java
index c9fe94ebcf..86d4931ba4 100644
--- a/app/src/main/java/org/thunderdog/challegram/FileProvider.java
+++ b/app/src/main/java/org/thunderdog/challegram/FileProvider.java
@@ -14,9 +14,6 @@
*/
package org.thunderdog.challegram;
-import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
-
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
@@ -42,6 +39,7 @@
import org.drinkmore.Tracer;
import org.thunderdog.challegram.telegram.TdlibAccount;
import org.thunderdog.challegram.telegram.TdlibManager;
+import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
@@ -326,8 +324,8 @@ private static PathStrategy parsePathStrategy(Context context, String authority)
}
int type;
- while ((type = in.next()) != END_DOCUMENT) {
- if (type == START_TAG) {
+ while ((type = in.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG) {
final String tag = in.getName();
final String name = in.getAttributeValue(null, ATTR_NAME);
diff --git a/app/src/main/java/org/thunderdog/challegram/MainActivity.java b/app/src/main/java/org/thunderdog/challegram/MainActivity.java
index 14ed2bbb20..2b3151aae4 100644
--- a/app/src/main/java/org/thunderdog/challegram/MainActivity.java
+++ b/app/src/main/java/org/thunderdog/challegram/MainActivity.java
@@ -88,6 +88,7 @@
import org.thunderdog.challegram.ui.SettingsPrivacyController;
import org.thunderdog.challegram.ui.SettingsPrivacyKeyController;
import org.thunderdog.challegram.ui.SettingsThemeController;
+import org.thunderdog.challegram.ui.EditChatFolderInviteLinkController;
import org.thunderdog.challegram.unsorted.Settings;
import org.thunderdog.challegram.util.Crash;
import org.thunderdog.challegram.widget.GearView;
@@ -1245,6 +1246,8 @@ private static ViewController> restoreController (BaseActivity context, Tdlib
restore = new SettingsFoldersController(context, tdlib);
} else if (id == R.id.controller_editChatFolders) {
restore = new EditChatFolderController(context, tdlib);
+ } else if (id == R.id.controller_editChatFolderInviteLink) {
+ restore = new EditChatFolderInviteLinkController(context, tdlib);
} else if (id == R.id.controller_bug_killer) {
restore = new SettingsBugController(context, tdlib);
} else {
diff --git a/app/src/main/java/org/thunderdog/challegram/U.java b/app/src/main/java/org/thunderdog/challegram/U.java
index c120c2bb56..20f51592db 100644
--- a/app/src/main/java/org/thunderdog/challegram/U.java
+++ b/app/src/main/java/org/thunderdog/challegram/U.java
@@ -107,7 +107,6 @@
import org.thunderdog.challegram.core.Background;
import org.thunderdog.challegram.core.Lang;
import org.thunderdog.challegram.data.TD;
-import org.thunderdog.challegram.emoji.EmojiSpan;
import org.thunderdog.challegram.loader.ImageGalleryFile;
import org.thunderdog.challegram.loader.ImageLoader;
import org.thunderdog.challegram.loader.ImageReader;
@@ -127,6 +126,7 @@
import org.thunderdog.challegram.ui.TextController;
import org.thunderdog.challegram.util.AppBuildInfo;
import org.thunderdog.challegram.util.Permissions;
+import org.thunderdog.challegram.util.text.TextReplacementSpan;
import org.thunderdog.challegram.widget.NoScrollTextView;
import java.io.BufferedReader;
@@ -2056,7 +2056,7 @@ public static float measureEmojiText (@Nullable CharSequence in, final int start
}
final Spannable s = (Spannable) in;
- EmojiSpan[] spans = s.getSpans(start, end, EmojiSpan.class);
+ TextReplacementSpan[] spans = s.getSpans(start, end, TextReplacementSpan.class);
if (spans == null || spans.length == 0) {
return measureText(in, start, end, p);
}
@@ -2069,7 +2069,7 @@ public static float measureEmojiText (@Nullable CharSequence in, final int start
float textWidth = 0;
int startIndex = start;
- for (EmojiSpan span : spans) {
+ for (TextReplacementSpan span : spans) {
int spanStart = s.getSpanStart(span);
if (startIndex < spanStart) {
textWidth += measureText(in, startIndex, spanStart, p);
@@ -3686,4 +3686,10 @@ public static Set unmodifiableTreeSetOf (long[] array) {
}
return Collections.unmodifiableSet(set);
}
+
+ public static long[] concat (long[] first, long[] second) {
+ long[] result = Arrays.copyOf(first, first.length + second.length);
+ System.arraycopy(second, 0, result, first.length, second.length);
+ return result;
+ }
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/base/SettingView.java b/app/src/main/java/org/thunderdog/challegram/component/base/SettingView.java
index db04307b3d..722e7d333b 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/base/SettingView.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/base/SettingView.java
@@ -91,6 +91,7 @@ public class SettingView extends FrameLayoutFix implements FactorAnimator.Target
public static final int TYPE_SETTING_INACTIVE = 0x04;
public static final int TYPE_INFO_MULTILINE = 0x05;
public static final int TYPE_INFO_COMPACT = 0x07;
+ public static final int TYPE_INFO_SUPERCOMPACT = 0x08;
private static final int FLAG_CENTER_ICON = 1 << 3;
private static final int FLAG_DATA_SUBTITLE = 1 << 5;
@@ -170,7 +171,8 @@ public void setType (int type) {
break;
}
case TYPE_SETTING:
- case TYPE_SETTING_INACTIVE: {
+ case TYPE_SETTING_INACTIVE:
+ case TYPE_INFO_SUPERCOMPACT: {
setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Screen.dp(55f)));
break;
}
@@ -340,8 +342,8 @@ public void setIconOverlay (IconOverlay overlay) {
this.overlay = overlay;
}
- public void setCenterIcon () {
- flags |= FLAG_CENTER_ICON;
+ public void setCenterIcon (boolean centerIcon) {
+ flags = BitwiseUtils.setFlag(flags, FLAG_CENTER_ICON, centerIcon);
}
private int lastIconResource;
@@ -590,7 +592,10 @@ private void buildLayout (int totalWidth, int totalHeight) {
availWidth -= emojiStatusHelper.getWidth(Screen.dp(6));
- if (type == TYPE_INFO_COMPACT) {
+ if (type == TYPE_INFO_SUPERCOMPACT) {
+ boolean hasName = !StringUtils.isEmpty(swapDataAndName ? itemData : itemName);
+ pTop = Screen.dp((hasName ? 10f : 21f) + 13f);
+ } else if (type == TYPE_INFO_COMPACT) {
pTop = Screen.dp(15f + 13f);
} else {
pTop = Screen.dp(21f + 13f);
@@ -604,7 +609,7 @@ private void buildLayout (int totalWidth, int totalHeight) {
displayItemName = itemName;
}
- if (type == TYPE_INFO || type == TYPE_INFO_COMPACT || type == TYPE_INFO_MULTILINE) {
+ if (type == TYPE_INFO || type == TYPE_INFO_COMPACT || type == TYPE_INFO_SUPERCOMPACT || type == TYPE_INFO_MULTILINE) {
pDataLeft = pLeft;
pDataTop = pTop;
pTop = pTop + Screen.dp(20f);
@@ -766,9 +771,19 @@ public void setUnreadCounter (int unreadCount, boolean muted, boolean animated)
counter.setCount(unreadCount, muted, animated);
}
+ private @Nullable TooltipOverlayView.LocationProvider tooltipLocationProvider;
+
+ public void setTooltipLocationProvider (@Nullable TooltipOverlayView.LocationProvider tooltipLocationProvider) {
+ this.tooltipLocationProvider = tooltipLocationProvider;
+ }
+
@Override
public void getTargetBounds (View targetView, Rect outRect) {
- if (type == TYPE_INFO || type == TYPE_INFO_COMPACT || (type == TYPE_INFO_MULTILINE && text == null)) {
+ if (tooltipLocationProvider != null) {
+ tooltipLocationProvider.getTargetBounds(targetView, outRect);
+ return;
+ }
+ if (type == TYPE_INFO || type == TYPE_INFO_COMPACT || type == TYPE_INFO_SUPERCOMPACT || (type == TYPE_INFO_MULTILINE && text == null)) {
if (itemData != null) {
int dataTop = (int) (pDataTop - Screen.dp(13f));
Paint.FontMetricsInt fm = Paints.getTextPaint16().getFontMetricsInt();
@@ -864,7 +879,7 @@ protected void onDraw (Canvas c) {
final int dataColor = defaultTextColor();
- if (type == TYPE_INFO || type == TYPE_INFO_COMPACT || (type == TYPE_INFO_MULTILINE && text == null)) {
+ if (type == TYPE_INFO || type == TYPE_INFO_COMPACT || type == TYPE_INFO_SUPERCOMPACT || (type == TYPE_INFO_MULTILINE && text == null)) {
if (displayItemName != null) {
int subtitleColor = Theme.getColor(dataColorId != 0 ? dataColorId : ColorId.textLight);
if ((flags & FLAG_DATA_SUBTITLE) != 0) {
diff --git a/app/src/main/java/org/thunderdog/challegram/component/dialogs/ChatsAdapter.java b/app/src/main/java/org/thunderdog/challegram/component/dialogs/ChatsAdapter.java
index 40ca25f81e..e5876b8a70 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/dialogs/ChatsAdapter.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/dialogs/ChatsAdapter.java
@@ -18,6 +18,7 @@
import android.view.ViewGroup;
import android.widget.TextView;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@@ -31,8 +32,10 @@
import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.ui.ChatsController;
import org.thunderdog.challegram.v.ChatsRecyclerView;
+import org.thunderdog.challegram.widget.SuggestedChatsView;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
@@ -41,12 +44,15 @@
import me.vkryl.td.ChatPosition;
public class ChatsAdapter extends RecyclerView.Adapter {
+
+ private static final int ARCHIVE_INDEX = 0;
+
private final ChatsController context;
private final ArrayList chats;
private int totalRes = R.string.xChats;
- private LinearLayoutManager layoutManager;
+ private final LinearLayoutManager layoutManager;
public ChatsAdapter (ChatsController context, LinearLayoutManager layoutManager) {
this.context = context;
@@ -68,11 +74,33 @@ public void setTotalRes (@StringRes int totalRes) {
this.totalRes = totalRes;
}
+ @NonNull
@Override
- public ChatsViewHolder onCreateViewHolder (ViewGroup parent, int viewType) {
+ public ChatsViewHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) {
return ChatsViewHolder.create(context.context(), context.tdlib(), viewType, context.isInForceTouchMode() ? null : context, context, context);
}
+ private long[] suggestedChatIds = ArrayUtils.EMPTY_LONGS;
+
+ public void setSuggestedChatIds (long[] chatIds) {
+ chatIds = chatIds != null ? chatIds : ArrayUtils.EMPTY_LONGS;
+ boolean notifyInserted = !hasSuggestedChats() && chatIds.length > 0;
+ boolean notifyRemoved = hasSuggestedChats() && chatIds.length == 0;
+ boolean notifyChanged = hasSuggestedChats() && chatIds.length > 0 && !Arrays.equals(suggestedChatIds, chatIds);
+ this.suggestedChatIds = chatIds;
+ if (notifyInserted) {
+ notifyItemInserted(0);
+ } else if (notifyRemoved) {
+ notifyItemRemoved(0);
+ } else if (notifyChanged) {
+ notifyItemChanged(0);
+ }
+ }
+
+ public long[] getSuggestedChatIds () {
+ return suggestedChatIds;
+ }
+
private boolean needArchive, hasArchive;
public void setNeedArchive (boolean needArchive) {
@@ -91,11 +119,11 @@ public void checkArchive () {
if (this.hasArchive != hasArchive) {
this.hasArchive = hasArchive;
if (hasArchive) {
- chats.add(0, newArchive());
- notifyChatAppeared(-1, 0);
+ chats.add(ARCHIVE_INDEX, newArchive());
+ notifyChatAppeared(-1, getItemPositionByChatIndex(ARCHIVE_INDEX));
} else {
- chats.remove(0);
- notifyItemRemoved(0);
+ chats.remove(ARCHIVE_INDEX);
+ notifyItemRemoved(getItemPositionByChatIndex(ARCHIVE_INDEX));
}
invalidateAttachedItemDecorations();
}
@@ -111,7 +139,7 @@ private TGChat newArchive () {
public int updateArchive (int reason) {
if (this.hasArchive) {
- TGChat chat = getChatAt(0);
+ TGChat chat = getChatAt(ARCHIVE_INDEX);
if (chat != null && chat.isArchive()) {
switch (reason) {
case ARCHIVE_UPDATE_COUNTER:
@@ -125,7 +153,7 @@ public int updateArchive (int reason) {
chat.onArchiveChanged();
break;
}
- return 0;
+ return getItemPositionByChatIndex(ARCHIVE_INDEX);
}
}
return -1;
@@ -135,16 +163,18 @@ public int updateArchive (int reason) {
public void onViewRecycled (ChatsViewHolder holder) {
if (holder.getItemViewType() == VIEW_TYPE_CHAT) {
((ChatView) holder.itemView).setChat(null);
+ } else if (holder.getItemViewType() == VIEW_TYPE_SUGGESTED_CHATS) {
+ ((SuggestedChatsView) holder.itemView).setChatIds(ArrayUtils.EMPTY_LONGS, false);
}
}
@Override
- public void onBindViewHolder (ChatsViewHolder holder, int position) {
+ public void onBindViewHolder (@NonNull ChatsViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case VIEW_TYPE_CHAT: {
- TGChat currentChat = chats.get(position);
- TGChat nextChat = position + 1 < chats.size() ? chats.get(position + 1) : null;
+ TGChat currentChat = getChatByItemPosition(position);
+ TGChat nextChat = getChatByItemPosition(position + 1);
holder.setChat(currentChat, false, nextChat != null && currentChat.isPinnedOrSpecial() && !nextChat.isPinnedOrSpecial(), context.isChatSelected(currentChat));
break;
}
@@ -162,6 +192,10 @@ public void onBindViewHolder (ChatsViewHolder holder, int position) {
((TextView) holder.itemView).setText(context.isEndReached() ? Lang.getString(R.string.NoChats) : "");
break;
}
+ case VIEW_TYPE_SUGGESTED_CHATS: {
+ holder.setChatIds(suggestedChatIds);
+ break;
+ }
}
}
@@ -176,6 +210,10 @@ public void onViewAttachedToWindow (ChatsViewHolder holder) {
case VIEW_TYPE_INFO: {
break;
}
+ case VIEW_TYPE_SUGGESTED_CHATS: {
+ ((SuggestedChatsView) holder.itemView).attach();
+ break;
+ }
}
}
@@ -190,38 +228,114 @@ public void onViewDetachedFromWindow (ChatsViewHolder holder) {
case VIEW_TYPE_INFO: {
break;
}
+ case VIEW_TYPE_SUGGESTED_CHATS: {
+ ((SuggestedChatsView) holder.itemView).detach();
+ break;
+ }
}
}
@Override
public int getItemCount () {
- return chats.isEmpty()/* && canLoadMore*/ ? 0 : chats.size() + 1;
+ int itemCount = 0;
+ if (hasSuggestedChats()) {
+ itemCount++;
+ }
+ if (hasChats()) {
+ itemCount += chats.size() + 1;
+ }
+ return itemCount;
+ }
+
+ public int getChatCount () {
+ return chats.size();
}
public boolean hasChats () {
return !chats.isEmpty();
}
+ public boolean hasSuggestedChats () {
+ return suggestedChatIds.length > 0;
+ }
+
+ @IntDef({VIEW_TYPE_CHAT, VIEW_TYPE_INFO, VIEW_TYPE_EMPTY, VIEW_TYPE_SUGGESTED_CHATS})
+ public @interface ViewType {
+ }
+
public static final int VIEW_TYPE_CHAT = 0;
public static final int VIEW_TYPE_INFO = 1;
public static final int VIEW_TYPE_EMPTY = 2;
+ public static final int VIEW_TYPE_SUGGESTED_CHATS = 3;
@Override
public int getItemViewType (int position) {
- return chats.isEmpty() ? VIEW_TYPE_EMPTY : position == chats.size() ? VIEW_TYPE_INFO : VIEW_TYPE_CHAT;
+ if (hasSuggestedChats() && position == 0) {
+ return VIEW_TYPE_SUGGESTED_CHATS;
+ }
+ if (hasChats()) {
+ int chatIndex = getChatIndexByItemPosition(position);
+ if (chatIndex >= 0 && chatIndex < chats.size()) {
+ return VIEW_TYPE_CHAT;
+ }
+ if (chatIndex == chats.size()) {
+ return VIEW_TYPE_INFO;
+ }
+ throw new IllegalArgumentException("position = " + position);
+ }
+ return VIEW_TYPE_EMPTY;
}
public void updateInfo () {
- if (!chats.isEmpty()) {
- notifyItemChanged(chats.size());
+ if (hasChats()) {
+ notifyItemChanged(getInfoItemPosition());
}
}
- public TGChat getChatAt (int index) {
+ private TGChat getChatAt (int index) {
return index >= 0 && index < chats.size() ? chats.get(index) : null;
}
- public int indexOfChat (long chatId) {
+ public int getFirstChatItemPosition () {
+ return hasChats() ? getItemPositionByChatIndex(0) : -1;
+ }
+
+ public int getLastChatItemPosition () {
+ return hasChats() ? getItemPositionByChatIndex(getChatCount() - 1) : -1;
+ }
+
+ public int getArchiveItemPosition () {
+ return hasChats() && hasArchive() ? getItemPositionByChatIndex(ARCHIVE_INDEX) : -1;
+ }
+
+ public int getInfoItemPosition () {
+ return hasChats() ? getItemPositionByChatIndex(getChatCount()) : -1;
+ }
+
+ public int getItemPositionByChatIndex (int chatIndex) {
+ if (chatIndex == -1) {
+ return -1;
+ }
+ return hasSuggestedChats() ? chatIndex + 1 : chatIndex;
+ }
+
+ public int getChatIndexByItemPosition (int itemPosition) {
+ if (itemPosition == RecyclerView.NO_POSITION) {
+ return -1;
+ }
+ return hasSuggestedChats() ? itemPosition - 1 : itemPosition;
+ }
+
+ public int findChatItemPosition (long chatId) {
+ return getItemPositionByChatIndex(indexOfChat(chatId));
+ }
+
+ public TGChat getChatByItemPosition (int itemPosition) {
+ int index = getChatIndexByItemPosition(itemPosition);
+ return index != -1 ? getChatAt(index) : null;
+ }
+
+ private int indexOfChat (long chatId) {
if (chatId == 0)
return -1;
int i = 0;
@@ -234,11 +348,7 @@ public int indexOfChat (long chatId) {
return -1;
}
- public int indexOfChat (TGChat chat) {
- return chats.indexOf(chat);
- }
-
- public int indexOfSecretChat (int secretChatId) {
+ private int indexOfSecretChat (int secretChatId) {
int i = 0;
for (TGChat chat : chats) {
if (chat.isSecretChat() && chat.getSecretChatId() == secretChatId) {
@@ -254,7 +364,7 @@ public void addMore (TGChat[] data) {
return;
int addedItemCount = 0;
int atIndex = chats.size();
- TGChat lastChat = !this.chats.isEmpty() ? this.chats.get(atIndex - 1) : null;
+ TGChat lastChat = getChatAt(atIndex - 1);
if (atIndex == 0 && needArchive) {
chats.ensureCapacity(atIndex + data.length + 1);
chats.add(newArchive());
@@ -273,9 +383,10 @@ public void addMore (TGChat[] data) {
}
}
if (addedItemCount > 0) {
- notifyItemRangeInserted(atIndex, addedItemCount);
+ int positionStart = getItemPositionByChatIndex(atIndex);
+ notifyItemRangeInserted(positionStart, addedItemCount);
if (firstAddedItem != null && lastChat != null && lastChat.isPinnedOrSpecial() != firstAddedItem.isPinnedOrSpecial()) {
- notifyItemChanged(atIndex - 1);
+ notifyItemChanged(positionStart - 1);
}
}
}
@@ -283,7 +394,7 @@ public void addMore (TGChat[] data) {
public int updateMessageInteractionInfo (long chatId, long messageId, @Nullable TdApi.MessageInteractionInfo interactionInfo) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateMessageInteractionInfo(chatId, messageId, interactionInfo)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -291,7 +402,7 @@ public int updateMessageInteractionInfo (long chatId, long messageId, @Nullable
public int updateMessageContent (long chatId, long messageId, TdApi.MessageContent newContent) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateMessageContent(chatId, messageId, newContent)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -299,7 +410,7 @@ public int updateMessageContent (long chatId, long messageId, TdApi.MessageConte
public int updateMessagesDeleted (long chatId, long[] messageIds) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateMessagesDeleted(chatId, messageIds)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -307,7 +418,7 @@ public int updateMessagesDeleted (long chatId, long[] messageIds) {
public int updateChatReadInbox (long chatId, final long lastReadInboxMessageId, final int unreadCount) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateChatReadInbox(chatId, lastReadInboxMessageId, unreadCount)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -315,7 +426,7 @@ public int updateChatReadInbox (long chatId, final long lastReadInboxMessageId,
public int updateChatUnreadReactionCount (long chatId, int unreadReactionCount) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateChatUnreadReactionCount(chatId, unreadReactionCount)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -323,7 +434,7 @@ public int updateChatUnreadReactionCount (long chatId, int unreadReactionCount)
public int updateChatUnreadMentionCount (long chatId, int unreadMentionCount) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateChatUnreadMentionCount(chatId, unreadMentionCount)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -331,7 +442,7 @@ public int updateChatUnreadMentionCount (long chatId, int unreadMentionCount) {
public int updateChatHasScheduledMessages (long chatId, boolean hasScheduledMessages) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateChatHasScheduledMessages(chatId, hasScheduledMessages)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -339,7 +450,7 @@ public int updateChatHasScheduledMessages (long chatId, boolean hasScheduledMess
public int updateChatDraftMessage (long chatId, TdApi.DraftMessage draftMessage) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateDraftMessage(chatId, draftMessage)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -347,7 +458,7 @@ public int updateChatDraftMessage (long chatId, TdApi.DraftMessage draftMessage)
public int updateChatReadOutbox (long chatId, final long lastReadOutboxMessageId) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateChatReadOutbox(chatId, lastReadOutboxMessageId)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -356,7 +467,7 @@ public void updateUser (ChatsRecyclerView recyclerView, TdApi.User user) {
int i = 0;
for (TGChat chat : chats) {
if (chat.updateUser(user) || chat.checkOnline()) {
- recyclerView.invalidateViewAt(i);
+ recyclerView.invalidateViewAt(getItemPositionByChatIndex(i));
}
i++;
}
@@ -367,14 +478,14 @@ public int updateUserStatus (long userId, int startIndex) {
int index = 0;
for (TGChat chat : chats) {
if (chat.getChatUserId() == userId && chat.checkOnline())
- return index;
+ return getItemPositionByChatIndex(index);
index++;
}
} else {
for (int index = startIndex; index < chats.size(); index++) {
TGChat chat = chats.get(index);
if (chat.getChatUserId() == userId && chat.checkOnline()) {
- return index;
+ return getItemPositionByChatIndex(index);
}
}
}
@@ -384,7 +495,7 @@ public int updateUserStatus (long userId, int startIndex) {
public int updateChatTitle (long chatId, String title) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateChatTitle(chatId, title)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -392,7 +503,7 @@ public int updateChatTitle (long chatId, String title) {
public int updateChatPermissions (long chatId, TdApi.ChatPermissions permissions) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateChatPermissions(chatId, permissions)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -400,7 +511,7 @@ public int updateChatPermissions (long chatId, TdApi.ChatPermissions permissions
public int updateChatClientData (long chatId, String clientData) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateChatClientData(chatId, clientData)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -408,7 +519,7 @@ public int updateChatClientData (long chatId, String clientData) {
public int updateChatMarkedAsUnread (long chatId, boolean isMarkedAsUnread) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateMarkedAsUnread(chatId, isMarkedAsUnread)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -417,7 +528,7 @@ public int updateChatTopMessage (long chatId, TdApi.Message topMessage) {
int index = indexOfChat(chatId);
if (index != -1) {
chats.get(index).updateTopMessage(chatId, topMessage);
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -456,15 +567,17 @@ public int getHeaderChatCount (boolean includeSponsor, @Nullable ArrayList
}
public void movePinnedChat (int fromPosition, int toPosition) {
- TGChat fromChat = getChatAt(fromPosition);
- TGChat toChat = getChatAt(toPosition);
+ TGChat fromChat = getChatByItemPosition(fromPosition);
+ TGChat toChat = getChatByItemPosition(toPosition);
if (fromChat == null || !fromChat.isPinned() || toChat == null || !toChat.isPinned()) {
return;
}
ArrayList chatIds = new ArrayList<>();
int promotedCount = getHeaderChatCount(true, chatIds);
promotedCount -= chatIds.size();
- ArrayUtils.move(chatIds, fromPosition - promotedCount, toPosition - promotedCount);
+ int fromIndex = getChatIndexByItemPosition(fromPosition);
+ int toIndex = getChatIndexByItemPosition(toPosition);
+ ArrayUtils.move(chatIds, fromIndex - promotedCount, toIndex - promotedCount);
context.tdlib().client().send(new TdApi.SetPinnedChats(context.chatList(), ArrayUtils.asArray(chatIds)), context.tdlib().okHandler());
}
@@ -478,7 +591,7 @@ public int refreshLastMessage (long chatId, long messageId, boolean needRebuild)
if (needRebuild) {
chat.setText();
}
- return index;
+ return getItemPositionByChatIndex(index);
}
}
return -1;
@@ -487,7 +600,7 @@ public int refreshLastMessage (long chatId, long messageId, boolean needRebuild)
public int updateMessageSendSucceeded (TdApi.Message message, long oldMessageId) {
int index = indexOfChat(message.chatId);
if (index != -1 && chats.get(index).updateMessageSendSucceeded(message, oldMessageId)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -530,22 +643,22 @@ private boolean needShadowDecoration (TGChat a, TGChat b) {
}
public int removeChatById (TdApi.Chat chat, int fromIndex, Tdlib.ChatChange changeInfo) {
- final int position = hasArchive ? fromIndex + 1 : fromIndex;
- if (position != -1) {
- TGChat removedChat = removeChat(position);
+ final int chatIndex = hasArchive ? fromIndex + 1 : fromIndex;
+ if (chatIndex != -1) {
+ TGChat removedChat = removeChat(chatIndex);
if (removedChat.getChatId() != chat.id)
throw new IllegalStateException();
- TGChat prevChat = getChatAt(position - 1);
- TGChat nextChat = getChatAt(position);
+ TGChat prevChat = getChatAt(chatIndex - 1);
+ TGChat nextChat = getChatAt(chatIndex);
boolean invalidateDecorations = changeInfo.metadataChanged() ||
needShadowDecoration(prevChat, removedChat) ||
needShadowDecoration(nextChat, removedChat);
removedChat.updateChatPosition(chat.id, changeInfo.position, changeInfo.sourceChanged(), changeInfo.pinStateChanged());
- notifyItemRemoved(position);
- notifyItemChanged(chats.size());
+ notifyItemRemoved(getItemPositionByChatIndex(chatIndex));
+ notifyItemChanged(getInfoItemPosition());
context.checkListState();
return invalidateDecorations || needShadowDecoration(prevChat, nextChat) ? ORDER_INVALIDATE_DECORATIONS : 0;
}
@@ -576,26 +689,28 @@ public int moveChat (TdApi.Chat chat, int fromIndex, int toIndex, Tdlib.ChatChan
int flags = invalidateDecorations ? ORDER_INVALIDATE_DECORATIONS : 0;
if (fromIndex != toIndex) {
flags |= ORDER_REMAIN_SCROLL;
- notifyChatAppeared(fromIndex, toIndex);
+ int fromPosition = getItemPositionByChatIndex(fromIndex);
+ int toPosition = getItemPositionByChatIndex(toIndex);
+ notifyChatAppeared(fromPosition, toPosition);
}
return flags;
}
public int addChat (TdApi.Chat chat, int atIndex, Tdlib.ChatChange changeInfo) {
TGChat newChat = new TGChat(context.getParentOrSelf(), context.chatList(), chat, false);
- int position = hasArchive ? atIndex + 1 : atIndex;
+ int index = hasArchive ? atIndex + 1 : atIndex;
newChat.makeMeasures();
int flags = changeInfo.metadataChanged() ? ORDER_INVALIDATE_DECORATIONS : 0;
- addChat(position, newChat);
- notifyChatAppeared(-1, position);
- notifyItemChanged(chats.size());
+ addChat(index, newChat);
+ notifyChatAppeared(-1, getItemPositionByChatIndex(index));
+ notifyItemChanged(getInfoItemPosition());
context.checkListState();
return flags;
}
public int updateChat (TdApi.Chat chat, int index, Tdlib.ChatChange changeInfo) {
- final int position = hasArchive ? index + 1 : index;
- TGChat parsedChat = chats.get(position);
+ final int chatIndex = hasArchive ? index + 1 : index;
+ TGChat parsedChat = chats.get(chatIndex);
if (parsedChat.getChatId() != chat.id)
throw new IllegalStateException();
parsedChat.updateChatPosition(chat.id, changeInfo.position, changeInfo.sourceChanged(), changeInfo.pinStateChanged());
@@ -605,7 +720,7 @@ public int updateChat (TdApi.Chat chat, int index, Tdlib.ChatChange changeInfo)
public int updateChatPhoto (long chatId, TdApi.ChatPhotoInfo photo) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateChatPhoto(chatId, photo)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -613,7 +728,7 @@ public int updateChatPhoto (long chatId, TdApi.ChatPhotoInfo photo) {
public int updateChatSettings (long chatId, final TdApi.ChatNotificationSettings settings) {
int index = indexOfChat(chatId);
if (index != -1 && chats.get(index).updateChatSettings(chatId, settings)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -625,7 +740,7 @@ public void updateNotificationSettings (TdApi.NotificationSettingsScope scope, T
public int updateSecretChat (TdApi.SecretChat secretChat) {
int index = indexOfSecretChat(secretChat.id);
if (index != -1 && chats.get(index).updateSecretChat(secretChat)) {
- return index;
+ return getItemPositionByChatIndex(index);
}
return -1;
}
@@ -648,7 +763,7 @@ public TGChat getLastChat () {
// Adapter state
- private void notifyChatAppeared (int fromIndex, int atIndex) {
+ private void notifyChatAppeared (int fromPosition, int toPosition) {
int firstItem = layoutManager.findFirstVisibleItemPosition();
int offset;
if (firstItem != -1) {
@@ -657,10 +772,10 @@ private void notifyChatAppeared (int fromIndex, int atIndex) {
} else {
offset = 0;
}
- if (fromIndex == -1) {
- notifyItemInserted(atIndex);
+ if (fromPosition == -1) {
+ notifyItemInserted(toPosition);
} else {
- notifyItemMoved(fromIndex, atIndex);
+ notifyItemMoved(fromPosition, toPosition);
}
if (firstItem != -1) {
layoutManager.scrollToPositionWithOffset(firstItem, offset);
@@ -671,6 +786,13 @@ public void notifyAllChanged () {
notifyItemRangeChanged(0, getItemCount());
}
+ public void notifyLastItemChanged () {
+ int itemCount = getItemCount();
+ if (itemCount > 0) {
+ notifyItemChanged(itemCount - 1);
+ }
+ }
+
// Search mode
private ArrayList attachedToRecyclers;
diff --git a/app/src/main/java/org/thunderdog/challegram/component/dialogs/ChatsViewHolder.java b/app/src/main/java/org/thunderdog/challegram/component/dialogs/ChatsViewHolder.java
index c370b7ade9..6a2b948ee8 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/dialogs/ChatsViewHolder.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/dialogs/ChatsViewHolder.java
@@ -22,8 +22,12 @@
import android.widget.TextView;
import androidx.annotation.Nullable;
+import androidx.annotation.Px;
+import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;
+import org.thunderdog.challegram.R;
+import org.thunderdog.challegram.component.dialogs.ChatsAdapter.ViewType;
import org.thunderdog.challegram.core.Lang;
import org.thunderdog.challegram.data.TGChat;
import org.thunderdog.challegram.navigation.ViewController;
@@ -33,15 +37,32 @@
import org.thunderdog.challegram.tool.Fonts;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.ui.ChatsController;
+import org.thunderdog.challegram.ui.ListItem;
+import org.thunderdog.challegram.ui.SettingHolder;
+import org.thunderdog.challegram.unsorted.Settings;
import org.thunderdog.challegram.widget.BaseView;
import org.thunderdog.challegram.widget.ListInfoView;
import org.thunderdog.challegram.widget.NoScrollTextView;
+import org.thunderdog.challegram.widget.SuggestedChatsView;
public class ChatsViewHolder extends RecyclerView.ViewHolder {
public ChatsViewHolder (View itemView) {
super(itemView);
}
+ public static @Px int measureHeightForType (@ViewType int viewType) {
+ switch (viewType) {
+ case ChatsAdapter.VIEW_TYPE_CHAT:
+ return ChatView.getViewHeight(Settings.instance().getChatListMode());
+ case ChatsAdapter.VIEW_TYPE_INFO:
+ return SettingHolder.measureHeightForType(ListItem.TYPE_LIST_INFO_VIEW);
+ case ChatsAdapter.VIEW_TYPE_SUGGESTED_CHATS:
+ return Screen.dp(SuggestedChatsView.DEFAULT_HEIGHT_DP);
+ default:
+ throw new IllegalArgumentException("viewType = " + viewType);
+ }
+ }
+
public void setChat (TGChat chat, boolean needBackground, boolean noSeparator, boolean isSelected) {
((ChatView) itemView).setChat(chat);
((ChatView) itemView).setNeedBackground(needBackground);
@@ -60,7 +81,14 @@ public void setEmpty (int empty) {
((ListInfoView) itemView).showEmpty(Lang.getString(empty));
}
- public static ChatsViewHolder create (Context context, Tdlib tdlib, int viewType, @Nullable ChatsController parentController, @Nullable ViewController> themeProvider, BaseView.ActionListProvider actionListProvider) {
+ public void setChatIds (long[] chatIds) {
+ boolean hasContent = Boolean.TRUE.equals(itemView.getTag(R.id.state));
+ boolean animated = ViewCompat.isAttachedToWindow(itemView) && hasContent;
+ itemView.setTag(R.id.state, chatIds.length > 0);
+ ((SuggestedChatsView) itemView).setChatIds(chatIds, animated);
+ }
+
+ public static ChatsViewHolder create (Context context, Tdlib tdlib, @ViewType int viewType, @Nullable ChatsController parentController, @Nullable ViewController> themeProvider, BaseView.ActionListProvider actionListProvider) {
switch (viewType) {
case ChatsAdapter.VIEW_TYPE_CHAT: {
ChatView view = new ChatView(context, tdlib);
@@ -100,6 +128,16 @@ public static ChatsViewHolder create (Context context, Tdlib tdlib, int viewType
textView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return new ChatsViewHolder(textView);
}
+ case ChatsAdapter.VIEW_TYPE_SUGGESTED_CHATS: {
+ SuggestedChatsView view = new SuggestedChatsView(context, tdlib);
+ view.setId(R.id.btn_chatsSuggestion);
+ view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
+ view.setOnClickListener(parentController);
+ if (themeProvider != null) {
+ themeProvider.addThemeInvalidateListener(view);
+ }
+ return new ChatsViewHolder(view);
+ }
default: {
throw new IllegalArgumentException("viewType == " + viewType);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/component/user/UserView.java b/app/src/main/java/org/thunderdog/challegram/component/user/UserView.java
index fc04fc0513..813f3816ba 100644
--- a/app/src/main/java/org/thunderdog/challegram/component/user/UserView.java
+++ b/app/src/main/java/org/thunderdog/challegram/component/user/UserView.java
@@ -462,7 +462,7 @@ public List getDrawModifiers () {
public void setChecked (boolean isChecked, boolean animated) {
if (checkBoxHelper == null) {
- checkBoxHelper = new SimplestCheckBoxHelper(this, avatarReceiver);
+ checkBoxHelper = new SimplestCheckBoxHelper(this);
}
checkBoxHelper.setIsChecked(isChecked, animated);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/config/Config.java b/app/src/main/java/org/thunderdog/challegram/config/Config.java
index 5c2a8daf59..fc3861a0f8 100644
--- a/app/src/main/java/org/thunderdog/challegram/config/Config.java
+++ b/app/src/main/java/org/thunderdog/challegram/config/Config.java
@@ -35,6 +35,7 @@ public class Config {
public static final boolean CHAT_FOLDERS_SMART_CHAT_DELETION_ENABLED = true;
public static final boolean CHAT_FOLDERS_HIDE_BOTTOM_BAR_ON_SCROLL = true;
public static final boolean CHAT_FOLDERS_APPEARANCE_IS_GLOBAL = true;
+ public static final boolean CHAT_FOLDERS_REDESIGN = true;
public static final boolean RESTRICT_HIDING_MAIN_LIST = true;
public static final boolean SEARCH_MESSAGES_ONLY_IN_SELECTED_FOLDER = BuildConfig.EXPERIMENTAL;
diff --git a/app/src/main/java/org/thunderdog/challegram/core/Lang.java b/app/src/main/java/org/thunderdog/challegram/core/Lang.java
index a9ba312f41..d33f25765f 100644
--- a/app/src/main/java/org/thunderdog/challegram/core/Lang.java
+++ b/app/src/main/java/org/thunderdog/challegram/core/Lang.java
@@ -3230,6 +3230,10 @@ public static int gravity (int gravity) {
return gravity() | gravity;
}
+ public static int reverseGravity (int gravity) {
+ return reverseGravity() | gravity;
+ }
+
private static void setLanguageAllowLowercase (boolean allowLowercase, boolean sendEvents) {
if (Lang.languageAllowLowercase != allowLowercase) {
Lang.languageAllowLowercase = allowLowercase;
diff --git a/app/src/main/java/org/thunderdog/challegram/data/DoubleTextWrapper.java b/app/src/main/java/org/thunderdog/challegram/data/DoubleTextWrapper.java
index 63b9e452ba..cbe0707467 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/DoubleTextWrapper.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/DoubleTextWrapper.java
@@ -15,6 +15,7 @@
package org.thunderdog.challegram.data;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -35,6 +36,7 @@
import org.thunderdog.challegram.telegram.TdlibContext;
import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.theme.Theme;
+import org.thunderdog.challegram.tool.DrawAlgorithms;
import org.thunderdog.challegram.tool.Drawables;
import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.PorterDuffPaint;
@@ -47,6 +49,7 @@
import org.thunderdog.challegram.util.text.TextColorSetOverride;
import org.thunderdog.challegram.util.text.TextColorSets;
import org.thunderdog.challegram.util.text.TextMedia;
+import org.thunderdog.challegram.widget.SimplestCheckBoxHelper;
import me.vkryl.android.animator.BounceAnimator;
import me.vkryl.android.util.MultipleViewProvider;
@@ -270,9 +273,10 @@ public void updateSubtitle () {
}
private TdApi.ChatMessageSender chatMessageSender;
+ private @Nullable SimplestCheckBoxHelper checkBoxHelper;
private boolean isPremiumLocked;
private boolean drawAnonymousIcon;
- private boolean drawFakeCheckbox;
+ private boolean drawCrossIcon;
public void setChatMessageSender (TdApi.ChatMessageSender sender) {
this.chatMessageSender = sender;
@@ -282,8 +286,35 @@ public void setChatMessageSender (TdApi.ChatMessageSender sender) {
buildTitle();
}
- public void setDrawFakeCheckbox (boolean drawFakeCheckbox) {
- this.drawFakeCheckbox = drawFakeCheckbox;
+ public void setIsChecked (boolean isChecked, boolean animated) {
+ if (isChecked != isChecked()) {
+ if (checkBoxHelper == null) {
+ checkBoxHelper = new SimplestCheckBoxHelper(currentViews);
+ }
+ checkBoxHelper.setIsChecked(isChecked, animated);
+ if (isChecked) {
+ this.drawCrossIcon = false;
+ }
+ currentViews.invalidate();
+ }
+ }
+
+ public boolean isChecked () {
+ return checkBoxHelper != null && checkBoxHelper.isChecked();
+ }
+
+ public void setDrawCrossIcon (boolean drawCrossIcon) {
+ if (this.drawCrossIcon != drawCrossIcon) {
+ this.drawCrossIcon = drawCrossIcon;
+ if (drawCrossIcon) {
+ setIsChecked(/* isChecked */ false, /* animated */ false);
+ }
+ currentViews.invalidate();
+ }
+ }
+
+ public boolean isDrawCrossIcon () {
+ return drawCrossIcon;
}
public boolean isPremiumLocked () {
@@ -487,7 +518,6 @@ public void onAttachToView () {
public void draw (T view, Receiver receiver, Canvas c, ComplexReceiver emojiStatusReceiver) {
int left = Screen.dp(72f);
- boolean rtl = Lang.rtl();
int viewWidth = view.getMeasuredWidth();
final float anonymousFactor = isAnonymous.getFloatValue();
@@ -507,22 +537,27 @@ public void draw (T view, Receiver receiver,
}
}
- if (drawFakeCheckbox) {
- double radians = Math.toRadians(45f);
- float cx = receiver.centerX() + (float) ((double) (receiver.getWidth() / 2) * Math.sin(radians));
- float cy = receiver.centerY() + (float) ((double) (receiver.getHeight() / 2) * Math.cos(radians));
- c.drawCircle(cx, cy, Screen.dp(11.5f), Paints.fillingPaint(Theme.fillingColor()));
- c.drawCircle(cx, cy, Screen.dp(10f), Paints.fillingPaint(Theme.radioFillingColor()));
- c.save();
- float lineSize = Screen.dp(2);
- float x1 = cx - Screen.dp(1.5f);
- float y1 = cy + Screen.dp(5.5f);
- float w2 = Screen.dp(10f);
- float h1 = Screen.dp(6f);
- c.rotate(-45f, x1, y1);
- c.drawRect(x1, y1 - h1, x1 + lineSize, y1, Paints.fillingPaint(Theme.radioCheckColor()));
- c.drawRect(x1, y1 - lineSize, x1 + w2, y1, Paints.fillingPaint(Theme.radioCheckColor()));
- c.restore();
+ final float checkFactor = checkBoxHelper != null ? checkBoxHelper.getCheckFactor() : 0f;
+ boolean drawCheckBox = checkFactor > 0f;
+ if (drawCheckBox || drawCrossIcon) {
+ if (drawCheckBox) {
+ DrawAlgorithms.drawSimplestCheckBox(c, receiver, checkFactor);
+ } else {
+ float lineSize = Screen.dp(2);
+ double radians = Math.toRadians(45f);
+ float cx = receiver.centerX() + (float) ((double) (receiver.getWidth() / 2) * Math.sin(radians));
+ float cy = receiver.centerY() + (float) ((double) (receiver.getHeight() / 2) * Math.cos(radians));
+ c.drawCircle(cx, cy, Screen.dp(11.5f), Paints.fillingPaint(Theme.fillingColor()));
+ int backgroundColor = Theme.textDecentColor();
+ c.drawCircle(cx, cy, Screen.dp(10f), Paints.fillingPaint(backgroundColor));
+ c.save();
+ float h = Screen.dp(5.5f);
+ c.rotate(45f, cx, cy);
+ int color = Theme.isDark() ? Color.BLACK : Color.WHITE;
+ c.drawRect(cx - h, cy - lineSize / 2f, cx + h, cy + lineSize / 2f, Paints.fillingPaint(color));
+ c.drawRect(cx - lineSize / 2f, cy - h, cx + lineSize / 2f, cy + h, Paints.fillingPaint(color));
+ c.restore();
+ }
}
if (drawAnonymousIcon) {
Drawable incognitoIcon = view.getSparseDrawable(R.drawable.dot_baseline_acc_anon_24, ColorId.icon);
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TD.java b/app/src/main/java/org/thunderdog/challegram/data/TD.java
index 0c9b118e35..1f0c398a64 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TD.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TD.java
@@ -2253,6 +2253,7 @@ public static String toErrorString (@Nullable TdApi.Object object) {
public static final String ERROR_USER_CHANNELS_TOO_MUCH = "USER_CHANNELS_TOO_MUCH";
public static final String ERROR_CHANNELS_ADMIN_PUBLIC_TOO_MUCH = "CHANNELS_ADMIN_PUBLIC_TOO_MUCH";
public static final String ERROR_CHANNELS_ADMIN_LOCATED_TOO_MUCH = "CHANNELS_ADMIN_LOCATED_TOO_MUCH";
+ public static final String ERROR_CHATLISTS_TOO_MUCH = "CHATLISTS_TOO_MUCH";
public static @Nullable String translateError (int code, String message) {
if (StringUtils.isEmpty(message)) {
@@ -2291,6 +2292,8 @@ public static String toErrorString (@Nullable TdApi.Object object) {
case "PEER_FLOOD": res = R.string.NobodyLikesSpam2; break;
case "STICKERSET_INVALID": res = R.string.error_STICKERSET_INVALID; break;
case "CHANNELS_TOO_MUCH": res = R.string.error_CHANNELS_TOO_MUCH; break;
+ case ERROR_CHATLISTS_TOO_MUCH: res = R.string.error_CHATLISTS_TOO_MUCH; break;
+ case "INVITES_TOO_MUCH": res = R.string.error_INVITES_TOO_MUCH; break;
case "BOTS_TOO_MUCH": res = R.string.error_BOTS_TOO_MUCH; break;
case "ADMINS_TOO_MUCH": res = R.string.error_ADMINS_TOO_MUCH; break;
case "Not enough rights to invite members to the group chat": res = R.string.YouCantInviteMembers; break;
@@ -5375,6 +5378,14 @@ public static int chatTypeAccentColorId (@IdRes int chatType) {
}
}
+ public static @Nullable String getIconName (@Nullable TdApi.ChatFolderInfo info) {
+ return info != null && info.icon != null ? info.icon.name : null;
+ }
+
+ public static @Nullable String getIconName (@Nullable TdApi.ChatFolder folder) {
+ return folder != null && folder.icon != null ? folder.icon.name : null;
+ }
+
public static @DrawableRes int iconByName (String iconName, @DrawableRes int defaultIcon) {
if (StringUtils.isEmpty(iconName))
return defaultIcon;
@@ -5396,7 +5407,7 @@ public static int chatTypeAccentColorId (@IdRes int chatType) {
case "Cat":
return R.drawable.templarian_baseline_cat_24;
case "Crown":
- return R.drawable.baseline_crown_circle_24;
+ return R.drawable.baseline_crown_24;
case "Favorite":
return R.drawable.baseline_star_24;
case "Flower":
@@ -5418,7 +5429,6 @@ public static int chatTypeAccentColorId (@IdRes int chatType) {
case "Work":
return R.drawable.baseline_work_24;
case "Airplane":
- // return R.drawable.baseline_flight_24;
return R.drawable.baseline_logo_telegram_24;
case "Book":
return R.drawable.baseline_book_24;
@@ -5431,13 +5441,11 @@ public static int chatTypeAccentColorId (@IdRes int chatType) {
case "Note":
return R.drawable.baseline_music_note_24;
case "Palette":
- // return R.drawable.baseline_palette_24;
- return R.drawable.baseline_brush_24;
+ return R.drawable.baseline_palette_24;
case "Unread":
return R.drawable.baseline_mark_chat_unread_24;
case "Travel":
- // return R.drawable.baseline_explore_24;
- return R.drawable.baseline_flight_24;
+ return R.drawable.baseline_airplane_24;
case "Custom":
return R.drawable.baseline_folder_24;
case "Trade":
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGAvatars.java b/app/src/main/java/org/thunderdog/challegram/data/TGAvatars.java
index 28c7322dc2..1d00cefa3a 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGAvatars.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGAvatars.java
@@ -2,6 +2,7 @@
import android.graphics.Canvas;
import android.view.Gravity;
+import android.view.View;
import androidx.annotation.Dimension;
import androidx.annotation.NonNull;
@@ -10,7 +11,6 @@
import androidx.core.util.ObjectsCompat;
import org.drinkless.tdlib.TdApi;
-import org.thunderdog.challegram.component.chat.MessageView;
import org.thunderdog.challegram.loader.AvatarReceiver;
import org.thunderdog.challegram.loader.ComplexReceiver;
import org.thunderdog.challegram.telegram.Tdlib;
@@ -25,6 +25,7 @@
import me.vkryl.android.AnimatorUtils;
import me.vkryl.android.animator.FactorAnimator;
import me.vkryl.android.animator.ListAnimator;
+import me.vkryl.android.util.SingleViewProvider;
import me.vkryl.android.util.ViewProvider;
import me.vkryl.core.MathUtils;
import me.vkryl.td.Td;
@@ -54,6 +55,10 @@ public interface Callback {
void onInvalidateMedia (TGAvatars avatars);
}
+ public TGAvatars (@NonNull Tdlib tdlib, @NonNull Callback callback, @NonNull View view) {
+ this(tdlib, callback, new SingleViewProvider(view));
+ }
+
public TGAvatars (@NonNull Tdlib tdlib, @NonNull Callback callback, @Nullable ViewProvider viewProvider) {
this.tdlib = tdlib;
this.callback = callback;
@@ -182,7 +187,7 @@ public float getAvatarsVisibility () {
return MathUtils.clamp(countAnimator.getFactor());
}
- public void draw (@NonNull MessageView view, @NonNull Canvas c, @Nullable ComplexReceiver avatarsReceiver, int x, int cy, int gravity, float alpha) {
+ public void draw (@NonNull Canvas c, @Nullable ComplexReceiver avatarsReceiver, int x, int cy, int gravity, float alpha) {
if (animator == null || animator.size() == 0 || alpha == 0f || avatarsReceiver == null) {
return;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGCommentButton.java b/app/src/main/java/org/thunderdog/challegram/data/TGCommentButton.java
index 97b44b1708..343ad42048 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGCommentButton.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGCommentButton.java
@@ -450,7 +450,7 @@ private void drawInline (@NonNull MessageView view, @NonNull Canvas c, @NonNull
int avatarsX = right - (useBubbles ? Screen.dp(16f) : Screen.dp(38f));
int avatarsY = rect.centerY();
- avatars.draw(view, c, view.getAvatarsReceiver(), avatarsX, avatarsY, Gravity.RIGHT, alpha);
+ avatars.draw(c, view.getAvatarsReceiver(), avatarsX, avatarsY, Gravity.RIGHT, alpha);
int badgeX = avatarsX - Math.round(avatars.getAnimatedWidth()) - Screen.dp(8f) - Screen.dp(BADGE_RADIUS);
int badgeY = rect.centerY();
@@ -511,7 +511,7 @@ private void drawBubble (@NonNull MessageView view, @NonNull Canvas c, @NonNull
int avatarsX = right - Screen.dp(6f);
int avatarsY = rect.centerY();
- avatars.draw(view, c, view.getAvatarsReceiver(), avatarsX, avatarsY, Gravity.RIGHT, alpha);
+ avatars.draw(c, view.getAvatarsReceiver(), avatarsX, avatarsY, Gravity.RIGHT, alpha);
float badgeX = avatarsX - avatars.getAnimatedWidth() - Screen.dp(8f) - Screen.dp(BADGE_RADIUS);
float badgeY = rect.centerY();
diff --git a/app/src/main/java/org/thunderdog/challegram/data/TGReactions.java b/app/src/main/java/org/thunderdog/challegram/data/TGReactions.java
index 39b5c253ca..c4a882ec6c 100644
--- a/app/src/main/java/org/thunderdog/challegram/data/TGReactions.java
+++ b/app/src/main/java/org/thunderdog/challegram/data/TGReactions.java
@@ -1025,7 +1025,7 @@ public void drawReactionInBubble (MessageView view, Canvas c, float x, float y,
if (visibility > 0f) {
c.drawRoundRect(rect, radius, radius, Paints.fillingPaint( ColorUtils.alphaColor(alpha, backgroundColor)));
- avatars.draw(view, c, view.getReactionAvatarsReceiver(), avatarsX, getReactionBubbleHeight() / 2, Gravity.LEFT, alpha);
+ avatars.draw(c, view.getReactionAvatarsReceiver(), avatarsX, getReactionBubbleHeight() / 2, Gravity.LEFT, alpha);
counter.draw(c, textX, getReactionBubbleHeight() / 2f, Gravity.LEFT, alpha, view, ColorId.badgeFailedText);
if (!isHidden) {
drawReceiver(c, Screen.dp(-1), imgY, Screen.dp(-1) + imageSize, imgY + imageSize, alpha);
diff --git a/app/src/main/java/org/thunderdog/challegram/emoji/EmojiSpan.java b/app/src/main/java/org/thunderdog/challegram/emoji/EmojiSpan.java
index 559c990d07..24351d0376 100644
--- a/app/src/main/java/org/thunderdog/challegram/emoji/EmojiSpan.java
+++ b/app/src/main/java/org/thunderdog/challegram/emoji/EmojiSpan.java
@@ -15,15 +15,13 @@
package org.thunderdog.challegram.emoji;
import android.graphics.Canvas;
-import android.graphics.Paint;
import android.text.Layout;
import android.view.View;
import org.thunderdog.challegram.loader.ComplexReceiver;
+import org.thunderdog.challegram.util.text.TextReplacementSpan;
-public interface EmojiSpan {
- int getRawSize (Paint paint);
-
+public interface EmojiSpan extends TextReplacementSpan {
boolean isCustomEmoji ();
default long getCustomEmojiId () {
return 0;
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/EditHeaderView.java b/app/src/main/java/org/thunderdog/challegram/navigation/EditHeaderView.java
index dff6f3230b..b8af14d978 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/EditHeaderView.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/EditHeaderView.java
@@ -26,15 +26,19 @@
import android.view.ViewGroup;
import android.widget.EditText;
+import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.core.Lang;
import org.thunderdog.challegram.loader.ImageGalleryFile;
import org.thunderdog.challegram.loader.ImageReceiver;
+import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.PorterDuffColorId;
import org.thunderdog.challegram.tool.Drawables;
import org.thunderdog.challegram.tool.Keyboard;
import org.thunderdog.challegram.tool.Paints;
+import org.thunderdog.challegram.tool.PorterDuffPaint;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.Views;
import org.thunderdog.challegram.unsorted.Size;
@@ -49,11 +53,12 @@
public class EditHeaderView extends FrameLayoutFix implements RtlCheckListener, Destroyable, StretchyHeaderView, TextWatcher, HeaderView.OffsetChangeListener {
private final ViewController> parent;
- private HeaderEditText input;
+ private final HeaderEditText input;
private final ImageReceiver receiver;
private final Path clipPath = new Path();
- private final Drawable icon;
+ private Drawable icon;
+ private @PorterDuffColorId int iconColorId = ColorId.white;
public EditHeaderView (Context context, ViewController> parent) {
super(context);
@@ -274,7 +279,7 @@ protected void onDraw (Canvas c) {
int cx = receiver.centerX();
int cy = receiver.centerY();
c.drawCircle(cx, cy, avatarRadius, Paints.fillingPaint(0x20000000));
- Drawables.draw(c, icon, cx - (int) (icon.getMinimumWidth() * .5f), cy - (int) (icon.getMinimumHeight() * .5f), Paints.whitePorterDuffPaint());
+ Drawables.draw(c, icon, cx - (int) (icon.getMinimumWidth() * .5f), cy - (int) (icon.getMinimumHeight() * .5f), PorterDuffPaint.get(iconColorId));
}
public void setInputEnabled (boolean enabled) {
@@ -288,6 +293,12 @@ public void setPhoto (ImageGalleryFile file) {
receiver.requestFile(file);
}
+ public void setIcon (@DrawableRes int iconRes, @PorterDuffColorId int colorId) {
+ icon = Drawables.get(getResources(), iconRes);
+ iconColorId = colorId;
+ invalidate();
+ }
+
public ImageGalleryFile getImageFile () {
return file;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/OptionsLayout.java b/app/src/main/java/org/thunderdog/challegram/navigation/OptionsLayout.java
index 6b6d80d40f..68f88ca4f6 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/OptionsLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/OptionsLayout.java
@@ -134,11 +134,14 @@ protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
}
}
- public static @ColorId int getOptionColorId (int color) {
+ public static @ColorId int getOptionColorId (@ViewController.OptionColor int color) {
switch (color) {
case ViewController.OptionColor.NORMAL: {
return ColorId.text;
}
+ case ViewController.OptionColor.INACTIVE: {
+ return ColorId.controlInactive;
+ }
case ViewController.OptionColor.RED: {
return ColorId.textNegative;
}
@@ -152,7 +155,7 @@ protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
throw new IllegalArgumentException("color == " + color);
}
- public static TextView genOptionView (Context context, int id, CharSequence string, int color, int icon, OnClickListener onClickListener, @Nullable ThemeListenerList themeProvider, @Nullable ThemeDelegate forcedTheme) {
+ public static TextView genOptionView (Context context, int id, CharSequence string, @ViewController.OptionColor int color, int icon, OnClickListener onClickListener, @Nullable ThemeListenerList themeProvider, @Nullable ThemeDelegate forcedTheme) {
EmojiTextView text = new EmojiTextView(context);
text.setScrollDisabled(true);
@@ -176,7 +179,14 @@ public static TextView genOptionView (Context context, int id, CharSequence stri
if (icon != 0) {
Drawable drawable = Drawables.get(context.getResources(), icon);
if (drawable != null) {
- final int drawableColorId = color == ViewController.OptionColor.NORMAL ? ColorId.icon : colorId;
+ final int drawableColorId;
+ if (color == ViewController.OptionColor.NORMAL) {
+ drawableColorId = ColorId.icon;
+ } else if (color == ViewController.OptionColor.INACTIVE) {
+ drawableColorId = ColorId.controlInactive;
+ } else {
+ drawableColorId = colorId;
+ }
drawable.setColorFilter(Paints.getColorFilter(forcedTheme != null ? forcedTheme.getColor(drawableColorId) : Theme.getColor(drawableColorId)));
if (themeProvider != null) {
themeProvider.addThemeFilterListener(drawable, drawableColorId);
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/OverlayButtonWrap.java b/app/src/main/java/org/thunderdog/challegram/navigation/OverlayButtonWrap.java
index 0a4ba49e49..c2cb9da080 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/OverlayButtonWrap.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/OverlayButtonWrap.java
@@ -33,6 +33,7 @@
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
+import androidx.core.content.res.ResourcesCompat;
import org.thunderdog.challegram.core.Lang;
import org.thunderdog.challegram.support.RippleSupport;
@@ -83,6 +84,11 @@ public void initWithList (final @NonNull ViewController> parent, int overlayCo
addMainButton(parent, ids[0], resources[0], backgrounds[0], colors[0], overlayColorId, overlayIconColorId);
}
+ @IdRes
+ public int getMainButtonId () {
+ return mainButton != null ? mainButton.getId() : ResourcesCompat.ID_NULL;
+ }
+
public void replaceMainButton (@IdRes int id, @DrawableRes int icon) {
mainButton.setId(id);
mainButton.replaceIcon(icon);
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/TooltipOverlayView.java b/app/src/main/java/org/thunderdog/challegram/navigation/TooltipOverlayView.java
index 1efd3f1904..cc498e9537 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/TooltipOverlayView.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/TooltipOverlayView.java
@@ -30,6 +30,7 @@
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import androidx.core.os.CancellationSignal;
import androidx.core.view.ViewCompat;
@@ -857,6 +858,11 @@ public boolean layout (int parentWidth, int parentHeight) {
locationProvider.getTargetBounds(view, innerRect);
} else if (view instanceof LocationProvider) {
((LocationProvider) view).getTargetBounds(view, innerRect);
+ } else {
+ Object tag = view.getTag(R.id.tag_tooltip_location_provider);
+ if (tag instanceof LocationProvider) {
+ ((LocationProvider) tag).getTargetBounds(view, innerRect);
+ }
}
boolean hasIcon = hasIcon();
@@ -1290,7 +1296,7 @@ public TooltipInfo show (Tdlib tdlib, TdApi.FormattedText text) {
return show(new TooltipContentViewText(parentView, tdlib, text, 0, urlOpenParameters));
}
- public TooltipInfo show (Tdlib tdlib, int stringRes) {
+ public TooltipInfo show (Tdlib tdlib, @StringRes int stringRes) {
return show(tdlib, new TdApi.FormattedText(Lang.getString(stringRes), null));
}
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/ViewController.java b/app/src/main/java/org/thunderdog/challegram/navigation/ViewController.java
index 26038fec1f..22cb457d0f 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/ViewController.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/ViewController.java
@@ -2286,14 +2286,14 @@ public final PopupLayout showWarning (CharSequence info, RunnableBool callback)
}
public static class OptionItem {
- public static final OptionItem SEPARATOR = new OptionItem(0, null, 0, 0);
+ public static final OptionItem SEPARATOR = new OptionItem(0, null, OptionColor.NORMAL, 0);
public final int id;
public final CharSequence name;
public final int color;
public final int icon;
- public OptionItem (int id, CharSequence name, int color, int icon) {
+ public OptionItem (int id, CharSequence name, @OptionColor int color, int icon) {
this.id = id;
this.name = name;
this.color = color;
@@ -2307,7 +2307,6 @@ public static class Builder {
private int icon;
public Builder () {
- this.id = id;
}
public Builder id (int id) {
@@ -2357,7 +2356,7 @@ public static class Builder {
private CharSequence info;
private CharSequence title;
private OptionItem subtitle;
- private List items = new ArrayList<>();
+ private final List items = new ArrayList<>();
public Builder () {
}
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerController.java b/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerController.java
index a97c52ab33..707b15c6b1 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerController.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerController.java
@@ -660,9 +660,9 @@ protected long getPagerItemId (int position) {
}
protected abstract void onCreateView (Context context, FrameLayoutFix contentView, ViewPager pager);
protected abstract ViewController> onCreatePagerItemForPosition (Context context, int position);
- protected @Nullable abstract String[] getPagerSections ();
+ protected @Nullable abstract CharSequence[] getPagerSections ();
protected @Nullable List getPagerSectionItems () {
- String[] pagerSections = getPagerSections();
+ CharSequence[] pagerSections = getPagerSections();
if (pagerSections == null) {
return cachedPagerSectionItems = null;
}
@@ -682,7 +682,7 @@ protected long getPagerItemId (int position) {
}
}
List pagerSectionItems = new ArrayList<>(pagerSections.length);
- for (String pagerSection : pagerSections) {
+ for (CharSequence pagerSection : pagerSections) {
pagerSectionItems.add(new ViewPagerTopView.Item(pagerSection));
}
cachedPagerSectionItems = pagerSectionItems;
diff --git a/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerTopView.java b/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerTopView.java
index d228dc6e2e..3d05e6d151 100644
--- a/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerTopView.java
+++ b/app/src/main/java/org/thunderdog/challegram/navigation/ViewPagerTopView.java
@@ -17,6 +17,7 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.SystemClock;
import android.text.Layout;
@@ -36,6 +37,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.Px;
+import org.thunderdog.challegram.BuildConfig;
import org.thunderdog.challegram.U;
import org.thunderdog.challegram.component.sticker.TGStickerObj;
import org.thunderdog.challegram.core.Lang;
@@ -60,6 +62,7 @@
import java.util.ArrayList;
import java.util.List;
+import me.vkryl.android.AnimatorUtils;
import me.vkryl.android.widget.FrameLayoutFix;
import me.vkryl.core.ColorUtils;
import me.vkryl.core.MathUtils;
@@ -157,24 +160,28 @@ public void setStaticWidth (@Px int staticWidth) {
this.staticWidth = staticWidth;
}
- public int calculateWidth (TextPaint paint, @Px int horizontalSpacing) {
+ public int calculateWidth (TextPaint paint, @Px int horizontalSpacing, float labelFactor) {
final int width;
+ final float labelWidth = string != null ? U.measureEmojiText(string, paint) * labelFactor : 0f;
+ final float counterWidthWithSpacing = counter != null ? counter.getScaledWidth(horizontalSpacing) : 0f;
+ final float iconWidth = iconRes != 0 ? Screen.dp(ICON_SIZE) : 0;
+ final float iconWidthWithLabelSpacing = iconRes != 0 ? (iconWidth + horizontalSpacing * labelFactor) : 0;
if (staticWidth != -1) {
width = staticWidth;
} else if (counter != null) {
if (string != null) {
- width = (int) (U.measureEmojiText(string, paint) + counter.getScaledWidth(horizontalSpacing)) + (iconRes != 0 ? Screen.dp(ICON_SIZE) + horizontalSpacing : 0);
+ width = (int) (labelWidth + counterWidthWithSpacing + iconWidthWithLabelSpacing);
} else if (imageReceiver != null) {
width = (int) counter.getWidth() + imageReceiverSize;
} else if (iconRes != 0) {
- width = Screen.dp(ICON_SIZE) + (int) counter.getScaledWidth(horizontalSpacing);
+ width = (int) (iconWidth + counterWidthWithSpacing);
} else {
width = (int) counter.getWidth()/* + Screen.dp(6f) */; // ???
}
} else if (string != null) {
- width = (int) U.measureEmojiText(string, paint) + (iconRes != 0 ? Screen.dp(ICON_SIZE) + horizontalSpacing : 0);
+ width = (int) (labelWidth + iconWidthWithLabelSpacing);
} else if (iconRes != 0) {
- width = Screen.dp(ICON_SIZE)/* + Screen.dp(6f)*/; // ???
+ width = (int) iconWidth/* + Screen.dp(6f)*/; // ???
} else {
width = 0;
}
@@ -201,15 +208,21 @@ public void trimString (int availWidth, TextPaint paint) {
}
}
- public void untrimString (TextPaint paint) {
+ public void untrimString (TextPaint textPaint) {
if (string != null) {
- ellipsizedStringLayout = U.createLayout(string, (int) Math.ceil(U.measureEmojiText(string, paint)), paint);
+ int textWidth = (int) Math.ceil(U.measureEmojiText(string, textPaint));
+ if (ellipsizedStringLayout == null || !ellipsizedStringLayout.getText().equals(string) ||
+ ellipsizedStringLayout.getPaint() != textPaint ||
+ ellipsizedStringLayout.getWidth() != textWidth) {
+ ellipsizedStringLayout = U.createLayout(string, textWidth, textPaint);
+ }
} else {
ellipsizedStringLayout = null;
}
actualWidth = width;
}
}
+
private List- items;
private int maxItemWidth;
@@ -247,16 +260,19 @@ public void checkRtl () {
public void setItemPadding (@Px int itemPadding) {
if (this.itemPadding != itemPadding) {
this.itemPadding = itemPadding;
- this.lastMeasuredWidth = 0;
- requestLayout();
+ if (items != null && !items.isEmpty()) {
+ relayout();
+ }
}
}
public void setItemSpacing (@Px int itemSpacing) {
if (this.itemSpacing != itemSpacing) {
this.itemSpacing = itemSpacing;
- this.lastMeasuredWidth = 0;
- requestLayout();
+ if (items != null && !items.isEmpty()) {
+ measureItems();
+ relayout();
+ }
}
}
@@ -277,6 +293,18 @@ public boolean isDrawSelectionAtTop () {
return drawSelectionAtTop;
}
+ private boolean showLabelOnActiveOnly;
+
+ public void setShowLabelOnActiveOnly (boolean showLabelOnActiveOnly) {
+ if (this.showLabelOnActiveOnly != showLabelOnActiveOnly) {
+ this.showLabelOnActiveOnly = showLabelOnActiveOnly;
+ if (items != null && !items.isEmpty()) {
+ measureItems();
+ relayout();
+ }
+ }
+ }
+
private OnItemClickListener listener;
public void setOnItemClickListener (OnItemClickListener listener) {
@@ -382,19 +410,11 @@ public void setItemAt (int index, String text) {
}
public void setItemAt (int index, Item item) {
- Item oldItem = this.items.get(index);
this.items.set(index, item);
onUpdateItems();
- totalWidth -= oldItem.width + itemPadding * 2;
- int textColor = Theme.headerTextColor();
- TextPaint paint = Paints.getViewPagerTextPaint(textColor, item.needFakeBold);
- item.calculateWidth(paint, itemSpacing);
- totalWidth += item.width + itemPadding * 2;
- maxItemWidth = totalWidth / items.size();
-
- this.lastMeasuredWidth = 0;
- requestLayout();
+ measureItem(item, index, getItemTextPaint(item));
+ relayout();
invalidate();
}
@@ -427,18 +447,11 @@ public void setItems (@NonNull List
- items) {
this.items = items;
onUpdateItems();
- this.totalWidth = 0;
- this.lastMeasuredWidth = 0;
- int i = 0;
- int textColor = Theme.headerTextColor();
- for (Item item : items) {
- TextPaint paint = Paints.getViewPagerTextPaint(textColor, item.needFakeBold);
- item.calculateWidth(paint, itemSpacing);
- totalWidth += item.width + itemPadding * 2;
+ measureItems();
+ for (int i = 0; i < items.size(); i++) {
addView(newBackgroundView(i));
- i++;
}
- maxItemWidth = items.isEmpty() ? 0 : totalWidth / items.size();
+ relayout();
}
public void addItem (String item) {
@@ -469,20 +482,19 @@ public void addItemAtIndex (Item item, int index) {
}
onUpdateItems();
- int textColor = Theme.headerTextColor();
- TextPaint paint = Paints.getViewPagerTextPaint(textColor, item.needFakeBold);
- item.calculateWidth(paint, itemSpacing);
+ if (index <= (int) selectionFactor) {
+ selectionFactor++;
+ }
+
+ TextPaint paint = getItemTextPaint(item);
+ measureItem(item, index, paint);
int width = item.width;
totalWidth += width + itemPadding * 2;
maxItemWidth = totalWidth / items.size();
commonItemWidth = calculateCommonItemWidth(width);
- if (index <= (int) selectionFactor) {
- selectionFactor++;
- }
-
final int availTextWidth = commonItemWidth - itemPadding * 2;
if (!shouldWrapContent() && width < availTextWidth) {
item.trimString(availTextWidth, paint);
@@ -515,14 +527,42 @@ public void removeItemAt (int index) {
invalidate();
}
+ private TextPaint getItemTextPaint (Item item) {
+ return Paints.getViewPagerTextPaint(Theme.headerTextColor(), item.needFakeBold);
+ }
+
+ private void relayout () {
+ this.totalWidth = calculateTotalWidth();
+ this.maxItemWidth = items == null || items.isEmpty() ? 0 : (totalWidth / items.size());
+ this.lastMeasuredWidth = 0; // force layout
+ requestLayout();
+ }
+
+ private void measureItems () {
+ if (items == null || items.isEmpty()) {
+ return;
+ }
+ for (int i = 0; i < items.size(); i++) {
+ Item item = items.get(i);
+ measureItem(item, i, getItemTextPaint(item));
+ }
+ }
+
+ private void measureItem (Item item, int itemIndex, TextPaint paint) {
+ item.calculateWidth(paint, itemSpacing, getLabelFactor(itemIndex));
+ }
+
private boolean shouldWrapContent () {
return getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT;
}
- private int getTotalWidth () {
+ private int calculateTotalWidth () {
+ if (items == null || items.isEmpty()) {
+ return 0;
+ }
int sum = 0;
for (Item item : items) {
- sum += item.width;
+ sum += item.width + itemPadding * 2;
}
return sum;
}
@@ -549,7 +589,10 @@ public void requestItemLayoutAt (int index) {
@Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
if (shouldWrapContent()) {
- int totalWidth = itemPadding * 2 * items.size() + getTotalWidth();
+ int totalWidth = calculateTotalWidth();
+ if (totalWidth != this.totalWidth && BuildConfig.DEBUG) {
+ throw new IllegalStateException("this.totalWidth = " + this.totalWidth + ", totalWidth = " + totalWidth);
+ }
super.onMeasure(MeasureSpec.makeMeasureSpec(totalWidth, MeasureSpec.EXACTLY), heightMeasureSpec);
layout(totalWidth, true);
} else {
@@ -580,12 +623,11 @@ private void layout (int width, boolean wrapContent) {
lastMeasuredWidth = width;
commonItemWidth = calculateCommonItemWidth(width);
- int textColor = Theme.headerTextColor();
final int availTextWidth = commonItemWidth - itemPadding * 2;
for (Item item : items) {
- TextPaint textPaint = Paints.getViewPagerTextPaint(textColor, item.needFakeBold);
+ TextPaint textPaint = getItemTextPaint(item);
if (!wrapContent && item.width < availTextWidth) {
item.trimString(availTextWidth, textPaint);
} else {
@@ -679,7 +721,12 @@ public void setSelectionFactor (float factor) {
fromIndex = toIndex = -1;
}
- recalculateSelection(selectionFactor, true);
+ if (showLabelOnActiveOnly) {
+ measureItems();
+ relayout();
+ } else {
+ recalculateSelection(selectionFactor, true);
+ }
invalidate();
}
}
@@ -815,24 +862,7 @@ public void draw (Canvas c) {
c.translate(item.translationX, 0f);
}
- float factor;
- if (fromIndex != -1 && toIndex != -1) {
- int diff = Math.abs(toIndex - fromIndex);
- if (itemIndex == toIndex) {
- factor = Math.abs(selectionFactor - fromIndex) / (float) diff;
- } else if (itemIndex == fromIndex) {
- factor = 1f - Math.abs(selectionFactor - fromIndex) / (float) diff;
- } else {
- factor = 0f;
- }
- } else {
- float abs = Math.abs(selectionFactor - (float) itemIndex);
- if (abs <= 1f) {
- factor = 1f - abs;
- } else {
- factor = 0f;
- }
- }
+ float factor = getSelectionFactor(itemIndex);
final int itemWidth;
if (wrapContent) {
@@ -867,10 +897,7 @@ public void draw (Canvas c) {
stringX = cx + horizontalPadding;
}
int stringY = viewHeight / 2 - item.ellipsizedStringLayout.getHeight() / 2;
- c.translate(stringX, stringY);
- item.ellipsizedStringLayout.getPaint().setColor(color);
- item.ellipsizedStringLayout.draw(c);
- c.translate(-stringX, -stringY);
+ drawLabel(c, item.ellipsizedStringLayout, stringX, stringY, color, factor);
item.counter.draw(c, cx + itemWidth - horizontalPadding - item.counter.getWidth() / 2f, viewHeight / 2f, Gravity.CENTER, textAlpha, backgroundAlpha, imageAlpha, item.provider, ColorId.NONE);
} else if (item.imageReceiver != null) {
int size = item.imageReceiverSize;
@@ -898,10 +925,7 @@ public void draw (Canvas c) {
stringX = cx + itemWidth / 2 - item.actualWidth / 2;
}
int stringY = viewHeight / 2 - item.ellipsizedStringLayout.getHeight() / 2;
- c.translate(stringX, stringY);
- item.ellipsizedStringLayout.getPaint().setColor(color);
- item.ellipsizedStringLayout.draw(c);
- c.translate(-stringX, -stringY);
+ drawLabel(c, item.ellipsizedStringLayout, stringX, stringY, color, factor);
} else if (item.iconRes != 0) {
Drawable drawable = item.getIcon();
Drawables.draw(c, drawable, cx + itemWidth / 2 - drawable.getMinimumWidth() / 2, viewHeight / 2 - drawable.getMinimumHeight() / 2, Paints.getPorterDuffPaint(color));
@@ -940,6 +964,76 @@ public void draw (Canvas c) {
}
}
+ public boolean getItemRect (int position, Rect outRect) {
+ View child = getChildAt(position);
+ if (child != null) {
+ outRect.set(0, 0, child.getWidth(), child.getHeight());
+ outRect.offset((int) child.getX(), (int) child.getY());
+ return true;
+ }
+ return false;
+ }
+
+ private void drawLabel (Canvas c, Layout layout, float x, float y, int color, float selectionFactor) {
+ float labelFactor = getLabelFactorBySelectionFactor(selectionFactor);
+ if (labelFactor <= 0f) {
+ return;
+ }
+ float textWidth = layout.getWidth();
+ float clipRight = x + textWidth * labelFactor;
+ boolean isVisible = clipRight - x >= 1f;
+ boolean clipText = labelFactor < 1f && isVisible;
+ if (clipText) {
+ c.save();
+ c.clipRect(x, 0, clipRight, getHeight());
+ }
+ if (isVisible) {
+ c.translate(x, y);
+ layout.getPaint().setColor(color);
+ layout.draw(c);
+ c.translate(-x, -y);
+ }
+ if (clipText) {
+ c.restore();
+ }
+ }
+
+ private float getLabelFactor (int itemIndex) {
+ if (showLabelOnActiveOnly) {
+ return getLabelFactorBySelectionFactor(getSelectionFactor(itemIndex));
+ }
+ return 1f;
+ }
+
+ private float getLabelFactorBySelectionFactor (float selectionFactor) {
+ if (showLabelOnActiveOnly) {
+ return AnimatorUtils.ACCELERATE_DECELERATE_INTERPOLATOR.getInterpolation(selectionFactor);
+ }
+ return 1f;
+ }
+
+ private float getSelectionFactor (int itemIndex) {
+ float factor;
+ if (fromIndex != -1 && toIndex != -1) {
+ int diff = Math.abs(toIndex - fromIndex);
+ if (itemIndex == toIndex) {
+ factor = Math.abs(selectionFactor - fromIndex) / (float) diff;
+ } else if (itemIndex == fromIndex) {
+ factor = 1f - Math.abs(selectionFactor - fromIndex) / (float) diff;
+ } else {
+ factor = 0f;
+ }
+ } else {
+ float abs = Math.abs(selectionFactor - (float) itemIndex);
+ if (abs <= 1f) {
+ factor = 1f - abs;
+ } else {
+ factor = 0f;
+ }
+ }
+ return factor;
+ }
+
private static final CounterAlphaProvider DEFAULT_COUNTER_ALPHA_PROVIDER = new CounterAlphaProvider() {
};
@@ -1067,7 +1161,7 @@ public boolean onTouchEvent (MotionEvent e) {
return true;
}
- public boolean inSlideOff() {
+ public boolean inSlideOff () {
return inSlideOff;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/ChatFolderListener.java b/app/src/main/java/org/thunderdog/challegram/telegram/ChatFolderListener.java
new file mode 100644
index 0000000000..fb7838cf5e
--- /dev/null
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/ChatFolderListener.java
@@ -0,0 +1,34 @@
+/*
+ * This file is a part of Telegram X
+ * Copyright © 2014 (tgx-android@pm.me)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * File created on 19/01/2024
+ */
+package org.thunderdog.challegram.telegram;
+
+import org.drinkless.tdlib.TdApi;
+
+public interface ChatFolderListener {
+ default void onChatFolderNewChatsChanged (int chatFolderId) {
+ }
+
+ default void onChatFolderInviteLinkDeleted (int chatFolderId, String inviteLink) {
+
+ }
+
+ default void onChatFolderInviteLinkChanged (int chatFolderId, TdApi.ChatFolderInviteLink inviteLink) {
+
+ }
+
+ default void onChatFolderInviteLinkCreated (int chatFolderId, TdApi.ChatFolderInviteLink inviteLink) {
+
+ }
+}
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/ChatFolderStyle.java b/app/src/main/java/org/thunderdog/challegram/telegram/ChatFolderStyle.java
index fc78afce52..f747c13658 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/ChatFolderStyle.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/ChatFolderStyle.java
@@ -20,7 +20,7 @@
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.SOURCE)
-@IntDef({ChatFolderStyle.LABEL_ONLY, ChatFolderStyle.ICON_ONLY, ChatFolderStyle.LABEL_AND_ICON})
+@IntDef({ChatFolderStyle.LABEL_ONLY, ChatFolderStyle.ICON_ONLY, ChatFolderStyle.LABEL_AND_ICON, ChatFolderStyle.ICON_WITH_LABEL_ON_ACTIVE})
public @interface ChatFolderStyle {
- int LABEL_ONLY = 0, ICON_ONLY = 1, LABEL_AND_ICON = 2;
+ int LABEL_ONLY = 0, ICON_ONLY = 1, LABEL_AND_ICON = 2, ICON_WITH_LABEL_ON_ACTIVE = 3;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
index b9ff9c34dc..97a3f5b8bb 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/Tdlib.java
@@ -1793,6 +1793,15 @@ static Client.ResultHandler toTdlibHandler (ResultHandl
}
};
}
+
+ default ResultHandler doOnResult (RunnableData action) {
+ return (result, error) -> {
+ onResult(result, error);
+ if (result != null) {
+ action.runWithData(result);
+ }
+ };
+ }
}
public void send (TdApi.Function function, ResultHandler handler) {
@@ -3487,7 +3496,36 @@ public boolean chatPinned (TdApi.ChatList chatList, long chatId) {
}
}
- public int chatFoldersCount () {
+ public boolean isFolderShareable (int chatFolderId) {
+ TdApi.ChatFolderInfo chatFolderInfo = chatFolderInfo(chatFolderId);
+ return chatFolderInfo != null && chatFolderInfo.isShareable;
+ }
+
+ public boolean canAddShareableFolder () {
+ return shareableChatFolderCount() < addedShareableChatFolderCountMax();
+ }
+
+ public int shareableChatFolderCount () {
+ synchronized (dataLock) {
+ int count = 0;
+ for (TdApi.ChatFolderInfo chatFolder : chatFolders) {
+ if (chatFolder.isShareable) {
+ count++;
+ }
+ }
+ return count;
+ }
+ }
+
+ public int addedShareableChatFolderCountMax () {
+ return addedShareableChatFolderMaxCount;
+ }
+
+ public boolean canCreateChatFolder () {
+ return chatFolderCount() < chatFolderCountMax();
+ }
+
+ public int chatFolderCount () {
synchronized (dataLock) {
return chatFolders.length;
}
@@ -3506,6 +3544,9 @@ public int mainChatListPosition () {
}
public TdApi.ChatFolderInfo chatFolderInfo (int chatFolderId) {
+ if (chatFolderId == 0) {
+ return null;
+ }
synchronized (dataLock) {
if (chatFolders != null) {
for (TdApi.ChatFolderInfo filter : chatFolders) {
@@ -7023,15 +7064,23 @@ public boolean suggestOnlyApiStickers () {
public int maxMessageTextLength () {
return maxMessageTextLength;
}
-
- public long chatFolderCountMax () {
+
+ public int chatFolderCountMax () {
return chatFolderMaxCount;
}
- public long chatFolderChosenChatCountMax () {
+ public int chatFolderChosenChatCountMax () {
return folderChosenChatMaxCount;
}
+ public int chatFolderInviteLinkCountMax () {
+ return chatFolderInviteLinkMaxCount;
+ }
+
+ public long chatFolderUpdatePeriodMillis () {
+ return TimeUnit.SECONDS.toMillis(chatFolderUpdatePeriod);
+ }
+
public long telegramAntiSpamUserId () {
return antiSpamBotUserId;
}
@@ -8351,7 +8400,7 @@ public TdApi.ChatActiveStories getActiveStories (long chatId, boolean allowReque
}
return null;
}
-
+
@TdlibThread
private void updateStoryListChatCount (TdApi.UpdateStoryListChatCount update) {
synchronized (dataLock) {
@@ -11589,4 +11638,28 @@ public void removeChatsFromChatFolder (int chatFolderId, TdApi.ChatFolder chatFo
}
});
}
+
+ public void processChatFolderNewChats (int chatFolderId, long[] addedChatIds, ResultHandler resultHandler) {
+ send(new TdApi.ProcessChatFolderNewChats(chatFolderId, addedChatIds), resultHandler.doOnResult((result) -> {
+ listeners().notifyChatFolderNewChatsChanged(chatFolderId);
+ }));
+ }
+
+ public void deleteChatFolderInviteLink (int chatFolderId, String inviteLink, ResultHandler resultHandler) {
+ send(new TdApi.DeleteChatFolderInviteLink(chatFolderId, inviteLink), resultHandler.doOnResult((result) -> {
+ listeners().notifyChatFolderInviteLinkDeleted(chatFolderId, inviteLink);
+ }));
+ }
+
+ public void createChatFolderInviteLink (int chatFolderId, String name, long[] chatIds, ResultHandler resultHandler) {
+ send(new TdApi.CreateChatFolderInviteLink(chatFolderId, name, chatIds), resultHandler.doOnResult((result) -> {
+ listeners().notifyChatFolderInviteLinkCreated(chatFolderId, result);
+ }));
+ }
+
+ public void editChatFolderInviteLink (int chatFolderId, String inviteLink, String name, long[] chatIds, ResultHandler resultHandler) {
+ send(new TdApi.EditChatFolderInviteLink(chatFolderId, inviteLink, name, chatIds), resultHandler.doOnResult((result) -> {
+ listeners().notifyChatFolderInviteLinkChanged(chatFolderId, result);
+ }));
+ }
}
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibListeners.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibListeners.java
index a2540a06ee..b0d64398a5 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibListeners.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibListeners.java
@@ -41,6 +41,7 @@ public class TdlibListeners {
final ReferenceList messageEditListeners;
final ReferenceList chatListeners;
final ReferenceList chatFoldersListeners;
+ final ReferenceIntMap chatFolderListeners;
final ReferenceMap chatListListeners;
final ReferenceList storyListeners;
final ReferenceList settingsListeners;
@@ -84,6 +85,7 @@ public TdlibListeners (Tdlib tdlib) {
this.storyListeners = new ReferenceList<>();
this.chatListListeners = new ReferenceMap<>(true);
this.chatFoldersListeners = new ReferenceList<>(true);
+ this.chatFolderListeners = new ReferenceIntMap<>(true);
this.settingsListeners = new ReferenceList<>(true);
this.stickersListeners = new ReferenceList<>(true);
this.animationsListeners = new ReferenceList<>();
@@ -430,6 +432,21 @@ public void unsubscribeFromChatFoldersUpdates (ChatFoldersListener listener) {
chatFoldersListeners.remove(listener);
}
+ @AnyThread
+ public void addChatFolderListener (int chatFolderId, ChatFolderListener listener) {
+ if (chatFolderId != 0) {
+ chatFolderListeners.add(chatFolderId, listener);
+ }
+
+ }
+
+ @AnyThread
+ public void removeChatFolderListener (int chatFolderId, ChatFolderListener listener) {
+ if (chatFolderId != 0) {
+ chatFolderListeners.remove(chatFolderId, listener);
+ }
+ }
+
@AnyThread
public void addReactionLoadListener (String reactionKey, ReactionLoadListener listener) {
reactionLoadListeners.add(reactionKey, listener);
@@ -828,6 +845,32 @@ public void notifyReactionLoaded (String reactionKey) {
}
}
+ // notifyChatFolder*
+
+ public void notifyChatFolderNewChatsChanged (int chatFolderId) {
+ runUpdate(chatFolderListeners.iterator(chatFolderId), (listener) -> {
+ listener.onChatFolderNewChatsChanged(chatFolderId);
+ });
+ }
+
+ public void notifyChatFolderInviteLinkDeleted (int chatFolderId, String inviteLink) {
+ runUpdate(chatFolderListeners.iterator(chatFolderId), (listener) -> {
+ listener.onChatFolderInviteLinkDeleted(chatFolderId, inviteLink);
+ });
+ }
+
+ public void notifyChatFolderInviteLinkChanged (int chatFolderId, TdApi.ChatFolderInviteLink inviteLink) {
+ runUpdate(chatFolderListeners.iterator(chatFolderId), (listener) -> {
+ listener.onChatFolderInviteLinkChanged(chatFolderId, inviteLink);
+ });
+ }
+
+ public void notifyChatFolderInviteLinkCreated (int chatFolderId, TdApi.ChatFolderInviteLink inviteLink) {
+ runUpdate(chatFolderListeners.iterator(chatFolderId), (listener) -> {
+ listener.onChatFolderInviteLinkCreated(chatFolderId, inviteLink);
+ });
+ }
+
// updateMessageInteractionInfo
private static void updateMessageInteractionInfo (TdApi.UpdateMessageInteractionInfo update, @Nullable Iterator list) {
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibSettingsManager.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibSettingsManager.java
index 798c833c26..2324b3a9c2 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibSettingsManager.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibSettingsManager.java
@@ -1073,6 +1073,9 @@ public boolean hasNotificationProblems () {
private static final int DEFAULT_CHAT_FOLDER_BADGE_FLAGS = 0;
private boolean isMainChatListEnabled () {
+ if (Config.RESTRICT_HIDING_MAIN_LIST) {
+ return true;
+ }
if (_mainChatListEnabled == null) {
_mainChatListEnabled = Settings.instance().getBoolean(key(MAIN_CHAT_LIST_ENABLED, tdlib.accountId()), DEFAULT_MAIN_CHAT_LIST_ENABLED);
}
@@ -1080,6 +1083,9 @@ private boolean isMainChatListEnabled () {
}
private void setMainChatListEnabled (boolean isMainChatListEnabled) {
+ if (Config.RESTRICT_HIDING_MAIN_LIST && !isMainChatListEnabled) {
+ return;
+ }
if (isMainChatListEnabled() != isMainChatListEnabled) {
_mainChatListEnabled = isMainChatListEnabled;
Settings.instance().putBoolean(key(MAIN_CHAT_LIST_ENABLED, tdlib.accountId()), isMainChatListEnabled);
@@ -1134,6 +1140,29 @@ public boolean isChatFolderEnabled (int chatFolderId) {
return !disabledChatFolderIds.has(chatFolderId);
}
+ public void setChatFolderEnabled (int chatFolderId, boolean isEnabled) {
+ if (isChatFolderEnabled(chatFolderId) == isEnabled) {
+ return;
+ }
+ IntSet disabledChatFolderIds = disabledChatFolderIds();
+ if (isEnabled) {
+ disabledChatFolderIds.remove(chatFolderId);
+ } else {
+ disabledChatFolderIds.add(chatFolderId);
+ }
+ if (disabledChatFolderIds.isEmpty()) {
+ Settings.instance().remove(key(DISABLED_CHAT_FILTER_IDS, tdlib.accountId()));
+ } else {
+ Settings.instance().putIntArray(key(DISABLED_CHAT_FILTER_IDS, tdlib.accountId()), disabledChatFolderIds.toArray());
+ }
+ if (chatListPositionListeners != null) {
+ TdApi.ChatListFolder chatList = new TdApi.ChatListFolder(chatFolderId);
+ for (ChatListPositionListener chatListPositionListener : chatListPositionListeners) {
+ chatListPositionListener.onChatListStateChanged(tdlib, chatList, isEnabled);
+ }
+ }
+ }
+
public boolean isChatListEnabled (TdApi.ChatList chatList) {
switch (chatList.getConstructor()) {
case TdApi.ChatListMain.CONSTRUCTOR: {
@@ -1171,17 +1200,7 @@ public void setChatListEnabled (TdApi.ChatList chatList, boolean isEnabled) {
}
case TdApi.ChatListFolder.CONSTRUCTOR: {
int chatFolderId = ((TdApi.ChatListFolder) chatList).chatFolderId;
- IntSet disabledChatFolderIds = disabledChatFolderIds();
- if (isEnabled) {
- disabledChatFolderIds.remove(chatFolderId);
- } else {
- disabledChatFolderIds.add(chatFolderId);
- }
- if (disabledChatFolderIds.isEmpty()) {
- Settings.instance().remove(key(DISABLED_CHAT_FILTER_IDS, tdlib.accountId()));
- } else {
- Settings.instance().putIntArray(key(DISABLED_CHAT_FILTER_IDS, tdlib.accountId()), disabledChatFolderIds.toArray());
- }
+ setChatFolderEnabled(chatFolderId, isEnabled);
break;
}
default: {
@@ -1189,11 +1208,6 @@ public void setChatListEnabled (TdApi.ChatList chatList, boolean isEnabled) {
throw Td.unsupported(chatList);
}
}
- if (chatListPositionListeners != null) {
- for (ChatListPositionListener chatListPositionListener : chatListPositionListeners) {
- chatListPositionListener.onChatListStateChanged(tdlib, chatList, isEnabled);
- }
- }
}
private IntSet disabledChatFolderIds () {
diff --git a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java
index 8da681a2c9..19fb3b5922 100644
--- a/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java
+++ b/app/src/main/java/org/thunderdog/challegram/telegram/TdlibUi.java
@@ -42,6 +42,7 @@
import androidx.annotation.StringRes;
import androidx.collection.LongSparseArray;
import androidx.core.os.CancellationSignal;
+import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -100,6 +101,7 @@
import org.thunderdog.challegram.tool.Strings;
import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.tool.Views;
+import org.thunderdog.challegram.ui.ChatFolderInviteLinkController;
import org.thunderdog.challegram.ui.ChatJoinRequestsController;
import org.thunderdog.challegram.ui.ChatLinkMembersController;
import org.thunderdog.challegram.ui.ChatLinksController;
@@ -160,6 +162,7 @@
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -1459,6 +1462,10 @@ private void shareUser (ViewController> context, TdApi.User user) {
}
}
+ public void shareUrl (TdlibDelegate context, String url) {
+ shareUrl(context, url, null, url, null);
+ }
+
public void shareUrl (TdlibDelegate context, String url, String internalShareText, String externalShareText, String externalShareButton) {
if (!StringUtils.isEmpty(url)) {
ShareController c = new ShareController(context.context(), context.tdlib());
@@ -2534,6 +2541,28 @@ private void checkInviteLink (final TdlibDelegate context, final String inviteLi
});
}
+ private static void checkChatFolderInviteLink(TdlibDelegate context, String inviteLinkUrl, TdApi.InternalLinkTypeChatFolderInvite invite, @Nullable UrlOpenParameters openParameters) {
+ context.tdlib().send(new TdApi.CheckChatFolderInviteLink(invite.inviteLink), (result, error) -> {
+ if (error != null) {
+ showLinkTooltip(context.tdlib(), R.drawable.baseline_error_24, TD.toErrorString(error), openParameters);
+ } else {
+ showChatFolderInviteLinkInfo(context, inviteLinkUrl, result);
+ }
+ });
+ }
+
+ private static void showChatFolderInviteLinkInfo (TdlibDelegate context, String inviteLinkUrl, TdApi.ChatFolderInviteLinkInfo inviteLinkInfo) {
+ if (TdlibManager.inBackgroundThread()) {
+ context.tdlib().ui().post(() -> {
+ showChatFolderInviteLinkInfo(context, inviteLinkUrl, inviteLinkInfo);
+ });
+ return;
+ }
+ ChatFolderInviteLinkController controller = new ChatFolderInviteLinkController(context.context(), context.tdlib());
+ controller.setArguments(new ChatFolderInviteLinkController.Arguments(inviteLinkUrl, inviteLinkInfo));
+ controller.show();
+ }
+
private boolean installLanguage (final TdlibDelegate context, final String languagePackId, @Nullable UrlOpenParameters openParameters) {
if (TD.isLocalLanguagePackId(languagePackId)) {
Log.e("Attempt to install custom local languagePackId:%s", languagePackId);
@@ -3553,7 +3582,6 @@ public void openInternalLinkType (TdlibDelegate context, @Nullable String origin
}
case TdApi.InternalLinkTypeStory.CONSTRUCTOR:
- case TdApi.InternalLinkTypeChatFolderInvite.CONSTRUCTOR:
case TdApi.InternalLinkTypeDefaultMessageAutoDeleteTimerSettings.CONSTRUCTOR:
case TdApi.InternalLinkTypeAttachmentMenuBot.CONSTRUCTOR:
@@ -3639,6 +3667,15 @@ public void openInternalLinkType (TdlibDelegate context, @Nullable String origin
}
break;
}
+ case TdApi.InternalLinkTypeChatFolderInvite.CONSTRUCTOR: {
+ if (Settings.instance().chatFoldersEnabled()) {
+ TdApi.InternalLinkTypeChatFolderInvite invite = (TdApi.InternalLinkTypeChatFolderInvite) linkType;
+ checkChatFolderInviteLink(context, originalUrl, invite, openParameters);
+ } else {
+ showLinkTooltip(tdlib, R.drawable.baseline_warning_24, Lang.getString(R.string.InternalUrlUnsupported), openParameters);
+ }
+ break;
+ }
case TdApi.InternalLinkTypeUnknownDeepLink.CONSTRUCTOR: {
// TODO progress
TdApi.InternalLinkTypeUnknownDeepLink unknownDeepLink = (TdApi.InternalLinkTypeUnknownDeepLink) linkType;
@@ -3713,7 +3750,7 @@ public void showUrlOptions (TdlibDelegate context, String url, Future context, long[] chatI
for (TdApi.ChatFolderInfo chatFolderInfo : chatFolders) {
items.add(new ListItem(ListItem.TYPE_SETTING, R.id.chatFolder, TD.findFolderIcon(chatFolderInfo.icon, R.drawable.baseline_folder_24), chatFolderInfo.title).setIntValue(chatFolderInfo.id));
}
- if (tdlib.chatFoldersCount() < tdlib.chatFolderCountMax()) {
+ if (tdlib.canCreateChatFolder()) {
items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_createNewFolder, R.drawable.baseline_create_new_folder_24, R.string.CreateNewFolder).setTextColorId(ColorId.textNeutral));
}
SettingsWrap[] settings = new SettingsWrap[1];
@@ -4891,6 +4928,13 @@ public void showAddChatsToFolderOptions (ViewController> context, long[] chatI
}));
}
+ public void showDeleteChatFolderConfirm (ViewController> context, boolean hasMyInviteLinks, Runnable after) {
+ // TODO(nikita-toropov) wording
+ int infoRes = hasMyInviteLinks ? R.string.DeleteFolderWithInviteLinksConfirm : R.string.RemoveFolderConfirm;
+ int actionRes = hasMyInviteLinks ? R.string.Delete : R.string.Remove;
+ context.showConfirm(Lang.getMarkdownString(context, infoRes), Lang.getString(actionRes), R.drawable.baseline_delete_24, ViewController.OptionColor.RED, after);
+ }
+
public boolean processChatAction (ViewController> context, final TdApi.ChatList chatList, final long chatId, final @Nullable ThreadInfo messageThread, final TdApi.MessageSource source, final int actionId, @Nullable Runnable after) {
TdApi.Chat chat = tdlib.chat(chatId);
if (chat == null)
@@ -6605,7 +6649,24 @@ public static CharSequence getTdlibVersionSignature () {
RESTRICT_VOICE_AND_VIDEO_MESSAGES = 2;
}
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ PremiumLimit.SHAREABLE_FOLDER_COUNT,
+ PremiumLimit.CHAT_FOLDER_COUNT,
+ PremiumLimit.CHAT_FOLDER_INVITE_LINK_COUNT,
+ })
+ public @interface PremiumLimit {
+ int
+ SHAREABLE_FOLDER_COUNT = 1,
+ CHAT_FOLDER_COUNT = 2,
+ CHAT_FOLDER_INVITE_LINK_COUNT = 3;
+ }
+
public boolean showPremiumAlert (ViewController> context, View view, @PremiumFeature int premiumFeature) {
+ return showPremiumAlert(context, context.context().tooltipManager(), view, premiumFeature);
+ }
+
+ public boolean showPremiumAlert (ViewController> context, TooltipOverlayView tooltipManager, View view, @PremiumFeature int premiumFeature) {
if (tdlib.hasPremium())
return false;
int stringRes;
@@ -6619,21 +6680,51 @@ public boolean showPremiumAlert (ViewController> context, View view, @PremiumF
default:
throw new IllegalStateException();
}
- // TODO proper alert with sections
- context
- .context()
- .tooltipManager()
+ showPremiumRequiredTooltip(context, tooltipManager, view, stringRes);
+ return true;
+ }
+
+ public void showPremiumLimitInfo (ViewController> context, View view, @PremiumLimit int premiumLimit) {
+ showPremiumLimitInfo(context, context.context().tooltipManager(), view, premiumLimit);
+ }
+
+ public void showPremiumLimitInfo (ViewController> context, TooltipOverlayView tooltipManager, View view, @PremiumLimit int premiumLimit) {
+ if (tdlib.hasPremium())
+ return;
+ switch (premiumLimit) {
+ case PremiumLimit.SHAREABLE_FOLDER_COUNT:
+ showPremiumLimitTooltip(context, tooltipManager, view, R.string.PremiumRequiredAddShareableFolder, new TdApi.PremiumLimitTypeShareableChatFolderCount());
+ break;
+ case PremiumLimit.CHAT_FOLDER_COUNT:
+ showPremiumLimitTooltip(context, tooltipManager, view, R.string.PremiumRequiredCreateFolder, new TdApi.PremiumLimitTypeChatFolderCount());
+ break;
+ case PremiumLimit.CHAT_FOLDER_INVITE_LINK_COUNT:
+ showPremiumRequiredTooltip(context, tooltipManager, view, R.string.PremiumRequiredCreateChatFolderInviteLink);
+ break;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ private void showPremiumLimitTooltip (ViewController> context, TooltipOverlayView tooltipManager, View view, @StringRes int markdownStringRes, TdApi.PremiumLimitType premiumLimitType) {
+ WeakReference viewRef = new WeakReference<>(view);
+ Object viewTag = view.getTag();
+ tdlib.send(new TdApi.GetPremiumLimit(premiumLimitType), (result, error) -> context.runOnUiThreadOptional(() -> {
+ View targetView = viewRef.get();
+ if (targetView != null && ViewCompat.isAttachedToWindow(targetView) && viewTag == targetView.getTag() && result.defaultValue < result.premiumValue) {
+ showPremiumRequiredTooltip(context, tooltipManager, targetView, markdownStringRes, result.defaultValue, result.premiumValue);
+ }
+ }));
+ }
+
+ private void showPremiumRequiredTooltip (ViewController> context, TooltipOverlayView tooltipManager, View view, @StringRes int markdownStringRes, Object... formatArgs) {
+ // TODO proper alert with sections
+ tooltipManager
.builder(view)
.icon(R.drawable.baseline_warning_24)
.controller(context)
- .show(tdlib,
- Strings.buildMarkdown(context,
- Lang.getString(stringRes),
- null
- )
- )
+ .show(tdlib, Lang.getMarkdownString(context, markdownStringRes, formatArgs))
.hideDelayed();
- return true;
}
// Video Chats & Live Streams
diff --git a/app/src/main/java/org/thunderdog/challegram/tool/Views.java b/app/src/main/java/org/thunderdog/challegram/tool/Views.java
index e7008c7d3e..59f75e1207 100644
--- a/app/src/main/java/org/thunderdog/challegram/tool/Views.java
+++ b/app/src/main/java/org/thunderdog/challegram/tool/Views.java
@@ -864,6 +864,16 @@ public static void setRightMargin (View view, int margin) {
}
}
+ public static void setLeftMargin (View view, int margin) {
+ if (view != null) {
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
+ if (params.leftMargin != margin) {
+ params.leftMargin = margin;
+ view.setLayoutParams(params);
+ }
+ }
+ }
+
public static int getTopMargin (View view) {
if (view != null) {
ViewGroup.LayoutParams params = view.getLayoutParams();
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderIconSelector.java b/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderIconSelector.java
index 8e9b0f72a7..0aabce6f2a 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderIconSelector.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderIconSelector.java
@@ -14,16 +14,15 @@
*/
package org.thunderdog.challegram.ui;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
import android.content.Context;
+import android.graphics.Canvas;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.Nullable;
+import androidx.core.util.ObjectsCompat;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -35,6 +34,7 @@
import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Drawables;
+import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.Views;
import org.thunderdog.challegram.widget.PopupLayout;
@@ -44,6 +44,7 @@
import java.util.List;
import me.vkryl.android.widget.FrameLayoutFix;
+import me.vkryl.core.ObjectUtils;
import me.vkryl.core.StringUtils;
public class ChatFolderIconSelector {
@@ -52,12 +53,14 @@ public class ChatFolderIconSelector {
private final Delegate delegate;
private final View popupView;
private final GridLayoutManager layoutManager;
+ private final @Nullable String selectedIconName;
private PopupLayout popupLayout;
- public ChatFolderIconSelector (ViewController> owner, Delegate delegate) {
+ public ChatFolderIconSelector (ViewController> owner, @Nullable String selectedIconName, Delegate delegate) {
this.context = owner.context();
this.delegate = delegate;
+ this.selectedIconName = selectedIconName;
List items = new ArrayList<>(TD.ICON_NAMES.length);
for (String iconName : TD.ICON_NAMES) {
@@ -72,10 +75,18 @@ protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
int size = MeasureSpec.getSize(widthMeasureSpec);
setMeasuredDimension(size, size);
}
+
+ @Override
+ protected void onDraw (Canvas canvas) {
+ ListItem item = (ListItem) getTag();
+ String iconName = item.getStringValue();
+ if (isSelectedIcon(iconName)) {
+ canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, Screen.dp(24f), Paints.fillingPaint(Theme.fillingColor()));
+ }
+ super.onDraw(canvas);
+ }
};
imageView.setScaleType(ImageView.ScaleType.CENTER);
- imageView.setColorFilter(Theme.getColor(ColorId.icon));
- owner.addThemeFilterListener(imageView, ColorId.icon);
Views.setClickable(imageView);
imageView.setOnClickListener(v -> {
ListItem item = (ListItem) imageView.getTag();
@@ -94,6 +105,12 @@ protected void setCustom (ListItem item, SettingHolder holder, int position) {
int iconResource = item.getIconResource();
if (iconResource != 0) {
imageView.setImageDrawable(Drawables.get(imageView.getResources(), iconResource));
+ String iconName = item.getStringValue();
+ boolean isSelected = ObjectsCompat.equals(iconName, selectedIconName);
+ int iconColorId = isSelected ? ColorId.iconActive : ColorId.icon;
+ imageView.setColorFilter(Theme.getColor(iconColorId));
+ owner.removeThemeListenerByTarget(imageView);
+ owner.addThemeFilterListener(imageView, iconColorId);
} else {
imageView.setImageDrawable(null);
}
@@ -112,9 +129,9 @@ protected void setCustom (ListItem item, SettingHolder holder, int position) {
owner.addThemeInvalidateListener(shadowView);
FrameLayoutFix popupView = new FrameLayoutFix(context);
- popupView.addView(shadowView, FrameLayoutFix.newParams(MATCH_PARENT, Screen.dp(7f), Gravity.TOP));
- popupView.addView(recyclerView, FrameLayoutFix.newParams(MATCH_PARENT, WRAP_CONTENT, Gravity.TOP, 0, Screen.dp(7f), 0, 0));
- popupView.setLayoutParams(FrameLayoutFix.newParams(MATCH_PARENT, WRAP_CONTENT, Gravity.BOTTOM));
+ popupView.addView(shadowView, FrameLayoutFix.newParams(ViewGroup.LayoutParams.MATCH_PARENT, Screen.dp(7f), Gravity.TOP));
+ popupView.addView(recyclerView, FrameLayoutFix.newParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.TOP, 0, Screen.dp(7f), 0, 0));
+ popupView.setLayoutParams(FrameLayoutFix.newParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM));
this.popupView = popupView;
}
@@ -152,6 +169,18 @@ private int computeSpanCount (int width) {
return Math.max(width / itemSize, 3);
}
+ private boolean isSelectedIcon (@Nullable String iconName) {
+ return isSameIcon(selectedIconName, iconName);
+ }
+
+ private static boolean isSameIcon (@Nullable String a, @Nullable String b) {
+ return ObjectUtils.equals(a, b) || (isFolderIcon(a) && isFolderIcon(b));
+ }
+
+ private static boolean isFolderIcon (@Nullable String iconName) {
+ return StringUtils.isEmpty(iconName) || "Custom".equals(iconName);
+ }
+
public interface Delegate {
void onIconClick (@Nullable TdApi.ChatFolderIcon icon);
@@ -160,8 +189,8 @@ default void onShow () {}
default void onDismiss () {}
}
- public static ChatFolderIconSelector show (ViewController> owner, Delegate delegate) {
- ChatFolderIconSelector selector = new ChatFolderIconSelector(owner, delegate);
+ public static ChatFolderIconSelector show (ViewController> owner, @Nullable String selectedIconName, Delegate delegate) {
+ ChatFolderIconSelector selector = new ChatFolderIconSelector(owner, selectedIconName, delegate);
selector.show();
return selector;
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderInviteLinkController.java b/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderInviteLinkController.java
new file mode 100644
index 0000000000..fbeec2a6df
--- /dev/null
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderInviteLinkController.java
@@ -0,0 +1,181 @@
+/*
+ * This file is a part of Telegram X
+ * Copyright © 2014 (tgx-android@pm.me)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * File created on 19/01/2024
+ */
+package org.thunderdog.challegram.ui;
+
+import android.content.Context;
+import android.view.WindowManager;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.drinkless.tdlib.TdApi;
+import org.thunderdog.challegram.R;
+import org.thunderdog.challegram.navigation.HeaderView;
+import org.thunderdog.challegram.navigation.ViewController;
+import org.thunderdog.challegram.telegram.Tdlib;
+import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.ColorState;
+import org.thunderdog.challegram.theme.Theme;
+import org.thunderdog.challegram.tool.Screen;
+import org.thunderdog.challegram.widget.PopupLayout;
+import org.thunderdog.challegram.widget.ViewPager;
+
+import me.vkryl.android.widget.FrameLayoutFix;
+import me.vkryl.core.ObjectUtils;
+
+public class ChatFolderInviteLinkController extends BottomSheetViewController {
+
+ @IntDef({MODE_INVITE_LINK, MODE_NEW_CHATS, MODE_DELETE_FOLDER})
+ public @interface Mode {
+ }
+
+ public static final int MODE_INVITE_LINK = 0;
+ public static final int MODE_NEW_CHATS = 1;
+ public static final int MODE_DELETE_FOLDER = 2;
+
+ public static class Arguments {
+ public final @Mode int mode;
+ public final int chatFolderId;
+ public final String chatFolderTitle;
+ public final long[] selectableChatIds;
+ public final @Nullable String inviteLinkUrl;
+ public final @Nullable TdApi.ChatFolderInviteLinkInfo inviteLinkInfo;
+
+ public Arguments (@NonNull String inviteLink, @NonNull TdApi.ChatFolderInviteLinkInfo inviteLinkInfo) {
+ this.mode = MODE_INVITE_LINK;
+ this.chatFolderId = inviteLinkInfo.chatFolderInfo.id;
+ this.chatFolderTitle = inviteLinkInfo.chatFolderInfo.title;
+ this.selectableChatIds = inviteLinkInfo.missingChatIds;
+ this.inviteLinkUrl = ObjectUtils.requireNonNull(inviteLink);
+ this.inviteLinkInfo = ObjectUtils.requireNonNull(inviteLinkInfo);
+ }
+
+ private Arguments (@Mode int mode, int chatFolderId, String chatFolderTitle, long[] chatIds) {
+ this.mode = mode;
+ this.chatFolderId = chatFolderId;
+ this.chatFolderTitle = chatFolderTitle;
+ this.selectableChatIds = chatIds;
+ this.inviteLinkUrl = null;
+ this.inviteLinkInfo = null;
+ }
+
+ public static Arguments newChats (TdApi.ChatFolderInfo chatFolderInfo, long[] chatIds) {
+ return newChats(chatFolderInfo.id, chatFolderInfo.title, chatIds);
+ }
+
+ public static Arguments newChats (int chatFolderId, String chatFolderTitle, long[] chatIds) {
+ return new Arguments(MODE_NEW_CHATS, chatFolderId, chatFolderTitle, chatIds);
+ }
+
+ public static Arguments deleteFolder (TdApi.ChatFolderInfo chatFolderInfo, long[] chatIds) {
+ return deleteFolder(chatFolderInfo.id, chatFolderInfo.title, chatIds);
+ }
+
+ public static Arguments deleteFolder (int chatFolderId, String chatFolderTitle, long[] chatIds) {
+ return new Arguments(MODE_DELETE_FOLDER, chatFolderId, chatFolderTitle, chatIds);
+ }
+ }
+
+ private final ChatFolderInviteLinkControllerPage singlePage;
+
+ public ChatFolderInviteLinkController (Context context, Tdlib tdlib) {
+ super(context, tdlib);
+ this.singlePage = new ChatFolderInviteLinkControllerPage(this);
+ }
+
+ @Override
+ public int getId () {
+ return R.id.controller_chatFolderInviteLink;
+ }
+
+ @Override
+ protected int getPagerItemCount () {
+ return 1;
+ }
+
+ @Override
+ protected void onBeforeCreateView () {
+ singlePage.getValue();
+ }
+
+ @Override
+ protected void onAfterCreateView () {
+ setLickViewColor(Theme.getColor(ColorId.headerLightBackground));
+ }
+
+ @Override
+ public void onThemeColorsChanged (boolean areTemp, ColorState state) {
+ super.onThemeColorsChanged(areTemp, state);
+ setLickViewColor(Theme.getColor(ColorId.headerLightBackground));
+ }
+
+ @Override
+ public void setArguments (Arguments args) {
+ super.setArguments(args);
+ singlePage.setArguments(args);
+ }
+
+ @Override
+ protected void onCreateView (Context context, FrameLayoutFix contentView, ViewPager pager) {
+ pager.setOffscreenPageLimit(1);
+ tdlib.ui().post(this::launchOpenAnimation);
+ }
+
+ @Override
+ protected ViewController> onCreatePagerItemForPosition (Context context, int position) {
+ if (position != 0) return null;
+ setHeaderPosition(getContentOffset() + HeaderView.getTopOffset());
+ setDefaultListenersAndDecorators(singlePage);
+ return singlePage;
+ }
+
+ @Override
+ protected int getHeaderHeight () {
+ return Screen.dp(56f);
+ }
+
+ @Override
+ protected int getContentOffset () {
+ return (getTargetHeight() - getHeaderHeight(true)) / 2;
+ }
+
+ @Override
+ protected boolean canHideByScroll () {
+ return true;
+ }
+
+ @Override
+ protected HeaderView onCreateHeaderView () {
+ return singlePage.getHeaderView();
+ }
+
+ @Override
+ public boolean needsTempUpdates () {
+ return true;
+ }
+
+ @Override
+ protected void setupPopupLayout (PopupLayout popupLayout) {
+ popupLayout.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
+ popupLayout.setBoundController(singlePage);
+ popupLayout.setPopupHeightProvider(this);
+ popupLayout.init(true);
+ popupLayout.setHideKeyboard();
+ popupLayout.setNeedRootInsets();
+ popupLayout.setTouchProvider(this);
+ popupLayout.setIgnoreHorizontal();
+ }
+}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderInviteLinkControllerPage.java b/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderInviteLinkControllerPage.java
new file mode 100644
index 0000000000..f5a17b4023
--- /dev/null
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ChatFolderInviteLinkControllerPage.java
@@ -0,0 +1,553 @@
+/*
+ * This file is a part of Telegram X
+ * Copyright © 2014 (tgx-android@pm.me)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * File created on 19/01/2024
+ */
+package org.thunderdog.challegram.ui;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.core.content.res.ResourcesCompat;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.drinkless.tdlib.TdApi;
+import org.thunderdog.challegram.R;
+import org.thunderdog.challegram.charts.LayoutHelper;
+import org.thunderdog.challegram.core.Lang;
+import org.thunderdog.challegram.data.DoubleTextWrapper;
+import org.thunderdog.challegram.navigation.BackHeaderButton;
+import org.thunderdog.challegram.navigation.HeaderView;
+import org.thunderdog.challegram.navigation.ViewController;
+import org.thunderdog.challegram.support.RippleSupport;
+import org.thunderdog.challegram.telegram.TdlibUi;
+import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.ColorState;
+import org.thunderdog.challegram.theme.Theme;
+import org.thunderdog.challegram.tool.Fonts;
+import org.thunderdog.challegram.tool.Screen;
+import org.thunderdog.challegram.tool.UI;
+import org.thunderdog.challegram.tool.Views;
+import org.thunderdog.challegram.ui.ChatFolderInviteLinkController.Arguments;
+import org.thunderdog.challegram.v.CustomRecyclerView;
+import org.thunderdog.challegram.widget.CheckBoxView;
+import org.thunderdog.challegram.widget.SeparatorView;
+import org.thunderdog.challegram.widget.SmallChatView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import me.vkryl.android.widget.FrameLayoutFix;
+import me.vkryl.core.ArrayUtils;
+import me.vkryl.core.collection.LongSet;
+
+public class ChatFolderInviteLinkControllerPage extends BottomSheetViewController.BottomSheetBaseRecyclerViewController implements View.OnClickListener {
+
+ private static final int NO_CHAT_FOLDER_ID = 0;
+ private static final float BUTTON_HEIGHT_DP = 56f;
+
+ private final BottomSheetViewController> parent;
+ private final LongSet selectedChatIds = new LongSet();
+
+ private @ChatFolderInviteLinkController.Mode int mode = ChatFolderInviteLinkController.MODE_INVITE_LINK;
+ private int chatFolderId = NO_CHAT_FOLDER_ID;
+ private String chatFolderTitle;
+ private long[] selectableChatIds = ArrayUtils.EMPTY_LONGS;
+ private @Nullable String inviteLinkUrl;
+ private @Nullable TdApi.ChatFolderInviteLinkInfo inviteLinkInfo;
+ private SettingsAdapter adapter;
+ private FrameLayoutFix actionButton;
+
+ public ChatFolderInviteLinkControllerPage (BottomSheetViewController> parent) {
+ super(parent.context(), parent.tdlib());
+ this.parent = parent;
+ }
+
+ @Override
+ public void setArguments (Arguments args) {
+ super.setArguments(args);
+ mode = args.mode;
+ chatFolderId = args.chatFolderId;
+ chatFolderTitle = args.chatFolderTitle;
+ inviteLinkUrl = args.inviteLinkUrl;
+ inviteLinkInfo = args.inviteLinkInfo;
+ selectableChatIds = args.selectableChatIds;
+ selectedChatIds.addAll(selectableChatIds);
+ }
+
+ @Override
+ public int getId () {
+ return R.id.controller_chatFolderInviteLink;
+ }
+
+ public HeaderView getHeaderView () {
+ if (headerView == null) {
+ headerView = new HeaderView(context);
+ headerView.initWithSingleController(this, false);
+ headerView.getFilling().setColor(Theme.fillingColor());
+ headerView.getFilling().setShadowAlpha(1f);
+ headerView.setWillNotDraw(false);
+ addThemeInvalidateListener(headerView);
+ }
+ return headerView;
+ }
+
+ @Override
+ public int getItemsHeight (RecyclerView parent) {
+ int totalHeight = 0;
+ List items = adapter.getItems();
+ for (ListItem item : items) {
+ int itemHeight;
+ if (item.getViewType() == ListItem.TYPE_DESCRIPTION) {
+ itemHeight = Math.max(Screen.dp(20f), item.getHeight());
+ } else {
+ itemHeight = SettingHolder.measureHeightForType(item);
+ }
+ totalHeight += itemHeight;
+ }
+ return totalHeight;
+ }
+
+ @Override
+ protected View onCreateView (Context context) {
+ View view = super.onCreateView(context);
+
+ FrameLayoutFix wrap = (FrameLayoutFix) view;
+ wrap.addView(actionButton = createActionButton(), LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, BUTTON_HEIGHT_DP, Gravity.BOTTOM));
+ updateActionButton();
+
+ SeparatorView bottomShadowView = SeparatorView.simpleSeparator(context, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 1f, Gravity.BOTTOM), false);
+ bottomShadowView.setAlignBottom();
+ wrap.addView(bottomShadowView);
+
+ int buttonHeight = Screen.dp(BUTTON_HEIGHT_DP);
+ Views.setBottomMargin(getRecyclerView(), buttonHeight);
+ Views.setBottomMargin(bottomShadowView, buttonHeight);
+ return view;
+ }
+
+ @Override
+ protected void onCreateView (Context context, CustomRecyclerView recyclerView) {
+ getHeaderView();
+ addThemeInvalidateListener(recyclerView);
+
+ recyclerView.setItemAnimator(null);
+ recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
+ recyclerView.setLayoutManager(new LinearLayoutManager(context, RecyclerView.VERTICAL, false));
+
+ adapter = new Adapter(this);
+ adapter.setNoEmptyProgress();
+ recyclerView.setAdapter(adapter);
+ buildCells();
+ }
+
+ private FrameLayoutFix createActionButton () {
+ TextView button = new TextView(context);
+ button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16f);
+ button.setTypeface(Fonts.getRobotoMedium());
+ button.setPadding(Screen.dp(12f), 0, Screen.dp(12f), 0);
+ button.setEllipsize(TextUtils.TruncateAt.END);
+ button.setGravity(Gravity.CENTER);
+ button.setAllCaps(true);
+
+ FrameLayoutFix frame = new FrameLayoutFix(context);
+ frame.setOnClickListener(this);
+ frame.setId(R.id.btn_done);
+ frame.addView(button, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER_HORIZONTAL));
+ RippleSupport.setSimpleWhiteBackground(frame, this);
+ return frame;
+ }
+
+ private void updateActionButton () {
+ boolean enabled = !isFinishing();
+ actionButton.setEnabled(enabled);
+ int textColorId;
+ if (enabled) {
+ textColorId = mode == ChatFolderInviteLinkController.MODE_DELETE_FOLDER ? ColorId.textNegative : ColorId.textLink;
+ } else {
+ textColorId = ColorId.textLight;
+ }
+ TextView textView = (TextView) actionButton.getChildAt(0);
+ textView.setTextColor(Theme.getColor(textColorId));
+ removeThemeListenerByTarget(textView);
+ addThemeTextColorListener(textView, textColorId);
+ CharSequence text;
+ switch (mode) {
+ case ChatFolderInviteLinkController.MODE_INVITE_LINK:
+ case ChatFolderInviteLinkController.MODE_NEW_CHATS:
+ text = getJoinChatsActionText();
+ break;
+ case ChatFolderInviteLinkController.MODE_DELETE_FOLDER:
+ text = getDeleteFolderActionText();
+ break;
+ default:
+ throw new IllegalStateException("mode = " + mode);
+ }
+ Views.setMediumText(textView, text);
+ }
+
+ private CharSequence getJoinChatsActionText () {
+ boolean hasFolder = chatFolderId != NO_CHAT_FOLDER_ID;
+ if (selectableChatIds.length == 0) {
+ return Lang.getString(R.string.OK);
+ }
+ int selectedChatCount = selectedChatIds.size();
+ if (selectedChatCount == 0) {
+ return Lang.getString(hasFolder ? R.string.DoNotJoinAnyChats : R.string.DoNotAddFolder);
+ }
+ return Lang.plural(hasFolder ? R.string.JoinXChats : R.string.AddFolderAndJoinXChats, selectedChatCount);
+ }
+
+ private CharSequence getDeleteFolderActionText () {
+ int selectedChatCount = selectedChatIds.size();
+ if (selectedChatCount == 0) {
+ return Lang.getString(R.string.DeleteFolder);
+ }
+ return Lang.plural(R.string.DeleteFolderAndLeaveXChats, selectedChatCount);
+ }
+
+ private void buildCells () {
+ List items = new ArrayList<>();
+
+ int selectableChatCount = selectableChatIds.length;
+ if (selectableChatCount > 0) {
+ int headerPluralRes;
+ int headerViewType;
+ switch (mode) {
+ case ChatFolderInviteLinkController.MODE_INVITE_LINK:
+ headerPluralRes = R.string.xChatsToJoin;
+ headerViewType = ListItem.TYPE_HEADER_WITH_CHECKBOX;
+ break;
+ case ChatFolderInviteLinkController.MODE_NEW_CHATS:
+ headerPluralRes = R.string.xNewChatsToJoin;
+ headerViewType = ListItem.TYPE_HEADER_WITH_CHECKBOX;
+ break;
+ case ChatFolderInviteLinkController.MODE_DELETE_FOLDER:
+ headerPluralRes = R.string.xChatsToLeave;
+ headerViewType = ListItem.TYPE_HEADER_WITH_CHECKBOX;
+ break;
+ default:
+ throw new IllegalStateException("mode = " + mode);
+ }
+ items.add(new ListItem(ListItem.TYPE_EMPTY_OFFSET_SMALL));
+ items.add(new ListItem(headerViewType, R.id.btn_check, 0, Lang.plural(headerPluralRes, selectableChatCount)));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ addChats(items, selectableChatIds, true);
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ int descriptionRes;
+ switch (mode) {
+ case ChatFolderInviteLinkController.MODE_INVITE_LINK:
+ case ChatFolderInviteLinkController.MODE_NEW_CHATS:
+ descriptionRes = R.string.ChatFolderInviteLinkJoinChatsHint;
+ break;
+ case ChatFolderInviteLinkController.MODE_DELETE_FOLDER:
+ descriptionRes = R.string.ChatFolderInviteLinkLeaveChatsHint;
+ break;
+ default:
+ descriptionRes = ResourcesCompat.ID_NULL;
+ break;
+ }
+ if (descriptionRes != ResourcesCompat.ID_NULL) {
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, descriptionRes));
+ }
+ }
+
+ int addedChatCount = inviteLinkInfo != null ? inviteLinkInfo.addedChatIds.length : 0;
+ if (addedChatCount > 0) {
+ items.add(new ListItem(ListItem.TYPE_HEADER_PADDED, 0, 0, Lang.plural(R.string.xChatsAlreadyJoined, addedChatCount)));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ addChats(items, inviteLinkInfo.addedChatIds, false);
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ }
+
+ adapter.replaceItems(items);
+ }
+
+ private void addChats (List outList, long[] chatIds, boolean selectable) {
+ for (int index = 0; index < chatIds.length; index++) {
+ if (index > 0) {
+ outList.add(new ListItem(ListItem.TYPE_SEPARATOR));
+ }
+ long chatId = chatIds[index];
+ outList.add(chatItem(chatId, selectable));
+ }
+ }
+
+ private ListItem chatItem (long chatId, boolean selectable) {
+ TdApi.Chat chat = tdlib.chatStrict(chatId);
+ boolean isPublic = tdlib.chatPublic(chatId);
+ CharSequence subtitle;
+ if (tdlib.isChannel(chatId)) {
+ subtitle = Lang.getString(isPublic ? R.string.ChannelPublic : R.string.ChannelPrivate);
+ } else if (tdlib.isMultiChat(chat)) {
+ subtitle = Lang.getString(isPublic ? R.string.GroupPublic : R.string.GroupPrivate);
+ } else {
+ subtitle = "";
+ }
+ DoubleTextWrapper data = new DoubleTextWrapper(tdlib, chat);
+ data.setForcedSubtitle(subtitle);
+ data.setAdminSignVisible(false, false);
+ data.setIsChecked(selectedChatIds.has(chatId), /* animated */ false);
+
+ ListItem item = new ListItem(ListItem.TYPE_CHAT_SMALL, R.id.chat);
+ item.setBoolValue(selectable);
+ item.setLongId(chatId);
+ item.setData(data);
+ return item;
+ }
+
+ @Override
+ public CharSequence getName () {
+ return chatFolderTitle;
+ }
+
+ @Override
+ public void onThemeColorsChanged (boolean areTemp, ColorState state) {
+ super.onThemeColorsChanged(areTemp, state);
+ if (headerView != null) {
+ headerView.resetColors(this, null);
+ }
+ }
+
+ @Override
+ protected void openMoreMenu () {
+ if (isFinishing()) return;
+ Options.Builder builder = new Options.Builder();
+ builder.item(new OptionItem(R.id.btn_copyLink, Lang.getString(R.string.InviteLinkCopy), OptionColor.NORMAL, R.drawable.baseline_content_copy_24));
+ builder.item(new OptionItem(R.id.btn_shareFolder, Lang.getString(R.string.ShareFolder), OptionColor.NORMAL, R.drawable.baseline_share_arrow_24));
+ Options options = builder.build();
+ showOptions(options, (view, id) -> {
+ if (id == R.id.btn_copyLink) {
+ UI.copyText(inviteLinkUrl, R.string.CopiedLink);
+ } else if (id == R.id.btn_shareFolder) {
+ parent.setDismissListener((popup) -> {
+ popup.setDismissListener(null);
+ tdlib.ui().shareUrl(this, inviteLinkUrl);
+ });
+ parent.hidePopupWindow(true);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ return true;
+ });
+ }
+
+ private boolean isFinishing () {
+ return parent.getPopupLayout().isWindowHidden();
+ }
+
+ @Override
+ protected int getBackButton () {
+ return BackHeaderButton.TYPE_CLOSE;
+ }
+
+ @Override
+ protected int getMenuId () {
+ Arguments arguments = getArgumentsStrict();
+ return arguments.mode == ChatFolderInviteLinkController.MODE_INVITE_LINK ? R.id.menu_more : 0;
+ }
+
+ @Override
+ protected int getHeaderTextColorId () {
+ return ColorId.text;
+ }
+
+ @Override
+ protected int getHeaderColorId () {
+ return ColorId.filling;
+ }
+
+ @Override
+ protected int getHeaderIconColorId () {
+ return ColorId.icon;
+ }
+
+ @Override
+ public boolean needsTempUpdates () {
+ return true;
+ }
+
+ @Override
+ public void onClick (View v) {
+ if (isFinishing()) return;
+ if (v.getId() == R.id.chat) {
+ onChatItemClicked((ListItem) v.getTag());
+ } else if (v.getId() == R.id.btn_done) {
+ switch (mode) {
+ case ChatFolderInviteLinkController.MODE_INVITE_LINK:
+ if (selectedChatIds.isEmpty()) {
+ parent.hidePopupWindow(true);
+ } else {
+ joinSelectedChats();
+ }
+ break;
+ case ChatFolderInviteLinkController.MODE_NEW_CHATS:
+ processNewChats();
+ break;
+ case ChatFolderInviteLinkController.MODE_DELETE_FOLDER:
+ showDeleteFolderConfirm();
+ break;
+ }
+ } else if (v.getId() == R.id.btn_check) {
+ if (selectedChatIds.size() < selectableChatIds.length) {
+ selectedChatIds.addAll(selectableChatIds);
+ } else {
+ selectedChatIds.clear();
+ }
+ adapter.updateAllValuedSettingsById(R.id.chat);
+ onSelectedChatCountChanged();
+ }
+ }
+
+ private void showDeleteFolderConfirm () {
+ TdApi.ChatFolderInfo chatFolderInfo = tdlib.chatFolderInfo(chatFolderId);
+ boolean hasMyInviteLinks = chatFolderInfo != null && chatFolderInfo.hasMyInviteLinks;
+ tdlib.ui().showDeleteChatFolderConfirm(this, hasMyInviteLinks, this::deleteFolder);
+ }
+
+ private void onChatItemClicked (ListItem item) {
+ boolean selectable = item.getBoolValue();
+ if (!selectable) {
+ return;
+ }
+ long chatId = item.getLongId();
+ boolean isChecked = selectedChatIds.add(chatId) || !selectedChatIds.remove(chatId);
+ DoubleTextWrapper data = (DoubleTextWrapper) item.getData();
+ data.setIsChecked(isChecked, /* animated */ true);
+ onSelectedChatCountChanged();
+ }
+
+ private void onSelectedChatCountChanged () {
+ adapter.updateValuedSettingById(R.id.btn_check);
+ updateActionButton();
+ }
+
+ private void joinSelectedChats () {
+ if (mode != ChatFolderInviteLinkController.MODE_INVITE_LINK) {
+ throw new IllegalStateException("mode = " + mode);
+ }
+ if (selectedChatIds.isEmpty()) {
+ return;
+ }
+ if (chatFolderId == NO_CHAT_FOLDER_ID) {
+ if (!tdlib.canAddShareableFolder()) {
+ UI.forceVibrateError(actionButton);
+ if (tdlib.hasPremium()) {
+ showTooltip(actionButton, R.string.ShareableFoldersLimitReached, tdlib.addedShareableChatFolderCountMax());
+ } else {
+ tdlib.ui().showPremiumLimitInfo(this, actionButton, TdlibUi.PremiumLimit.SHAREABLE_FOLDER_COUNT);
+ }
+ return;
+ }
+
+ if (!tdlib.canCreateChatFolder()) {
+ UI.forceVibrateError(actionButton);
+ if (tdlib.hasPremium()) {
+ showTooltip(actionButton, R.string.ChatFolderLimitReached, tdlib.chatFolderCountMax());
+ } else {
+ tdlib.ui().showPremiumLimitInfo(this, actionButton, TdlibUi.PremiumLimit.CHAT_FOLDER_COUNT);
+ }
+ return;
+ }
+ }
+ long[] chatIds = selectedChatIds.toArray();
+ tdlib.send(new TdApi.AddChatFolderByInviteLink(inviteLinkUrl, chatIds), tdlib.typedOkHandler(() -> {
+ UI.showToast(R.string.Done, Toast.LENGTH_SHORT);
+ }));
+ parent.hidePopupWindow(true);
+ }
+
+ private void processNewChats () {
+ if (mode != ChatFolderInviteLinkController.MODE_NEW_CHATS) {
+ throw new IllegalStateException("mode = " + mode);
+ }
+ long[] chatIds = selectedChatIds.toArray();
+ tdlib.processChatFolderNewChats(chatFolderId, chatIds, tdlib.typedOkHandler(() -> {
+ UI.showToast(R.string.Done, Toast.LENGTH_SHORT);
+ }));
+ parent.hidePopupWindow(true);
+ }
+
+ private void deleteFolder () {
+ if (mode != ChatFolderInviteLinkController.MODE_DELETE_FOLDER) {
+ throw new IllegalStateException("mode = " + mode);
+ }
+ long[] leaveChatIds = selectedChatIds.toArray();
+ tdlib.send(new TdApi.DeleteChatFolder(chatFolderId, leaveChatIds), tdlib.typedOkHandler(() -> {
+ UI.showToast(R.string.Done, Toast.LENGTH_SHORT);
+ }));
+ parent.hidePopupWindow(true);
+ }
+
+ private void showTooltip (View view, @StringRes int markdownStringRes, Object... formatArgs) {
+ showTooltip(view, Lang.getMarkdownString(this, markdownStringRes, formatArgs));
+ }
+
+ private void showTooltip (View view, CharSequence text) {
+ context()
+ .tooltipManager()
+ .builder(view)
+ .controller(this)
+ .show(tdlib, text)
+ .hideDelayed();
+ }
+
+ private class Adapter extends SettingsAdapter {
+ public Adapter (ViewController> context) {
+ super(context);
+ }
+
+ @NonNull
+ @Override
+ public SettingHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) {
+ SettingHolder viewHolder = super.onCreateViewHolder(parent, viewType);
+ if (viewType == ListItem.TYPE_DESCRIPTION) {
+ viewHolder.itemView.addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+ ListItem item = (ListItem) v.getTag();
+ item.setHeight(bottom - top);
+ });
+ }
+ return viewHolder;
+ }
+
+ @Override
+ protected void modifyChatView (ListItem item, SmallChatView chatView, @Nullable CheckBoxView checkBox, boolean isUpdate) {
+ if (item.getId() == R.id.chat) {
+ chatView.setEnabled(item.getBoolValue());
+
+ DoubleTextWrapper data = (DoubleTextWrapper) item.getData();
+ data.setIsChecked(selectedChatIds.has(item.getLongId()), isUpdate);
+ }
+ }
+
+ @Override
+ protected void setHeaderCheckBoxState (ListItem item, CheckBoxView checkBox, boolean isUpdate) {
+ if (item.getId() == R.id.btn_check) {
+ checkBox.setChecked(selectedChatIds.size() > 0, isUpdate);
+ checkBox.setDisabled(selectableChatIds.length == 0, isUpdate);
+ checkBox.setPartially(selectedChatIds.size() < selectableChatIds.length, isUpdate);
+ } else {
+ super.setHeaderCheckBoxState(item, checkBox, isUpdate);
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ChatsController.java b/app/src/main/java/org/thunderdog/challegram/ui/ChatsController.java
index 66f96248ab..75a3685465 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ChatsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ChatsController.java
@@ -47,6 +47,7 @@
import org.thunderdog.challegram.component.chat.MessagesManager;
import org.thunderdog.challegram.component.dialogs.ChatView;
import org.thunderdog.challegram.component.dialogs.ChatsAdapter;
+import org.thunderdog.challegram.component.dialogs.ChatsViewHolder;
import org.thunderdog.challegram.component.dialogs.SearchManager;
import org.thunderdog.challegram.config.Config;
import org.thunderdog.challegram.core.Lang;
@@ -70,6 +71,7 @@
import org.thunderdog.challegram.navigation.ViewPagerController;
import org.thunderdog.challegram.support.ViewSupport;
import org.thunderdog.challegram.telegram.ChatFilter;
+import org.thunderdog.challegram.telegram.ChatFolderListener;
import org.thunderdog.challegram.telegram.ChatListListener;
import org.thunderdog.challegram.telegram.ChatListener;
import org.thunderdog.challegram.telegram.ConnectionListener;
@@ -100,6 +102,7 @@
import org.thunderdog.challegram.tool.Views;
import org.thunderdog.challegram.unsorted.Settings;
import org.thunderdog.challegram.unsorted.Test;
+import org.thunderdog.challegram.util.Poller;
import org.thunderdog.challegram.util.StringList;
import org.thunderdog.challegram.v.ChatsRecyclerView;
import org.thunderdog.challegram.widget.BaseView;
@@ -142,7 +145,9 @@ public class ChatsController extends TelegramViewController chatFolderNewChatsPoller;
+
private Intent shareIntent;
public ChatsController (Context context, Tdlib tdlib) {
@@ -206,6 +213,14 @@ public boolean isPicker () {
return chatList;
}
+ public int chatFolderId () {
+ TdApi.ChatList chatList = chatList();
+ if (TD.isChatListFolder(chatList)) {
+ return ((TdApi.ChatListFolder) chatList).chatFolderId;
+ }
+ return NO_CHAT_FOLDER_ID;
+ }
+
private boolean needMessagesSearch;
@Override
@@ -243,7 +258,7 @@ public ChatPinSeparatorDecoration (ChatsController context) {
}
@Override
- public void onDraw (Canvas c, RecyclerView parent, RecyclerView.State state) {
+ public void onDraw (@NonNull Canvas c, RecyclerView parent, @NonNull RecyclerView.State state) {
final int childCount = parent.getChildCount();
final int separatorLeft = Screen.dp(72f);
final int separatorHeight = Math.max(1, Screen.dp(.5f, 3f));
@@ -257,16 +272,37 @@ public void onDraw (Canvas c, RecyclerView parent, RecyclerView.State state) {
for (int i = 0; i < childCount; i++) {
View view = parent.getChildAt(i);
+ RecyclerView.ViewHolder viewHolder = parent.getChildViewHolder(view);
+ int viewType = viewHolder.getItemViewType();
+ final int offsetTop, right;
if (view instanceof ChatView) {
- final int offsetTop = ((ChatView) view).isDragging() || (context.animatorFlags & ANIMATOR_FLAG_DRAGGING) != 0 ? 0 : (int) view.getTranslationY();
+ offsetTop = ((ChatView) view).isDragging() || (context.animatorFlags & ANIMATOR_FLAG_DRAGGING) != 0 ? 0 : (int) view.getTranslationY();
TGChat chat = ((ChatView) view).getChat();
- final int right = view.getMeasuredWidth();
+ right = view.getMeasuredWidth();
boolean needSeparator = true;
if (chat != null) {
int adapterPosition = parent.getChildAdapterPosition(view);
if (adapterPosition != RecyclerView.NO_POSITION) {
- TGChat nextChat = context.adapter.getChatAt(adapterPosition + 1);
+ TGChat nextChat = context.adapter.getChatByItemPosition(adapterPosition + 1);
boolean needSplit = nextChat != null && (chat.isArchive() != nextChat.isArchive() || chat.isPinnedOrSpecial() != nextChat.isPinnedOrSpecial());
+ boolean needShadowTop = adapterPosition > 0 && context.adapter.getItemViewType(adapterPosition - 1) == ChatsAdapter.VIEW_TYPE_SUGGESTED_CHATS;
+
+ if (needShadowTop) {
+ int top = view.getTop() + offsetTop;
+ if (shadowFactor != 0f) {
+
+ final int alpha = (int) (255f * maxAlpha * shadowFactor);
+ topShadowPaint.setAlpha(alpha);
+ c.save();
+ c.translate(0, top - ShadowView.simpleTopShadowHeight());
+ c.drawRect(0, 0, right, ShadowView.simpleTopShadowHeight(), topShadowPaint);
+ c.restore();
+ }
+ if (lineFactor != 0f) {
+ final int color = ColorUtils.alphaColor(lineFactor, Theme.separatorColor());
+ c.drawRect(0, top - separatorHeight, right, top, Paints.fillingPaint(color));
+ }
+ }
if (needSplit) {
final int top = view.getBottom() + offsetTop;
@@ -298,42 +334,6 @@ public void onDraw (Canvas c, RecyclerView parent, RecyclerView.State state) {
needSeparator = false;
}
-
- if (context.liveLocationHelper != null && (adapterPosition == context.getLiveLocationPosition())) {
- int decoratedTop = parent.getLayoutManager().getDecoratedTop(view) + offsetTop;
- int top = view.getTop() + offsetTop;
- int fillingTop = decoratedTop;
- if (adapterPosition > 0) {
- View prevView = parent.getLayoutManager().findViewByPosition(adapterPosition - 1);
- if (prevView != null) {
- fillingTop = Math.max(fillingTop, prevView.getBottom() + (int) prevView.getTranslationY());
- }
- }
- if (decoratedTop < top && top > 0) {
- c.drawRect(0, fillingTop, right, top, Paints.fillingPaint(Theme.backgroundColor()));
- needLiveLocation = true;
- liveLocationClipTop = fillingTop;
- liveLocationRight = right;
- liveLocationBottom = top;
- liveLocationTop = decoratedTop;
-
- if (adapterPosition == 0) {
- if (shadowFactor != 0f) {
- final int alpha = (int) (255f * maxAlpha * shadowFactor);
- topShadowPaint.setAlpha(alpha);
- c.save();
- c.translate(0, top);
- c.drawRect(0, 0, right, ShadowView.simpleTopShadowHeight(), topShadowPaint);
- c.restore();
- }
-
- if (lineFactor != 0f) {
- final int color = ColorUtils.alphaColor(lineFactor, Theme.separatorColor());
- c.drawRect(0, top - separatorHeight, right, top, Paints.fillingPaint(color));
- }
- }
- }
- }
}
}
if (needSeparator) {
@@ -344,6 +344,50 @@ public void onDraw (Canvas c, RecyclerView parent, RecyclerView.State state) {
c.drawRect(separatorLeft, separatorTop, right, separatorTop + separatorHeight, Paints.fillingPaint(separatorColor));
}
}
+ } else if (viewType == ChatsAdapter.VIEW_TYPE_SUGGESTED_CHATS) {
+ offsetTop = (int) view.getTranslationY();
+ right = view.getWidth();
+ c.drawRect(view.getX(), view.getY(), view.getX() + view.getWidth(), view.getY() + view.getHeight(), Paints.fillingPaint(Theme.backgroundColor()));
+ } else {
+ offsetTop = (int) view.getTranslationY();
+ right = view.getWidth();
+ }
+
+ int adapterPosition = parent.getChildAdapterPosition(view);
+ if (context.liveLocationHelper != null && (adapterPosition == context.getLiveLocationPosition())) {
+ int decoratedTop = parent.getLayoutManager().getDecoratedTop(view) + offsetTop;
+ int top = view.getTop() + offsetTop;
+ int fillingTop = decoratedTop;
+ if (adapterPosition > 0) {
+ View prevView = parent.getLayoutManager().findViewByPosition(adapterPosition - 1);
+ if (prevView != null) {
+ fillingTop = Math.max(fillingTop, prevView.getBottom() + (int) prevView.getTranslationY());
+ }
+ }
+ if (decoratedTop < top && top > 0) {
+ c.drawRect(0, fillingTop, right, top, Paints.fillingPaint(Theme.backgroundColor()));
+ needLiveLocation = true;
+ liveLocationClipTop = fillingTop;
+ liveLocationRight = right;
+ liveLocationBottom = top;
+ liveLocationTop = decoratedTop;
+
+ if (adapterPosition == 0 && viewType == ChatsAdapter.VIEW_TYPE_CHAT) {
+ if (shadowFactor != 0f) {
+ final int alpha = (int) (255f * maxAlpha * shadowFactor);
+ topShadowPaint.setAlpha(alpha);
+ c.save();
+ c.translate(0, top);
+ c.drawRect(0, 0, right, ShadowView.simpleTopShadowHeight(), topShadowPaint);
+ c.restore();
+ }
+
+ if (lineFactor != 0f) {
+ final int color = ColorUtils.alphaColor(lineFactor, Theme.separatorColor());
+ c.drawRect(0, top - separatorHeight, right, top, Paints.fillingPaint(color));
+ }
+ }
+ }
}
}
@@ -356,22 +400,29 @@ public void onDraw (Canvas c, RecyclerView parent, RecyclerView.State state) {
}
@Override
- public void getItemOffsets (Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+ public void getItemOffsets (@NonNull Rect outRect, @NonNull View view, RecyclerView parent, @NonNull RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
if (position == RecyclerView.NO_POSITION) {
outRect.bottom = outRect.top = 0;
return;
}
- TGChat current = context.adapter.getChatAt(position);
+ TGChat current = context.adapter.getChatByItemPosition(position);
if (current == null) {
if (context.adapter.getItemCount() > 0 && context.adapter.hasArchive() && context.hideArchive && position == context.adapter.getItemCount() - 1) {
outRect.bottom = Math.max(0, parent.getMeasuredHeight() - context.calculateTotalScrollContentHeight());
+ } else {
+ outRect.bottom = 0;
+ }
+ if (context.liveLocationHelper != null && context.liveLocationHelper.isVisible() && position == context.getLiveLocationPosition()) {
+ outRect.top = LiveLocationHelper.height();
+ } else {
+ outRect.top = 0;
}
return;
}
- TGChat next = context.adapter.getChatAt(position + 1);
+ TGChat next = context.adapter.getChatByItemPosition(position + 1);
if (next != null && (current.isArchive() != next.isArchive() || current.isPinnedOrSpecial() != next.isPinnedOrSpecial())) {
outRect.bottom = Screen.dp(12f);
} else {
@@ -428,7 +479,7 @@ protected View onCreateView (Context context) {
chatsView = (ChatsRecyclerView) Views.inflate(context(), R.layout.recycler_chats, contentView);
chatsView.setMeasureListener((v, oldWidth, oldHeight, newWidth, newHeight) -> {
if (newHeight != oldHeight && adapter.hasArchive() && hideArchive && adapter.getItemCount() > 0) {
- adapter.notifyItemChanged(adapter.getItemCount() - 1);
+ adapter.notifyLastItemChanged();
}
});
chatsView.setItemAnimator(null);
@@ -441,7 +492,7 @@ protected View onCreateView (Context context) {
touchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
@Override
- public int getMovementFlags (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ public int getMovementFlags (@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
if (viewHolder.getItemViewType() == ChatsAdapter.VIEW_TYPE_CHAT) {
TGChat chat = ((ChatView) viewHolder.itemView).getChat();
if (chat != null && chat.isPinned() && adapter.canDragPinnedChats() && filter == null) {
@@ -458,7 +509,7 @@ public boolean isLongPressDragEnabled () {
}
@Override
- public void onMoved (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y) {
+ public void onMoved (@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, int fromPos, @NonNull RecyclerView.ViewHolder target, int toPos, int x, int y) {
super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y);
viewHolder.itemView.invalidate();
target.itemView.invalidate();
@@ -471,12 +522,12 @@ public void onMoved (RecyclerView recyclerView, RecyclerView.ViewHolder viewHold
private int dragTo = -1;
@Override
- public boolean onMove (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
- int fromPosition = viewHolder.getAdapterPosition();
- int toPosition = target.getAdapterPosition();
+ public boolean onMove (@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
+ int fromPosition = viewHolder.getBindingAdapterPosition();
+ int toPosition = target.getBindingAdapterPosition();
- TGChat fromChat = adapter.getChatAt(fromPosition);
- TGChat toChat = adapter.getChatAt(toPosition);
+ TGChat fromChat = adapter.getChatByItemPosition(fromPosition);
+ TGChat toChat = adapter.getChatByItemPosition(toPosition);
if (fromChat == null || toChat == null || !fromChat.isPinned() || !toChat.isPinned()) {
return false;
@@ -504,7 +555,7 @@ public void onSelectedChanged (RecyclerView.ViewHolder viewHolder, int actionSta
}
@Override
- public void clearView (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
+ public void clearView (@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (dragFrom != -1 && dragTo != -1 && dragFrom != dragTo) {
adapter.savePinnedChats();
@@ -512,7 +563,9 @@ public void clearView (RecyclerView recyclerView, RecyclerView.ViewHolder viewHo
dragFrom = dragTo = -1;
chatsView.setItemAnimator(null);
setAnimatorFlag(ANIMATOR_FLAG_DRAGGING, false);
- ((ChatView) viewHolder.itemView).setIsDragging(false);
+ if (viewHolder.getItemViewType() == ChatsAdapter.VIEW_TYPE_CHAT) {
+ ((ChatView) viewHolder.itemView).setIsDragging(false);
+ }
if (dragTooltip != null) {
dragTooltip.hideDelayed();
}
@@ -546,6 +599,7 @@ public void onSwiped (@NonNull RecyclerView.ViewHolder viewHolder, int direction
Settings.instance().addChatListModeListener(this);
TGLegacyManager.instance().addEmojiListener(this);
tdlib.context().dateManager().addListener(this);
+ tdlib.listeners().addChatFolderListener(chatFolderId(), this);
tdlib.awaitMyUserOrUnauthorizedState(() -> {
executeOnUiThreadOptional(() -> {
@@ -590,7 +644,7 @@ public void onScrollStateChanged (@NonNull RecyclerView recyclerView, int newSta
setArchiveCollapsed(false);
View view = manager.findViewByPosition(firstVisiblePosition);
int top = view != null ? -manager.getDecoratedTop(view) : 0;
- int itemHeight = ChatView.getViewHeight(Settings.instance().getChatListMode());
+ int itemHeight = ChatsViewHolder.measureHeightForType(ChatsAdapter.VIEW_TYPE_CHAT);
if (top < itemHeight / 2) {
chatsView.smoothScrollBy(0, -top);
} else {
@@ -666,6 +720,36 @@ public void onChatListItemChanged (TdlibChatList chatList, TdApi.Chat chat, @Ite
return contentView;
}
+ @Override
+ protected void onFocusStateChanged () {
+ super.onFocusStateChanged();
+ if (isFocused()) {
+ int chatFolderId = chatFolderId();
+ if (chatFolderId != NO_CHAT_FOLDER_ID && tdlib.isFolderShareable(chatFolderId) && !isFiltered()) {
+ startChatFolderNewChatsPolling(chatFolderId);
+ }
+ } else {
+ stopChatFolderNewChatsPolling();
+ }
+ }
+
+ private void startChatFolderNewChatsPolling (int chatFolderId) {
+ stopChatFolderNewChatsPolling();
+ chatFolderNewChatsPoller = new Poller<>(tdlib, tdlib::chatFolderUpdatePeriodMillis, () -> new TdApi.GetChatFolderNewChats(chatFolderId), (result, error) -> {
+ if (result != null) {
+ adapter.setSuggestedChatIds(result.chatIds);
+ }
+ });
+ chatFolderNewChatsPoller.start();
+ }
+
+ private void stopChatFolderNewChatsPolling () {
+ if (chatFolderNewChatsPoller != null) {
+ chatFolderNewChatsPoller.stop();
+ chatFolderNewChatsPoller = null;
+ }
+ }
+
@TdlibThread
private void displayChats (List entries) {
int initialLoadCount = chatsView.getInitialLoadCount();
@@ -689,7 +773,7 @@ public boolean isPullingArchive () {
if (!hideArchive || adapter == null || !adapter.hasArchive())
return false;
LinearLayoutManager manager = (LinearLayoutManager) chatsView.getLayoutManager();
- return manager.findFirstVisibleItemPosition() == 0;
+ return manager.findFirstVisibleItemPosition() == adapter.getArchiveItemPosition();
}
private void setArchiveCollapsed (boolean collapsed) {
@@ -700,9 +784,12 @@ private void setArchiveCollapsed (boolean collapsed) {
this.archiveCollapsed = collapsed;
UI.post(() -> {
if (!isDestroyed()) {
- chatsView.saveScrollPosition();
- adapter.notifyItemChanged(0);
- chatsView.restoreScrollPosition();
+ int archivePosition = adapter.getArchiveItemPosition();
+ if (archivePosition != -1) {
+ chatsView.saveScrollPosition();
+ adapter.notifyItemChanged(archivePosition);
+ chatsView.restoreScrollPosition();
+ }
}
});
}
@@ -715,7 +802,7 @@ private void setHideArchive (boolean hide) {
if (chatsView == null) {
return;
}
- adapter.notifyItemChanged(adapter.getItemCount() - 1);
+ adapter.notifyLastItemChanged();
if (hide) {
onHideArchiveRequested();
// TODO scroll by so it becomes invisible
@@ -764,7 +851,7 @@ public void onLiveLocationClick (float x, float y) {
int top = view != null ? view.getTop() : 0;
if (top > 0) {
int decoratedTop = manager.getDecoratedTop(view);
- if (view instanceof ChatView && decoratedTop < top && y < top && y >= decoratedTop) {
+ if (/*view instanceof ChatView && */decoratedTop < top && y < top && y >= decoratedTop) {
y -= decoratedTop;
chatsView.stopScroll();
liveLocationHelper.onClickAt(x, y);
@@ -775,7 +862,7 @@ public void onLiveLocationClick (float x, float y) {
@Override
public boolean onBeforeVisibilityStateChanged (LiveLocationHelper helper, boolean isVisible, boolean willAnimate) {
- if (chatsView == null || !(parentController != null ? parentController.isFocused() : isFocused()) || adapter.getChats().isEmpty()) {
+ if (chatsView == null || !(parentController != null ? parentController.isFocused() : isFocused()) || !adapter.hasChats()) {
return false;
}
@@ -795,20 +882,26 @@ public boolean onBeforeVisibilityStateChanged (LiveLocationHelper helper, boolea
@Override
public void onAfterVisibilityStateChanged (LiveLocationHelper helper, boolean isVisible, boolean willAnimate) {
- if (chatsView != null && !adapter.getChats().isEmpty()) {
+ if (chatsView != null && adapter.hasChats()) {
int position = getLiveLocationPosition();
adapter.notifyItemChanged(position);
if (position > 0) {
adapter.notifyItemChanged(position - 1);
}
if (adapter.hasArchive() && hideArchive) {
- adapter.notifyItemChanged(adapter.getItemCount() - 1);
+ adapter.notifyLastItemChanged();
}
}
}
public int getLiveLocationPosition () {
- return adapter.hasArchive() ? 1 : 0;
+ if (adapter.hasSuggestedChats()) {
+ return 0;
+ }
+ if (adapter.hasChats()) {
+ return adapter.getFirstChatItemPosition() + (adapter.hasArchive() ? 1 : 0);
+ }
+ return 0;
}
@Override
@@ -898,16 +991,16 @@ public void onScrollToTopRequested () {
LinearLayoutManager manager = (LinearLayoutManager) chatsView.getLayoutManager();
int pinnedItemCount = adapter.getHeaderChatCount(true, null);
- int itemHeight = ChatView.getViewHeight(Settings.instance().getChatListMode());
+ int chatItemHeight = ChatsViewHolder.measureHeightForType(ChatsAdapter.VIEW_TYPE_CHAT);
int firstVisiblePosition = manager.findFirstVisibleItemPosition();
if (firstVisiblePosition == RecyclerView.NO_POSITION) {
return;
}
- int totalScrollBy = itemHeight * firstVisiblePosition;
+ int totalScrollBy = chatItemHeight * firstVisiblePosition;
int separatorItemCount = firstVisiblePosition;
- int chatsCount = adapter.getItemCount() - 1;
+ int chatsCount = adapter.getChatCount();
if (adapter.hasArchive() && chatsCount > 1 && firstVisiblePosition > 0) {
separatorItemCount--;
@@ -928,7 +1021,11 @@ public void onScrollToTopRequested () {
totalScrollBy += separatorItemCount * Screen.separatorSize();
if (adapter.hasArchive() && hideArchive) {
- totalScrollBy -= itemHeight + (liveLocationHelper != null && liveLocationHelper.isVisible() ? Screen.dp(1f) : Screen.dp(12f));
+ totalScrollBy -= chatItemHeight + (liveLocationHelper != null && liveLocationHelper.isVisible() ? Screen.dp(1f) : Screen.dp(12f));
+ }
+
+ if (adapter.hasSuggestedChats()) {
+ totalScrollBy += ChatsViewHolder.measureHeightForType(ChatsAdapter.VIEW_TYPE_SUGGESTED_CHATS);
}
View firstView = manager.findViewByPosition(firstVisiblePosition);
@@ -941,11 +1038,11 @@ public void onScrollToTopRequested () {
protected int calculateTotalScrollContentHeight () {
int pinnedItemCount = adapter.getHeaderChatCount(true, null);
- int itemHeight = ChatView.getViewHeight(Settings.instance().getChatListMode());
+ int chatHeight = ChatsViewHolder.measureHeightForType(ChatsAdapter.VIEW_TYPE_CHAT);
- int chatsCount = adapter.getItemCount() - 1;
+ int chatsCount = adapter.getChatCount();
- int totalScrollBy = itemHeight * chatsCount;
+ int totalScrollBy = chatHeight * chatsCount;
int separatorItemCount = chatsCount;
if (adapter.hasArchive() && chatsCount > 1) {
@@ -967,10 +1064,14 @@ protected int calculateTotalScrollContentHeight () {
totalScrollBy += separatorItemCount * Screen.separatorSize();
if (adapter.hasArchive() && hideArchive) {
- totalScrollBy -= itemHeight + (liveLocationHelper != null && liveLocationHelper.isVisible() ? Screen.dp(1f) : Screen.dp(12f));
+ totalScrollBy -= chatHeight + (liveLocationHelper != null && liveLocationHelper.isVisible() ? Screen.dp(1f) : Screen.dp(12f));
}
- totalScrollBy += SettingHolder.measureHeightForType(ListItem.TYPE_LIST_INFO_VIEW);
+ totalScrollBy += ChatsViewHolder.measureHeightForType(ChatsAdapter.VIEW_TYPE_INFO);
+
+ if (adapter.hasSuggestedChats()) {
+ totalScrollBy += ChatsViewHolder.measureHeightForType(ChatsAdapter.VIEW_TYPE_SUGGESTED_CHATS);
+ }
return totalScrollBy;
}
@@ -995,7 +1096,7 @@ public int getBackButton () {
}
private boolean isBaseController () {
- return pickerDelegate == null && ((getArguments() != null && getArguments().isBaseController) || chatList().getConstructor() == TdApi.ChatListMain.CONSTRUCTOR); // FIXME replace with indexInStack() == 0
+ return pickerDelegate == null && ((getArguments() != null && getArguments().isBaseController) || TD.isChatListMain(chatList())); // FIXME replace with indexInStack() == 0
}
@Override
@@ -1004,7 +1105,7 @@ protected int getMenuId () {
}
private boolean isArchiveChatList () {
- return pickerDelegate == null && filter == null && chatList().getConstructor() == TdApi.ChatListArchive.CONSTRUCTOR;
+ return pickerDelegate == null && !isFiltered() && TD.isChatListArchive(chatList());
}
@Override
@@ -1637,14 +1738,14 @@ public void onMoreItemPressed (int id) {
}
};
if (id == R.id.more_btn_archiveUnarchive) {
- boolean isUnarchvie = chatList().getConstructor() == TdApi.ChatListArchive.CONSTRUCTOR;
+ boolean isUnarchive = chatList().getConstructor() == TdApi.ChatListArchive.CONSTRUCTOR;
showOptions(
- Lang.pluralBold(isUnarchvie ? R.string.UnarchiveXChats : R.string.ArchiveXChats, selectedChats.size()),
+ Lang.pluralBold(isUnarchive ? R.string.UnarchiveXChats : R.string.ArchiveXChats, selectedChats.size()),
new int[] {R.id.btn_archiveUnarchiveChat, R.id.btn_cancel},
- new String[] {Lang.getString(isUnarchvie ? R.string.Unarchive : R.string.Archive), Lang.getString(R.string.Cancel)}, null,
- new int[] {isUnarchvie ? R.drawable.baseline_unarchive_24 : R.drawable.baseline_archive_24, R.drawable.baseline_cancel_24}, (v, optionId) -> {
+ new String[] {Lang.getString(isUnarchive ? R.string.Unarchive : R.string.Archive), Lang.getString(R.string.Cancel)}, null,
+ new int[] {isUnarchive ? R.drawable.baseline_unarchive_24 : R.drawable.baseline_archive_24, R.drawable.baseline_cancel_24}, (v, optionId) -> {
if (optionId == R.id.btn_archiveUnarchiveChat) {
- TdApi.ChatList chatList = isUnarchvie ? new TdApi.ChatListMain() : new TdApi.ChatListArchive();
+ TdApi.ChatList chatList = isUnarchive ? new TdApi.ChatListMain() : new TdApi.ChatListArchive();
for (int i = 0; i < selectedChats.size(); i++) {
tdlib.client().send(new TdApi.AddChatToList(selectedChats.keyAt(i), chatList), result -> {
switch (result.getConstructor()) {
@@ -1963,6 +2064,14 @@ public void onClick (View v) {
onClick(chat.getChat());
}
}
+ } else if (v.getId() == R.id.btn_chatsSuggestion) {
+ long[] suggestedChatIds = adapter.getSuggestedChatIds();
+ TdApi.ChatFolderInfo chatFolderInfo = tdlib.chatFolderInfo(chatFolderId());
+ if (suggestedChatIds.length > 0 && chatFolderInfo != null) {
+ ChatFolderInviteLinkController controller = new ChatFolderInviteLinkController(context, tdlib);
+ controller.setArguments(ChatFolderInviteLinkController.Arguments.newChats(chatFolderInfo, suggestedChatIds));
+ controller.show();
+ }
}
}
@@ -2217,7 +2326,7 @@ public final void checkDisplayProgress () {
public final void checkDisplayNoChats () {
adapter.checkArchive();
- boolean noChats = list.isEndReached() && adapter.getChats().size() == 0;
+ boolean noChats = list.isEndReached() && !adapter.hasChats();
setDisplayNoChats(noChats);
}
@@ -2298,7 +2407,7 @@ private void prepareNoChats () {
parentController.navigateTo(c);
}
} else if (viewId == R.id.btn_editFolder) {
- int chatFolderId = ((TdApi.ChatListFolder) chatList()).chatFolderId;
+ int chatFolderId = chatFolderId();
tdlib.send(new TdApi.GetChatFolder(chatFolderId), (chatFolder, error) -> runOnUiThreadOptional(() -> {
if (parentController == null)
return;
@@ -2320,7 +2429,7 @@ private void prepareNoChats () {
} else if (chatList instanceof TdApi.ChatListArchive) {
items.add(new ListItem(ListItem.TYPE_EMPTY, 0, 0, R.string.NoArchive));
} else if (chatList instanceof TdApi.ChatListFolder) {
- items.add(new ListItem(ListItem.TYPE_ICONIZED_EMPTY, R.id.changePhoneText, R.drawable.baseline_folder_96, Lang.getMarkdownString(this, R.string.FolderNoChatsToDisplay)));
+ items.add(new ListItem(ListItem.TYPE_ICONIZED_EMPTY, R.id.changePhoneText, R.drawable.baseline_folder_open_96, Lang.getMarkdownString(this, R.string.FolderNoChatsToDisplay)));
items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
items.add(new ListItem(ListItem.TYPE_BUTTON, R.id.btn_editFolder, 0, R.string.EditFolder));
items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
@@ -2576,6 +2685,7 @@ public void destroy () {
TGLegacyManager.instance().removeEmojiListener(this);
tdlib.contacts().removeListener(this);
tdlib.context().dateManager().removeListener(this);
+ tdlib.listeners().removeChatFolderListener(chatFolderId(), this);
}
// Updates
@@ -2885,6 +2995,15 @@ public void onUserStatusChanged (long userId, TdApi.UserStatus status, boolean u
}
}
+ @Override
+ public void onChatFolderNewChatsChanged (int chatFolderId) {
+ runOnUiThreadOptional(() -> {
+ if (chatFolderId == chatFolderId() && chatFolderNewChatsPoller != null && chatFolderNewChatsPoller.isStarted()) {
+ chatFolderNewChatsPoller.restart();
+ }
+ });
+ }
+
// System sharing
public void shareIntent (Intent intent) {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditBaseController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditBaseController.java
index 520f7b85eb..dff6f3f5dd 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EditBaseController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditBaseController.java
@@ -109,6 +109,10 @@ protected View onCreateView (Context context) {
return wrapper;
}
+ public RecyclerView getRecyclerView () {
+ return recyclerView;
+ }
+
@Override
public int getRootColorId () {
return getRecyclerBackgroundColorId();
@@ -169,6 +173,10 @@ protected boolean isDoneVisible () {
return doneVisible;
}
+ protected void onDoneVisibleChanged (boolean isVisible) {
+ // override
+ }
+
protected void setDoneVisible (boolean isVisible) {
if (this.doneVisible != isVisible) {
this.doneVisible = isVisible;
@@ -189,6 +197,7 @@ protected void setDoneVisible (boolean isVisible) {
}
doneButton.setIsVisible(isVisible, false);
}
+ onDoneVisibleChanged(isVisible);
}
}
@@ -198,6 +207,7 @@ protected void setInstantDoneVisible (boolean isVisible) {
this.doneVisibilityFactor = 1f;
doneButton.setMaximumAlpha(1f);
doneButton.setIsVisible(isVisible, false);
+ onDoneVisibleChanged(isVisible);
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderController.java
index a4c5ff24da..42e3020c15 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderController.java
@@ -14,23 +14,26 @@
*/
package org.thunderdog.challegram.ui;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
import android.content.Context;
+import android.graphics.Rect;
import android.os.Bundle;
+import android.text.InputFilter;
import android.text.InputType;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
import android.widget.FrameLayout;
import android.widget.ImageView;
-import android.widget.LinearLayout;
+import androidx.annotation.AnyThread;
import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
+import androidx.core.util.ObjectsCompat;
import androidx.core.view.ViewCompat;
+import androidx.core.widget.TextViewKt;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
@@ -39,25 +42,32 @@
import org.thunderdog.challegram.component.attach.CustomItemAnimator;
import org.thunderdog.challegram.component.base.SettingView;
import org.thunderdog.challegram.component.user.RemoveHelper;
+import org.thunderdog.challegram.config.Config;
import org.thunderdog.challegram.core.Lang;
import org.thunderdog.challegram.data.AvatarPlaceholder;
import org.thunderdog.challegram.data.TD;
import org.thunderdog.challegram.data.TGFoundChat;
-import org.thunderdog.challegram.navigation.HeaderView;
+import org.thunderdog.challegram.navigation.EditHeaderView;
+import org.thunderdog.challegram.navigation.NavigationController;
import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.support.RippleSupport;
import org.thunderdog.challegram.support.ViewSupport;
+import org.thunderdog.challegram.telegram.ChatFolderListener;
+import org.thunderdog.challegram.telegram.ChatFoldersListener;
import org.thunderdog.challegram.telegram.Tdlib;
import org.thunderdog.challegram.telegram.TdlibAccentColor;
+import org.thunderdog.challegram.telegram.TdlibUi;
import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Drawables;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.UI;
import org.thunderdog.challegram.tool.Views;
+import org.thunderdog.challegram.unsorted.Size;
import org.thunderdog.challegram.util.AdapterSubListUpdateCallback;
+import org.thunderdog.challegram.util.CharacterStyleFilter;
import org.thunderdog.challegram.util.ListItemDiffUtilCallback;
-import org.thunderdog.challegram.v.CustomRecyclerView;
+import org.thunderdog.challegram.util.PremiumLockModifier;
import org.thunderdog.challegram.widget.BetterChatView;
import org.thunderdog.challegram.widget.MaterialEditTextGroup;
@@ -65,13 +75,15 @@
import java.util.List;
import java.util.Set;
+import kotlin.Unit;
import me.vkryl.android.AnimatorUtils;
+import me.vkryl.android.text.CodePointCountFilter;
import me.vkryl.android.widget.FrameLayoutFix;
import me.vkryl.core.ArrayUtils;
import me.vkryl.core.StringUtils;
import me.vkryl.td.Td;
-public class EditChatFolderController extends RecyclerViewController implements View.OnClickListener, SettingsAdapter.TextChangeListener, SelectChatsController.Delegate {
+public class EditChatFolderController extends EditBaseController implements View.OnClickListener, View.OnLongClickListener, SettingsAdapter.TextChangeListener, SelectChatsController.Delegate, ChatFoldersListener, ChatFolderListener {
private static final int NO_CHAT_FOLDER_ID = 0;
private static final int COLLAPSED_CHAT_COUNT = 3;
@@ -129,24 +141,29 @@ public static EditChatFolderController newFolder (Context context, Tdlib tdlib,
private final @IdRes int excludedChatsPreviousItemId = R.id.btn_folderExcludeChats;
private final @IdRes int includedChatsNextItemId = ViewCompat.generateViewId();
private final @IdRes int excludedChatsNextItemId = ViewCompat.generateViewId();
+ private final @IdRes int inviteLinksPreviousItemId = R.id.btn_createInviteLink;
+ private final @IdRes int inviteLinksNextItemId = ViewCompat.generateViewId();
private boolean showAllIncludedChats;
private boolean showAllExcludedChats;
private SettingsAdapter adapter;
- private ListItem input;
+ private @Nullable ListItem input;
+ private @Nullable EditHeaderView headerCell;
- private int chatFolderId;
+ private volatile int chatFolderId;
private TdApi.ChatFolder originChatFolder;
private TdApi.ChatFolder editedChatFolder;
+ private @Nullable TdApi.ChatFolderInviteLink[] inviteLinks;
+
public EditChatFolderController (Context context, Tdlib tdlib) {
super(context, tdlib);
}
@Override
public boolean needAsynchronousAnimation () {
- return originChatFolder == null && chatFolderId != NO_CHAT_FOLDER_ID;
+ return chatFolderId != NO_CHAT_FOLDER_ID && (originChatFolder == null || originChatFolder.isShareable);
}
@Override
@@ -165,6 +182,22 @@ public CharSequence getName () {
return chatFolderId != NO_CHAT_FOLDER_ID ? arguments.chatFolderName : Lang.getString(R.string.NewFolder);
}
+ @Override
+ public View getCustomHeaderCell () {
+ return headerCell;
+ }
+
+ @Override
+ protected int getHeaderHeight () {
+ //noinspection deprecation
+ return headerCell != null ? Size.getHeaderBigPortraitSize(false) : super.getHeaderHeight();
+ }
+
+ @Override
+ protected int getRecyclerBackgroundColorId () {
+ return ColorId.background;
+ }
+
@Override
public void setArguments (Arguments args) {
super.setArguments(args);
@@ -174,46 +207,146 @@ public void setArguments (Arguments args) {
}
@Override
- protected void onCreateView (Context context, CustomRecyclerView recyclerView) {
+ protected void onCreateView (Context context, FrameLayoutFix contentView, RecyclerView recyclerView) {
+ if (Config.CHAT_FOLDERS_REDESIGN) {
+ headerCell = new EditHeaderView(context, this);
+ headerCell.setInput(editedChatFolder.title);
+ headerCell.setInputOptions(R.string.FolderNameHint, InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
+ headerCell.setOnPhotoClickListener(this::showIconSelector);
+ headerCell.setImeOptions(EditorInfo.IME_ACTION_DONE);
+ headerCell.getInputView().setFilters(new InputFilter[] {
+ new CodePointCountFilter(MAX_CHAT_FOLDER_TITLE_LENGTH),
+ new CharacterStyleFilter()
+ });
+ TextViewKt.doAfterTextChanged(headerCell.getInputView(), (editable) -> {
+ onTitleChanged(editable != null ? editable.toString() : "");
+ return Unit.INSTANCE;
+ });
+ setLockFocusView(headerCell.getInputView(), /* showAlways */ StringUtils.isEmpty(editedChatFolder.title));
+ //noinspection deprecation
+ Views.setTopMargin(recyclerView, Size.getHeaderSizeDifference(false));
+ updateFolderIcon();
+ }
+
ArrayList items = new ArrayList<>();
- items.add(new ListItem(ListItem.TYPE_HEADER_PADDED, 0, 0, R.string.FolderName));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(input = new ListItem(ListItem.TYPE_CUSTOM_SINGLE, R.id.input).setStringValue(editedChatFolder.title));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
- items.add(new ListItem(ListItem.TYPE_HEADER_PADDED, 0, 0, R.string.FolderIncludedChats));
+ if (Config.CHAT_FOLDERS_REDESIGN) {
+ if (chatFolderId != NO_CHAT_FOLDER_ID) {
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_visible, 0, R.string.FolderVisible));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ } else {
+ items.add(new ListItem(ListItem.TYPE_EMPTY_OFFSET_SMALL));
+ }
+ } else {
+ items.add(new ListItem(ListItem.TYPE_HEADER_PADDED, 0, 0, R.string.FolderName));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(input = new ListItem(ListItem.TYPE_CUSTOM_SINGLE, R.id.input).setStringValue(editedChatFolder.title));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ }
+
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.FolderIncludedChats));
items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_folderIncludeChats, R.drawable.baseline_add_24, R.string.FolderActionIncludeChats).setTextColorId(ColorId.inlineText));
fillIncludedChats(editedChatFolder, items);
items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM, includedChatsNextItemId));
items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.FolderIncludedChatsInfo)));
- items.add(new ListItem(ListItem.TYPE_HEADER_PADDED, 0, 0, R.string.FolderExcludedChats));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_folderExcludeChats, R.drawable.baseline_add_24, R.string.FolderActionExcludeChats).setTextColorId(ColorId.inlineText));
- fillExcludedChats(editedChatFolder, items);
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM, excludedChatsNextItemId));
- items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.FolderExcludedChatsInfo)));
+ if (!isShareable()) {
+ items.add(new ListItem(ListItem.TYPE_HEADER_PADDED, 0, 0, R.string.FolderExcludedChats));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_folderExcludeChats, R.drawable.baseline_add_24, R.string.FolderActionExcludeChats).setTextColorId(ColorId.inlineText));
+ fillExcludedChats(editedChatFolder, items);
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM, excludedChatsNextItemId));
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, Lang.getMarkdownString(this, R.string.FolderExcludedChatsInfo)));
+ }
if (chatFolderId != NO_CHAT_FOLDER_ID) {
+ items.add(new ListItem(ListItem.TYPE_HEADER_PADDED, 0, 0, R.string.InviteLinks));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP, 0));
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_createInviteLink, R.drawable.baseline_add_link_24, R.string.CreateANewLink)
+ .setTextColorId(ColorId.inlineText)
+ .setDrawModifier(new PremiumLockModifier()));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM, inviteLinksNextItemId));
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, R.string.ChatFolderInviteLinksInfo));
items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_removeFolder, R.drawable.baseline_delete_forever_24, R.string.RemoveFolder).setTextColorId(ColorId.textNegative));
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_removeFolder, R.drawable.baseline_delete_24, R.string.RemoveFolder).setTextColorId(ColorId.textNegative));
items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
items.add(new ListItem(ListItem.TYPE_PADDING).setHeight(Screen.dp(12f)));
}
+ items.add(new ListItem(ListItem.TYPE_ZERO_VIEW));
+
adapter = new Adapter(this);
- adapter.setLockFocusOn(this, /* showAlways */ StringUtils.isEmpty(editedChatFolder.title));
- adapter.setTextChangeListener(this);
+ if (input != null) {
+ adapter.setLockFocusOn(this, /* showAlways */ StringUtils.isEmpty(editedChatFolder.title));
+ adapter.setTextChangeListener(this);
+ }
adapter.setItems(items, false);
CustomItemAnimator itemAnimator = new CustomItemAnimator(AnimatorUtils.DECELERATE_INTERPOLATOR, 180l);
itemAnimator.setSupportsChangeAnimations(false);
recyclerView.setItemAnimator(itemAnimator);
+ recyclerView.addItemDecoration(new ItemDecoration());
recyclerView.setAdapter(adapter);
RemoveHelper.attach(recyclerView, new RemoveHelperCallback());
- if (originChatFolder == null && chatFolderId != NO_CHAT_FOLDER_ID) {
- loadChatFolder();
+ if (chatFolderId != NO_CHAT_FOLDER_ID) {
+ if (originChatFolder == null) {
+ loadChatFolder();
+ }
+ updateInviteLinks();
+ tdlib.listeners().subscribeToChatFoldersUpdates(this);
+ tdlib.listeners().addChatFolderListener(chatFolderId, this);
+ }
+ }
+
+ @Override
+ protected void onDoneVisibleChanged (boolean isVisible) {
+ if (recyclerView != null) {
+ recyclerView.invalidateItemDecorations();
+ adapter.notifyLastItemChanged();
+ }
+ }
+
+ @Override
+ public void destroy () {
+ super.destroy();
+ tdlib.listeners().unsubscribeFromChatFoldersUpdates(this);
+ tdlib.listeners().removeChatFolderListener(chatFolderId, this);
+ }
+
+ private boolean firstFocus;
+
+ @Override
+ public void onFocus () {
+ super.onFocus();
+ if (firstFocus) {
+ firstFocus = false;
+ } else {
+ updateInviteLinks();
+ if (chatFolderId != NO_CHAT_FOLDER_ID) {
+ checkFolderDeleted();
+ }
+ }
+ }
+
+ @Override
+ public void onChatFolderInviteLinkDeleted (int chatFolderId, String inviteLink) {
+ if (this.chatFolderId == chatFolderId) {
+ updateInviteLinks();
+ }
+ }
+
+ @Override
+ public void onChatFolderInviteLinkCreated (int chatFolderId, TdApi.ChatFolderInviteLink inviteLink) {
+ if (this.chatFolderId == chatFolderId) {
+ updateInviteLinks();
+ }
+ }
+
+ @Override
+ public void onChatFolderInviteLinkChanged (int chatFolderId, TdApi.ChatFolderInviteLink inviteLink) {
+ if (this.chatFolderId == chatFolderId) {
+ updateInviteLinks();
}
}
@@ -249,35 +382,18 @@ public boolean restoreInstanceState (Bundle in, String keyPrefix) {
return false;
}
- @Override
- protected int getMenuId () {
- return R.id.menu_done;
- }
-
- @Override
- public void fillMenuItems (int id, HeaderView header, LinearLayout menu) {
- if (id == R.id.menu_done) {
- header.addDoneButton(menu, this).setVisibility(canSaveChanges() ? View.VISIBLE : View.GONE);
- }
- }
-
- @Override
- public void onMenuItemPressed (int id, View view) {
- if (id == R.id.menu_btn_done) {
- saveChanges();
- }
- }
-
@Override
public void onClick (View v) {
int id = v.getId();
if (id == R.id.btn_folderIncludeChats) {
+ boolean showChatTypes = !isShareable();
SelectChatsController selectChats = new SelectChatsController(context, tdlib);
- selectChats.setArguments(SelectChatsController.Arguments.includedChats(this, chatFolderId, editedChatFolder));
+ selectChats.setArguments(SelectChatsController.Arguments.includedChats(this, chatFolderId, editedChatFolder, showChatTypes));
navigateTo(selectChats);
} else if (id == R.id.btn_folderExcludeChats) {
+ boolean showChatTypes = !isShareable();
SelectChatsController selectChats = new SelectChatsController(context, tdlib);
- selectChats.setArguments(SelectChatsController.Arguments.excludedChats(this, chatFolderId, editedChatFolder));
+ selectChats.setArguments(SelectChatsController.Arguments.excludedChats(this, chatFolderId, editedChatFolder, showChatTypes));
navigateTo(selectChats);
} else if (id == R.id.btn_showAdvanced) {
ListItem item = (ListItem) v.getTag();
@@ -292,8 +408,34 @@ public void onClick (View v) {
updateExcludedChats();
}
}
+ } else if (id == R.id.btn_visible) {
+ UI.forceVibrate(v, false);
+ boolean isEnabled = adapter.toggleView(v);
+ tdlib.settings().setChatFolderEnabled(chatFolderId, isEnabled);
} else if (id == R.id.btn_removeFolder) {
- showRemoveFolderConfirm();
+ if (originChatFolder.isShareable) {
+ tdlib.send(new TdApi.GetChatFolderChatsToLeave(chatFolderId), (result, error) -> runOnUiThreadOptional(() -> {
+ if (error != null) {
+ UI.showError(error);
+ } else if (result.totalCount > 0) {
+ ChatFolderInviteLinkController controller = new ChatFolderInviteLinkController(context, tdlib);
+ controller.setArguments(ChatFolderInviteLinkController.Arguments.deleteFolder(chatFolderId, originChatFolder.title, result.chatIds));
+ controller.show();
+ } else {
+ showRemoveFolderConfirm();
+ }
+ }));
+ } else {
+ showRemoveFolderConfirm();
+ }
+ } else if (id == R.id.btn_createInviteLink) {
+ onCreateInviteLinkClick(v);
+ } else if (id == R.id.btn_inviteLink) {
+ ListItem item = (ListItem) v.getTag();
+ TdApi.ChatFolderInviteLink inviteLink = (TdApi.ChatFolderInviteLink) item.getData();
+ EditChatFolderInviteLinkController controller = new EditChatFolderInviteLinkController(context, tdlib);
+ controller.setArguments(new EditChatFolderInviteLinkController.Arguments(chatFolderId, editedChatFolder, inviteLink));
+ navigateTo(controller);
} else if (id == R.id.chat || ArrayUtils.contains(TD.CHAT_TYPES, id)) {
int position = getRecyclerView().getChildAdapterPosition(v);
ListItem item = (ListItem) v.getTag();
@@ -301,15 +443,40 @@ public void onClick (View v) {
}
}
+ @Override
+ public boolean onLongClick (View v) {
+ int id = v.getId();
+ if (id == R.id.btn_inviteLink) {
+ ListItem item = (ListItem) v.getTag();
+ TdApi.ChatFolderInviteLink inviteLink = (TdApi.ChatFolderInviteLink) item.getData();
+ showInviteLinkOptions(inviteLink);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean onDoneClick () {
+ if (!isInProgress()) {
+ saveChanges(this::closeSelf);
+ }
+ return true;
+ }
+
@Override
public boolean onBackPressed (boolean fromTop) {
- if (hasChanges()) {
+ if (hasUnsavedChanges()) {
showUnsavedChangesPromptBeforeLeaving(/* onConfirm */ null);
return true;
}
return super.onBackPressed(fromTop);
}
+ @Override
+ public boolean canSlideBackFrom (NavigationController navigationController, float x, float y) {
+ return !hasUnsavedChanges();
+ }
+
@Override
public void onBlur () {
super.onBlur();
@@ -320,13 +487,26 @@ public void onBlur () {
@Override
public void onPrepareToShow () {
super.onPrepareToShow();
- updateMenuButton();
+ updateDoneButton();
+ }
+
+ @Override
+ public void onChatFoldersChanged (TdApi.ChatFolderInfo[] chatFolders, int mainChatListPosition) {
+ if (chatFolderId != NO_CHAT_FOLDER_ID && isFocused()) {
+ checkFolderDeleted();
+ }
}
@Override
public void onTextChanged (int id, ListItem item, MaterialEditTextGroup v, String text) {
+ if (item == input) {
+ onTitleChanged(text);
+ }
+ }
+
+ private void onTitleChanged (String text) {
editedChatFolder.title = text;
- updateMenuButton();
+ updateDoneButton();
}
private void fillIncludedChats (TdApi.ChatFolder chatFolder, List outList) {
@@ -402,14 +582,21 @@ private void loadChatFolder () {
if (error != null) {
UI.showError(error);
} else {
+ boolean isFirstLoad = this.originChatFolder == null;
updateChatFolder(chatFolder);
+ if (isFirstLoad && (!chatFolder.isShareable || inviteLinks != null)) {
+ executeScheduledAnimation();
+ }
}
}));
}
private void updateChatFolder (TdApi.ChatFolder chatFolder) {
+ if (this.originChatFolder == null) {
+ this.originChatFolder = TD.copyOf(chatFolder);
+ }
this.editedChatFolder = chatFolder;
- updateMenuButton();
+ updateDoneButton();
updateIncludedChats();
updateExcludedChats();
}
@@ -455,28 +642,43 @@ private void updateExcludedChats () {
}
private void updateFolderName () {
- if (StringUtils.isEmpty(editedChatFolder.title) && editedChatFolder.pinnedChatIds.length == 0 && editedChatFolder.includedChatIds.length == 0) {
- int[] includedChatTypes = TD.includedChatTypes(editedChatFolder);
- if (includedChatTypes.length == 1) {
- int includedChatType = includedChatTypes[0];
- String chatTypeName = Lang.getString(TD.chatTypeName(includedChatType));
- boolean hasChanges = false;
- if (input.setStringValueIfChanged(chatTypeName)) {
- editedChatFolder.title = chatTypeName;
- hasChanges = true;
- }
- if (editedChatFolder.icon == null) {
- TdApi.ChatFolderIcon chatTypeIcon = TD.chatTypeIcon(includedChatType);
- if (chatTypeIcon != null) {
- editedChatFolder.icon = chatTypeIcon;
- hasChanges = true;
- }
- }
- if (hasChanges) {
- adapter.updateSimpleItemById(input.getId());
- }
+ if (!StringUtils.isEmpty(editedChatFolder.title)) {
+ return;
+ }
+ if (editedChatFolder.pinnedChatIds.length > 0 || editedChatFolder.includedChatIds.length > 0) {
+ return;
+ }
+ int[] includedChatTypes = TD.includedChatTypes(editedChatFolder);
+ if (includedChatTypes.length != 1) {
+ return;
+ }
+ int includedChatType = includedChatTypes[0];
+ String chatTypeName = Lang.getString(TD.chatTypeName(includedChatType));
+ boolean isNameChanged = false;
+ boolean isIconChanged = false;
+ if (!StringUtils.equalsOrBothEmpty(editedChatFolder.title, chatTypeName)) {
+ editedChatFolder.title = chatTypeName;
+ isNameChanged = true;
+ if (input != null) {
+ input.setStringValue(chatTypeName);
+ }
+ if (headerCell != null) {
+ headerCell.setInput(chatTypeName);
+ }
+ }
+ if (editedChatFolder.icon == null) {
+ TdApi.ChatFolderIcon chatTypeIcon = TD.chatTypeIcon(includedChatType);
+ if (chatTypeIcon != null) {
+ editedChatFolder.icon = chatTypeIcon;
+ isIconChanged = true;
}
}
+ if (input != null && (isNameChanged || isIconChanged)) {
+ adapter.updateSimpleItemById(input.getId());
+ }
+ if (headerCell != null && isIconChanged) {
+ updateFolderIcon();
+ }
}
@Override
@@ -545,17 +747,17 @@ private void showRemoveConditionConfirm (int position, ListItem item) {
editedChatFolder.excludeArchived = false;
}
updateFolderName();
- updateMenuButton();
+ updateDoneButton();
});
}
private void showRemoveFolderConfirm () {
- showConfirm(Lang.getString(R.string.RemoveFolderConfirm), Lang.getString(R.string.Remove), R.drawable.baseline_delete_24, OptionColor.RED, () -> {
+ tdlib.ui().showDeleteChatFolderConfirm(this, hasInviteLinks(), () -> {
deleteChatFolder(chatFolderId);
});
}
- private boolean hasChanges () {
+ private boolean hasUnsavedChanges () {
TdApi.ChatFolder originChatFolder = this.originChatFolder != null ? this.originChatFolder : EMPTY_CHAT_FOLDER;
TdApi.ChatFolder editedChatFolder = this.editedChatFolder != null ? this.editedChatFolder : EMPTY_CHAT_FOLDER;
return !TD.contentEquals(originChatFolder, editedChatFolder);
@@ -571,27 +773,40 @@ private boolean canSaveChanges () {
return false;
}
return (editedChatFolder.includeContacts || editedChatFolder.includeNonContacts || editedChatFolder.includeGroups || editedChatFolder.includeChannels || editedChatFolder.includeBots || editedChatFolder.pinnedChatIds.length > 0 || editedChatFolder.includedChatIds.length > 0) &&
- (chatFolderId == NO_CHAT_FOLDER_ID || hasChanges());
+ (chatFolderId == NO_CHAT_FOLDER_ID || hasUnsavedChanges());
}
- private void saveChanges () {
+ private void saveChanges (Runnable after) {
if (chatFolderId != NO_CHAT_FOLDER_ID) {
- editChatFolder(chatFolderId, TD.copyOf(editedChatFolder));
+ editChatFolder(chatFolderId, TD.copyOf(editedChatFolder), after);
} else {
- createChatFolder(TD.copyOf(editedChatFolder));
+ createChatFolder(TD.copyOf(editedChatFolder), after);
}
}
- private void createChatFolder (TdApi.ChatFolder chatFolder) {
- tdlib.send(new TdApi.CreateChatFolder(chatFolder), tdlib.successHandler(this::closeSelf));
+ private void createChatFolder (TdApi.ChatFolder chatFolder, Runnable after) {
+ executeWithProgress(new TdApi.CreateChatFolder(chatFolder), after);
}
- private void editChatFolder (int chatFolderId, TdApi.ChatFolder chatFolder) {
- tdlib.send(new TdApi.EditChatFolder(chatFolderId, chatFolder), tdlib.successHandler(this::closeSelf));
+ private void editChatFolder (int chatFolderId, TdApi.ChatFolder chatFolder, Runnable after) {
+ executeWithProgress(new TdApi.EditChatFolder(chatFolderId, chatFolder), after);
}
private void deleteChatFolder (int chatFolderId) {
- tdlib.send(new TdApi.DeleteChatFolder(chatFolderId, null), tdlib.typedOkHandler(this::closeSelf));
+ executeWithProgress(new TdApi.DeleteChatFolder(chatFolderId, null), this::closeSelf);
+ }
+
+ private void executeWithProgress (TdApi.Function> request, Runnable onResult) {
+ setInProgress(true);
+ tdlib.send(request, (result, error) -> runOnUiThreadOptional(() -> {
+ setInProgress(false);
+ updateDoneButton();
+ if (error != null) {
+ UI.showError(error);
+ } else {
+ onResult.run();
+ }
+ }));
}
private void closeSelf () {
@@ -600,16 +815,26 @@ private void closeSelf () {
}
}
- private void updateMenuButton () {
- if (headerView != null) {
- headerView.updateButton(getMenuId(), R.id.menu_btn_done, canSaveChanges() ? View.VISIBLE : View.GONE, 0);
+ private void updateFolderIcon () {
+ if (input != null) {
+ adapter.updateSimpleItemById(input.getId());
}
+ if (headerCell != null) {
+ int iconResource = TD.findFolderIcon(editedChatFolder.icon, R.drawable.baseline_folder_24);
+ headerCell.setIcon(iconResource, ColorId.white);
+ }
+ }
+
+ private void updateDoneButton () {
+ boolean isDoneVisible = isInProgress() || canSaveChanges();
+ setDoneVisible(isDoneVisible);
}
private class Adapter extends SettingsAdapter {
public Adapter (ViewController> context) {
super(context);
}
+
@Override
protected void setChatData (ListItem item, int position, BetterChatView chatView) {
if (item.getId() == R.id.chat) {
@@ -628,7 +853,7 @@ protected void setChatData (ListItem item, int position, BetterChatView chatView
@Override
protected SettingHolder initCustom (ViewGroup parent) {
FrameLayoutFix frameLayout = new FrameLayoutFix(parent.getContext());
- frameLayout.setLayoutParams(new RecyclerView.LayoutParams(MATCH_PARENT, Screen.dp(57f)));
+ frameLayout.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Screen.dp(57f)));
ViewSupport.setThemedBackground(frameLayout, ColorId.filling, EditChatFolderController.this);
MaterialEditTextGroup editText = new MaterialEditTextGroup(parent.getContext(), false);
@@ -642,7 +867,7 @@ protected SettingHolder initCustom (ViewGroup parent) {
editText.getEditText().setLineDisabled(true);
editText.getEditText().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
- FrameLayout.LayoutParams editTextParams = new FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT);
+ FrameLayout.LayoutParams editTextParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
editTextParams.leftMargin = Screen.dp(16f);
editTextParams.rightMargin = Screen.dp(57f);
editTextParams.bottomMargin = Screen.dp(8f);
@@ -681,8 +906,33 @@ protected void setCustom (ListItem item, SettingHolder holder, int position) {
@Override
protected void setValuedSetting (ListItem item, SettingView view, boolean isUpdate) {
+ view.setDrawModifier(item.getDrawModifier());
+
+ if (item.getId() == R.id.btn_createInviteLink) {
+ view.setName(hasInviteLinks() ? R.string.CreateANewLink : R.string.CreateAnInviteLink);
+ PremiumLockModifier modifier = (PremiumLockModifier) ObjectsCompat.requireNonNull(item.getDrawModifier());
+ boolean showLockIcon = !canCreateInviteLink() && !tdlib.hasPremium() && chatTypeCount() == 0;
+ modifier.setVisible(showLockIcon);
+ view.setTooltipLocationProvider(showLockIcon ? modifier : null);
+ } else if (item.getId() == R.id.btn_inviteLink) {
+ TdApi.ChatFolderInviteLink inviteLink = (TdApi.ChatFolderInviteLink) item.getData();
+ view.setData(Lang.plural(R.string.xChats, inviteLink.chatIds.length));
+ } else if (item.getId() == R.id.btn_visible) {
+ boolean isEnabled = tdlib.settings().isChatFolderEnabled(chatFolderId);
+ view.getToggler().setRadioEnabled(isEnabled, isUpdate);
+ }
+
+ if (item.getId() == R.id.btn_createInviteLink) {
+ view.setIgnoreEnabled(true);
+ view.setVisuallyEnabled(canCreateInviteLink(), isUpdate);
+ } else {
+ view.setIgnoreEnabled(false);
+ }
+
if (item.getId() == R.id.btn_folderIncludeChats || item.getId() == R.id.btn_folderExcludeChats) {
view.setIconColorId(ColorId.inlineIcon);
+ } else if (item.getId() == R.id.btn_createInviteLink) {
+ view.setIconColorId(canCreateInviteLink() ? ColorId.inlineIcon : ColorId.textLight);
} else if (item.getId() == R.id.btn_removeFolder) {
view.setIconColorId(ColorId.iconNegative);
} else {
@@ -703,8 +953,16 @@ public boolean areItemsTheSame (ListItem oldItem, ListItem newItem) {
if (oldItem.getId() == R.id.chat) {
return oldItem.getLongId() == newItem.getLongId();
}
- if (oldItem.getViewType() == ListItem.TYPE_SEPARATOR)
- return oldItem.getIntValue() == newItem.getIntValue() && oldItem.getLongValue() == newItem.getLongValue();
+ if (oldItem.getId() == R.id.btn_inviteLink) {
+ TdApi.ChatFolderInviteLink oldData = (TdApi.ChatFolderInviteLink) oldItem.getData();
+ TdApi.ChatFolderInviteLink newData = (TdApi.ChatFolderInviteLink) newItem.getData();
+ return ObjectsCompat.equals(oldData.inviteLink, newData.inviteLink);
+ }
+ if (oldItem.getViewType() == ListItem.TYPE_SEPARATOR) {
+ return oldItem.getIntValue() == newItem.getIntValue() &&
+ oldItem.getLongValue() == newItem.getLongValue() &&
+ ObjectsCompat.equals(oldItem.getStringValue(), newItem.getStringValue());
+ }
return true;
}
@@ -717,24 +975,257 @@ public boolean areContentsTheSame (ListItem oldItem, ListItem newItem) {
private class RemoveHelperCallback implements RemoveHelper.Callback {
@Override
public boolean canRemove (RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int position) {
+ Object tag = viewHolder.itemView.getTag();
+ if (tag instanceof ListItem) {
+ ListItem item = (ListItem) tag;
+ if (item.getId() == R.id.btn_inviteLink) {
+ return true;
+ }
+ }
return viewHolder.getItemViewType() == ListItem.TYPE_CHAT_BETTER;
}
@Override
public void onRemove (RecyclerView.ViewHolder viewHolder) {
ListItem item = (ListItem) viewHolder.itemView.getTag();
- int position = viewHolder.getAbsoluteAdapterPosition();
- showRemoveConditionConfirm(position, item);
+ if (item.getViewType() == ListItem.TYPE_CHAT_BETTER) {
+ int position = viewHolder.getAbsoluteAdapterPosition();
+ showRemoveConditionConfirm(position, item);
+ } else if (item.getId() == R.id.btn_inviteLink) {
+ TdApi.ChatFolderInviteLink data = (TdApi.ChatFolderInviteLink) item.getData();
+ showDeleteInviteLinkConfirm(data.inviteLink);
+ }
}
}
private void showIconSelector () {
- ChatFolderIconSelector.show(this, icon -> {
- if (!Td.equalsTo(editedChatFolder.icon, icon)) {
- editedChatFolder.icon = icon;
- adapter.updateSimpleItemById(input.getId());
- updateMenuButton();
+ ChatFolderIconSelector.show(this, TD.getIconName(editedChatFolder), selectedIcon -> {
+ if (!Td.equalsTo(editedChatFolder.icon, selectedIcon)) {
+ editedChatFolder.icon = selectedIcon;
+ updateFolderIcon();
+ updateDoneButton();
+ }
+ });
+ }
+
+ @AnyThread
+ private void updateInviteLinks () {
+ if (chatFolderId == NO_CHAT_FOLDER_ID) {
+ return;
+ }
+ tdlib.send(new TdApi.GetChatFolderInviteLinks(chatFolderId), (result, error) -> {
+ TdApi.ChatFolderInviteLink[] inviteLinks = result != null ? result.inviteLinks : new TdApi.ChatFolderInviteLink[0];
+ runOnUiThreadOptional(() -> {
+ boolean isFirstLoad = this.inviteLinks == null;
+ updateInviteLinks(inviteLinks);
+ if (isFirstLoad && originChatFolder != null) {
+ executeScheduledAnimation();
+ }
+ });
+ });
+ }
+
+ private void updateInviteLinks (TdApi.ChatFolderInviteLink[] inviteLinks) {
+ this.inviteLinks = inviteLinks;
+ int previousItemIndex = adapter.indexOfViewById(inviteLinksPreviousItemId);
+ if (previousItemIndex == -1) {
+ return;
+ }
+ int nextItemIndex = adapter.indexOfViewById(inviteLinksNextItemId);
+ int firstItemIndex = previousItemIndex + 1;
+ TEMP_ITEM_LIST.clear();
+ fillInviteLinks(inviteLinks, TEMP_ITEM_LIST);
+ if (firstItemIndex < nextItemIndex) {
+ List oldList = adapter.getItems().subList(firstItemIndex, nextItemIndex);
+ DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtilCallback(oldList, TEMP_ITEM_LIST));
+ oldList.clear();
+ oldList.addAll(TEMP_ITEM_LIST);
+ diffResult.dispatchUpdatesTo(new AdapterSubListUpdateCallback(adapter, firstItemIndex));
+ } else if (TEMP_ITEM_LIST.size() > 0) {
+ adapter.addItems(firstItemIndex, TEMP_ITEM_LIST.toArray(new ListItem[0]));
+ }
+ TEMP_ITEM_LIST.clear();
+ adapter.updateValuedSettingById(R.id.btn_createInviteLink);
+ }
+
+ /**
+ * @noinspection SameParameterValue
+ */
+ private void fillInviteLinks (TdApi.ChatFolderInviteLink[] inviteLinks, List outList) {
+ for (TdApi.ChatFolderInviteLink inviteLink : inviteLinks) {
+ outList.add(new ListItem(ListItem.TYPE_SEPARATOR).setStringValue(inviteLink.inviteLink));
+ outList.add(inviteLink(inviteLink));
+ }
+ }
+
+ private ListItem inviteLink (TdApi.ChatFolderInviteLink inviteLink) {
+ String name = getName(inviteLink);
+ return new ListItem(ListItem.TYPE_VALUED_SETTING_COMPACT, R.id.btn_inviteLink, R.drawable.baseline_link_24, name).setData(inviteLink);
+ }
+
+ private void onCreateInviteLinkClick (View view) {
+ if (chatTypeCount() > 0) {
+ UI.forceVibrateError(view);
+ showTooltip(view, R.string.ChatFolderInviteLinkChatTypesNotSupported);
+ return;
+ }
+ if (!isShareable() && !tdlib.canAddShareableFolder()) {
+ showShareableFoldersLimitReached(view);
+ return;
+ }
+ if (inviteLinkCount() >= tdlib.chatFolderInviteLinkCountMax()) {
+ showInviteLinksLimitReached(view);
+ return;
+ }
+ if (hasUnsavedChanges()) {
+ showConfirm(Lang.getString(R.string.ChatFolderSaveChangesBeforeCreatingANewInviteLink), Lang.getString(R.string.LocalizationEditConfirmSave), () -> {
+ saveChanges(this::createInviteLink);
+ });
+ } else {
+ createInviteLink();
+ }
+ }
+
+ private void showShareableFoldersLimitReached (View view) {
+ UI.forceVibrateError(view);
+ if (tdlib.hasPremium()) {
+ showTooltip(view, R.string.ShareableFoldersLimitReached, tdlib.addedShareableChatFolderCountMax());
+ } else {
+ tdlib.ui().showPremiumLimitInfo(this, view, TdlibUi.PremiumLimit.SHAREABLE_FOLDER_COUNT);
+ }
+ }
+
+ private void showInviteLinksLimitReached (View view) {
+ UI.forceVibrateError(view);
+ if (tdlib.hasPremium()) {
+ showTooltip(view, R.string.ChatFolderInviteLinksLimitReached, tdlib.chatFolderInviteLinkCountMax());
+ } else {
+ tdlib.ui().showPremiumLimitInfo(this, view, TdlibUi.PremiumLimit.CHAT_FOLDER_INVITE_LINK_COUNT);
+ }
+ }
+
+ private void showTooltip (View view, @StringRes int markdownStringRes, Object... formatArgs) {
+ context.tooltipManager().builder(view).controller(this).show(tdlib, Lang.getMarkdownString(this, markdownStringRes, formatArgs)).hideDelayed();
+ }
+
+ private void showInviteLinkOptions (TdApi.ChatFolderInviteLink inviteLink) {
+ Options.Builder builder = new Options.Builder();
+ builder.info(getName(inviteLink));
+ builder.item(new OptionItem(R.id.btn_copyLink, Lang.getString(R.string.InviteLinkCopy), OptionColor.NORMAL, R.drawable.baseline_content_copy_24));
+ builder.item(new OptionItem(R.id.btn_shareLink, Lang.getString(R.string.ShareLink), OptionColor.NORMAL, R.drawable.baseline_share_arrow_24));
+ builder.item(new OptionItem(R.id.btn_deleteLink, Lang.getString(R.string.InviteLinkDelete), OptionColor.RED, R.drawable.baseline_delete_24));
+ showOptions(builder.build(), (view, id) -> {
+ if (id == R.id.btn_copyLink) {
+ copyInviteLink(inviteLink);
+ } else if (id == R.id.btn_shareLink) {
+ shareInviteLink(inviteLink);
+ } else if (id == R.id.btn_deleteLink) {
+ showDeleteInviteLinkConfirm(inviteLink.inviteLink);
}
+ return true;
});
}
+
+ private void copyInviteLink (TdApi.ChatFolderInviteLink inviteLink) {
+ UI.copyText(inviteLink.inviteLink, R.string.CopiedLink);
+ }
+
+ private void shareInviteLink (TdApi.ChatFolderInviteLink inviteLink) {
+ tdlib.ui().shareUrl(this, inviteLink.inviteLink);
+ }
+
+ private void showDeleteInviteLinkConfirm (String inviteLink) {
+ showConfirm(Lang.getString(R.string.AreYouSureDeleteInviteLink), Lang.getString(R.string.InviteLinkDelete), R.drawable.baseline_delete_24, OptionColor.RED, () -> {
+ deleteInviteLink(inviteLink);
+ });
+ }
+
+ private void createInviteLink () {
+ tdlib.send(new TdApi.GetChatsForChatFolderInviteLink(chatFolderId), (result, error) -> runOnUiThreadOptional(() -> {
+ if (error != null) {
+ UI.showError(error);
+ } else if (result.totalCount == 0) {
+ EditChatFolderInviteLinkController controller = new EditChatFolderInviteLinkController(context, tdlib);
+ controller.setArguments(new EditChatFolderInviteLinkController.Arguments(chatFolderId, editedChatFolder));
+ navigateTo(controller);
+ } else {
+ createInviteLink(result.chatIds);
+ }
+ }));
+ }
+
+ private void createInviteLink (long[] shareableChatIds) {
+ tdlib.createChatFolderInviteLink(chatFolderId, /* name */ "", shareableChatIds, (inviteLink, error) -> runOnUiThreadOptional(() -> {
+ if (error != null) {
+ View targetView = getRecyclerView().findViewById(R.id.btn_createInviteLink);
+ if (TD.ERROR_CHATLISTS_TOO_MUCH.equals(error.message) && targetView != null) {
+ showShareableFoldersLimitReached(targetView);
+ } else {
+ UI.showError(error);
+ }
+ } else {
+ EditChatFolderInviteLinkController controller = new EditChatFolderInviteLinkController(context, tdlib);
+ controller.setArguments(new EditChatFolderInviteLinkController.Arguments(chatFolderId, editedChatFolder, shareableChatIds, inviteLink));
+ navigateTo(controller);
+ }
+ }));
+ }
+
+ private void deleteInviteLink (String inviteLink) {
+ tdlib.deleteChatFolderInviteLink(chatFolderId, inviteLink, tdlib.typedOkHandler());
+ }
+
+ private String getName (TdApi.ChatFolderInviteLink inviteLink) {
+ if (StringUtils.isEmptyOrInvisible(inviteLink.name)) {
+ return StringUtils.urlWithoutProtocol(inviteLink.inviteLink);
+ }
+ return inviteLink.name;
+ }
+
+ private int inviteLinkCount () {
+ return inviteLinks != null ? inviteLinks.length : 0;
+ }
+
+ private int chatTypeCount () {
+ return TD.countIncludedChatTypes(editedChatFolder) + TD.countExcludedChatTypes(editedChatFolder);
+ }
+
+ private boolean hasInviteLinks () {
+ return inviteLinkCount() > 0;
+ }
+
+ private boolean canCreateInviteLink () {
+ int chatTypeCount = chatTypeCount();
+ if (chatTypeCount > 0) {
+ return false;
+ }
+ int inviteLinkCount = inviteLinkCount();
+ if (inviteLinkCount >= tdlib.chatFolderInviteLinkCountMax()) {
+ return false;
+ }
+ return isShareable() || tdlib.canAddShareableFolder();
+ }
+
+ private boolean isShareable () {
+ return editedChatFolder.isShareable || hasInviteLinks();
+ }
+
+ private void checkFolderDeleted () {
+ if (chatFolderId == NO_CHAT_FOLDER_ID) return;
+ boolean isFolderDeleted = tdlib.chatFolderInfo(chatFolderId) == null;
+ if (isFolderDeleted) {
+ closeSelf();
+ }
+ }
+
+ private class ItemDecoration extends RecyclerView.ItemDecoration {
+ @Override
+ public void getItemOffsets (@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
+ outRect.setEmpty();
+ RecyclerView.ViewHolder viewHolder = parent.getChildViewHolder(view);
+ if (viewHolder.getItemViewType() == ListItem.TYPE_ZERO_VIEW && isDoneVisible()) {
+ outRect.bottom = Screen.dp(76);
+ }
+ }
+ }
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderInviteLinkController.java b/app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderInviteLinkController.java
new file mode 100644
index 0000000000..a14224fb1a
--- /dev/null
+++ b/app/src/main/java/org/thunderdog/challegram/ui/EditChatFolderInviteLinkController.java
@@ -0,0 +1,555 @@
+/*
+ * This file is a part of Telegram X
+ * Copyright © 2014 (tgx-android@pm.me)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * File created on 19/01/2024
+ */
+package org.thunderdog.challegram.ui;
+
+import android.content.Context;
+import android.text.InputType;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.res.ResourcesCompat;
+import androidx.core.util.ObjectsCompat;
+import androidx.core.view.ViewCompat;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import org.drinkless.tdlib.TdApi;
+import org.thunderdog.challegram.R;
+import org.thunderdog.challegram.U;
+import org.thunderdog.challegram.charts.LayoutHelper;
+import org.thunderdog.challegram.component.base.SettingView;
+import org.thunderdog.challegram.core.Lang;
+import org.thunderdog.challegram.data.DoubleTextWrapper;
+import org.thunderdog.challegram.data.TD;
+import org.thunderdog.challegram.navigation.NavigationController;
+import org.thunderdog.challegram.navigation.ViewController;
+import org.thunderdog.challegram.telegram.Tdlib;
+import org.thunderdog.challegram.theme.ColorId;
+import org.thunderdog.challegram.theme.Theme;
+import org.thunderdog.challegram.tool.Screen;
+import org.thunderdog.challegram.tool.UI;
+import org.thunderdog.challegram.tool.Views;
+import org.thunderdog.challegram.widget.CheckBoxView;
+import org.thunderdog.challegram.widget.MaterialEditText;
+import org.thunderdog.challegram.widget.MaterialEditTextGroup;
+import org.thunderdog.challegram.widget.SmallChatView;
+
+import java.util.ArrayList;
+import java.util.Set;
+
+import me.vkryl.android.widget.FrameLayoutFix;
+import me.vkryl.core.ArrayUtils;
+import me.vkryl.core.StringUtils;
+import me.vkryl.core.collection.LongSet;
+
+public class EditChatFolderInviteLinkController extends EditBaseController implements View.OnClickListener, View.OnLongClickListener, SettingsAdapter.TextChangeListener {
+
+ public static class Arguments {
+ private final int chatFolderId;
+ private final long[] chatIds;
+ private final long[] shareableChatIds;
+ private final String chatFolderName;
+ private final @Nullable TdApi.ChatFolderInviteLink inviteLink;
+
+ public Arguments (int chatFolderId, TdApi.ChatFolder chatFolder) {
+ this(chatFolderId, chatFolder, ArrayUtils.EMPTY_LONGS, /* inviteLink */ null);
+ }
+
+ public Arguments (int chatFolderId, TdApi.ChatFolder chatFolder, TdApi.ChatFolderInviteLink inviteLink) {
+ this(chatFolderId, chatFolder, inviteLink.chatIds, inviteLink);
+ }
+
+ public Arguments (int chatFolderId, TdApi.ChatFolder chatFolder, long[] shareableChatIds, @Nullable TdApi.ChatFolderInviteLink inviteLink) {
+ this.chatFolderId = chatFolderId;
+ this.chatFolderName = chatFolder.title;
+ this.chatIds = U.concat(chatFolder.pinnedChatIds, chatFolder.includedChatIds);
+ this.shareableChatIds = shareableChatIds;
+ this.inviteLink = inviteLink;
+ }
+ }
+
+ private final ChatIdSet selectedChatIds = new ChatIdSet();
+ private final ChatIdSet includedChatIds = new ChatIdSet();
+ private final ChatIdSet shareableChatIds = new ChatIdSet();
+ private final Adapter adapter = new Adapter(this);
+
+ private int chatFolderId;
+ private String chatFolderName;
+ private long[] chatIds;
+ private TdApi.ChatFolderInviteLink inviteLink;
+ private String inviteLinkName;
+
+ private final int headerId = ViewCompat.generateViewId();
+
+ public EditChatFolderInviteLinkController (Context context, Tdlib tdlib) {
+ super(context, tdlib);
+ }
+
+ @Override
+ public int getId () {
+ return R.id.controller_editChatFolderInviteLink;
+ }
+
+ @Override
+ public CharSequence getName () {
+ return Lang.getString(R.string.ShareFolder);
+ }
+
+ @Override
+ protected int getRecyclerBackgroundColorId () {
+ return ColorId.background;
+ }
+
+ @Override
+ public void setArguments (Arguments args) {
+ super.setArguments(args);
+ this.chatFolderId = args.chatFolderId;
+ this.chatFolderName = args.chatFolderName;
+ if (args.shareableChatIds.length > 0) {
+ this.chatIds = U.concat(args.shareableChatIds, ArrayUtils.removeAll(args.chatIds, args.shareableChatIds));
+ } else {
+ this.chatIds = args.chatIds;
+ }
+ updateInviteLink(args.inviteLink);
+ }
+
+ @Override
+ protected void onCreateView (Context context, FrameLayoutFix contentView, RecyclerView recyclerView) {
+ ArrayList items = new ArrayList<>();
+
+ if (isNoChatsToShare()) {
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION_CENTERED, 0, 0, R.string.ChatFolderInviteLinkNoChatsToShare));
+ } else {
+ items.add(new ListItem(ListItem.TYPE_EDITTEXT_CHANNEL_DESCRIPTION, R.id.btn_inviteLinkName, R.drawable.baseline_info_24, R.string.InviteLinkAdminName));
+ //noinspection ConstantValue
+ if (inviteLink != null) {
+ String link = StringUtils.urlWithoutProtocol(inviteLink.inviteLink);
+ items.add(new ListItem(ListItem.TYPE_VALUED_SETTING, R.id.btn_inviteLink, R.drawable.baseline_link_24, link));
+ }
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, R.id.edit_description));
+ }
+
+ // chats
+ if (chatIds != null && chatIds.length > 0) {
+ if (isNoChatsToShare()) {
+ items.add(new ListItem(ListItem.TYPE_HEADER_PADDED, 0, 0, R.string.ChatFolderInviteLinkNoChatsToShareHeader));
+ } else {
+ items.add(new ListItem(ListItem.TYPE_HEADER_WITH_CHECKBOX, headerId));
+ }
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ boolean addSeparator = false;
+ for (long chatId : chatIds) {
+ if (addSeparator) {
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR));
+ } else {
+ addSeparator = true;
+ }
+ items.add(chatItem(chatId));
+ }
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+
+ // info
+ if (isNoChatsToShare()) {
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, R.string.ChatFolderInviteLinkNoChatsToShareInfo));
+ } else {
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, 0, 0, R.string.ChatFolderInviteLinkSelectGroupsAndChannelsInfo).setTextPaddingRight(Screen.dp(56f)));
+ }
+ }
+
+ adapter.setLockFocusOn(this, false);
+ adapter.setTextChangeListener(this);
+ adapter.setItems(items, false);
+ recyclerView.setLayoutManager(new LinearLayoutManager(context));
+ recyclerView.setAdapter(adapter);
+ updateDoneButton();
+
+ if (!isNoChatsToShare()) {
+ updateShareableChats();
+ }
+ }
+
+ @Override
+ public void onClick (View v) {
+ if (v.getId() == R.id.chat) {
+ ListItem item = (ListItem) v.getTag();
+ long chatId = item.getLongId();
+ if (shareableChatIds.has(chatId)) {
+ boolean isChecked = selectedChatIds.add(chatId) || !selectedChatIds.remove(chatId);
+ DoubleTextWrapper chat = (DoubleTextWrapper) item.getData();
+ chat.setIsChecked(isChecked, /* animated */ true);
+ updateItemsWithCounter();
+ updateDoneButton();
+ } else {
+ context.tooltipManager()
+ .builder(v)
+ .controller(this)
+ .locate((targetView, outRect) -> outRect.set(Screen.dp(48f), Screen.dp(5f), Screen.dp(58f), targetView.getHeight() - Screen.dp(5f)))
+ .show(tdlib, R.string.ThisChatCantBeShared)
+ .hideDelayed();
+ }
+ } else if (v.getId() == R.id.btn_inviteLink) {
+ showInviteLinkOptions();
+ } else if (v.getId() == headerId) {
+ if (selectedChatIds.size() < shareableChatIds.size()) {
+ selectedChatIds.addAll(shareableChatIds);
+ } else {
+ selectedChatIds.clear();
+ }
+ onSelectedChatsChanged();
+ }
+ }
+
+ @Override
+ public boolean onLongClick (View v) {
+ if (v.getId() == R.id.btn_inviteLink) {
+ copyInviteLink();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean onDoneClick () {
+ if (isInProgress()) {
+ return true;
+ }
+ if (hasUnsavedChanges()) {
+ saveChanges();
+ } else {
+ showInviteLinkOptions();
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onBackPressed (boolean fromTop) {
+ if (hasUnsavedChanges()) {
+ showUnsavedChangesPromptBeforeLeaving(/* onConfirm */ null);
+ return true;
+ }
+ return super.onBackPressed(fromTop);
+ }
+
+ @Override
+ public boolean canSlideBackFrom (NavigationController navigationController, float x, float y) {
+ return !hasUnsavedChanges();
+ }
+
+ private void updateInviteLink (@Nullable TdApi.ChatFolderInviteLink inviteLink) {
+ this.inviteLink = inviteLink;
+ if (inviteLinkName == null && inviteLink != null) {
+ inviteLinkName = inviteLink.name;
+ }
+ selectedChatIds.clear();
+ includedChatIds.clear();
+ if (inviteLink != null) {
+ selectedChatIds.addAll(inviteLink.chatIds);
+ includedChatIds.addAll(inviteLink.chatIds);
+ }
+ onSelectedChatsChanged();
+ }
+
+ private void updateShareableChats () {
+ tdlib.send(new TdApi.GetChatsForChatFolderInviteLink(chatFolderId), (result, error) -> runOnUiThreadOptional(() -> {
+ if (error != null) {
+ UI.showError(error);
+ } else {
+ updateShareableChats(result);
+ }
+ }));
+ }
+
+ private void updateShareableChats (TdApi.Chats chats) {
+ shareableChatIds.clear();
+ shareableChatIds.addAll(chats.chatIds);
+
+ Arguments arguments = getArgumentsStrict();
+ if (arguments.inviteLink != null) {
+ shareableChatIds.addAll(arguments.inviteLink.chatIds); // TODO(nikita-toropov) ???
+ }
+
+ updateItemsWithCounter();
+ updateChatItems();
+ }
+
+ private void onSelectedChatsChanged () {
+ if (getWrapUnchecked() == null) {
+ return;
+ }
+ updateItemsWithCounter();
+ updateChatItems();
+ updateDoneButton();
+ }
+
+ private void updateItemsWithCounter () {
+ adapter.updateValuedSettingById(R.id.edit_description);
+ adapter.updateValuedSettingById(headerId);
+ }
+
+ private void updateChatItems () {
+ adapter.updateAllValuedSettingsById(R.id.chat);
+ }
+
+ private boolean hasSelectedChats () {
+ return !selectedChatIds.isEmpty();
+ }
+
+ private boolean hasUnsavedChanges () {
+ if (inviteLink == null) {
+ return false;
+ }
+ return !ObjectsCompat.equals(inviteLink.name, inviteLinkName) || !includedChatIds.equals(selectedChatIds);
+ }
+
+ private void saveChanges () {
+ if (!hasUnsavedChanges()) return;
+ TdApi.ChatFolderInviteLink inviteLink = ObjectsCompat.requireNonNull(this.inviteLink);
+ long[] chatIds = selectedChatIds.toArray();
+ setInProgress(true);
+ tdlib.editChatFolderInviteLink(chatFolderId, inviteLink.inviteLink, inviteLinkName, chatIds, (result, error) -> runOnUiThreadOptional(() -> {
+ setInProgress(false);
+ CharSequence message;
+ if (error != null) {
+ message = TD.toErrorString(error);
+ updateDoneButton();
+ } else {
+ message = Lang.getString(R.string.Saved);
+ updateInviteLink(result);
+ }
+ context.tooltipManager()
+ .builder(getDoneButton())
+ .controller(this)
+ .show(tdlib, message)
+ .hideDelayed();
+ }));
+ }
+
+ private void showInviteLinkOptions () {
+ Options.Builder builder = new Options.Builder();
+ builder.item(new OptionItem(R.id.btn_copyLink, Lang.getString(R.string.InviteLinkCopy), OptionColor.NORMAL, R.drawable.baseline_content_copy_24));
+ builder.item(new OptionItem(R.id.btn_shareLink, Lang.getString(R.string.ShareLink), OptionColor.NORMAL, R.drawable.baseline_share_arrow_24));
+ builder.item(new OptionItem(R.id.btn_deleteLink, Lang.getString(R.string.InviteLinkDelete), OptionColor.RED, R.drawable.baseline_delete_24));
+ showOptions(builder.build(), (view, id) -> {
+ if (id == R.id.btn_copyLink) {
+ copyInviteLink();
+ } else if (id == R.id.btn_shareLink) {
+ shareInviteLink();
+ } else if (id == R.id.btn_deleteLink) {
+ showDeleteInviteLinkConfirm();
+ }
+ return true;
+ });
+ }
+
+ private void copyInviteLink () {
+ UI.copyText(inviteLink.inviteLink, R.string.CopiedLink);
+ }
+
+ private void shareInviteLink () {
+ tdlib.ui().shareUrl(this, inviteLink.inviteLink);
+ }
+
+ private void showDeleteInviteLinkConfirm () {
+ showConfirm(Lang.getString(R.string.AreYouSureDeleteInviteLink), Lang.getString(R.string.InviteLinkDelete), R.drawable.baseline_delete_24, OptionColor.RED, this::deleteInviteLink);
+ }
+
+ private void deleteInviteLink () {
+ tdlib.deleteChatFolderInviteLink(chatFolderId, inviteLink.inviteLink, (result, error) -> runOnUiThreadOptional(() -> {
+ if (error != null) {
+ UI.showError(error);
+ } else {
+ navigateBack();
+ }
+ }));
+ }
+
+ private ListItem chatItem (long chatId) {
+ TdApi.Chat chat = tdlib.chatStrict(chatId);
+ boolean isPublic = tdlib.chatPublic(chatId);
+ CharSequence subtitle;
+ if (tdlib.isChannel(chatId)) {
+ subtitle = Lang.getString(isPublic ? R.string.ChannelPublic : R.string.ChannelPrivate);
+ } else if (tdlib.isMultiChat(chat)) {
+ subtitle = Lang.getString(isPublic ? R.string.GroupPublic : R.string.GroupPrivate);
+ } else if (tdlib.isBotChat(chat)) {
+ subtitle = Lang.getString(R.string.Bot);
+ } else if (tdlib.isUserChat(chat)) {
+ subtitle = Lang.getString(R.string.PrivateChat);
+ } else {
+ subtitle = "";
+ }
+ DoubleTextWrapper data = new DoubleTextWrapper(tdlib, chat);
+ data.setAdminSignVisible(false, false);
+ data.setForcedSubtitle(subtitle);
+ return new ListItem(ListItem.TYPE_CHAT_SMALL, R.id.chat).setLongId(chatId).setData(data);
+ }
+
+ private @DrawableRes int doneIconRes = ResourcesCompat.ID_NULL;
+
+ private void updateDoneButton () {
+ if (inviteLink == null) {
+ return;
+ }
+ int iconRes = hasUnsavedChanges() ? R.drawable.baseline_check_24 : R.drawable.baseline_share_arrow_24;
+ if (doneIconRes != iconRes) {
+ doneIconRes = iconRes;
+ setDoneIcon(iconRes);
+ }
+ boolean isVisible = isInProgress() || hasSelectedChats();
+ if (isFocused()) {
+ setDoneVisible(isVisible);
+ } else {
+ setInstantDoneVisible(isVisible);
+ }
+ }
+
+ private class Adapter extends SettingsAdapter {
+ public Adapter (ViewController> context) {
+ super(context);
+ }
+
+ @NonNull
+ @Override
+ public SettingHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) {
+ SettingHolder holder = super.onCreateViewHolder(parent, viewType);
+ if (viewType == ListItem.TYPE_EDITTEXT_CHANNEL_DESCRIPTION) {
+ int verticalPadding = Screen.dp(13f);
+ FrameLayoutFix frameLayout = (FrameLayoutFix) holder.itemView;
+ frameLayout.setPadding(frameLayout.getPaddingLeft(), verticalPadding, frameLayout.getPaddingRight(), verticalPadding);
+ MaterialEditTextGroup editTextGroup = (MaterialEditTextGroup) frameLayout.getChildAt(0);
+ MaterialEditText editText = editTextGroup.getEditText();
+ editText.setLineDisabled(false);
+ editText.setPadding(0, editText.getPaddingTop(), 0, editText.getPaddingBottom());
+
+ int startMargin = Screen.dp(58f);
+ if (Lang.rtl()) {
+ Views.setRightMargin(editTextGroup, startMargin);
+ } else {
+ Views.setLeftMargin(editTextGroup, startMargin);
+ }
+ ImageView iconView = new ImageView(frameLayout.getContext());
+ iconView.setColorFilter(Theme.iconColor());
+ addThemeFilterListener(iconView, ColorId.icon);
+ iconView.setId(android.R.id.icon);
+ iconView.setPadding(Screen.dp(2f), 0, Screen.dp(2f), 0);
+ frameLayout.addView(iconView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Lang.gravity(Gravity.CENTER_VERTICAL)));
+ }
+ return holder;
+ }
+
+ @Override
+ protected void modifyEditText (ListItem item, ViewGroup parent, MaterialEditTextGroup editText) {
+ if (item.getId() == R.id.btn_inviteLinkName) {
+ editText.setEmptyHint(R.string.ChatFolderInviteLinkNameHint);
+ editText.setText(inviteLinkName);
+ editText.getEditText().setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
+
+ ImageView iconView = parent.findViewById(android.R.id.icon);
+ iconView.setImageResource(item.getIconResource());
+ }
+ }
+
+ @Override
+ protected void modifyDescription (ListItem item, TextView textView) {
+ if (item.getId() == R.id.edit_description) {
+ int selectedChatCount = selectedChatIds.size();
+ if (selectedChatCount > 0) {
+ textView.setText(Lang.pluralBold(R.string.ChatFolderInviteLinkInfo, selectedChatCount, chatFolderName));
+ } else {
+ textView.setText(Lang.getStringBold(R.string.ChatFolderInviteLinkInfoNoCounter, chatFolderName));
+ }
+ }
+ }
+
+ @Override
+ protected void setValuedSetting (ListItem item, SettingView view, boolean isUpdate) {
+ if (item.getId() == R.id.btn_inviteLink) {
+ view.setCenterIcon(true);
+ view.setData(Lang.getString(R.string.FolderLink));
+ } else {
+ view.setCenterIcon(false);
+ }
+ }
+
+ @Override
+ protected void setHeaderText (ListItem item, TextView view, boolean isUpdate) {
+ if (item.getId() == headerId) {
+ if (chatIds != null && chatIds.length > 0) {
+ Views.setMediumText(view, Lang.getString(R.string.xOfYChatsSelected, selectedChatIds.size(), chatIds.length));
+ } else {
+ view.setText(null);
+ }
+ } else {
+ super.setHeaderText(item, view, isUpdate);
+ }
+ }
+
+ @Override
+ protected void setHeaderCheckBoxState (ListItem item, CheckBoxView checkBox, boolean isUpdate) {
+ if (item.getId() == headerId) {
+ checkBox.setChecked(!selectedChatIds.isEmpty(), isUpdate);
+ checkBox.setDisabled(shareableChatIds.isEmpty(), isUpdate);
+ checkBox.setPartially(selectedChatIds.size() < shareableChatIds.size(), isUpdate);
+ } else {
+ super.setHeaderCheckBoxState(item, checkBox, isUpdate);
+ }
+ }
+
+ @Override
+ protected void modifyChatView (ListItem item, SmallChatView chatView, @Nullable CheckBoxView checkBox, boolean isUpdate) {
+ if (item.getId() == R.id.chat) {
+ long chatId = item.getLongId();
+ DoubleTextWrapper chat = (DoubleTextWrapper) item.getData();
+ chat.setDrawCrossIcon(!shareableChatIds.has(chatId));
+ chat.setIsChecked(selectedChatIds.has(chatId), isUpdate);
+ }
+ }
+ }
+
+ @Override
+ public void onTextChanged (int id, ListItem item, MaterialEditTextGroup v, String text) {
+ if (id == R.id.btn_inviteLinkName) {
+ inviteLinkName = v.getText().toString();
+ updateDoneButton();
+ }
+ }
+
+ private boolean isNoChatsToShare () {
+ return inviteLink == null;
+ }
+
+ private static class ChatIdSet extends LongSet {
+ @Override
+ public boolean equals (@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ChatIdSet) {
+ return set.equals(((ChatIdSet) obj).set);
+ }
+ if (obj instanceof Set) {
+ return set.equals(obj);
+ }
+ return false;
+ }
+ }
+}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/FeatureToggles.java b/app/src/main/java/org/thunderdog/challegram/ui/FeatureToggles.java
index db136635fc..2c573ad30d 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/FeatureToggles.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/FeatureToggles.java
@@ -1,7 +1,5 @@
package org.thunderdog.challegram.ui;
-import static androidx.core.text.HtmlCompat.fromHtml;
-
import android.content.Context;
import android.view.View;
@@ -67,8 +65,8 @@ protected void setValuedSetting (ListItem item, SettingView view, boolean isUpda
header("Threads > First Open"),
shadowTop(),
toggle("Scroll to header message", () -> SCROLL_TO_HEADER_MESSAGE_ON_THREAD_FIRST_OPEN, (value) -> SCROLL_TO_HEADER_MESSAGE_ON_THREAD_FIRST_OPEN = value),
- descriptionSmall(fromHtml("On - Trying to show header message fully unless it takes more than half of the RecyclerView's height in which messages display, in which case it should scroll to the maximum position that fits that half", HtmlCompat.FROM_HTML_MODE_COMPACT)),
- descriptionSmall(fromHtml("Off - Showing with displayed header message preview showing and \"Discussion started\" aligned right below it", HtmlCompat.FROM_HTML_MODE_COMPACT)),
+ descriptionSmall(HtmlCompat.fromHtml("On - Trying to show header message fully unless it takes more than half of the RecyclerView's height in which messages display, in which case it should scroll to the maximum position that fits that half", HtmlCompat.FROM_HTML_MODE_COMPACT)),
+ descriptionSmall(HtmlCompat.fromHtml("Off - Showing with displayed header message preview showing and \"Discussion started\" aligned right below it", HtmlCompat.FROM_HTML_MODE_COMPACT)),
shadowBottom(),
header("Threads > Preview"),
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ListItem.java b/app/src/main/java/org/thunderdog/challegram/ui/ListItem.java
index afa2dfdf44..cb0baea3da 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ListItem.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ListItem.java
@@ -162,6 +162,9 @@ public class ListItem {
public static final int TYPE_GIFT_HEADER = 142;
+ public static final int TYPE_HEADER_WITH_TEXT_BUTTON = 143;
+ public static final int TYPE_HEADER_WITH_CHECKBOX = 144;
+
private static final int FLAG_SELECTED = 1;
private static final int FLAG_BOOL_VALUE = 1 << 1;
private static final int FLAG_USE_SELECTION_INDEX = 1 << 2;
@@ -184,7 +187,7 @@ public class ListItem {
private String stringKey, stringValue;
private @PorterDuffColorId int textColorId;
private TdlibAccentColor accentColor;
- private int textPaddingLeft;
+ private int textPaddingLeft, textPaddingRight;
private int intValue;
private long longValue;
@@ -333,6 +336,11 @@ public ListItem setTextPaddingLeft (int paddingLeft) {
return this;
}
+ public ListItem setTextPaddingRight (int textPaddingRight) {
+ this.textPaddingRight = textPaddingRight;
+ return this;
+ }
+
public ListItem setData (Object data) {
this.data = data;
return this;
@@ -365,6 +373,10 @@ public int getTextPaddingLeft () {
return textPaddingLeft;
}
+ public int getTextPaddingRight () {
+ return textPaddingRight;
+ }
+
public ListItem setStringValue (String value) {
this.stringValue = value;
return this;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/MainController.java b/app/src/main/java/org/thunderdog/challegram/ui/MainController.java
index 2adb7d3b60..1f5d3f2d96 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/MainController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/MainController.java
@@ -21,12 +21,16 @@
import android.content.pm.PackageManager;
import android.graphics.BitmapFactory;
import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Build;
import android.os.Parcelable;
import android.os.SystemClock;
import android.provider.ContactsContract;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -36,12 +40,16 @@
import android.widget.TextView;
import android.widget.Toast;
+import androidx.annotation.AnyThread;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.annotation.StringRes;
+import androidx.collection.SparseArrayCompat;
+import androidx.core.util.ObjectsCompat;
+import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import org.drinkless.tdlib.TdApi;
@@ -67,6 +75,7 @@
import org.thunderdog.challegram.navigation.RecyclerViewProvider;
import org.thunderdog.challegram.navigation.SettingsWrap;
import org.thunderdog.challegram.navigation.SettingsWrapBuilder;
+import org.thunderdog.challegram.navigation.TooltipOverlayView;
import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.navigation.ViewPagerController;
import org.thunderdog.challegram.navigation.ViewPagerHeaderViewCompact;
@@ -84,10 +93,13 @@
import org.thunderdog.challegram.telegram.TdlibManager;
import org.thunderdog.challegram.telegram.TdlibOptionListener;
import org.thunderdog.challegram.telegram.TdlibSettingsManager;
+import org.thunderdog.challegram.telegram.TdlibUi;
import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.theme.PropertyId;
import org.thunderdog.challegram.theme.Theme;
+import org.thunderdog.challegram.tool.Drawables;
import org.thunderdog.challegram.tool.Fonts;
+import org.thunderdog.challegram.tool.Paints;
import org.thunderdog.challegram.tool.Screen;
import org.thunderdog.challegram.tool.Strings;
import org.thunderdog.challegram.tool.TGMimeType;
@@ -99,6 +111,7 @@
import org.thunderdog.challegram.util.AppUpdater;
import org.thunderdog.challegram.util.StringList;
import org.thunderdog.challegram.util.text.Counter;
+import org.thunderdog.challegram.util.text.IconSpan;
import org.thunderdog.challegram.util.text.TextColorSet;
import org.thunderdog.challegram.widget.BubbleLayout;
import org.thunderdog.challegram.widget.NoScrollTextView;
@@ -422,7 +435,7 @@ private int applySection (int section, long pagerItemId, ChatsController chatsCo
if (filter == FILTER_NONE && !menuNeedArchive) {
item = getDefaultMainItem();
} else {
- String sectionName = getMenuSectionName(pagerItemId, pagerItemPosition, /* hasFolders */ false, ChatFolderStyle.LABEL_ONLY).toUpperCase();
+ CharSequence sectionName = getMenuSectionName(pagerItemId, pagerItemPosition, /* hasFolders */ false, ChatFolderStyle.LABEL_ONLY, /* upperCase */ true);
item = new ViewPagerTopView.Item(sectionName);
}
getDefaultSectionItems().set(0, item);
@@ -854,7 +867,9 @@ public void onPageSelected (int position, int actualPosition) {
composeWrap.show();
}
if (hasFolders()) {
- composeWrap.replaceMainButton(R.id.btn_float_compose, R.drawable.baseline_create_24);
+ if (composeWrap.getMainButtonId() != R.id.btn_float_compose) {
+ composeWrap.replaceMainButton(R.id.btn_float_compose, R.drawable.baseline_create_24);
+ }
return;
}
switch (position) {
@@ -1275,19 +1290,7 @@ private long getPagerItemId (TdApi.ChatList chatList) {
public boolean onPagerItemLongClick (int index) {
if (hasFolders()) {
TdApi.ChatList chatList = pagerChatLists.get(index);
- String title;
- if (TD.isChatListMain(chatList)) {
- title = Lang.getString(R.string.CategoryMain);
- } else if (TD.isChatListArchive(chatList)) {
- title = Lang.getString(R.string.CategoryArchive);
- } else if (TD.isChatListFolder(chatList)) {
- int chatFolderId = ((TdApi.ChatListFolder) chatList).chatFolderId;
- TdApi.ChatFolderInfo chatFolderInfo = tdlib.chatFolderInfo(chatFolderId);
- title = chatFolderInfo != null ? chatFolderInfo.title : null;
- } else {
- title = null;
- }
- showChatListOptions(title, chatList);
+ showChatListOptions(chatList);
return true;
}
return false;
@@ -1327,28 +1330,46 @@ private ChatsController newChatsController (TdApi.ChatList chatList, @Filter int
return chats;
}
- private String getMenuSectionName (long pagerItemId, int pagerItemPosition, boolean hasFolders, @ChatFolderStyle int chatFolderStyle) {
- return Lang.getString(getMenuSectionNameRes(pagerItemId, pagerItemPosition, hasFolders, chatFolderStyle));
- }
-
- private int getMenuSectionNameRes (long pagerItemId, int pagerItemPosition, boolean hasFolders, @ChatFolderStyle int chatFolderStyle) {
+ private CharSequence getMenuSectionName (long pagerItemId, int pagerItemPosition, boolean hasFolders, @ChatFolderStyle int chatFolderStyle, boolean upperCase) {
int selectedFilter = getSelectedFilter(pagerItemId);
boolean isMain = pagerItemId == MAIN_PAGER_ITEM_ID;
boolean isArchive = pagerItemId == ARCHIVE_PAGER_ITEM_ID;
if (!isMain && !isArchive) {
throw new UnsupportedOperationException();
}
+ if (hasFolders && Config.CHAT_FOLDERS_REDESIGN && selectedFilter != FILTER_NONE) {
+ String source;
+ if (chatFolderStyle == ChatFolderStyle.ICON_ONLY) {
+ source = "∇";
+ } else {
+ int sourceRes = isMain ? getMainSectionNameRes(FILTER_NONE, /* hasFolders */ true) : getArchiveSectionNameRes(FILTER_NONE, chatFolderStyle);
+ source = (upperCase ? Lang.getString(sourceRes).toUpperCase() : Lang.getString(sourceRes)) + " ∇";
+ }
+ SpannableString string = new SpannableString(source);
+ // TODO(nikita-toropov) icon color
+ string.setSpan(new IconSpan(R.drawable.baseline_filter_variant, ColorId.iconActive), string.length() - 1, string.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ return string;
+ }
+ String sectionName;
//noinspection ConstantConditions
if (isArchive || (isMain && menuNeedArchive && (!hasFolders || pagerItemPosition > 0))) {
- return getArchiveSectionName(selectedFilter, chatFolderStyle);
+ sectionName = Lang.getString(getArchiveSectionNameRes(selectedFilter, chatFolderStyle));
+ } else {
+ sectionName = Lang.getString(getMainSectionNameRes(selectedFilter, hasFolders));
}
+ return upperCase ? sectionName.toUpperCase() : sectionName;
+ }
+
+ @StringRes
+ private int getMainSectionNameRes (int selectedFilter, boolean hasFolders) {
if (selectedFilter != FILTER_NONE) {
return getFilterName(selectedFilter);
}
return hasFolders ? R.string.CategoryMain : R.string.Chats;
}
- private int getArchiveSectionName (int selectedFilter, @ChatFolderStyle int chatFolderStyle) {
+ @StringRes
+ private int getArchiveSectionNameRes (int selectedFilter, @ChatFolderStyle int chatFolderStyle) {
if (chatFolderStyle == ChatFolderStyle.LABEL_ONLY) {
switch (selectedFilter) {
case FILTER_UNREAD:
@@ -1371,19 +1392,31 @@ private int getArchiveSectionName (int selectedFilter, @ChatFolderStyle int chat
return selectedFilter == FILTER_NONE ? R.string.CategoryArchive : getFilterName(selectedFilter);
}
- private String getMenuSectionName (long pagerItemId, String folderName, int chatFolderStyle) {
+ private CharSequence getFolderSectionName (long pagerItemId, String folderName, @ChatFolderStyle int chatFolderStyle) {
int selectedFilter = getSelectedFilter(pagerItemId);
+ CharSequence sectionName;
if (selectedFilter != FILTER_NONE) {
- String filterName = Lang.getString(getFilterName(selectedFilter));
- if (chatFolderStyle == ChatFolderStyle.LABEL_ONLY) {
- return Lang.getString(R.string.format_folderAndFilter, folderName, filterName);
+ if (Config.CHAT_FOLDERS_REDESIGN) {
+ String source = chatFolderStyle == ChatFolderStyle.ICON_ONLY ? "∇" : folderName + " ∇";
+ SpannableString string = new SpannableString(source);
+ // TODO(nikita-toropov) icon color
+ string.setSpan(new IconSpan(R.drawable.baseline_filter_variant, ColorId.iconActive), string.length() - 1, string.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ sectionName = string;
+ } else {
+ String filterName = Lang.getString(getFilterName(selectedFilter));
+ if (chatFolderStyle == ChatFolderStyle.LABEL_ONLY) {
+ sectionName = Lang.getString(R.string.format_folderAndFilter, folderName, filterName);
+ } else {
+ sectionName = filterName;
+ }
}
- return filterName;
+ } else {
+ sectionName = folderName;
}
- return folderName;
+ return Emoji.instance().replaceEmoji(sectionName);
}
- private @StringRes int getFilterName (int filter) {
+ private @StringRes int getFilterName (@Filter int filter) {
switch (filter) {
case FILTER_UNREAD:
return R.string.CategoryUnread;
@@ -1461,12 +1494,12 @@ private boolean hasFolders () {
}
@Override
- protected String[] getPagerSections () {
+ protected CharSequence[] getPagerSections () {
if (hasFolders()) {
throw new UnsupportedOperationException();
}
- return new String[] {
- getMenuSectionName(MAIN_PAGER_ITEM_ID, /* pagerItemPosition */ 0, /* hasFolders */ false, ChatFolderStyle.LABEL_ONLY).toUpperCase(),
+ return new CharSequence[] {
+ getMenuSectionName(MAIN_PAGER_ITEM_ID, /* pagerItemPosition */ 0, /* hasFolders */ false, ChatFolderStyle.LABEL_ONLY, /* upperCase */ true),
Lang.getString(R.string.Calls).toUpperCase()/*, UI.getString(R.string.Contacts).toUpperCase()*/
};
}
@@ -1477,7 +1510,7 @@ protected String[] getPagerSections () {
private ViewPagerTopView.Item getDefaultMainItem () {
if (defaultMainItem == null) {
- String mainItem = getMenuSectionName(MAIN_PAGER_ITEM_ID, 0, false, ChatFolderStyle.LABEL_ONLY).toUpperCase();
+ CharSequence mainItem = getMenuSectionName(MAIN_PAGER_ITEM_ID, 0, false, ChatFolderStyle.LABEL_ONLY, /* upperCase */ true);
UnreadCounterColorSet unreadCounterColorSet = new UnreadCounterColorSet();
Counter unreadCounter = new Counter.Builder()
.textSize(12f)
@@ -2037,22 +2070,43 @@ private void checkSyncAlert () {
// Chat folders
- private void showChatListOptions (@Nullable CharSequence title, TdApi.ChatList chatList) {
+ private void showChatListOptions (TdApi.ChatList chatList) {
boolean isMain = TD.isChatListMain(chatList);
boolean isFolder = TD.isChatListFolder(chatList);
boolean isArchive = TD.isChatListArchive(chatList);
int chatFolderId = isFolder ? ((TdApi.ChatListFolder) chatList).chatFolderId : 0;
+ TdApi.ChatFolderInfo chatFolderInfo;
+ String title;
+ if (isMain) {
+ title = Lang.getString(R.string.CategoryMain);
+ chatFolderInfo = null;
+ } else if (isArchive) {
+ title = Lang.getString(R.string.CategoryArchive);
+ chatFolderInfo = null;
+ } else if (isFolder) {
+ chatFolderInfo = tdlib.chatFolderInfo(chatFolderId);
+ title = chatFolderInfo != null ? chatFolderInfo.title : null;
+ } else {
+ title = null;
+ chatFolderInfo = null;
+ }
Options.Builder options = new Options.Builder();
if (!StringUtils.isEmptyOrBlank(title)) {
options.info(title);
}
+ if (tdlib.hasUnreadChats(chatList)) {
+ options.item(new OptionItem(R.id.btn_markFolderAsRead, Lang.getString(R.string.MarkFolderAsRead), OptionColor.NORMAL, R.drawable.baseline_done_all_24));
+ }
if (isFolder) {
options.item(new OptionItem(R.id.btn_editFolder, Lang.getString(R.string.EditFolder), OptionColor.NORMAL, R.drawable.baseline_edit_24));
int chatFolderStyle = tdlib.settings().chatFolderStyle();
- if (chatFolderStyle == ChatFolderStyle.ICON_ONLY || chatFolderStyle == ChatFolderStyle.LABEL_AND_ICON) {
+ if (chatFolderStyle == ChatFolderStyle.ICON_ONLY || chatFolderStyle == ChatFolderStyle.LABEL_AND_ICON || chatFolderStyle == ChatFolderStyle.ICON_WITH_LABEL_ON_ACTIVE) {
options.item(new OptionItem(R.id.btn_changeFolderIcon, Lang.getString(R.string.ChatFolderChangeIcon), OptionColor.NORMAL, R.drawable.baseline_image_24));
}
options.item(new OptionItem(R.id.btn_folderIncludeChats, Lang.getString(R.string.ChatFolderAddChats), OptionColor.NORMAL, R.drawable.baseline_add_24));
+ if (chatFolderInfo != null) {
+ options.item(new OptionItem(R.id.btn_shareFolder, Lang.getString(R.string.ShareFolder), OptionColor.NORMAL, R.drawable.baseline_share_arrow_24));
+ }
}
if (isMain) {
if (!Config.RESTRICT_HIDING_MAIN_LIST) {
@@ -2095,20 +2149,21 @@ private void showChatListOptions (@Nullable CharSequence title, TdApi.ChatList c
if (error != null) {
UI.showError(error);
} else {
+ boolean showChatTypes = !chatFolder.isShareable;
SelectChatsController controller = new SelectChatsController(context, tdlib);
- controller.setArguments(SelectChatsController.Arguments.includedChats(chatFolderId, chatFolder));
+ controller.setArguments(SelectChatsController.Arguments.includedChats(chatFolderId, chatFolder, showChatTypes));
navigateTo(controller);
}
}));
} else if (id == R.id.btn_changeFolderIcon) {
- ChatFolderIconSelector.show(this, icon -> {
+ ChatFolderIconSelector.show(this, TD.getIconName(chatFolderInfo), selectedIcon -> {
tdlib.send(new TdApi.GetChatFolder(chatFolderId), (chatFolder, getError) -> {
if (getError != null) {
UI.showError(getError);
} else {
- if (!Td.equalsTo(chatFolder.icon, icon)) {
- chatFolder.icon = icon;
- tdlib.send(new TdApi.EditChatFolder(chatFolderId, chatFolder), (chatFolderInfo, editError) -> {
+ if (!Td.equalsTo(chatFolder.icon, selectedIcon)) {
+ chatFolder.icon = selectedIcon;
+ tdlib.send(new TdApi.EditChatFolder(chatFolderId, chatFolder), (info, editError) -> {
if (editError != null) {
UI.showError(editError);
}
@@ -2117,17 +2172,218 @@ private void showChatListOptions (@Nullable CharSequence title, TdApi.ChatList c
}
});
});
+ } else if (id == R.id.btn_shareFolder) {
+ TdApi.ChatFolderInfo info = ObjectsCompat.requireNonNull(chatFolderInfo);
+ if (info.hasMyInviteLinks) {
+ tdlib.send(new TdApi.GetChatFolderInviteLinks(chatFolderId), (result, error) -> {
+ if (error != null) {
+ UI.showError(error);
+ } else {
+ runOnUiThreadOptional(() -> showChatFolderInviteLinks(chatFolderId, title, result.inviteLinks));
+ }
+ });
+ } else {
+ return onCreateChatFolderInviteLinkClick(v, chatFolderId, info.isShareable, /* chatFolderInviteLinkCount */ 0L);
+ }
} else if (id == R.id.btn_removeFolder) {
- showConfirm(Lang.getString(R.string.RemoveFolderConfirm), Lang.getString(R.string.Remove), R.drawable.baseline_delete_24, OptionColor.RED, () -> {
- tdlib.send(new TdApi.DeleteChatFolder(chatFolderId, null), tdlib.typedOkHandler());
- });
+ TdApi.ChatFolderInfo info = ObjectsCompat.requireNonNull(chatFolderInfo);
+ if (info.isShareable) {
+ tdlib.send(new TdApi.GetChatFolderChatsToLeave(chatFolderId), (result, error) -> runOnUiThreadOptional(() -> {
+ if (error != null) {
+ UI.showError(error);
+ } else if (result.totalCount > 0) {
+ ChatFolderInviteLinkController controller = new ChatFolderInviteLinkController(context, tdlib);
+ controller.setArguments(ChatFolderInviteLinkController.Arguments.deleteFolder(info, result.chatIds));
+ controller.show();
+ } else {
+ showDeleteChatFolderConfirm(chatFolderId, info.hasMyInviteLinks);
+ }
+ }));
+ } else {
+ showDeleteChatFolderConfirm(chatFolderId, info.hasMyInviteLinks);
+ }
} else if (id == R.id.btn_chatFolders) {
navigateTo(new SettingsFoldersController(context, tdlib));
+ } else if (id == R.id.btn_markFolderAsRead) {
+ tdlib.readAllChats(chatList, /* after */ null);
+ }
+ return true;
+ });
+ }
+
+ private void showDeleteChatFolderConfirm (int chatFolderId, boolean hasMyInviteLinks) {
+ tdlib.ui().showDeleteChatFolderConfirm(this, hasMyInviteLinks, () -> {
+ tdlib.send(new TdApi.DeleteChatFolder(chatFolderId, null), tdlib.typedOkHandler());
+ });
+ }
+
+ private void showChatFolderInviteLinks (int chatFolderId, @Nullable CharSequence title, TdApi.ChatFolderInviteLink[] inviteLinks) {
+ Options.Builder options = new Options.Builder();
+ if (!StringUtils.isEmptyOrBlank(title)) {
+ options.info(title);
+ }
+ long chatFolderInviteLinkCount = inviteLinks.length;
+ long chatFolderInviteLinkCountMax = tdlib.chatFolderInviteLinkCountMax();
+ boolean canCreateInviteLink = chatFolderInviteLinkCount < chatFolderInviteLinkCountMax;
+ int createNewLinkOptionColor = canCreateInviteLink ? OptionColor.BLUE : OptionColor.INACTIVE;
+ SparseArrayCompat inviteLinkById = new SparseArrayCompat<>(inviteLinks.length);
+ for (TdApi.ChatFolderInviteLink inviteLink : inviteLinks) {
+ int id = ViewCompat.generateViewId();
+ inviteLinkById.append(id, inviteLink);
+ String name = StringUtils.isEmptyOrInvisible(inviteLink.name) ? StringUtils.urlWithoutProtocol(inviteLink.inviteLink) : inviteLink.name;
+ options.item(new OptionItem(id, name, OptionColor.NORMAL, R.drawable.baseline_link_24));
+ }
+ options.item(new OptionItem(R.id.btn_createInviteLink, Lang.getString(R.string.CreateANewLink), createNewLinkOptionColor, R.drawable.baseline_add_link_24));
+ PopupLayout popupLayout = showOptions(options.build(), (view, id) -> {
+ if (id == R.id.btn_createInviteLink) {
+ return onCreateChatFolderInviteLinkClick(view, chatFolderId, true, chatFolderInviteLinkCount);
+ }
+ TdApi.ChatFolderInviteLink inviteLink = ObjectsCompat.requireNonNull(inviteLinkById.get(id));
+ showChatFolderInviteLinkOptions(chatFolderId, inviteLink);
+ return true;
+ });
+ if (!canCreateInviteLink) {
+ View button = popupLayout.getBoundView().findViewById(R.id.btn_createInviteLink);
+ if (button instanceof TextView) {
+ TextView textView = (TextView) button;
+ Views.setMediumText(textView, textView.getText());
+ Drawable[] drawables = textView.getCompoundDrawables();
+ Drawable lock = Drawables.get(R.drawable.baseline_lock_16);
+ lock.setColorFilter(Paints.getColorFilter(Theme.textAccentColor()));
+ addThemeFilterListener(lock, ColorId.text);
+ button.setTag(R.id.tag_tooltip_location_provider, (TooltipOverlayView.LocationProvider) (targetView, outRect) -> {
+ outRect.set(lock.getBounds());
+ int top = (targetView.getHeight() - outRect.height()) / 2;
+ int left = Lang.rtl() ? targetView.getPaddingLeft() : targetView.getWidth() - targetView.getPaddingRight() - outRect.width();
+ outRect.offsetTo(left, top);
+ outRect.inset(0, -Screen.dp(10f));
+ });
+ if (Lang.rtl()) {
+ textView.setCompoundDrawablesWithIntrinsicBounds(lock, null, drawables[2], null);
+ } else {
+ textView.setCompoundDrawablesWithIntrinsicBounds(drawables[0], null, lock, null);
+ }
+ }
+ }
+ }
+
+ private boolean onCreateChatFolderInviteLinkClick (View view, int chatFolderId, boolean isChatFolderShareable, long chatFolderInviteLinkCount) {
+ if (chatFolderInviteLinkCount == 0 && !isChatFolderShareable && !tdlib.canAddShareableFolder()) {
+ showShareableFoldersLimitReached(view);
+ return false;
+ }
+ if (chatFolderInviteLinkCount >= tdlib.chatFolderInviteLinkCountMax()) {
+ showChatFolderInviteLinksLimitReached(view);
+ return false;
+ }
+ createChatFolderInviteLink(chatFolderId);
+ return true;
+ }
+
+ private void showShareableFoldersLimitReached (View view) {
+ UI.forceVibrateError(view);
+ PopupLayout popupLayout = PopupLayout.parentOf(view);
+ if (tdlib.hasPremium()) {
+ CharSequence message = Lang.getMarkdownString(this, R.string.ShareableFoldersLimitReached, tdlib.addedShareableChatFolderCountMax());
+ popupLayout.tooltipManager().builder(view).show(tdlib, message).hideDelayed();
+ } else {
+ tdlib.ui().showPremiumLimitInfo(this, popupLayout.tooltipManager(), view, TdlibUi.PremiumLimit.SHAREABLE_FOLDER_COUNT);
+ }
+ }
+
+ private void showChatFolderInviteLinksLimitReached (View view) {
+ UI.forceVibrateError(view);
+ PopupLayout popupLayout = PopupLayout.parentOf(view);
+ if (tdlib.hasPremium()) {
+ CharSequence message = Lang.getMarkdownString(this, R.string.ChatFolderInviteLinksLimitReached, tdlib.chatFolderInviteLinkCountMax());
+ popupLayout.tooltipManager().builder(view).show(tdlib, message).hideDelayed();
+ } else {
+ tdlib.ui().showPremiumLimitInfo(this, popupLayout.tooltipManager(), view, TdlibUi.PremiumLimit.CHAT_FOLDER_INVITE_LINK_COUNT);
+ }
+ }
+
+ private void showChatFolderInviteLinkOptions (int chatFolderId, TdApi.ChatFolderInviteLink inviteLink) {
+ String title = StringUtils.isEmptyOrInvisible(inviteLink.name) ? StringUtils.urlWithoutProtocol(inviteLink.inviteLink) : inviteLink.name;
+ Options.Builder options = new Options.Builder();
+ options.info(title);
+ options.item(new OptionItem(R.id.btn_shareLink, Lang.getString(R.string.ShareLink), OptionColor.NORMAL, R.drawable.baseline_share_arrow_24));
+ options.item(new OptionItem(R.id.btn_copyLink, Lang.getString(R.string.InviteLinkCopy), OptionColor.NORMAL, R.drawable.baseline_content_copy_24));
+ options.item(new OptionItem(R.id.btn_edit, Lang.getString(R.string.InviteLinkEdit), OptionColor.NORMAL, R.drawable.baseline_edit_24));
+ showOptions(options.build(), (view, id) -> {
+ if (id == R.id.btn_shareLink) {
+ tdlib.ui().shareUrl(this, inviteLink.inviteLink);
+ } else if (id == R.id.btn_copyLink) {
+ UI.copyText(inviteLink.inviteLink, R.string.CopiedLink);
+ } else if (id == R.id.btn_edit) {
+ tdlib.send(new TdApi.GetChatFolder(chatFolderId), (chatFolder, error) -> runOnUiThreadOptional(() -> {
+ if (error != null) {
+ UI.showError(error);
+ } else {
+ EditChatFolderInviteLinkController controller = new EditChatFolderInviteLinkController(context, tdlib);
+ controller.setArguments(new EditChatFolderInviteLinkController.Arguments(chatFolderId, chatFolder, inviteLink));
+ navigateTo(controller);
+ }
+ }));
+ } else {
+ throw new UnsupportedOperationException();
}
return true;
});
}
+ @AnyThread
+ private void createChatFolderInviteLink (int chatFolderId) {
+ tdlib.send(new TdApi.GetChatFolder(chatFolderId), (chatFolder, error) -> {
+ if (error != null) {
+ UI.showError(error);
+ } else {
+ createChatFolderInviteLink(chatFolderId, chatFolder);
+ }
+ });
+ }
+
+ @AnyThread
+ private void createChatFolderInviteLink (int chatFolderId, TdApi.ChatFolder chatFolder) {
+ if (TD.countIncludedChatTypes(chatFolder) > 0 || TD.countExcludedChatTypes(chatFolder) > 0) {
+ runOnUiThreadOptional(() -> {
+ CharSequence message = Lang.getMarkdownString(this, R.string.ChatFolderInviteLinkChatTypesNotSupported);
+ if (showFolderTooltip(chatFolderId, message)) {
+ View topView = headerCell != null ? headerCell.getTopView() : null;
+ if (topView != null) {
+ UI.forceVibrateError(topView);
+ }
+ } else {
+ UI.showCustomToast(message, Toast.LENGTH_LONG, 0);
+ }
+ });
+ return;
+ }
+ tdlib.send(new TdApi.GetChatsForChatFolderInviteLink(chatFolderId), (chats, error) -> {
+ if (error != null) {
+ UI.showError(error);
+ } else {
+ createChatFolderInviteLink(chatFolderId, chatFolder, chats.chatIds);
+ }
+ });
+ }
+
+ @AnyThread
+ private void createChatFolderInviteLink (int chatFolderId, TdApi.ChatFolder chatFolder, long[] shareableChatIds) {
+ if (shareableChatIds.length == 0) {
+ UI.showCustomToast(R.string.ChatFolderInviteLinkNoChatsToShare, Toast.LENGTH_SHORT, 0);
+ return;
+ }
+ tdlib.createChatFolderInviteLink(chatFolderId, /* name */ "", shareableChatIds, (inviteLink, error) -> runOnUiThreadOptional(() -> {
+ if (error != null) {
+ UI.showError(error);
+ } else {
+ EditChatFolderInviteLinkController controller = new EditChatFolderInviteLinkController(context, tdlib);
+ controller.setArguments(new EditChatFolderInviteLinkController.Arguments(chatFolderId, chatFolder, shareableChatIds, inviteLink));
+ navigateTo(controller);
+ }
+ }));
+ }
+
private @Nullable ViewGroup bottomBar;
private @Px float bottomBarOffsetByPlayer;
private @Px float bottomBarOffsetByScroll;
@@ -2167,12 +2423,57 @@ protected boolean applyPlayerOffset (float factor, float top) {
return result;
}
+ private boolean showFolderTooltip (int chatFolderId, CharSequence message) {
+ return showFolderTooltip(new TdApi.ChatListFolder(chatFolderId), message);
+ }
+
+ private boolean showFolderTooltip (TdApi.ChatList chatList, CharSequence message) {
+ if (headerCell == null) {
+ return false;
+ }
+ int index = indexOfChatList(chatList);
+ if (index == -1) {
+ return false;
+ }
+ ViewPagerTopView topView = headerCell.getTopView();
+ Rect itemRect = new Rect();
+ if (topView == null || !topView.getItemRect(index, itemRect)) {
+ return false;
+ }
+ int topViewX = Math.round(topView.getX());
+ int topViewParentWidth = ((View) topView.getParent()).getWidth();
+ int horizontalInset = Screen.dp(16f);
+ itemRect.left = Math.max(itemRect.left, horizontalInset - topViewX);
+ itemRect.right = Math.min(itemRect.right, topViewParentWidth - topViewX - horizontalInset);
+ if (itemRect.isEmpty()) {
+ return false;
+ }
+ context()
+ .tooltipManager()
+ .builder(topView)
+ .locate((targetView, outRect) -> outRect.set(itemRect))
+ .controller(this)
+ .show(tdlib, message)
+ .hideDelayed(3500, TimeUnit.MILLISECONDS);
+ return true;
+ }
+
+ private int indexOfChatList (TdApi.ChatList chatList) {
+ for (int i = 0; i < pagerChatLists.size(); i++) {
+ if (Td.equalsTo(chatList, pagerChatLists.get(i))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
private void checkTabs () {
if (headerCell == null)
return;
ViewPagerHeaderViewCompact headerCellView = (ViewPagerHeaderViewCompact) headerCell.getView();
boolean hasFolders = hasFolders();
boolean displayTabsAtBottom = displayTabsAtBottom();
+ headerCell.getTopView().setShowLabelOnActiveOnly(hasFolders && tdlib.settings().chatFolderStyle() == ChatFolderStyle.ICON_WITH_LABEL_ON_ACTIVE);
headerCell.getTopView().setUseDarkBackground(displayTabsAtBottom);
headerCell.getTopView().setDrawSelectionAtTop(displayTabsAtBottom);
headerCell.getTopView().setSlideOffDirection(displayTabsAtBottom ? ViewPagerTopView.SLIDE_OFF_DIRECTION_TOP : ViewPagerTopView.SLIDE_OFF_DIRECTION_BOTTOM);
@@ -2356,19 +2657,19 @@ private void updatePagerSections (TdApi.ChatFolderInfo[] chatFolders, int mainCh
}
private ViewPagerTopView.Item buildMainSectionItem (int pagerItemPosition, @ChatFolderStyle int chatFolderStyle) {
- CharSequence sectionName = getMenuSectionName(MAIN_PAGER_ITEM_ID, pagerItemPosition, /* hasFolders */ true, chatFolderStyle);
+ CharSequence sectionName = getMenuSectionName(MAIN_PAGER_ITEM_ID, pagerItemPosition, /* hasFolders */ true, chatFolderStyle, /* upperCase */ false);
int iconResource = menuNeedArchive ? R.drawable.baseline_archive_24 : R.drawable.baseline_forum_24;
return buildSectionItem(MAIN_PAGER_ITEM_ID, pagerItemPosition, ChatPosition.CHAT_LIST_MAIN, sectionName, iconResource, chatFolderStyle);
}
private ViewPagerTopView.Item buildArchiveSectionItem (int pagerItemPosition, @ChatFolderStyle int chatFolderStyle) {
- CharSequence sectionName = getMenuSectionName(ARCHIVE_PAGER_ITEM_ID, pagerItemPosition, /* hasFolders */ true, chatFolderStyle);
+ CharSequence sectionName = getMenuSectionName(ARCHIVE_PAGER_ITEM_ID, pagerItemPosition, /* hasFolders */ true, chatFolderStyle, /* upperCase */ false);
int iconResource = R.drawable.baseline_archive_24;
return buildSectionItem(ARCHIVE_PAGER_ITEM_ID, pagerItemPosition, ChatPosition.CHAT_LIST_ARCHIVE, sectionName, iconResource, chatFolderStyle);
}
private ViewPagerTopView.Item buildSectionItem (long pagerItemId, int pagerItemPosition, TdApi.ChatList chatList, TdApi.ChatFolderInfo chatFolderInfo, @ChatFolderStyle int chatFolderStyle) {
- CharSequence sectionName = Emoji.instance().replaceEmoji(getMenuSectionName(pagerItemId, chatFolderInfo.title, chatFolderStyle));
+ CharSequence sectionName = getFolderSectionName(pagerItemId, chatFolderInfo.title, chatFolderStyle);
int iconResource = TD.findFolderIcon(chatFolderInfo.icon, R.drawable.baseline_folder_24);
return buildSectionItem(pagerItemId, pagerItemPosition, chatList, sectionName, iconResource, chatFolderStyle);
}
@@ -2387,8 +2688,8 @@ private ViewPagerTopView.Item buildSectionItem (long pagerItemId, int pagerItemP
} else {
item = new ViewPagerTopView.Item(iconResource, unreadCounter);
}
- } else if (chatFolderStyle == ChatFolderStyle.LABEL_AND_ICON ||
- chatFolderStyle == ChatFolderStyle.ICON_ONLY && selectedFilter != FILTER_NONE) {
+ } else if (chatFolderStyle == ChatFolderStyle.LABEL_AND_ICON || chatFolderStyle == ChatFolderStyle.ICON_WITH_LABEL_ON_ACTIVE ||
+ (chatFolderStyle == ChatFolderStyle.ICON_ONLY && selectedFilter != FILTER_NONE)) {
item = new ViewPagerTopView.Item(sectionName, iconResource, unreadCounter);
} else if (chatFolderStyle == ChatFolderStyle.ICON_ONLY) {
item = new ViewPagerTopView.Item(iconResource, unreadCounter);
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsPagerController.java b/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsPagerController.java
index cefde2dbd8..1403729459 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsPagerController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/MessageOptionsPagerController.java
@@ -114,7 +114,7 @@ public MessageOptionsPagerController (Context context, Tdlib tdlib, Options opti
.drawable(R.drawable.baseline_favorite_16, 16f, 6f, Gravity.LEFT)
.build(), this, Screen.dp(16));
counters[ALL_REACTED_POSITION].counter.setCount(message.getMessageReactions().getTotalCount(), false);
- state.headerAlwaysVisibleCountersWidth += counters[ALL_REACTED_POSITION].calculateWidth(null, Screen.dp(ViewPagerTopView.DEFAULT_ITEM_SPACING));
+ state.headerAlwaysVisibleCountersWidth += counters[ALL_REACTED_POSITION].calculateWidth(null, Screen.dp(ViewPagerTopView.DEFAULT_ITEM_SPACING), /* labelFactor */ 1f);
} else {
ALL_REACTED_POSITION = -1;
}
@@ -126,7 +126,7 @@ public MessageOptionsPagerController (Context context, Tdlib tdlib, Options opti
.drawable(R.drawable.baseline_visibility_16, 16f, 6f, Gravity.LEFT)
.build(), this, Screen.dp(16));
counters[SEEN_POSITION].counter.setCount(1, false);
- int itemWidth = counters[SEEN_POSITION].calculateWidth(null, Screen.dp(ViewPagerTopView.DEFAULT_ITEM_SPACING)); // - Screen.dp(16);
+ int itemWidth = counters[SEEN_POSITION].calculateWidth(null, Screen.dp(ViewPagerTopView.DEFAULT_ITEM_SPACING), /* labelFactor */ 1f); // - Screen.dp(16);
state.headerAlwaysVisibleCountersWidth += itemWidth;
counters[SEEN_POSITION].setStaticWidth(itemWidth - Screen.dp(16));
counters[SEEN_POSITION].counter.setCount(Tdlib.CHAT_LOADING, false);
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SelectChatsController.java b/app/src/main/java/org/thunderdog/challegram/ui/SelectChatsController.java
index e0cd33fdd6..3206f1d712 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SelectChatsController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SelectChatsController.java
@@ -51,6 +51,7 @@
import org.thunderdog.challegram.loader.ComplexReceiver;
import org.thunderdog.challegram.loader.ImageFile;
import org.thunderdog.challegram.loader.ImageReceiver;
+import org.thunderdog.challegram.navigation.NavigationController;
import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.telegram.ChatListListener;
import org.thunderdog.challegram.telegram.Tdlib;
@@ -111,30 +112,32 @@ public static class Arguments {
private final @Nullable Delegate delegate;
private final Set selectedChatIds;
private final Set selectedChatTypes;
+ private final boolean showChatTypes;
- private Arguments (@Mode int mode, @Nullable Delegate delegate, int chatFolderId, @Nullable TdApi.ChatFolder chatFolder, Set selectedChatIds, Set selectedChatTypes) {
+ private Arguments (@Mode int mode, @Nullable Delegate delegate, int chatFolderId, @Nullable TdApi.ChatFolder chatFolder, Set selectedChatIds, Set selectedChatTypes, boolean showChatTypes) {
this.mode = mode;
this.delegate = delegate;
this.chatFolder = chatFolder;
this.chatFolderId = chatFolderId;
this.selectedChatIds = selectedChatIds;
this.selectedChatTypes = selectedChatTypes;
+ this.showChatTypes = showChatTypes;
}
- public static Arguments includedChats (int chatFolderId, TdApi.ChatFolder chatFolder) {
- return includedChats(null, chatFolderId, chatFolder);
+ public static Arguments includedChats (int chatFolderId, TdApi.ChatFolder chatFolder, boolean showChatTypes) {
+ return includedChats(null, chatFolderId, chatFolder, showChatTypes);
}
- public static Arguments includedChats (@Nullable Delegate delegate, int chatFolderId, TdApi.ChatFolder chatFolder) {
+ public static Arguments includedChats (@Nullable Delegate delegate, int chatFolderId, TdApi.ChatFolder chatFolder, boolean showChatTypes) {
Set selectedChatIds = unmodifiableLinkedHashSetOf(chatFolder.pinnedChatIds, chatFolder.includedChatIds);
Set selectedChatTypes = U.unmodifiableTreeSetOf(TD.includedChatTypes(chatFolder));
- return new Arguments(MODE_FOLDER_INCLUDE_CHATS, delegate, chatFolderId, chatFolder, selectedChatIds, selectedChatTypes);
+ return new Arguments(MODE_FOLDER_INCLUDE_CHATS, delegate, chatFolderId, chatFolder, selectedChatIds, selectedChatTypes, showChatTypes);
}
- public static Arguments excludedChats (@Nullable Delegate delegate, int chatFolderId, TdApi.ChatFolder chatFolder) {
+ public static Arguments excludedChats (@Nullable Delegate delegate, int chatFolderId, TdApi.ChatFolder chatFolder, boolean showChatTypes) {
Set selectedChatIds = unmodifiableLinkedHashSetOf(chatFolder.excludedChatIds);
Set selectedChatTypes = U.unmodifiableTreeSetOf(TD.excludedChatTypes(chatFolder));
- return new Arguments(MODE_FOLDER_EXCLUDE_CHATS, delegate, chatFolderId, chatFolder, selectedChatIds, selectedChatTypes);
+ return new Arguments(MODE_FOLDER_EXCLUDE_CHATS, delegate, chatFolderId, chatFolder, selectedChatIds, selectedChatTypes, showChatTypes);
}
private static Set unmodifiableLinkedHashSetOf (long[]... arrays) {
@@ -163,6 +166,7 @@ private static Set unmodifiableLinkedHashSetOf (long[]... arrays) {
private Set selectedChatIds = Collections.emptySet();
private Set selectedChatTypes = Collections.emptySet();
+ private boolean showChatTypes;
private int secretChatCount;
private int nonSecretChatCount;
@@ -184,6 +188,7 @@ public void setArguments (Arguments args) {
delegate = args.delegate;
selectedChatIds = new LinkedHashSet<>(args.selectedChatIds);
selectedChatTypes = new TreeSet<>(args.selectedChatTypes);
+ showChatTypes = args.showChatTypes;
secretChatCount = 0;
nonSecretChatCount = 0;
@@ -244,19 +249,21 @@ protected void onCreateView (Context context, CustomRecyclerView recyclerView) {
items.add(new ListItem(ListItem.TYPE_DESCRIPTION, R.id.description, 0, description));
}
- items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.ChatTypes));
- items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
- if (arguments.mode == MODE_FOLDER_INCLUDE_CHATS) {
- for (int chatType : TD.CHAT_TYPES_TO_INCLUDE) {
- items.add(chatTypeItem(chatType));
+ if (showChatTypes) {
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.ChatTypes));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ if (arguments.mode == MODE_FOLDER_INCLUDE_CHATS) {
+ for (int chatType : TD.CHAT_TYPES_TO_INCLUDE) {
+ items.add(chatTypeItem(chatType));
+ }
}
- }
- if (arguments.mode == MODE_FOLDER_EXCLUDE_CHATS) {
- for (int chatType : TD.CHAT_TYPES_TO_EXCLUDE) {
- items.add(chatTypeItem(chatType));
+ if (arguments.mode == MODE_FOLDER_EXCLUDE_CHATS) {
+ for (int chatType : TD.CHAT_TYPES_TO_EXCLUDE) {
+ items.add(chatTypeItem(chatType));
+ }
}
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
}
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.Chats));
items.add(new ListItem(ListItem.TYPE_SHADOW_TOP, chatsHeaderId));
@@ -341,6 +348,11 @@ public boolean onBackPressed (boolean fromTop) {
return super.onBackPressed(fromTop);
}
+ @Override
+ public boolean canSlideBackFrom (NavigationController navigationController, float x, float y) {
+ return !hasChanges();
+ }
+
private void updateDoneButton () {
setDoneVisible(hasChanges(), true);
}
@@ -791,7 +803,7 @@ private static int getIntrinsicWidth (int labelWidth, boolean hasIcon) {
}
@Override
- public void draw (Canvas canvas) {
+ public void draw (@NonNull Canvas canvas) {
Rect bounds = getBounds();
if (bounds.isEmpty() || alpha == 0) {
return;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SetSenderControllerPage.java b/app/src/main/java/org/thunderdog/challegram/ui/SetSenderControllerPage.java
index 0bbbe3eb03..c1c15201a7 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SetSenderControllerPage.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SetSenderControllerPage.java
@@ -207,14 +207,14 @@ private DoubleTextWrapper parseObject (TdApi.Object object) {
DoubleTextWrapper d = new DoubleTextWrapper(tdlib, Td.getSenderUserId(sender.sender), true);
d.setChatMessageSender(sender);
d.setForcedSubtitle(Lang.getString(R.string.YourAccount));
- d.setDrawFakeCheckbox(Td.getSenderId(currentSender) == Td.getSenderId(sender.sender));
+ d.setIsChecked(Td.getSenderId(currentSender) == Td.getSenderId(sender.sender), /* animated */ false);
return d;
} else {
TdApi.Chat chat = tdlib.chat(Td.getSenderId(sender.sender));
if (chat != null) {
DoubleTextWrapper d = new DoubleTextWrapper(tdlib, chat);
d.setChatMessageSender(sender);
- d.setDrawFakeCheckbox(Td.getSenderId(currentSender) == Td.getSenderId(sender.sender));
+ d.setIsChecked(Td.getSenderId(currentSender) == Td.getSenderId(sender.sender), /* animated */ false);
if (Td.getSenderId(sender.sender) == this.chat.id) {
d.setForcedSubtitle(Lang.getString(R.string.AnonymousAdmin));
} else {
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingHolder.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingHolder.java
index 215524056d..c15ec8302b 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingHolder.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingHolder.java
@@ -35,12 +35,14 @@
import android.widget.RelativeLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.thunderdog.challegram.FillingDrawable;
import org.thunderdog.challegram.R;
+import org.thunderdog.challegram.charts.LayoutHelper;
import org.thunderdog.challegram.charts.data.ChartDataUtil;
import org.thunderdog.challegram.charts.view_data.ChartHeaderView;
import org.thunderdog.challegram.component.RelativeSessionLayout;
@@ -206,7 +208,9 @@ public static int measureHeightForType (int viewType) {
return Screen.dp(32f) + Screen.dp(4f);
}
case ListItem.TYPE_HEADER:
- case ListItem.TYPE_HEADER_WITH_ACTION: {
+ case ListItem.TYPE_HEADER_WITH_ACTION:
+ case ListItem.TYPE_HEADER_WITH_TEXT_BUTTON:
+ case ListItem.TYPE_HEADER_WITH_CHECKBOX: {
return Screen.dp(32f);
}
case ListItem.TYPE_SESSION:
@@ -484,6 +488,8 @@ public static boolean isValuedType (int viewType) {
case ListItem.TYPE_CHECKBOX_OPTION_DOUBLE_LINE:
case ListItem.TYPE_CHECKBOX_OPTION_MULTILINE:
case ListItem.TYPE_REACTION_CHECKBOX:
+ case ListItem.TYPE_HEADER_WITH_TEXT_BUTTON:
+ case ListItem.TYPE_HEADER_WITH_CHECKBOX:
case ListItem.TYPE_RADIO_OPTION:
case ListItem.TYPE_RADIO_OPTION_LEFT:
case ListItem.TYPE_RADIO_OPTION_WITH_AVATAR:
@@ -1277,6 +1283,8 @@ public void getItemOffsets (Rect outRect, View view, RecyclerView parent, Recycl
case ListItem.TYPE_HEADER:
case ListItem.TYPE_HEADER_MULTILINE:
case ListItem.TYPE_HEADER_WITH_ACTION:
+ case ListItem.TYPE_HEADER_WITH_TEXT_BUTTON:
+ case ListItem.TYPE_HEADER_WITH_CHECKBOX:
case ListItem.TYPE_HEADER_PADDED: {
final boolean isRtl = Lang.rtl();
@@ -1292,29 +1300,69 @@ public void getItemOffsets (Rect outRect, View view, RecyclerView parent, Recycl
}
textView.setTypeface(Fonts.getRobotoMedium());
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15f);
- if (viewType != ListItem.TYPE_HEADER_WITH_ACTION) {
- textView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, viewType == ListItem.TYPE_HEADER_MULTILINE ? ViewGroup.LayoutParams.WRAP_CONTENT : Screen.dp(32f) + paddingTop));
- adapter.modifyHeaderTextView(textView, Screen.dp(32f), paddingTop);
- return new SettingHolder(textView);
+
+ if (viewType == ListItem.TYPE_HEADER_WITH_ACTION) {
+ FrameLayoutFix wrapView = new FrameLayoutFix(context);
+ textView.setLayoutParams(FrameLayoutFix.newParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ wrapView.addView(textView);
+
+ ImageView imageView = new ImageView(context);
+ imageView.setLayoutParams(FrameLayoutFix.newParams(Screen.dp(52f), ViewGroup.LayoutParams.MATCH_PARENT, isRtl ? Gravity.LEFT : Gravity.RIGHT));
+ imageView.setScaleType(ImageView.ScaleType.CENTER);
+ imageView.setColorFilter(Theme.getColor(ColorId.background_textLight));
+ if (themeProvider != null) {
+ themeProvider.addThemeFilterListener(imageView, ColorId.background_textLight);
+ }
+ imageView.setOnClickListener(onClickListener);
+ wrapView.addView(imageView);
+ wrapView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Screen.dp(32f)));
+ return new SettingHolder(wrapView);
}
- FrameLayoutFix wrapView = new FrameLayoutFix(context);
- textView.setLayoutParams(FrameLayoutFix.newParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- wrapView.addView(textView);
+ if (viewType == ListItem.TYPE_HEADER_WITH_TEXT_BUTTON) {
+ textView.setId(android.R.id.text1);
+ LinearLayout wrapView = new LinearLayout(context);
+ wrapView.addView(textView, LayoutHelper.createLinear(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
- ImageView imageView = new ImageView(context);
- imageView.setLayoutParams(FrameLayoutFix.newParams(Screen.dp(52f), ViewGroup.LayoutParams.MATCH_PARENT, isRtl ? Gravity.LEFT : Gravity.RIGHT));
- imageView.setScaleType(ImageView.ScaleType.CENTER);
- imageView.setColorFilter(Theme.getColor(ColorId.background_textLight));
- if (themeProvider != null) {
- themeProvider.addThemeFilterListener(imageView, ColorId.background_textLight);
+ ScalableTextView textButton = new ScalableTextView(context) {
+ @Override
+ protected void onReplaceText (@NonNull CharSequence replacement) {
+ setText(replacement);
+ }
+ };
+ textButton.setPadding(Screen.dp(16f), 0, Screen.dp(16f), 0);
+ textButton.setGravity(Gravity.CENTER_VERTICAL);
+ textButton.setSingleLine(true);
+ textButton.setTextColor(Theme.textAccent2Color());
+ if (themeProvider != null) {
+ themeProvider.addThemeTextColorListener(textButton, ColorId.background_text);
+ }
+ textButton.setTypeface(Fonts.getRobotoRegular());
+ textButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15f);
+ textButton.setOnClickListener(onClickListener);
+ wrapView.addView(textButton, isRtl ? 0 : 1, LayoutHelper.createLinear(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ wrapView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Screen.dp(32f)));
+ return new SettingHolder(wrapView);
}
- imageView.setOnClickListener(onClickListener);
- wrapView.addView(imageView);
- wrapView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Screen.dp(32f)));
+ if (viewType == ListItem.TYPE_HEADER_WITH_CHECKBOX) {
+ textView.setId(android.R.id.text1);
+ FrameLayoutFix wrapView = new FrameLayoutFix(context);
+ wrapView.addView(textView, FrameLayoutFix.newParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- return new SettingHolder(wrapView);
+ CheckBoxView checkBox = CheckBoxView.simpleCheckBox(context, isRtl);
+ checkBox.setOnClickListener(onClickListener);
+ if (themeProvider != null) {
+ themeProvider.addThemeInvalidateListener(checkBox);
+ }
+ wrapView.addView(checkBox);
+ wrapView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, Screen.dp(32f)));
+ return new SettingHolder(wrapView);
+ }
+
+ textView.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, viewType == ListItem.TYPE_HEADER_MULTILINE ? ViewGroup.LayoutParams.WRAP_CONTENT : Screen.dp(32f) + paddingTop));
+ adapter.modifyHeaderTextView(textView, Screen.dp(32f), paddingTop);
+ return new SettingHolder(textView);
}
case ListItem.TYPE_DESCRIPTION:
case ListItem.TYPE_DESCRIPTION_CENTERED:
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsAdapter.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsAdapter.java
index 4464540aa5..918e3f6d1b 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsAdapter.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsAdapter.java
@@ -339,6 +339,10 @@ protected void setHeaderText (ListItem item, TextView view, boolean isUpdate) {
Views.setMediumText(view, item.getString());
}
+ protected void setHeaderCheckBoxState (ListItem item, CheckBoxView checkBox, boolean isUpdate) {
+ checkBox.setChecked(item.isSelected(), isUpdate);
+ }
+
protected void setPlace (ListItem item, int position, MediaLocationPlaceView view, boolean isUpdate) {
// Override
}
@@ -703,6 +707,26 @@ public void updateValuedSettingByPosition (int position) {
break;
}
+ case ListItem.TYPE_HEADER_WITH_TEXT_BUTTON: {
+ TextView textView = view.findViewById(android.R.id.text1);
+ setHeaderText(item, textView, /* isUpdate */ true);
+
+ int buttonIndex = (((ViewGroup) view).indexOfChild(textView) + 1) % 2;
+ ScalableTextView textButton = (ScalableTextView) ((ViewGroup) view).getChildAt(buttonIndex);
+ setButtonText(item, textButton, /* isUpdate */ true);
+ break;
+ }
+
+ case ListItem.TYPE_HEADER_WITH_CHECKBOX: {
+ ok = true;
+ TextView textView = (TextView) ((ViewGroup) view).getChildAt(0);
+ setHeaderText(item, textView, /* isUpdate */ true);
+
+ CheckBoxView checkBox = (CheckBoxView) ((ViewGroup) view).getChildAt(1);
+ setHeaderCheckBoxState(item, checkBox, /* isUpdate */ true);
+ break;
+ }
+
case ListItem.TYPE_BUTTON: {
if (ok = view instanceof ViewGroup && ((ViewGroup) view).getChildAt(0) instanceof ScalableTextView) {
setButtonText(item, (ScalableTextView) ((ViewGroup) view).getChildAt(0), true);
@@ -1269,7 +1293,9 @@ protected boolean needsBackground (ListItem item) {
case ListItem.TYPE_SHADOW_BOTTOM:
case ListItem.TYPE_HEADER:
case ListItem.TYPE_HEADER_PADDED:
- case ListItem.TYPE_HEADER_WITH_ACTION: {
+ case ListItem.TYPE_HEADER_WITH_ACTION:
+ case ListItem.TYPE_HEADER_WITH_TEXT_BUTTON:
+ case ListItem.TYPE_HEADER_WITH_CHECKBOX: {
return true;
}
}
@@ -1770,6 +1796,32 @@ public void updateView (SettingHolder holder, int position, int viewType) {
Views.setGravity((FrameLayout.LayoutParams) imageView.getLayoutParams(), Lang.rtl() ? Gravity.LEFT : Gravity.RIGHT);
break;
}
+ case ListItem.TYPE_HEADER_WITH_TEXT_BUTTON: {
+ TextView textView = holder.itemView.findViewById(android.R.id.text1);
+ textView.setTextColor(Theme.getColor(item.getTextColorId(ColorId.background_textLight)));
+ textView.setGravity(Lang.gravity(Gravity.CENTER_VERTICAL));
+ setHeaderText(item, textView, false);
+
+ int buttonIndex = (((ViewGroup) holder.itemView).indexOfChild(textView) + 1) % 2;
+ ScalableTextView textButton = (ScalableTextView) ((ViewGroup) holder.itemView).getChildAt(buttonIndex);
+ textButton.setId(item.getId());
+ textButton.setTag(item);
+ setButtonText(item, textButton, false);
+ break;
+ }
+ case ListItem.TYPE_HEADER_WITH_CHECKBOX: {
+ TextView textView = (TextView) ((ViewGroup) holder.itemView).getChildAt(0);
+ textView.setGravity(Lang.gravity(Gravity.CENTER_VERTICAL));
+ textView.setTextColor(Theme.getColor(item.getTextColorId(ColorId.background_textLight)));
+ setHeaderText(item, textView, false);
+
+ CheckBoxView checkBox = (CheckBoxView) ((ViewGroup) holder.itemView).getChildAt(1);
+ checkBox.setId(item.getId());
+
+ setHeaderCheckBoxState(item, checkBox, false);
+ Views.setGravity(checkBox, Lang.reverseGravity(Gravity.CENTER_VERTICAL));
+ break;
+ }
case ListItem.TYPE_COUNTRY: {
ViewGroup group = (ViewGroup) holder.itemView;
((TextView) group.getChildAt(0)).setText(item.getString());
@@ -1781,10 +1833,11 @@ public void updateView (SettingHolder holder, int position, int viewType) {
case ListItem.TYPE_DESCRIPTION_CENTERED: {
TextView textView = (TextView) holder.itemView;
textView.setTextColor(Theme.getColor(item.getTextColorId(viewType == ListItem.TYPE_DESCRIPTION_CENTERED ? ColorId.textLight : ColorId.background_textLight)));
- int padding = Screen.dp(16f) + item.getTextPaddingLeft();
+ int paddingLeft = Screen.dp(16f) + item.getTextPaddingLeft();
+ int paddingRight = Screen.dp(16f) + item.getTextPaddingRight();
textView.setText(item.getString());
- if (holder.itemView.getPaddingLeft() != padding) {
- holder.itemView.setPadding(padding, holder.itemView.getPaddingTop(), holder.itemView.getPaddingRight(), holder.itemView.getPaddingBottom());
+ if (holder.itemView.getPaddingLeft() != paddingLeft || holder.itemView.getPaddingRight() != paddingRight) {
+ holder.itemView.setPadding(paddingLeft, holder.itemView.getPaddingTop(), paddingRight, holder.itemView.getPaddingBottom());
}
if (viewType != ListItem.TYPE_DESCRIPTION_CENTERED) {
textView.setGravity(Lang.gravity(Gravity.CENTER_VERTICAL));
@@ -2221,6 +2274,13 @@ public void updateAllValuedSettings (@NonNull Filter filter) {
}
}
+ public void notifyLastItemChanged () {
+ int itemCount = getItemCount();
+ if (itemCount > 0) {
+ notifyItemChanged(itemCount - 1);
+ }
+ }
+
public void notifyItemsChanged (@NonNull Filter filter) {
notifyItemsChangedImpl(item -> filter.accept(item) ? wouldUpdateItem(item) : CellFilterImpl.REJECTED);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/SettingsFoldersController.java b/app/src/main/java/org/thunderdog/challegram/ui/SettingsFoldersController.java
index cabb696b83..47cd9d7fb5 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/SettingsFoldersController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/SettingsFoldersController.java
@@ -33,19 +33,23 @@
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.SimpleItemAnimator;
import org.drinkless.tdlib.TdApi;
-import org.thunderdog.challegram.BuildConfig;
import org.thunderdog.challegram.R;
import org.thunderdog.challegram.component.base.SettingView;
import org.thunderdog.challegram.component.user.RemoveHelper;
import org.thunderdog.challegram.config.Config;
import org.thunderdog.challegram.core.Lang;
+import org.thunderdog.challegram.data.TD;
import org.thunderdog.challegram.emoji.Emoji;
import org.thunderdog.challegram.navigation.SettingsWrapBuilder;
+import org.thunderdog.challegram.navigation.TooltipOverlayView;
import org.thunderdog.challegram.telegram.ChatFolderStyle;
import org.thunderdog.challegram.telegram.ChatFoldersListener;
import org.thunderdog.challegram.telegram.Tdlib;
+import org.thunderdog.challegram.telegram.TdlibSettingsManager;
+import org.thunderdog.challegram.telegram.TdlibUi;
import org.thunderdog.challegram.theme.ColorId;
import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Paints;
@@ -55,7 +59,9 @@
import org.thunderdog.challegram.unsorted.Settings;
import org.thunderdog.challegram.util.AdapterSubListUpdateCallback;
import org.thunderdog.challegram.util.DrawModifier;
+import org.thunderdog.challegram.util.EndIconModifier;
import org.thunderdog.challegram.util.ListItemDiffUtilCallback;
+import org.thunderdog.challegram.util.PremiumLockModifier;
import org.thunderdog.challegram.util.text.Text;
import org.thunderdog.challegram.v.CustomRecyclerView;
import org.thunderdog.challegram.widget.NonMaterialButton;
@@ -75,8 +81,9 @@
import me.vkryl.core.collection.IntList;
import me.vkryl.core.lambda.RunnableBool;
import me.vkryl.td.ChatPosition;
+import me.vkryl.td.Td;
-public class SettingsFoldersController extends RecyclerViewController implements View.OnClickListener, View.OnLongClickListener, ChatFoldersListener {
+public class SettingsFoldersController extends RecyclerViewController implements View.OnClickListener, View.OnLongClickListener, ChatFoldersListener, TdlibSettingsManager.ChatListPositionListener {
private static final long MAIN_CHAT_FOLDER_ID = Long.MIN_VALUE;
private static final long ARCHIVE_CHAT_FOLDER_ID = Long.MIN_VALUE + 1;
@@ -86,6 +93,10 @@ public class SettingsFoldersController extends RecyclerViewController impl
private final @IdRes int chatFoldersPreviousItemId = ViewCompat.generateViewId();
private final @IdRes int recommendedChatFoldersPreviousItemId = ViewCompat.generateViewId();
+ private final PremiumLockModifier premiumLockModifier = new PremiumLockModifier();
+ private final DrawModifier hasInviteLinksModifier = new EndIconModifier(R.drawable.baseline_link_24, ColorId.icon);
+ private final DrawModifier disabledFolderModifier = new EndIconModifier(R.drawable.baseline_eye_off_24, ColorId.icon);
+
private int chatFolderGroupItemCount, recommendedChatFolderGroupItemCount;
private boolean recommendedChatFoldersInitialized;
@@ -120,6 +131,10 @@ public CharSequence getName () {
@Override
protected void onCreateView (Context context, CustomRecyclerView recyclerView) {
+ RecyclerView.ItemAnimator itemAnimator = recyclerView.getItemAnimator();
+ if (itemAnimator instanceof SimpleItemAnimator) {
+ ((SimpleItemAnimator) itemAnimator).setSupportsChangeAnimations(false);
+ }
itemTouchHelper = RemoveHelper.attach(recyclerView, new ItemTouchHelperCallback());
TdApi.ChatFolderInfo[] chatFolders = tdlib.chatFolders();
@@ -134,10 +149,22 @@ protected void onCreateView (Context context, CustomRecyclerView recyclerView) {
items.add(new ListItem(ListItem.TYPE_VALUED_SETTING_COMPACT, R.id.btn_chatFolderStyle, 0, R.string.ChatFoldersAppearance));
items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_appBadge, 0, R.string.BadgeCounter));
+ if (Config.CHAT_FOLDERS_REDESIGN) {
+ items.add(new ListItem(ListItem.TYPE_SEPARATOR_FULL));
+ items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_archiveAsFolder, 0, R.string.ArchiveAsFolder));
+ }
// items.add(new ListItem(ListItem.TYPE_RADIO_SETTING, R.id.btn_countMutedChats, 0, R.string.CountMutedChats));
- items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM, chatFoldersPreviousItemId));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ items.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.ChatFolders));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_TOP, chatFoldersPreviousItemId));
items.addAll(chatFolderItemList);
+ int newFolderIconRes = Config.CHAT_FOLDERS_REDESIGN ? R.drawable.baseline_add_24 : R.drawable.baseline_create_new_folder_24;
+ items.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_createNewFolder, newFolderIconRes, R.string.CreateNewFolder).setTextColorId(ColorId.inlineText));
+ items.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ int infoRes = Config.CHAT_FOLDERS_REDESIGN ? R.string.ChatFoldersReorderTip : R.string.ChatFoldersInfo;
+ items.add(new ListItem(ListItem.TYPE_DESCRIPTION, recommendedChatFoldersPreviousItemId, 0, infoRes));
+
items.add(new ListItem(ListItem.TYPE_PADDING).setHeight(Screen.dp(12f)));
adapter = new SettingsAdapter(this) {
@@ -147,24 +174,27 @@ protected SettingHolder initCustom (ViewGroup parent, int customViewType) {
switch (customViewType) {
case TYPE_CHAT_FOLDER: {
SettingView settingView = new SettingView(parent.getContext(), tdlib);
- settingView.setType(SettingView.TYPE_SETTING);
- settingView.addToggler();
- settingView.forcePadding(0, Screen.dp(66f));
- settingView.setOnTouchListener(new ChatFolderOnTouchListener());
+ settingView.setType(SettingView.TYPE_INFO_SUPERCOMPACT);
+ settingView.setSwapDataAndName();
settingView.setOnClickListener(SettingsFoldersController.this);
settingView.setOnLongClickListener(SettingsFoldersController.this);
- settingView.getToggler().setOnClickListener(v -> {
- ListItem item = (ListItem) settingView.getTag();
- TdApi.ChatList chatList = getChatList(item);
- if (Config.RESTRICT_HIDING_MAIN_LIST && isMainChatFolder(item) && settingView.getToggler().isEnabled()) {
- return;
- }
- UI.forceVibrate(v, false);
- boolean enabled = settingView.getToggler().toggle(true);
- settingView.setVisuallyEnabled(enabled, true);
- settingView.setIconColorId(enabled ? ColorId.icon : ColorId.iconLight);
- tdlib.settings().setChatListEnabled(chatList, enabled);
- });
+ if (!Config.CHAT_FOLDERS_REDESIGN) {
+ settingView.addToggler();
+ settingView.forcePadding(0, Screen.dp(66f));
+ settingView.setOnTouchListener(new ChatFolderOnTouchListener());
+ settingView.getToggler().setOnClickListener(v -> {
+ ListItem item = (ListItem) settingView.getTag();
+ if (Config.RESTRICT_HIDING_MAIN_LIST && isMainChatFolder(item) && settingView.getToggler().isEnabled()) {
+ return;
+ }
+ UI.forceVibrate(v, false);
+ boolean enabled = settingView.getToggler().toggle(true);
+ settingView.setVisuallyEnabled(enabled, true);
+ settingView.setIconColorId(enabled ? ColorId.icon : ColorId.iconLight);
+ TdApi.ChatList chatList = getChatList(item);
+ tdlib.settings().setChatListEnabled(chatList, enabled);
+ });
+ }
addThemeInvalidateListener(settingView);
return new SettingHolder(settingView);
}
@@ -175,7 +205,7 @@ protected SettingHolder initCustom (ViewGroup parent, int customViewType) {
settingView.setOnClickListener(SettingsFoldersController.this);
addThemeInvalidateListener(settingView);
- FrameLayout.LayoutParams params = FrameLayoutFix.newParams(Screen.dp(29f), Screen.dp(28f), (Lang.rtl() ? Gravity.LEFT : Gravity.RIGHT) | Gravity.CENTER_VERTICAL);
+ FrameLayout.LayoutParams params = FrameLayoutFix.newParams(Screen.dp(29f), Screen.dp(28f), Lang.reverseGravity(Gravity.CENTER_VERTICAL));
params.leftMargin = params.rightMargin = Screen.dp(17f);
NonMaterialButton button = new NonMaterialButton(parent.getContext()) {
@Override
@@ -200,28 +230,47 @@ protected void modifyCustom (SettingHolder holder, int position, ListItem item,
SettingView settingView = (SettingView) holder.itemView;
settingView.setIcon(item.getIconResource());
settingView.setName(item.getString());
+ settingView.setData(item.getStringValue());
settingView.setTextColorId(item.getTextColorId(ColorId.NONE));
settingView.setIgnoreEnabled(true);
settingView.setEnabled(true);
- settingView.setDrawModifier(item.getDrawModifier());
boolean isEnabled;
if (isMainChatFolder(item) || isArchiveChatFolder(item)) {
isEnabled = tdlib.settings().isChatListEnabled(getChatList(item));
- settingView.setClickable(false);
- settingView.setLongClickable(false);
+ settingView.setClickable(Config.CHAT_FOLDERS_REDESIGN);
+ settingView.setLongClickable(Config.CHAT_FOLDERS_REDESIGN);
+ DrawModifier drawModifier = item.getDrawModifier();
+ settingView.setDrawModifier(drawModifier);
+ if (drawModifier instanceof PremiumLockModifier) {
+ settingView.setTooltipLocationProvider((PremiumLockModifier) drawModifier);
+ } else {
+ settingView.setTooltipLocationProvider(null);
+ }
} else if (isChatFolder(item)) {
isEnabled = tdlib.settings().isChatFolderEnabled(item.getIntValue());
settingView.setClickable(true);
settingView.setLongClickable(true);
+ DrawModifier drawModifier;
+ TdApi.ChatFolderInfo info = (TdApi.ChatFolderInfo) item.getData();
+ if (!isEnabled) {
+ drawModifier = disabledFolderModifier;
+ } else if (info.hasMyInviteLinks) {
+ drawModifier = hasInviteLinksModifier;
+ } else {
+ drawModifier = item.getDrawModifier();
+ }
+ settingView.setDrawModifier(drawModifier);
} else {
throw new IllegalArgumentException();
}
settingView.setVisuallyEnabled(isEnabled, false);
- settingView.getToggler().setRadioEnabled(isEnabled, false);
settingView.setIconColorId(isEnabled ? ColorId.icon : ColorId.iconLight);
- if (Config.RESTRICT_HIDING_MAIN_LIST) {
- settingView.getToggler().setVisibility(isMainChatFolder(item) ? View.GONE : View.VISIBLE);
+ if (!Config.CHAT_FOLDERS_REDESIGN) {
+ settingView.getToggler().setRadioEnabled(isEnabled, false);
+ if (Config.RESTRICT_HIDING_MAIN_LIST) {
+ settingView.getToggler().setVisibility(isMainChatFolder(item) ? View.GONE : View.VISIBLE);
+ }
}
} else if (customViewType == TYPE_RECOMMENDED_CHAT_FOLDER) {
SettingView settingView = (SettingView) holder.itemView;
@@ -242,14 +291,19 @@ protected void modifyCustom (SettingHolder holder, int position, ListItem item,
@Override
protected void setValuedSetting (ListItem item, SettingView view, boolean isUpdate) {
if (item.getId() == R.id.btn_createNewFolder) {
- boolean canCreateChatFolder = canCreateChatFolder();
+ boolean canCreateChatFolder = tdlib.canCreateChatFolder();
view.setIgnoreEnabled(true);
view.setVisuallyEnabled(canCreateChatFolder, isUpdate);
view.setIconColorId(canCreateChatFolder ? ColorId.inlineIcon : ColorId.iconLight);
+ PremiumLockModifier modifier = (canCreateChatFolder || tdlib.hasPremium()) ? null : premiumLockModifier;
+ view.setDrawModifier(modifier);
+ view.setTooltipLocationProvider(modifier);
} else {
view.setIgnoreEnabled(false);
view.setEnabledAnimated(true, isUpdate);
view.setIconColorId(ColorId.NONE);
+ view.setDrawModifier(item.getDrawModifier());
+ view.setTooltipLocationProvider(null);
}
if (item.getId() == R.id.btn_chatFolderStyle) {
int positionRes;
@@ -266,6 +320,9 @@ protected void setValuedSetting (ListItem item, SettingView view, boolean isUpda
case ChatFolderStyle.ICON_ONLY:
styleRes = R.string.IconOnly;
break;
+ case ChatFolderStyle.ICON_WITH_LABEL_ON_ACTIVE:
+ styleRes = R.string.IconWithLabelOnActiveFolder;
+ break;
default:
case ChatFolderStyle.LABEL_ONLY:
styleRes = R.string.LabelOnly;
@@ -275,13 +332,18 @@ protected void setValuedSetting (ListItem item, SettingView view, boolean isUpda
} else if (item.getId() == R.id.btn_countMutedChats) {
boolean isEnabled = BitwiseUtils.hasFlag(tdlib.settings().getChatFolderBadgeFlags(), Settings.BADGE_FLAG_MUTED);
view.getToggler().setRadioEnabled(isEnabled, isUpdate);
+ } else if (item.getId() == R.id.btn_archiveAsFolder) {
+ boolean showArchiveAsFolder = tdlib.settings().isChatListEnabled(ChatPosition.CHAT_LIST_ARCHIVE);
+ view.getToggler().setRadioEnabled(showArchiveAsFolder, isUpdate);
}
}
};
adapter.setItems(items, false);
+ recyclerView.addItemDecoration(new ItemDecoration());
recyclerView.setAdapter(adapter);
tdlib.listeners().subscribeToChatFoldersUpdates(this);
+ tdlib.settings().addChatListPositionListener(this);
updateRecommendedChatFolders();
}
@@ -332,10 +394,22 @@ public void onChatFoldersChanged (TdApi.ChatFolderInfo[] chatFolders, int mainCh
});
}
+ @Override
+ public void onChatListStateChanged (Tdlib tdlib, TdApi.ChatList chatList, boolean isEnabled) {
+ runOnUiThreadOptional(() -> {
+ int startIndex = indexOfFirstChatFolder();
+ if (startIndex == RecyclerView.NO_POSITION) return;
+ int index = adapter.indexOfView((item) -> isChatFolder(item) && Td.equalsTo(chatList, getChatList(item)), startIndex, false);
+ if (index != RecyclerView.NO_POSITION) {
+ adapter.notifyItemChanged(index);
+ }
+ });
+ }
+
@Override
public void onClick (View v) {
if (v.getId() == R.id.btn_createNewFolder) {
- if (canCreateChatFolder()) {
+ if (tdlib.canCreateChatFolder()) {
navigateTo(EditChatFolderController.newFolder(context, tdlib));
} else {
showChatFolderLimitReached(v);
@@ -343,11 +417,14 @@ public void onClick (View v) {
} else if (v.getId() == R.id.chatFolder) {
ListItem item = (ListItem) v.getTag();
if (isMainChatFolder(item) || isArchiveChatFolder(item)) {
+ if (Config.CHAT_FOLDERS_REDESIGN) {
+ showTooltip(v, Lang.getString(R.string.HoldAndDragToReorder), View::getDrawingRect);
+ }
return;
}
editChatFolder((TdApi.ChatFolderInfo) item.getData());
} else if (v.getId() == R.id.recommendedChatFolder) {
- if (canCreateChatFolder()) {
+ if (tdlib.canCreateChatFolder()) {
ListItem item = (ListItem) v.getTag();
TdApi.ChatFolder chatFolder = (TdApi.ChatFolder) item.getData();
chatFolder.icon = tdlib.chatFolderIcon(chatFolder);
@@ -358,7 +435,7 @@ public void onClick (View v) {
} else if (v.getId() == R.id.btn_double) {
Object tag = v.getTag();
if (tag instanceof TdApi.ChatFolder) {
- if (canCreateChatFolder()) {
+ if (tdlib.canCreateChatFolder()) {
v.setEnabled(false);
TdApi.ChatFolder chatFolder = (TdApi.ChatFolder) tag;
WeakReference viewRef = new WeakReference<>(v);
@@ -384,9 +461,11 @@ public void onClick (View v) {
new ListItem(ListItem.TYPE_SHADOW_TOP).setTextColorId(ColorId.background),
new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_labelOnly, 0, R.string.LabelOnly, R.id.btn_chatFolderStyle, chatFolderStyle == ChatFolderStyle.LABEL_ONLY),
new ListItem(ListItem.TYPE_SEPARATOR_FULL),
+ new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_labelAndIcon, 0, R.string.LabelAndIcon, R.id.btn_chatFolderStyle, chatFolderStyle == ChatFolderStyle.LABEL_AND_ICON),
+ new ListItem(ListItem.TYPE_SEPARATOR_FULL),
new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_iconOnly, 0, R.string.IconOnly, R.id.btn_chatFolderStyle, chatFolderStyle == ChatFolderStyle.ICON_ONLY),
new ListItem(ListItem.TYPE_SEPARATOR_FULL),
- new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_labelAndIcon, 0, R.string.LabelAndIcon, R.id.btn_chatFolderStyle, chatFolderStyle == ChatFolderStyle.LABEL_AND_ICON),
+ new ListItem(ListItem.TYPE_RADIO_OPTION, R.id.btn_iconWithLabelOnActiveFolder, 0, R.string.IconWithLabelOnActiveFolder, R.id.btn_chatFolderStyle, chatFolderStyle == ChatFolderStyle.ICON_WITH_LABEL_ON_ACTIVE)
};
SettingsWrapBuilder settings = new SettingsWrapBuilder(R.id.btn_chatFolderStyle)
.setRawItems(items)
@@ -398,6 +477,8 @@ public void onClick (View v) {
style = ChatFolderStyle.ICON_ONLY;
} else if (selection == R.id.btn_labelAndIcon) {
style = ChatFolderStyle.LABEL_AND_ICON;
+ } else if (selection == R.id.btn_iconWithLabelOnActiveFolder) {
+ style = ChatFolderStyle.ICON_WITH_LABEL_ON_ACTIVE;
} else {
style = ChatFolderStyle.LABEL_ONLY;
}
@@ -415,12 +496,20 @@ public void onClick (View v) {
SettingsNotificationController c = new SettingsNotificationController(context, tdlib);
c.setArguments(new SettingsNotificationController.Args(SettingsNotificationController.Section.APP_BADGE, 0));
navigateTo(c);
+ } else if (v.getId() == R.id.btn_archiveAsFolder) {
+ boolean showArchiveAsFolder = adapter.toggleView(v);
+ tdlib.settings().setChatListEnabled(ChatPosition.CHAT_LIST_ARCHIVE, showArchiveAsFolder);
+ updateChatFolders();
}
}
@Override
public boolean onLongClick (View v) {
if (v.getId() == R.id.chatFolder) {
+ if (Config.CHAT_FOLDERS_REDESIGN) {
+ startDrag(getRecyclerView().getChildViewHolder(v));
+ return true;
+ }
ListItem item = (ListItem) v.getTag();
if (isMainChatFolder(item) || isArchiveChatFolder(item)) {
return false;
@@ -455,28 +544,20 @@ private void showChatFolderLimitReached (View view) {
if (tdlib.hasPremium()) {
showTooltip(view, Lang.getMarkdownString(this, R.string.ChatFolderLimitReached, tdlib.chatFolderCountMax()));
} else {
- Object viewTag = view.getTag();
- WeakReference viewRef = new WeakReference<>(view);
- tdlib.send(new TdApi.GetPremiumLimit(new TdApi.PremiumLimitTypeChatFolderCount()), (premiumLimit, error) -> runOnUiThreadOptional(() -> {
- View v = viewRef.get();
- if (v == null || !ViewCompat.isAttachedToWindow(v) || viewTag != v.getTag())
- return;
- CharSequence text;
- if (premiumLimit != null) {
- text = Lang.getMarkdownString(this, R.string.PremiumRequiredCreateFolder, premiumLimit.defaultValue, premiumLimit.premiumValue);
- } else {
- text = Lang.getMarkdownString(this, R.string.ChatFolderLimitReached, tdlib.chatFolderCountMax());
- }
- showTooltip(v, text);
- }));
+ tdlib.ui().showPremiumLimitInfo(this, view, TdlibUi.PremiumLimit.CHAT_FOLDER_COUNT);
}
}
private void showTooltip (View view, CharSequence text) {
+ showTooltip(view, text, null);
+ }
+
+ private void showTooltip (View view, CharSequence text, @Nullable TooltipOverlayView.LocationProvider locationProvider) {
context()
.tooltipManager()
.builder(view)
.controller(this)
+ .locate(locationProvider)
.show(tdlib, text)
.hideDelayed(3500, TimeUnit.MILLISECONDS);
}
@@ -491,15 +572,16 @@ private void showChatFolderOptions (TdApi.ChatFolderInfo chatFolderInfo) {
if (id == R.id.btn_edit) {
editChatFolder(chatFolderInfo);
} else if (id == R.id.btn_delete) {
- showRemoveFolderConfirm(chatFolderInfo.id);
+ showRemoveFolderConfirm(chatFolderInfo);
}
return true;
});
}
- private void showRemoveFolderConfirm (int chatFolderId) {
- showConfirm(Lang.getString(R.string.RemoveFolderConfirm), Lang.getString(R.string.Remove), R.drawable.baseline_delete_24, OptionColor.RED, () -> {
- deleteChatFolder(chatFolderId);
+ private void showRemoveFolderConfirm (TdApi.ChatFolderInfo info) {
+ tdlib.ui().showDeleteChatFolderConfirm(this, info.hasMyInviteLinks, () -> {
+ // TODO(nikita-toropov) leave chats suggestion
+ deleteChatFolder(info.id);
});
}
@@ -555,9 +637,9 @@ private void reorderChatFolders () {
int lastIndex = indexOfLastChatFolder();
if (firstIndex == RecyclerView.NO_POSITION || lastIndex == RecyclerView.NO_POSITION)
return;
- int mainChatListPosition = 0;
- int archiveChatListPosition = 0;
- IntList chatFoldersIds = new IntList(tdlib.chatFoldersCount());
+ int mainChatListPosition = tdlib.mainChatListPosition();
+ int archiveChatListPosition = tdlib.settings().archiveChatListPosition();
+ IntList chatFoldersIds = new IntList(tdlib.chatFolderCount());
int folderPosition = 0;
for (int index = firstIndex; index <= lastIndex; index++) {
ListItem item = adapter.getItem(index);
@@ -587,14 +669,15 @@ private void reorderChatFolders () {
return;
}
tdlib.settings().setArchiveChatListPosition(archiveChatListPosition);
- if (chatFoldersIds.size() > 0) {
- tdlib.send(new TdApi.ReorderChatFolders(chatFoldersIds.get(), mainChatListPosition), (ok, error) -> {
- if (error != null) {
- UI.showError(error);
- runOnUiThreadOptional(this::updateChatFolders);
- }
- });
+ if (chatFoldersIds.isEmpty()) {
+ return;
}
+ tdlib.send(new TdApi.ReorderChatFolders(chatFoldersIds.get(), mainChatListPosition), (ok, error) -> runOnUiThreadOptional(() -> {
+ if (error != null) {
+ UI.showError(error);
+ updateChatFolders();
+ }
+ }));
}
private boolean isChatFolder (ListItem item) {
@@ -626,13 +709,12 @@ private boolean canMoveChatFolder (ListItem item) {
}
private int indexOfFirstChatFolder () {
- int index = indexOfChatFolderGroup();
- return index == RecyclerView.NO_POSITION ? RecyclerView.NO_POSITION : index + 2 /* header, shadowTop */;
+ return indexOfChatFolderGroup();
}
private int indexOfLastChatFolder () {
int index = indexOfChatFolderGroup();
- return index == RecyclerView.NO_POSITION ? RecyclerView.NO_POSITION : index + chatFolderGroupItemCount - 2 /* shadowBottom, separator */;
+ return index == RecyclerView.NO_POSITION ? RecyclerView.NO_POSITION : index + chatFolderGroupItemCount - 1;
}
private int indexOfChatFolderGroup () {
@@ -646,16 +728,19 @@ private int indexOfRecommendedChatFolderGroup () {
}
private List buildChatFolderItemList (TdApi.ChatFolderInfo[] chatFolders, int mainChatListPosition, int archiveChatListPosition) {
- int chatFolderCount = chatFolders.length + 2; /* All Chats, Archived */
+ boolean showArchiveAsFolder = !Config.CHAT_FOLDERS_REDESIGN || tdlib.settings().isChatListEnabled(ChatPosition.CHAT_LIST_ARCHIVE);
+ int chatFolderCount = chatFolders.length + (showArchiveAsFolder ? 2 : 1); /* All Chats, Archived */
int chatFolderIndex = 0;
mainChatListPosition = MathUtils.clamp(mainChatListPosition, 0, chatFolders.length);
- archiveChatListPosition = MathUtils.clamp(archiveChatListPosition, 0, chatFolderCount - 1);
- if (mainChatListPosition >= archiveChatListPosition) {
- mainChatListPosition++;
+ if (showArchiveAsFolder) {
+ archiveChatListPosition = MathUtils.clamp(archiveChatListPosition, 0, chatFolderCount - 1);
+ if (mainChatListPosition >= archiveChatListPosition) {
+ mainChatListPosition++;
+ }
+ } else {
+ archiveChatListPosition = -1;
}
- List itemList = new ArrayList<>(chatFolderCount + 5);
- itemList.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.ChatFolders));
- itemList.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ List itemList = new ArrayList<>();
for (int position = 0; position < chatFolderCount; position++) {
if (position == mainChatListPosition) {
itemList.add(mainChatFolderItem());
@@ -664,30 +749,32 @@ private List buildChatFolderItemList (TdApi.ChatFolderInfo[] chatFolde
} else if (chatFolderIndex < chatFolders.length) {
TdApi.ChatFolderInfo chatFolder = chatFolders[chatFolderIndex++];
itemList.add(chatFolderItem(chatFolder));
- } else if (BuildConfig.DEBUG) {
- throw new RuntimeException();
+ } else {
+ throw new IllegalStateException(String.format("position=%d mainChatListPosition=%d archiveChatListPosition=%d chatFolderIndex=%d chatFolderCount=%d", position, mainChatListPosition, archiveChatListPosition, chatFolderIndex, chatFolderCount));
}
}
- itemList.add(new ListItem(ListItem.TYPE_SETTING, R.id.btn_createNewFolder, R.drawable.baseline_create_new_folder_24, R.string.CreateNewFolder).setTextColorId(ColorId.inlineText));
- itemList.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
- itemList.add(new ListItem(ListItem.TYPE_DESCRIPTION, recommendedChatFoldersPreviousItemId, 0, R.string.ChatFoldersInfo));
return itemList;
}
+ private static final int RECOMMENDED_FOLDERS_HEADER_ID = ViewCompat.generateViewId();
+ private static final int RECOMMENDED_FOLDERS_SHADOW_TOP_ID = ViewCompat.generateViewId();
+ private static final int RECOMMENDED_FOLDERS_SHADOW_BOTTOM_ID = ViewCompat.generateViewId();
+
private List buildRecommendedChatFolderItemList (TdApi.RecommendedChatFolder[] recommendedChatFolders) {
if (recommendedChatFolders.length == 0) {
return Collections.emptyList();
}
List itemList = new ArrayList<>(recommendedChatFolders.length * 2 - 1 + 3);
- itemList.add(new ListItem(ListItem.TYPE_HEADER, 0, 0, R.string.RecommendedFolders));
- itemList.add(new ListItem(ListItem.TYPE_SHADOW_TOP));
+ itemList.add(new ListItem(ListItem.TYPE_HEADER, RECOMMENDED_FOLDERS_HEADER_ID, 0, R.string.RecommendedFolders));
+ itemList.add(new ListItem(ListItem.TYPE_SHADOW_TOP, RECOMMENDED_FOLDERS_SHADOW_TOP_ID));
for (int index = 0; index < recommendedChatFolders.length; index++) {
+ TdApi.RecommendedChatFolder recommendedChatFolder = recommendedChatFolders[index];
if (index > 0) {
- itemList.add(new ListItem(ListItem.TYPE_SEPARATOR));
+ itemList.add(new ListItem(ListItem.TYPE_SEPARATOR).setStringValue(recommendedChatFolder.folder.title));
}
- itemList.add(recommendedChatFolderItem(recommendedChatFolders[index]));
+ itemList.add(recommendedChatFolderItem(recommendedChatFolder));
}
- itemList.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM));
+ itemList.add(new ListItem(ListItem.TYPE_SHADOW_BOTTOM, RECOMMENDED_FOLDERS_SHADOW_BOTTOM_ID));
return itemList;
}
@@ -695,8 +782,13 @@ private ListItem mainChatFolderItem () {
ListItem item = new ListItem(ListItem.TYPE_CUSTOM - TYPE_CHAT_FOLDER, R.id.chatFolder);
item.setString(R.string.CategoryMain);
item.setLongId(MAIN_CHAT_FOLDER_ID);
- item.setIconRes(tdlib.hasPremium() ? R.drawable.baseline_drag_handle_24 : R.drawable.deproko_baseline_lock_24);
- item.setDrawModifier(new FolderBadge(Lang.getString(R.string.MainListBadge)));
+ if (Config.CHAT_FOLDERS_REDESIGN) {
+ item.setIconRes(R.drawable.baseline_forum_24);
+ item.setDrawModifier(tdlib.hasPremium() ? null : premiumLockModifier);
+ } else {
+ item.setIconRes(tdlib.hasPremium() ? R.drawable.baseline_drag_handle_24 : R.drawable.deproko_baseline_lock_24);
+ item.setDrawModifier(new FolderBadge(Lang.getString(R.string.MainListBadge)));
+ }
return item;
}
@@ -704,13 +796,24 @@ private ListItem archiveChatFolderItem () {
ListItem item = new ListItem(ListItem.TYPE_CUSTOM - TYPE_CHAT_FOLDER, R.id.chatFolder);
item.setString(R.string.CategoryArchive);
item.setLongId(ARCHIVE_CHAT_FOLDER_ID);
- item.setIconRes(R.drawable.baseline_drag_handle_24);
- item.setDrawModifier(new FolderBadge(Lang.getString(R.string.LocalFolderBadge)));
+ if (Config.CHAT_FOLDERS_REDESIGN) {
+ item.setIconRes(R.drawable.baseline_archive_24);
+ item.setStringValue(Lang.getString(R.string.LocalFolder));
+ } else {
+ item.setIconRes(R.drawable.baseline_drag_handle_24);
+ item.setDrawModifier(new FolderBadge(Lang.getString(R.string.LocalFolderBadge)));
+ }
return item;
}
private ListItem chatFolderItem (TdApi.ChatFolderInfo chatFolderInfo) {
- ListItem item = new ListItem(ListItem.TYPE_CUSTOM - TYPE_CHAT_FOLDER, R.id.chatFolder, R.drawable.baseline_drag_handle_24, Emoji.instance().replaceEmoji(chatFolderInfo.title));
+ int iconRes;
+ if (Config.CHAT_FOLDERS_REDESIGN) {
+ iconRes = TD.findFolderIcon(chatFolderInfo.icon, R.drawable.baseline_folder_24);
+ } else {
+ iconRes = R.drawable.baseline_drag_handle_24;
+ }
+ ListItem item = new ListItem(ListItem.TYPE_CUSTOM - TYPE_CHAT_FOLDER, R.id.chatFolder, iconRes, Emoji.instance().replaceEmoji(chatFolderInfo.title));
item.setIntValue(chatFolderInfo.id);
item.setLongId(chatFolderInfo.id);
item.setData(chatFolderInfo);
@@ -726,10 +829,6 @@ private ListItem recommendedChatFolderItem (TdApi.RecommendedChatFolder recommen
return item;
}
- private boolean canCreateChatFolder () {
- return tdlib.chatFoldersCount() < tdlib.chatFolderCountMax();
- }
-
private void updateChatFolders () {
updateChatFolders(tdlib.chatFolders(), tdlib.mainChatListPosition(), tdlib.settings().archiveChatListPosition());
}
@@ -801,16 +900,23 @@ private static DiffUtil.Callback chatFoldersDiff (List oldList, List oldL
return new ListItemDiffUtilCallback(oldList, newList) {
@Override
public boolean areItemsTheSame (ListItem oldItem, ListItem newItem) {
- if (oldItem.getViewType() == newItem.getViewType() && oldItem.getId() == newItem.getId()) {
- if (oldItem.getId() == R.id.recommendedChatFolder) {
- CharSequence a = oldItem.getString();
- CharSequence b = newItem.getString();
- return ObjectUtils.equals(a, b);
- }
- return true;
+ if (oldItem.getViewType() != newItem.getViewType() || oldItem.getId() != newItem.getId()) {
+ return false;
}
- return false;
+ if (oldItem.getId() == R.id.recommendedChatFolder) {
+ CharSequence a = oldItem.getString();
+ CharSequence b = newItem.getString();
+ return ObjectUtils.equals(a, b);
+ }
+ if (oldItem.getViewType() == ListItem.TYPE_SEPARATOR) {
+ return ObjectUtils.equals(oldItem.getStringValue(), newItem.getStringValue());
+ }
+ return true;
}
@Override
@@ -841,9 +950,15 @@ public boolean areContentsTheSame (ListItem oldItem, ListItem newItem) {
String b = newItem.getStringValue();
return ObjectUtils.equals(a, b);
}
- CharSequence a = oldItem.getString();
- CharSequence b = newItem.getString();
- return ObjectUtils.equals(a, b);
+ if (oldItem.getViewType() == ListItem.TYPE_SEPARATOR) {
+ return true;
+ }
+ if (oldItem.getViewType() == ListItem.TYPE_HEADER) {
+ CharSequence a = oldItem.getString();
+ CharSequence b = newItem.getString();
+ return ObjectUtils.equals(a, b);
+ }
+ return true;
}
};
}
@@ -869,7 +984,7 @@ public boolean canRemove (RecyclerView recyclerView, RecyclerView.ViewHolder vie
@Override
public void onRemove (RecyclerView.ViewHolder viewHolder) {
ListItem item = (ListItem) viewHolder.itemView.getTag();
- showRemoveFolderConfirm(item.getIntValue());
+ showRemoveFolderConfirm((TdApi.ChatFolderInfo) item.getData());
}
@Override
@@ -895,7 +1010,7 @@ public boolean onMove (@NonNull RecyclerView recyclerView, @NonNull RecyclerView
public boolean canDropOver (@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder source, @NonNull RecyclerView.ViewHolder target) {
ListItem sourceItem = (ListItem) source.itemView.getTag();
ListItem targetItem = (ListItem) target.itemView.getTag();
- return isChatFolder(sourceItem) && isChatFolder(targetItem) && (canMoveChatFolder(targetItem) || BuildConfig.DEBUG && isArchiveChatFolder(sourceItem));
+ return isChatFolder(sourceItem) && isChatFolder(targetItem) && canMoveChatFolder(targetItem);
}
@Override
@@ -919,6 +1034,48 @@ public boolean onTouch (View view, MotionEvent event) {
}
}
+ private static class ItemDecoration extends RecyclerView.ItemDecoration {
+ @Override
+ public void onDrawOver (@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
+ if (!Config.CHAT_FOLDERS_REDESIGN) return;
+ boolean isRtl = Lang.rtl();
+ int separatorColor = Theme.separatorColor();
+ int separatorHeight = Math.max(1, Screen.dp(.5f));
+ Paint sepratorPaint = Paints.fillingPaint(separatorColor);
+ int startOffset = Screen.dp(72f);
+ int childCount = parent.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = parent.getChildAt(i);
+ if (!isChatFolder(child) || isDragging(child)) {
+ continue;
+ }
+ int layoutPosition = parent.getChildLayoutPosition(child);
+ if (layoutPosition == RecyclerView.NO_POSITION) {
+ continue;
+ }
+ RecyclerView.ViewHolder next = parent.findViewHolderForLayoutPosition(layoutPosition + 1);
+ if (next == null || isChatFolder(next.itemView) && isDragging(next.itemView)) {
+ continue;
+ }
+ int bottom = child.getBottom();
+ int top = bottom - separatorHeight;
+ if (isRtl) {
+ c.drawRect(0, top, child.getWidth() - startOffset, bottom, sepratorPaint);
+ } else {
+ c.drawRect(startOffset, top, child.getWidth(), bottom, sepratorPaint);
+ }
+ }
+ }
+
+ private boolean isChatFolder (View view) {
+ return view.getId() == R.id.chatFolder;
+ }
+
+ private boolean isDragging (View view) {
+ return view.getTranslationY() != 0f;
+ }
+ }
+
private static class FolderBadge implements DrawModifier {
private final Text text;
diff --git a/app/src/main/java/org/thunderdog/challegram/ui/ShareController.java b/app/src/main/java/org/thunderdog/challegram/ui/ShareController.java
index 2d2368d28a..f3a5e23a9f 100644
--- a/app/src/main/java/org/thunderdog/challegram/ui/ShareController.java
+++ b/app/src/main/java/org/thunderdog/challegram/ui/ShareController.java
@@ -946,7 +946,7 @@ protected View onCreateView (Context context) {
headerCell.initWithMargin(Screen.dp(56f) * getMenuItemCount(), false);
headerCell.setThemedTextColor(ColorId.text, ColorId.textLight, this);
updateHeader();
- if (Settings.instance().chatFoldersEnabled() && TD.isChatListMain(displayingChatList) && tdlib.chatFoldersCount() > 0) {
+ if (Settings.instance().chatFoldersEnabled() && TD.isChatListMain(displayingChatList) && tdlib.chatFolderCount() > 0) {
headerCell.setTitle(R.string.CategoryMain);
headerCell.setTitleIcon(R.drawable.baseline_keyboard_arrow_down_20);
headerCell.setOnClickListener(v -> {
diff --git a/app/src/main/java/org/thunderdog/challegram/util/EndIconModifier.java b/app/src/main/java/org/thunderdog/challegram/util/EndIconModifier.java
new file mode 100644
index 0000000000..4733a6e5be
--- /dev/null
+++ b/app/src/main/java/org/thunderdog/challegram/util/EndIconModifier.java
@@ -0,0 +1,78 @@
+/*
+ * This file is a part of Telegram X
+ * Copyright © 2014 (tgx-android@pm.me)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * File created on 19/01/2024
+ */
+package org.thunderdog.challegram.util;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import androidx.annotation.DrawableRes;
+
+import org.thunderdog.challegram.navigation.TooltipOverlayView;
+import org.thunderdog.challegram.theme.PorterDuffColorId;
+import org.thunderdog.challegram.tool.Drawables;
+import org.thunderdog.challegram.tool.PorterDuffPaint;
+import org.thunderdog.challegram.tool.Screen;
+
+public class EndIconModifier implements DrawModifier, TooltipOverlayView.LocationProvider {
+
+ private static final float HORIZONTAL_OFFSET_DP = 16f;
+
+ private final Drawable drawable;
+ private final @PorterDuffColorId int iconColorId;
+
+ private boolean isVisible = true;
+
+ public EndIconModifier (@DrawableRes int iconRes, @PorterDuffColorId int colorId) {
+ drawable = Drawables.get(iconRes);
+ iconColorId = colorId;
+ }
+
+ @Override
+ public void afterDraw (View view, Canvas c) {
+ if (!isVisible) return;
+ Drawables.draw(c, drawable, getX(view), getY(view), PorterDuffPaint.get(iconColorId));
+ }
+
+ @Override
+ public int getWidth () {
+ return isVisible ? Screen.dp(HORIZONTAL_OFFSET_DP) * 2 + drawable.getIntrinsicWidth() : 0;
+ }
+
+ public void setVisible (boolean isVisible) {
+ this.isVisible = isVisible;
+ }
+
+ @Override
+ public void getTargetBounds (View targetView, Rect outRect) {
+ if (isVisible) {
+ int x = getX(targetView);
+ int y = getY(targetView);
+ outRect.set(x, y, x + drawable.getIntrinsicWidth(), y + drawable.getIntrinsicHeight());
+ outRect.inset(0, -Screen.dp(10f));
+ } else {
+ targetView.getDrawingRect(outRect);
+ }
+ }
+
+ private int getX (View view) {
+ return view.getWidth() - drawable.getIntrinsicWidth() - Screen.dp(HORIZONTAL_OFFSET_DP);
+ }
+
+ private int getY (View view) {
+ return Math.round((view.getHeight() - drawable.getIntrinsicHeight()) / 2f);
+ }
+}
diff --git a/app/src/main/java/org/thunderdog/challegram/util/Poller.java b/app/src/main/java/org/thunderdog/challegram/util/Poller.java
new file mode 100644
index 0000000000..f7945339b9
--- /dev/null
+++ b/app/src/main/java/org/thunderdog/challegram/util/Poller.java
@@ -0,0 +1,102 @@
+/*
+ * This file is a part of Telegram X
+ * Copyright © 2014 (tgx-android@pm.me)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * File created on 19/01/2024
+ */
+package org.thunderdog.challegram.util;
+
+import android.os.Handler;
+
+import androidx.annotation.Nullable;
+
+import org.drinkless.tdlib.TdApi;
+import org.thunderdog.challegram.telegram.Tdlib;
+import org.thunderdog.challegram.tool.UI;
+
+import me.vkryl.core.lambda.Future;
+import me.vkryl.core.lambda.FutureLong;
+
+public class Poller {
+
+ private static final long MIN_POLLING_DELAY_MILLIS = 1000;
+
+ private final Handler handler;
+ private final Tdlib tdlib;
+ private final Tdlib.ResultHandler resultHandler;
+ private final Future> requestFactory;
+ private final FutureLong pollingDelayMillis;
+
+ private @Nullable Runnable runnable;
+
+ public Poller (Tdlib tdlib, FutureLong pollingDelayMillis, Future> requestFactory, Tdlib.ResultHandler resultHandler) {
+ this(tdlib, UI.getAppHandler(), pollingDelayMillis, requestFactory, resultHandler);
+ }
+
+ public Poller (Tdlib tdlib, Handler handler, FutureLong pollingDelayMillis, Future> requestFactory, Tdlib.ResultHandler resultHandler) {
+ this.tdlib = tdlib;
+ this.handler = handler;
+ this.pollingDelayMillis = pollingDelayMillis;
+ this.resultHandler = resultHandler;
+ this.requestFactory = requestFactory;
+ }
+
+ public void start () {
+ checkCallingThread();
+ if (runnable == null) {
+ runnable = createRunnable();
+ handler.post(runnable);
+ }
+ }
+
+ public void stop () {
+ checkCallingThread();
+ if (runnable != null) {
+ handler.removeCallbacks(runnable);
+ runnable = null;
+ }
+ }
+
+ public void restart () {
+ stop();
+ start();
+ }
+
+ public boolean isStarted () {
+ checkCallingThread();
+ return runnable != null;
+ }
+
+ private void checkCallingThread () {
+ if (handler.getLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException();
+ }
+ }
+
+ private Runnable createRunnable() {
+ return new Runnable() {
+ @Override
+ public void run () {
+ TdApi.Function request = requestFactory.getValue();
+ tdlib.send(request, (result, error) -> handler.post(() -> {
+ if (runnable != this) {
+ return;
+ }
+ resultHandler.onResult(result, error);
+ if (runnable == this) {
+ long delayMillis = Math.max(MIN_POLLING_DELAY_MILLIS, pollingDelayMillis.getLongValue());
+ handler.postDelayed(runnable, delayMillis);
+ }
+ }));
+ }
+ };
+ }
+}
diff --git a/app/src/main/java/org/thunderdog/challegram/util/PremiumLockModifier.java b/app/src/main/java/org/thunderdog/challegram/util/PremiumLockModifier.java
new file mode 100644
index 0000000000..1ac82646cd
--- /dev/null
+++ b/app/src/main/java/org/thunderdog/challegram/util/PremiumLockModifier.java
@@ -0,0 +1,24 @@
+/*
+ * This file is a part of Telegram X
+ * Copyright © 2014 (tgx-android@pm.me)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * File created on 19/01/2024
+ */
+package org.thunderdog.challegram.util;
+
+import org.thunderdog.challegram.R;
+import org.thunderdog.challegram.theme.ColorId;
+
+public class PremiumLockModifier extends EndIconModifier {
+ public PremiumLockModifier () {
+ super(R.drawable.baseline_lock_16, ColorId.text);
+ }
+}
diff --git a/app/src/main/java/org/thunderdog/challegram/util/text/FormattedCounterAnimator.java b/app/src/main/java/org/thunderdog/challegram/util/text/FormattedCounterAnimator.java
index 51aad8bed8..8a395c23e6 100644
--- a/app/src/main/java/org/thunderdog/challegram/util/text/FormattedCounterAnimator.java
+++ b/app/src/main/java/org/thunderdog/challegram/util/text/FormattedCounterAnimator.java
@@ -1,9 +1,5 @@
package org.thunderdog.challegram.util.text;
-import static me.vkryl.android.animator.CounterAnimator.Part.POSITION_BOTTOM;
-import static me.vkryl.android.animator.CounterAnimator.Part.POSITION_NORMAL;
-import static me.vkryl.android.animator.CounterAnimator.Part.POSITION_UP;
-
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
@@ -145,23 +141,23 @@ private void setCounterImpl (long count, @Nullable List>
protected void onPartAdded (@NonNull Part part, long oldCount, long newCount, boolean isReturned) {
if (callback.shouldAnimatePartVerticalPosition(part, oldCount, newCount)) {
if (oldCount < newCount) {
- part.setVerticalPositionFrom(POSITION_UP);
+ part.setVerticalPositionFrom(CounterAnimator.Part.POSITION_UP);
} else if (oldCount > newCount) {
- part.setVerticalPositionFrom(POSITION_BOTTOM);
+ part.setVerticalPositionFrom(CounterAnimator.Part.POSITION_BOTTOM);
}
}
- part.setVerticalPositionTo(POSITION_NORMAL);
+ part.setVerticalPositionTo(CounterAnimator.Part.POSITION_NORMAL);
}
protected void onPartRemoved (@NonNull Part part, long oldCount, long newCount) {
if (callback.shouldAnimatePartVerticalPosition(part, oldCount, newCount)) {
if (oldCount < newCount) {
- part.setVerticalPositionTo(POSITION_BOTTOM);
+ part.setVerticalPositionTo(CounterAnimator.Part.POSITION_BOTTOM);
} else if (oldCount > newCount) {
- part.setVerticalPositionTo(POSITION_UP);
+ part.setVerticalPositionTo(CounterAnimator.Part.POSITION_UP);
}
} else {
- part.setVerticalPositionTo(POSITION_NORMAL);
+ part.setVerticalPositionTo(CounterAnimator.Part.POSITION_NORMAL);
}
}
}
diff --git a/app/src/main/java/org/thunderdog/challegram/util/text/IconSpan.java b/app/src/main/java/org/thunderdog/challegram/util/text/IconSpan.java
new file mode 100644
index 0000000000..a0120aea5d
--- /dev/null
+++ b/app/src/main/java/org/thunderdog/challegram/util/text/IconSpan.java
@@ -0,0 +1,65 @@
+/*
+ * This file is a part of Telegram X
+ * Copyright © 2014 (tgx-android@pm.me)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * File created on 19/01/2024
+ */
+package org.thunderdog.challegram.util.text;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.Drawable;
+import android.text.style.DynamicDrawableSpan;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.thunderdog.challegram.theme.PorterDuffColorId;
+import org.thunderdog.challegram.tool.Drawables;
+import org.thunderdog.challegram.tool.PorterDuffPaint;
+
+public class IconSpan extends DynamicDrawableSpan implements TextReplacementSpan {
+
+ private final Drawable drawable;
+ private final @PorterDuffColorId int iconColorId;
+
+ public IconSpan (@DrawableRes int iconRes, @PorterDuffColorId int iconColorId) {
+ super(DynamicDrawableSpan.ALIGN_BOTTOM);
+ this.drawable = Drawables.get(iconRes);
+ this.drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ this.iconColorId = iconColorId;
+ }
+
+ @Override
+ public Drawable getDrawable () {
+ return drawable;
+ }
+
+ @Override
+ public void draw (@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {
+ drawable.setColorFilter(PorterDuffPaint.get(iconColorId).getColorFilter());
+ super.draw(canvas, text, start, end, x, top, y, bottom, paint);
+ }
+
+ @Override
+ public int getSize (@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) {
+ if (fm != null) {
+ paint.getFontMetricsInt(fm);
+ }
+ return getRawSize(paint);
+ }
+
+ @Override
+ public int getRawSize (Paint paint) {
+ return drawable.getIntrinsicWidth();
+ }
+}
diff --git a/app/src/main/java/org/thunderdog/challegram/util/text/TextReplacementSpan.java b/app/src/main/java/org/thunderdog/challegram/util/text/TextReplacementSpan.java
new file mode 100644
index 0000000000..997a3d421c
--- /dev/null
+++ b/app/src/main/java/org/thunderdog/challegram/util/text/TextReplacementSpan.java
@@ -0,0 +1,21 @@
+/*
+ * This file is a part of Telegram X
+ * Copyright © 2014 (tgx-android@pm.me)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * File created on 19/01/2024
+ */
+package org.thunderdog.challegram.util.text;
+
+import android.graphics.Paint;
+
+public interface TextReplacementSpan {
+ int getRawSize (Paint paint);
+}
diff --git a/app/src/main/java/org/thunderdog/challegram/v/ChatsRecyclerView.java b/app/src/main/java/org/thunderdog/challegram/v/ChatsRecyclerView.java
index b029f45f02..b28eb66ddb 100644
--- a/app/src/main/java/org/thunderdog/challegram/v/ChatsRecyclerView.java
+++ b/app/src/main/java/org/thunderdog/challegram/v/ChatsRecyclerView.java
@@ -227,13 +227,13 @@ public void updateUserStatus (long userId) {
if (updated == -1)
break;
View view = manager.findViewByPosition(updated);
- if (view instanceof ChatView && ((ChatView) view).getChatId() == adapter.getChatAt(updated).getChatId()) {
+ if (view instanceof ChatView && ((ChatView) view).getChatId() == adapter.getChatByItemPosition(updated).getChatId()) {
// ((ChatView) view).updateOnline();
view.invalidate();
} else {
adapter.notifyItemChanged(updated);
}
- startIndex = updated + 1;
+ startIndex = adapter.getChatIndexByItemPosition(updated) + 1;
}
}
@@ -280,7 +280,7 @@ public void updateArchive (int updateReason) {
}
public void updateChatSelectionState (long chatId, boolean isSelected) {
- int index = adapter.indexOfChat(chatId);
+ int index = adapter.findChatItemPosition(chatId);
if (index != -1) {
View view = getLayoutManager().findViewByPosition(index);
if (view instanceof ChatView && ((ChatView) view).getChatId() == chatId) {
@@ -293,7 +293,7 @@ public void updateChatSelectionState (long chatId, boolean isSelected) {
public void updateChatPosition (long chatId, TdApi.ChatPosition position, boolean orderChanged, boolean sourceChanged, boolean pinStateChanged) {
if (sourceChanged) {
- int i = adapter.indexOfChat(chatId);
+ int i = adapter.findChatItemPosition(chatId);
if (i != -1) {
invalidateViewAt(i);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/BetterChatView.java b/app/src/main/java/org/thunderdog/challegram/widget/BetterChatView.java
index 79648cb0c0..0d178ed94c 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/BetterChatView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/BetterChatView.java
@@ -147,7 +147,7 @@ public void performDestroy () {
public void setIsChecked (boolean isChecked, boolean animated) {
if (isChecked != (checkBoxHelper != null && checkBoxHelper.isChecked())) {
if (checkBoxHelper == null) {
- checkBoxHelper = new SimplestCheckBoxHelper(this, avatarReceiver);
+ checkBoxHelper = new SimplestCheckBoxHelper(this);
}
checkBoxHelper.setIsChecked(isChecked, animated);
}
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/ChartLayout.java b/app/src/main/java/org/thunderdog/challegram/widget/ChartLayout.java
index 46002d71da..668c09c948 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/ChartLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/ChartLayout.java
@@ -12,11 +12,9 @@
*/
package org.thunderdog.challegram.widget;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
import android.content.Context;
import android.graphics.Canvas;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
@@ -61,7 +59,7 @@ public ChartLayout (@NonNull Context context) {
this.progressReceiver.detach();
setWillNotDraw(false);
- setLayoutParams(new RecyclerView.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
private Tdlib tdlib;
@@ -247,7 +245,7 @@ public void initWithType (Tdlib tdlib, int type, Delegate delegate, @Nullable Vi
// zoomedChartView.sharedUiComponents = sharedUiComponents;
addView(chartView);
- addView(chartView.legendSignatureView, WRAP_CONTENT, WRAP_CONTENT);
+ addView(chartView.legendSignatureView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
/*addView(zoomedChartView);
addView(zoomedChartView.legendSignatureView, WRAP_CONTENT, WRAP_CONTENT);*/
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/PopupLayout.java b/app/src/main/java/org/thunderdog/challegram/widget/PopupLayout.java
index 07a8fca5c9..53c4aff027 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/PopupLayout.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/PopupLayout.java
@@ -26,6 +26,7 @@
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.WindowManager;
+import android.widget.FrameLayout;
import android.widget.PopupWindow;
import androidx.annotation.Nullable;
@@ -42,6 +43,7 @@
import org.thunderdog.challegram.navigation.MenuMoreWrap;
import org.thunderdog.challegram.navigation.OptionsLayout;
import org.thunderdog.challegram.navigation.RootDrawable;
+import org.thunderdog.challegram.navigation.TooltipOverlayView;
import org.thunderdog.challegram.navigation.ViewController;
import org.thunderdog.challegram.theme.Theme;
import org.thunderdog.challegram.tool.Keyboard;
@@ -911,6 +913,25 @@ public void addStatusBar () {
useStatusBar = true;
}
+ private @Nullable TooltipOverlayView tooltipOverlayView;
+
+ public TooltipOverlayView tooltipManager () {
+ if (tooltipOverlayView == null) {
+ tooltipOverlayView = new TooltipOverlayView(getContext());
+ tooltipOverlayView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
+ tooltipOverlayView.setAvailabilityListener((overlayView, hasChildren) -> {
+ if (hasChildren) {
+ if (tooltipOverlayView.getParent() != null)
+ return;
+ addView(tooltipOverlayView);
+ } else {
+ removeView(tooltipOverlayView);
+ }
+ });
+ }
+ return tooltipOverlayView;
+ }
+
public static PopupLayout parentOf (View view) {
ViewParent parent = view.getParent();
while (parent != null && !(parent instanceof PopupLayout)) {
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/ScalableTextView.java b/app/src/main/java/org/thunderdog/challegram/widget/ScalableTextView.java
index d3b1130fdf..e7d61c70c5 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/ScalableTextView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/ScalableTextView.java
@@ -16,6 +16,8 @@
import android.content.Context;
+import androidx.annotation.NonNull;
+
import org.thunderdog.challegram.tool.Views;
import me.vkryl.android.AnimatorUtils;
@@ -56,7 +58,7 @@ private void setFactor (float factor) {
if (this.factor != factor) {
this.factor = factor;
if (factor >= .5f && futureReplacement != null) {
- Views.setMediumText(this, futureReplacement);
+ onReplaceText(futureReplacement);
futureReplacement = null;
}
float alpha = (factor <= .5f ? 1f - factor / .5f : (factor - .5f) / .5f);
@@ -76,4 +78,8 @@ public void onFactorChanged (int id, float factor, float fraction, FactorAnimato
public void onFactorChangeFinished (int id, float finalFactor, FactorAnimator callee) {
}
+
+ protected void onReplaceText (@NonNull CharSequence replacement) {
+ Views.setMediumText(this, replacement);
+ }
}
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/SimplestCheckBoxHelper.java b/app/src/main/java/org/thunderdog/challegram/widget/SimplestCheckBoxHelper.java
index 8e5f8aee55..ce44d33466 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/SimplestCheckBoxHelper.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/SimplestCheckBoxHelper.java
@@ -16,14 +16,16 @@
import android.view.View;
-import org.thunderdog.challegram.loader.Receiver;
+import androidx.annotation.Nullable;
import me.vkryl.android.AnimatorUtils;
import me.vkryl.android.animator.FactorAnimator;
+import me.vkryl.android.util.SingleViewProvider;
+import me.vkryl.android.util.ViewProvider;
public class SimplestCheckBoxHelper implements FactorAnimator.Target {
- private final View target;
- private final Receiver receiver;
+ private final ViewProvider viewProvider;
+ private final @Nullable Listener listener;
private float factor;
private FactorAnimator animator;
@@ -33,9 +35,17 @@ public interface Listener {
void onCheckFactorChanged (float factor);
}
- public SimplestCheckBoxHelper (View target, Receiver receiver) {
- this.target = target;
- this.receiver = receiver;
+ public SimplestCheckBoxHelper (View target) {
+ this(new SingleViewProvider(target), target instanceof Listener ? (Listener) target : null);
+ }
+
+ public SimplestCheckBoxHelper (ViewProvider viewProvider) {
+ this(viewProvider, null);
+ }
+
+ public SimplestCheckBoxHelper (ViewProvider viewProvider, @Nullable Listener listener) {
+ this.viewProvider = viewProvider;
+ this.listener = listener;
}
public boolean isChecked () {
@@ -67,9 +77,9 @@ public void setIsChecked (boolean isChecked, boolean isAnimated) {
private void setCheckFactor (float factor) {
if (this.factor != factor) {
this.factor = factor;
- target.invalidate();
- if (target instanceof Listener) {
- ((Listener) target).onCheckFactorChanged(factor);
+ viewProvider.invalidate();
+ if (listener != null) {
+ listener.onCheckFactorChanged(factor);
}
}
}
@@ -78,9 +88,4 @@ private void setCheckFactor (float factor) {
public void onFactorChanged (int id, float factor, float fraction, FactorAnimator callee) {
setCheckFactor(factor);
}
-
- @Override
- public void onFactorChangeFinished (int id, float finalFactor, FactorAnimator callee) {
-
- }
}
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/SuggestedChatsView.java b/app/src/main/java/org/thunderdog/challegram/widget/SuggestedChatsView.java
new file mode 100644
index 0000000000..133a13c865
--- /dev/null
+++ b/app/src/main/java/org/thunderdog/challegram/widget/SuggestedChatsView.java
@@ -0,0 +1,149 @@
+/*
+ * This file is a part of Telegram X
+ * Copyright © 2014 (tgx-android@pm.me)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * File created on 19/01/2024
+ */
+package org.thunderdog.challegram.widget;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.view.Gravity;
+import android.view.View;
+
+import androidx.annotation.Dimension;
+import androidx.annotation.NonNull;
+
+import org.drinkless.tdlib.TdApi;
+import org.thunderdog.challegram.R;
+import org.thunderdog.challegram.core.Lang;
+import org.thunderdog.challegram.data.TD;
+import org.thunderdog.challegram.data.TGAvatars;
+import org.thunderdog.challegram.loader.ComplexReceiver;
+import org.thunderdog.challegram.support.RippleSupport;
+import org.thunderdog.challegram.telegram.Tdlib;
+import org.thunderdog.challegram.theme.Theme;
+import org.thunderdog.challegram.tool.Drawables;
+import org.thunderdog.challegram.tool.Paints;
+import org.thunderdog.challegram.tool.Screen;
+import org.thunderdog.challegram.tool.Views;
+import org.thunderdog.challegram.util.text.Text;
+import org.thunderdog.challegram.util.text.TextEntity;
+
+import java.util.Arrays;
+
+import me.vkryl.core.lambda.Destroyable;
+
+public final class SuggestedChatsView extends View implements TGAvatars.Callback, AttachDelegate, Destroyable {
+
+ @Dimension(unit = Dimension.DP)
+ public static final float DEFAULT_HEIGHT_DP = 36f;
+
+ private final Tdlib tdlib;
+ private final TGAvatars avatars;
+ private final ComplexReceiver complexReceiver;
+ private final Drawable icon;
+ private final Text text;
+
+ public SuggestedChatsView (Context context, Tdlib tdlib) {
+ super(context);
+ this.tdlib = tdlib;
+ this.complexReceiver = new ComplexReceiver(this);
+ this.avatars = new TGAvatars(tdlib, this, this);
+ this.icon = Drawables.get(context.getResources(), R.drawable.baseline_info_16);
+ this.text = new Text.Builder("", Integer.MAX_VALUE, Paints.robotoStyleProvider(15f), Theme::textAccent2Color)
+ .singleLine()
+ .build();
+
+ setMinimumHeight(Screen.dp(DEFAULT_HEIGHT_DP));
+ Views.setClickable(this);
+ RippleSupport.setTransparentSelector(this);
+ }
+
+ public void setChatIds (long[] chatIds, boolean animated) {
+ avatars.setChatIds(Arrays.copyOf(chatIds, Math.min(3, chatIds.length)), animated);
+
+ int chatCount = chatIds.length;
+ if (chatCount > 0) {
+ String in = Lang.plural(R.string.xNewChatsAvailableToJoin, chatCount);
+ TdApi.FormattedText formattedText = TD.toFormattedText(in, false);
+ if (TD.parseMarkdownWithEntities(formattedText) && formattedText.entities.length > 0) {
+ TextEntity[] entities = TextEntity.valueOf(tdlib, formattedText, null);
+ for (TextEntity entity : entities) {
+ if (entity.isBold()) {
+ entity.setCustomColorSet(Theme::textLinkColor);
+ }
+ }
+ text.set(getTextMaxWidth(), formattedText.text, entities);
+ } else {
+ text.set(getTextMaxWidth(), in, null);
+ }
+ }
+ }
+
+ @Override
+ protected void onDraw (@NonNull Canvas canvas) {
+ int cy = getHeight() / 2;
+ avatars.draw(canvas, complexReceiver, Screen.dp(7f), cy, Gravity.LEFT, 1f);
+
+ int textX = Math.round(avatars.getAnimatedWidth() + avatars.getAvatarsVisibility() * Screen.dp(7f)) + Screen.dp(7f);
+ int textY = cy - text.getLineCenterY();
+ text.draw(canvas, textX, textY);
+
+ Drawables.drawCentered(canvas, icon, getWidth() - Screen.dp(20f), cy, Paints.getBackgroundIconPorterDuffPaint());
+ }
+
+ @Override
+ public void onSizeChanged () {
+ if (isLaidOut()) {
+ text.changeMaxWidth(getTextMaxWidth());
+ }
+ invalidate();
+ }
+
+ @Override
+ protected void onSizeChanged (int width, int height, int oldWidth, int oldHeight) {
+ super.onSizeChanged(width, height, oldWidth, oldHeight);
+ if (width != oldWidth) {
+ text.changeMaxWidth(getTextMaxWidth());
+ invalidate();
+ }
+ }
+
+ @Override
+ public void onInvalidateMedia (TGAvatars avatars) {
+ avatars.requestFiles(complexReceiver, /* isUpdate */ true, /* neverClear */ false);
+ }
+
+ @Override
+ public void attach () {
+ complexReceiver.attach();
+ }
+
+ @Override
+ public void detach () {
+ complexReceiver.detach();
+ }
+
+ @Override
+ public void performDestroy () {
+ text.performDestroy();
+ complexReceiver.performDestroy();
+ }
+
+ private int getTextMaxWidth () {
+ if (!isLaidOut()) {
+ return Integer.MAX_VALUE;
+ }
+ return getWidth() - Math.round(avatars.getAnimatedWidth()) - Screen.dp(54);
+ }
+}
diff --git a/app/src/main/java/org/thunderdog/challegram/widget/VerticalChatView.java b/app/src/main/java/org/thunderdog/challegram/widget/VerticalChatView.java
index 2cd7916d4c..8d69d7626d 100644
--- a/app/src/main/java/org/thunderdog/challegram/widget/VerticalChatView.java
+++ b/app/src/main/java/org/thunderdog/challegram/widget/VerticalChatView.java
@@ -78,7 +78,7 @@ public VerticalChatView (@NonNull Context context, Tdlib tdlib) {
public void setIsChecked (boolean isChecked, boolean animated) {
if (isChecked != (checkBoxHelper != null && checkBoxHelper.isChecked())) {
if (checkBoxHelper == null) {
- checkBoxHelper = new SimplestCheckBoxHelper(this, avatarReceiver);
+ checkBoxHelper = new SimplestCheckBoxHelper(this);
}
checkBoxHelper.setIsChecked(isChecked, animated);
}
diff --git a/app/src/main/res/drawable/baseline_airplane_24.xml b/app/src/main/res/drawable/baseline_airplane_24.xml
new file mode 100644
index 0000000000..ec058e88e8
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_airplane_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/baseline_crown_24.xml b/app/src/main/res/drawable/baseline_crown_24.xml
new file mode 100644
index 0000000000..4c2c2784e1
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_crown_24.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/baseline_filter_variant.xml b/app/src/main/res/drawable/baseline_filter_variant.xml
new file mode 100644
index 0000000000..bf8e328aaa
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_filter_variant.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/baseline_folder_open_96.xml b/app/src/main/res/drawable/baseline_folder_open_96.xml
new file mode 100644
index 0000000000..1153298dca
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_folder_open_96.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/baseline_info_16.xml b/app/src/main/res/drawable/baseline_info_16.xml
new file mode 100755
index 0000000000..fbc6491352
--- /dev/null
+++ b/app/src/main/res/drawable/baseline_info_16.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
index f68f91940d..db098d89cc 100644
--- a/app/src/main/res/values/ids.xml
+++ b/app/src/main/res/values/ids.xml
@@ -245,6 +245,8 @@
+
+
@@ -336,6 +338,7 @@
+
@@ -789,6 +792,7 @@
+
@@ -1276,6 +1280,7 @@
+
@@ -1284,6 +1289,7 @@
+
@@ -1344,6 +1350,8 @@
+
+
@@ -1354,6 +1362,7 @@
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2ddac38833..1e51eaabcb 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -566,6 +566,7 @@
Anyone who has Telegram installed will be able to join your group by following this link.
Add administrator
Invite Link
+ Folder Link
Dismiss admin
Broadcast
Leave channel
@@ -1008,6 +1009,7 @@
Email address hasn\'t changed.
Invalid email format.
Hint: %1$s
+ Private Chat
Private Chats
Private Chats and Mentions
Message Preview
@@ -1300,6 +1302,8 @@
No Photo set
Public Photo will be visible for everyone who is restricted from seeing your regular profile photo.
Group
+ Public Group
+ Private Group
Rename contact
Themes
Themes and Chats
@@ -1908,6 +1912,8 @@
t.me/%1$s is your current public link.
Loading actions…
Join Chat
+ Join %1$s chat
+ Join %1$s chats
Request to Join Channel
Request to Join Group
%1$s will be able to return to the group.
@@ -2570,7 +2576,10 @@
Delete and Disable
Mark as read
Mark as unread
+ Mark Folder as Read
Create Link
+ Create a New Link
+ Create an Invite Link
URL
Save
Cancel
@@ -3231,6 +3240,8 @@
There are too many bots in this group. Please remove some of the bots you\'re not using first.
Sorry, this group has too many admins. Please remove one of the existing admins first.
Folder must contain at least one chat
+ Sorry, you have too many shareable folders.
+ Sorry, you have too many invite links.
Notification Style
Sound and pop-up
@@ -4693,6 +4704,8 @@
Subscribe to **Telegram Premium** to move the "%1$s" folder.
You have reached the limit of **%1$d** folders. You can double the limit to **%2$d** folders by subscribing to **Telegram Premium**.
Sorry, you can\'t add more than **%1$d** chats to a folder. You can increase this limit to **%2$d** by subscribing to **Telegram Premium**.
+ You can only have **%1$d** shareable folders. Upgrade to **Telegram Premium** to increase this limit to **%2$d**.
+ You reached the limit of links, either remove some or upgrade to Telegram Premium.
%1$s doesn\'t accept voice messages.
%1$s doesn\'t accept video messages.
@@ -4907,6 +4920,7 @@
Chat types
Chat Folders
Create folders for different groups of chats and quickly switch between them.
+ Tip: Hold and drag to reorder
Settings
Appearance
Top
@@ -4915,6 +4929,9 @@
Add Chats
You have reached the limit of **%1$d** folders.
Sorry, you can\'t add more than **%1$d** chats to a folder. Please create a new one.
+ Sorry, you can\'t have more than **%1$d** links to a folder.
+ Sorry, you can\'t have more than **%1$d** shareable folders.
+ Folder visible
New Folder
Create New Folder
Hide Folder
@@ -4922,8 +4939,10 @@
Hide All Chats
Edit Folder
Edit Folders
+ Share Folder
Remove Folder
Are you sure you want to remove this folder? Your chats will not be deleted.
+ Are you sure you want to delete this folder? This will also **deactivate all the invite links** created to share this folder.
**Folder is empty**\n\nNo chats currently belong to this folder.
Are you sure you want to remove "%1$s" from the always included list?
Are you sure you want to remove "%1$s" from the always included list?
@@ -4949,11 +4968,13 @@
Add to folder
Remove from folder
Choose a folder
+ Local Folder
local folder
main list
Icon only
Label only
Label with icon
+ [WIP] Icon with label on active folder
Display folders at the top
Show %1$s more
Show %1$s more
@@ -4997,7 +5018,7 @@
This message is a reply to the message that was deleted.
This message is from another chat. Tap again to view.
-
+
Swipe to choose specific link preview
This is a service message from Telegram
@@ -5130,4 +5151,42 @@
**Note**: you will need **GitHub account** to report this issue.\n\nAlternatively you can copy the link and send it to tgx-android@pm.me
Report on GitHub
+ Add Folder and Join %1$s chat
+ Add Folder and Join %1$s chats
+ Do not add folder
+ Do not join any chats
+ Do not join any new chats
+ Delete Folder
+ Delete Folder and Leave %1$s chat
+ Delete Folder and Leave %1$s chats
+
+ You can deselect the chats you don’t want to join.
+ You can deselect the chats you don’t want to leave.
+ Anyone with this link can add %1$s folder and the chats selected below.
+ Anyone with this link can add %2$s folder and the %1$s chat selected below.
+ Anyone with this link can add %2$s folder and the %1$s chats selected below.
+ Select groups and channels that you want everyone who adds this folder to join.
+ You can’t share folders which include or exclude specific chat types like **Groups**, **Contacts**, etc.
+ There are no chats in this folder that you can share with others.
+ These chats cannot be shared
+ You can only share groups and channels in which you are allowed to create invite links.
+ Name
+ Share access to some of this folder’s groups and channels with others.
+ Changes must be saved before creating a new link
+
+ %1$s chat to join
+ %1$s chats to join
+ %1$s chat to leave
+ %1$s chats to leave
+ %1$s chat already joined
+ %1$s chats already joined
+ %1$s new chat to join
+ %1$s new chats to join
+ **%1$s new chat** available to join
+ **%1$s new chats** available to join
+ %1$d/%2$d chats selected
+ This chat can’t be shared
+ Archive as Folder
+ Hold and drag to reorder
+
From 0341ea43acd0b739589de615a7becf995402e28c Mon Sep 17 00:00:00 2001
From: vkryl <6242627+vkryl@users.noreply.github.com>
Date: Tue, 6 Feb 2024 15:57:21 +0400
Subject: [PATCH 60/61] Upgraded TDLib to tdlib/td@d79bd4b
---
tdlib | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tdlib b/tdlib
index 800b09a4bd..83869d6dfd 160000
--- a/tdlib
+++ b/tdlib
@@ -1 +1 @@
-Subproject commit 800b09a4bd2ed28ac03bb8de2f09d4131d71e63b
+Subproject commit 83869d6dfd6209ff0cf4d55072edaa92105988e2
From fac8968eae46b4e64929d7b3a21d034314a7c70f Mon Sep 17 00:00:00 2001
From: tgx-server
Date: Tue, 6 Feb 2024 11:58:19 +0000
Subject: [PATCH 61/61] Version bump to `1685`
---
version.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/version.properties b/version.properties
index 7154b7a5e6..ce2adfd53b 100644
--- a/version.properties
+++ b/version.properties
@@ -1,5 +1,5 @@
# App
-version.app=1684
+version.app=1685
version.major=0
# Anchor date point in app versioning
version.creation=873642600564