Java tutorial
/* * This is the source code of Telegram for Android v. 3.x.x. * It is licensed under GNU GPL v. 2 or later. * You should have received a copy of the license in this archive (see LICENSE). * * Copyright Nikolai Kudashov, 2013-2016. */ package org.telegram.ui.Cells; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.support.v4.content.ContextCompat; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; import android.view.Gravity; import android.view.MotionEvent; import android.widget.FrameLayout; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.R; import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.TLRPC; import org.telegram.ui.Components.CheckBox; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LetterDrawable; import org.telegram.ui.Components.LinkPath; import org.telegram.ui.ActionBar.Theme; import java.io.File; import java.util.ArrayList; import java.util.Locale; public class SharedLinkCell extends FrameLayout { public interface SharedLinkCellDelegate { void needOpenWebView(TLRPC.WebPage webPage); boolean canPerformActions(); } private boolean linkPreviewPressed; private LinkPath urlPath = new LinkPath(); private static Paint urlPaint; private int pressedLink; private ImageReceiver linkImageView; private boolean drawLinkImageView; private LetterDrawable letterDrawable; private CheckBox checkBox; private SharedLinkCellDelegate delegate; private boolean needDivider; ArrayList<String> links = new ArrayList<>(); private int linkY; private ArrayList<StaticLayout> linkLayout = new ArrayList<>(); private int titleY = AndroidUtilities.dp(7); private StaticLayout titleLayout; private int descriptionY = AndroidUtilities.dp(27); private StaticLayout descriptionLayout; private int description2Y = AndroidUtilities.dp(27); private StaticLayout descriptionLayout2; private MessageObject message; private static TextPaint titleTextPaint; private static TextPaint descriptionTextPaint; private static Paint paint; private static int TEXT_COLOR; public SharedLinkCell(Context context) { super(context); if (titleTextPaint == null) { titleTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); titleTextPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); TEXT_COLOR = ContextCompat.getColor(context, R.color.primary_text); titleTextPaint.setColor(TEXT_COLOR); descriptionTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); paint = new Paint(); paint.setColor(ContextCompat.getColor(context, R.color.divider)); paint.setStrokeWidth(1); urlPaint = new Paint(); urlPaint.setColor(Theme.MSG_LINK_SELECT_BACKGROUND_COLOR); } titleTextPaint.setTextSize(AndroidUtilities.dp(16)); descriptionTextPaint.setTextSize(AndroidUtilities.dp(16)); setWillNotDraw(false); linkImageView = new ImageReceiver(this); letterDrawable = new LetterDrawable(); checkBox = new CheckBox(context, R.drawable.round_check2); checkBox.setVisibility(INVISIBLE); addView(checkBox, LayoutHelper.createFrame(22, 22, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 0 : 44, 44, LocaleController.isRTL ? 44 : 0, 0)); } @SuppressLint("DrawAllocation") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { drawLinkImageView = false; descriptionLayout = null; titleLayout = null; descriptionLayout2 = null; description2Y = descriptionY; linkLayout.clear(); links.clear(); int maxWidth = MeasureSpec.getSize(widthMeasureSpec) - AndroidUtilities.dp(AndroidUtilities.leftBaseline) - AndroidUtilities.dp(8); String title = null; String description = null; String description2 = null; String webPageLink = null; boolean hasPhoto = false; if (message.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage && message.messageOwner.media.webpage instanceof TLRPC.TL_webPage) { TLRPC.WebPage webPage = message.messageOwner.media.webpage; if (message.photoThumbs == null && webPage.photo != null) { message.generateThumbs(true); } hasPhoto = webPage.photo != null && message.photoThumbs != null; title = webPage.title; if (title == null) { title = webPage.site_name; } description = webPage.description; webPageLink = webPage.url; } if (message != null && !message.messageOwner.entities.isEmpty()) { for (int a = 0; a < message.messageOwner.entities.size(); a++) { TLRPC.MessageEntity entity = message.messageOwner.entities.get(a); if (entity.length <= 0 || entity.offset < 0 || entity.offset >= message.messageOwner.message.length()) { continue; } else if (entity.offset + entity.length > message.messageOwner.message.length()) { entity.length = message.messageOwner.message.length() - entity.offset; } if (a == 0 && webPageLink != null && !(entity.offset == 0 && entity.length == message.messageOwner.message.length())) { if (message.messageOwner.entities.size() == 1) { if (description == null) { description2 = message.messageOwner.message; } } else { description2 = message.messageOwner.message; } } try { String link = null; if (entity instanceof TLRPC.TL_messageEntityTextUrl || entity instanceof TLRPC.TL_messageEntityUrl) { if (entity instanceof TLRPC.TL_messageEntityUrl) { link = message.messageOwner.message.substring(entity.offset, entity.offset + entity.length); } else { link = entity.url; } if (title == null || title.length() == 0) { title = link; Uri uri = Uri.parse(title); title = uri.getHost(); if (title == null) { title = link; } int index; if (title != null && (index = title.lastIndexOf('.')) >= 0) { title = title.substring(0, index); if ((index = title.lastIndexOf('.')) >= 0) { title = title.substring(index + 1); } title = title.substring(0, 1).toUpperCase() + title.substring(1); } if (entity.offset != 0 || entity.length != message.messageOwner.message.length()) { description = message.messageOwner.message; } } } else if (entity instanceof TLRPC.TL_messageEntityEmail) { if (title == null || title.length() == 0) { link = "mailto:" + message.messageOwner.message.substring(entity.offset, entity.offset + entity.length); title = message.messageOwner.message.substring(entity.offset, entity.offset + entity.length); if (entity.offset != 0 || entity.length != message.messageOwner.message.length()) { description = message.messageOwner.message; } } } if (link != null) { if (link.toLowerCase().indexOf("http") != 0 && link.toLowerCase().indexOf("mailto") != 0) { links.add("http://" + link); } else { links.add(link); } } } catch (Exception e) { FileLog.e("tmessages", e); } } } if (webPageLink != null && links.isEmpty()) { links.add(webPageLink); } if (title != null) { try { int width = (int) Math.ceil(titleTextPaint.measureText(title)); CharSequence titleFinal = TextUtils.ellipsize(title.replace('\n', ' '), titleTextPaint, Math.min(width, maxWidth), TextUtils.TruncateAt.END); titleLayout = new StaticLayout(titleFinal, titleTextPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } catch (Exception e) { FileLog.e("tmessages", e); } letterDrawable.setTitle(title); } if (description != null) { try { descriptionLayout = ChatMessageCell.generateStaticLayout(description, descriptionTextPaint, maxWidth, maxWidth, 0, 3); if (descriptionLayout.getLineCount() > 0) { description2Y = descriptionY + descriptionLayout.getLineBottom(descriptionLayout.getLineCount() - 1) + AndroidUtilities.dp(1); } } catch (Exception e) { FileLog.e("tmessages", e); } } if (description2 != null) { try { descriptionLayout2 = ChatMessageCell.generateStaticLayout(description2, descriptionTextPaint, maxWidth, maxWidth, 0, 3); int height = descriptionLayout2.getLineBottom(descriptionLayout2.getLineCount() - 1); if (descriptionLayout != null) { description2Y += AndroidUtilities.dp(10); } } catch (Exception e) { FileLog.e("tmessages", e); } } if (!links.isEmpty()) { for (int a = 0; a < links.size(); a++) { try { String link = links.get(a); int width = (int) Math.ceil(descriptionTextPaint.measureText(link)); CharSequence linkFinal = TextUtils.ellipsize(link.replace('\n', ' '), descriptionTextPaint, Math.min(width, maxWidth), TextUtils.TruncateAt.MIDDLE); StaticLayout layout = new StaticLayout(linkFinal, descriptionTextPaint, maxWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); linkY = description2Y; if (descriptionLayout2 != null && descriptionLayout2.getLineCount() != 0) { linkY += descriptionLayout2.getLineBottom(descriptionLayout2.getLineCount() - 1) + AndroidUtilities.dp(1); } linkLayout.add(layout); } catch (Exception e) { FileLog.e("tmessages", e); } } } int maxPhotoWidth = AndroidUtilities.dp(52); int x = LocaleController.isRTL ? MeasureSpec.getSize(widthMeasureSpec) - AndroidUtilities.dp(10) - maxPhotoWidth : AndroidUtilities.dp(10); letterDrawable.setBounds(x, AndroidUtilities.dp(10), x + maxPhotoWidth, AndroidUtilities.dp(62)); if (hasPhoto) { TLRPC.PhotoSize currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(message.photoThumbs, maxPhotoWidth, true); TLRPC.PhotoSize currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(message.photoThumbs, 80); if (currentPhotoObjectThumb == currentPhotoObject) { currentPhotoObjectThumb = null; } currentPhotoObject.size = -1; if (currentPhotoObjectThumb != null) { currentPhotoObjectThumb.size = -1; } linkImageView.setImageCoords(x, AndroidUtilities.dp(10), maxPhotoWidth, maxPhotoWidth); String fileName = FileLoader.getAttachFileName(currentPhotoObject); boolean photoExist = true; File cacheFile = FileLoader.getPathToAttach(currentPhotoObject, true); if (!cacheFile.exists()) { photoExist = false; } String filter = String.format(Locale.US, "%d_%d", maxPhotoWidth, maxPhotoWidth); if (photoExist || MediaController.getInstance().canDownloadMedia(MediaController.AUTODOWNLOAD_MASK_PHOTO) || FileLoader.getInstance().isLoadingFile(fileName)) { linkImageView.setImage(currentPhotoObject.location, filter, currentPhotoObjectThumb != null ? currentPhotoObjectThumb.location : null, String.format(Locale.US, "%d_%d_b", maxPhotoWidth, maxPhotoWidth), 0, null, false); } else { if (currentPhotoObjectThumb != null) { linkImageView.setImage(null, null, currentPhotoObjectThumb.location, String.format(Locale.US, "%d_%d_b", maxPhotoWidth, maxPhotoWidth), 0, null, false); } else { linkImageView.setImageBitmap((Drawable) null); } } drawLinkImageView = true; } int height = 0; if (titleLayout != null && titleLayout.getLineCount() != 0) { height += titleLayout.getLineBottom(titleLayout.getLineCount() - 1); } if (descriptionLayout != null && descriptionLayout.getLineCount() != 0) { height += descriptionLayout.getLineBottom(descriptionLayout.getLineCount() - 1); } if (descriptionLayout2 != null && descriptionLayout2.getLineCount() != 0) { height += descriptionLayout2.getLineBottom(descriptionLayout2.getLineCount() - 1); if (descriptionLayout != null) { height += AndroidUtilities.dp(10); } } for (int a = 0; a < linkLayout.size(); a++) { StaticLayout layout = linkLayout.get(a); if (layout.getLineCount() > 0) { height += layout.getLineBottom(layout.getLineCount() - 1); } } if (hasPhoto) { height = Math.max(AndroidUtilities.dp(48), height); } checkBox.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(22), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(22), MeasureSpec.EXACTLY)); setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), Math.max(AndroidUtilities.dp(72), height + AndroidUtilities.dp(16)) + (needDivider ? 1 : 0)); } public void setLink(MessageObject messageObject, boolean divider) { needDivider = divider; resetPressedLink(); message = messageObject; requestLayout(); } public void setDelegate(SharedLinkCellDelegate sharedLinkCellDelegate) { delegate = sharedLinkCellDelegate; } public MessageObject getMessage() { return message; } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (drawLinkImageView) { linkImageView.onDetachedFromWindow(); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (drawLinkImageView) { linkImageView.onAttachedToWindow(); } } @Override public boolean onTouchEvent(MotionEvent event) { boolean result = false; if (message != null && !linkLayout.isEmpty() && delegate != null && delegate.canPerformActions()) { if (event.getAction() == MotionEvent.ACTION_DOWN || linkPreviewPressed && event.getAction() == MotionEvent.ACTION_UP) { int x = (int) event.getX(); int y = (int) event.getY(); int offset = 0; boolean ok = false; for (int a = 0; a < linkLayout.size(); a++) { StaticLayout layout = linkLayout.get(a); if (layout.getLineCount() > 0) { int height = layout.getLineBottom(layout.getLineCount() - 1); int linkPosX = AndroidUtilities .dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline); if (x >= linkPosX + layout.getLineLeft(0) && x <= linkPosX + layout.getLineWidth(0) && y >= linkY + offset && y <= linkY + offset + height) { ok = true; if (event.getAction() == MotionEvent.ACTION_DOWN) { resetPressedLink(); pressedLink = a; linkPreviewPressed = true; try { urlPath.setCurrentLayout(layout, 0, 0); layout.getSelectionPath(0, layout.getText().length(), urlPath); } catch (Exception e) { FileLog.e("tmessages", e); } result = true; } else if (linkPreviewPressed) { try { TLRPC.WebPage webPage = pressedLink == 0 && message.messageOwner.media != null ? message.messageOwner.media.webpage : null; if (webPage != null && Build.VERSION.SDK_INT >= 16 && webPage.embed_url != null && webPage.embed_url.length() != 0) { delegate.needOpenWebView(webPage); } else { Browser.openUrl(getContext(), links.get(pressedLink)); } } catch (Exception e) { FileLog.e("tmessages", e); } resetPressedLink(); result = true; } break; } offset += height; } } if (!ok) { resetPressedLink(); } } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { resetPressedLink(); } } else { resetPressedLink(); } return result || super.onTouchEvent(event); } public String getLink(int num) { if (num < 0 || num >= links.size()) { return null; } return links.get(num); } protected void resetPressedLink() { pressedLink = -1; linkPreviewPressed = false; invalidate(); } public void setChecked(boolean checked, boolean animated) { if (checkBox.getVisibility() != VISIBLE) { checkBox.setVisibility(VISIBLE); } checkBox.setChecked(checked, animated); } @Override protected void onDraw(Canvas canvas) { if (titleLayout != null) { canvas.save(); canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline), titleY); titleLayout.draw(canvas); canvas.restore(); } if (descriptionLayout != null) { descriptionTextPaint.setColor(TEXT_COLOR); canvas.save(); canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline), descriptionY); descriptionLayout.draw(canvas); canvas.restore(); } if (descriptionLayout2 != null) { descriptionTextPaint.setColor(TEXT_COLOR); canvas.save(); canvas.translate(AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline), description2Y); descriptionLayout2.draw(canvas); canvas.restore(); } if (!linkLayout.isEmpty()) { descriptionTextPaint.setColor(Theme.MSG_LINK_TEXT_COLOR); int offset = 0; for (int a = 0; a < linkLayout.size(); a++) { StaticLayout layout = linkLayout.get(a); if (layout.getLineCount() > 0) { canvas.save(); canvas.translate( AndroidUtilities.dp(LocaleController.isRTL ? 8 : AndroidUtilities.leftBaseline), linkY + offset); if (pressedLink == a) { canvas.drawPath(urlPath, urlPaint); } layout.draw(canvas); canvas.restore(); offset += layout.getLineBottom(layout.getLineCount() - 1); } } } letterDrawable.draw(canvas); if (drawLinkImageView) { linkImageView.draw(canvas); } if (needDivider) { if (LocaleController.isRTL) { canvas.drawLine(0, getMeasuredHeight() - 1, getMeasuredWidth() - AndroidUtilities.dp(AndroidUtilities.leftBaseline), getMeasuredHeight() - 1, paint); } else { canvas.drawLine(AndroidUtilities.dp(AndroidUtilities.leftBaseline), getMeasuredHeight() - 1, getMeasuredWidth(), getMeasuredHeight() - 1, paint); } } } public static void resetDivider() { titleTextPaint = null; } }