package org.telegram.ui; import android.content.Context; import android.content.Intent; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.util.SparseIntArray; import android.util.TypedValue; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.core.graphics.ColorUtils; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ChatObject; import org.telegram.messenger.ContactsController; import org.telegram.messenger.DocumentObject; import org.telegram.messenger.Emoji; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MessagesController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.SvgHelper; import org.telegram.messenger.UserConfig; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.ManageChatTextCell; import org.telegram.ui.Cells.ManageChatUserCell; import org.telegram.ui.Cells.ShadowSectionCell; import org.telegram.ui.Cells.TextInfoPrivacyCell; import org.telegram.ui.Cells.TextSettingsCell; import org.telegram.ui.Components.BackupImageView; import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.DotDividerSpan; import org.telegram.ui.Components.FlickerLoadingView; import org.telegram.ui.Components.InviteLinkBottomSheet; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LinkActionView; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.TimerParticles; import org.telegram.ui.Components.RecyclerItemsEnterAnimator; import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; public class ManageLinksActivity extends BaseFragment { private ListAdapter listViewAdapter; private RecyclerListView listView; private TLRPC.Chat currentChat; private TLRPC.ChatFull info; private TLRPC.TL_chatInviteExported invite; private long adminId; private boolean isChannel; private long currentChatId; private int helpRow; private int permanentLinkHeaderRow; private int permanentLinkRow; private int dividerRow; private int createNewLinkRow; private int linksStartRow; private int linksEndRow; private int linksLoadingRow; private int revokedLinksStartRow; private int revokedLinksEndRow; private int revokedDivider; private int lastDivider; private int revokedHeader; private int revokeAllDivider; private int revokeAllRow; private int createLinkHelpRow; private int linksHeaderRow; private int creatorRow; private int creatorDividerRow; private int adminsHeaderRow; private int adminsDividerRow; private int adminsStartRow; private int adminsEndRow; boolean linksLoading; private int rowCount; Drawable linkIcon; Drawable linkIconRevoked; boolean hasMore; boolean deletingRevokedLinks; boolean loadAdmins; boolean adminsLoaded; private int invitesCount; private boolean isOpened; private boolean transitionFinished; private RecyclerItemsEnterAnimator recyclerItemsEnterAnimator; private ArrayList invites = new ArrayList<>(); private ArrayList revokedInvites = new ArrayList<>(); private HashMap users = new HashMap<>(); private InviteLinkBottomSheet inviteLinkBottomSheet; private ArrayList admins = new ArrayList<>(); long timeDif; private boolean isPublic; private boolean canEdit; Runnable updateTimerRunnable = new Runnable() { @Override public void run() { if (listView == null) { return; } for (int i = 0; i < listView.getChildCount(); i++) { View child = listView.getChildAt(i); if (child instanceof LinkCell) { LinkCell linkCell = (LinkCell) child; if (linkCell.timerRunning) { linkCell.setLink(linkCell.invite, linkCell.position); } } } AndroidUtilities.runOnUIThread(this, 500); } }; private static class EmptyView extends LinearLayout implements NotificationCenter.NotificationCenterDelegate { private BackupImageView stickerView; private final int currentAccount = UserConfig.selectedAccount; private static final String stickerSetName = AndroidUtilities.STICKERS_PLACEHOLDER_PACK_NAME; public EmptyView(Context context) { super(context); setPadding(0, AndroidUtilities.dp(12), 0, AndroidUtilities.dp(12)); setOrientation(LinearLayout.VERTICAL); stickerView = new BackupImageView(context); addView(stickerView, LayoutHelper.createLinear(104, 104, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 0, 2, 0, 0)); } private void setSticker() { TLRPC.TL_messages_stickerSet set = MediaDataController.getInstance(currentAccount).getStickerSetByName(stickerSetName); if (set == null) { set = MediaDataController.getInstance(currentAccount).getStickerSetByEmojiOrName(stickerSetName); } if (set != null && set.documents.size() >= 4) { TLRPC.Document document = set.documents.get(3); ImageLocation imageLocation = ImageLocation.getForDocument(document); SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(document, Theme.key_windowBackgroundGray, 1.0f); stickerView.setImage(imageLocation, "104_104", "tgs", svgThumb, set); } else { MediaDataController.getInstance(currentAccount).loadStickersByEmojiOrName(stickerSetName, false, set == null); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); setSticker(); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.diceStickersDidLoad); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.diceStickersDidLoad); } @Override public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.diceStickersDidLoad) { String name = (String) args[0]; if (stickerSetName.equals(name)) { setSticker(); } } } } public ManageLinksActivity(long chatId, long adminId, int invitesCount) { super(); currentChatId = chatId; this.invitesCount = invitesCount; currentChat = MessagesController.getInstance(currentAccount).getChat(chatId); isChannel = ChatObject.isChannel(currentChat) && !currentChat.megagroup; if (adminId == 0) { this.adminId = getAccountInstance().getUserConfig().clientUserId; } else { this.adminId = adminId; } TLRPC.User user = getMessagesController().getUser(this.adminId); canEdit = (this.adminId == getAccountInstance().getUserConfig().clientUserId) || (user != null && !user.bot); } boolean loadRevoked = false; private void loadLinks(boolean notify) { if (loadAdmins && !adminsLoaded) { linksLoading = true; TLRPC.TL_messages_getAdminsWithInvites req = new TLRPC.TL_messages_getAdminsWithInvites(); req.peer = getMessagesController().getInputPeer(-currentChatId); int reqId = getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> getNotificationCenter().doOnIdle(() -> { linksLoading = false; if (error == null) { TLRPC.TL_messages_chatAdminsWithInvites adminsWithInvites = (TLRPC.TL_messages_chatAdminsWithInvites) response; for (int i = 0; i < adminsWithInvites.admins.size(); i++) { TLRPC.TL_chatAdminWithInvites admin = adminsWithInvites.admins.get(i); if (admin.admin_id != getAccountInstance().getUserConfig().clientUserId) { admins.add(admin); } } for (int i = 0; i < adminsWithInvites.users.size(); i++) { TLRPC.User user = adminsWithInvites.users.get(i); users.put(user.id, user); } } int oldRowsCount = rowCount; adminsLoaded = true; hasMore = false; if (admins.size() > 0) { if (recyclerItemsEnterAnimator != null && !isPaused && isOpened) { recyclerItemsEnterAnimator.showItemsAnimated(oldRowsCount + 1); } } if (!hasMore || (invites.size() + revokedInvites.size() + admins.size()) >= 5) { resumeDelayedFragmentAnimation(); } if (!hasMore && !loadRevoked) { hasMore = true; loadRevoked = true; loadLinks(false); } updateRows(true); }))); getConnectionsManager().bindRequestToGuid(reqId, getClassGuid()); } else { TLRPC.TL_messages_getExportedChatInvites req = new TLRPC.TL_messages_getExportedChatInvites(); req.peer = getMessagesController().getInputPeer(-currentChatId); if (adminId == getUserConfig().getClientUserId()) { req.admin_id = getMessagesController().getInputUser(getUserConfig().getCurrentUser()); } else { req.admin_id = getMessagesController().getInputUser(adminId); } boolean revoked = loadRevoked; if (loadRevoked) { req.revoked = true; if (!revokedInvites.isEmpty()) { req.flags |= 4; req.offset_link = revokedInvites.get(revokedInvites.size() - 1).link; req.offset_date = revokedInvites.get(revokedInvites.size() - 1).date; } } else { if (!invites.isEmpty()) { req.flags |= 4; req.offset_link = invites.get(invites.size() - 1).link; req.offset_date = invites.get(invites.size() - 1).date; } } linksLoading = true; TLRPC.TL_chatInviteExported inviteFinal = isPublic ? null : invite; int reqId = getConnectionsManager().sendRequest(req, (response, error) -> { TLRPC.TL_chatInviteExported permanentLink = null; if (error == null) { TLRPC.TL_messages_exportedChatInvites invites = (TLRPC.TL_messages_exportedChatInvites) response; if (invites.invites.size() > 0 && inviteFinal != null) { for (int i = 0; i < invites.invites.size(); i++) { if (((TLRPC.TL_chatInviteExported) invites.invites.get(i)).link.equals(inviteFinal.link)) { permanentLink = (TLRPC.TL_chatInviteExported) invites.invites.remove(i); break; } } } } TLRPC.TL_chatInviteExported finalPermanentLink = permanentLink; AndroidUtilities.runOnUIThread(() -> getNotificationCenter().doOnIdle(() -> { DiffCallback callback = saveListState(); linksLoading = false; hasMore = false; if (finalPermanentLink != null) { invite = finalPermanentLink; if (info != null) { info.exported_invite = finalPermanentLink; } } boolean updateByDiffUtils = false; if (error == null) { TLRPC.TL_messages_exportedChatInvites invites = (TLRPC.TL_messages_exportedChatInvites) response; if (revoked) { for (int i = 0; i < invites.invites.size(); i++) { TLRPC.TL_chatInviteExported in = (TLRPC.TL_chatInviteExported) invites.invites.get(i); fixDate(in); this.revokedInvites.add(in); } } else { if (adminId != getAccountInstance().getUserConfig().clientUserId && this.invites.size() == 0 && invites.invites.size() > 0) { invite = (TLRPC.TL_chatInviteExported) invites.invites.get(0); invites.invites.remove(0); } for (int i = 0; i < invites.invites.size(); i++) { TLRPC.TL_chatInviteExported in = (TLRPC.TL_chatInviteExported) invites.invites.get(i); fixDate(in); this.invites.add(in); } } for (int i = 0; i < invites.users.size(); i++) { users.put(invites.users.get(i).id, invites.users.get(i)); } int oldRowsCount = rowCount; if (invites.invites.size() == 0) { hasMore = false; } else if (revoked) { hasMore = this.revokedInvites.size() + 1 < invites.count; } else { hasMore = this.invites.size() + 1 < invites.count; } if (invites.invites.size() > 0 && isOpened) { if (recyclerItemsEnterAnimator != null && !isPaused) { recyclerItemsEnterAnimator.showItemsAnimated(oldRowsCount + 1); } } else { updateByDiffUtils = true; } if (info != null && !revoked) { info.invitesCount = invites.count; getMessagesStorage().saveChatLinksCount(currentChatId, info.invitesCount); } } else { hasMore = false; } boolean loadNext = false; if (!hasMore && !loadRevoked && adminId == getAccountInstance().getUserConfig().clientUserId) { hasMore = true; loadAdmins = true; loadNext = true; } else if (!hasMore && !loadRevoked) { hasMore = true; loadRevoked = true; loadNext = true; } if (!hasMore || (invites.size() + revokedInvites.size() + admins.size()) >= 5) { resumeDelayedFragmentAnimation(); } if (loadNext) { loadLinks(false); } if (updateByDiffUtils && listViewAdapter != null && listView.getChildCount() > 0) { updateRecyclerViewAnimated(callback); } else { updateRows(true); } })); }); getConnectionsManager().bindRequestToGuid(reqId, getClassGuid()); } if (notify) { updateRows(true); } } private void updateRows(boolean notify) { currentChat = MessagesController.getInstance(currentAccount).getChat(currentChatId); if (currentChat == null) { return; } creatorRow = -1; creatorDividerRow = -1; linksStartRow = -1; linksEndRow = -1; linksLoadingRow = -1; revokedLinksStartRow = -1; revokedLinksEndRow = -1; revokedHeader = -1; revokedDivider = -1; lastDivider = -1; revokeAllRow = -1; revokeAllDivider = -1; createLinkHelpRow = -1; helpRow = -1; createNewLinkRow = -1; adminsEndRow = -1; adminsStartRow = -1; adminsDividerRow = -1; adminsHeaderRow = -1; linksHeaderRow = -1; dividerRow = -1; rowCount = 0; boolean otherAdmin = adminId != getAccountInstance().getUserConfig().clientUserId; if (otherAdmin) { creatorRow = rowCount++; creatorDividerRow = rowCount++; } else { helpRow = rowCount++; } permanentLinkHeaderRow = rowCount++; permanentLinkRow = rowCount++; if (!otherAdmin) { dividerRow = rowCount++; createNewLinkRow = rowCount++; } else if (!invites.isEmpty()) { dividerRow = rowCount++; linksHeaderRow = rowCount++; } if (!invites.isEmpty()) { linksStartRow = rowCount; rowCount += invites.size(); linksEndRow = rowCount; } if (!otherAdmin && invites.isEmpty() && createNewLinkRow >= 0 && (!linksLoading || loadAdmins || loadRevoked)) { createLinkHelpRow = rowCount++; } if (!otherAdmin && admins.size() > 0) { if ((!invites.isEmpty() || createNewLinkRow >= 0) && createLinkHelpRow == -1) { adminsDividerRow = rowCount++; } adminsHeaderRow = rowCount++; adminsStartRow = rowCount; rowCount += admins.size(); adminsEndRow = rowCount; } if (!revokedInvites.isEmpty()) { if (adminsStartRow >= 0) { revokedDivider = rowCount++; } else if ((!invites.isEmpty() || createNewLinkRow >= 0) && createLinkHelpRow == -1) { revokedDivider = rowCount++; } else if (otherAdmin && linksStartRow == -1) { revokedDivider = rowCount++; } revokedHeader = rowCount++; revokedLinksStartRow = rowCount; rowCount += revokedInvites.size(); revokedLinksEndRow = rowCount; revokeAllDivider = rowCount++; revokeAllRow = rowCount++; } if (!loadAdmins && !loadRevoked && (linksLoading || hasMore) && !otherAdmin) { linksLoadingRow = rowCount++; } if (!invites.isEmpty() || !revokedInvites.isEmpty()) { lastDivider = rowCount++; } if (listViewAdapter != null && notify) { listViewAdapter.notifyDataSetChanged(); } } @Override public View createView(Context context) { actionBar.setBackButtonImage(R.drawable.ic_ab_back); actionBar.setAllowOverlayTitle(true); actionBar.setTitle(LocaleController.getString("InviteLinks", R.string.InviteLinks)); actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() { @Override public void onItemClick(int id) { if (id == -1) { finishFragment(); } } }); fragmentView = new FrameLayout(context) { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); AndroidUtilities.runOnUIThread(updateTimerRunnable, 500); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); AndroidUtilities.cancelRunOnUIThread(updateTimerRunnable); } }; fragmentView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundGray)); fragmentView.setTag(Theme.key_windowBackgroundGray); FrameLayout frameLayout = (FrameLayout) fragmentView; listView = new RecyclerListView(context); LinearLayoutManager layoutManager = new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) { @Override public boolean supportsPredictiveItemAnimations() { return false; } }; listView.setLayoutManager(layoutManager); listView.setAdapter(listViewAdapter = new ListAdapter(context)); listView.setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if (hasMore && !linksLoading) { int lastPosition = layoutManager.findLastVisibleItemPosition(); if (rowCount - lastPosition < 10) { loadLinks(true); } } } }); recyclerItemsEnterAnimator = new RecyclerItemsEnterAnimator(listView, false); DefaultItemAnimator defaultItemAnimator = new DefaultItemAnimator(); defaultItemAnimator.setDelayAnimations(false); defaultItemAnimator.setSupportsChangeAnimations(false); listView.setItemAnimator(defaultItemAnimator); listView.setVerticalScrollbarPosition(LocaleController.isRTL ? RecyclerListView.SCROLLBAR_POSITION_LEFT : RecyclerListView.SCROLLBAR_POSITION_RIGHT); frameLayout.addView(listView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); listView.setOnItemClickListener((view, position) -> { if (position == creatorRow) { TLRPC.User user = users.get(invite.admin_id); if (user != null) { Bundle bundle = new Bundle(); bundle.putLong("user_id", user.id); MessagesController.getInstance(UserConfig.selectedAccount).putUser(user, false); ProfileActivity profileActivity = new ProfileActivity(bundle); presentFragment(profileActivity); } } else if (position == createNewLinkRow) { LinkEditActivity linkEditActivity = new LinkEditActivity(LinkEditActivity.CREATE_TYPE, currentChatId); linkEditActivity.setCallback(linkEditActivityCallback); presentFragment(linkEditActivity); } else if (position >= linksStartRow && position < linksEndRow) { TLRPC.TL_chatInviteExported invite = invites.get(position - linksStartRow); inviteLinkBottomSheet = new InviteLinkBottomSheet(context, invite, info, users, this, currentChatId, false, isChannel); inviteLinkBottomSheet.setCanEdit(canEdit); inviteLinkBottomSheet.show(); } else if (position >= revokedLinksStartRow && position < revokedLinksEndRow) { TLRPC.TL_chatInviteExported invite = revokedInvites.get(position - revokedLinksStartRow); inviteLinkBottomSheet = new InviteLinkBottomSheet(context, invite, info, users, this, currentChatId, false, isChannel); inviteLinkBottomSheet.show(); } else if (position == revokeAllRow) { if (deletingRevokedLinks) { return; } AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setTitle(LocaleController.getString("DeleteAllRevokedLinks", R.string.DeleteAllRevokedLinks)); builder.setMessage(LocaleController.getString("DeleteAllRevokedLinkHelp", R.string.DeleteAllRevokedLinkHelp)); builder.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), (dialogInterface2, i2) -> { TLRPC.TL_messages_deleteRevokedExportedChatInvites req = new TLRPC.TL_messages_deleteRevokedExportedChatInvites(); req.peer = getMessagesController().getInputPeer(-currentChatId); if (adminId == getUserConfig().getClientUserId()) { req.admin_id = getMessagesController().getInputUser(getUserConfig().getCurrentUser()); } else { req.admin_id = getMessagesController().getInputUser(adminId); } deletingRevokedLinks = true; getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { deletingRevokedLinks = false; if (error == null) { DiffCallback callback = saveListState(); revokedInvites.clear(); updateRecyclerViewAnimated(callback); } })); }); builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showDialog(builder.create()); } else if (position >= adminsStartRow && position < adminsEndRow) { int p = position - adminsStartRow; TLRPC.TL_chatAdminWithInvites admin = admins.get(p); if (users.containsKey(admin.admin_id)) { getMessagesController().putUser(users.get(admin.admin_id), false); } ManageLinksActivity fragment = new ManageLinksActivity(currentChatId, admin.admin_id, admin.invites_count); fragment.setInfo(info, null); presentFragment(fragment); } }); listView.setOnItemLongClickListener((view, position) -> { if ((position >= linksStartRow && position < linksEndRow) || (position >= revokedLinksStartRow && position < revokedLinksEndRow)) { LinkCell cell = (LinkCell) view; cell.optionsView.callOnClick(); view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); return true; } return false; }); linkIcon = ContextCompat.getDrawable(context, R.drawable.msg_link_1); linkIconRevoked = ContextCompat.getDrawable(context, R.drawable.msg_link_2); linkIcon.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.MULTIPLY)); updateRows(true); timeDif = getConnectionsManager().getCurrentTime() - (System.currentTimeMillis() / 1000L); return fragmentView; } public void setInfo(TLRPC.ChatFull chatFull, TLRPC.ExportedChatInvite invite) { info = chatFull; this.invite = (TLRPC.TL_chatInviteExported) invite; isPublic = !TextUtils.isEmpty(currentChat.username); loadLinks(true); } @Override public void onResume() { super.onResume(); if (listViewAdapter != null) { listViewAdapter.notifyDataSetChanged(); } } @SuppressWarnings("FieldCanBeLocal") public class HintInnerCell extends FrameLayout { private EmptyView emptyView; private TextView messageTextView; public HintInnerCell(Context context) { super(context); emptyView = new EmptyView(context); addView(emptyView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 0, 10, 0, 0)); messageTextView = new TextView(context); messageTextView.setTextColor(Theme.getColor(Theme.key_chats_message)); messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); messageTextView.setGravity(Gravity.CENTER); messageTextView.setText(isChannel ? LocaleController.getString("PrimaryLinkHelpChannel", R.string.PrimaryLinkHelpChannel) : LocaleController.getString("PrimaryLinkHelp", R.string.PrimaryLinkHelp)); addView(messageTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.LEFT, 52, 143, 52, 18)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), heightMeasureSpec); } } private class ListAdapter extends RecyclerListView.SelectionAdapter { private Context mContext; public ListAdapter(Context context) { mContext = context; } @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int position = holder.getAdapterPosition(); if (creatorRow == position) { return true; } else if (createNewLinkRow == position) { return true; } else if (position >= linksStartRow && position < linksEndRow) { return true; } else if (position >= revokedLinksStartRow && position < revokedLinksEndRow) { return true; } else if (position == revokeAllRow) { return true; } else if (position >= adminsStartRow && position < adminsEndRow) { return true; } return false; } @Override public int getItemCount() { return rowCount; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { case 0: default: view = new HintInnerCell(mContext); view.setBackgroundDrawable(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundWhite)); break; case 1: view = new HeaderCell(mContext, 23); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case 2: LinkActionView linkActionView = new LinkActionView(mContext, ManageLinksActivity.this, null, currentChatId, true, isChannel); linkActionView.setPermanent(true); linkActionView.setDelegate(new LinkActionView.Delegate() { @Override public void revokeLink() { revokePermanent(); } @Override public void showUsersForPermanentLink() { inviteLinkBottomSheet = new InviteLinkBottomSheet(linkActionView.getContext(), invite, info, users, ManageLinksActivity.this, currentChatId, true, isChannel); inviteLinkBottomSheet.show(); } }); view = linkActionView; view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case 3: view = new TextCell(mContext); view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case 4: view = new ShadowSectionCell(mContext); break; case 5: view = new LinkCell(mContext); break; case 6: FlickerLoadingView flickerLoadingView = new FlickerLoadingView(mContext); flickerLoadingView.setIsSingleCell(true); flickerLoadingView.setViewType(FlickerLoadingView.INVITE_LINKS_TYPE); flickerLoadingView.showDate(false); view = flickerLoadingView; view.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); break; case 7: view = new ShadowSectionCell(mContext); view.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); break; case 8: TextSettingsCell revokeAll = new TextSettingsCell(mContext); revokeAll.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); revokeAll.setText(LocaleController.getString("DeleteAllRevokedLinks", R.string.DeleteAllRevokedLinks), false); revokeAll.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteRedText5)); view = revokeAll; break; case 9: TextInfoPrivacyCell cell = new TextInfoPrivacyCell(mContext); cell.setText(LocaleController.getString("CreateNewLinkHelp", R.string.CreateNewLinkHelp)); cell.setBackground(Theme.getThemedDrawable(mContext, R.drawable.greydivider_bottom, Theme.key_windowBackgroundGrayShadow)); view = cell; break; case 10: ManageChatUserCell userCell = new ManageChatUserCell(mContext, 8, 6, false); userCell.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); view = userCell; break; } view.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); return new RecyclerListView.Holder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { switch (holder.getItemViewType()) { case 2: LinkActionView linkActionView = (LinkActionView) holder.itemView; linkActionView.setCanEdit(adminId == getAccountInstance().getUserConfig().clientUserId); if (isPublic && adminId == getAccountInstance().getUserConfig().clientUserId) { if (info != null) { linkActionView.setLink("https://t.me/" + currentChat.username); linkActionView.setUsers(0, null); linkActionView.hideRevokeOption(true); } } else { linkActionView.hideRevokeOption(!canEdit); if (invite != null) { TLRPC.TL_chatInviteExported inviteExported = invite; linkActionView.setLink(inviteExported.link); linkActionView.loadUsers(inviteExported, currentChatId); } else { linkActionView.setLink(null); linkActionView.loadUsers(null, currentChatId); } } break; case 1: HeaderCell headerCell = (HeaderCell) holder.itemView; if (position == permanentLinkHeaderRow) { if (isPublic && adminId == getAccountInstance().getUserConfig().clientUserId) { headerCell.setText(LocaleController.getString("PublicLink", R.string.PublicLink)); } else if (adminId == getAccountInstance().getUserConfig().clientUserId) { headerCell.setText(LocaleController.getString("ChannelInviteLinkTitle", R.string.ChannelInviteLinkTitle)); } else { headerCell.setText(LocaleController.getString("PermanentLinkForThisAdmin", R.string.PermanentLinkForThisAdmin)); } } else if (position == revokedHeader) { headerCell.setText(LocaleController.getString("RevokedLinks", R.string.RevokedLinks)); } else if (position == linksHeaderRow) { headerCell.setText(LocaleController.getString("LinksCreatedByThisAdmin", R.string.LinksCreatedByThisAdmin)); } else if (position == adminsHeaderRow) { headerCell.setText(LocaleController.getString("LinksCreatedByOtherAdmins", R.string.LinksCreatedByOtherAdmins)); } break; case 3: TextCell textCell = (TextCell) holder.itemView; Drawable drawable1 = mContext.getResources().getDrawable(R.drawable.poll_add_circle); Drawable drawable2 = mContext.getResources().getDrawable(R.drawable.poll_add_plus); drawable1.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_switchTrackChecked), PorterDuff.Mode.MULTIPLY)); drawable2.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_checkboxCheck), PorterDuff.Mode.MULTIPLY)); CombinedDrawable combinedDrawable = new CombinedDrawable(drawable1, drawable2); textCell.setTextAndIcon(LocaleController.getString("CreateNewLink", R.string.CreateNewLink), combinedDrawable, !invites.isEmpty()); break; case 5: TLRPC.TL_chatInviteExported invite; boolean drawDivider = true; if (position >= linksStartRow && position < linksEndRow) { invite = invites.get(position - linksStartRow); if (position == linksEndRow - 1) { drawDivider = false; } } else { invite = revokedInvites.get(position - revokedLinksStartRow); if (position == revokedLinksEndRow - 1) { drawDivider = false; } } LinkCell cell = (LinkCell) holder.itemView; cell.setLink(invite, position - linksStartRow); cell.drawDivider = drawDivider; break; case 10: ManageChatUserCell userCell = (ManageChatUserCell) holder.itemView; TLRPC.User user; int count; drawDivider = true; if (position == creatorRow) { user = getMessagesController().getUser(adminId); count = invitesCount; drawDivider = false; } else { int p = position - adminsStartRow; TLRPC.TL_chatAdminWithInvites admin = admins.get(p); user = users.get(admin.admin_id); count = admin.invites_count; if (position == adminsEndRow - 1) { drawDivider = false; } } if (user != null) { userCell.setData(user, ContactsController.formatName(user.first_name, user.last_name), LocaleController.formatPluralString("InviteLinkCount", count), drawDivider); } } } @Override public void onViewRecycled(RecyclerView.ViewHolder holder) { if (holder.itemView instanceof ManageChatUserCell) { ((ManageChatUserCell) holder.itemView).recycle(); } } @Override public int getItemViewType(int position) { if (position == helpRow) { return 0; } else if (position == permanentLinkHeaderRow || position == revokedHeader || position == adminsHeaderRow || position == linksHeaderRow) { return 1; } else if (position == permanentLinkRow) { return 2; } else if (position == createNewLinkRow) { return 3; } else if (position == dividerRow || position == revokedDivider || position == revokeAllDivider || position == creatorDividerRow || position == adminsDividerRow) { return 4; } else if ((position >= linksStartRow && position < linksEndRow) || (position >= revokedLinksStartRow && position < revokedLinksEndRow)) { return 5; } else if (position == linksLoadingRow) { return 6; } else if (position == lastDivider) { return 7; } else if (position == revokeAllRow) { return 8; } else if (position == createLinkHelpRow) { return 9; } else if (position == creatorRow || (position >= adminsStartRow && position < adminsEndRow)) { return 10; } return 1; } } private void revokePermanent() { if (adminId == getAccountInstance().getUserConfig().clientUserId) { TLRPC.TL_messages_exportChatInvite req = new TLRPC.TL_messages_exportChatInvite(); req.peer = getMessagesController().getInputPeer(-currentChatId); req.legacy_revoke_permanent = true; TLRPC.TL_chatInviteExported oldInvite = invite; invite = null; info.exported_invite = null; final int reqId = getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (error == null) { invite = (TLRPC.TL_chatInviteExported) response; if (info != null) { info.exported_invite = invite; } if (getParentActivity() == null) { return; } oldInvite.revoked = true; DiffCallback callback = saveListState(); revokedInvites.add(0, oldInvite); updateRecyclerViewAnimated(callback); BulletinFactory.of(this).createSimpleBulletin(R.raw.linkbroken, LocaleController.getString("InviteRevokedHint", R.string.InviteRevokedHint)).show(); } })); AndroidUtilities.updateVisibleRows(listView); getConnectionsManager().bindRequestToGuid(reqId, classGuid); } else { revokeLink(invite); } } public static class TextCell extends FrameLayout { private SimpleTextView textView; private ImageView imageView; boolean divider; public TextCell(Context context) { super(context); textView = new SimpleTextView(context); textView.setTextSize(16); textView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT); textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText2)); textView.setTag(Theme.key_windowBackgroundWhiteBlueText2); addView(textView); imageView = new ImageView(context); imageView.setScaleType(ImageView.ScaleType.CENTER); addView(imageView); setWillNotDraw(false); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = AndroidUtilities.dp(48); textView.measure(MeasureSpec.makeMeasureSpec(width - AndroidUtilities.dp(71 + 23), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(20), MeasureSpec.EXACTLY)); imageView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(50), MeasureSpec.EXACTLY)); setMeasuredDimension(width, AndroidUtilities.dp(50)); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { int height = bottom - top; int width = right - left; int viewLeft; int viewTop = (height - textView.getTextHeight()) / 2; if (LocaleController.isRTL) { viewLeft = getMeasuredWidth() - textView.getMeasuredWidth() - AndroidUtilities.dp(imageView.getVisibility() == VISIBLE ? 70 : 25); } else { viewLeft = AndroidUtilities.dp(imageView.getVisibility() == VISIBLE ? 70 : 25); } textView.layout(viewLeft, viewTop, viewLeft + textView.getMeasuredWidth(), viewTop + textView.getMeasuredHeight()); viewLeft = !LocaleController.isRTL ? (AndroidUtilities.dp(70) - imageView.getMeasuredWidth()) / 2 : width - imageView.getMeasuredWidth() - AndroidUtilities.dp(25); imageView.layout(viewLeft, 0, viewLeft + imageView.getMeasuredWidth(), imageView.getMeasuredHeight()); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (divider) { canvas.drawLine(AndroidUtilities.dp(70), getMeasuredHeight() - 1, getMeasuredWidth() + AndroidUtilities.dp(23), getMeasuredHeight(), Theme.dividerPaint); } } public void setTextAndIcon(String text, Drawable icon, boolean divider) { textView.setText(text); imageView.setImageDrawable(icon); this.divider = divider; } } private class LinkCell extends FrameLayout { private final static int LINK_STATE_BLUE = 0; private final static int LINK_STATE_GREEN = 1; private final static int LINK_STATE_YELLOW = 2; private final static int LINK_STATE_RED = 3; private final static int LINK_STATE_GRAY = 4; int lastDrawingState; TextView titleView; TextView subtitleView; TLRPC.TL_chatInviteExported invite; int position; Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); Paint paint2 = new Paint(Paint.ANTI_ALIAS_FLAG); RectF rectF = new RectF(); ImageView optionsView; int animateFromState; float animateToStateProgress = 1f; float lastDrawExpringProgress; boolean animateHideExpiring; boolean drawDivider; public LinkCell(@NonNull Context context) { super(context); paint2.setStyle(Paint.Style.STROKE); paint2.setStrokeCap(Paint.Cap.ROUND); LinearLayout linearLayout = new LinearLayout(context); linearLayout.setOrientation(LinearLayout.VERTICAL); addView(linearLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 70, 0, 30, 0)); titleView = new TextView(context); titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); titleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); titleView.setLines(1); titleView.setEllipsize(TextUtils.TruncateAt.END); subtitleView = new TextView(context); subtitleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13); subtitleView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText)); linearLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); linearLayout.addView(subtitleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 6, 0, 0)); optionsView = new ImageView(context); optionsView.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_ab_other)); optionsView.setScaleType(ImageView.ScaleType.CENTER); optionsView.setColorFilter(Theme.getColor(Theme.key_stickers_menu)); optionsView.setOnClickListener(view -> { if (invite == null) { return; } ArrayList items = new ArrayList<>(); ArrayList icons = new ArrayList<>(); final ArrayList actions = new ArrayList<>(); boolean redLastItem = false; if (invite.revoked) { items.add(LocaleController.getString("Delete", R.string.Delete)); icons.add(R.drawable.msg_delete); actions.add(4); redLastItem = true; } else { items.add(LocaleController.getString("CopyLink", R.string.CopyLink)); icons.add(R.drawable.msg_copy); actions.add(0); items.add(LocaleController.getString("ShareLink", R.string.ShareLink)); icons.add(R.drawable.msg_share); actions.add(1); if (!invite.permanent && canEdit) { items.add(LocaleController.getString("EditLink", R.string.EditLink)); icons.add(R.drawable.msg_edit); actions.add(2); } if (canEdit) { items.add(LocaleController.getString("RevokeLink", R.string.RevokeLink)); icons.add(R.drawable.msg_delete); actions.add(3); redLastItem = true; } } AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity()); builder.setItems(items.toArray(new CharSequence[0]), AndroidUtilities.toIntArray(icons), (dialogInterface, i) -> { switch (actions.get(i)) { case 0: try { if (invite.link == null) { return; } android.content.ClipboardManager clipboard = (android.content.ClipboardManager) ApplicationLoader.applicationContext.getSystemService(Context.CLIPBOARD_SERVICE); android.content.ClipData clip = android.content.ClipData.newPlainText("label", invite.link); clipboard.setPrimaryClip(clip); BulletinFactory.createCopyLinkBulletin(ManageLinksActivity.this).show(); } catch (Exception e) { FileLog.e(e); } break; case 1: try { if (invite.link == null) { return; } Intent intent = new Intent(Intent.ACTION_SEND); intent.setType("text/plain"); intent.putExtra(Intent.EXTRA_TEXT, invite.link); startActivityForResult(Intent.createChooser(intent, LocaleController.getString("InviteToGroupByLink", R.string.InviteToGroupByLink)), 500); } catch (Exception e) { FileLog.e(e); } break; case 2: editLink(invite); break; case 3: TLRPC.TL_chatInviteExported inviteFinal = invite; AlertDialog.Builder builder2 = new AlertDialog.Builder(getParentActivity()); builder2.setMessage(LocaleController.getString("RevokeAlert", R.string.RevokeAlert)); builder2.setTitle(LocaleController.getString("RevokeLink", R.string.RevokeLink)); builder2.setPositiveButton(LocaleController.getString("RevokeButton", R.string.RevokeButton), (dialogInterface2, i2) -> revokeLink(inviteFinal)); builder2.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showDialog(builder2.create()); break; case 4: inviteFinal = invite; builder2 = new AlertDialog.Builder(getParentActivity()); builder2.setTitle(LocaleController.getString("DeleteLink", R.string.DeleteLink)); builder2.setMessage(LocaleController.getString("DeleteLinkHelp", R.string.DeleteLinkHelp)); builder2.setPositiveButton(LocaleController.getString("Delete", R.string.Delete), (dialogInterface2, i2) -> deleteLink(inviteFinal)); builder2.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null); showDialog(builder2.create()); break; } }); builder.setTitle(LocaleController.getString("InviteLink", R.string.InviteLink)); AlertDialog alert = builder.create(); builder.show(); if (redLastItem) { alert.setItemColor(items.size() - 1, Theme.getColor(Theme.key_dialogTextRed2), Theme.getColor(Theme.key_dialogRedIcon)); } }); optionsView.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), 1)); addView(optionsView, LayoutHelper.createFrame(40, 48, Gravity.RIGHT | Gravity.CENTER_VERTICAL)); setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); setWillNotDraw(false); } boolean timerRunning; @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64), MeasureSpec.EXACTLY)); paint2.setStrokeWidth(AndroidUtilities.dp(2)); } private TimerParticles timerParticles = new TimerParticles(); @Override protected void onDraw(Canvas canvas) { if (invite == null) { return; } int cX = AndroidUtilities.dp(36); int cY = getMeasuredHeight() / 2; int drawState; float progress = 0; float timeProgress = 1f; if (invite.expired || invite.revoked) { drawState = invite.revoked ? LINK_STATE_GRAY : LINK_STATE_RED; } else if (invite.expire_date > 0 || invite.usage_limit > 0) { float usageProgress = 1f; if (invite.expire_date > 0) { long currentTime = System.currentTimeMillis() + timeDif * 1000L; long expireTime = invite.expire_date * 1000L; long date = (invite.start_date <= 0 ? invite.date : invite.start_date) * 1000L; long from = currentTime - date; long to = expireTime - date; timeProgress = (1f - from / (float) to); } if (invite.usage_limit > 0) { usageProgress = (invite.usage_limit - invite.usage) / (float) invite.usage_limit; } progress = Math.min(timeProgress, usageProgress); if (progress <= 0) { invite.expired = true; drawState = LINK_STATE_RED; AndroidUtilities.updateVisibleRows(listView); } else { drawState = LINK_STATE_GREEN; } } else { drawState = LINK_STATE_BLUE; } if (drawState != lastDrawingState && lastDrawingState >= 0) { animateFromState = lastDrawingState; animateToStateProgress = 0f; if (hasProgress(animateFromState) && !hasProgress(drawState)) { animateHideExpiring = true; } else { animateHideExpiring = false; } } lastDrawingState = drawState; if (animateToStateProgress != 1f) { animateToStateProgress += 16f / 250f; if (animateToStateProgress >= 1f) { animateToStateProgress = 1f; animateHideExpiring = false; } else { invalidate(); } } int color; if (animateToStateProgress != 1f) { color = ColorUtils.blendARGB(getColor(animateFromState, progress), getColor(drawState, progress), animateToStateProgress); } else { color = getColor(drawState, progress); } paint.setColor(color); canvas.drawCircle(cX, cY, AndroidUtilities.dp(32) / 2f, paint); if (animateHideExpiring || (!invite.expired && invite.expire_date > 0 && !invite.revoked)) { if (animateHideExpiring) { timeProgress = lastDrawExpringProgress; } paint2.setColor(color); rectF.set(cX - AndroidUtilities.dp(20), cY - AndroidUtilities.dp(20), cX + AndroidUtilities.dp(20), cY + AndroidUtilities.dp(20)); if (animateToStateProgress != 1f && (!hasProgress(animateFromState) || animateHideExpiring)) { canvas.save(); float a = (animateHideExpiring ? (1f - animateToStateProgress) : animateToStateProgress); float s = (float) (0.7 + 0.3f * a); canvas.scale(s, s, rectF.centerX(), rectF.centerY()); canvas.drawArc(rectF, -90, -timeProgress * 360, false, paint2); timerParticles.draw(canvas, paint2, rectF, -timeProgress * 360, a); canvas.restore(); } else { canvas.drawArc(rectF, -90, -timeProgress * 360, false, paint2); timerParticles.draw(canvas, paint2, rectF, -timeProgress * 360, 1f); } if (!isPaused) { invalidate(); } lastDrawExpringProgress = timeProgress; } if (invite.revoked) { linkIconRevoked.setBounds(cX - AndroidUtilities.dp(12), cY - AndroidUtilities.dp(12), cX + AndroidUtilities.dp(12), cY + AndroidUtilities.dp(12)); linkIconRevoked.draw(canvas); } else { linkIcon.setBounds(cX - AndroidUtilities.dp(12), cY - AndroidUtilities.dp(12), cX + AndroidUtilities.dp(12), cY + AndroidUtilities.dp(12)); linkIcon.draw(canvas); } if (drawDivider) { canvas.drawLine(AndroidUtilities.dp(70), getMeasuredHeight() - 1, getMeasuredWidth() + AndroidUtilities.dp(23), getMeasuredHeight(), Theme.dividerPaint); } } private boolean hasProgress(int state) { return state == LINK_STATE_YELLOW || state == LINK_STATE_GREEN; } private int getColor(int state, float progress) { if (state == LINK_STATE_RED) { return Theme.getColor(Theme.key_chat_attachAudioBackground); } else if (state == LINK_STATE_GREEN) { if (progress > 0.5f) { float p = (progress - 0.5f) / 0.5f; return ColorUtils.blendARGB(Theme.getColor(Theme.key_chat_attachLocationBackground), Theme.getColor(Theme.key_chat_attachPollBackground), (1f - p)); } else { float p = progress / 0.5f; return ColorUtils.blendARGB(Theme.getColor(Theme.key_chat_attachPollBackground), Theme.getColor(Theme.key_chat_attachAudioBackground), (1f - p)); } } else if (state == LINK_STATE_YELLOW) { return Theme.getColor(Theme.key_chat_attachPollBackground); } else if (state == LINK_STATE_GRAY) { return Theme.getColor(Theme.key_chats_unreadCounterMuted); } else { return Theme.getColor(Theme.key_featuredStickers_addButton); } } public void setLink(TLRPC.TL_chatInviteExported invite, int position) { timerRunning = false; if (this.invite == null || invite == null || !this.invite.link.equals(invite.link)) { lastDrawingState = -1; animateToStateProgress = 1f; } this.invite = invite; this.position = position; if (invite == null) { return; } if (!TextUtils.isEmpty(invite.title)) { SpannableStringBuilder builder = new SpannableStringBuilder(invite.title); Emoji.replaceEmoji(builder, titleView.getPaint().getFontMetricsInt(), (int) titleView.getPaint().getTextSize(), false); titleView.setText(builder); } else if (invite.link.startsWith("https://t.me/+")) { titleView.setText(invite.link.substring("https://t.me/+".length())); } else if (invite.link.startsWith("https://t.me/joinchat/")) { titleView.setText(invite.link.substring("https://t.me/joinchat/".length())); } else if (invite.link.startsWith("https://")) { titleView.setText(invite.link.substring("https://".length())); } else { titleView.setText(invite.link); } String joinedString = ""; if (invite.usage == 0 && invite.usage_limit == 0 && invite.requested == 0) { joinedString = LocaleController.getString("NoOneJoinedYet", R.string.NoOneJoinedYet); } else { if (invite.usage_limit > 0 && invite.usage == 0 && !invite.expired && !invite.revoked) { joinedString = LocaleController.formatPluralString("CanJoin", invite.usage_limit); } else if (invite.usage_limit > 0 && invite.expired && invite.revoked) { joinedString = LocaleController.formatPluralString("PeopleJoined", invite.usage) + ", " + LocaleController.formatPluralString("PeopleJoinedRemaining", (invite.usage_limit - invite.usage)); } else { if (invite.usage > 0) { joinedString = LocaleController.formatPluralString("PeopleJoined", invite.usage); } if (invite.requested > 0) { if (invite.usage > 0) { joinedString = joinedString + ", "; } joinedString = joinedString + LocaleController.formatPluralString("JoinRequests", invite.requested); } } } if (invite.permanent && !invite.revoked) { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(joinedString); DotDividerSpan dotDividerSpan = new DotDividerSpan(); dotDividerSpan.setTopPadding(AndroidUtilities.dp(1.5f)); spannableStringBuilder.append(" . ").setSpan(dotDividerSpan, spannableStringBuilder.length() - 3, spannableStringBuilder.length() - 2, 0); spannableStringBuilder.append(LocaleController.getString("Permanent", R.string.Permanent)); subtitleView.setText(spannableStringBuilder); } else if (invite.expired || invite.revoked) { if (invite.revoked && invite.usage == 0) { joinedString = LocaleController.getString("NoOneJoined", R.string.NoOneJoined); } SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(joinedString); DotDividerSpan dotDividerSpan = new DotDividerSpan(); dotDividerSpan.setTopPadding(AndroidUtilities.dp(1.5f)); spannableStringBuilder.append(" . ").setSpan(dotDividerSpan, spannableStringBuilder.length() - 3, spannableStringBuilder.length() - 2, 0); if (!invite.revoked && invite.usage_limit > 0 && invite.usage >= invite.usage_limit) { spannableStringBuilder.append(LocaleController.getString("LinkLimitReached", R.string.LinkLimitReached)); } else { spannableStringBuilder.append(invite.revoked ? LocaleController.getString("Revoked", R.string.Revoked) : LocaleController.getString("Expired", R.string.Expired)); } subtitleView.setText(spannableStringBuilder); } else if (invite.expire_date > 0) { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(joinedString); DotDividerSpan dotDividerSpan = new DotDividerSpan(); dotDividerSpan.setTopPadding(AndroidUtilities.dp(1.5f)); spannableStringBuilder.append(" . ").setSpan(dotDividerSpan, spannableStringBuilder.length() - 3, spannableStringBuilder.length() - 2, 0); long currentTime = System.currentTimeMillis() + timeDif * 1000L; long expireTime = invite.expire_date * 1000L; long timeLeft = expireTime - currentTime; if (timeLeft < 0) { timeLeft = 0; } if (timeLeft > 86400000L) { spannableStringBuilder.append(LocaleController.formatPluralString("DaysLeft", (int) (timeLeft / 86400000L))); } else { int s = (int) ((timeLeft / 1000) % 60); int m = (int) ((timeLeft / 1000 / 60) % 60); int h = (int) ((timeLeft / 1000 / 60 / 60)); spannableStringBuilder.append(String.format(Locale.ENGLISH, "%02d", h)).append(String.format(Locale.ENGLISH, ":%02d", m)).append(String.format(Locale.ENGLISH, ":%02d", s)); timerRunning = true; } subtitleView.setText(spannableStringBuilder); } else { subtitleView.setText(joinedString); } } } public void deleteLink(TLRPC.TL_chatInviteExported invite) { TLRPC.TL_messages_deleteExportedChatInvite req = new TLRPC.TL_messages_deleteExportedChatInvite(); req.link = invite.link; req.peer = getMessagesController().getInputPeer(-currentChatId); getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (error == null) { linkEditActivityCallback.onLinkRemoved(invite); } })); } public void editLink(TLRPC.TL_chatInviteExported invite) { LinkEditActivity activity = new LinkEditActivity(LinkEditActivity.EDIT_TYPE, currentChatId); activity.setCallback(linkEditActivityCallback); activity.setInviteToEdit(invite); presentFragment(activity); } public void revokeLink(TLRPC.TL_chatInviteExported invite) { TLRPC.TL_messages_editExportedChatInvite req = new TLRPC.TL_messages_editExportedChatInvite(); req.link = invite.link; req.revoked = true; req.peer = getMessagesController().getInputPeer(-currentChatId); getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (error == null) { if (response instanceof TLRPC.TL_messages_exportedChatInviteReplaced) { TLRPC.TL_messages_exportedChatInviteReplaced replaced = (TLRPC.TL_messages_exportedChatInviteReplaced) response; if (!isPublic) { ManageLinksActivity.this.invite = (TLRPC.TL_chatInviteExported) replaced.new_invite; } invite.revoked = true; DiffCallback callback = saveListState(); if (isPublic && adminId == getAccountInstance().getUserConfig().getClientUserId()) { invites.remove(invite); invites.add(0, (TLRPC.TL_chatInviteExported) replaced.new_invite); } else if (this.invite != null) { this.invite = (TLRPC.TL_chatInviteExported) replaced.new_invite; } revokedInvites.add(0, invite); updateRecyclerViewAnimated(callback); } else { linkEditActivityCallback.onLinkEdited(invite, response); if (info != null) { info.invitesCount--; if (info.invitesCount < 0) { info.invitesCount = 0; } getMessagesStorage().saveChatLinksCount(currentChatId, info.invitesCount); } } if (getParentActivity() != null) { BulletinFactory.of(this).createSimpleBulletin(R.raw.linkbroken, LocaleController.getString("InviteRevokedHint", R.string.InviteRevokedHint)).show(); } } })); } private final LinkEditActivity.Callback linkEditActivityCallback = new LinkEditActivity.Callback() { @Override public void onLinkCreated(TLObject response) { if (response instanceof TLRPC.TL_chatInviteExported) { AndroidUtilities.runOnUIThread(() -> { DiffCallback callback = saveListState(); invites.add(0, (TLRPC.TL_chatInviteExported) response); if (info != null) { info.invitesCount++; getMessagesStorage().saveChatLinksCount(currentChatId, info.invitesCount); } updateRecyclerViewAnimated(callback); }, 200); } } @Override public void onLinkEdited(TLRPC.TL_chatInviteExported inviteToEdit, TLObject response) { if (response instanceof TLRPC.TL_messages_exportedChatInvite) { TLRPC.TL_chatInviteExported edited = (TLRPC.TL_chatInviteExported) ((TLRPC.TL_messages_exportedChatInvite) response).invite; fixDate(edited); for (int i = 0; i < invites.size(); i++) { if (invites.get(i).link.equals(inviteToEdit.link)) { if (edited.revoked) { DiffCallback callback = saveListState(); invites.remove(i); revokedInvites.add(0, edited); updateRecyclerViewAnimated(callback); } else { invites.set(i, edited); updateRows(true); } return; } } } } @Override public void onLinkRemoved(TLRPC.TL_chatInviteExported removedInvite) { for (int i = 0; i < revokedInvites.size(); i++) { if (revokedInvites.get(i).link.equals(removedInvite.link)) { DiffCallback callback = saveListState(); revokedInvites.remove(i); updateRecyclerViewAnimated(callback); return; } } } @Override public void revokeLink(TLRPC.TL_chatInviteExported inviteFinal) { ManageLinksActivity.this.revokeLink(inviteFinal); } }; private void updateRecyclerViewAnimated(DiffCallback callback) { if (isPaused || listViewAdapter == null || listView == null) { updateRows(true); return; } updateRows(false); callback.fillPositions(callback.newPositionToItem); DiffUtil.calculateDiff(callback).dispatchUpdatesTo(listViewAdapter); AndroidUtilities.updateVisibleRows(listView); } private class DiffCallback extends DiffUtil.Callback { int oldRowCount; int oldLinksStartRow; int oldLinksEndRow; int oldRevokedLinksStartRow; int oldRevokedLinksEndRow; int oldAdminsStartRow; int oldAdminsEndRow; SparseIntArray oldPositionToItem = new SparseIntArray(); SparseIntArray newPositionToItem = new SparseIntArray(); ArrayList oldLinks = new ArrayList<>(); ArrayList oldRevokedLinks = new ArrayList<>(); @Override public int getOldListSize() { return oldRowCount; } @Override public int getNewListSize() { return rowCount; } @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { if (oldItemPosition >= oldLinksStartRow && oldItemPosition < oldLinksEndRow || oldItemPosition >= oldRevokedLinksStartRow && oldItemPosition < oldRevokedLinksEndRow) { if (newItemPosition >= linksStartRow && newItemPosition < linksEndRow || newItemPosition >= revokedLinksStartRow && newItemPosition < revokedLinksEndRow) { TLRPC.TL_chatInviteExported newItem; TLRPC.TL_chatInviteExported oldItem; if (newItemPosition >= linksStartRow && newItemPosition < linksEndRow) { newItem = invites.get(newItemPosition - linksStartRow); } else { newItem = revokedInvites.get(newItemPosition - revokedLinksStartRow); } if (oldItemPosition >= oldLinksStartRow && oldItemPosition < oldLinksEndRow) { oldItem = oldLinks.get(oldItemPosition - oldLinksStartRow); } else { oldItem = oldRevokedLinks.get(oldItemPosition - oldRevokedLinksStartRow); } return oldItem.link.equals(newItem.link); } } if (oldItemPosition >= oldAdminsStartRow && oldItemPosition < oldAdminsEndRow && newItemPosition >= adminsStartRow && newItemPosition < adminsEndRow) { return (oldItemPosition - oldAdminsStartRow) == (newItemPosition - adminsStartRow); } int oldItem = oldPositionToItem.get(oldItemPosition, -1); int newItem = newPositionToItem.get(newItemPosition, -1); return oldItem >= 0 && oldItem == newItem; } @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { return areItemsTheSame(oldItemPosition, newItemPosition); } public void fillPositions(SparseIntArray sparseIntArray) { sparseIntArray.clear(); int pointer = 0; put(++pointer, helpRow, sparseIntArray); put(++pointer, permanentLinkHeaderRow, sparseIntArray); put(++pointer, permanentLinkRow, sparseIntArray); put(++pointer, dividerRow, sparseIntArray); put(++pointer, createNewLinkRow, sparseIntArray); put(++pointer, revokedHeader, sparseIntArray); put(++pointer, revokeAllRow, sparseIntArray); put(++pointer, createLinkHelpRow, sparseIntArray); put(++pointer, creatorRow, sparseIntArray); put(++pointer, creatorDividerRow, sparseIntArray); put(++pointer, adminsHeaderRow, sparseIntArray); put(++pointer, linksHeaderRow, sparseIntArray); put(++pointer, linksLoadingRow, sparseIntArray); } private void put(int id, int position, SparseIntArray sparseIntArray) { if (position >= 0) { sparseIntArray.put(position, id); } } } private DiffCallback saveListState() { DiffCallback callback = new DiffCallback(); callback.fillPositions(callback.oldPositionToItem); callback.oldLinksStartRow = linksStartRow; callback.oldLinksEndRow = linksEndRow; callback.oldRevokedLinksStartRow = revokedLinksStartRow; callback.oldRevokedLinksEndRow = revokedLinksEndRow; callback.oldAdminsStartRow = adminsStartRow; callback.oldAdminsEndRow = adminsEndRow; callback.oldRowCount = rowCount; callback.oldLinks.clear(); callback.oldLinks.addAll(invites); callback.oldRevokedLinks.clear(); callback.oldRevokedLinks.addAll(revokedInvites); return callback; } public void fixDate(TLRPC.TL_chatInviteExported edited) { if (edited.expire_date > 0) { edited.expired = getConnectionsManager().getCurrentTime() >= edited.expire_date; } else if (edited.usage_limit > 0) { edited.expired = edited.usage >= edited.usage_limit; } } @Override public ArrayList getThemeDescriptions() { ArrayList themeDescriptions = new ArrayList<>(); ThemeDescription.ThemeDescriptionDelegate cellDelegate = () -> { if (listView != null) { int count = listView.getChildCount(); for (int a = 0; a < count; a++) { View child = listView.getChildAt(a); if (child instanceof ManageChatUserCell) { ((ManageChatUserCell) child).update(0); } if (child instanceof LinkActionView) { ((LinkActionView) child).updateColors(); } } } if (inviteLinkBottomSheet != null) { inviteLinkBottomSheet.updateColors(); } }; themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{HeaderCell.class, TextCell.class, LinkActionView.class, LinkCell.class}, null, null, null, Theme.key_windowBackgroundWhite)); themeDescriptions.add(new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_windowBackgroundGray)); themeDescriptions.add(new ThemeDescription(fragmentView, ThemeDescription.FLAG_BACKGROUND | ThemeDescription.FLAG_CHECKTAG, null, null, null, null, Theme.key_windowBackgroundWhite)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_BACKGROUND, null, null, null, null, Theme.key_actionBarDefault)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_LISTGLOWCOLOR, null, null, null, null, Theme.key_actionBarDefault)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_ITEMSCOLOR, null, null, null, null, Theme.key_actionBarDefaultIcon)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_TITLECOLOR, null, null, null, null, Theme.key_actionBarDefaultTitle)); themeDescriptions.add(new ThemeDescription(actionBar, ThemeDescription.FLAG_AB_SELECTORCOLOR, null, null, null, null, Theme.key_actionBarDefaultSelector)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_SELECTOR, null, null, null, null, Theme.key_listSelector)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{View.class}, Theme.dividerPaint, null, null, Theme.key_divider)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextInfoPrivacyCell.class}, null, null, null, Theme.key_windowBackgroundGrayShadow)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextInfoPrivacyCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText4)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{ManageChatUserCell.class}, new String[]{"nameTextView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{ManageChatUserCell.class}, new String[]{"statusColor"}, null, null, cellDelegate, Theme.key_windowBackgroundWhiteGrayText)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{ManageChatUserCell.class}, new String[]{"statusOnlineColor"}, null, null, cellDelegate, Theme.key_windowBackgroundWhiteBlueText)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{ManageChatUserCell.class}, null, Theme.avatarDrawables, null, Theme.key_avatar_text)); themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, cellDelegate, Theme.key_avatar_backgroundRed)); themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, cellDelegate, Theme.key_avatar_backgroundOrange)); themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, cellDelegate, Theme.key_avatar_backgroundViolet)); themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, cellDelegate, Theme.key_avatar_backgroundGreen)); themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, cellDelegate, Theme.key_avatar_backgroundCyan)); themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, cellDelegate, Theme.key_avatar_backgroundBlue)); themeDescriptions.add(new ThemeDescription(null, 0, null, null, null, cellDelegate, Theme.key_avatar_backgroundPink)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{HintInnerCell.class}, new String[]{"messageTextView"}, null, null, null, Theme.key_chats_message)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_chats_unreadCounterMuted)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueButton)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_CHECKTAG, new Class[]{ManageChatTextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueIcon)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueText2)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_BACKGROUNDFILTER, new Class[]{TextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_switchTrackChecked)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{TextCell.class}, new String[]{"imageView"}, null, null, null, Theme.key_checkboxCheck)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{HeaderCell.class}, new String[]{"textView"}, null, null, null, Theme.key_windowBackgroundWhiteBlueHeader)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{LinkCell.class}, new String[]{"titleView"}, null, null, null, Theme.key_windowBackgroundWhiteBlackText)); themeDescriptions.add(new ThemeDescription(listView, 0, new Class[]{LinkCell.class}, new String[]{"subtitleView"}, null, null, null, Theme.key_windowBackgroundWhiteGrayText)); themeDescriptions.add(new ThemeDescription(listView, ThemeDescription.FLAG_IMAGECOLOR, new Class[]{LinkCell.class}, new String[]{"optionsView"}, null, null, null, Theme.key_stickers_menu)); return themeDescriptions; } @Override public boolean needDelayOpenAnimation() { return true; } int animationIndex = -1; @Override protected void onTransitionAnimationEnd(boolean isOpen, boolean backward) { super.onTransitionAnimationEnd(isOpen, backward); if (isOpen) { isOpened = true; if (backward && inviteLinkBottomSheet != null && inviteLinkBottomSheet.isNeedReopen) { inviteLinkBottomSheet.show(); } } NotificationCenter.getInstance(currentAccount).onAnimationFinish(animationIndex); } @Override protected void onTransitionAnimationStart(boolean isOpen, boolean backward) { super.onTransitionAnimationStart(isOpen, backward); animationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(animationIndex, null); } }