package org.telegram.ui.Cells; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.text.Layout; import android.text.SpannableStringBuilder; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; import android.view.Gravity; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.DownloadController; import org.telegram.messenger.Emoji; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLoader; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Components.CheckBox2; import org.telegram.ui.Components.DotDividerSpan; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.MediaActionDrawable; import org.telegram.ui.Components.RadialProgress2; import org.telegram.ui.FilteredSearchView; public class SharedAudioCell extends FrameLayout implements DownloadController.FileDownloadProgressListener, NotificationCenter.NotificationCenterDelegate { private SpannableStringBuilder dotSpan; private CheckBox2 checkBox; private boolean needDivider; private boolean buttonPressed; private boolean miniButtonPressed; private int hasMiniProgress; private int buttonX; private int buttonY; private int titleY = AndroidUtilities.dp(9); private StaticLayout titleLayout; private int descriptionY = AndroidUtilities.dp(29); private StaticLayout descriptionLayout; private int captionY = AndroidUtilities.dp(29); private StaticLayout captionLayout; private MessageObject currentMessageObject; private boolean checkForButtonPress; private int currentAccount = UserConfig.selectedAccount; private int TAG; private int buttonState; private int miniButtonState; private RadialProgress2 radialProgress; private int viewType; public final static int VIEW_TYPE_DEFAULT = 0; public final static int VIEW_TYPE_GLOBAL_SEARCH = 1; private StaticLayout dateLayout; private int dateLayoutX; private TextPaint description2TextPaint; private TextPaint captionTextPaint; public SharedAudioCell(Context context) { this(context, VIEW_TYPE_DEFAULT); } public SharedAudioCell(Context context, int viewType) { super(context); this.viewType = viewType; setFocusable(true); radialProgress = new RadialProgress2(this); radialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); TAG = DownloadController.getInstance(currentAccount).generateObserverTag(); setWillNotDraw(false); checkBox = new CheckBox2(context, 22); checkBox.setVisibility(INVISIBLE); checkBox.setColor(null, Theme.key_windowBackgroundWhite, Theme.key_checkboxCheck); checkBox.setDrawUnchecked(false); checkBox.setDrawBackgroundAsArc(3); addView(checkBox, LayoutHelper.createFrame(24, 24, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 38.1f, 32.1f, LocaleController.isRTL ? 6 : 0, 0)); if (viewType == VIEW_TYPE_GLOBAL_SEARCH) { description2TextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); description2TextPaint.setTextSize(AndroidUtilities.dp(13)); dotSpan = new SpannableStringBuilder("."); dotSpan.setSpan(new DotDividerSpan(), 0, 1, 0); } captionTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); captionTextPaint.setTextSize(AndroidUtilities.dp(13)); } @SuppressLint("DrawAllocation") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { descriptionLayout = null; titleLayout = null; captionLayout = null; int viewWidth = MeasureSpec.getSize(widthMeasureSpec); int maxWidth = viewWidth - AndroidUtilities.dp(AndroidUtilities.leftBaseline) - AndroidUtilities.dp(8 + 20); int dateWidth = 0; if (viewType == VIEW_TYPE_GLOBAL_SEARCH) { String str = LocaleController.stringForMessageListDate(currentMessageObject.messageOwner.date); int width = (int) Math.ceil(description2TextPaint.measureText(str)); dateLayout = ChatMessageCell.generateStaticLayout(str, description2TextPaint, width, width, 0, 1); dateLayoutX = maxWidth - width - AndroidUtilities.dp(8) + AndroidUtilities.dp(20); dateWidth = width + AndroidUtilities.dp(12); } try { CharSequence title; if (viewType == VIEW_TYPE_GLOBAL_SEARCH && (currentMessageObject.isVoice() || currentMessageObject.isRoundVideo())) { title = FilteredSearchView.createFromInfoString(currentMessageObject); } else { title = currentMessageObject.getMusicTitle().replace('\n', ' '); } CharSequence titleH = AndroidUtilities.highlightText(title, currentMessageObject.highlightedWords); if (titleH != null) { title = titleH; } CharSequence titleFinal = TextUtils.ellipsize(title, Theme.chat_contextResult_titleTextPaint, maxWidth - dateWidth, TextUtils.TruncateAt.END); titleLayout = new StaticLayout(titleFinal, Theme.chat_contextResult_titleTextPaint, maxWidth + AndroidUtilities.dp(4) - dateWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } catch (Exception e) { FileLog.e(e); } if (currentMessageObject.hasHighlightedWords()) { CharSequence caption = Emoji.replaceEmoji(currentMessageObject.messageOwner.message.replace("\n", " ").replaceAll(" +", " ").trim(), Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false); CharSequence sequence = AndroidUtilities.highlightText(caption, currentMessageObject.highlightedWords); if (sequence != null) { sequence = TextUtils.ellipsize(AndroidUtilities.ellipsizeCenterEnd(sequence, currentMessageObject.highlightedWords.get(0), maxWidth, captionTextPaint, 130), captionTextPaint, maxWidth, TextUtils.TruncateAt.END); captionLayout = new StaticLayout(sequence, captionTextPaint, maxWidth + AndroidUtilities.dp(4), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } } try { if (viewType == VIEW_TYPE_GLOBAL_SEARCH && (currentMessageObject.isVoice() || currentMessageObject.isRoundVideo())) { CharSequence duration = AndroidUtilities.formatDuration(currentMessageObject.getDuration(), false); TextPaint paint = viewType == VIEW_TYPE_GLOBAL_SEARCH ? description2TextPaint : Theme.chat_contextResult_descriptionTextPaint; duration = TextUtils.ellipsize(duration, paint, maxWidth, TextUtils.TruncateAt.END); descriptionLayout = new StaticLayout(duration, paint, maxWidth + AndroidUtilities.dp(4), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } else { CharSequence author = currentMessageObject.getMusicAuthor().replace('\n', ' '); CharSequence authorH = AndroidUtilities.highlightText(author, currentMessageObject.highlightedWords); if (authorH != null) { author = authorH; } if (viewType == VIEW_TYPE_GLOBAL_SEARCH) { author = new SpannableStringBuilder(author).append(' ').append(dotSpan).append(' ').append(FilteredSearchView.createFromInfoString(currentMessageObject)); } TextPaint paint = viewType == VIEW_TYPE_GLOBAL_SEARCH ? description2TextPaint : Theme.chat_contextResult_descriptionTextPaint; author = TextUtils.ellipsize(author, paint, maxWidth, TextUtils.TruncateAt.END); descriptionLayout = new StaticLayout(author, paint, maxWidth + AndroidUtilities.dp(4), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } } catch (Exception e) { FileLog.e(e); } setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.dp(56) + (captionLayout == null ? 0 : AndroidUtilities.dp(18)) + (needDivider ? 1 : 0)); int maxPhotoWidth = AndroidUtilities.dp(52); int x = LocaleController.isRTL ? MeasureSpec.getSize(widthMeasureSpec) - AndroidUtilities.dp(8) - maxPhotoWidth : AndroidUtilities.dp(8); radialProgress.setProgressRect(buttonX = x + AndroidUtilities.dp(4), buttonY = AndroidUtilities.dp(6), x + AndroidUtilities.dp(48), AndroidUtilities.dp(50)); measureChildWithMargins(checkBox, widthMeasureSpec, 0, heightMeasureSpec, 0); if (captionLayout != null) { captionY = AndroidUtilities.dp(29); descriptionY = AndroidUtilities.dp(29) + AndroidUtilities.dp(18); } else { descriptionY = AndroidUtilities.dp(29); } } public void setMessageObject(MessageObject messageObject, boolean divider) { needDivider = divider; currentMessageObject = messageObject; TLRPC.Document document = messageObject.getDocument(); TLRPC.PhotoSize thumb = document != null ? FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 360) : null; if (thumb instanceof TLRPC.TL_photoSize || thumb instanceof TLRPC.TL_photoSizeProgressive) { radialProgress.setImageOverlay(thumb, document, messageObject); } else { String artworkUrl = messageObject.getArtworkUrl(true); if (!TextUtils.isEmpty(artworkUrl)) { radialProgress.setImageOverlay(artworkUrl); } else { radialProgress.setImageOverlay(null, null, null); } } updateButtonState(false, false); requestLayout(); } public void setChecked(boolean checked, boolean animated) { if (checkBox.getVisibility() != VISIBLE) { checkBox.setVisibility(VISIBLE); } checkBox.setChecked(checked, animated); } public void setCheckForButtonPress(boolean value) { checkForButtonPress = value; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); radialProgress.onAttachedToWindow(); updateButtonState(false, false); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidReset); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingPlayStateChanged); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidStart); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); radialProgress.onDetachedFromWindow(); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagePlayingDidReset); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagePlayingPlayStateChanged); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagePlayingDidStart); } public MessageObject getMessage() { return currentMessageObject; } public void initStreamingIcons() { radialProgress.initMiniIcons(); } private boolean checkAudioMotionEvent(MotionEvent event) { int x = (int) event.getX(); int y = (int) event.getY(); boolean result = false; int side = AndroidUtilities.dp(36); boolean area = false; if (miniButtonState >= 0) { int offset = AndroidUtilities.dp(27); area = x >= buttonX + offset && x <= buttonX + offset + side && y >= buttonY + offset && y <= buttonY + offset + side; } if (event.getAction() == MotionEvent.ACTION_DOWN) { if (area) { miniButtonPressed = true; radialProgress.setPressed(miniButtonPressed, true); invalidate(); result = true; } else if (checkForButtonPress && radialProgress.getProgressRect().contains(x, y)) { buttonPressed = true; radialProgress.setPressed(buttonPressed, false); invalidate(); result = true; } } else if (event.getAction() == MotionEvent.ACTION_UP) { if (miniButtonPressed) { miniButtonPressed = false; playSoundEffect(SoundEffectConstants.CLICK); didPressedMiniButton(true); invalidate(); } else if (buttonPressed) { buttonPressed = false; playSoundEffect(SoundEffectConstants.CLICK); didPressedButton(); invalidate(); } } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { miniButtonPressed = false; buttonPressed = false; invalidate(); } else if (event.getAction() == MotionEvent.ACTION_MOVE) { if (!area && miniButtonPressed) { miniButtonPressed = false; invalidate(); } } radialProgress.setPressed(miniButtonPressed, true); return result; } @Override public boolean onTouchEvent(MotionEvent event) { if (currentMessageObject == null) { return super.onTouchEvent(event); } boolean result = checkAudioMotionEvent(event); if (event.getAction() == MotionEvent.ACTION_CANCEL) { miniButtonPressed = false; buttonPressed = false; result = false; radialProgress.setPressed(buttonPressed, false); radialProgress.setPressed(miniButtonPressed, true); } return result; } private void didPressedMiniButton(boolean animated) { if (miniButtonState == 0) { miniButtonState = 1; radialProgress.setProgress(0, false); FileLoader.getInstance(currentAccount).loadFile(currentMessageObject.getDocument(), currentMessageObject, 1, 0); radialProgress.setMiniIcon(getMiniIconForCurrentState(), false, true); invalidate(); } else if (miniButtonState == 1) { if (MediaController.getInstance().isPlayingMessage(currentMessageObject)) { MediaController.getInstance().cleanupPlayer(true, true); } miniButtonState = 0; FileLoader.getInstance(currentAccount).cancelLoadFile(currentMessageObject.getDocument()); radialProgress.setMiniIcon(getMiniIconForCurrentState(), false, true); invalidate(); } } public void didPressedButton() { if (buttonState == 0) { if (miniButtonState == 0) { FileLoader.getInstance(currentAccount).loadFile(currentMessageObject.getDocument(), currentMessageObject, 1, 0); } if (needPlayMessage(currentMessageObject)) { if (hasMiniProgress == 2 && miniButtonState != 1) { miniButtonState = 1; radialProgress.setProgress(0, false); radialProgress.setMiniIcon(getMiniIconForCurrentState(), false, true); } buttonState = 1; radialProgress.setIcon(getIconForCurrentState(), false, true); invalidate(); } } else if (buttonState == 1) { boolean result = MediaController.getInstance().pauseMessage(currentMessageObject); if (result) { buttonState = 0; radialProgress.setIcon(getIconForCurrentState(), false, true); invalidate(); } } else if (buttonState == 2) { radialProgress.setProgress(0, false); FileLoader.getInstance(currentAccount).loadFile(currentMessageObject.getDocument(), currentMessageObject, 1, 0); buttonState = 4; radialProgress.setIcon(getIconForCurrentState(), false, true); invalidate(); } else if (buttonState == 4) { FileLoader.getInstance(currentAccount).cancelLoadFile(currentMessageObject.getDocument()); buttonState = 2; radialProgress.setIcon(getIconForCurrentState(), false, true); invalidate(); } } @Override protected void onDraw(Canvas canvas) { if (viewType == VIEW_TYPE_GLOBAL_SEARCH) { description2TextPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText3)); } if (dateLayout != null) { canvas.save(); canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline) + (LocaleController.isRTL ? 0 : dateLayoutX), titleY); dateLayout.draw(canvas); canvas.restore(); } if (titleLayout != null) { canvas.save(); canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline) + (LocaleController.isRTL && dateLayout != null ? dateLayout.getWidth() + AndroidUtilities.dp(4) : 0), titleY); titleLayout.draw(canvas); canvas.restore(); } if (captionLayout != null) { captionTextPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); canvas.save(); canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline), captionY); captionLayout.draw(canvas); canvas.restore(); } if (descriptionLayout != null) { Theme.chat_contextResult_descriptionTextPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2)); canvas.save(); canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline), descriptionY); descriptionLayout.draw(canvas); canvas.restore(); } radialProgress.setProgressColor(Theme.getColor(buttonPressed ? Theme.key_chat_inAudioSelectedProgress : Theme.key_chat_inAudioProgress)); radialProgress.draw(canvas); if (needDivider) { canvas.drawLine(AndroidUtilities.dp(72), getHeight() - 1, getWidth() - getPaddingRight(), getHeight() - 1, Theme.dividerPaint); } } private int getMiniIconForCurrentState() { if (miniButtonState < 0) { return MediaActionDrawable.ICON_NONE; } if (miniButtonState == 0) { return MediaActionDrawable.ICON_DOWNLOAD; } else { return MediaActionDrawable.ICON_CANCEL; } } private int getIconForCurrentState() { if (buttonState == 1) { return MediaActionDrawable.ICON_PAUSE; } else if (buttonState == 2) { return MediaActionDrawable.ICON_DOWNLOAD; } else if (buttonState == 4) { return MediaActionDrawable.ICON_CANCEL; } return MediaActionDrawable.ICON_PLAY; } public void updateButtonState(boolean ifSame, boolean animated) { String fileName = currentMessageObject.getFileName(); if (TextUtils.isEmpty(fileName)) { return; } boolean fileExists = currentMessageObject.attachPathExists || currentMessageObject.mediaExists; if (SharedConfig.streamMedia && currentMessageObject.isMusic() && (int) currentMessageObject.getDialogId() != 0) { hasMiniProgress = fileExists ? 1 : 2; fileExists = true; } else { hasMiniProgress = 0; miniButtonState = -1; } if (hasMiniProgress != 0) { radialProgress.setMiniProgressBackgroundColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outLoader : Theme.key_chat_inLoader)); boolean playing = MediaController.getInstance().isPlayingMessage(currentMessageObject); if (!playing || playing && MediaController.getInstance().isMessagePaused()) { buttonState = 0; } else { buttonState = 1; } radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); if (hasMiniProgress == 1) { DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); miniButtonState = -1; radialProgress.setMiniIcon(getMiniIconForCurrentState(), ifSame, animated); } else { DownloadController.getInstance(currentAccount).addLoadingFileObserver(fileName, currentMessageObject, this); if (!FileLoader.getInstance(currentAccount).isLoadingFile(fileName)) { miniButtonState = 0; radialProgress.setMiniIcon(getMiniIconForCurrentState(), ifSame, animated); } else { miniButtonState = 1; radialProgress.setMiniIcon(getMiniIconForCurrentState(), ifSame, animated); Float progress = ImageLoader.getInstance().getFileProgress(fileName); if (progress != null) { radialProgress.setProgress(progress, animated); } else { radialProgress.setProgress(0, animated); } } } } else if (fileExists) { DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); boolean playing = MediaController.getInstance().isPlayingMessage(currentMessageObject); if (!playing || playing && MediaController.getInstance().isMessagePaused()) { buttonState = 0; } else { buttonState = 1; } radialProgress.setProgress(1, animated); radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); invalidate(); } else { DownloadController.getInstance(currentAccount).addLoadingFileObserver(fileName, currentMessageObject, this); boolean isLoading = FileLoader.getInstance(currentAccount).isLoadingFile(fileName); if (!isLoading) { buttonState = 2; radialProgress.setProgress(0, animated); } else { buttonState = 4; Float progress = ImageLoader.getInstance().getFileProgress(fileName); if (progress != null) { radialProgress.setProgress(progress, animated); } else { radialProgress.setProgress(0, animated); } } radialProgress.setIcon(getIconForCurrentState(), ifSame, animated); invalidate(); } } @Override public void onFailedDownload(String fileName, boolean canceled) { updateButtonState(true, canceled); } @Override public void onSuccessDownload(String fileName) { radialProgress.setProgress(1, true); updateButtonState(false, true); } @Override public void onProgressDownload(String fileName, long downloadSize, long totalSize) { float progress = Math.min(1f, downloadSize / (float) totalSize); radialProgress.setProgress(progress, true); if (hasMiniProgress != 0) { if (miniButtonState != 1) { updateButtonState(false, true); } } else { if (buttonState != 4) { updateButtonState(false, true); } } } @Override public void onProgressUpload(String fileName, long uploadedSize, long totalSize, boolean isEncrypted) { } @Override public int getObserverTag() { return TAG; } protected boolean needPlayMessage(MessageObject messageObject) { return false; } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); if (currentMessageObject.isMusic()) { info.setText(LocaleController.formatString("AccDescrMusicInfo", R.string.AccDescrMusicInfo, currentMessageObject.getMusicAuthor(), currentMessageObject.getMusicTitle())); } else if (titleLayout != null && descriptionLayout != null) { info.setText(titleLayout.getText() + ", " + descriptionLayout.getText()); } if (checkBox.isChecked()) { info.setCheckable(true); info.setChecked(true); } } @Override public void didReceivedNotification(int id, int account, Object... args) { updateButtonState(false, true); } }