/* * This is the source code of Telegram for Android v. 5.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-2018. */ package org.telegram.ui; import static org.telegram.messenger.MessageObject.POSITION_FLAG_BOTTOM; import static org.telegram.messenger.MessageObject.POSITION_FLAG_LEFT; import static org.telegram.messenger.MessageObject.POSITION_FLAG_RIGHT; import static org.telegram.messenger.MessageObject.POSITION_FLAG_TOP; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.IntEvaluator; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.database.DataSetObserver; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Typeface; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.text.Editable; import android.text.Layout; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; import android.text.TextWatcher; import android.text.style.MetricAffectingSpan; import android.text.style.URLSpan; import android.util.Property; import android.util.SparseArray; import android.util.TypedValue; import android.view.ActionMode; import android.view.DisplayCutout; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.SoundEffectConstants; import android.view.TextureView; import android.view.VelocityTracker; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.WindowInsets; import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.DecelerateInterpolator; import android.view.inputmethod.EditorInfo; import android.webkit.CookieManager; import android.webkit.JavascriptInterface; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.FrameLayout; import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.Keep; import androidx.annotation.NonNull; import androidx.collection.LongSparseArray; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManagerFixed; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.ViewPager; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import org.json.JSONObject; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.DownloadController; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.ImageLoader; import org.telegram.messenger.ImageLocation; import org.telegram.messenger.ImageReceiver; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaController; import org.telegram.messenger.MessageObject; import org.telegram.messenger.MessagesController; import org.telegram.messenger.MessagesStorage; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.UserConfig; import org.telegram.messenger.Utilities; import org.telegram.messenger.WebFile; import org.telegram.messenger.browser.Browser; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.ActionBar; import org.telegram.ui.ActionBar.ActionBarMenuItem; import org.telegram.ui.ActionBar.ActionBarPopupWindow; import org.telegram.ui.ActionBar.BackDrawable; import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.SimpleTextView; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.Cells.HeaderCell; import org.telegram.ui.Cells.TextSelectionHelper; import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.AnchorSpan; import org.telegram.ui.Components.AnimatedArrowDrawable; import org.telegram.ui.Components.AnimationProperties; import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.CloseProgressDrawable2; import org.telegram.ui.Components.CombinedDrawable; import org.telegram.ui.Components.ContextProgressView; import org.telegram.ui.Components.CubicBezierInterpolator; import org.telegram.ui.Components.EditTextBoldCursor; import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LineProgressView; import org.telegram.ui.Components.LinkPath; import org.telegram.ui.Components.MediaActionDrawable; import org.telegram.ui.Components.RadialProgress2; import org.telegram.ui.Components.RadioButton; import org.telegram.ui.Components.RecyclerListView; import org.telegram.ui.Components.SeekBar; import org.telegram.ui.Components.SeekBarView; import org.telegram.ui.Components.ShareAlert; import org.telegram.ui.Components.StaticLayoutEx; import org.telegram.ui.Components.TableLayout; import org.telegram.ui.Components.TextPaintImageReceiverSpan; import org.telegram.ui.Components.TextPaintMarkSpan; import org.telegram.ui.Components.TextPaintSpan; import org.telegram.ui.Components.TextPaintUrlSpan; import org.telegram.ui.Components.TextPaintWebpageUrlSpan; import org.telegram.ui.Components.TypefaceSpan; import org.telegram.ui.Components.WebPlayerView; import java.io.File; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; public class ArticleViewer implements NotificationCenter.NotificationCenterDelegate { private Activity parentActivity; private BaseFragment parentFragment; private ArrayList createdWebViews = new ArrayList<>(); private View customView; private FrameLayout fullscreenVideoContainer; private TextureView fullscreenTextureView; private AspectRatioFrameLayout fullscreenAspectRatioView; private WebChromeClient.CustomViewCallback customViewCallback; private TLRPC.Chat loadedChannel; private boolean loadingChannel; private Object lastInsets; private boolean hasCutout; private boolean isVisible; private boolean collapsed; private boolean attachedToWindow; private int currentAccount; private int lastBlockNum = 1; private int animationInProgress; private Runnable animationEndRunnable; private long transitionAnimationStartTime; private DecelerateInterpolator interpolator = new DecelerateInterpolator(1.5f); private ArrayList pagesStack = new ArrayList<>(); private WindowManager.LayoutParams windowLayoutParams; private WindowView windowView; private FrameLayout containerView; private FrameLayout headerView; private SimpleTextView titleTextView; private LineProgressView lineProgressView; private Runnable lineProgressTickRunnable; private ImageView backButton; private ActionBarMenuItem menuButton; private FrameLayout menuContainer; private ContextProgressView progressView; private BackDrawable backDrawable; private Dialog visibleDialog; private Paint backgroundPaint; private Drawable layerShadowDrawable; private Paint scrimPaint; private AnimatorSet progressViewAnimation; private AnimatorSet runAfterKeyboardClose; private boolean keyboardVisible; private FrameLayout searchContainer; private ImageView clearButton; private EditTextBoldCursor searchField; private boolean animateClear = true; private boolean ignoreOnTextChange; private View searchShadow; private FrameLayout searchPanel; private ImageView searchUpButton; private ImageView searchDownButton; private SimpleTextView searchCountText; private RecyclerListView[] listView; private LinearLayoutManager[] layoutManager; private WebpageAdapter[] adapter; private AnimatorSet pageSwitchAnimation; private Paint headerPaint = new Paint(); private Paint statusBarPaint = new Paint(); private Paint headerProgressPaint = new Paint(); private ActionBarPopupWindow popupWindow; private ActionBarPopupWindow.ActionBarPopupWindowLayout popupLayout; private TextView deleteView; private Rect popupRect; private WebPlayerView currentPlayingVideo; private WebPlayerView fullscreenedVideo; private Drawable slideDotDrawable; private Drawable slideDotBigDrawable; private int openUrlReqId; private int previewsReqId; private int lastReqId; private int currentHeaderHeight; private boolean checkingForLongPress = false; private CheckForLongPress pendingCheckForLongPress = null; private int pressCount = 0; private CheckForTap pendingCheckForTap = null; private TextPaintUrlSpan pressedLink; private BottomSheet linkSheet; private int pressedLayoutY; private DrawingText pressedLinkOwnerLayout; private View pressedLinkOwnerView; private boolean drawBlockSelection; private LinkPath urlPath = new LinkPath(); private static final int search_item = 1; private static final int share_item = 2; private static final int open_item = 3; private static final int settings_item = 4; private int anchorsOffsetMeasuredWidth; TextSelectionHelper.ArticleTextSelectionHelper textSelectionHelper; TextSelectionHelper.ArticleTextSelectionHelper textSelectionHelperBottomSheet; PinchToZoomHelper pinchToZoomHelper; private int allowAnimationIndex = -1; private final String BOTTOM_SHEET_VIEW_TAG = "bottomSheet"; @SuppressLint("StaticFieldLeak") private static volatile ArticleViewer Instance = null; public static ArticleViewer getInstance() { ArticleViewer localInstance = Instance; if (localInstance == null) { synchronized (ArticleViewer.class) { localInstance = Instance; if (localInstance == null) { Instance = localInstance = new ArticleViewer(); } } } return localInstance; } public static boolean hasInstance() { return Instance != null; } private int selectedFont = 0; private FontCell[] fontCells = new FontCell[2]; private static class TL_pageBlockRelatedArticlesChild extends TLRPC.PageBlock { private TLRPC.TL_pageBlockRelatedArticles parent; private int num; } private static class TL_pageBlockRelatedArticlesShadow extends TLRPC.PageBlock { private TLRPC.TL_pageBlockRelatedArticles parent; } private static class TL_pageBlockDetailsChild extends TLRPC.PageBlock { private TLRPC.PageBlock parent; private TLRPC.PageBlock block; } private static class TL_pageBlockDetailsBottom extends TLRPC.PageBlock { private TLRPC.TL_pageBlockDetails parent; } private class TL_pageBlockListParent extends TLRPC.PageBlock { private TLRPC.TL_pageBlockList pageBlockList; private ArrayList items = new ArrayList<>(); private int maxNumWidth; private int lastMaxNumCalcWidth; private int lastFontSize; private int level; } private class TL_pageBlockListItem extends TLRPC.PageBlock { private TL_pageBlockListParent parent; private TLRPC.PageBlock blockItem; private TLRPC.RichText textItem; private String num; private DrawingText numLayout; private int index = Integer.MAX_VALUE; } private class TL_pageBlockOrderedListParent extends TLRPC.PageBlock { private TLRPC.TL_pageBlockOrderedList pageBlockOrderedList; private ArrayList items = new ArrayList<>(); private int maxNumWidth; private int lastMaxNumCalcWidth; private int lastFontSize; private int level; } private class TL_pageBlockOrderedListItem extends TLRPC.PageBlock { private TL_pageBlockOrderedListParent parent; private TLRPC.PageBlock blockItem; private TLRPC.RichText textItem; private String num; private DrawingText numLayout; private int index = Integer.MAX_VALUE; } private static class TL_pageBlockEmbedPostCaption extends TLRPC.TL_pageBlockEmbedPost { private TLRPC.TL_pageBlockEmbedPost parent; } public class DrawingText implements TextSelectionHelper.TextLayoutBlock { public StaticLayout textLayout; public LinkPath textPath; public LinkPath markPath; public LinkPath searchPath; public int searchIndex = -1; public TLRPC.PageBlock parentBlock; public Object parentText; public int x; public int y; public int row; public CharSequence prefix; public void draw(Canvas canvas) { if (!searchResults.isEmpty()) { SearchResult result = searchResults.get(currentSearchIndex); if (result.block == parentBlock && (result.text == parentText || result.text instanceof String && parentText == null)) { if (searchIndex != result.index) { searchPath = new LinkPath(true); searchPath.setAllowReset(false); searchPath.setCurrentLayout(textLayout, result.index, 0); searchPath.setBaselineShift(0); textLayout.getSelectionPath(result.index, result.index + searchText.length(), searchPath); searchPath.setAllowReset(true); } } else { searchIndex = -1; searchPath = null; } } else { searchIndex = -1; searchPath = null; } if (searchPath != null) { canvas.drawPath(searchPath, webpageSearchPaint); } if (textPath != null) { canvas.drawPath(textPath, webpageUrlPaint); } if (markPath != null) { canvas.drawPath(markPath, webpageMarkPaint); } drawLayoutLink(canvas, this); textLayout.draw(canvas); } public CharSequence getText() { return textLayout.getText(); } public int getLineCount() { return textLayout.getLineCount(); } public int getLineAscent(int line) { return textLayout.getLineAscent(line); } public float getLineLeft(int line) { return textLayout.getLineLeft(line); } public float getLineWidth(int line) { return textLayout.getLineWidth(line); } public int getHeight() { return textLayout.getHeight(); } public int getWidth() { return textLayout.getWidth(); } @Override public StaticLayout getLayout() { return textLayout; } @Override public int getX() { return x; } @Override public int getY() { return y; } @Override public int getRow() { return row; } @Override public CharSequence getPrefix() { return prefix; } } private class TextSizeCell extends FrameLayout { private SeekBarView sizeBar; private int startFontSize = 12; private int endFontSize = 30; private int lastWidth; private TextPaint textPaint; public TextSizeCell(Context context) { super(context); setWillNotDraw(false); textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); textPaint.setTextSize(AndroidUtilities.dp(16)); sizeBar = new SeekBarView(context); sizeBar.setReportChanges(true); sizeBar.setDelegate(new SeekBarView.SeekBarViewDelegate() { @Override public void onSeekBarDrag(boolean stop, float progress) { int fontSize = Math.round(startFontSize + (endFontSize - startFontSize) * progress); if (fontSize != SharedConfig.ivFontSize) { SharedConfig.ivFontSize = fontSize; SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences.Editor editor = preferences.edit(); editor.putInt("iv_font_size", SharedConfig.ivFontSize); editor.commit(); adapter[0].searchTextOffset.clear(); updatePaintSize(); invalidate(); } } @Override public void onSeekBarPressed(boolean pressed) { } @Override public CharSequence getContentDescription() { return String.valueOf(Math.round(startFontSize + (endFontSize - startFontSize) * sizeBar.getProgress())); } @Override public int getStepsCount() { return endFontSize - startFontSize; } }); addView(sizeBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 38, Gravity.LEFT | Gravity.TOP, 5, 5, 39, 0)); } @Override protected void onDraw(Canvas canvas) { textPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteValueText)); canvas.drawText("" + SharedConfig.ivFontSize, getMeasuredWidth() - AndroidUtilities.dp(39), AndroidUtilities.dp(28), textPaint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int w = MeasureSpec.getSize(widthMeasureSpec); if (lastWidth != w) { sizeBar.setProgress((SharedConfig.ivFontSize - startFontSize) / (float) (endFontSize - startFontSize)); lastWidth = w; } } @Override public void invalidate() { super.invalidate(); sizeBar.invalidate(); } } public static class FontCell extends FrameLayout { private TextView textView; private RadioButton radioButton; public FontCell(Context context) { super(context); setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), 2)); radioButton = new RadioButton(context); radioButton.setSize(AndroidUtilities.dp(20)); radioButton.setColor(Theme.getColor(Theme.key_dialogRadioBackground), Theme.getColor(Theme.key_dialogRadioBackgroundChecked)); addView(radioButton, LayoutHelper.createFrame(22, 22, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 0 : 22), 13, (LocaleController.isRTL ? 22 : 0), 0)); textView = new TextView(context); textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); textView.setLines(1); textView.setMaxLines(1); textView.setSingleLine(true); textView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, (LocaleController.isRTL ? 17 : 17 + 45), 0, (LocaleController.isRTL ? 17 + 45 : 17), 0)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(48), MeasureSpec.EXACTLY)); } public void select(boolean value, boolean animated) { radioButton.setChecked(value, animated); } public void setTextAndTypeface(String text, Typeface typeface) { textView.setText(text); textView.setTypeface(typeface); setContentDescription(text); invalidate(); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setClassName(RadioButton.class.getName()); info.setChecked(radioButton.isChecked()); info.setCheckable(true); } } private final class CheckForTap implements Runnable { public void run() { if (pendingCheckForLongPress == null) { pendingCheckForLongPress = new CheckForLongPress(); } pendingCheckForLongPress.currentPressCount = ++pressCount; if (windowView != null) { windowView.postDelayed(pendingCheckForLongPress, ViewConfiguration.getLongPressTimeout() - ViewConfiguration.getTapTimeout()); } } } public static final Property ARTICLE_VIEWER_INNER_TRANSLATION_X = new AnimationProperties.FloatProperty("innerTranslationX") { @Override public void setValue(WindowView object, float value) { object.setInnerTranslationX(value); } @Override public Float get(WindowView object) { return object.getInnerTranslationX(); } }; private boolean closeAnimationInProgress; private class WindowView extends FrameLayout { private final Paint blackPaint = new Paint(); private Runnable attachRunnable; private boolean selfLayout; private int startedTrackingPointerId; private boolean maybeStartTracking; private boolean startedTracking; private boolean movingPage; private int startMovingHeaderHeight; private int startedTrackingX; private int startedTrackingY; private VelocityTracker tracker; private float innerTranslationX; private float alpha; private int bX, bWidth, bY, bHeight; public WindowView(Context context) { super(context); } @TargetApi(21) @Override public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { WindowInsets oldInsets = (WindowInsets) lastInsets; lastInsets = insets; if (oldInsets == null || !oldInsets.toString().equals(insets.toString())) { if (windowView != null) { windowView.requestLayout(); } } if (Build.VERSION.SDK_INT >= 28 && parentActivity != null) { DisplayCutout cutout = parentActivity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout(); if (cutout != null) { List rects = cutout.getBoundingRects(); if (rects != null && !rects.isEmpty()) { hasCutout = rects.get(0).height() != 0; } } } return super.dispatchApplyWindowInsets(insets); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = View.MeasureSpec.getSize(widthMeasureSpec); int heightSize = View.MeasureSpec.getSize(heightMeasureSpec); if (Build.VERSION.SDK_INT >= 21 && lastInsets != null) { setMeasuredDimension(widthSize, heightSize); WindowInsets insets = (WindowInsets) lastInsets; if (AndroidUtilities.incorrectDisplaySizeFix) { if (heightSize > AndroidUtilities.displaySize.y) { heightSize = AndroidUtilities.displaySize.y; } heightSize += AndroidUtilities.statusBarHeight; } heightSize -= insets.getSystemWindowInsetBottom(); widthSize -= insets.getSystemWindowInsetRight() + insets.getSystemWindowInsetLeft(); if (insets.getSystemWindowInsetRight() != 0) { bWidth = insets.getSystemWindowInsetRight(); bHeight = heightSize; } else if (insets.getSystemWindowInsetLeft() != 0) { bWidth = insets.getSystemWindowInsetLeft(); bHeight = heightSize; } else { bWidth = widthSize; bHeight = insets.getStableInsetBottom(); } heightSize -= insets.getSystemWindowInsetTop(); } else { setMeasuredDimension(widthSize, heightSize); } menuButton.setAdditionalYOffset(-(currentHeaderHeight - AndroidUtilities.dp(56)) / 2 + (Build.VERSION.SDK_INT < 21 ? AndroidUtilities.statusBarHeight : 0)); keyboardVisible = heightSize < AndroidUtilities.displaySize.y - AndroidUtilities.dp(100); containerView.measure(View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(heightSize, View.MeasureSpec.EXACTLY)); fullscreenVideoContainer.measure(View.MeasureSpec.makeMeasureSpec(widthSize, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(heightSize, View.MeasureSpec.EXACTLY)); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (pinchToZoomHelper.isInOverlayMode()) { ev.offsetLocation(-containerView.getX(), -containerView.getY()); return pinchToZoomHelper.onTouchEvent(ev); } TextSelectionHelper.TextSelectionOverlay selectionOverlay = textSelectionHelper.getOverlayView(getContext()); MotionEvent textSelectionEv = MotionEvent.obtain(ev); textSelectionEv.offsetLocation(-containerView.getX(), -containerView.getY()); if (textSelectionHelper.isSelectionMode() && textSelectionHelper.getOverlayView(getContext()).onTouchEvent(textSelectionEv)) { return true; } if (selectionOverlay.checkOnTap(ev)) { ev.setAction(MotionEvent.ACTION_CANCEL); } if (ev.getAction() == MotionEvent.ACTION_DOWN && textSelectionHelper.isSelectionMode() && (ev.getY() < containerView.getTop() || ev.getY() > containerView.getBottom())) { if (textSelectionHelper.getOverlayView(getContext()).onTouchEvent(textSelectionEv)) { return super.dispatchTouchEvent(ev); } else { return true; } } return super.dispatchTouchEvent(ev); } @SuppressWarnings("DrawAllocation") @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (selfLayout) { return; } int width = right - left; if (anchorsOffsetMeasuredWidth != width) { for (int i = 0; i < listView.length; i++) { for (HashMap.Entry entry : adapter[i].anchorsOffset.entrySet()) { entry.setValue(-1); } } anchorsOffsetMeasuredWidth = width; } int x; int y = 0; if (Build.VERSION.SDK_INT >= 21 && lastInsets != null) { WindowInsets insets = (WindowInsets) lastInsets; x = insets.getSystemWindowInsetLeft(); if (insets.getSystemWindowInsetRight() != 0) { bX = width - bWidth; bY = 0; } else if (insets.getSystemWindowInsetLeft() != 0) { bX = 0; bY = 0; } else { bX = 0; bY = bottom - top - bHeight; } y += insets.getSystemWindowInsetTop(); } else { x = 0; } containerView.layout(x, y, x + containerView.getMeasuredWidth(), y + containerView.getMeasuredHeight()); fullscreenVideoContainer.layout(x, y, x + fullscreenVideoContainer.getMeasuredWidth(), y + fullscreenVideoContainer.getMeasuredHeight()); if (runAfterKeyboardClose != null) { runAfterKeyboardClose.start(); runAfterKeyboardClose = null; } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); attachedToWindow = true; } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); attachedToWindow = false; } @Override public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { handleTouchEvent(null); super.requestDisallowInterceptTouchEvent(disallowIntercept); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return !collapsed && (handleTouchEvent(ev) || super.onInterceptTouchEvent(ev)); } @Override public boolean onTouchEvent(MotionEvent event) { return !collapsed && (handleTouchEvent(event) || super.onTouchEvent(event)); } @Keep public void setInnerTranslationX(float value) { innerTranslationX = value; if (parentActivity instanceof LaunchActivity) { ((LaunchActivity) parentActivity).drawerLayoutContainer.setAllowDrawContent(!isVisible || alpha != 1.0f || innerTranslationX != 0); } invalidate(); } @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { int width = getMeasuredWidth(); int translationX = (int) innerTranslationX; final int restoreCount = canvas.save(); canvas.clipRect(translationX, 0, width, getHeight()); final boolean result = super.drawChild(canvas, child, drawingTime); canvas.restoreToCount(restoreCount); if (translationX != 0 && child == containerView) { float opacity = Math.min(0.8f, (width - translationX) / (float) width); if (opacity < 0) { opacity = 0; } scrimPaint.setColor((int) (((0x99000000 & 0xff000000) >>> 24) * opacity) << 24); canvas.drawRect(0, 0, translationX, getHeight(), scrimPaint); final float alpha = Math.max(0, Math.min((width - translationX) / (float) AndroidUtilities.dp(20), 1.0f)); layerShadowDrawable.setBounds(translationX - layerShadowDrawable.getIntrinsicWidth(), child.getTop(), translationX, child.getBottom()); layerShadowDrawable.setAlpha((int) (0xff * alpha)); layerShadowDrawable.draw(canvas); } return result; } @Keep public float getInnerTranslationX() { return innerTranslationX; } private void prepareForMoving(MotionEvent ev) { maybeStartTracking = false; startedTracking = true; startedTrackingX = (int) ev.getX(); if (pagesStack.size() > 1) { movingPage = true; startMovingHeaderHeight = currentHeaderHeight; listView[1].setVisibility(VISIBLE); listView[1].setAlpha(1.0f); listView[1].setTranslationX(0.0f); listView[0].setBackgroundColor(backgroundPaint.getColor()); updateInterfaceForCurrentPage(pagesStack.get(pagesStack.size() - 2), true, -1); } else { movingPage = false; } cancelCheckLongPress(); } public boolean handleTouchEvent(MotionEvent event) { if (pageSwitchAnimation == null && !closeAnimationInProgress && fullscreenVideoContainer.getVisibility() != VISIBLE && !textSelectionHelper.isSelectionMode()) { if (event != null && event.getAction() == MotionEvent.ACTION_DOWN && !startedTracking && !maybeStartTracking) { startedTrackingPointerId = event.getPointerId(0); maybeStartTracking = true; startedTrackingX = (int) event.getX(); startedTrackingY = (int) event.getY(); if (tracker != null) { tracker.clear(); } } else if (event != null && event.getAction() == MotionEvent.ACTION_MOVE && event.getPointerId(0) == startedTrackingPointerId) { if (tracker == null) { tracker = VelocityTracker.obtain(); } int dx = Math.max(0, (int) (event.getX() - startedTrackingX)); int dy = Math.abs((int) event.getY() - startedTrackingY); tracker.addMovement(event); if (maybeStartTracking && !startedTracking && dx >= AndroidUtilities.getPixelsInCM(0.4f, true) && Math.abs(dx) / 3 > dy) { prepareForMoving(event); } else if (startedTracking) { pressedLinkOwnerLayout = null; pressedLinkOwnerView = null; if (movingPage) { listView[0].setTranslationX(dx); } else { containerView.setTranslationX(dx); setInnerTranslationX(dx); } } } else if (event != null && event.getPointerId(0) == startedTrackingPointerId && (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_POINTER_UP)) { if (tracker == null) { tracker = VelocityTracker.obtain(); } tracker.computeCurrentVelocity(1000); float velX = tracker.getXVelocity(); float velY = tracker.getYVelocity(); if (!startedTracking && velX >= 3500 && velX > Math.abs(velY)) { prepareForMoving(event); } if (startedTracking) { View movingView = movingPage ? listView[0] : containerView; float x = movingView.getX(); final boolean backAnimation = x < movingView.getMeasuredWidth() / 3.0f && (velX < 3500 || velX < velY); float distToMove; AnimatorSet animatorSet = new AnimatorSet(); if (!backAnimation) { distToMove = movingView.getMeasuredWidth() - x; if (movingPage) { animatorSet.playTogether( ObjectAnimator.ofFloat(listView[0], View.TRANSLATION_X, movingView.getMeasuredWidth()) ); } else { animatorSet.playTogether( ObjectAnimator.ofFloat(containerView, View.TRANSLATION_X, movingView.getMeasuredWidth()), ObjectAnimator.ofFloat(this, ARTICLE_VIEWER_INNER_TRANSLATION_X, (float) movingView.getMeasuredWidth()) ); } } else { distToMove = x; if (movingPage) { animatorSet.playTogether( ObjectAnimator.ofFloat(listView[0], View.TRANSLATION_X, 0) ); } else { animatorSet.playTogether( ObjectAnimator.ofFloat(containerView, View.TRANSLATION_X, 0), ObjectAnimator.ofFloat(this, ARTICLE_VIEWER_INNER_TRANSLATION_X, 0.0f) ); } } animatorSet.setDuration(Math.max((int) (200.0f / movingView.getMeasuredWidth() * distToMove), 50)); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animator) { if (movingPage) { listView[0].setBackgroundDrawable(null); if (!backAnimation) { WebpageAdapter adapterToUpdate = adapter[1]; adapter[1] = adapter[0]; adapter[0] = adapterToUpdate; RecyclerListView listToUpdate = listView[1]; listView[1] = listView[0]; listView[0] = listToUpdate; LinearLayoutManager layoutManagerToUpdate = layoutManager[1]; layoutManager[1] = layoutManager[0]; layoutManager[0] = layoutManagerToUpdate; pagesStack.remove(pagesStack.size() - 1); textSelectionHelper.setParentView(listView[0]); textSelectionHelper.layoutManager = layoutManager[0]; titleTextView.setText(adapter[0].currentPage.site_name == null ? "" : adapter[0].currentPage.site_name); textSelectionHelper.clear(true); headerView.invalidate(); } listView[1].setVisibility(GONE); headerView.invalidate(); } else { if (!backAnimation) { saveCurrentPagePosition(); onClosed(); } } movingPage = false; startedTracking = false; closeAnimationInProgress = false; } }); animatorSet.start(); closeAnimationInProgress = true; } else { maybeStartTracking = false; startedTracking = false; movingPage = false; } if (tracker != null) { tracker.recycle(); tracker = null; } } else if (event == null) { maybeStartTracking = false; startedTracking = false; movingPage = false; if (tracker != null) { tracker.recycle(); tracker = null; } if (textSelectionHelper != null && !textSelectionHelper.isSelectionMode()) { textSelectionHelper.clear(); } } return startedTracking; } return false; } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (bWidth != 0 && bHeight != 0) { blackPaint.setAlpha((int) (255 * windowView.getAlpha())); if (bX == 0 && bY == 0) { canvas.drawRect(bX, bY, bX + bWidth, bY + bHeight, blackPaint); } else { canvas.drawRect(bX - getTranslationX(), bY, bX + bWidth - getTranslationX(), bY + bHeight, blackPaint); } } } @Override protected void onDraw(Canvas canvas) { int w = getMeasuredWidth(); int h = getMeasuredHeight(); canvas.drawRect(innerTranslationX, 0, w, h, backgroundPaint); if (Build.VERSION.SDK_INT >= 21 && lastInsets != null) { WindowInsets insets = (WindowInsets) lastInsets; canvas.drawRect(innerTranslationX, 0, w, insets.getSystemWindowInsetTop(), statusBarPaint); if (hasCutout) { int left = insets.getSystemWindowInsetLeft(); if (left != 0) { canvas.drawRect(0, 0, left, h, statusBarPaint); } int right = insets.getSystemWindowInsetRight(); if (right != 0) { canvas.drawRect(getMeasuredWidth() - right, 0, getMeasuredWidth(), h, statusBarPaint); } } } } @Keep @Override public void setAlpha(float value) { backgroundPaint.setAlpha((int) (255 * value)); statusBarPaint.setAlpha((int) (255 * value)); alpha = value; if (parentActivity instanceof LaunchActivity) { ((LaunchActivity) parentActivity).drawerLayoutContainer.setAllowDrawContent(!isVisible || alpha != 1.0f || innerTranslationX != 0); } invalidate(); } @Keep @Override public float getAlpha() { return alpha; } @Override public boolean dispatchKeyEventPreIme(KeyEvent event) { if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) { if (searchField.isFocused()) { searchField.clearFocus(); AndroidUtilities.hideKeyboard(searchField); } else { close(true, false); } return true; } return super.dispatchKeyEventPreIme(event); } } class CheckForLongPress implements Runnable { public int currentPressCount; public void run() { if (checkingForLongPress && windowView != null) { checkingForLongPress = false; if (pressedLink != null) { windowView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); showCopyPopup(pressedLink.getUrl()); pressedLink = null; pressedLinkOwnerLayout = null; if (pressedLinkOwnerView != null) { pressedLinkOwnerView.invalidate(); } } else if (pressedLinkOwnerView != null && textSelectionHelper.isSelectable(pressedLinkOwnerView)) { if (pressedLinkOwnerView.getTag() != null && pressedLinkOwnerView.getTag() == BOTTOM_SHEET_VIEW_TAG && textSelectionHelperBottomSheet != null) { textSelectionHelperBottomSheet.trySelect(pressedLinkOwnerView); } else { textSelectionHelper.trySelect(pressedLinkOwnerView); } if (textSelectionHelper.isSelectionMode()) { windowView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); } } else if (pressedLinkOwnerLayout != null && pressedLinkOwnerView != null) { windowView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); int[] location = new int[2]; pressedLinkOwnerView.getLocationInWindow(location); int y = location[1] + pressedLayoutY - AndroidUtilities.dp(54); if (y < 0) { y = 0; } pressedLinkOwnerView.invalidate(); drawBlockSelection = true; showPopup(pressedLinkOwnerView, Gravity.TOP, 0, y); listView[0].setLayoutFrozen(true); listView[0].setLayoutFrozen(false); } } } } private void createPaint(boolean update) { if (quoteLinePaint == null) { quoteLinePaint = new Paint(); preformattedBackgroundPaint = new Paint(); tableLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); tableLinePaint.setStyle(Paint.Style.STROKE); tableLinePaint.setStrokeWidth(AndroidUtilities.dp(1)); tableHalfLinePaint = new Paint(); tableHalfLinePaint.setStyle(Paint.Style.STROKE); tableHalfLinePaint.setStrokeWidth(AndroidUtilities.dp(1) / 2.0f); tableHeaderPaint = new Paint(); tableStripPaint = new Paint(); urlPaint = new Paint(); webpageUrlPaint = new Paint(Paint.ANTI_ALIAS_FLAG); webpageSearchPaint = new Paint(Paint.ANTI_ALIAS_FLAG); photoBackgroundPaint = new Paint(); dividerPaint = new Paint(); webpageMarkPaint = new Paint(Paint.ANTI_ALIAS_FLAG); } else if (!update) { return; } int color2 = Theme.getColor(Theme.key_windowBackgroundWhite); float lightness = (0.2126f * Color.red(color2) + 0.7152f * Color.green(color2) + 0.0722f * Color.blue(color2)) / 255.0f; webpageSearchPaint.setColor(lightness <= 0.705f ? 0xffd1982e : 0xffffe669); webpageUrlPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteLinkSelection) & 0x33ffffff); urlPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteLinkSelection) & 0x33ffffff); tableHalfLinePaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteInputField)); tableLinePaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteInputField)); photoBackgroundPaint.setColor(0x0f000000); dividerPaint.setColor(Theme.getColor(Theme.key_divider)); webpageMarkPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteLinkSelection) & 0x33ffffff); int color = Theme.getColor(Theme.key_switchTrack); int r = Color.red(color); int g = Color.green(color); int b = Color.blue(color); tableStripPaint.setColor(Color.argb(20, r, g, b)); tableHeaderPaint.setColor(Color.argb(34, r, g, b)); color = Theme.getColor(Theme.key_windowBackgroundWhiteLinkSelection); r = Color.red(color); g = Color.green(color); b = Color.blue(color); preformattedBackgroundPaint.setColor(Color.argb(20, r, g, b)); quoteLinePaint.setColor(Theme.getColor(Theme.key_chat_inReplyLine)); } private void showCopyPopup(String urlFinal) { if (parentActivity == null) { return; } if (linkSheet != null) { linkSheet.dismiss(); linkSheet = null; } BottomSheet.Builder builder = new BottomSheet.Builder(parentActivity); builder.setTitle(urlFinal); builder.setItems(new CharSequence[]{LocaleController.getString("Open", R.string.Open), LocaleController.getString("Copy", R.string.Copy)}, (dialog, which) -> { if (parentActivity == null) { return; } if (which == 0) { int index; if ((index = urlFinal.lastIndexOf('#')) != -1) { String webPageUrl; if (!TextUtils.isEmpty(adapter[0].currentPage.cached_page.url)) { webPageUrl = adapter[0].currentPage.cached_page.url.toLowerCase(); } else { webPageUrl = adapter[0].currentPage.url.toLowerCase(); } String anchor; try { anchor = URLDecoder.decode(urlFinal.substring(index + 1), "UTF-8"); } catch (Exception ignore) { anchor = ""; } if (urlFinal.toLowerCase().contains(webPageUrl)) { if (TextUtils.isEmpty(anchor)) { layoutManager[0].scrollToPositionWithOffset(0, 0); checkScrollAnimated(); } else { scrollToAnchor(anchor); } return; } } Browser.openUrl(parentActivity, urlFinal); } else if (which == 1) { String url = urlFinal; if (url.startsWith("mailto:")) { url = url.substring(7); } else if (url.startsWith("tel:")) { url = url.substring(4); } AndroidUtilities.addToClipboard(url); } }); BottomSheet sheet = builder.create(); showDialog(sheet); } private void showPopup(View parent, int gravity, int x, int y) { if (popupWindow != null && popupWindow.isShowing()) { popupWindow.dismiss(); return; } if (popupLayout == null) { popupRect = new android.graphics.Rect(); popupLayout = new ActionBarPopupWindow.ActionBarPopupWindowLayout(parentActivity); popupLayout.setPadding(AndroidUtilities.dp(1), AndroidUtilities.dp(1), AndroidUtilities.dp(1), AndroidUtilities.dp(1)); popupLayout.setBackgroundDrawable(parentActivity.getResources().getDrawable(R.drawable.menu_copy)); popupLayout.setAnimationEnabled(false); popupLayout.setOnTouchListener((v, event) -> { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { if (popupWindow != null && popupWindow.isShowing()) { v.getHitRect(popupRect); if (!popupRect.contains((int) event.getX(), (int) event.getY())) { popupWindow.dismiss(); } } } return false; }); popupLayout.setDispatchKeyEventListener(keyEvent -> { if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK && keyEvent.getRepeatCount() == 0 && popupWindow != null && popupWindow.isShowing()) { popupWindow.dismiss(); } }); popupLayout.setShownFromBotton(false); deleteView = new TextView(parentActivity); deleteView.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), 2)); deleteView.setGravity(Gravity.CENTER_VERTICAL); deleteView.setPadding(AndroidUtilities.dp(20), 0, AndroidUtilities.dp(20), 0); deleteView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); deleteView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); deleteView.setText(LocaleController.getString("Copy", R.string.Copy).toUpperCase()); deleteView.setOnClickListener(v -> { if (pressedLinkOwnerLayout != null) { AndroidUtilities.addToClipboard(pressedLinkOwnerLayout.getText()); Toast.makeText(parentActivity, LocaleController.getString("TextCopied", R.string.TextCopied), Toast.LENGTH_SHORT).show(); } if (popupWindow != null && popupWindow.isShowing()) { popupWindow.dismiss(true); } }); popupLayout.addView(deleteView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 48)); popupWindow = new ActionBarPopupWindow(popupLayout, LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT); popupWindow.setAnimationEnabled(false); popupWindow.setAnimationStyle(R.style.PopupContextAnimation); popupWindow.setOutsideTouchable(true); popupWindow.setClippingEnabled(true); popupWindow.setInputMethodMode(ActionBarPopupWindow.INPUT_METHOD_NOT_NEEDED); popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED); popupWindow.getContentView().setFocusableInTouchMode(true); popupWindow.setOnDismissListener(() -> { if (pressedLinkOwnerView != null) { pressedLinkOwnerLayout = null; pressedLinkOwnerView.invalidate(); pressedLinkOwnerView = null; } }); } deleteView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem)); if (popupLayout != null) { popupLayout.setBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground)); } popupLayout.measure(View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST), View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(1000), View.MeasureSpec.AT_MOST)); popupWindow.setFocusable(true); popupWindow.showAtLocation(parent, gravity, x, y); popupWindow.startAnimation(); } private TLRPC.RichText getBlockCaption(TLRPC.PageBlock block, int type) { if (type == 2) { TLRPC.RichText text1 = getBlockCaption(block, 0); if (text1 instanceof TLRPC.TL_textEmpty) { text1 = null; } TLRPC.RichText text2 = getBlockCaption(block, 1); if (text2 instanceof TLRPC.TL_textEmpty) { text2 = null; } if (text1 != null && text2 == null) { return text1; } else if (text1 == null && text2 != null) { return text2; } else if (text1 != null && text2 != null) { TLRPC.TL_textPlain text3 = new TLRPC.TL_textPlain(); text3.text = " "; TLRPC.TL_textConcat textConcat = new TLRPC.TL_textConcat(); textConcat.texts.add(text1); textConcat.texts.add(text3); textConcat.texts.add(text2); return textConcat; } else { return null; } } if (block instanceof TLRPC.TL_pageBlockEmbedPost) { TLRPC.TL_pageBlockEmbedPost blockEmbedPost = (TLRPC.TL_pageBlockEmbedPost) block; if (type == 0) { return blockEmbedPost.caption.text; } else if (type == 1) { return blockEmbedPost.caption.credit; } } else if (block instanceof TLRPC.TL_pageBlockSlideshow) { TLRPC.TL_pageBlockSlideshow pageBlockSlideshow = (TLRPC.TL_pageBlockSlideshow) block; if (type == 0) { return pageBlockSlideshow.caption.text; } else if (type == 1) { return pageBlockSlideshow.caption.credit; } } else if (block instanceof TLRPC.TL_pageBlockPhoto) { TLRPC.TL_pageBlockPhoto pageBlockPhoto = (TLRPC.TL_pageBlockPhoto) block; if (type == 0) { return pageBlockPhoto.caption.text; } else if (type == 1) { return pageBlockPhoto.caption.credit; } } else if (block instanceof TLRPC.TL_pageBlockCollage) { TLRPC.TL_pageBlockCollage pageBlockCollage = (TLRPC.TL_pageBlockCollage) block; if (type == 0) { return pageBlockCollage.caption.text; } else if (type == 1) { return pageBlockCollage.caption.credit; } } else if (block instanceof TLRPC.TL_pageBlockEmbed) { TLRPC.TL_pageBlockEmbed pageBlockEmbed = (TLRPC.TL_pageBlockEmbed) block; if (type == 0) { return pageBlockEmbed.caption.text; } else if (type == 1) { return pageBlockEmbed.caption.credit; } } else if (block instanceof TLRPC.TL_pageBlockBlockquote) { TLRPC.TL_pageBlockBlockquote pageBlockBlockquote = (TLRPC.TL_pageBlockBlockquote) block; return pageBlockBlockquote.caption; } else if (block instanceof TLRPC.TL_pageBlockVideo) { TLRPC.TL_pageBlockVideo pageBlockVideo = (TLRPC.TL_pageBlockVideo) block; if (type == 0) { return pageBlockVideo.caption.text; } else if (type == 1) { return pageBlockVideo.caption.credit; } } else if (block instanceof TLRPC.TL_pageBlockPullquote) { TLRPC.TL_pageBlockPullquote pageBlockPullquote = (TLRPC.TL_pageBlockPullquote) block; return pageBlockPullquote.caption; } else if (block instanceof TLRPC.TL_pageBlockAudio) { TLRPC.TL_pageBlockAudio pageBlockAudio = (TLRPC.TL_pageBlockAudio) block; if (type == 0) { return pageBlockAudio.caption.text; } else if (type == 1) { return pageBlockAudio.caption.credit; } } else if (block instanceof TLRPC.TL_pageBlockCover) { TLRPC.TL_pageBlockCover pageBlockCover = (TLRPC.TL_pageBlockCover) block; return getBlockCaption(pageBlockCover.cover, type); } else if (block instanceof TLRPC.TL_pageBlockMap) { TLRPC.TL_pageBlockMap pageBlockMap = (TLRPC.TL_pageBlockMap) block; if (type == 0) { return pageBlockMap.caption.text; } else if (type == 1) { return pageBlockMap.caption.credit; } } return null; } private View getLastNonListCell(View view) { if (view instanceof BlockListItemCell) { BlockListItemCell cell = (BlockListItemCell) view; if (cell.blockLayout != null) { return getLastNonListCell(cell.blockLayout.itemView); } } else if (view instanceof BlockOrderedListItemCell) { BlockOrderedListItemCell cell = (BlockOrderedListItemCell) view; if (cell.blockLayout != null) { return getLastNonListCell(cell.blockLayout.itemView); } } return view; } private boolean isListItemBlock(TLRPC.PageBlock block) { return block instanceof TL_pageBlockListItem || block instanceof TL_pageBlockOrderedListItem; } private TLRPC.PageBlock getLastNonListPageBlock(TLRPC.PageBlock block) { if (block instanceof TL_pageBlockListItem) { TL_pageBlockListItem blockListItem = (TL_pageBlockListItem) block; if (blockListItem.blockItem != null) { return getLastNonListPageBlock(blockListItem.blockItem); } else { return blockListItem.blockItem; } } else if (block instanceof TL_pageBlockOrderedListItem) { TL_pageBlockOrderedListItem blockListItem = (TL_pageBlockOrderedListItem) block; if (blockListItem.blockItem != null) { return getLastNonListPageBlock(blockListItem.blockItem); } else { return blockListItem.blockItem; } } return block; } private boolean openAllParentBlocks(TL_pageBlockDetailsChild child) { TLRPC.PageBlock parentBlock = getLastNonListPageBlock(child.parent); if (parentBlock instanceof TLRPC.TL_pageBlockDetails) { TLRPC.TL_pageBlockDetails blockDetails = (TLRPC.TL_pageBlockDetails) parentBlock; if (!blockDetails.open) { blockDetails.open = true; return true; } return false; } else if (parentBlock instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild parent = (TL_pageBlockDetailsChild) parentBlock; parentBlock = getLastNonListPageBlock(parent.block); boolean opened = false; if (parentBlock instanceof TLRPC.TL_pageBlockDetails) { TLRPC.TL_pageBlockDetails blockDetails = (TLRPC.TL_pageBlockDetails) parentBlock; if (!blockDetails.open) { blockDetails.open = true; opened = true; } } return openAllParentBlocks(parent) || opened; } return false; } private TLRPC.PageBlock fixListBlock(TLRPC.PageBlock parentBlock, TLRPC.PageBlock childBlock) { if (parentBlock instanceof TL_pageBlockListItem) { TL_pageBlockListItem blockListItem = (TL_pageBlockListItem) parentBlock; blockListItem.blockItem = childBlock; return parentBlock; } else if (parentBlock instanceof TL_pageBlockOrderedListItem) { TL_pageBlockOrderedListItem blockListItem = (TL_pageBlockOrderedListItem) parentBlock; blockListItem.blockItem = childBlock; return parentBlock; } return childBlock; } private TLRPC.PageBlock wrapInTableBlock(TLRPC.PageBlock parentBlock, TLRPC.PageBlock childBlock) { if (parentBlock instanceof TL_pageBlockListItem) { TL_pageBlockListItem parent = (TL_pageBlockListItem) parentBlock; TL_pageBlockListItem item = new TL_pageBlockListItem(); item.parent = parent.parent; item.blockItem = wrapInTableBlock(parent.blockItem, childBlock); return item; } else if (parentBlock instanceof TL_pageBlockOrderedListItem) { TL_pageBlockOrderedListItem parent = (TL_pageBlockOrderedListItem) parentBlock; TL_pageBlockOrderedListItem item = new TL_pageBlockOrderedListItem(); item.parent = parent.parent; item.blockItem = wrapInTableBlock(parent.blockItem, childBlock); return item; } return childBlock; } private void updateInterfaceForCurrentPage(TLRPC.WebPage webPage, boolean previous, int order) { if (webPage == null || webPage.cached_page == null) { return; } if (!previous && order != 0) { WebpageAdapter adapterToUpdate = adapter[1]; adapter[1] = adapter[0]; adapter[0] = adapterToUpdate; RecyclerListView listToUpdate = listView[1]; listView[1] = listView[0]; listView[0] = listToUpdate; LinearLayoutManager layoutManagerToUpdate = layoutManager[1]; layoutManager[1] = layoutManager[0]; layoutManager[0] = layoutManagerToUpdate; int index1 = containerView.indexOfChild(listView[0]); int index2 = containerView.indexOfChild(listView[1]); if (order == 1) { if (index1 < index2) { containerView.removeView(listView[0]); containerView.addView(listView[0], index2); } } else { if (index2 < index1) { containerView.removeView(listView[0]); containerView.addView(listView[0], index1); } } pageSwitchAnimation = new AnimatorSet(); listView[0].setVisibility(View.VISIBLE); int index = order == 1 ? 0 : 1; listView[index].setBackgroundColor(backgroundPaint.getColor()); if (Build.VERSION.SDK_INT >= 18) { listView[index].setLayerType(View.LAYER_TYPE_HARDWARE, null); } if (order == 1) { pageSwitchAnimation.playTogether(ObjectAnimator.ofFloat(listView[0], View.TRANSLATION_X, AndroidUtilities.dp(56), 0), ObjectAnimator.ofFloat(listView[0], View.ALPHA, 0.0f, 1.0f)); } else if (order == -1) { listView[0].setAlpha(1.0f); listView[0].setTranslationX(0.0f); pageSwitchAnimation.playTogether(ObjectAnimator.ofFloat(listView[1], View.TRANSLATION_X, 0, AndroidUtilities.dp(56)), ObjectAnimator.ofFloat(listView[1], View.ALPHA, 1.0f, 0.0f)); } pageSwitchAnimation.setDuration(150); pageSwitchAnimation.setInterpolator(interpolator); pageSwitchAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { listView[1].setVisibility(View.GONE); textSelectionHelper.setParentView(listView[0]); textSelectionHelper.layoutManager = layoutManager[0]; listView[index].setBackgroundDrawable(null); if (Build.VERSION.SDK_INT >= 18) { listView[index].setLayerType(View.LAYER_TYPE_NONE, null); } pageSwitchAnimation = null; } }); pageSwitchAnimation.start(); } if (!previous) { titleTextView.setText(webPage.site_name == null ? "" : webPage.site_name); textSelectionHelper.clear(true); headerView.invalidate(); } int index = previous ? 1 : 0; TLRPC.WebPage page = previous ? pagesStack.get(pagesStack.size() - 2) : webPage; adapter[index].isRtl = webPage.cached_page.rtl; adapter[index].cleanup(); adapter[index].currentPage = page; int numBlocks = 0; int count = page.cached_page.blocks.size(); for (int a = 0; a < count; a++) { TLRPC.PageBlock block = page.cached_page.blocks.get(a); if (a == 0) { block.first = true; if (block instanceof TLRPC.TL_pageBlockCover) { TLRPC.TL_pageBlockCover pageBlockCover = (TLRPC.TL_pageBlockCover) block; TLRPC.RichText caption = getBlockCaption(pageBlockCover, 0); TLRPC.RichText credit = getBlockCaption(pageBlockCover, 1); if ((caption != null && !(caption instanceof TLRPC.TL_textEmpty) || credit != null && !(credit instanceof TLRPC.TL_textEmpty)) && count > 1) { TLRPC.PageBlock next = page.cached_page.blocks.get(1); if (next instanceof TLRPC.TL_pageBlockChannel) { adapter[index].channelBlock = (TLRPC.TL_pageBlockChannel) next; } } } } else if (a == 1 && adapter[index].channelBlock != null) { continue; } adapter[index].addBlock(adapter[index], block, 0, 0, a == count - 1 ? a : 0); } adapter[index].notifyDataSetChanged(); if (pagesStack.size() == 1 || order == -1) { SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("articles", Activity.MODE_PRIVATE); String key = "article" + page.id; int position = preferences.getInt(key, -1); int offset; if (preferences.getBoolean(key + "r", true) == AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y) { offset = preferences.getInt(key + "o", 0) - listView[index].getPaddingTop(); } else { offset = AndroidUtilities.dp(10); } if (position != -1) { layoutManager[index].scrollToPositionWithOffset(position, offset); } } else { layoutManager[index].scrollToPositionWithOffset(0, 0); } if (!previous) { checkScrollAnimated(); } } private boolean addPageToStack(TLRPC.WebPage webPage, String anchor, int order) { saveCurrentPagePosition(); pagesStack.add(webPage); showSearch(false); updateInterfaceForCurrentPage(webPage, false, order); return scrollToAnchor(anchor); } private boolean scrollToAnchor(String anchor) { if (TextUtils.isEmpty(anchor)) { return false; } anchor = anchor.toLowerCase(); Integer row = adapter[0].anchors.get(anchor); if (row != null) { TLRPC.TL_textAnchor textAnchor = adapter[0].anchorsParent.get(anchor); if (textAnchor != null) { TLRPC.TL_pageBlockParagraph paragraph = new TLRPC.TL_pageBlockParagraph(); paragraph.text = textAnchor.text; int type = adapter[0].getTypeForBlock(paragraph); RecyclerView.ViewHolder holder = adapter[0].onCreateViewHolder(null, type); adapter[0].bindBlockToHolder(type, holder, paragraph, 0, 0); BottomSheet.Builder builder = new BottomSheet.Builder(parentActivity); builder.setApplyTopPadding(false); builder.setApplyBottomPadding(false); LinearLayout linearLayout = new LinearLayout(parentActivity); linearLayout.setOrientation(LinearLayout.VERTICAL); textSelectionHelperBottomSheet = new TextSelectionHelper.ArticleTextSelectionHelper(); textSelectionHelperBottomSheet.setParentView(linearLayout); textSelectionHelperBottomSheet.setCallback(new TextSelectionHelper.Callback() { @Override public void onStateChanged(boolean isSelected) { if (linkSheet != null) { linkSheet.setDisableScroll(isSelected); } } }); TextView textView = new TextView(parentActivity) { @Override protected void onDraw(Canvas canvas) { canvas.drawLine(0, getMeasuredHeight() - 1, getMeasuredWidth(), getMeasuredHeight() - 1, dividerPaint); super.onDraw(canvas); } }; textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); textView.setText(LocaleController.getString("InstantViewReference", R.string.InstantViewReference)); textView.setGravity((adapter[0].isRtl ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL); textView.setTextColor(getTextColor()); textView.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); linearLayout.addView(textView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, AndroidUtilities.dp(48) + 1)); holder.itemView.setTag(BOTTOM_SHEET_VIEW_TAG); linearLayout.addView(holder.itemView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 7, 0, 0)); View overlayView = textSelectionHelperBottomSheet.getOverlayView(parentActivity); FrameLayout frameLayout = new FrameLayout(parentActivity) { @Override public boolean dispatchTouchEvent(MotionEvent ev) { TextSelectionHelper.TextSelectionOverlay selectionOverlay = textSelectionHelperBottomSheet.getOverlayView(getContext()); MotionEvent textSelectionEv = MotionEvent.obtain(ev); textSelectionEv.offsetLocation(-linearLayout.getX(), -linearLayout.getY()); if (textSelectionHelperBottomSheet.isSelectionMode() && textSelectionHelperBottomSheet.getOverlayView(getContext()).onTouchEvent(textSelectionEv)) { return true; } if (selectionOverlay.checkOnTap(ev)) { ev.setAction(MotionEvent.ACTION_CANCEL); } if (ev.getAction() == MotionEvent.ACTION_DOWN && textSelectionHelperBottomSheet.isSelectionMode() && (ev.getY() < linearLayout.getTop() || ev.getY() > linearLayout.getBottom())) { if (textSelectionHelperBottomSheet.getOverlayView(getContext()).onTouchEvent(textSelectionEv)) { return super.dispatchTouchEvent(ev); } else { return true; } } return super.dispatchTouchEvent(ev); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); heightMeasureSpec = MeasureSpec.makeMeasureSpec(linearLayout.getMeasuredHeight() + AndroidUtilities.dp(8), MeasureSpec.EXACTLY); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } }; builder.setDelegate(new BottomSheet.BottomSheetDelegate() { @Override public boolean canDismiss() { if (textSelectionHelperBottomSheet != null && textSelectionHelperBottomSheet.isSelectionMode()){ textSelectionHelperBottomSheet.clear(); return false; } return true; } }); frameLayout.addView(linearLayout, LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT); frameLayout.addView(overlayView, LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT); builder.setCustomView(frameLayout); if (textSelectionHelper.isSelectionMode()) { textSelectionHelper.clear(); } showDialog(linkSheet = builder.create()); } else { if (row < 0 || row >= adapter[0].blocks.size()) { return false; } TLRPC.PageBlock originalBlock = adapter[0].blocks.get(row); TLRPC.PageBlock block = getLastNonListPageBlock(originalBlock); if (block instanceof TL_pageBlockDetailsChild) { if (openAllParentBlocks((TL_pageBlockDetailsChild) block)) { adapter[0].updateRows(); adapter[0].notifyDataSetChanged(); } } int position = adapter[0].localBlocks.indexOf(originalBlock); if (position != -1) { row = position; } Integer offset = adapter[0].anchorsOffset.get(anchor); if (offset != null) { if (offset == -1) { int type = adapter[0].getTypeForBlock(originalBlock); RecyclerView.ViewHolder holder = adapter[0].onCreateViewHolder(null, type); adapter[0].bindBlockToHolder(type, holder, originalBlock, 0, 0); holder.itemView.measure(View.MeasureSpec.makeMeasureSpec(listView[0].getMeasuredWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); offset = adapter[0].anchorsOffset.get(anchor); if (offset == -1) { offset = 0; } } } else { offset = 0; } layoutManager[0].scrollToPositionWithOffset(row, currentHeaderHeight - AndroidUtilities.dp(56) - offset); } return true; } return false; } private boolean removeLastPageFromStack() { if (pagesStack.size() < 2) { return false; } pagesStack.remove(pagesStack.size() - 1); updateInterfaceForCurrentPage(pagesStack.get(pagesStack.size() - 1), false, -1); return true; } protected void startCheckLongPress(float x, float y, View parentView) { if (checkingForLongPress) { return; } checkingForLongPress = true; if (pendingCheckForTap == null) { pendingCheckForTap = new CheckForTap(); } if (parentView.getTag() != null && parentView.getTag() == BOTTOM_SHEET_VIEW_TAG && textSelectionHelperBottomSheet != null) { textSelectionHelperBottomSheet.setMaybeView((int) x, (int) y, parentView); } else { textSelectionHelper.setMaybeView((int) x, (int) y, parentView); } windowView.postDelayed(pendingCheckForTap, ViewConfiguration.getTapTimeout()); } protected void cancelCheckLongPress() { checkingForLongPress = false; if (pendingCheckForLongPress != null) { windowView.removeCallbacks(pendingCheckForLongPress); pendingCheckForLongPress = null; } if (pendingCheckForTap != null) { windowView.removeCallbacks(pendingCheckForTap); pendingCheckForTap = null; } } private static final int TEXT_FLAG_REGULAR = 0; private static final int TEXT_FLAG_MEDIUM = 1; private static final int TEXT_FLAG_ITALIC = 2; private static final int TEXT_FLAG_MONO = 4; private static final int TEXT_FLAG_URL = 8; private static final int TEXT_FLAG_UNDERLINE = 16; private static final int TEXT_FLAG_STRIKE = 32; private static final int TEXT_FLAG_MARKED = 64; private static final int TEXT_FLAG_SUB = 128; private static final int TEXT_FLAG_SUP = 256; private static final int TEXT_FLAG_WEBPAGE_URL = 512; private static TextPaint audioTimePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); private static TextPaint errorTextPaint; private static SparseArray photoCaptionTextPaints = new SparseArray<>(); private static SparseArray photoCreditTextPaints = new SparseArray<>(); private static SparseArray titleTextPaints = new SparseArray<>(); private static SparseArray kickerTextPaints = new SparseArray<>(); private static SparseArray headerTextPaints = new SparseArray<>(); private static SparseArray subtitleTextPaints = new SparseArray<>(); private static SparseArray subheaderTextPaints = new SparseArray<>(); private static SparseArray authorTextPaints = new SparseArray<>(); private static SparseArray footerTextPaints = new SparseArray<>(); private static SparseArray paragraphTextPaints = new SparseArray<>(); private static SparseArray listTextPaints = new SparseArray<>(); private static SparseArray preformattedTextPaints = new SparseArray<>(); private static SparseArray quoteTextPaints = new SparseArray<>(); private static SparseArray embedPostTextPaints = new SparseArray<>(); private static SparseArray embedPostCaptionTextPaints = new SparseArray<>(); private static SparseArray mediaCaptionTextPaints = new SparseArray<>(); private static SparseArray mediaCreditTextPaints = new SparseArray<>(); private static SparseArray relatedArticleTextPaints = new SparseArray<>(); private static SparseArray detailsTextPaints = new SparseArray<>(); private static SparseArray tableTextPaints = new SparseArray<>(); private static TextPaint embedPostAuthorPaint; private static TextPaint embedPostDatePaint; private static TextPaint channelNamePaint; private static TextPaint channelNamePhotoPaint; private static TextPaint relatedArticleHeaderPaint; private static TextPaint relatedArticleTextPaint; private static TextPaint listTextPointerPaint; private static TextPaint listTextNumPaint; private static Paint photoBackgroundPaint; private static Paint preformattedBackgroundPaint; private static Paint quoteLinePaint; private static Paint dividerPaint; private static Paint tableLinePaint; private static Paint tableHalfLinePaint; private static Paint tableHeaderPaint; private static Paint tableStripPaint; private static Paint urlPaint; private static Paint webpageUrlPaint; private static Paint webpageSearchPaint; private static Paint webpageMarkPaint; private int getTextFlags(TLRPC.RichText richText) { if (richText instanceof TLRPC.TL_textFixed) { return getTextFlags(richText.parentRichText) | TEXT_FLAG_MONO; } else if (richText instanceof TLRPC.TL_textItalic) { return getTextFlags(richText.parentRichText) | TEXT_FLAG_ITALIC; } else if (richText instanceof TLRPC.TL_textBold) { return getTextFlags(richText.parentRichText) | TEXT_FLAG_MEDIUM; } else if (richText instanceof TLRPC.TL_textUnderline) { return getTextFlags(richText.parentRichText) | TEXT_FLAG_UNDERLINE; } else if (richText instanceof TLRPC.TL_textStrike) { return getTextFlags(richText.parentRichText) | TEXT_FLAG_STRIKE; } else if (richText instanceof TLRPC.TL_textEmail) { return getTextFlags(richText.parentRichText) | TEXT_FLAG_URL; } else if (richText instanceof TLRPC.TL_textPhone) { return getTextFlags(richText.parentRichText) | TEXT_FLAG_URL; } else if (richText instanceof TLRPC.TL_textUrl) { TLRPC.TL_textUrl textUrl = (TLRPC.TL_textUrl) richText; if (textUrl.webpage_id != 0) { return getTextFlags(richText.parentRichText) | TEXT_FLAG_WEBPAGE_URL; } else { return getTextFlags(richText.parentRichText) | TEXT_FLAG_URL; } } else if (richText instanceof TLRPC.TL_textSubscript) { return getTextFlags(richText.parentRichText) | TEXT_FLAG_SUB; } else if (richText instanceof TLRPC.TL_textSuperscript) { return getTextFlags(richText.parentRichText) | TEXT_FLAG_SUP; } else if (richText instanceof TLRPC.TL_textMarked) { return getTextFlags(richText.parentRichText) | TEXT_FLAG_MARKED; } else if (richText != null) { return getTextFlags(richText.parentRichText); } return TEXT_FLAG_REGULAR; } private TLRPC.RichText getLastRichText(TLRPC.RichText richText) { if (richText == null) { return null; } if (richText instanceof TLRPC.TL_textFixed) { return getLastRichText(((TLRPC.TL_textFixed) richText).text); } else if (richText instanceof TLRPC.TL_textItalic) { return getLastRichText(((TLRPC.TL_textItalic) richText).text); } else if (richText instanceof TLRPC.TL_textBold) { return getLastRichText(((TLRPC.TL_textBold) richText).text); } else if (richText instanceof TLRPC.TL_textUnderline) { return getLastRichText(((TLRPC.TL_textUnderline) richText).text); } else if (richText instanceof TLRPC.TL_textStrike) { return getLastRichText(((TLRPC.TL_textStrike) richText).text); } else if (richText instanceof TLRPC.TL_textEmail) { return getLastRichText(((TLRPC.TL_textEmail) richText).text); } else if (richText instanceof TLRPC.TL_textUrl) { return getLastRichText(((TLRPC.TL_textUrl) richText).text); } else if (richText instanceof TLRPC.TL_textAnchor) { getLastRichText(((TLRPC.TL_textAnchor) richText).text); } else if (richText instanceof TLRPC.TL_textSubscript) { return getLastRichText(((TLRPC.TL_textSubscript) richText).text); } else if (richText instanceof TLRPC.TL_textSuperscript) { return getLastRichText(((TLRPC.TL_textSuperscript) richText).text); } else if (richText instanceof TLRPC.TL_textMarked) { return getLastRichText(((TLRPC.TL_textMarked) richText).text); } else if (richText instanceof TLRPC.TL_textPhone) { return getLastRichText(((TLRPC.TL_textPhone) richText).text); } return richText; } private CharSequence getText(WebpageAdapter adapter, View parentView, TLRPC.RichText parentRichText, TLRPC.RichText richText, TLRPC.PageBlock parentBlock, int maxWidth) { return getText(adapter.currentPage, parentView, parentRichText, richText, parentBlock, maxWidth); } private CharSequence getText(TLRPC.WebPage page, View parentView, TLRPC.RichText parentRichText, TLRPC.RichText richText, TLRPC.PageBlock parentBlock, int maxWidth) { if (richText == null) { return null; } if (richText instanceof TLRPC.TL_textFixed) { return getText(page, parentView, parentRichText, ((TLRPC.TL_textFixed) richText).text, parentBlock, maxWidth); } else if (richText instanceof TLRPC.TL_textItalic) { return getText(page, parentView, parentRichText, ((TLRPC.TL_textItalic) richText).text, parentBlock, maxWidth); } else if (richText instanceof TLRPC.TL_textBold) { return getText(page, parentView, parentRichText, ((TLRPC.TL_textBold) richText).text, parentBlock, maxWidth); } else if (richText instanceof TLRPC.TL_textUnderline) { return getText(page, parentView, parentRichText, ((TLRPC.TL_textUnderline) richText).text, parentBlock, maxWidth); } else if (richText instanceof TLRPC.TL_textStrike) { return getText(page, parentView, parentRichText, ((TLRPC.TL_textStrike) richText).text, parentBlock, maxWidth); } else if (richText instanceof TLRPC.TL_textEmail) { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(getText(page, parentView, parentRichText, ((TLRPC.TL_textEmail) richText).text, parentBlock, maxWidth)); MetricAffectingSpan[] innerSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), MetricAffectingSpan.class); if (spannableStringBuilder.length() != 0) { spannableStringBuilder.setSpan(new TextPaintUrlSpan(innerSpans == null || innerSpans.length == 0 ? getTextPaint(parentRichText, richText, parentBlock) : null, "mailto:" + getUrl(richText)), 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } return spannableStringBuilder; } else if (richText instanceof TLRPC.TL_textUrl) { TLRPC.TL_textUrl textUrl = (TLRPC.TL_textUrl) richText; SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(getText(page, parentView, parentRichText, ((TLRPC.TL_textUrl) richText).text, parentBlock, maxWidth)); MetricAffectingSpan[] innerSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), MetricAffectingSpan.class); TextPaint paint = innerSpans == null || innerSpans.length == 0 ? getTextPaint(parentRichText, richText, parentBlock) : null; MetricAffectingSpan span; if (textUrl.webpage_id != 0) { span = new TextPaintWebpageUrlSpan(paint, getUrl(richText)); } else { span = new TextPaintUrlSpan(paint, getUrl(richText)); } if (spannableStringBuilder.length() != 0) { spannableStringBuilder.setSpan(span, 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } return spannableStringBuilder; } else if (richText instanceof TLRPC.TL_textPlain) { return ((TLRPC.TL_textPlain) richText).text; } else if (richText instanceof TLRPC.TL_textAnchor) { TLRPC.TL_textAnchor textAnchor = (TLRPC.TL_textAnchor) richText; SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(getText(page, parentView, parentRichText, textAnchor.text, parentBlock, maxWidth)); spannableStringBuilder.setSpan(new AnchorSpan(textAnchor.name), 0, spannableStringBuilder.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); return spannableStringBuilder; } else if (richText instanceof TLRPC.TL_textEmpty) { return ""; } else if (richText instanceof TLRPC.TL_textConcat) { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(); int count = richText.texts.size(); for (int a = 0; a < count; a++) { TLRPC.RichText innerRichText = richText.texts.get(a); TLRPC.RichText lastRichText = getLastRichText(innerRichText); boolean extraSpace = maxWidth >= 0 && innerRichText instanceof TLRPC.TL_textUrl && ((TLRPC.TL_textUrl) innerRichText).webpage_id != 0; if (extraSpace && spannableStringBuilder.length() != 0 && spannableStringBuilder.charAt(spannableStringBuilder.length() - 1) != '\n') { spannableStringBuilder.append(" "); spannableStringBuilder.setSpan(new TextSelectionHelper.IgnoreCopySpannable(), spannableStringBuilder.length() - 1, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } CharSequence innerText = getText(page, parentView, parentRichText, innerRichText, parentBlock, maxWidth); int flags = getTextFlags(lastRichText); int startLength = spannableStringBuilder.length(); spannableStringBuilder.append(innerText); if (flags != 0 && !(innerText instanceof SpannableStringBuilder)) { if ((flags & TEXT_FLAG_URL) != 0 || (flags & TEXT_FLAG_WEBPAGE_URL) != 0) { String url = getUrl(innerRichText); if (url == null) { url = getUrl(parentRichText); } MetricAffectingSpan span; if ((flags & TEXT_FLAG_WEBPAGE_URL) != 0) { span = new TextPaintWebpageUrlSpan(getTextPaint(parentRichText, lastRichText, parentBlock), url); } else { span = new TextPaintUrlSpan(getTextPaint(parentRichText, lastRichText, parentBlock), url); } if (startLength != spannableStringBuilder.length()) { spannableStringBuilder.setSpan(span, startLength, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } else { if (startLength != spannableStringBuilder.length()) { spannableStringBuilder.setSpan(new TextPaintSpan(getTextPaint(parentRichText, lastRichText, parentBlock)), startLength, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } } if (extraSpace && a != count - 1) { spannableStringBuilder.append(" "); spannableStringBuilder.setSpan(new TextSelectionHelper.IgnoreCopySpannable(), spannableStringBuilder.length() - 1, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } return spannableStringBuilder; } else if (richText instanceof TLRPC.TL_textSubscript) { return getText(page, parentView, parentRichText, ((TLRPC.TL_textSubscript) richText).text, parentBlock, maxWidth); } else if (richText instanceof TLRPC.TL_textSuperscript) { return getText(page, parentView, parentRichText, ((TLRPC.TL_textSuperscript) richText).text, parentBlock, maxWidth); } else if (richText instanceof TLRPC.TL_textMarked) { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(getText(page, parentView, parentRichText, ((TLRPC.TL_textMarked) richText).text, parentBlock, maxWidth)); MetricAffectingSpan[] innerSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), MetricAffectingSpan.class); if (spannableStringBuilder.length() != 0) { spannableStringBuilder.setSpan(new TextPaintMarkSpan(innerSpans == null || innerSpans.length == 0 ? getTextPaint(parentRichText, richText, parentBlock) : null), 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } return spannableStringBuilder; } else if (richText instanceof TLRPC.TL_textPhone) { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(getText(page, parentView, parentRichText, ((TLRPC.TL_textPhone) richText).text, parentBlock, maxWidth)); MetricAffectingSpan[] innerSpans = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), MetricAffectingSpan.class); if (spannableStringBuilder.length() != 0) { spannableStringBuilder.setSpan(new TextPaintUrlSpan(innerSpans == null || innerSpans.length == 0 ? getTextPaint(parentRichText, richText, parentBlock) : null, "tel:" + getUrl(richText)), 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } return spannableStringBuilder; } else if (richText instanceof TLRPC.TL_textImage) { TLRPC.TL_textImage textImage = (TLRPC.TL_textImage) richText; TLRPC.Document document = WebPageUtils.getDocumentWithId(page, textImage.document_id); if (document != null) { SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("*"); int w = AndroidUtilities.dp(textImage.w); int h = AndroidUtilities.dp(textImage.h); maxWidth = Math.abs(maxWidth); if (w > maxWidth) { float scale = maxWidth / (float) w; w = maxWidth; h *= scale; } if (parentView != null) { int color = Theme.getColor(Theme.key_windowBackgroundWhite); float lightness = (0.2126f * Color.red(color) + 0.7152f * Color.green(color) + 0.0722f * Color.blue(color)) / 255.0f; spannableStringBuilder.setSpan(new TextPaintImageReceiverSpan(parentView, document, page, w, h, false, lightness <= 0.705f), 0, spannableStringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } return spannableStringBuilder; } else { return ""; } } return "not supported " + richText; } public static CharSequence getPlainText(TLRPC.RichText richText) { if (richText == null) { return ""; } if (richText instanceof TLRPC.TL_textFixed) { return getPlainText(((TLRPC.TL_textFixed) richText).text); } else if (richText instanceof TLRPC.TL_textItalic) { return getPlainText(((TLRPC.TL_textItalic) richText).text); } else if (richText instanceof TLRPC.TL_textBold) { return getPlainText(((TLRPC.TL_textBold) richText).text); } else if (richText instanceof TLRPC.TL_textUnderline) { return getPlainText(((TLRPC.TL_textUnderline) richText).text); } else if (richText instanceof TLRPC.TL_textStrike) { return getPlainText(((TLRPC.TL_textStrike) richText).text); } else if (richText instanceof TLRPC.TL_textEmail) { return getPlainText(((TLRPC.TL_textEmail) richText).text); } else if (richText instanceof TLRPC.TL_textUrl) { return getPlainText(((TLRPC.TL_textUrl) richText).text); } else if (richText instanceof TLRPC.TL_textPlain) { return ((TLRPC.TL_textPlain) richText).text; } else if (richText instanceof TLRPC.TL_textAnchor) { return getPlainText(((TLRPC.TL_textAnchor) richText).text); } else if (richText instanceof TLRPC.TL_textEmpty) { return ""; } else if (richText instanceof TLRPC.TL_textConcat) { StringBuilder stringBuilder = new StringBuilder(); int count = richText.texts.size(); for (int a = 0; a < count; a++) { stringBuilder.append(getPlainText(richText.texts.get(a))); } return stringBuilder; } else if (richText instanceof TLRPC.TL_textSubscript) { return getPlainText(((TLRPC.TL_textSubscript) richText).text); } else if (richText instanceof TLRPC.TL_textSuperscript) { return getPlainText(((TLRPC.TL_textSuperscript) richText).text); } else if (richText instanceof TLRPC.TL_textMarked) { return getPlainText(((TLRPC.TL_textMarked) richText).text); } else if (richText instanceof TLRPC.TL_textPhone) { return getPlainText(((TLRPC.TL_textPhone) richText).text); } else if (richText instanceof TLRPC.TL_textImage) { return ""; } return ""; } public static String getUrl(TLRPC.RichText richText) { if (richText instanceof TLRPC.TL_textFixed) { return getUrl(((TLRPC.TL_textFixed) richText).text); } else if (richText instanceof TLRPC.TL_textItalic) { return getUrl(((TLRPC.TL_textItalic) richText).text); } else if (richText instanceof TLRPC.TL_textBold) { return getUrl(((TLRPC.TL_textBold) richText).text); } else if (richText instanceof TLRPC.TL_textUnderline) { return getUrl(((TLRPC.TL_textUnderline) richText).text); } else if (richText instanceof TLRPC.TL_textStrike) { return getUrl(((TLRPC.TL_textStrike) richText).text); } else if (richText instanceof TLRPC.TL_textEmail) { return ((TLRPC.TL_textEmail) richText).email; } else if (richText instanceof TLRPC.TL_textUrl) { return ((TLRPC.TL_textUrl) richText).url; }else if (richText instanceof TLRPC.TL_textPhone) { return ((TLRPC.TL_textPhone) richText).phone; } return null; } private int getTextColor() { return Theme.getColor(Theme.key_windowBackgroundWhiteBlackText); } private int getLinkTextColor() { return Theme.getColor(Theme.key_windowBackgroundWhiteLinkText); } private static int getGrayTextColor() { return Theme.getColor(Theme.key_windowBackgroundWhiteGrayText); } private TextPaint getTextPaint(TLRPC.RichText parentRichText, TLRPC.RichText richText, TLRPC.PageBlock parentBlock) { int flags = getTextFlags(richText); SparseArray currentMap = null; int textSize = AndroidUtilities.dp(14); int textColor = 0xffff0000; int additionalSize = AndroidUtilities.dp(SharedConfig.ivFontSize - 16); if (parentBlock instanceof TLRPC.TL_pageBlockPhoto) { TLRPC.TL_pageBlockPhoto pageBlockPhoto = (TLRPC.TL_pageBlockPhoto) parentBlock; if (pageBlockPhoto.caption.text == richText || pageBlockPhoto.caption.text == parentRichText) { currentMap = photoCaptionTextPaints; textSize = AndroidUtilities.dp(14); } else { currentMap = photoCreditTextPaints; textSize = AndroidUtilities.dp(12); } textColor = getGrayTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockMap) { TLRPC.TL_pageBlockMap pageBlockMap = (TLRPC.TL_pageBlockMap) parentBlock; if (pageBlockMap.caption.text == richText || pageBlockMap.caption.text == parentRichText) { currentMap = photoCaptionTextPaints; textSize = AndroidUtilities.dp(14); } else { currentMap = photoCreditTextPaints; textSize = AndroidUtilities.dp(12); } textColor = getGrayTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockTitle) { currentMap = titleTextPaints; textSize = AndroidUtilities.dp(23); textColor = getTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockKicker) { currentMap = kickerTextPaints; textSize = AndroidUtilities.dp(14); textColor = getTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockAuthorDate) { currentMap = authorTextPaints; textSize = AndroidUtilities.dp(14); textColor = getGrayTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockFooter) { currentMap = footerTextPaints; textSize = AndroidUtilities.dp(14); textColor = getGrayTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockSubtitle) { currentMap = subtitleTextPaints; textSize = AndroidUtilities.dp(20); textColor = getTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockHeader) { currentMap = headerTextPaints; textSize = AndroidUtilities.dp(20); textColor = getTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockSubheader) { currentMap = subheaderTextPaints; textSize = AndroidUtilities.dp(17); textColor = getTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockBlockquote) { TLRPC.TL_pageBlockBlockquote pageBlockBlockquote = (TLRPC.TL_pageBlockBlockquote) parentBlock; if (pageBlockBlockquote.text == parentRichText) { currentMap = quoteTextPaints; textSize = AndroidUtilities.dp(15); textColor = getTextColor(); } else if (pageBlockBlockquote.caption == parentRichText) { currentMap = photoCaptionTextPaints; textSize = AndroidUtilities.dp(14); textColor = getGrayTextColor(); } } else if (parentBlock instanceof TLRPC.TL_pageBlockPullquote) { TLRPC.TL_pageBlockPullquote pageBlockBlockquote = (TLRPC.TL_pageBlockPullquote) parentBlock; if (pageBlockBlockquote.text == parentRichText) { currentMap = quoteTextPaints; textSize = AndroidUtilities.dp(15); textColor = getTextColor(); } else if (pageBlockBlockquote.caption == parentRichText) { currentMap = photoCaptionTextPaints; textSize = AndroidUtilities.dp(14); textColor = getGrayTextColor(); } } else if (parentBlock instanceof TLRPC.TL_pageBlockPreformatted) { currentMap = preformattedTextPaints; textSize = AndroidUtilities.dp(14); textColor = getTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockParagraph) { currentMap = paragraphTextPaints; textSize = AndroidUtilities.dp(16); textColor = getTextColor(); } else if (isListItemBlock(parentBlock)) { currentMap = listTextPaints; textSize = AndroidUtilities.dp(16); textColor = getTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockEmbed) { TLRPC.TL_pageBlockEmbed pageBlockEmbed = (TLRPC.TL_pageBlockEmbed) parentBlock; if (pageBlockEmbed.caption.text == richText || pageBlockEmbed.caption.text == parentRichText) { currentMap = photoCaptionTextPaints; textSize = AndroidUtilities.dp(14); } else { currentMap = photoCreditTextPaints; textSize = AndroidUtilities.dp(12); } textColor = getGrayTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockSlideshow) { TLRPC.TL_pageBlockSlideshow pageBlockSlideshow = (TLRPC.TL_pageBlockSlideshow) parentBlock; if (pageBlockSlideshow.caption.text == richText || pageBlockSlideshow.caption.text == parentRichText) { currentMap = photoCaptionTextPaints; textSize = AndroidUtilities.dp(14); } else { currentMap = photoCreditTextPaints; textSize = AndroidUtilities.dp(12); } textColor = getGrayTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockCollage) { TLRPC.TL_pageBlockCollage pageBlockCollage = (TLRPC.TL_pageBlockCollage) parentBlock; if (pageBlockCollage.caption.text == richText || pageBlockCollage.caption.text == parentRichText) { currentMap = photoCaptionTextPaints; textSize = AndroidUtilities.dp(14); } else { currentMap = photoCreditTextPaints; textSize = AndroidUtilities.dp(12); } textColor = getGrayTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockEmbedPost) { TLRPC.TL_pageBlockEmbedPost pageBlockEmbedPost = (TLRPC.TL_pageBlockEmbedPost) parentBlock; if (richText == pageBlockEmbedPost.caption.text) { currentMap = photoCaptionTextPaints; textSize = AndroidUtilities.dp(14); textColor = getGrayTextColor(); } else if (richText == pageBlockEmbedPost.caption.credit) { currentMap = photoCreditTextPaints; textSize = AndroidUtilities.dp(12); textColor = getGrayTextColor(); } else if (richText != null) { currentMap = embedPostTextPaints; textSize = AndroidUtilities.dp(14); textColor = getTextColor(); } } else if (parentBlock instanceof TLRPC.TL_pageBlockVideo) { TLRPC.TL_pageBlockVideo pageBlockVideo = (TLRPC.TL_pageBlockVideo) parentBlock; if (richText == pageBlockVideo.caption.text) { currentMap = mediaCaptionTextPaints; textSize = AndroidUtilities.dp(14); } else { currentMap = mediaCreditTextPaints; textSize = AndroidUtilities.dp(12); } textColor = getTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockAudio) { TLRPC.TL_pageBlockAudio pageBlockAudio = (TLRPC.TL_pageBlockAudio) parentBlock; if (richText == pageBlockAudio.caption.text) { currentMap = mediaCaptionTextPaints; textSize = AndroidUtilities.dp(14); } else { currentMap = mediaCreditTextPaints; textSize = AndroidUtilities.dp(12); } textColor = getTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockRelatedArticles) { currentMap = relatedArticleTextPaints; textSize = AndroidUtilities.dp(15); textColor = getGrayTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockDetails) { currentMap = detailsTextPaints; textSize = AndroidUtilities.dp(15); textColor = getTextColor(); } else if (parentBlock instanceof TLRPC.TL_pageBlockTable) { currentMap = tableTextPaints; textSize = AndroidUtilities.dp(15); textColor = getTextColor(); } if ((flags & TEXT_FLAG_SUP) != 0 || (flags & TEXT_FLAG_SUB) != 0) { textSize -= AndroidUtilities.dp(4); } if (currentMap == null) { if (errorTextPaint == null) { errorTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); errorTextPaint.setColor(0xffff0000); } errorTextPaint.setTextSize(AndroidUtilities.dp(14)); return errorTextPaint; } TextPaint paint = currentMap.get(flags); if (paint == null) { paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); if ((flags & TEXT_FLAG_MONO) != 0) { paint.setTypeface(AndroidUtilities.getTypeface("fonts/rmono.ttf")); } else { if (parentBlock instanceof TLRPC.TL_pageBlockRelatedArticles) { paint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); } else if (selectedFont == 1 || parentBlock instanceof TLRPC.TL_pageBlockTitle || parentBlock instanceof TLRPC.TL_pageBlockKicker || parentBlock instanceof TLRPC.TL_pageBlockHeader || parentBlock instanceof TLRPC.TL_pageBlockSubtitle || parentBlock instanceof TLRPC.TL_pageBlockSubheader) { if (parentBlock instanceof TLRPC.TL_pageBlockTitle || parentBlock instanceof TLRPC.TL_pageBlockHeader || parentBlock instanceof TLRPC.TL_pageBlockSubtitle || parentBlock instanceof TLRPC.TL_pageBlockSubheader) { paint.setTypeface(AndroidUtilities.getTypeface("fonts/mw_bold.ttf")); } else { if ((flags & TEXT_FLAG_MEDIUM) != 0 && (flags & TEXT_FLAG_ITALIC) != 0) { paint.setTypeface(Typeface.create("serif", Typeface.BOLD_ITALIC)); } else if ((flags & TEXT_FLAG_MEDIUM) != 0) { paint.setTypeface(Typeface.create("serif", Typeface.BOLD)); } else if ((flags & TEXT_FLAG_ITALIC) != 0) { paint.setTypeface(Typeface.create("serif", Typeface.ITALIC)); } else { paint.setTypeface(Typeface.create("serif", Typeface.NORMAL)); } } } else { if ((flags & TEXT_FLAG_MEDIUM) != 0 && (flags & TEXT_FLAG_ITALIC) != 0) { paint.setTypeface(AndroidUtilities.getTypeface("fonts/rmediumitalic.ttf")); } else if ((flags & TEXT_FLAG_MEDIUM) != 0) { paint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); } else if ((flags & TEXT_FLAG_ITALIC) != 0) { paint.setTypeface(AndroidUtilities.getTypeface("fonts/ritalic.ttf")); } } } if ((flags & TEXT_FLAG_STRIKE) != 0) { paint.setFlags(paint.getFlags() | TextPaint.STRIKE_THRU_TEXT_FLAG); } if ((flags & TEXT_FLAG_UNDERLINE) != 0) { paint.setFlags(paint.getFlags() | TextPaint.UNDERLINE_TEXT_FLAG); } if ((flags & TEXT_FLAG_URL) != 0 || (flags & TEXT_FLAG_WEBPAGE_URL) != 0) { paint.setFlags(paint.getFlags()); textColor = getLinkTextColor(); } if ((flags & TEXT_FLAG_SUP) != 0) { paint.baselineShift -= AndroidUtilities.dp(6.0f); } else if ((flags & TEXT_FLAG_SUB) != 0) { paint.baselineShift += AndroidUtilities.dp(2.0f); } paint.setColor(textColor); currentMap.put(flags, paint); } paint.setTextSize(textSize + additionalSize); return paint; } private DrawingText createLayoutForText(View parentView, CharSequence plainText, TLRPC.RichText richText, int width, int textY, TLRPC.PageBlock parentBlock, Layout.Alignment align, WebpageAdapter parentAdapter) { return createLayoutForText(parentView, plainText, richText, width, 0, parentBlock, align, 0, parentAdapter); } private DrawingText createLayoutForText(View parentView, CharSequence plainText, TLRPC.RichText richText, int width, int textY, TLRPC.PageBlock parentBlock, WebpageAdapter parentAdapter) { return createLayoutForText(parentView, plainText, richText, width, textY, parentBlock, Layout.Alignment.ALIGN_NORMAL, 0, parentAdapter); } private DrawingText createLayoutForText(View parentView, CharSequence plainText, TLRPC.RichText richText, int width, int textY, TLRPC.PageBlock parentBlock, Layout.Alignment align, int maxLines, WebpageAdapter parentAdapter) { if (plainText == null && (richText == null || richText instanceof TLRPC.TL_textEmpty)) { return null; } if (width < 0) { width = AndroidUtilities.dp(10); } CharSequence text; if (plainText != null) { text = plainText; } else { text = getText(parentAdapter, parentView, richText, richText, parentBlock, width); } if (TextUtils.isEmpty(text)) { return null; } int additionalSize = AndroidUtilities.dp(SharedConfig.ivFontSize - 16); TextPaint paint; if (parentBlock instanceof TLRPC.TL_pageBlockEmbedPost && richText == null) { TLRPC.TL_pageBlockEmbedPost pageBlockEmbedPost = (TLRPC.TL_pageBlockEmbedPost) parentBlock; if (pageBlockEmbedPost.author == plainText) { if (embedPostAuthorPaint == null) { embedPostAuthorPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); embedPostAuthorPaint.setColor(getTextColor()); } embedPostAuthorPaint.setTextSize(AndroidUtilities.dp(15) + additionalSize); paint = embedPostAuthorPaint; } else { if (embedPostDatePaint == null) { embedPostDatePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); embedPostDatePaint.setColor(getGrayTextColor()); } embedPostDatePaint.setTextSize(AndroidUtilities.dp(14) + additionalSize); paint = embedPostDatePaint; } } else if (parentBlock instanceof TLRPC.TL_pageBlockChannel) { if (channelNamePaint == null) { channelNamePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); channelNamePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); channelNamePhotoPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); channelNamePhotoPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); } channelNamePaint.setColor(getTextColor()); channelNamePaint.setTextSize(AndroidUtilities.dp(15)); channelNamePhotoPaint.setColor(0xffffffff); channelNamePhotoPaint.setTextSize(AndroidUtilities.dp(15)); paint = parentAdapter.channelBlock != null ? channelNamePhotoPaint : channelNamePaint; } else if (parentBlock instanceof TL_pageBlockRelatedArticlesChild) { TL_pageBlockRelatedArticlesChild pageBlockRelatedArticlesChild = (TL_pageBlockRelatedArticlesChild) parentBlock; if (plainText == pageBlockRelatedArticlesChild.parent.articles.get(pageBlockRelatedArticlesChild.num).title) { if (relatedArticleHeaderPaint == null) { relatedArticleHeaderPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); relatedArticleHeaderPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); } relatedArticleHeaderPaint.setColor(getTextColor()); relatedArticleHeaderPaint.setTextSize(AndroidUtilities.dp(15) + additionalSize); paint = relatedArticleHeaderPaint; } else { if (relatedArticleTextPaint == null) { relatedArticleTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); } relatedArticleTextPaint.setColor(getGrayTextColor()); relatedArticleTextPaint.setTextSize(AndroidUtilities.dp(14) + additionalSize); paint = relatedArticleTextPaint; } } else if (isListItemBlock(parentBlock) && plainText != null) { if (listTextPointerPaint == null) { listTextPointerPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); listTextPointerPaint.setColor(getTextColor()); } if (listTextNumPaint == null) { listTextNumPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); listTextNumPaint.setColor(getTextColor()); } listTextPointerPaint.setTextSize(AndroidUtilities.dp(19) + additionalSize); listTextNumPaint.setTextSize(AndroidUtilities.dp(16) + additionalSize); if (parentBlock instanceof TL_pageBlockListItem && !((TL_pageBlockListItem) parentBlock).parent.pageBlockList.ordered) { paint = listTextPointerPaint; } else { paint = listTextNumPaint; } } else { paint = getTextPaint(richText, richText, parentBlock); } StaticLayout result; if (maxLines != 0) { if (parentBlock instanceof TLRPC.TL_pageBlockPullquote) { result = StaticLayoutEx.createStaticLayout(text, paint, width, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false, TextUtils.TruncateAt.END, width, maxLines); } else { result = StaticLayoutEx.createStaticLayout(text, paint, width, align, 1.0f, AndroidUtilities.dp(4), false, TextUtils.TruncateAt.END, width, maxLines); } } else { if (text.charAt(text.length() - 1) == '\n') { text = text.subSequence(0, text.length() - 1); } if (parentBlock instanceof TLRPC.TL_pageBlockPullquote) { result = new StaticLayout(text, paint, width, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); } else { result = new StaticLayout(text, paint, width, align, 1.0f, AndroidUtilities.dp(4), false); } } if (result == null) { return null; } CharSequence finalText = result.getText(); LinkPath textPath = null; LinkPath markPath = null; if (textY >= 0) { if (result != null && !searchResults.isEmpty() && searchText != null) { String lowerString = text.toString().toLowerCase(); int startIndex = 0; int index; while ((index = lowerString.indexOf(searchText, startIndex)) >= 0) { startIndex = index + searchText.length(); if (index == 0 || AndroidUtilities.isPunctuationCharacter(lowerString.charAt(index - 1))) { adapter[0].searchTextOffset.put(searchText + parentBlock + richText + index, textY + result.getLineTop(result.getLineForOffset(index))); } } } } if (result != null && finalText instanceof Spanned) { Spanned spanned = (Spanned) finalText; try { AnchorSpan[] innerSpans = spanned.getSpans(0, spanned.length(), AnchorSpan.class); int linesCount = result.getLineCount(); if (innerSpans != null && innerSpans.length > 0) { for (int a = 0; a < innerSpans.length; a++) { if (linesCount <= 1) { parentAdapter.anchorsOffset.put(innerSpans[a].getName(), textY); } else { parentAdapter.anchorsOffset.put(innerSpans[a].getName(), textY + result.getLineTop(result.getLineForOffset(spanned.getSpanStart(innerSpans[a])))); } } } } catch (Exception ignore) { } try { TextPaintWebpageUrlSpan[] innerSpans = spanned.getSpans(0, spanned.length(), TextPaintWebpageUrlSpan.class); if (innerSpans != null && innerSpans.length > 0) { textPath = new LinkPath(true); textPath.setAllowReset(false); for (int a = 0; a < innerSpans.length; a++) { int start = spanned.getSpanStart(innerSpans[a]); int end = spanned.getSpanEnd(innerSpans[a]); textPath.setCurrentLayout(result, start, 0); int shift = innerSpans[a].getTextPaint() != null ? innerSpans[a].getTextPaint().baselineShift : 0; textPath.setBaselineShift(shift != 0 ? shift + AndroidUtilities.dp(shift > 0 ? 5 : -2) : 0); result.getSelectionPath(start, end, textPath); } textPath.setAllowReset(true); } } catch (Exception ignore) { } try { TextPaintMarkSpan[] innerSpans = spanned.getSpans(0, spanned.length(), TextPaintMarkSpan.class); if (innerSpans != null && innerSpans.length > 0) { markPath = new LinkPath(true); markPath.setAllowReset(false); for (int a = 0; a < innerSpans.length; a++) { int start = spanned.getSpanStart(innerSpans[a]); int end = spanned.getSpanEnd(innerSpans[a]); markPath.setCurrentLayout(result, start, 0); int shift = innerSpans[a].getTextPaint() != null ? innerSpans[a].getTextPaint().baselineShift : 0; markPath.setBaselineShift(shift != 0 ? shift + AndroidUtilities.dp(shift > 0 ? 5 : -2) : 0); result.getSelectionPath(start, end, markPath); } markPath.setAllowReset(true); } } catch (Exception ignore) { } } DrawingText drawingText = new DrawingText(); drawingText.textLayout = result; drawingText.textPath = textPath; drawingText.markPath = markPath; drawingText.parentBlock = parentBlock; drawingText.parentText = richText; return drawingText; } private void drawLayoutLink(Canvas canvas, DrawingText layout) { if (canvas == null || layout == null || pressedLinkOwnerLayout != layout) { return; } if (pressedLink != null) { canvas.drawPath(urlPath, urlPaint); } else if (drawBlockSelection && layout != null) { float width; float x; if (layout.getLineCount() == 1) { width = layout.getLineWidth(0); x = layout.getLineLeft(0); } else { width = layout.getWidth(); x = 0; } canvas.drawRect(-AndroidUtilities.dp(2) + x, 0, x + width + AndroidUtilities.dp(2), layout.getHeight(), urlPaint); } } private boolean checkLayoutForLinks(WebpageAdapter adapter, MotionEvent event, View parentView, DrawingText drawingText, int layoutX, int layoutY) { if (pageSwitchAnimation != null || parentView == null || !textSelectionHelper.isSelectable(parentView)) { return false; } pressedLinkOwnerView = parentView; if (drawingText != null) { StaticLayout layout = drawingText.textLayout; int x = (int) event.getX(); int y = (int) event.getY(); boolean removeLink = false; if (event.getAction() == MotionEvent.ACTION_DOWN) { float width = 0; float left = Integer.MAX_VALUE; for (int a = 0, N = layout.getLineCount(); a < N; a++) { width = Math.max(layout.getLineWidth(a), width); left = Math.min(layout.getLineLeft(a), left); } if (x >= layoutX + left && x <= left + layoutX + width && y >= layoutY && y <= layoutY + layout.getHeight()) { pressedLinkOwnerLayout = drawingText; pressedLinkOwnerView = parentView; pressedLayoutY = layoutY; CharSequence text = layout.getText(); if (text instanceof Spannable) { try { int checkX = x - layoutX; int checkY = y - layoutY; final int line = layout.getLineForVertical(checkY); final int off = layout.getOffsetForHorizontal(line, checkX); left = layout.getLineLeft(line); if (left <= checkX && left + layout.getLineWidth(line) >= checkX) { Spannable buffer = (Spannable) layout.getText(); TextPaintUrlSpan[] link = buffer.getSpans(off, off, TextPaintUrlSpan.class); if (link != null && link.length > 0) { pressedLink = link[0]; int pressedStart = buffer.getSpanStart(pressedLink); int pressedEnd = buffer.getSpanEnd(pressedLink); for (int a = 1; a < link.length; a++) { TextPaintUrlSpan span = link[a]; int start = buffer.getSpanStart(span); int end = buffer.getSpanEnd(span); if (pressedStart > start || end > pressedEnd) { pressedLink = span; pressedStart = start; pressedEnd = end; } } try { urlPath.setUseRoundRect(true); urlPath.setCurrentLayout(layout, pressedStart, 0); int shift = pressedLink.getTextPaint() != null ? pressedLink.getTextPaint().baselineShift : 0; urlPath.setBaselineShift(shift != 0 ? shift + AndroidUtilities.dp(shift > 0 ? 5 : -2) : 0); layout.getSelectionPath(pressedStart, pressedEnd, urlPath); parentView.invalidate(); } catch (Exception e) { FileLog.e(e); } } } } catch (Exception e) { FileLog.e(e); } } } } else if (event.getAction() == MotionEvent.ACTION_UP) { if (pressedLink != null) { removeLink = true; String url = pressedLink.getUrl(); if (url != null) { if (linkSheet != null) { linkSheet.dismiss(); linkSheet = null; } int index; boolean isAnchor = false; String anchor; if ((index = url.lastIndexOf('#')) != -1) { String webPageUrl; if (!TextUtils.isEmpty(adapter.currentPage.cached_page.url)) { webPageUrl = adapter.currentPage.cached_page.url.toLowerCase(); } else { webPageUrl = adapter.currentPage.url.toLowerCase(); } try { anchor = URLDecoder.decode(url.substring(index + 1), "UTF-8"); } catch (Exception ignore) { anchor = ""; } if (url.toLowerCase().contains(webPageUrl)) { if (TextUtils.isEmpty(anchor)) { layoutManager[0].scrollToPositionWithOffset(0, 0); checkScrollAnimated(); } else { scrollToAnchor(anchor); } isAnchor = true; } } else { anchor = null; } if (!isAnchor) { openWebpageUrl(pressedLink.getUrl(), anchor); } } } } else if (event.getAction() == MotionEvent.ACTION_CANCEL && (popupWindow == null || !popupWindow.isShowing())) { removeLink = true; } if (removeLink) { removePressedLink(); } } if (event.getAction() == MotionEvent.ACTION_DOWN) { startCheckLongPress(event.getX(), event.getY(), parentView); } if (event.getAction() != MotionEvent.ACTION_DOWN && event.getAction() != MotionEvent.ACTION_MOVE) { cancelCheckLongPress(); } if (parentView instanceof BlockDetailsCell) { return pressedLink != null; } else { return pressedLinkOwnerLayout != null; } } private void removePressedLink() { if (pressedLink == null && pressedLinkOwnerView == null) { return; } View parentView = pressedLinkOwnerView; pressedLink = null; pressedLinkOwnerLayout = null; pressedLinkOwnerView = null; if (parentView != null) { parentView.invalidate(); } } private void openWebpageUrl(String url, String anchor) { if (openUrlReqId != 0) { ConnectionsManager.getInstance(currentAccount).cancelRequest(openUrlReqId, false); openUrlReqId = 0; } int reqId = ++lastReqId; showProgressView(true, true); final TLRPC.TL_messages_getWebPage req = new TLRPC.TL_messages_getWebPage(); req.url = url; req.hash = 0; openUrlReqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (openUrlReqId == 0 || reqId != lastReqId) { return; } openUrlReqId = 0; showProgressView(true, false); if (isVisible) { if (response instanceof TLRPC.TL_webPage && ((TLRPC.TL_webPage) response).cached_page instanceof TLRPC.TL_page) { addPageToStack((TLRPC.TL_webPage) response, anchor, 1); } else { Browser.openUrl(parentActivity, req.url); } } })); } @Override public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.messagePlayingDidStart) { MessageObject messageObject = (MessageObject) args[0]; if (listView != null) { for (int i = 0; i < listView.length; i++) { int count = listView[i].getChildCount(); for (int a = 0; a < count; a++) { View view = listView[i].getChildAt(a); if (view instanceof BlockAudioCell) { BlockAudioCell cell = (BlockAudioCell) view; cell.updateButtonState(true); } } } } } else if (id == NotificationCenter.messagePlayingDidReset || id == NotificationCenter.messagePlayingPlayStateChanged) { if (listView != null) { for (int i = 0; i < listView.length; i++) { int count = listView[i].getChildCount(); for (int a = 0; a < count; a++) { View view = listView[i].getChildAt(a); if (view instanceof BlockAudioCell) { BlockAudioCell cell = (BlockAudioCell) view; MessageObject messageObject = cell.getMessageObject(); if (messageObject != null) { cell.updateButtonState(true); } } } } } } else if (id == NotificationCenter.messagePlayingProgressDidChanged) { Integer mid = (Integer) args[0]; if (listView != null) { for (int i = 0; i < listView.length; i++) { int count = listView[i].getChildCount(); for (int a = 0; a < count; a++) { View view = listView[i].getChildAt(a); if (view instanceof BlockAudioCell) { BlockAudioCell cell = (BlockAudioCell) view; MessageObject playing = cell.getMessageObject(); if (playing != null && playing.getId() == mid) { MessageObject player = MediaController.getInstance().getPlayingMessageObject(); if (player != null) { playing.audioProgress = player.audioProgress; playing.audioProgressSec = player.audioProgressSec; playing.audioPlayerDuration = player.audioPlayerDuration; cell.updatePlayingMessageProgress(); } break; } } } } } } } public void updateThemeColors(float progress) { refreshThemeColors(); updatePaintColors(); if (windowView != null) { listView[0].invalidateViews(); listView[1].invalidateViews(); windowView.invalidate(); searchPanel.invalidate(); if (progress == 1) { adapter[0].notifyDataSetChanged(); adapter[1].notifyDataSetChanged(); } } } private void updatePaintSize() { for (int i = 0; i < 2; i++) { adapter[i].notifyDataSetChanged(); } } private void updatePaintFonts() { ApplicationLoader.applicationContext.getSharedPreferences("articles", Activity.MODE_PRIVATE).edit().putInt("font_type", selectedFont).commit(); Typeface typefaceNormal = selectedFont == 0 ? Typeface.DEFAULT : Typeface.SERIF; Typeface typefaceItalic = selectedFont == 0 ? AndroidUtilities.getTypeface("fonts/ritalic.ttf") : Typeface.create("serif", Typeface.ITALIC); Typeface typefaceBold = selectedFont == 0 ? AndroidUtilities.getTypeface("fonts/rmedium.ttf") : Typeface.create("serif", Typeface.BOLD); Typeface typefaceBoldItalic = selectedFont == 0 ? AndroidUtilities.getTypeface("fonts/rmediumitalic.ttf") : Typeface.create("serif", Typeface.BOLD_ITALIC); for (int a = 0; a < quoteTextPaints.size(); a++) { updateFontEntry(quoteTextPaints.keyAt(a), quoteTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < preformattedTextPaints.size(); a++) { updateFontEntry(preformattedTextPaints.keyAt(a), preformattedTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < paragraphTextPaints.size(); a++) { updateFontEntry(paragraphTextPaints.keyAt(a), paragraphTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < listTextPaints.size(); a++) { updateFontEntry(listTextPaints.keyAt(a), listTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < embedPostTextPaints.size(); a++) { updateFontEntry(embedPostTextPaints.keyAt(a), embedPostTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < mediaCaptionTextPaints.size(); a++) { updateFontEntry(mediaCaptionTextPaints.keyAt(a), mediaCaptionTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < mediaCreditTextPaints.size(); a++) { updateFontEntry(mediaCreditTextPaints.keyAt(a), mediaCreditTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < photoCaptionTextPaints.size(); a++) { updateFontEntry(photoCaptionTextPaints.keyAt(a), photoCaptionTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < photoCreditTextPaints.size(); a++) { updateFontEntry(photoCreditTextPaints.keyAt(a), photoCreditTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < authorTextPaints.size(); a++) { updateFontEntry(authorTextPaints.keyAt(a), authorTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < footerTextPaints.size(); a++) { updateFontEntry(footerTextPaints.keyAt(a), footerTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < embedPostCaptionTextPaints.size(); a++) { updateFontEntry(embedPostCaptionTextPaints.keyAt(a), embedPostCaptionTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < relatedArticleTextPaints.size(); a++) { updateFontEntry(relatedArticleTextPaints.keyAt(a), relatedArticleTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < detailsTextPaints.size(); a++) { updateFontEntry(detailsTextPaints.keyAt(a), detailsTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } for (int a = 0; a < tableTextPaints.size(); a++) { updateFontEntry(tableTextPaints.keyAt(a), tableTextPaints.valueAt(a), typefaceNormal, typefaceBoldItalic, typefaceBold, typefaceItalic); } } private void updateFontEntry(int flags, TextPaint paint, Typeface typefaceNormal, Typeface typefaceBoldItalic, Typeface typefaceBold, Typeface typefaceItalic) { if ((flags & TEXT_FLAG_MEDIUM) != 0 && (flags & TEXT_FLAG_ITALIC) != 0) { paint.setTypeface(typefaceBoldItalic); } else if ((flags & TEXT_FLAG_MEDIUM) != 0) { paint.setTypeface(typefaceBold); } else if ((flags & TEXT_FLAG_ITALIC) != 0) { paint.setTypeface(typefaceItalic); } else if ((flags & TEXT_FLAG_MONO) != 0) { //change nothing } else { paint.setTypeface(typefaceNormal); } } private void updatePaintColors() { backgroundPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite)); for (int i = 0; i < listView.length; i++) { listView[i].setGlowColor(Theme.getColor(Theme.key_windowBackgroundWhite)); } if (listTextPointerPaint != null) { listTextPointerPaint.setColor(getTextColor()); } if (listTextNumPaint != null) { listTextNumPaint.setColor(getTextColor()); } if (embedPostAuthorPaint != null) { embedPostAuthorPaint.setColor(getTextColor()); } if (channelNamePaint != null) { channelNamePaint.setColor(getTextColor()); } if (channelNamePhotoPaint != null) { channelNamePhotoPaint.setColor(0xffffffff); } if (relatedArticleHeaderPaint != null) { relatedArticleHeaderPaint.setColor(getTextColor()); } if (relatedArticleTextPaint != null) { relatedArticleTextPaint.setColor(getGrayTextColor()); } if (embedPostDatePaint != null) { embedPostDatePaint.setColor(getGrayTextColor()); } createPaint(true); setMapColors(titleTextPaints); setMapColors(kickerTextPaints); setMapColors(subtitleTextPaints); setMapColors(headerTextPaints); setMapColors(subheaderTextPaints); setMapColors(quoteTextPaints); setMapColors(preformattedTextPaints); setMapColors(paragraphTextPaints); setMapColors(listTextPaints); setMapColors(embedPostTextPaints); setMapColors(mediaCaptionTextPaints); setMapColors(mediaCreditTextPaints); setMapColors(photoCaptionTextPaints); setMapColors(photoCreditTextPaints); setMapColors(authorTextPaints); setMapColors(footerTextPaints); setMapColors(embedPostCaptionTextPaints); setMapColors(relatedArticleTextPaints); setMapColors(detailsTextPaints); setMapColors(tableTextPaints); } private void setMapColors(SparseArray map) { for (int a = 0; a < map.size(); a++) { int flags = map.keyAt(a); TextPaint paint = map.valueAt(a); if ((flags & TEXT_FLAG_URL) != 0 || (flags & TEXT_FLAG_WEBPAGE_URL) != 0) { paint.setColor(getLinkTextColor()); } else { paint.setColor(getTextColor()); } } } public void setParentActivity(Activity activity, BaseFragment fragment) { parentFragment = fragment; currentAccount = UserConfig.selectedAccount; NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingProgressDidChanged); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidReset); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingPlayStateChanged); NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.messagePlayingDidStart); if (parentActivity == activity) { updatePaintColors(); refreshThemeColors(); return; } parentActivity = activity; SharedPreferences sharedPreferences = ApplicationLoader.applicationContext.getSharedPreferences("articles", Activity.MODE_PRIVATE); selectedFont = sharedPreferences.getInt("font_type", 0); createPaint(false); backgroundPaint = new Paint(); layerShadowDrawable = activity.getResources().getDrawable(R.drawable.layer_shadow); slideDotDrawable = activity.getResources().getDrawable(R.drawable.slide_dot_small); slideDotBigDrawable = activity.getResources().getDrawable(R.drawable.slide_dot_big); scrimPaint = new Paint(); windowView = new WindowView(activity); windowView.setWillNotDraw(false); windowView.setClipChildren(true); windowView.setFocusable(false); containerView = new FrameLayout(activity) { @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { if (windowView.movingPage) { int width = getMeasuredWidth(); int translationX = (int) listView[0].getTranslationX(); int clipLeft = 0; int clipRight = width; if (child == listView[1]) { clipRight = translationX; } else if (child == listView[0]) { clipLeft = translationX; } final int restoreCount = canvas.save(); canvas.clipRect(clipLeft, 0, clipRight, getHeight()); final boolean result = super.drawChild(canvas, child, drawingTime); canvas.restoreToCount(restoreCount); if (translationX != 0) { if (child == listView[0]) { final float alpha = Math.max(0, Math.min((width - translationX) / (float) AndroidUtilities.dp(20), 1.0f)); layerShadowDrawable.setBounds(translationX - layerShadowDrawable.getIntrinsicWidth(), child.getTop(), translationX, child.getBottom()); layerShadowDrawable.setAlpha((int) (0xff * alpha)); layerShadowDrawable.draw(canvas); } else if (child == listView[1]) { float opacity = Math.min(0.8f, (width - translationX) / (float) width); if (opacity < 0) { opacity = 0; } scrimPaint.setColor((int) (((0x99000000 & 0xff000000) >>> 24) * opacity) << 24); canvas.drawRect(clipLeft, 0, clipRight, getHeight(), scrimPaint); } } return result; } else { return super.drawChild(canvas, child, drawingTime); } } }; windowView.addView(containerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.LEFT)); //containerView.setFitsSystemWindows(true); if (Build.VERSION.SDK_INT >= 21) { windowView.setFitsSystemWindows(true); containerView.setOnApplyWindowInsetsListener((v, insets) -> { if (Build.VERSION.SDK_INT >= 30) { return WindowInsets.CONSUMED; } else { return insets.consumeSystemWindowInsets(); } }); } fullscreenVideoContainer = new FrameLayout(activity); fullscreenVideoContainer.setBackgroundColor(0xff000000); fullscreenVideoContainer.setVisibility(View.INVISIBLE); windowView.addView(fullscreenVideoContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); fullscreenAspectRatioView = new AspectRatioFrameLayout(activity); fullscreenAspectRatioView.setVisibility(View.GONE); fullscreenVideoContainer.addView(fullscreenAspectRatioView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER)); fullscreenTextureView = new TextureView(activity); listView = new RecyclerListView[2]; adapter = new WebpageAdapter[2]; layoutManager = new LinearLayoutManager[2]; for (int i = 0; i < listView.length; i++) { WebpageAdapter webpageAdapter = adapter[i] = new WebpageAdapter(parentActivity); listView[i] = new RecyclerListView(activity) { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); int count = getChildCount(); for (int a = 0; a < count; a++) { View child = getChildAt(a); if (child.getTag() instanceof Integer) { Integer tag = (Integer) child.getTag(); if (tag == 90) { int bottom = child.getBottom(); if (bottom < getMeasuredHeight()) { int height = getMeasuredHeight(); child.layout(0, height - child.getMeasuredHeight(), child.getMeasuredWidth(), height); break; } } } } } @Override public boolean onInterceptTouchEvent(MotionEvent e) { if (pressedLinkOwnerLayout != null && pressedLink == null && (popupWindow == null || !popupWindow.isShowing()) && (e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_CANCEL)) { pressedLink = null; pressedLinkOwnerLayout = null; pressedLinkOwnerView = null; } else if (pressedLinkOwnerLayout != null && pressedLink != null && e.getAction() == MotionEvent.ACTION_UP) { checkLayoutForLinks(webpageAdapter, e, pressedLinkOwnerView, pressedLinkOwnerLayout, 0, 0); } return super.onInterceptTouchEvent(e); } @Override public boolean onTouchEvent(MotionEvent e) { if (pressedLinkOwnerLayout != null && pressedLink == null && (popupWindow == null || !popupWindow.isShowing()) && (e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_CANCEL)) { pressedLink = null; pressedLinkOwnerLayout = null; pressedLinkOwnerView = null; } return super.onTouchEvent(e); } @Override public void setTranslationX(float translationX) { super.setTranslationX(translationX); if (windowView.movingPage) { containerView.invalidate(); float progress = translationX / getMeasuredWidth(); setCurrentHeaderHeight((int) (windowView.startMovingHeaderHeight + (AndroidUtilities.dp(56) - windowView.startMovingHeaderHeight) * progress)); } } }; ((DefaultItemAnimator) listView[i].getItemAnimator()).setDelayAnimations(false); listView[i].setLayoutManager(layoutManager[i] = new LinearLayoutManager(parentActivity, LinearLayoutManager.VERTICAL, false)); listView[i].setAdapter(webpageAdapter); listView[i].setClipToPadding(false); listView[i].setVisibility(i == 0 ? View.VISIBLE : View.GONE); listView[i].setPadding(0, AndroidUtilities.dp(56), 0, 0); listView[i].setTopGlowOffset(AndroidUtilities.dp(56)); containerView.addView(listView[i], LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); listView[i].setOnItemLongClickListener((view, position) -> { if (view instanceof BlockRelatedArticlesCell) { BlockRelatedArticlesCell cell = (BlockRelatedArticlesCell) view; showCopyPopup(cell.currentBlock.parent.articles.get(cell.currentBlock.num).url); return true; } return false; }); listView[i].setOnItemClickListener((view, position, x, y) -> { if (textSelectionHelper != null) { if (textSelectionHelper.isSelectionMode()) { textSelectionHelper.clear(); return; } textSelectionHelper.clear(); } if (view instanceof ReportCell && webpageAdapter.currentPage != null) { ReportCell cell = (ReportCell) view; if (previewsReqId != 0 || cell.hasViews && x < view.getMeasuredWidth() / 2) { return; } TLObject object = MessagesController.getInstance(currentAccount).getUserOrChat("previews"); if (object instanceof TLRPC.TL_user) { openPreviewsChat((TLRPC.User) object, webpageAdapter.currentPage.id); } else { final int currentAccount = UserConfig.selectedAccount; final long pageId = webpageAdapter.currentPage.id; showProgressView(true, true); TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); req.username = "previews"; previewsReqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (previewsReqId == 0) { return; } previewsReqId = 0; showProgressView(true, false); if (response != null) { TLRPC.TL_contacts_resolvedPeer res = (TLRPC.TL_contacts_resolvedPeer) response; MessagesController.getInstance(currentAccount).putUsers(res.users, false); MessagesStorage.getInstance(currentAccount).putUsersAndChats(res.users, res.chats, false, true); if (!res.users.isEmpty()) { openPreviewsChat(res.users.get(0), pageId); } } })); } } else if (position >= 0 && position < webpageAdapter.localBlocks.size()) { TLRPC.PageBlock pageBlock = webpageAdapter.localBlocks.get(position); TLRPC.PageBlock originalBlock = pageBlock; pageBlock = getLastNonListPageBlock(pageBlock); if (pageBlock instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild detailsChild = (TL_pageBlockDetailsChild) pageBlock; pageBlock = detailsChild.block; } if (pageBlock instanceof TLRPC.TL_pageBlockChannel) { TLRPC.TL_pageBlockChannel pageBlockChannel = (TLRPC.TL_pageBlockChannel) pageBlock; MessagesController.getInstance(currentAccount).openByUserName(pageBlockChannel.channel.username, parentFragment, 2); close(false, true); } else if (pageBlock instanceof TL_pageBlockRelatedArticlesChild) { TL_pageBlockRelatedArticlesChild pageBlockRelatedArticlesChild = (TL_pageBlockRelatedArticlesChild) pageBlock; openWebpageUrl(pageBlockRelatedArticlesChild.parent.articles.get(pageBlockRelatedArticlesChild.num).url, null); } else if (pageBlock instanceof TLRPC.TL_pageBlockDetails) { view = getLastNonListCell(view); if (!(view instanceof BlockDetailsCell)) { return; } pressedLinkOwnerLayout = null; pressedLinkOwnerView = null; int index = webpageAdapter.blocks.indexOf(originalBlock); if (index < 0) { return; } TLRPC.TL_pageBlockDetails pageBlockDetails = (TLRPC.TL_pageBlockDetails) pageBlock; pageBlockDetails.open = !pageBlockDetails.open; int oldCount = webpageAdapter.getItemCount(); webpageAdapter.updateRows(); int newCount = webpageAdapter.getItemCount(); int changeCount = Math.abs(newCount - oldCount); BlockDetailsCell cell = (BlockDetailsCell) view; cell.arrow.setAnimationProgressAnimated(pageBlockDetails.open ? 0.0f : 1.0f); cell.invalidate(); if (changeCount != 0) { if (pageBlockDetails.open) { webpageAdapter.notifyItemRangeInserted(position + 1, changeCount); } else { webpageAdapter.notifyItemRangeRemoved(position + 1, changeCount); } } } } }); listView[i].setOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { textSelectionHelper.stopScrolling(); } } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { if (recyclerView.getChildCount() == 0) { return; } textSelectionHelper.onParentScrolled(); headerView.invalidate(); checkScroll(dy); } }); } headerPaint.setColor(0xff000000); statusBarPaint.setColor(0xff000000); headerProgressPaint.setColor(0xff242426); headerView = new FrameLayout(activity) { @Override protected void onDraw(Canvas canvas) { int width = getMeasuredWidth(); int height = getMeasuredHeight(); canvas.drawRect(0, 0, width, height, headerPaint); if (layoutManager == null) { return; } int first = layoutManager[0].findFirstVisibleItemPosition(); int last = layoutManager[0].findLastVisibleItemPosition(); int count = layoutManager[0].getItemCount(); View view; if (last >= count - 2) { view = layoutManager[0].findViewByPosition(count - 2); } else { view = layoutManager[0].findViewByPosition(first); } if (view == null) { return; } float itemProgress = width / (float) (count - 1); int childCount = layoutManager[0].getChildCount(); float viewHeight = view.getMeasuredHeight(); float viewProgress; if (last >= count - 2) { viewProgress = (count - 2 - first) * itemProgress * (listView[0].getMeasuredHeight() - view.getTop()) / viewHeight; } else { viewProgress = itemProgress * (1.0f - (Math.min(0, view.getTop() - listView[0].getPaddingTop()) + viewHeight) / viewHeight); } float progress = first * itemProgress + viewProgress; canvas.drawRect(0, 0, progress, height, headerProgressPaint); } }; headerView.setWillNotDraw(false); containerView.addView(headerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 56)); headerView.setOnClickListener(v -> listView[0].smoothScrollToPosition(0)); titleTextView = new SimpleTextView(activity); titleTextView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); titleTextView.setTextSize(20); titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); titleTextView.setTextColor(0xffb3b3b3); titleTextView.setPivotX(0.0f); titleTextView.setPivotY(AndroidUtilities.dp(28)); headerView.addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 56, Gravity.LEFT | Gravity.TOP, 72, 0, 48 * 2, 0)); lineProgressView = new LineProgressView(activity); lineProgressView.setProgressColor(0xffffffff); lineProgressView.setPivotX(0.0f); lineProgressView.setPivotY(AndroidUtilities.dp(2)); headerView.addView(lineProgressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 2, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 1)); lineProgressTickRunnable = () -> { float progressLeft = 0.7f - lineProgressView.getCurrentProgress(); if (progressLeft > 0.0f) { float tick; if (progressLeft < 0.25f) { tick = 0.01f; } else { tick = 0.02f; } lineProgressView.setProgress(lineProgressView.getCurrentProgress() + tick, true); AndroidUtilities.runOnUIThread(lineProgressTickRunnable, 100); } }; menuContainer = new FrameLayout(activity); headerView.addView(menuContainer, LayoutHelper.createFrame(48, 56, Gravity.TOP | Gravity.RIGHT)); searchShadow = new View(activity); searchShadow.setBackgroundResource(R.drawable.header_shadow); searchShadow.setAlpha(0.0f); containerView.addView(searchShadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 3, Gravity.LEFT | Gravity.TOP, 0, 56, 0, 0)); searchContainer = new FrameLayout(parentActivity); searchContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); searchContainer.setVisibility(View.INVISIBLE); if (Build.VERSION.SDK_INT < 21) { searchContainer.setAlpha(0.0f); } headerView.addView(searchContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 56)); searchField = new EditTextBoldCursor(parentActivity) { @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { if (!AndroidUtilities.showKeyboard(this)) { clearFocus(); requestFocus(); } } return super.onTouchEvent(event); } }; searchField.setCursorWidth(1.5f); searchField.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); searchField.setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); searchField.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18); searchField.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); searchField.setSingleLine(true); searchField.setHint(LocaleController.getString("Search", R.string.Search)); searchField.setBackgroundResource(0); searchField.setPadding(0, 0, 0, 0); int inputType = searchField.getInputType() | EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS; searchField.setInputType(inputType); if (Build.VERSION.SDK_INT < 23) { searchField.setCustomSelectionActionModeCallback(new ActionMode.Callback() { public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return false; } public void onDestroyActionMode(ActionMode mode) { } public boolean onCreateActionMode(ActionMode mode, Menu menu) { return false; } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { return false; } }); } searchField.setOnEditorActionListener((v, actionId, event) -> { if (event != null && (event.getAction() == KeyEvent.ACTION_UP && event.getKeyCode() == KeyEvent.KEYCODE_SEARCH || event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) { AndroidUtilities.hideKeyboard(searchField); } return false; }); searchField.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (ignoreOnTextChange) { ignoreOnTextChange = false; return; } processSearch(s.toString().toLowerCase()); if (clearButton != null) { if (TextUtils.isEmpty(s)) { if (clearButton.getTag() != null) { clearButton.setTag(null); clearButton.clearAnimation(); if (animateClear) { clearButton.animate().setInterpolator(new DecelerateInterpolator()).alpha(0.0f).setDuration(180).scaleY(0.0f).scaleX(0.0f).rotation(45).withEndAction(() -> clearButton.setVisibility(View.INVISIBLE)).start(); } else { clearButton.setAlpha(0.0f); clearButton.setRotation(45); clearButton.setScaleX(0.0f); clearButton.setScaleY(0.0f); clearButton.setVisibility(View.INVISIBLE); animateClear = true; } } } else { if (clearButton.getTag() == null) { clearButton.setTag(1); clearButton.clearAnimation(); clearButton.setVisibility(View.VISIBLE); if (animateClear) { clearButton.animate().setInterpolator(new DecelerateInterpolator()).alpha(1.0f).setDuration(180).scaleY(1.0f).scaleX(1.0f).rotation(0).start(); } else { clearButton.setAlpha(1.0f); clearButton.setRotation(0); clearButton.setScaleX(1.0f); clearButton.setScaleY(1.0f); animateClear = true; } } } } } @Override public void afterTextChanged(Editable s) { } }); searchField.setImeOptions(EditorInfo.IME_FLAG_NO_FULLSCREEN | EditorInfo.IME_ACTION_SEARCH); searchField.setTextIsSelectable(false); searchContainer.addView(searchField, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 36, Gravity.CENTER_VERTICAL, 72, 0, 48, 0)); clearButton = new ImageView(parentActivity) { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); clearAnimation(); if (getTag() == null) { clearButton.setVisibility(INVISIBLE); clearButton.setAlpha(0.0f); clearButton.setRotation(45); clearButton.setScaleX(0.0f); clearButton.setScaleY(0.0f); } else { clearButton.setAlpha(1.0f); clearButton.setRotation(0); clearButton.setScaleX(1.0f); clearButton.setScaleY(1.0f); } } }; clearButton.setImageDrawable(new CloseProgressDrawable2()); clearButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.MULTIPLY)); clearButton.setScaleType(ImageView.ScaleType.CENTER); clearButton.setAlpha(0.0f); clearButton.setRotation(45); clearButton.setScaleX(0.0f); clearButton.setScaleY(0.0f); clearButton.setOnClickListener(v -> { if (searchField.length() != 0) { searchField.setText(""); } searchField.requestFocus(); AndroidUtilities.showKeyboard(searchField); }); clearButton.setContentDescription(LocaleController.getString("ClearButton", R.string.ClearButton)); searchContainer.addView(clearButton, LayoutHelper.createFrame(48, LayoutHelper.MATCH_PARENT, Gravity.CENTER_VERTICAL | Gravity.RIGHT)); backButton = new ImageView(activity); backButton.setScaleType(ImageView.ScaleType.CENTER); backDrawable = new BackDrawable(false); backDrawable.setAnimationTime(200.0f); backDrawable.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); backDrawable.setRotatedColor(0xffb3b3b3); backDrawable.setRotation(1.0f, false); backButton.setImageDrawable(backDrawable); backButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); headerView.addView(backButton, LayoutHelper.createFrame(54, 56)); backButton.setOnClickListener(v -> { /*if (collapsed) { uncollapse(); } else { collapse(); }*/ if (searchContainer.getTag() != null) { showSearch(false); } else { close(true, true); } }); backButton.setContentDescription(LocaleController.getString("AccDescrGoBack", R.string.AccDescrGoBack)); menuButton = new ActionBarMenuItem(parentActivity, null, Theme.ACTION_BAR_WHITE_SELECTOR_COLOR, 0xffb3b3b3) { @Override public void toggleSubMenu() { super.toggleSubMenu(); listView[0].stopScroll(); checkScrollAnimated(); } }; menuButton.setLayoutInScreen(true); menuButton.setDuplicateParentStateEnabled(false); menuButton.setClickable(true); menuButton.setIcon(R.drawable.ic_ab_other); menuButton.addSubItem(search_item, R.drawable.msg_search, LocaleController.getString("Search", R.string.Search)); menuButton.addSubItem(share_item, R.drawable.msg_share, LocaleController.getString("ShareFile", R.string.ShareFile)); menuButton.addSubItem(open_item, R.drawable.msg_openin, LocaleController.getString("OpenInExternalApp", R.string.OpenInExternalApp)); menuButton.addSubItem(settings_item, R.drawable.menu_settings, LocaleController.getString("Settings", R.string.Settings)); menuButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.ACTION_BAR_WHITE_SELECTOR_COLOR)); menuButton.setContentDescription(LocaleController.getString("AccDescrMoreOptions", R.string.AccDescrMoreOptions)); menuContainer.addView(menuButton, LayoutHelper.createFrame(48, 56)); progressView = new ContextProgressView(activity, 2); progressView.setVisibility(View.GONE); menuContainer.addView(progressView, LayoutHelper.createFrame(48, 56)); menuButton.setOnClickListener(v -> menuButton.toggleSubMenu()); menuButton.setDelegate(id -> { if (adapter[0].currentPage == null || parentActivity == null) { return; } if (id == search_item) { showSearch(true); } else if (id == share_item) { showDialog(new ShareAlert(parentActivity, null, adapter[0].currentPage.url, false, adapter[0].currentPage.url, false)); } else if (id == open_item) { String webPageUrl; if (!TextUtils.isEmpty(adapter[0].currentPage.cached_page.url)) { webPageUrl = adapter[0].currentPage.cached_page.url; } else { webPageUrl = adapter[0].currentPage.url; } Browser.openUrl(parentActivity, webPageUrl, true, false); } else if (id == settings_item) { BottomSheet.Builder builder = new BottomSheet.Builder(parentActivity); builder.setApplyTopPadding(false); LinearLayout settingsContainer = new LinearLayout(parentActivity); settingsContainer.setPadding(0, 0, 0, AndroidUtilities.dp(4)); settingsContainer.setOrientation(LinearLayout.VERTICAL); HeaderCell headerCell = new HeaderCell(parentActivity); headerCell.setText(LocaleController.getString("FontSize", R.string.FontSize)); settingsContainer.addView(headerCell, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 3, 1, 3, 0)); TextSizeCell sizeCell = new TextSizeCell(parentActivity); settingsContainer.addView(sizeCell, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 3, 0, 3, 0)); headerCell = new HeaderCell(parentActivity); headerCell.setText(LocaleController.getString("FontType", R.string.FontType)); settingsContainer.addView(headerCell, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 3, 4, 3, 2)); for (int a = 0; a < 2; a++) { fontCells[a] = new FontCell(parentActivity); switch (a) { case 0: fontCells[a].setTextAndTypeface(LocaleController.getString("Default", R.string.Default), Typeface.DEFAULT); break; case 1: fontCells[a].setTextAndTypeface("Serif", Typeface.SERIF); break; } fontCells[a].select(a == selectedFont, false); fontCells[a].setTag(a); fontCells[a].setOnClickListener(v -> { int num = (Integer) v.getTag(); selectedFont = num; for (int a1 = 0; a1 < 2; a1++) { fontCells[a1].select(a1 == num, true); } updatePaintFonts(); for (int i = 0; i < listView.length; i++) { adapter[i].notifyDataSetChanged(); } }); settingsContainer.addView(fontCells[a], LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 50)); } builder.setCustomView(settingsContainer); showDialog(linkSheet = builder.create()); } }); searchPanel = new FrameLayout(parentActivity) { @Override public void onDraw(Canvas canvas) { int bottom = Theme.chat_composeShadowDrawable.getIntrinsicHeight(); Theme.chat_composeShadowDrawable.setBounds(0, 0, getMeasuredWidth(), bottom); Theme.chat_composeShadowDrawable.draw(canvas); canvas.drawRect(0, bottom, getMeasuredWidth(), getMeasuredHeight(), Theme.chat_composeBackgroundPaint); } }; searchPanel.setOnTouchListener((v, event) -> true); searchPanel.setWillNotDraw(false); searchPanel.setVisibility(View.INVISIBLE); searchPanel.setFocusable(true); searchPanel.setFocusableInTouchMode(true); searchPanel.setClickable(true); searchPanel.setPadding(0, AndroidUtilities.dp(3), 0, 0); containerView.addView(searchPanel, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 51, Gravity.BOTTOM)); searchUpButton = new ImageView(parentActivity); searchUpButton.setScaleType(ImageView.ScaleType.CENTER); searchUpButton.setImageResource(R.drawable.msg_go_up); searchUpButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.MULTIPLY)); searchUpButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_actionBarActionModeDefaultSelector), 1)); searchPanel.addView(searchUpButton, LayoutHelper.createFrame(48, 48, Gravity.RIGHT | Gravity.TOP, 0, 0, 48, 0)); searchUpButton.setOnClickListener(view -> scrollToSearchIndex(currentSearchIndex - 1)); searchUpButton.setContentDescription(LocaleController.getString("AccDescrSearchNext", R.string.AccDescrSearchNext)); searchDownButton = new ImageView(parentActivity); searchDownButton.setScaleType(ImageView.ScaleType.CENTER); searchDownButton.setImageResource(R.drawable.msg_go_down); searchDownButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.MULTIPLY)); searchDownButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_actionBarActionModeDefaultSelector), 1)); searchPanel.addView(searchDownButton, LayoutHelper.createFrame(48, 48, Gravity.RIGHT | Gravity.TOP, 0, 0, 0, 0)); searchDownButton.setOnClickListener(view -> scrollToSearchIndex(currentSearchIndex + 1)); searchDownButton.setContentDescription(LocaleController.getString("AccDescrSearchPrev", R.string.AccDescrSearchPrev)); searchCountText = new SimpleTextView(parentActivity); searchCountText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); searchCountText.setTextSize(15); searchCountText.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); searchCountText.setGravity(Gravity.LEFT); searchPanel.addView(searchCountText, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.CENTER_VERTICAL, 18, 0, 108, 0)); windowLayoutParams = new WindowManager.LayoutParams(); windowLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT; windowLayoutParams.format = PixelFormat.TRANSLUCENT; windowLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT; windowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; windowLayoutParams.type = WindowManager.LayoutParams.LAST_APPLICATION_WINDOW - 1; windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; if (Build.VERSION.SDK_INT >= 21) { windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; if (Build.VERSION.SDK_INT >= 28) { windowLayoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; } } textSelectionHelper = new TextSelectionHelper.ArticleTextSelectionHelper(); textSelectionHelper.setParentView(listView[0]); textSelectionHelper.layoutManager = layoutManager[0]; textSelectionHelper.setCallback(new TextSelectionHelper.Callback() { @Override public void onStateChanged(boolean isSelected) { if (isSelected) { showSearch(false); } } @Override public void onTextCopied() { BulletinFactory.of(containerView, null).createCopyBulletin(LocaleController.getString("TextCopied", R.string.TextCopied)).show(); } }); containerView.addView(textSelectionHelper.getOverlayView(activity)); pinchToZoomHelper = new PinchToZoomHelper(containerView, windowView); pinchToZoomHelper.setClipBoundsListener(new PinchToZoomHelper.ClipBoundsListener() { @Override public void getClipTopBottom(float[] topBottom) { topBottom[0] = currentHeaderHeight; topBottom[1] = listView[0].getMeasuredHeight(); } }); pinchToZoomHelper.setCallback(new PinchToZoomHelper.Callback() { @Override public void onZoomStarted(MessageObject messageObject) { if (listView[0] != null) { listView[0].cancelClickRunnables(true); } } }); updatePaintColors(); } private void showSearch(boolean show) { if (searchContainer == null || (searchContainer.getTag() != null) == show) { return; } searchContainer.setTag(show ? 1 : null); searchResults.clear(); searchText = null; adapter[0].searchTextOffset.clear(); currentSearchIndex = 0; if (attachedToWindow) { AnimatorSet animatorSet = new AnimatorSet(); animatorSet.setDuration(250); if (show) { searchContainer.setVisibility(View.VISIBLE); backDrawable.setRotation(0.0f, true); } else { menuButton.setVisibility(View.VISIBLE); listView[0].invalidateViews(); AndroidUtilities.hideKeyboard(searchField); updateWindowLayoutParamsForSearch(); } ArrayList animators = new ArrayList<>(); if (Build.VERSION.SDK_INT >= 21) { if (show) { searchContainer.setAlpha(1.0f); } int x = menuContainer.getLeft() + menuContainer.getMeasuredWidth() / 2; int y = menuContainer.getTop() + menuContainer.getMeasuredHeight() / 2; float rad = (float) Math.sqrt(x * x + y * y); Animator animator = ViewAnimationUtils.createCircularReveal(searchContainer, x, y, show ? 0 : rad, show ? rad : 0); animators.add(animator); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (!show) { searchContainer.setAlpha(0.0f); } } }); } else { animators.add(ObjectAnimator.ofFloat(searchContainer, View.ALPHA, show ? 1.0f : 0.0f)); } if (!show) { animators.add(ObjectAnimator.ofFloat(searchPanel, View.ALPHA, 0.0f)); } animators.add(ObjectAnimator.ofFloat(searchShadow, View.ALPHA, show ? 1.0f : 0.0f)); animatorSet.playTogether(animators); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (show) { updateWindowLayoutParamsForSearch(); searchField.requestFocus(); AndroidUtilities.showKeyboard(searchField); menuButton.setVisibility(View.INVISIBLE); } else { searchContainer.setVisibility(View.INVISIBLE); searchPanel.setVisibility(View.INVISIBLE); searchField.setText(""); } } @Override public void onAnimationStart(Animator animation) { if (!show) { backDrawable.setRotation(1.0f, true); } } }); animatorSet.setInterpolator(CubicBezierInterpolator.EASE_OUT); if (!show && !AndroidUtilities.usingHardwareInput && keyboardVisible) { runAfterKeyboardClose = animatorSet; AndroidUtilities.runOnUIThread(() -> { if (runAfterKeyboardClose != null) { runAfterKeyboardClose.start(); runAfterKeyboardClose = null; } }, 300); } else { animatorSet.start(); } } else { searchContainer.setAlpha(show ? 1.0f : 0.0f); menuButton.setVisibility(show ? View.INVISIBLE : View.VISIBLE); backDrawable.setRotation(show ? 0.0f : 1.0f, false); searchShadow.setAlpha(show ? 1.0f : 0.0f); if (show) { searchContainer.setVisibility(View.VISIBLE); } else { searchContainer.setVisibility(View.INVISIBLE); searchPanel.setVisibility(View.INVISIBLE); searchField.setText(""); } updateWindowLayoutParamsForSearch(); } } private void updateWindowLayoutParamsForSearch() { /*try { WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); if (searchContainer.getTag() != null) { windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; } else { windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; } wm.updateViewLayout(windowView, windowLayoutParams); } catch (Exception e) { FileLog.e(e); }*/ } private void updateSearchButtons() { if (searchResults == null) { return; } searchUpButton.setEnabled(!searchResults.isEmpty() && currentSearchIndex != 0); searchDownButton.setEnabled(!searchResults.isEmpty() && currentSearchIndex != searchResults.size() - 1); searchUpButton.setAlpha(searchUpButton.isEnabled() ? 1.0f : 0.5f); searchDownButton.setAlpha(searchDownButton.isEnabled() ? 1.0f : 0.5f); int count = searchResults.size(); if (count < 0) { searchCountText.setText(""); } else if (count == 0) { searchCountText.setText(LocaleController.getString("NoResult", R.string.NoResult)); } else if (count == 1) { searchCountText.setText(LocaleController.getString("OneResult", R.string.OneResult)); } else { searchCountText.setText(String.format(LocaleController.getPluralString("CountOfResults", count), currentSearchIndex + 1, count)); } } private Runnable searchRunnable; private ArrayList searchResults = new ArrayList<>(); private String searchText; private int currentSearchIndex; private int lastSearchIndex = -1; private static class SearchResult { private int index; private Object text; private TLRPC.PageBlock block; } private void processSearch(final String text) { if (searchRunnable != null) { AndroidUtilities.cancelRunOnUIThread(searchRunnable); searchRunnable = null; } if (TextUtils.isEmpty(text)) { searchResults.clear(); searchText = text; adapter[0].searchTextOffset.clear(); searchPanel.setVisibility(View.INVISIBLE); listView[0].invalidateViews(); scrollToSearchIndex(0); lastSearchIndex = -1; return; } int searchIndex = ++lastSearchIndex; AndroidUtilities.runOnUIThread(searchRunnable = () -> { HashMap copy = new HashMap<>(adapter[0].textToBlocks); ArrayList array = new ArrayList<>(adapter[0].textBlocks); searchRunnable = null; Utilities.searchQueue.postRunnable(() -> { ArrayList results = new ArrayList<>(); for (int b = 0, N = array.size(); b < N; b++) { Object object = array.get(b); TLRPC.PageBlock block = copy.get(object); String textToSearchIn = null; if (object instanceof TLRPC.RichText) { TLRPC.RichText richText = (TLRPC.RichText) object; CharSequence innerText = getText(adapter[0], null, richText, richText, block, 1000); if (!TextUtils.isEmpty(innerText)) { textToSearchIn = innerText.toString().toLowerCase(); } } else if (object instanceof String) { textToSearchIn = ((String) object).toLowerCase(); } if (textToSearchIn != null) { int startIndex = 0; int index; while ((index = textToSearchIn.indexOf(text, startIndex)) >= 0) { startIndex = index + text.length(); if (index == 0 || AndroidUtilities.isPunctuationCharacter(textToSearchIn.charAt(index - 1))) { SearchResult result = new SearchResult(); result.index = index; result.block = block; result.text = object; results.add(result); } } } } AndroidUtilities.runOnUIThread(() -> { if (searchIndex == lastSearchIndex) { searchPanel.setAlpha(1.0f); searchPanel.setVisibility(View.VISIBLE); searchResults = results; searchText = text; adapter[0].searchTextOffset.clear(); listView[0].invalidateViews(); scrollToSearchIndex(0); } }); }); }, 400); } private void scrollToSearchIndex(int index) { if (index < 0 || index >= searchResults.size()) { updateSearchButtons(); return; } currentSearchIndex = index; updateSearchButtons(); SearchResult result = searchResults.get(index); TLRPC.PageBlock block = getLastNonListPageBlock(result.block); int row = -1; for (int a = 0, N = adapter[0].blocks.size(); a < N; a++) { TLRPC.PageBlock localBlock = adapter[0].blocks.get(a); if (localBlock instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild child = (TL_pageBlockDetailsChild) localBlock; if (child.block == result.block || child.block == block) { if (openAllParentBlocks(child)) { adapter[0].updateRows(); adapter[0].notifyDataSetChanged(); } break; } } } for (int a = 0, N = adapter[0].localBlocks.size(); a < N; a++) { TLRPC.PageBlock localBlock = adapter[0].localBlocks.get(a); if (localBlock == result.block || localBlock == block) { row = a; break; } else if (localBlock instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild child = (TL_pageBlockDetailsChild) localBlock; if (child.block == result.block || child.block == block) { row = a; break; } } } if (row == -1) { return; } if (block instanceof TL_pageBlockDetailsChild) { if (openAllParentBlocks((TL_pageBlockDetailsChild) block)) { adapter[0].updateRows(); adapter[0].notifyDataSetChanged(); } } String key = searchText + result.block + result.text + result.index; Integer offset = adapter[0].searchTextOffset.get(key); if (offset == null) { int type = adapter[0].getTypeForBlock(result.block); RecyclerView.ViewHolder holder = adapter[0].onCreateViewHolder(null, type); adapter[0].bindBlockToHolder(type, holder, result.block, 0, 0); holder.itemView.measure(View.MeasureSpec.makeMeasureSpec(listView[0].getMeasuredWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); offset = adapter[0].searchTextOffset.get(key); if (offset == null) { offset = 0; } } layoutManager[0].scrollToPositionWithOffset(row, currentHeaderHeight - AndroidUtilities.dp(56) - offset + AndroidUtilities.dp(100)); listView[0].invalidateViews(); } public static class ScrollEvaluator extends IntEvaluator { @Override public Integer evaluate(float fraction, Integer startValue, Integer endValue) { return super.evaluate(fraction, startValue, endValue); } } private void checkScrollAnimated() { int maxHeight = AndroidUtilities.dp(56); if (currentHeaderHeight == maxHeight) { return; } ValueAnimator va = ValueAnimator.ofObject(new IntEvaluator(), currentHeaderHeight, AndroidUtilities.dp(56)).setDuration(180); va.setInterpolator(new DecelerateInterpolator()); va.addUpdateListener(animation -> setCurrentHeaderHeight((int) animation.getAnimatedValue())); va.start(); } private void setCurrentHeaderHeight(int newHeight) { if (searchContainer.getTag() != null) { return; } int maxHeight = AndroidUtilities.dp(56); int minHeight = Math.max(AndroidUtilities.statusBarHeight, AndroidUtilities.dp(24)); if (newHeight < minHeight) { newHeight = minHeight; } else if (newHeight > maxHeight) { newHeight = maxHeight; } float heightDiff = maxHeight - minHeight; if (heightDiff == 0) { heightDiff = 1; } currentHeaderHeight = newHeight; float scale = 0.8f + (currentHeaderHeight - minHeight) / heightDiff * 0.2f; float scale2 = 0.5f + (currentHeaderHeight - minHeight) / heightDiff * 0.5f; backButton.setScaleX(scale); backButton.setScaleY(scale); backButton.setTranslationY((maxHeight - currentHeaderHeight) / 2); menuContainer.setScaleX(scale); menuContainer.setScaleY(scale); titleTextView.setScaleX(scale); titleTextView.setScaleY(scale); lineProgressView.setScaleY(scale2); menuContainer.setTranslationY((maxHeight - currentHeaderHeight) / 2); titleTextView.setTranslationY((maxHeight - currentHeaderHeight) / 2); headerView.setTranslationY(currentHeaderHeight - maxHeight); searchShadow.setTranslationY(currentHeaderHeight - maxHeight); menuButton.setAdditionalYOffset(-(currentHeaderHeight - maxHeight) / 2 + (Build.VERSION.SDK_INT < 21 ? AndroidUtilities.statusBarHeight : 0)); textSelectionHelper.setTopOffset(currentHeaderHeight); for (int i = 0; i < listView.length; i++) { listView[i].setTopGlowOffset(currentHeaderHeight); } } private void checkScroll(int dy) { setCurrentHeaderHeight(currentHeaderHeight - dy); } private void openPreviewsChat(TLRPC.User user, long wid) { if (user == null || !(parentActivity instanceof LaunchActivity)) { return; } Bundle args = new Bundle(); args.putLong("user_id", user.id); args.putString("botUser", "webpage" + wid); ((LaunchActivity) parentActivity).presentFragment(new ChatActivity(args), false, true); close(false, true); } public boolean open(MessageObject messageObject) { return open(messageObject, null, null, true); } public boolean open(TLRPC.TL_webPage webpage, String url) { return open(null, webpage, url, true); } private boolean open(final MessageObject messageObject, TLRPC.WebPage webpage, String url, boolean first) { if (parentActivity == null || isVisible && !collapsed || messageObject == null && webpage == null) { return false; } String anchor = null; if (messageObject != null) { webpage = messageObject.messageOwner.media.webpage; int index; for (int a = 0; a < messageObject.messageOwner.entities.size(); a++) { TLRPC.MessageEntity entity = messageObject.messageOwner.entities.get(a); if (entity instanceof TLRPC.TL_messageEntityUrl) { try { url = messageObject.messageOwner.message.substring(entity.offset, entity.offset + entity.length).toLowerCase(); String webPageUrl; if (!TextUtils.isEmpty(webpage.cached_page.url)) { webPageUrl = webpage.cached_page.url.toLowerCase(); } else { webPageUrl = webpage.url.toLowerCase(); } if (url.contains(webPageUrl) || webPageUrl.contains(url)) { if ((index = url.lastIndexOf('#')) != -1) { anchor = url.substring(index + 1); } break; } } catch (Exception e) { FileLog.e(e); } } } } else if (url != null) { int index; if ((index = url.lastIndexOf('#')) != -1) { anchor = url.substring(index + 1); } } pagesStack.clear(); collapsed = false; containerView.setTranslationX(0); containerView.setTranslationY(0); listView[0].setTranslationY(0); listView[0].setTranslationX(0.0f); listView[1].setTranslationX(0.0f); listView[0].setAlpha(1.0f); windowView.setInnerTranslationX(0); layoutManager[0].scrollToPositionWithOffset(0, 0); if (first) { setCurrentHeaderHeight(AndroidUtilities.dp(56)); } else { checkScrollAnimated(); } boolean scrolledToAnchor = addPageToStack(webpage, anchor, 0); if (first) { final String anchorFinal = !scrolledToAnchor && anchor != null ? anchor : null; TLRPC.TL_messages_getWebPage req = new TLRPC.TL_messages_getWebPage(); req.url = webpage.url; if (webpage.cached_page instanceof TLRPC.TL_pagePart_layer82 || webpage.cached_page.part) { req.hash = 0; } else { req.hash = webpage.hash; } final TLRPC.WebPage webPageFinal = webpage; final int currentAccount = UserConfig.selectedAccount; ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (response instanceof TLRPC.TL_webPage) { final TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) response; if (webPage.cached_page == null) { return; } if (!pagesStack.isEmpty() && pagesStack.get(0) == webPageFinal) { if (messageObject != null) { messageObject.messageOwner.media.webpage = webPage; TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages(); messagesRes.messages.add(messageObject.messageOwner); MessagesStorage.getInstance(currentAccount).putMessages(messagesRes, messageObject.getDialogId(), -2, 0, false, messageObject.scheduled); } pagesStack.set(0, webPage); if (pagesStack.size() == 1) { ApplicationLoader.applicationContext.getSharedPreferences("articles", Activity.MODE_PRIVATE).edit().remove("article" + webPage.id).commit(); updateInterfaceForCurrentPage(webPage, false, 0); if (anchorFinal != null) { scrollToAnchor(anchorFinal); } } } LongSparseArray webpages = new LongSparseArray<>(1); webpages.put(webPage.id, webPage); MessagesStorage.getInstance(currentAccount).putWebPages(webpages); } else if (response instanceof TLRPC.TL_webPageNotModified) { TLRPC.TL_webPageNotModified webPage = (TLRPC.TL_webPageNotModified) response; if (webPageFinal != null && webPageFinal.cached_page != null) { if (webPageFinal.cached_page.views != webPage.cached_page_views) { webPageFinal.cached_page.views = webPage.cached_page_views; webPageFinal.cached_page.flags |= 8; for (int a = 0; a < adapter.length; a++) { if (adapter[a].currentPage == webPageFinal) { int p = adapter[a].getItemCount() - 1; RecyclerView.ViewHolder holder = listView[a].findViewHolderForAdapterPosition(p); if (holder != null) { adapter[a].onViewAttachedToWindow(holder); } } } if (messageObject != null) { TLRPC.TL_messages_messages messagesRes = new TLRPC.TL_messages_messages(); messagesRes.messages.add(messageObject.messageOwner); MessagesStorage.getInstance(currentAccount).putMessages(messagesRes, messageObject.getDialogId(), -2, 0, false, messageObject.scheduled); } } } } })); } lastInsets = null; if (!isVisible) { WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); if (attachedToWindow) { try { wm.removeView(windowView); } catch (Exception e) { //ignore } } try { if (Build.VERSION.SDK_INT >= 21) { windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; if (Build.VERSION.SDK_INT >= 28) { windowLayoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; } } //windowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN; windowView.setFocusable(false); containerView.setFocusable(false); wm.addView(windowView, windowLayoutParams); } catch (Exception e) { FileLog.e(e); return false; } } else { windowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); wm.updateViewLayout(windowView, windowLayoutParams); } isVisible = true; animationInProgress = 1; windowView.setAlpha(0); containerView.setAlpha(0); final AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether( ObjectAnimator.ofFloat(windowView, View.ALPHA, 0, 1.0f), ObjectAnimator.ofFloat(containerView, View.ALPHA, 0.0f, 1.0f), ObjectAnimator.ofFloat(windowView, View.TRANSLATION_X, AndroidUtilities.dp(56), 0) ); animationEndRunnable = () -> { if (containerView == null || windowView == null) { return; } if (Build.VERSION.SDK_INT >= 18) { containerView.setLayerType(View.LAYER_TYPE_NONE, null); } animationInProgress = 0; AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus()); }; animatorSet.setDuration(150); animatorSet.setInterpolator(interpolator); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { AndroidUtilities.runOnUIThread(() -> { NotificationCenter.getInstance(currentAccount).onAnimationFinish(allowAnimationIndex); if (animationEndRunnable != null) { animationEndRunnable.run(); animationEndRunnable = null; } }); } }); transitionAnimationStartTime = System.currentTimeMillis(); AndroidUtilities.runOnUIThread(() -> { allowAnimationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(allowAnimationIndex, new int[]{NotificationCenter.dialogsNeedReload, NotificationCenter.closeChats}); animatorSet.start(); }); if (Build.VERSION.SDK_INT >= 18) { containerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); } return true; } private void showProgressView(boolean useLine, final boolean show) { if (useLine) { AndroidUtilities.cancelRunOnUIThread(lineProgressTickRunnable); if (show) { lineProgressView.setProgress(0.0f, false); lineProgressView.setProgress(0.3f, true); AndroidUtilities.runOnUIThread(lineProgressTickRunnable, 100); } else { lineProgressView.setProgress(1.0f, true); } } else { if (progressViewAnimation != null) { progressViewAnimation.cancel(); } progressViewAnimation = new AnimatorSet(); if (show) { progressView.setVisibility(View.VISIBLE); menuContainer.setEnabled(false); progressViewAnimation.playTogether( ObjectAnimator.ofFloat(menuButton, View.SCALE_X, 0.1f), ObjectAnimator.ofFloat(menuButton, View.SCALE_Y, 0.1f), ObjectAnimator.ofFloat(menuButton, View.ALPHA, 0.0f), ObjectAnimator.ofFloat(progressView, View.SCALE_X, 1.0f), ObjectAnimator.ofFloat(progressView, View.SCALE_Y, 1.0f), ObjectAnimator.ofFloat(progressView, View.ALPHA, 1.0f)); } else { menuButton.setVisibility(View.VISIBLE); menuContainer.setEnabled(true); progressViewAnimation.playTogether( ObjectAnimator.ofFloat(progressView, View.SCALE_X, 0.1f), ObjectAnimator.ofFloat(progressView, View.SCALE_Y, 0.1f), ObjectAnimator.ofFloat(progressView, View.ALPHA, 0.0f), ObjectAnimator.ofFloat(menuButton, View.SCALE_X, 1.0f), ObjectAnimator.ofFloat(menuButton, View.SCALE_Y, 1.0f), ObjectAnimator.ofFloat(menuButton, View.ALPHA, 1.0f)); } progressViewAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (progressViewAnimation != null && progressViewAnimation.equals(animation)) { if (!show) { progressView.setVisibility(View.INVISIBLE); } else { menuButton.setVisibility(View.INVISIBLE); } } } @Override public void onAnimationCancel(Animator animation) { if (progressViewAnimation != null && progressViewAnimation.equals(animation)) { progressViewAnimation = null; } } }); progressViewAnimation.setDuration(150); progressViewAnimation.start(); } } public void collapse() { if (parentActivity == null || !isVisible || checkAnimation()) { return; } if (fullscreenVideoContainer.getVisibility() == View.VISIBLE) { if (customView != null) { fullscreenVideoContainer.setVisibility(View.INVISIBLE); customViewCallback.onCustomViewHidden(); fullscreenVideoContainer.removeView(customView); customView = null; } else if (fullscreenedVideo != null) { fullscreenedVideo.exitFullscreen(); } } try { if (visibleDialog != null) { visibleDialog.dismiss(); visibleDialog = null; } } catch (Exception e) { FileLog.e(e); } AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether( ObjectAnimator.ofFloat(containerView, View.TRANSLATION_X, containerView.getMeasuredWidth() - AndroidUtilities.dp(56)), ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, ActionBar.getCurrentActionBarHeight() + (Build.VERSION.SDK_INT >= 21 ? AndroidUtilities.statusBarHeight : 0)), ObjectAnimator.ofFloat(windowView, View.ALPHA, 0.0f), ObjectAnimator.ofFloat(listView[0], View.ALPHA, 0.0f), ObjectAnimator.ofFloat(listView[0], View.TRANSLATION_Y, -AndroidUtilities.dp(56)), ObjectAnimator.ofFloat(headerView, View.TRANSLATION_Y, 0), ObjectAnimator.ofFloat(backButton, View.SCALE_X, 1.0f), ObjectAnimator.ofFloat(backButton, View.SCALE_Y, 1.0f), ObjectAnimator.ofFloat(backButton, View.TRANSLATION_Y, 0), ObjectAnimator.ofFloat(menuContainer, View.SCALE_X, 1.0f), ObjectAnimator.ofFloat(menuContainer, View.TRANSLATION_Y, 0), ObjectAnimator.ofFloat(menuContainer, View.SCALE_Y, 1.0f) ); collapsed = true; animationInProgress = 2; animationEndRunnable = () -> { if (containerView == null) { return; } if (Build.VERSION.SDK_INT >= 18) { containerView.setLayerType(View.LAYER_TYPE_NONE, null); } animationInProgress = 0; //windowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); wm.updateViewLayout(windowView, windowLayoutParams); //onClosed(); //containerView.setScaleX(1.0f); //containerView.setScaleY(1.0f); }; animatorSet.setInterpolator(new DecelerateInterpolator()); animatorSet.setDuration(250); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (animationEndRunnable != null) { animationEndRunnable.run(); animationEndRunnable = null; } } }); transitionAnimationStartTime = System.currentTimeMillis(); if (Build.VERSION.SDK_INT >= 18) { containerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); } backDrawable.setRotation(1, true); animatorSet.start(); } public void uncollapse() { if (parentActivity == null || !isVisible || checkAnimation()) { return; } /*windowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); wm.updateViewLayout(windowView, windowLayoutParams);*/ AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether( ObjectAnimator.ofFloat(containerView, View.TRANSLATION_X, 0), ObjectAnimator.ofFloat(containerView, View.TRANSLATION_Y, 0), ObjectAnimator.ofFloat(windowView, View.ALPHA, 1.0f), ObjectAnimator.ofFloat(listView[0], View.ALPHA, 1.0f), ObjectAnimator.ofFloat(listView[0], View.TRANSLATION_Y, 0), ObjectAnimator.ofFloat(headerView, View.TRANSLATION_Y, 0), ObjectAnimator.ofFloat(backButton, View.SCALE_X, 1.0f), ObjectAnimator.ofFloat(backButton, View.SCALE_Y, 1.0f), ObjectAnimator.ofFloat(backButton, View.TRANSLATION_Y, 0), ObjectAnimator.ofFloat(menuContainer, View.SCALE_X, 1.0f), ObjectAnimator.ofFloat(menuContainer, View.TRANSLATION_Y, 0), ObjectAnimator.ofFloat(menuContainer, View.SCALE_Y, 1.0f) ); collapsed = false; animationInProgress = 2; animationEndRunnable = () -> { if (containerView == null) { return; } if (Build.VERSION.SDK_INT >= 18) { containerView.setLayerType(View.LAYER_TYPE_NONE, null); } animationInProgress = 0; //onClosed(); }; animatorSet.setDuration(250); animatorSet.setInterpolator(new DecelerateInterpolator()); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (animationEndRunnable != null) { animationEndRunnable.run(); animationEndRunnable = null; } } }); transitionAnimationStartTime = System.currentTimeMillis(); if (Build.VERSION.SDK_INT >= 18) { containerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); } backDrawable.setRotation(0, true); animatorSet.start(); } private void saveCurrentPagePosition() { if (adapter[0].currentPage == null) { return; } int position = layoutManager[0].findFirstVisibleItemPosition(); if (position != RecyclerView.NO_POSITION) { int offset; View view = layoutManager[0].findViewByPosition(position); if (view != null) { offset = view.getTop(); } else { offset = 0; } SharedPreferences.Editor editor = ApplicationLoader.applicationContext.getSharedPreferences("articles", Activity.MODE_PRIVATE).edit(); String key = "article" + adapter[0].currentPage.id; editor.putInt(key, position).putInt(key + "o", offset).putBoolean(key + "r", AndroidUtilities.displaySize.x > AndroidUtilities.displaySize.y).commit(); } } private void refreshThemeColors() { if (deleteView != null) { deleteView.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector), 2)); deleteView.setTextColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem)); } if (popupLayout != null) { popupLayout.setBackgroundColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground)); } if (searchContainer != null) { searchContainer.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); } if (searchField != null) { searchField.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); searchField.setCursorColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); searchField.setHintTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteHintText)); } if (searchUpButton != null) { searchUpButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.MULTIPLY)); searchUpButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_actionBarActionModeDefaultSelector), 1)); } if (searchDownButton != null) { searchDownButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.MULTIPLY)); searchDownButton.setBackgroundDrawable(Theme.createSelectorDrawable(Theme.getColor(Theme.key_actionBarActionModeDefaultSelector), 1)); } if (searchCountText != null) { searchCountText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); } if (menuButton != null) { menuButton.redrawPopup(Theme.getColor(Theme.key_actionBarDefaultSubmenuBackground)); menuButton.setPopupItemsColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItem), false); menuButton.setPopupItemsColor(Theme.getColor(Theme.key_actionBarDefaultSubmenuItemIcon), true); } if (clearButton != null) { clearButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.MULTIPLY)); } if (backDrawable != null) { backDrawable.setColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText)); } } public void close(boolean byBackPress, boolean force) { if (parentActivity == null || closeAnimationInProgress || !isVisible || checkAnimation()) { return; } if (fullscreenVideoContainer.getVisibility() == View.VISIBLE) { if (customView != null) { fullscreenVideoContainer.setVisibility(View.INVISIBLE); customViewCallback.onCustomViewHidden(); fullscreenVideoContainer.removeView(customView); customView = null; } else if (fullscreenedVideo != null) { fullscreenedVideo.exitFullscreen(); } if (!force) { return; } } if (textSelectionHelper.isSelectionMode()) { textSelectionHelper.clear(); return; } if (searchContainer.getTag() != null) { showSearch(false); return; } if (openUrlReqId != 0) { ConnectionsManager.getInstance(currentAccount).cancelRequest(openUrlReqId, true); openUrlReqId = 0; showProgressView(true, false); } if (previewsReqId != 0) { ConnectionsManager.getInstance(currentAccount).cancelRequest(previewsReqId, true); previewsReqId = 0; showProgressView(true, false); } saveCurrentPagePosition(); if (byBackPress && !force) { if (removeLastPageFromStack()) { return; } } NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagePlayingProgressDidChanged); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagePlayingDidReset); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagePlayingPlayStateChanged); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.messagePlayingDidStart); parentFragment = null; try { if (visibleDialog != null) { visibleDialog.dismiss(); visibleDialog = null; } } catch (Exception e) { FileLog.e(e); } AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether( ObjectAnimator.ofFloat(windowView, View.ALPHA, 0), ObjectAnimator.ofFloat(containerView, View.ALPHA, 0.0f), ObjectAnimator.ofFloat(windowView, View.TRANSLATION_X, 0, AndroidUtilities.dp(56)) ); animationInProgress = 2; animationEndRunnable = () -> { if (containerView == null) { return; } if (Build.VERSION.SDK_INT >= 18) { containerView.setLayerType(View.LAYER_TYPE_NONE, null); } animationInProgress = 0; onClosed(); }; animatorSet.setDuration(150); animatorSet.setInterpolator(interpolator); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (animationEndRunnable != null) { animationEndRunnable.run(); animationEndRunnable = null; } } }); transitionAnimationStartTime = System.currentTimeMillis(); if (Build.VERSION.SDK_INT >= 18) { containerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); } animatorSet.start(); } private void onClosed() { isVisible = false; for (int i = 0; i < listView.length; i++) { adapter[i].cleanup(); } try { parentActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } catch (Exception e) { FileLog.e(e); } for (int a = 0; a < createdWebViews.size(); a++) { BlockEmbedCell cell = createdWebViews.get(a); cell.destroyWebView(false); } containerView.post(() -> { try { if (windowView.getParent() != null) { WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); wm.removeView(windowView); } } catch (Exception e) { FileLog.e(e); } }); } private void loadChannel(final BlockChannelCell cell, WebpageAdapter adapter, TLRPC.Chat channel) { if (loadingChannel || TextUtils.isEmpty(channel.username)) { return; } loadingChannel = true; TLRPC.TL_contacts_resolveUsername req = new TLRPC.TL_contacts_resolveUsername(); req.username = channel.username; final int currentAccount = UserConfig.selectedAccount; ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { loadingChannel = false; if (parentFragment == null || adapter.blocks.isEmpty()) { return; } if (error == null) { TLRPC.TL_contacts_resolvedPeer res = (TLRPC.TL_contacts_resolvedPeer) response; if (!res.chats.isEmpty()) { MessagesController.getInstance(currentAccount).putUsers(res.users, false); MessagesController.getInstance(currentAccount).putChats(res.chats, false); MessagesStorage.getInstance(currentAccount).putUsersAndChats(res.users, res.chats, false, true); loadedChannel = res.chats.get(0); if (loadedChannel.left && !loadedChannel.kicked) { cell.setState(0, false); } else { cell.setState(4, false); } } else { cell.setState(4, false); } } else { cell.setState(4, false); } })); } private void joinChannel(final BlockChannelCell cell, final TLRPC.Chat channel) { final TLRPC.TL_channels_joinChannel req = new TLRPC.TL_channels_joinChannel(); req.channel = MessagesController.getInputChannel(channel); final int currentAccount = UserConfig.selectedAccount; ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { if (error != null) { AndroidUtilities.runOnUIThread(() -> { cell.setState(0, false); AlertsCreator.processError(currentAccount, error, parentFragment, req, true); }); return; } boolean hasJoinMessage = false; TLRPC.Updates updates = (TLRPC.Updates) response; for (int a = 0; a < updates.updates.size(); a++) { TLRPC.Update update = updates.updates.get(a); if (update instanceof TLRPC.TL_updateNewChannelMessage) { if (((TLRPC.TL_updateNewChannelMessage) update).message.action instanceof TLRPC.TL_messageActionChatAddUser) { hasJoinMessage = true; break; } } } MessagesController.getInstance(currentAccount).processUpdates(updates, false); if (!hasJoinMessage) { MessagesController.getInstance(currentAccount).generateJoinMessage(channel.id, true); } AndroidUtilities.runOnUIThread(() -> cell.setState(2, false)); AndroidUtilities.runOnUIThread(() -> MessagesController.getInstance(currentAccount).loadFullChat(channel.id, 0, true), 1000); MessagesStorage.getInstance(currentAccount).updateDialogsWithDeletedMessages(-channel.id, channel.id, new ArrayList<>(), null, true); }); } private boolean checkAnimation() { if (animationInProgress != 0) { if (Math.abs(transitionAnimationStartTime - System.currentTimeMillis()) >= 500) { if (animationEndRunnable != null) { animationEndRunnable.run(); animationEndRunnable = null; } animationInProgress = 0; } } return animationInProgress != 0; } public void destroyArticleViewer() { if (parentActivity == null || windowView == null) { return; } try { if (windowView.getParent() != null) { WindowManager wm = (WindowManager) parentActivity.getSystemService(Context.WINDOW_SERVICE); wm.removeViewImmediate(windowView); } windowView = null; } catch (Exception e) { FileLog.e(e); } for (int a = 0; a < createdWebViews.size(); a++) { BlockEmbedCell cell = createdWebViews.get(a); cell.destroyWebView(true); } createdWebViews.clear(); try { parentActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } catch (Exception e) { FileLog.e(e); } parentActivity = null; parentFragment = null; Instance = null; } public boolean isVisible() { return isVisible; } public void showDialog(Dialog dialog) { if (parentActivity == null) { return; } try { if (visibleDialog != null) { visibleDialog.dismiss(); visibleDialog = null; } } catch (Exception e) { FileLog.e(e); } try { visibleDialog = dialog; visibleDialog.setCanceledOnTouchOutside(true); visibleDialog.setOnDismissListener(dialog1 -> visibleDialog = null); dialog.show(); } catch (Exception e) { FileLog.e(e); } } private static final class WebPageUtils { private WebPageUtils() { } public static TLRPC.Photo getPhotoWithId(TLRPC.WebPage page, long id) { if (page == null || page.cached_page == null) { return null; } if (page.photo != null && page.photo.id == id) { return page.photo; } for (int a = 0; a < page.cached_page.photos.size(); a++) { TLRPC.Photo photo = page.cached_page.photos.get(a); if (photo.id == id) { return photo; } } return null; } public static TLRPC.Document getDocumentWithId(TLRPC.WebPage page, long id) { if (page == null || page.cached_page == null) { return null; } if (page.document != null && page.document.id == id) { return page.document; } for (int a = 0; a < page.cached_page.documents.size(); a++) { TLRPC.Document document = page.cached_page.documents.get(a); if (document.id == id) { return document; } } return null; } public static boolean isVideo(TLRPC.WebPage page, TLRPC.PageBlock block) { if (block instanceof TLRPC.TL_pageBlockVideo) { TLRPC.Document document = getDocumentWithId(page, ((TLRPC.TL_pageBlockVideo) block).video_id); if (document != null) { return MessageObject.isVideoDocument(document); } } return false; } public static TLObject getMedia(TLRPC.WebPage page, TLRPC.PageBlock block) { if (block instanceof TLRPC.TL_pageBlockPhoto) { return getPhotoWithId(page, ((TLRPC.TL_pageBlockPhoto) block).photo_id); } else if (block instanceof TLRPC.TL_pageBlockVideo) { return getDocumentWithId(page, ((TLRPC.TL_pageBlockVideo) block).video_id); } else { return null; } } public static File getMediaFile(TLRPC.WebPage page, TLRPC.PageBlock block) { if (block instanceof TLRPC.TL_pageBlockPhoto) { TLRPC.Photo photo = getPhotoWithId(page, ((TLRPC.TL_pageBlockPhoto) block).photo_id); if (photo != null) { TLRPC.PhotoSize sizeFull = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.getPhotoSize()); if (sizeFull != null) { return FileLoader.getPathToAttach(sizeFull, true); } } } else if (block instanceof TLRPC.TL_pageBlockVideo) { TLRPC.Document document = getDocumentWithId(page, ((TLRPC.TL_pageBlockVideo) block).video_id); if (document != null) { return FileLoader.getPathToAttach(document, true); } } return null; } } private class WebpageAdapter extends RecyclerListView.SelectionAdapter { private Context context; private ArrayList localBlocks = new ArrayList<>(); private ArrayList blocks = new ArrayList<>(); private ArrayList photoBlocks = new ArrayList<>(); private HashMap anchors = new HashMap<>(); private HashMap anchorsOffset = new HashMap<>(); private HashMap anchorsParent = new HashMap<>(); private HashMap audioBlocks = new HashMap<>(); private ArrayList audioMessages = new ArrayList<>(); private HashMap textToBlocks = new HashMap<>(); private ArrayList textBlocks = new ArrayList<>(); private HashMap searchTextOffset = new HashMap<>(); private TLRPC.WebPage currentPage; private TLRPC.TL_pageBlockChannel channelBlock; private boolean isRtl; public WebpageAdapter(Context ctx) { context = ctx; } private TLRPC.Photo getPhotoWithId(long id) { return WebPageUtils.getPhotoWithId(currentPage, id); } private TLRPC.Document getDocumentWithId(long id) { return WebPageUtils.getDocumentWithId(currentPage, id); } private void setRichTextParents(TLRPC.RichText parentRichText, TLRPC.RichText richText) { if (richText == null) { return; } richText.parentRichText = parentRichText; if (richText instanceof TLRPC.TL_textFixed) { setRichTextParents(richText, ((TLRPC.TL_textFixed) richText).text); } else if (richText instanceof TLRPC.TL_textItalic) { setRichTextParents(richText, ((TLRPC.TL_textItalic) richText).text); } else if (richText instanceof TLRPC.TL_textBold) { setRichTextParents(richText, ((TLRPC.TL_textBold) richText).text); } else if (richText instanceof TLRPC.TL_textUnderline) { setRichTextParents(richText, ((TLRPC.TL_textUnderline) richText).text); } else if (richText instanceof TLRPC.TL_textStrike) { setRichTextParents(richText, ((TLRPC.TL_textStrike) richText).text); } else if (richText instanceof TLRPC.TL_textEmail) { setRichTextParents(richText, ((TLRPC.TL_textEmail) richText).text); } else if (richText instanceof TLRPC.TL_textPhone) { setRichTextParents(richText, ((TLRPC.TL_textPhone) richText).text); } else if (richText instanceof TLRPC.TL_textUrl) { setRichTextParents(richText, ((TLRPC.TL_textUrl) richText).text); } else if (richText instanceof TLRPC.TL_textConcat) { int count = richText.texts.size(); for (int a = 0; a < count; a++) { setRichTextParents(richText, richText.texts.get(a)); } } else if (richText instanceof TLRPC.TL_textSubscript) { setRichTextParents(richText, ((TLRPC.TL_textSubscript) richText).text); } else if (richText instanceof TLRPC.TL_textSuperscript) { setRichTextParents(richText, ((TLRPC.TL_textSuperscript) richText).text); } else if (richText instanceof TLRPC.TL_textMarked) { setRichTextParents(richText, ((TLRPC.TL_textMarked) richText).text); } else if (richText instanceof TLRPC.TL_textAnchor) { TLRPC.TL_textAnchor textAnchor = (TLRPC.TL_textAnchor) richText; setRichTextParents(richText, textAnchor.text); String name = textAnchor.name.toLowerCase(); anchors.put(name, blocks.size()); if (textAnchor.text instanceof TLRPC.TL_textPlain) { TLRPC.TL_textPlain textPlain = (TLRPC.TL_textPlain) textAnchor.text; if (!TextUtils.isEmpty(textPlain.text)) { anchorsParent.put(name, textAnchor); } } else if (!(textAnchor.text instanceof TLRPC.TL_textEmpty)) { anchorsParent.put(name, textAnchor); } anchorsOffset.put(name, -1); } } private void addTextBlock(Object text, TLRPC.PageBlock block) { if (text instanceof TLRPC.TL_textEmpty || textToBlocks.containsKey(text)) { return; } textToBlocks.put(text, block); textBlocks.add(text); } private void setRichTextParents(TLRPC.PageBlock block) { if (block instanceof TLRPC.TL_pageBlockEmbedPost) { TLRPC.TL_pageBlockEmbedPost blockEmbedPost = (TLRPC.TL_pageBlockEmbedPost) block; setRichTextParents(null, blockEmbedPost.caption.text); setRichTextParents(null, blockEmbedPost.caption.credit); addTextBlock(blockEmbedPost.caption.text, blockEmbedPost); addTextBlock(blockEmbedPost.caption.credit, blockEmbedPost); } else if (block instanceof TLRPC.TL_pageBlockParagraph) { TLRPC.TL_pageBlockParagraph pageBlockParagraph = (TLRPC.TL_pageBlockParagraph) block; setRichTextParents(null, pageBlockParagraph.text); addTextBlock(pageBlockParagraph.text, pageBlockParagraph); } else if (block instanceof TLRPC.TL_pageBlockKicker) { TLRPC.TL_pageBlockKicker pageBlockKicker = (TLRPC.TL_pageBlockKicker) block; setRichTextParents(null, pageBlockKicker.text); addTextBlock(pageBlockKicker.text, pageBlockKicker); } else if (block instanceof TLRPC.TL_pageBlockFooter) { TLRPC.TL_pageBlockFooter pageBlockFooter = (TLRPC.TL_pageBlockFooter) block; setRichTextParents(null, pageBlockFooter.text); addTextBlock(pageBlockFooter.text, pageBlockFooter); } else if (block instanceof TLRPC.TL_pageBlockHeader) { TLRPC.TL_pageBlockHeader pageBlockHeader = (TLRPC.TL_pageBlockHeader) block; setRichTextParents(null, pageBlockHeader.text); addTextBlock(pageBlockHeader.text, pageBlockHeader); } else if (block instanceof TLRPC.TL_pageBlockPreformatted) { TLRPC.TL_pageBlockPreformatted pageBlockPreformatted = (TLRPC.TL_pageBlockPreformatted) block; setRichTextParents(null, pageBlockPreformatted.text); addTextBlock(pageBlockPreformatted.text, pageBlockPreformatted); } else if (block instanceof TLRPC.TL_pageBlockSubheader) { TLRPC.TL_pageBlockSubheader pageBlockTitle = (TLRPC.TL_pageBlockSubheader) block; setRichTextParents(null, pageBlockTitle.text); addTextBlock(pageBlockTitle.text, pageBlockTitle); } else if (block instanceof TLRPC.TL_pageBlockSlideshow) { TLRPC.TL_pageBlockSlideshow pageBlockSlideshow = (TLRPC.TL_pageBlockSlideshow) block; setRichTextParents(null, pageBlockSlideshow.caption.text); setRichTextParents(null, pageBlockSlideshow.caption.credit); addTextBlock(pageBlockSlideshow.caption.text, pageBlockSlideshow); addTextBlock(pageBlockSlideshow.caption.credit, pageBlockSlideshow); for (int a = 0, size = pageBlockSlideshow.items.size(); a < size; a++) { setRichTextParents(pageBlockSlideshow.items.get(a)); } } else if (block instanceof TLRPC.TL_pageBlockPhoto) { TLRPC.TL_pageBlockPhoto pageBlockPhoto = (TLRPC.TL_pageBlockPhoto) block; setRichTextParents(null, pageBlockPhoto.caption.text); setRichTextParents(null, pageBlockPhoto.caption.credit); addTextBlock(pageBlockPhoto.caption.text, pageBlockPhoto); addTextBlock(pageBlockPhoto.caption.credit, pageBlockPhoto); } else if (block instanceof TL_pageBlockListItem) { TL_pageBlockListItem pageBlockListItem = (TL_pageBlockListItem) block; if (pageBlockListItem.textItem != null) { setRichTextParents(null, pageBlockListItem.textItem); addTextBlock(pageBlockListItem.textItem, pageBlockListItem); } else if (pageBlockListItem.blockItem != null) { setRichTextParents(pageBlockListItem.blockItem); } } else if (block instanceof TL_pageBlockOrderedListItem) { TL_pageBlockOrderedListItem pageBlockOrderedListItem = (TL_pageBlockOrderedListItem) block; if (pageBlockOrderedListItem.textItem != null) { setRichTextParents(null, pageBlockOrderedListItem.textItem); addTextBlock(pageBlockOrderedListItem.textItem, pageBlockOrderedListItem); } else if (pageBlockOrderedListItem.blockItem != null) { setRichTextParents(pageBlockOrderedListItem.blockItem); } } else if (block instanceof TLRPC.TL_pageBlockCollage) { TLRPC.TL_pageBlockCollage pageBlockCollage = (TLRPC.TL_pageBlockCollage) block; setRichTextParents(null, pageBlockCollage.caption.text); setRichTextParents(null, pageBlockCollage.caption.credit); addTextBlock(pageBlockCollage.caption.text, pageBlockCollage); addTextBlock(pageBlockCollage.caption.credit, pageBlockCollage); for (int a = 0, size = pageBlockCollage.items.size(); a < size; a++) { setRichTextParents(pageBlockCollage.items.get(a)); } } else if (block instanceof TLRPC.TL_pageBlockEmbed) { TLRPC.TL_pageBlockEmbed pageBlockEmbed = (TLRPC.TL_pageBlockEmbed) block; setRichTextParents(null, pageBlockEmbed.caption.text); setRichTextParents(null, pageBlockEmbed.caption.credit); addTextBlock(pageBlockEmbed.caption.text, pageBlockEmbed); addTextBlock(pageBlockEmbed.caption.credit, pageBlockEmbed); } else if (block instanceof TLRPC.TL_pageBlockSubtitle) { TLRPC.TL_pageBlockSubtitle pageBlockSubtitle = (TLRPC.TL_pageBlockSubtitle) block; setRichTextParents(null, pageBlockSubtitle.text); addTextBlock(pageBlockSubtitle.text, pageBlockSubtitle); } else if (block instanceof TLRPC.TL_pageBlockBlockquote) { TLRPC.TL_pageBlockBlockquote pageBlockBlockquote = (TLRPC.TL_pageBlockBlockquote) block; setRichTextParents(null, pageBlockBlockquote.text); setRichTextParents(null, pageBlockBlockquote.caption); addTextBlock(pageBlockBlockquote.text, pageBlockBlockquote); addTextBlock(pageBlockBlockquote.caption, pageBlockBlockquote); } else if (block instanceof TLRPC.TL_pageBlockDetails) { TLRPC.TL_pageBlockDetails pageBlockDetails = (TLRPC.TL_pageBlockDetails) block; setRichTextParents(null, pageBlockDetails.title); addTextBlock(pageBlockDetails.title, pageBlockDetails); for (int a = 0, size = pageBlockDetails.blocks.size(); a < size; a++) { setRichTextParents(pageBlockDetails.blocks.get(a)); } } else if (block instanceof TLRPC.TL_pageBlockVideo) { TLRPC.TL_pageBlockVideo pageBlockVideo = (TLRPC.TL_pageBlockVideo) block; setRichTextParents(null, pageBlockVideo.caption.text); setRichTextParents(null, pageBlockVideo.caption.credit); addTextBlock(pageBlockVideo.caption.text, pageBlockVideo); addTextBlock(pageBlockVideo.caption.credit, pageBlockVideo); } else if (block instanceof TLRPC.TL_pageBlockPullquote) { TLRPC.TL_pageBlockPullquote pageBlockPullquote = (TLRPC.TL_pageBlockPullquote) block; setRichTextParents(null, pageBlockPullquote.text); setRichTextParents(null, pageBlockPullquote.caption); addTextBlock(pageBlockPullquote.text, pageBlockPullquote); addTextBlock(pageBlockPullquote.caption, pageBlockPullquote); } else if (block instanceof TLRPC.TL_pageBlockAudio) { TLRPC.TL_pageBlockAudio pageBlockAudio = (TLRPC.TL_pageBlockAudio) block; setRichTextParents(null, pageBlockAudio.caption.text); setRichTextParents(null, pageBlockAudio.caption.credit); addTextBlock(pageBlockAudio.caption.text, pageBlockAudio); addTextBlock(pageBlockAudio.caption.credit, pageBlockAudio); } else if (block instanceof TLRPC.TL_pageBlockTable) { TLRPC.TL_pageBlockTable pageBlockTable = (TLRPC.TL_pageBlockTable) block; setRichTextParents(null, pageBlockTable.title); addTextBlock(pageBlockTable.title, pageBlockTable); for (int a = 0, size = pageBlockTable.rows.size(); a < size; a++) { TLRPC.TL_pageTableRow row = pageBlockTable.rows.get(a); for (int b = 0, size2 = row.cells.size(); b < size2; b++) { TLRPC.TL_pageTableCell cell = row.cells.get(b); setRichTextParents(null, cell.text); addTextBlock(cell.text, pageBlockTable); } } } else if (block instanceof TLRPC.TL_pageBlockTitle) { TLRPC.TL_pageBlockTitle pageBlockTitle = (TLRPC.TL_pageBlockTitle) block; setRichTextParents(null, pageBlockTitle.text); addTextBlock(pageBlockTitle.text, pageBlockTitle); } else if (block instanceof TLRPC.TL_pageBlockCover) { TLRPC.TL_pageBlockCover pageBlockCover = (TLRPC.TL_pageBlockCover) block; setRichTextParents(pageBlockCover.cover); } else if (block instanceof TLRPC.TL_pageBlockAuthorDate) { TLRPC.TL_pageBlockAuthorDate pageBlockAuthorDate = (TLRPC.TL_pageBlockAuthorDate) block; setRichTextParents(null, pageBlockAuthorDate.author); addTextBlock(pageBlockAuthorDate.author, pageBlockAuthorDate); } else if (block instanceof TLRPC.TL_pageBlockMap) { TLRPC.TL_pageBlockMap pageBlockMap = (TLRPC.TL_pageBlockMap) block; setRichTextParents(null, pageBlockMap.caption.text); setRichTextParents(null, pageBlockMap.caption.credit); addTextBlock(pageBlockMap.caption.text, pageBlockMap); addTextBlock(pageBlockMap.caption.credit, pageBlockMap); } else if (block instanceof TLRPC.TL_pageBlockRelatedArticles) { TLRPC.TL_pageBlockRelatedArticles pageBlockRelatedArticles = (TLRPC.TL_pageBlockRelatedArticles) block; setRichTextParents(null, pageBlockRelatedArticles.title); addTextBlock(pageBlockRelatedArticles.title, pageBlockRelatedArticles); } } private void addBlock(WebpageAdapter adapter, TLRPC.PageBlock block, int level, int listLevel, int position) { TLRPC.PageBlock originalBlock = block; if (block instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild blockDetailsChild = (TL_pageBlockDetailsChild) block; block = blockDetailsChild.block; } if (!(block instanceof TLRPC.TL_pageBlockList || block instanceof TLRPC.TL_pageBlockOrderedList)) { setRichTextParents(block); addAllMediaFromBlock(adapter, block); } block = getLastNonListPageBlock(block); if (block instanceof TLRPC.TL_pageBlockUnsupported) { return; } else if (block instanceof TLRPC.TL_pageBlockAnchor) { anchors.put(((TLRPC.TL_pageBlockAnchor) block).name.toLowerCase(), blocks.size()); return; } if (!(block instanceof TLRPC.TL_pageBlockList || block instanceof TLRPC.TL_pageBlockOrderedList)) { blocks.add(originalBlock); } if (block instanceof TLRPC.TL_pageBlockAudio) { TLRPC.TL_pageBlockAudio blockAudio = (TLRPC.TL_pageBlockAudio) block; TLRPC.TL_message message = new TLRPC.TL_message(); message.out = true; message.id = block.mid = -((Long) blockAudio.audio_id).hashCode(); message.peer_id = new TLRPC.TL_peerUser(); message.from_id = new TLRPC.TL_peerUser(); message.from_id.user_id = message.peer_id.user_id = UserConfig.getInstance(currentAccount).getClientUserId(); message.date = (int) (System.currentTimeMillis() / 1000); message.message = ""; message.media = new TLRPC.TL_messageMediaDocument(); message.media.webpage = currentPage; message.media.flags |= 3; message.media.document = getDocumentWithId(blockAudio.audio_id); message.flags |= TLRPC.MESSAGE_FLAG_HAS_MEDIA | TLRPC.MESSAGE_FLAG_HAS_FROM_ID; MessageObject messageObject = new MessageObject(UserConfig.selectedAccount, message, false, true); audioMessages.add(messageObject); audioBlocks.put(blockAudio, messageObject); String author = messageObject.getMusicAuthor(false); String title = messageObject.getMusicTitle(false); if (!TextUtils.isEmpty(title) || !TextUtils.isEmpty(author)) { SpannableStringBuilder stringBuilder; if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(author)) { addTextBlock(String.format("%s - %s", author, title), block); } else if (!TextUtils.isEmpty(title)) { addTextBlock(title, block); } else { addTextBlock(author, block); } } } else if (block instanceof TLRPC.TL_pageBlockEmbedPost) { TLRPC.TL_pageBlockEmbedPost pageBlockEmbedPost = (TLRPC.TL_pageBlockEmbedPost) block; if (!pageBlockEmbedPost.blocks.isEmpty()) { block.level = -1; for (int b = 0; b < pageBlockEmbedPost.blocks.size(); b++) { TLRPC.PageBlock innerBlock = pageBlockEmbedPost.blocks.get(b); if (innerBlock instanceof TLRPC.TL_pageBlockUnsupported) { continue; } else if (innerBlock instanceof TLRPC.TL_pageBlockAnchor) { TLRPC.TL_pageBlockAnchor pageBlockAnchor = (TLRPC.TL_pageBlockAnchor) innerBlock; anchors.put(pageBlockAnchor.name.toLowerCase(), blocks.size()); continue; } innerBlock.level = 1; if (b == pageBlockEmbedPost.blocks.size() - 1) { innerBlock.bottom = true; } blocks.add(innerBlock); addAllMediaFromBlock(adapter, innerBlock); } if (!TextUtils.isEmpty(getPlainText(pageBlockEmbedPost.caption.text)) || !TextUtils.isEmpty(getPlainText(pageBlockEmbedPost.caption.credit))) { TL_pageBlockEmbedPostCaption pageBlockEmbedPostCaption = new TL_pageBlockEmbedPostCaption(); pageBlockEmbedPostCaption.parent = pageBlockEmbedPost; pageBlockEmbedPostCaption.caption = pageBlockEmbedPost.caption; blocks.add(pageBlockEmbedPostCaption); } } } else if (block instanceof TLRPC.TL_pageBlockRelatedArticles) { TLRPC.TL_pageBlockRelatedArticles pageBlockRelatedArticles = (TLRPC.TL_pageBlockRelatedArticles) block; TL_pageBlockRelatedArticlesShadow shadow = new TL_pageBlockRelatedArticlesShadow(); shadow.parent = pageBlockRelatedArticles; blocks.add(blocks.size() - 1, shadow); for (int b = 0, size = pageBlockRelatedArticles.articles.size(); b < size; b++) { TL_pageBlockRelatedArticlesChild child = new TL_pageBlockRelatedArticlesChild(); child.parent = pageBlockRelatedArticles; child.num = b; blocks.add(child); } if (position == 0) { shadow = new TL_pageBlockRelatedArticlesShadow(); shadow.parent = pageBlockRelatedArticles; blocks.add(shadow); } } else if (block instanceof TLRPC.TL_pageBlockDetails) { TLRPC.TL_pageBlockDetails pageBlockDetails = (TLRPC.TL_pageBlockDetails) block; for (int b = 0, size = pageBlockDetails.blocks.size(); b < size; b++) { TL_pageBlockDetailsChild child = new TL_pageBlockDetailsChild(); child.parent = originalBlock; child.block = pageBlockDetails.blocks.get(b); addBlock(adapter, wrapInTableBlock(originalBlock, child), level + 1, listLevel, position); } /*if (level == 0) { TL_pageBlockDetailsBottom child = new TL_pageBlockDetailsBottom(); child.parent = pageBlockDetails; blocks.add(wrapInTableBlock(originalBlock, child)); } else { TL_pageBlockDetailsBottom bottom = new TL_pageBlockDetailsBottom(); bottom.parent = pageBlockDetails; TL_pageBlockDetailsChild child = new TL_pageBlockDetailsChild(); child.parent = originalBlock; child.block = bottom; blocks.add(wrapInTableBlock(originalBlock, child)); }*/ } else if (block instanceof TLRPC.TL_pageBlockList) { TLRPC.TL_pageBlockList pageBlockList = (TLRPC.TL_pageBlockList) block; TL_pageBlockListParent pageBlockListParent = new TL_pageBlockListParent(); pageBlockListParent.pageBlockList = pageBlockList; pageBlockListParent.level = listLevel; for (int b = 0, size = pageBlockList.items.size(); b < size; b++) { TLRPC.PageListItem item = pageBlockList.items.get(b); TL_pageBlockListItem pageBlockListItem = new TL_pageBlockListItem(); pageBlockListItem.index = b; pageBlockListItem.parent = pageBlockListParent; if (pageBlockList.ordered) { if (isRtl) { pageBlockListItem.num = String.format(".%d", b + 1); } else { pageBlockListItem.num = String.format("%d.", b + 1); } } else { pageBlockListItem.num = "•"; } pageBlockListParent.items.add(pageBlockListItem); if (item instanceof TLRPC.TL_pageListItemText) { pageBlockListItem.textItem = ((TLRPC.TL_pageListItemText) item).text; } else if (item instanceof TLRPC.TL_pageListItemBlocks) { TLRPC.TL_pageListItemBlocks pageListItemBlocks = (TLRPC.TL_pageListItemBlocks) item; if (!pageListItemBlocks.blocks.isEmpty()) { pageBlockListItem.blockItem = pageListItemBlocks.blocks.get(0); } else { TLRPC.TL_pageListItemText text = new TLRPC.TL_pageListItemText(); TLRPC.TL_textPlain textPlain = new TLRPC.TL_textPlain(); textPlain.text = " "; text.text = textPlain; item = text; } } if (originalBlock instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild pageBlockDetailsChild = (TL_pageBlockDetailsChild) originalBlock; TL_pageBlockDetailsChild child = new TL_pageBlockDetailsChild(); child.parent = pageBlockDetailsChild.parent; child.block = pageBlockListItem; addBlock(adapter, child, level, listLevel + 1, position); } else { TLRPC.PageBlock finalBlock; if (b == 0) { finalBlock = fixListBlock(originalBlock, pageBlockListItem); } else { finalBlock = pageBlockListItem; } addBlock(adapter, finalBlock, level, listLevel + 1, position); } if (item instanceof TLRPC.TL_pageListItemBlocks) { TLRPC.TL_pageListItemBlocks pageListItemBlocks = (TLRPC.TL_pageListItemBlocks) item; for (int c = 1, size2 = pageListItemBlocks.blocks.size(); c < size2; c++) { pageBlockListItem = new TL_pageBlockListItem(); pageBlockListItem.blockItem = pageListItemBlocks.blocks.get(c); pageBlockListItem.parent = pageBlockListParent; if (originalBlock instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild pageBlockDetailsChild = (TL_pageBlockDetailsChild) originalBlock; TL_pageBlockDetailsChild child = new TL_pageBlockDetailsChild(); child.parent = pageBlockDetailsChild.parent; child.block = pageBlockListItem; addBlock(adapter, child, level, listLevel + 1, position); } else { addBlock(adapter, pageBlockListItem, level, listLevel + 1, position); } pageBlockListParent.items.add(pageBlockListItem); } } } } else if (block instanceof TLRPC.TL_pageBlockOrderedList) { TLRPC.TL_pageBlockOrderedList pageBlockOrderedList = (TLRPC.TL_pageBlockOrderedList) block; TL_pageBlockOrderedListParent pageBlockOrderedListParent = new TL_pageBlockOrderedListParent(); pageBlockOrderedListParent.pageBlockOrderedList = pageBlockOrderedList; pageBlockOrderedListParent.level = listLevel; for (int b = 0, size = pageBlockOrderedList.items.size(); b < size; b++) { TLRPC.PageListOrderedItem item = pageBlockOrderedList.items.get(b); TL_pageBlockOrderedListItem pageBlockOrderedListItem = new TL_pageBlockOrderedListItem(); pageBlockOrderedListItem.index = b; pageBlockOrderedListItem.parent = pageBlockOrderedListParent; pageBlockOrderedListParent.items.add(pageBlockOrderedListItem); if (item instanceof TLRPC.TL_pageListOrderedItemText) { TLRPC.TL_pageListOrderedItemText pageListOrderedItemText = (TLRPC.TL_pageListOrderedItemText) item; pageBlockOrderedListItem.textItem = pageListOrderedItemText.text; if (TextUtils.isEmpty(pageListOrderedItemText.num)) { if (isRtl) { pageBlockOrderedListItem.num = String.format(".%d", b + 1); } else { pageBlockOrderedListItem.num = String.format("%d.", b + 1); } } else { if (isRtl) { pageBlockOrderedListItem.num = "." + pageListOrderedItemText.num; } else { pageBlockOrderedListItem.num = pageListOrderedItemText.num + "."; } } } else if (item instanceof TLRPC.TL_pageListOrderedItemBlocks) { TLRPC.TL_pageListOrderedItemBlocks pageListOrderedItemBlocks = (TLRPC.TL_pageListOrderedItemBlocks) item; if (!pageListOrderedItemBlocks.blocks.isEmpty()) { pageBlockOrderedListItem.blockItem = pageListOrderedItemBlocks.blocks.get(0); } else { TLRPC.TL_pageListOrderedItemText text = new TLRPC.TL_pageListOrderedItemText(); TLRPC.TL_textPlain textPlain = new TLRPC.TL_textPlain(); textPlain.text = " "; text.text = textPlain; item = text; } if (TextUtils.isEmpty(pageListOrderedItemBlocks.num)) { if (isRtl) { pageBlockOrderedListItem.num = String.format(".%d", b + 1); } else { pageBlockOrderedListItem.num = String.format("%d.", b + 1); } } else { if (isRtl) { pageBlockOrderedListItem.num = "." + pageListOrderedItemBlocks.num; } else { pageBlockOrderedListItem.num = pageListOrderedItemBlocks.num + "."; } } } if (originalBlock instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild pageBlockDetailsChild = (TL_pageBlockDetailsChild) originalBlock; TL_pageBlockDetailsChild child = new TL_pageBlockDetailsChild(); child.parent = pageBlockDetailsChild.parent; child.block = pageBlockOrderedListItem; addBlock(adapter, child, level, listLevel + 1, position); } else { TLRPC.PageBlock finalBlock; if (b == 0) { finalBlock = fixListBlock(originalBlock, pageBlockOrderedListItem); } else { finalBlock = pageBlockOrderedListItem; } addBlock(adapter, finalBlock, level, listLevel + 1, position); } if (item instanceof TLRPC.TL_pageListOrderedItemBlocks) { TLRPC.TL_pageListOrderedItemBlocks pageListOrderedItemBlocks = (TLRPC.TL_pageListOrderedItemBlocks) item; for (int c = 1, size2 = pageListOrderedItemBlocks.blocks.size(); c < size2; c++) { pageBlockOrderedListItem = new TL_pageBlockOrderedListItem(); pageBlockOrderedListItem.blockItem = pageListOrderedItemBlocks.blocks.get(c); pageBlockOrderedListItem.parent = pageBlockOrderedListParent; if (originalBlock instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild pageBlockDetailsChild = (TL_pageBlockDetailsChild) originalBlock; TL_pageBlockDetailsChild child = new TL_pageBlockDetailsChild(); child.parent = pageBlockDetailsChild.parent; child.block = pageBlockOrderedListItem; addBlock(adapter, child, level, listLevel + 1, position); } else { addBlock(adapter, pageBlockOrderedListItem, level, listLevel + 1, position); } pageBlockOrderedListParent.items.add(pageBlockOrderedListItem); } } } } } private void addAllMediaFromBlock(WebpageAdapter adapter, TLRPC.PageBlock block) { if (block instanceof TLRPC.TL_pageBlockPhoto) { TLRPC.TL_pageBlockPhoto pageBlockPhoto = (TLRPC.TL_pageBlockPhoto) block; TLRPC.Photo photo = getPhotoWithId(pageBlockPhoto.photo_id); if (photo != null) { pageBlockPhoto.thumb = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, 56, true); pageBlockPhoto.thumbObject = photo; photoBlocks.add(block); } } else if (block instanceof TLRPC.TL_pageBlockVideo && WebPageUtils.isVideo(adapter.currentPage, block)) { TLRPC.TL_pageBlockVideo pageBlockVideo = (TLRPC.TL_pageBlockVideo) block; TLRPC.Document document = getDocumentWithId(pageBlockVideo.video_id); if (document != null) { pageBlockVideo.thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 56, true); pageBlockVideo.thumbObject = document; photoBlocks.add(block); } } else if (block instanceof TLRPC.TL_pageBlockSlideshow) { TLRPC.TL_pageBlockSlideshow slideshow = (TLRPC.TL_pageBlockSlideshow) block; int count = slideshow.items.size(); for (int a = 0; a < count; a++) { TLRPC.PageBlock innerBlock = slideshow.items.get(a); innerBlock.groupId = lastBlockNum; addAllMediaFromBlock(adapter, innerBlock); } lastBlockNum++; } else if (block instanceof TLRPC.TL_pageBlockCollage) { TLRPC.TL_pageBlockCollage collage = (TLRPC.TL_pageBlockCollage) block; int count = collage.items.size(); for (int a = 0; a < count; a++) { TLRPC.PageBlock innerBlock = collage.items.get(a); innerBlock.groupId = lastBlockNum; addAllMediaFromBlock(adapter, innerBlock); } lastBlockNum++; } else if (block instanceof TLRPC.TL_pageBlockCover) { TLRPC.TL_pageBlockCover pageBlockCover = (TLRPC.TL_pageBlockCover) block; addAllMediaFromBlock(adapter, pageBlockCover.cover); } } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { case 0: { view = new BlockParagraphCell(context, this); break; } case 1: { view = new BlockHeaderCell(context, this); break; } case 2: { view = new BlockDividerCell(context); break; } case 3: { view = new BlockEmbedCell(context, this); break; } case 4: { view = new BlockSubtitleCell(context, this); break; } case 5: { view = new BlockVideoCell(context, this, 0); break; } case 6: { view = new BlockPullquoteCell(context, this); break; } case 7: { view = new BlockBlockquoteCell(context, this); break; } case 8: { view = new BlockSlideshowCell(context, this); break; } case 9: { view = new BlockPhotoCell(context, this, 0); break; } case 10: { view = new BlockAuthorDateCell(context, this); break; } case 11: { view = new BlockTitleCell(context, this); break; } case 12: { view = new BlockListItemCell(context, this); break; } case 13: { view = new BlockFooterCell(context, this); break; } case 14: { view = new BlockPreformattedCell(context, this); break; } case 15: { view = new BlockSubheaderCell(context, this); break; } case 16: { view = new BlockEmbedPostCell(context, this); break; } case 17: { view = new BlockCollageCell(context, this); break; } case 18: { view = new BlockChannelCell(context, this, 0); break; } case 19: { view = new BlockAudioCell(context, this); break; } case 20: { view = new BlockKickerCell(context, this); break; } case 21: { view = new BlockOrderedListItemCell(context, this); break; } case 22: { view = new BlockMapCell(context, this, 0); break; } case 23: { view = new BlockRelatedArticlesCell(context, this); break; } case 24: { view = new BlockDetailsCell(context, this); break; } case 25: { view = new BlockTableCell(context, this); break; } case 26: { view = new BlockRelatedArticlesHeaderCell(context, this); break; } case 27: { view = new BlockDetailsBottomCell(context); break; } case 28: { view = new BlockRelatedArticlesShadowCell(context); break; } case 90: { view = new ReportCell(context); break; } case 100: default: { TextView textView = new TextView(context); textView.setBackgroundColor(0xffff0000); textView.setTextColor(0xff000000); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); view = textView; break; } } view.setLayoutParams(new RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)); view.setFocusable(true); return new RecyclerListView.Holder(view); } @Override public boolean isEnabled(RecyclerView.ViewHolder holder) { int type = holder.getItemViewType(); if (type == 23 || type == 24) { return true; } return false; } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (position < localBlocks.size()) { TLRPC.PageBlock block = localBlocks.get(position); bindBlockToHolder(holder.getItemViewType(), holder, block, position, localBlocks.size()); } } @Override public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) { if (holder.getItemViewType() == 90) { ReportCell cell = (ReportCell) holder.itemView; cell.setViews(currentPage.cached_page != null ? currentPage.cached_page.views : 0); } } private void bindBlockToHolder(int type, RecyclerView.ViewHolder holder, TLRPC.PageBlock block, int position, int total) { TLRPC.PageBlock originalBlock = block; if (block instanceof TLRPC.TL_pageBlockCover) { block = ((TLRPC.TL_pageBlockCover) block).cover; } else if (block instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild pageBlockDetailsChild = (TL_pageBlockDetailsChild) block; block = pageBlockDetailsChild.block; } switch (type) { case 0: { BlockParagraphCell cell = (BlockParagraphCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockParagraph) block); break; } case 1: { BlockHeaderCell cell = (BlockHeaderCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockHeader) block); break; } case 2: { BlockDividerCell cell = (BlockDividerCell) holder.itemView; break; } case 3: { BlockEmbedCell cell = (BlockEmbedCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockEmbed) block); break; } case 4: { BlockSubtitleCell cell = (BlockSubtitleCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockSubtitle) block); break; } case 5: { BlockVideoCell cell = (BlockVideoCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockVideo) block, position == 0, position == total - 1); cell.setParentBlock(channelBlock, originalBlock); break; } case 6: { BlockPullquoteCell cell = (BlockPullquoteCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockPullquote) block); break; } case 7: { BlockBlockquoteCell cell = (BlockBlockquoteCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockBlockquote) block); break; } case 8: { BlockSlideshowCell cell = (BlockSlideshowCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockSlideshow) block); break; } case 9: { BlockPhotoCell cell = (BlockPhotoCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockPhoto) block, position == 0, position == total - 1); cell.setParentBlock(originalBlock); break; } case 10: { BlockAuthorDateCell cell = (BlockAuthorDateCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockAuthorDate) block); break; } case 11: { BlockTitleCell cell = (BlockTitleCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockTitle) block); break; } case 12: { BlockListItemCell cell = (BlockListItemCell) holder.itemView; cell.setBlock((TL_pageBlockListItem) block); break; } case 13: { BlockFooterCell cell = (BlockFooterCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockFooter) block); break; } case 14: { BlockPreformattedCell cell = (BlockPreformattedCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockPreformatted) block); break; } case 15: { BlockSubheaderCell cell = (BlockSubheaderCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockSubheader) block); break; } case 16: { BlockEmbedPostCell cell = (BlockEmbedPostCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockEmbedPost) block); break; } case 17: { BlockCollageCell cell = (BlockCollageCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockCollage) block); break; } case 18: { BlockChannelCell cell = (BlockChannelCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockChannel) block); break; } case 19: { BlockAudioCell cell = (BlockAudioCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockAudio) block, position == 0, position == total - 1); break; } case 20: { BlockKickerCell cell = (BlockKickerCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockKicker) block); break; } case 21: { BlockOrderedListItemCell cell = (BlockOrderedListItemCell) holder.itemView; cell.setBlock((TL_pageBlockOrderedListItem) block); break; } case 22: { BlockMapCell cell = (BlockMapCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockMap) block, position == 0, position == total - 1); break; } case 23: { BlockRelatedArticlesCell cell = (BlockRelatedArticlesCell) holder.itemView; cell.setBlock((TL_pageBlockRelatedArticlesChild) block); break; } case 24: { BlockDetailsCell cell = (BlockDetailsCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockDetails) block); break; } case 25: { BlockTableCell cell = (BlockTableCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockTable) block); break; } case 26: { BlockRelatedArticlesHeaderCell cell = (BlockRelatedArticlesHeaderCell) holder.itemView; cell.setBlock((TLRPC.TL_pageBlockRelatedArticles) block); break; } case 27: { BlockDetailsBottomCell cell = (BlockDetailsBottomCell) holder.itemView; break; } case 100: { TextView textView = (TextView) holder.itemView; textView.setText("unsupported block " + block); break; } } } private int getTypeForBlock(TLRPC.PageBlock block) { if (block instanceof TLRPC.TL_pageBlockParagraph) { return 0; } else if (block instanceof TLRPC.TL_pageBlockHeader) { return 1; } else if (block instanceof TLRPC.TL_pageBlockDivider) { return 2; } else if (block instanceof TLRPC.TL_pageBlockEmbed) { return 3; } else if (block instanceof TLRPC.TL_pageBlockSubtitle) { return 4; } else if (block instanceof TLRPC.TL_pageBlockVideo) { return 5; } else if (block instanceof TLRPC.TL_pageBlockPullquote) { return 6; } else if (block instanceof TLRPC.TL_pageBlockBlockquote) { return 7; } else if (block instanceof TLRPC.TL_pageBlockSlideshow) { return 8; } else if (block instanceof TLRPC.TL_pageBlockPhoto) { return 9; } else if (block instanceof TLRPC.TL_pageBlockAuthorDate) { return 10; } else if (block instanceof TLRPC.TL_pageBlockTitle) { return 11; } else if (block instanceof TL_pageBlockListItem) { return 12; } else if (block instanceof TLRPC.TL_pageBlockFooter) { return 13; } else if (block instanceof TLRPC.TL_pageBlockPreformatted) { return 14; } else if (block instanceof TLRPC.TL_pageBlockSubheader) { return 15; } else if (block instanceof TLRPC.TL_pageBlockEmbedPost) { return 16; } else if (block instanceof TLRPC.TL_pageBlockCollage) { return 17; } else if (block instanceof TLRPC.TL_pageBlockChannel) { return 18; } else if (block instanceof TLRPC.TL_pageBlockAudio) { return 19; } else if (block instanceof TLRPC.TL_pageBlockKicker) { return 20; } else if (block instanceof TL_pageBlockOrderedListItem) { return 21; } else if (block instanceof TLRPC.TL_pageBlockMap) { return 22; } else if (block instanceof TL_pageBlockRelatedArticlesChild) { return 23; } else if (block instanceof TLRPC.TL_pageBlockDetails) { return 24; } else if (block instanceof TLRPC.TL_pageBlockTable) { return 25; } else if (block instanceof TLRPC.TL_pageBlockRelatedArticles) { return 26; } else if (block instanceof TL_pageBlockDetailsBottom) { return 27; } else if (block instanceof TL_pageBlockRelatedArticlesShadow) { return 28; } else if (block instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild pageBlockDetailsChild = (TL_pageBlockDetailsChild) block; return getTypeForBlock(pageBlockDetailsChild.block); } else if (block instanceof TLRPC.TL_pageBlockCover) { TLRPC.TL_pageBlockCover pageBlockCover = (TLRPC.TL_pageBlockCover) block; return getTypeForBlock(pageBlockCover.cover); } else { return 100; } } @Override public int getItemViewType(int position) { if (position == localBlocks.size()) { return 90; } return getTypeForBlock(localBlocks.get(position)); } public TLRPC.PageBlock getItem(int position) { return localBlocks.get(position); } @Override public int getItemCount() { return currentPage != null && currentPage.cached_page != null ? localBlocks.size() + 1 : 0; } private boolean isBlockOpened(TL_pageBlockDetailsChild child) { TLRPC.PageBlock parentBlock = getLastNonListPageBlock(child.parent); if (parentBlock instanceof TLRPC.TL_pageBlockDetails) { return ((TLRPC.TL_pageBlockDetails) parentBlock).open; } else if (parentBlock instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild parent = (TL_pageBlockDetailsChild) parentBlock; parentBlock = getLastNonListPageBlock(parent.block); if (parentBlock instanceof TLRPC.TL_pageBlockDetails && !((TLRPC.TL_pageBlockDetails) parentBlock).open) { return false; } return isBlockOpened(parent); } return false; } private void updateRows() { localBlocks.clear(); for (int a = 0, size = blocks.size(); a < size; a++) { TLRPC.PageBlock originalBlock = blocks.get(a); TLRPC.PageBlock block = getLastNonListPageBlock(originalBlock); if (block instanceof TL_pageBlockDetailsChild) { TL_pageBlockDetailsChild pageBlockDetailsChild = (TL_pageBlockDetailsChild) block; if (!isBlockOpened(pageBlockDetailsChild)) { continue; } } localBlocks.add(originalBlock); } } private void cleanup() { currentPage = null; blocks.clear(); photoBlocks.clear(); audioBlocks.clear(); audioMessages.clear(); anchors.clear(); anchorsParent.clear(); anchorsOffset.clear(); textBlocks.clear(); textToBlocks.clear(); channelBlock = null; notifyDataSetChanged(); } @Override public void notifyDataSetChanged() { updateRows(); super.notifyDataSetChanged(); } @Override public void notifyItemChanged(int position) { updateRows(); super.notifyItemChanged(position); } @Override public void notifyItemChanged(int position, Object payload) { updateRows(); super.notifyItemChanged(position, payload); } @Override public void notifyItemRangeChanged(int positionStart, int itemCount) { updateRows(); super.notifyItemRangeChanged(positionStart, itemCount); } @Override public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) { updateRows(); super.notifyItemRangeChanged(positionStart, itemCount, payload); } @Override public void notifyItemInserted(int position) { updateRows(); super.notifyItemInserted(position); } @Override public void notifyItemMoved(int fromPosition, int toPosition) { updateRows(); super.notifyItemMoved(fromPosition, toPosition); } @Override public void notifyItemRangeInserted(int positionStart, int itemCount) { updateRows(); super.notifyItemRangeInserted(positionStart, itemCount); } @Override public void notifyItemRemoved(int position) { updateRows(); super.notifyItemRemoved(position); } @Override public void notifyItemRangeRemoved(int positionStart, int itemCount) { updateRows(); super.notifyItemRangeRemoved(positionStart, itemCount); } } private class BlockVideoCell extends FrameLayout implements DownloadController.FileDownloadProgressListener, TextSelectionHelper.ArticleSelectableView { private DrawingText captionLayout; private DrawingText creditLayout; private ImageReceiver imageView; private RadialProgress2 radialProgress; private BlockChannelCell channelCell; private int currentType; private boolean isFirst; private int textX; private int textY; private int creditOffset; private int buttonX; private int buttonY; private boolean photoPressed; private int buttonState; private int buttonPressed; private int TAG; private TLRPC.TL_pageBlockVideo currentBlock; private TLRPC.PageBlock parentBlock; private TLRPC.Document currentDocument; private boolean isGif; private boolean autoDownload; private boolean cancelLoading; private MessageObject.GroupedMessagePosition groupPosition; private WebpageAdapter parentAdapter; public BlockVideoCell(Context context, WebpageAdapter adapter, int type) { super(context); parentAdapter = adapter; setWillNotDraw(false); imageView = new ImageReceiver(this); imageView.setNeedsQualityThumb(true); imageView.setShouldGenerateQualityThumb(true); currentType = type; radialProgress = new RadialProgress2(this); radialProgress.setProgressColor(0xffffffff); radialProgress.setColors(0x66000000, 0x7f000000, 0xffffffff, 0xffd9d9d9); TAG = DownloadController.getInstance(currentAccount).generateObserverTag(); channelCell = new BlockChannelCell(context, parentAdapter, 1); addView(channelCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); } public void setBlock(TLRPC.TL_pageBlockVideo block, boolean first, boolean last) { currentBlock = block; parentBlock = null; currentDocument = parentAdapter.getDocumentWithId(currentBlock.video_id); isGif = MessageObject.isVideoDocument(currentDocument) || MessageObject.isGifDocument(currentDocument)/* && currentBlock.autoplay*/; isFirst = first; channelCell.setVisibility(INVISIBLE); updateButtonState(false); requestLayout(); } public void setParentBlock(TLRPC.TL_pageBlockChannel channelBlock, TLRPC.PageBlock block) { parentBlock = block; if (channelBlock != null && parentBlock instanceof TLRPC.TL_pageBlockCover) { channelCell.setBlock(channelBlock); channelCell.setVisibility(VISIBLE); } } public View getChannelCell() { return channelCell; } @Override public boolean onTouchEvent(MotionEvent event) { if (pinchToZoomHelper.checkPinchToZoom(event, this, imageView, null)) { return true; } float x = event.getX(); float y = event.getY(); if (channelCell.getVisibility() == VISIBLE && y > channelCell.getTranslationY() && y < channelCell.getTranslationY() + AndroidUtilities.dp(39)) { if (parentAdapter.channelBlock != null && event.getAction() == MotionEvent.ACTION_UP) { MessagesController.getInstance(currentAccount).openByUserName(parentAdapter.channelBlock.channel.username, parentFragment, 2); close(false, true); } return true; } if (event.getAction() == MotionEvent.ACTION_DOWN && imageView.isInsideImage(x, y)) { if (buttonState != -1 && x >= buttonX && x <= buttonX + AndroidUtilities.dp(48) && y >= buttonY && y <= buttonY + AndroidUtilities.dp(48) || buttonState == 0) { buttonPressed = 1; invalidate(); } else { photoPressed = true; } } else if (event.getAction() == MotionEvent.ACTION_UP) { if (photoPressed) { photoPressed = false; openPhoto(currentBlock, parentAdapter); } else if (buttonPressed == 1) { buttonPressed = 0; playSoundEffect(SoundEffectConstants.CLICK); didPressedButton(true); invalidate(); } } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { photoPressed = false; } return photoPressed || buttonPressed != 0 || checkLayoutForLinks(parentAdapter, event, this, captionLayout, textX, textY) || checkLayoutForLinks(parentAdapter, event, this, creditLayout, textX, textY + creditOffset) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentType == 1) { width = ((View) getParent()).getMeasuredWidth(); height = ((View) getParent()).getMeasuredHeight(); } else if (currentType == 2) { height = (int) Math.ceil(groupPosition.ph * Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f); } if (currentBlock != null) { int photoWidth = width; int photoHeight = height; int photoX; int textWidth; if (currentType == 0 && currentBlock.level > 0) { textX = photoX = AndroidUtilities.dp(14 * currentBlock.level) + AndroidUtilities.dp(18); photoWidth -= photoX + AndroidUtilities.dp(18); textWidth = photoWidth; } else { photoX = 0; textX = AndroidUtilities.dp(18); textWidth = width - AndroidUtilities.dp(36); } if (currentDocument != null) { int size = AndroidUtilities.dp(48); TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(currentDocument.thumbs, 48); if (currentType == 0) { float scale; boolean found = false; for (int a = 0, count = currentDocument.attributes.size(); a < count; a++) { TLRPC.DocumentAttribute attribute = currentDocument.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeVideo) { scale = photoWidth / (float) attribute.w; height = (int) (scale * attribute.h); found = true; break; } } float w = thumb != null ? thumb.w : 100.0f; float h = thumb != null ? thumb.h : 100.0f; if (!found) { scale = photoWidth / w; height = (int) (scale * h); } if (parentBlock instanceof TLRPC.TL_pageBlockCover) { height = Math.min(height, photoWidth); } else { int maxHeight = (int) ((Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(56)) * 0.9f); if (height > maxHeight) { height = maxHeight; scale = height / h; photoWidth = (int) (scale * w); photoX += (width - photoX - photoWidth) / 2; } } if (height == 0) { height = AndroidUtilities.dp(100); } else if (height < size) { height = size; } photoHeight = height; } else if (currentType == 2) { if ((groupPosition.flags & POSITION_FLAG_RIGHT) == 0) { photoWidth -= AndroidUtilities.dp(2); } if ((groupPosition.flags & POSITION_FLAG_BOTTOM) == 0) { photoHeight -= AndroidUtilities.dp(2); } } imageView.setQualityThumbDocument(currentDocument); imageView.setImageCoords(photoX, (isFirst || currentType == 1 || currentType == 2 || currentBlock.level > 0) ? 0 : AndroidUtilities.dp(8), photoWidth, photoHeight); if (isGif) { autoDownload = DownloadController.getInstance(currentAccount).canDownloadMedia(DownloadController.AUTODOWNLOAD_TYPE_VIDEO, currentDocument.size); File path = FileLoader.getPathToAttach(currentDocument, true); if (autoDownload || path.exists()) { imageView.setStrippedLocation(null); imageView.setImage(ImageLocation.getForDocument(currentDocument), null, null, null, ImageLocation.getForDocument(thumb, currentDocument), "80_80_b", null, currentDocument.size, null, parentAdapter.currentPage, 1); } else { imageView.setStrippedLocation(ImageLocation.getForDocument(currentDocument)); imageView.setImage(null, null, null, null, ImageLocation.getForDocument(thumb, currentDocument), "80_80_b", null, currentDocument.size, null, parentAdapter.currentPage, 1); } } else { imageView.setStrippedLocation(null); imageView.setImage(null, null, ImageLocation.getForDocument(thumb, currentDocument), "80_80_b", 0, null, parentAdapter.currentPage, 1); } imageView.setAspectFit(true); buttonX = (int) (imageView.getImageX() + (imageView.getImageWidth() - size) / 2.0f); buttonY = (int) (imageView.getImageY() + (imageView.getImageHeight() - size) / 2.0f); radialProgress.setProgressRect(buttonX, buttonY, buttonX + size, buttonY + size); } textY = (int) (imageView.getImageY() + imageView.getImageHeight() + AndroidUtilities.dp(8)); if (currentType == 0) { captionLayout = createLayoutForText(this, null, currentBlock.caption.text, textWidth, textY, currentBlock, parentAdapter); if (captionLayout != null) { creditOffset = AndroidUtilities.dp(4) + captionLayout.getHeight(); height += creditOffset + AndroidUtilities.dp(4); captionLayout.x = textX; captionLayout.y = textY; } creditLayout = createLayoutForText(this, null, currentBlock.caption.credit, textWidth, textY + creditOffset, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (creditLayout != null) { height += AndroidUtilities.dp(4) + creditLayout.getHeight(); creditLayout.x = textX; creditLayout.y = textY + creditOffset; } } if (!isFirst && currentType == 0 && currentBlock.level <= 0) { height += AndroidUtilities.dp(8); } boolean nextIsChannel = parentBlock instanceof TLRPC.TL_pageBlockCover && parentAdapter.blocks.size() > 1 && parentAdapter.blocks.get(1) instanceof TLRPC.TL_pageBlockChannel; if (currentType != 2 && !nextIsChannel) { height += AndroidUtilities.dp(8); } } else { height = 1; } channelCell.measure(widthMeasureSpec, heightMeasureSpec); channelCell.setTranslationY(imageView.getImageHeight() - AndroidUtilities.dp(39)); setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (!imageView.hasBitmapImage() || imageView.getCurrentAlpha() != 1.0f) { canvas.drawRect(imageView.getDrawRegion(), photoBackgroundPaint); } if (!pinchToZoomHelper.isInOverlayModeFor(this)) { imageView.draw(canvas); if (imageView.getVisible()) { radialProgress.draw(canvas); } } int count = 0; if (captionLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this, count++); captionLayout.draw(canvas); canvas.restore(); } if (creditLayout != null) { canvas.save(); canvas.translate(textX, textY + creditOffset); drawTextSelection(canvas, this, count); creditLayout.draw(canvas); canvas.restore(); } if (currentBlock.level > 0) { canvas.drawRect(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(20), getMeasuredHeight() - (currentBlock.bottom ? AndroidUtilities.dp(6) : 0), quoteLinePaint); } } private int getIconForCurrentState() { if (buttonState == 0) { return MediaActionDrawable.ICON_DOWNLOAD; } else if (buttonState == 1) { return MediaActionDrawable.ICON_CANCEL; } else if (buttonState == 2) { return MediaActionDrawable.ICON_GIF; } else if (buttonState == 3) { return MediaActionDrawable.ICON_PLAY; } return MediaActionDrawable.ICON_NONE; } public void updateButtonState(boolean animated) { String fileName = FileLoader.getAttachFileName(currentDocument); File path = FileLoader.getPathToAttach(currentDocument, true); boolean fileExists = path.exists(); if (TextUtils.isEmpty(fileName)) { radialProgress.setIcon(MediaActionDrawable.ICON_NONE, false, false); return; } if (fileExists) { DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); if (!isGif) { buttonState = 3; } else { buttonState = -1; } radialProgress.setIcon(getIconForCurrentState(), false, animated); } else { DownloadController.getInstance(currentAccount).addLoadingFileObserver(fileName, null, this); float setProgress = 0; boolean progressVisible = false; if (!FileLoader.getInstance(currentAccount).isLoadingFile(fileName)) { if (!cancelLoading && autoDownload && isGif) { progressVisible = true; buttonState = 1; } else { buttonState = 0; } } else { progressVisible = true; buttonState = 1; Float progress = ImageLoader.getInstance().getFileProgress(fileName); setProgress = progress != null ? progress : 0; } radialProgress.setIcon(getIconForCurrentState(), progressVisible, animated); radialProgress.setProgress(setProgress, false); } invalidate(); } private void didPressedButton(boolean animated) { if (buttonState == 0) { cancelLoading = false; radialProgress.setProgress(0, false); if (isGif) { TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(currentDocument.thumbs, 40); imageView.setImage(ImageLocation.getForDocument(currentDocument), null, ImageLocation.getForDocument(thumb, currentDocument), "80_80_b", currentDocument.size, null, parentAdapter.currentPage, 1); } else { FileLoader.getInstance(currentAccount).loadFile(currentDocument, parentAdapter.currentPage, 1, 1); } buttonState = 1; radialProgress.setIcon(getIconForCurrentState(), true, animated); invalidate(); } else if (buttonState == 1) { cancelLoading = true; if (isGif) { imageView.cancelLoadImage(); } else { FileLoader.getInstance(currentAccount).cancelLoadFile(currentDocument); } buttonState = 0; radialProgress.setIcon(getIconForCurrentState(), false, animated); invalidate(); } else if (buttonState == 2) { imageView.setAllowStartAnimation(true); imageView.startAnimation(); buttonState = -1; radialProgress.setIcon(getIconForCurrentState(), false, animated); } else if (buttonState == 3) { openPhoto(currentBlock, parentAdapter); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); imageView.onDetachedFromWindow(); DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); imageView.onAttachedToWindow(); updateButtonState(false); } @Override public void onFailedDownload(String fileName, boolean canceled) { updateButtonState(false); } @Override public void onSuccessDownload(String fileName) { radialProgress.setProgress(1, true); if (isGif) { buttonState = 2; didPressedButton(true); } else { updateButtonState(true); } } @Override public void onProgressUpload(String fileName, long uploadedSize, long totalSize, boolean isEncrypted) { } @Override public void onProgressDownload(String fileName, long downloadSize, long totalSize) { radialProgress.setProgress(Math.min(1f, downloadSize / (float) totalSize), true); if (buttonState != 1) { updateButtonState(true); } } @Override public int getObserverTag() { return TAG; } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setEnabled(true); StringBuilder sb = new StringBuilder(LocaleController.getString("AttachVideo", R.string.AttachVideo)); if (captionLayout != null) { sb.append(", "); sb.append(captionLayout.getText()); } info.setText(sb.toString()); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (captionLayout != null) { blocks.add(captionLayout); } if (creditLayout != null) { blocks.add(creditLayout); } } } private class BlockAudioCell extends View implements DownloadController.FileDownloadProgressListener, TextSelectionHelper.ArticleSelectableView { private DrawingText captionLayout; private DrawingText creditLayout; private RadialProgress2 radialProgress; private SeekBar seekBar; private boolean isFirst; private int textX; private int textY = AndroidUtilities.dp(58); private int creditOffset; private String lastTimeString; private DrawingText titleLayout; private StaticLayout durationLayout; private int seekBarX; private int seekBarY; private int buttonX; private int buttonY; private int buttonState; private int buttonPressed; private int TAG; private TLRPC.TL_pageBlockAudio currentBlock; private TLRPC.Document currentDocument; private MessageObject currentMessageObject; private WebpageAdapter parentAdapter; public BlockAudioCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; radialProgress = new RadialProgress2(this); radialProgress.setCircleRadius(AndroidUtilities.dp(24)); TAG = DownloadController.getInstance(currentAccount).generateObserverTag(); seekBar = new SeekBar(this); seekBar.setDelegate(progress -> { if (currentMessageObject == null) { return; } currentMessageObject.audioProgress = progress; MediaController.getInstance().seekToProgress(currentMessageObject, progress); }); } public void setBlock(TLRPC.TL_pageBlockAudio block, boolean first, boolean last) { currentBlock = block; currentMessageObject = parentAdapter.audioBlocks.get(currentBlock); if (currentMessageObject != null) { currentDocument = currentMessageObject.getDocument(); } isFirst = first; seekBar.setColors(Theme.getColor(Theme.key_chat_inAudioSeekbar), Theme.getColor(Theme.key_chat_inAudioCacheSeekbar), Theme.getColor(Theme.key_chat_inAudioSeekbarFill), Theme.getColor(Theme.key_chat_inAudioSeekbarFill), Theme.getColor(Theme.key_chat_inAudioSeekbarSelected)); updateButtonState(false); requestLayout(); } public MessageObject getMessageObject() { return currentMessageObject; } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); boolean result = seekBar.onTouch(event.getAction(), event.getX() - seekBarX, event.getY() - seekBarY); if (result) { if (event.getAction() == MotionEvent.ACTION_DOWN) { getParent().requestDisallowInterceptTouchEvent(true); } invalidate(); return true; } if (event.getAction() == MotionEvent.ACTION_DOWN) { if (buttonState != -1 && x >= buttonX && x <= buttonX + AndroidUtilities.dp(48) && y >= buttonY && y <= buttonY + AndroidUtilities.dp(48) || buttonState == 0) { buttonPressed = 1; invalidate(); } } else if (event.getAction() == MotionEvent.ACTION_UP) { if (buttonPressed == 1) { buttonPressed = 0; playSoundEffect(SoundEffectConstants.CLICK); didPressedButton(true); invalidate(); } } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { buttonPressed = 0; } return buttonPressed != 0 || checkLayoutForLinks(parentAdapter, event, this, captionLayout, textX, textY) || checkLayoutForLinks(parentAdapter, event, this, creditLayout, textX, textY + creditOffset) || super.onTouchEvent(event); } @SuppressLint({"DrawAllocation", "NewApi"}) @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = AndroidUtilities.dp(54); if (currentBlock != null) { int textWidth; if (currentBlock.level > 0) { textX = AndroidUtilities.dp(14 * currentBlock.level) + AndroidUtilities.dp(18); } else { textX = AndroidUtilities.dp(18); } textWidth = width - textX - AndroidUtilities.dp(18); int size = AndroidUtilities.dp(44); buttonX = AndroidUtilities.dp(16); buttonY = AndroidUtilities.dp(5); radialProgress.setProgressRect(buttonX, buttonY, buttonX + size, buttonY + size); captionLayout = createLayoutForText(this, null, currentBlock.caption.text, textWidth, textY, currentBlock, parentAdapter); if (captionLayout != null) { creditOffset = AndroidUtilities.dp(8) + captionLayout.getHeight(); height += creditOffset + AndroidUtilities.dp(8); } creditLayout = createLayoutForText(this, null, currentBlock.caption.credit, textWidth, textY + creditOffset, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (creditLayout != null) { height += AndroidUtilities.dp(4) + creditLayout.getHeight(); } if (!isFirst && currentBlock.level <= 0) { height += AndroidUtilities.dp(8); } String author = currentMessageObject.getMusicAuthor(false); String title = currentMessageObject.getMusicTitle(false); seekBarX = buttonX + AndroidUtilities.dp(50) + size; int w = width - seekBarX - AndroidUtilities.dp(18); if (!TextUtils.isEmpty(title) || !TextUtils.isEmpty(author)) { SpannableStringBuilder stringBuilder; if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(author)) { stringBuilder = new SpannableStringBuilder(String.format("%s - %s", author, title)); } else if (!TextUtils.isEmpty(title)) { stringBuilder = new SpannableStringBuilder(title); } else { stringBuilder = new SpannableStringBuilder(author); } if (!TextUtils.isEmpty(author)) { TypefaceSpan span = new TypefaceSpan(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); stringBuilder.setSpan(span, 0, author.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); } CharSequence stringFinal = TextUtils.ellipsize(stringBuilder, Theme.chat_audioTitlePaint, w, TextUtils.TruncateAt.END); titleLayout = new DrawingText(); titleLayout.textLayout = new StaticLayout(stringFinal, audioTimePaint, w, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); titleLayout.parentBlock = currentBlock; seekBarY = buttonY + (size - AndroidUtilities.dp(30)) / 2 + AndroidUtilities.dp(11); } else { titleLayout = null; seekBarY = buttonY + (size - AndroidUtilities.dp(30)) / 2; } seekBar.setSize(w, AndroidUtilities.dp(30)); } else { height = 1; } setMeasuredDimension(width, height); updatePlayingMessageProgress(); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } radialProgress.setColors(Theme.key_chat_inLoader, Theme.key_chat_inLoaderSelected, Theme.key_chat_inMediaIcon, Theme.key_chat_inMediaIconSelected); radialProgress.setProgressColor(Theme.getColor(Theme.key_chat_inFileProgress)); radialProgress.draw(canvas); canvas.save(); canvas.translate(seekBarX, seekBarY); seekBar.draw(canvas); canvas.restore(); int count = 0; if (durationLayout != null) { canvas.save(); canvas.translate(buttonX + AndroidUtilities.dp(54), seekBarY + AndroidUtilities.dp(6)); durationLayout.draw(canvas); canvas.restore(); } if (titleLayout != null) { canvas.save(); titleLayout.x = buttonX + AndroidUtilities.dp(54); titleLayout.y = seekBarY - AndroidUtilities.dp(16); canvas.translate(titleLayout.x, titleLayout.y); drawTextSelection(canvas, this, count++); titleLayout.draw(canvas); canvas.restore(); } if (captionLayout != null) { canvas.save(); captionLayout.x = textX; captionLayout.y = textY; canvas.translate(textX, textY); drawTextSelection(canvas, this, count++); captionLayout.draw(canvas); canvas.restore(); } if (creditLayout != null) { canvas.save(); creditLayout.x = textX; creditLayout.y = textY + creditOffset; canvas.translate(textX, textY + creditOffset); drawTextSelection(canvas, this, count); creditLayout.draw(canvas); canvas.restore(); } if (currentBlock.level > 0) { canvas.drawRect(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(20), getMeasuredHeight() - (currentBlock.bottom ? AndroidUtilities.dp(6) : 0), quoteLinePaint); } } private int getIconForCurrentState() { if (buttonState == 1) { return MediaActionDrawable.ICON_PAUSE; } else if (buttonState == 2) { return MediaActionDrawable.ICON_DOWNLOAD; } else if (buttonState == 3) { return MediaActionDrawable.ICON_CANCEL; } return MediaActionDrawable.ICON_PLAY; } public void updatePlayingMessageProgress() { if (currentDocument == null || currentMessageObject == null) { return; } if (!seekBar.isDragging()) { seekBar.setProgress(currentMessageObject.audioProgress); } int duration = 0; if (MediaController.getInstance().isPlayingMessage(currentMessageObject)) { duration = currentMessageObject.audioProgressSec; } else { for (int a = 0; a < currentDocument.attributes.size(); a++) { TLRPC.DocumentAttribute attribute = currentDocument.attributes.get(a); if (attribute instanceof TLRPC.TL_documentAttributeAudio) { duration = attribute.duration; break; } } } String timeString = AndroidUtilities.formatShortDuration(duration); if (lastTimeString == null || lastTimeString != null && !lastTimeString.equals(timeString)) { lastTimeString = timeString; audioTimePaint.setTextSize(AndroidUtilities.dp(16)); int timeWidth = (int) Math.ceil(audioTimePaint.measureText(timeString)); durationLayout = new StaticLayout(timeString, audioTimePaint, timeWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); } audioTimePaint.setColor(getTextColor()); invalidate(); } public void updateButtonState(boolean animated) { String fileName = FileLoader.getAttachFileName(currentDocument); File path = FileLoader.getPathToAttach(currentDocument, true); boolean fileExists = path.exists(); if (TextUtils.isEmpty(fileName)) { radialProgress.setIcon(MediaActionDrawable.ICON_NONE, false, false); return; } 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.setIcon(getIconForCurrentState(), false, animated); } else { DownloadController.getInstance(currentAccount).addLoadingFileObserver(fileName, null, this); if (!FileLoader.getInstance(currentAccount).isLoadingFile(fileName)) { buttonState = 2; radialProgress.setProgress(0, animated); radialProgress.setIcon(getIconForCurrentState(), false, animated); } else { buttonState = 3; Float progress = ImageLoader.getInstance().getFileProgress(fileName); if (progress != null) { radialProgress.setProgress(progress, animated); } else { radialProgress.setProgress(0, animated); } radialProgress.setIcon(getIconForCurrentState(), true, animated); } } updatePlayingMessageProgress(); } private void didPressedButton(boolean animated) { if (buttonState == 0) { if (MediaController.getInstance().setPlaylist(parentAdapter.audioMessages, currentMessageObject, 0, false, null)) { buttonState = 1; radialProgress.setIcon(getIconForCurrentState(), false, animated); invalidate(); } } else if (buttonState == 1) { boolean result = MediaController.getInstance().pauseMessage(currentMessageObject); if (result) { buttonState = 0; radialProgress.setIcon(getIconForCurrentState(), false, animated); invalidate(); } } else if (buttonState == 2) { radialProgress.setProgress(0, false); FileLoader.getInstance(currentAccount).loadFile(currentDocument, parentAdapter.currentPage, 1, 1); buttonState = 3; radialProgress.setIcon(getIconForCurrentState(), true, animated); invalidate(); } else if (buttonState == 3) { FileLoader.getInstance(currentAccount).cancelLoadFile(currentDocument); buttonState = 2; radialProgress.setIcon(getIconForCurrentState(), false, animated); invalidate(); } } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); updateButtonState(false); } @Override public void onFailedDownload(String fileName, boolean canceled) { updateButtonState(true); } @Override public void onSuccessDownload(String fileName) { radialProgress.setProgress(1, true); updateButtonState(true); } @Override public void onProgressUpload(String fileName, long uploadedSize, long totalSize, boolean isEncrypted) { } @Override public void onProgressDownload(String fileName, long downloadSize, long totalSize) { radialProgress.setProgress(Math.min(1f, downloadSize / (float) totalSize), true); if (buttonState != 3) { updateButtonState(true); } } @Override public int getObserverTag() { return TAG; } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (titleLayout != null) { blocks.add(titleLayout); } if (captionLayout != null) { blocks.add(captionLayout); } if (creditLayout != null) { blocks.add(creditLayout); } } } private class BlockEmbedPostCell extends View implements TextSelectionHelper.ArticleSelectableView { private ImageReceiver avatarImageView; private AvatarDrawable avatarDrawable; private DrawingText dateLayout; private DrawingText nameLayout; private DrawingText captionLayout; private DrawingText creditLayout; private boolean avatarVisible; private int nameX; private int dateX; private int textX; private int textY; private int creditOffset; private int lineHeight; private TLRPC.TL_pageBlockEmbedPost currentBlock; private WebpageAdapter parentAdapter; public BlockEmbedPostCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; avatarImageView = new ImageReceiver(this); avatarImageView.setRoundRadius(AndroidUtilities.dp(20)); avatarImageView.setImageCoords(AndroidUtilities.dp(18 + 14), AndroidUtilities.dp(8), AndroidUtilities.dp(40), AndroidUtilities.dp(40)); avatarDrawable = new AvatarDrawable(); } public void setBlock(TLRPC.TL_pageBlockEmbedPost block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, captionLayout, textX, textY) || checkLayoutForLinks(parentAdapter, event, this, creditLayout, textX, textY + creditOffset) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height; if (currentBlock != null) { if (currentBlock instanceof TL_pageBlockEmbedPostCaption) { height = 0; textX = AndroidUtilities.dp(18); textY = AndroidUtilities.dp(4); int textWidth = width - AndroidUtilities.dp(36 + 14); captionLayout = createLayoutForText(this, null, currentBlock.caption.text, textWidth, textY, currentBlock, parentAdapter); if (captionLayout != null) { creditOffset = AndroidUtilities.dp(4) + captionLayout.getHeight(); height += creditOffset + AndroidUtilities.dp(4); } creditLayout = createLayoutForText(this, null, currentBlock.caption.credit, textWidth, textY + creditOffset, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (creditLayout != null) { height += AndroidUtilities.dp(4) + creditLayout.getHeight(); } } else { if (avatarVisible = (currentBlock.author_photo_id != 0)) { TLRPC.Photo photo = parentAdapter.getPhotoWithId(currentBlock.author_photo_id); if (avatarVisible = (photo instanceof TLRPC.TL_photo)) { avatarDrawable.setInfo(0, currentBlock.author, null); TLRPC.PhotoSize image = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.dp(40), true); avatarImageView.setImage(ImageLocation.getForPhoto(image, photo), "40_40", avatarDrawable, 0, null, parentAdapter.currentPage, 1); } } nameLayout = createLayoutForText(this, currentBlock.author, null, width - AndroidUtilities.dp(36 + 14 + (avatarVisible ? 40 + 14 : 0)), 0, currentBlock, Layout.Alignment.ALIGN_NORMAL, 1, parentAdapter); if (nameLayout != null){ nameLayout.x = AndroidUtilities.dp(18 + 14 + (avatarVisible ? 40 + 14 : 0)); nameLayout.y = AndroidUtilities.dp(dateLayout != null ? 10 : 19); } if (currentBlock.date != 0) { dateLayout = createLayoutForText(this, LocaleController.getInstance().chatFullDate.format((long) currentBlock.date * 1000), null, width - AndroidUtilities.dp(36 + 14 + (avatarVisible ? 40 + 14 : 0)), AndroidUtilities.dp(29), currentBlock, parentAdapter); } else { dateLayout = null; } height = AndroidUtilities.dp(40 + 8 + 8); if (currentBlock.blocks.isEmpty()) { textX = AndroidUtilities.dp(18 + 14); textY = AndroidUtilities.dp(40 + 8 + 8); int textWidth = width - AndroidUtilities.dp(36 + 14); captionLayout = createLayoutForText(this, null, currentBlock.caption.text, textWidth, textY, currentBlock, parentAdapter); if (captionLayout != null) { creditOffset = AndroidUtilities.dp(4) + captionLayout.getHeight(); height += creditOffset + AndroidUtilities.dp(4); } creditLayout = createLayoutForText(this, null, currentBlock.caption.credit, textWidth, textY + creditOffset, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (creditLayout != null) { height += AndroidUtilities.dp(4) + creditLayout.getHeight(); } } else { captionLayout = null; creditLayout = null; } if (dateLayout != null) { dateLayout.x = AndroidUtilities.dp(18 + 14 + (avatarVisible ? 40 + 14 : 0)); dateLayout.y = AndroidUtilities.dp(29); } if (captionLayout != null) { captionLayout.x = textX; captionLayout.y = textY; } if (creditLayout != null) { creditLayout.x = textX; creditLayout.y = textY; } } lineHeight = height; } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } int count = 0; if (!(currentBlock instanceof TL_pageBlockEmbedPostCaption)) { if (avatarVisible) { avatarImageView.draw(canvas); } if (nameLayout != null) { canvas.save(); canvas.translate(AndroidUtilities.dp(18 + 14 + (avatarVisible ? 40 + 14 : 0)), AndroidUtilities.dp(dateLayout != null ? 10 : 19)); drawTextSelection(canvas, this, count++); nameLayout.draw(canvas); canvas.restore(); } if (dateLayout != null) { canvas.save(); canvas.translate(AndroidUtilities.dp(18 + 14 + (avatarVisible ? 40 + 14 : 0)), AndroidUtilities.dp(29)); drawTextSelection(canvas, this, count++); dateLayout.draw(canvas); canvas.restore(); } canvas.drawRect(AndroidUtilities.dp(18), AndroidUtilities.dp(6), AndroidUtilities.dp(20), lineHeight - (currentBlock.level != 0 ? 0 : AndroidUtilities.dp(6)), quoteLinePaint); } if (captionLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this, count++); captionLayout.draw(canvas); canvas.restore(); } if (creditLayout != null) { canvas.save(); canvas.translate(textX, textY + creditOffset); drawTextSelection(canvas, this, count); creditLayout.draw(canvas); canvas.restore(); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (nameLayout != null) { blocks.add(nameLayout); } if (dateLayout != null) { blocks.add(dateLayout); } if (captionLayout != null) { blocks.add(captionLayout); } if (creditLayout != null) { blocks.add(creditLayout); } } } public class BlockParagraphCell extends View implements TextSelectionHelper.ArticleSelectableView { public DrawingText textLayout; public int textX; public int textY; private TLRPC.TL_pageBlockParagraph currentBlock; private WebpageAdapter parentAdapter; public BlockParagraphCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; } public void setBlock(TLRPC.TL_pageBlockParagraph block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { if (currentBlock.level == 0) { textY = AndroidUtilities.dp(8); textX = AndroidUtilities.dp(18); } else { textY = 0; textX = AndroidUtilities.dp(18 + 14 * currentBlock.level); } textLayout = createLayoutForText(this, null, currentBlock.text, width - AndroidUtilities.dp(18) - textX, textY, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, 0, parentAdapter); if (textLayout != null) { height = textLayout.getHeight(); if (currentBlock.level > 0) { height += AndroidUtilities.dp(8); } else { height += AndroidUtilities.dp(8 + 8); } textLayout.x = textX; textLayout.y = textY; } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this); textLayout.draw(canvas); canvas.restore(); } if (currentBlock.level > 0) { canvas.drawRect(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(20), getMeasuredHeight() - (currentBlock.bottom ? AndroidUtilities.dp(6) : 0), quoteLinePaint); } } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setEnabled(true); if (textLayout == null) return; info.setText(textLayout.getText()); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } } private class BlockEmbedCell extends FrameLayout implements TextSelectionHelper.ArticleSelectableView { private class TelegramWebviewProxy { @JavascriptInterface public void postEvent(final String eventName, final String eventData) { AndroidUtilities.runOnUIThread(() -> { if ("resize_frame".equals(eventName)) { try { JSONObject object = new JSONObject(eventData); exactWebViewHeight = Utilities.parseInt(object.getString("height")); requestLayout(); } catch (Throwable ignore) { } } }); } } private TouchyWebView webView; private WebPlayerView videoView; private DrawingText captionLayout; private DrawingText creditLayout; private int textX; private int textY; private int creditOffset; private int listX; private int exactWebViewHeight; private boolean wasUserInteraction; private TLRPC.TL_pageBlockEmbed currentBlock; private WebpageAdapter parentAdapter; public class TouchyWebView extends WebView { public TouchyWebView(Context context) { super(context); setFocusable(false); } @Override public boolean onTouchEvent(MotionEvent event) { wasUserInteraction = true; if (currentBlock != null) { if (currentBlock.allow_scrolling) { requestDisallowInterceptTouchEvent(true); } else { windowView.requestDisallowInterceptTouchEvent(true); } } return super.onTouchEvent(event); } } @SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface"}) public BlockEmbedCell(final Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; setWillNotDraw(false); videoView = new WebPlayerView(context, false, false, new WebPlayerView.WebPlayerViewDelegate() { @Override public void onInitFailed() { webView.setVisibility(VISIBLE); videoView.setVisibility(INVISIBLE); videoView.loadVideo(null, null, null, null, false); HashMap args = new HashMap<>(); args.put("Referer", ApplicationLoader.applicationContext.getPackageName()); webView.loadUrl(currentBlock.url, args); } @Override public void onVideoSizeChanged(float aspectRatio, int rotation) { fullscreenAspectRatioView.setAspectRatio(aspectRatio, rotation); } @Override public void onInlineSurfaceTextureReady() { } @Override public TextureView onSwitchToFullscreen(View controlsView, boolean fullscreen, float aspectRatio, int rotation, boolean byButton) { if (fullscreen) { fullscreenAspectRatioView.addView(fullscreenTextureView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); fullscreenAspectRatioView.setVisibility(View.VISIBLE); fullscreenAspectRatioView.setAspectRatio(aspectRatio, rotation); fullscreenedVideo = videoView; fullscreenVideoContainer.addView(controlsView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); fullscreenVideoContainer.setVisibility(VISIBLE); } else { fullscreenAspectRatioView.removeView(fullscreenTextureView); fullscreenedVideo = null; fullscreenAspectRatioView.setVisibility(View.GONE); fullscreenVideoContainer.setVisibility(INVISIBLE); } return fullscreenTextureView; } @Override public void prepareToSwitchInlineMode(boolean inline, Runnable switchInlineModeRunnable, float aspectRatio, boolean animated) { } @Override public TextureView onSwitchInlineMode(View controlsView, boolean inline, float aspectRatio, int rotation, boolean animated) { return null; } @Override public void onSharePressed() { if (parentActivity == null) { return; } showDialog(new ShareAlert(parentActivity, null, currentBlock.url, false, currentBlock.url, false)); } @Override public void onPlayStateChanged(WebPlayerView playerView, boolean playing) { if (playing) { if (currentPlayingVideo != null && currentPlayingVideo != playerView) { currentPlayingVideo.pause(); } currentPlayingVideo = playerView; try { parentActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } catch (Exception e) { FileLog.e(e); } } else { if (currentPlayingVideo == playerView) { currentPlayingVideo = null; } try { parentActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } catch (Exception e) { FileLog.e(e); } } } @Override public boolean checkInlinePermissions() { return false; } @Override public ViewGroup getTextureViewContainer() { return null; } }); addView(videoView); createdWebViews.add(this); webView = new TouchyWebView(context); webView.getSettings().setJavaScriptEnabled(true); webView.getSettings().setDomStorageEnabled(true); webView.getSettings().setAllowContentAccess(true); if (Build.VERSION.SDK_INT >= 17) { webView.getSettings().setMediaPlaybackRequiresUserGesture(false); webView.addJavascriptInterface(new TelegramWebviewProxy(), "TelegramWebviewProxy"); } if (Build.VERSION.SDK_INT >= 21) { webView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptThirdPartyCookies(webView, true); } webView.setWebChromeClient(new WebChromeClient() { @Override public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) { onShowCustomView(view, callback); } @Override public void onShowCustomView(View view, CustomViewCallback callback) { if (customView != null) { callback.onCustomViewHidden(); return; } customView = view; customViewCallback = callback; AndroidUtilities.runOnUIThread(() -> { if (customView != null) { fullscreenVideoContainer.addView(customView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); fullscreenVideoContainer.setVisibility(VISIBLE); } }, 100); } @Override public void onHideCustomView() { super.onHideCustomView(); if (customView == null) { return; } fullscreenVideoContainer.setVisibility(INVISIBLE); fullscreenVideoContainer.removeView(customView); if (customViewCallback != null && !customViewCallback.getClass().getName().contains(".chromium.")) { customViewCallback.onCustomViewHidden(); } customView = null; } }); webView.setWebViewClient(new WebViewClient() { @Override public void onLoadResource(WebView view, String url) { super.onLoadResource(view, url); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); //progressBar.setVisibility(INVISIBLE); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (wasUserInteraction) { Browser.openUrl(parentActivity, url); return true; } return false; } }); addView(webView); } public void destroyWebView(boolean completely) { try { webView.stopLoading(); webView.loadUrl("about:blank"); if (completely) { webView.destroy(); } currentBlock = null; } catch (Exception e) { FileLog.e(e); } videoView.destroy(); } public void setBlock(TLRPC.TL_pageBlockEmbed block) { TLRPC.TL_pageBlockEmbed previousBlock = currentBlock; currentBlock = block; webView.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite)); if (previousBlock != currentBlock) { wasUserInteraction = false; if (currentBlock.allow_scrolling) { webView.setVerticalScrollBarEnabled(true); webView.setHorizontalScrollBarEnabled(true); } else { webView.setVerticalScrollBarEnabled(false); webView.setHorizontalScrollBarEnabled(false); } exactWebViewHeight = 0; try { webView.loadUrl("about:blank"); } catch (Exception e) { FileLog.e(e); } try { if (currentBlock.html != null) { webView.loadDataWithBaseURL("https://telegram.org/embed", currentBlock.html, "text/html", "UTF-8", null); videoView.setVisibility(INVISIBLE); videoView.loadVideo(null, null, null, null, false); webView.setVisibility(VISIBLE); } else { TLRPC.Photo thumb = currentBlock.poster_photo_id != 0 ? parentAdapter.getPhotoWithId(currentBlock.poster_photo_id) : null; boolean handled = videoView.loadVideo(block.url, thumb, parentAdapter.currentPage, null, false); if (handled) { webView.setVisibility(INVISIBLE); videoView.setVisibility(VISIBLE); webView.stopLoading(); webView.loadUrl("about:blank"); } else { webView.setVisibility(VISIBLE); videoView.setVisibility(INVISIBLE); videoView.loadVideo(null, null, null, null, false); HashMap args = new HashMap<>(); args.put("Referer", ApplicationLoader.applicationContext.getPackageName()); webView.loadUrl(currentBlock.url, args); } } } catch (Exception e) { FileLog.e(e); } } requestLayout(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (!isVisible) { currentBlock = null; } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, captionLayout, textX, textY) || checkLayoutForLinks(parentAdapter, event, this, creditLayout, textX, textY + creditOffset) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height; if (currentBlock != null) { int listWidth = width; int textWidth; if (currentBlock.level > 0) { textX = listX = AndroidUtilities.dp(14 * currentBlock.level) + AndroidUtilities.dp(18); listWidth -= listX + AndroidUtilities.dp(18); textWidth = listWidth; } else { listX = 0; textX = AndroidUtilities.dp(18); textWidth = width - AndroidUtilities.dp(36); if (!currentBlock.full_width) { listWidth -= AndroidUtilities.dp(36); listX += AndroidUtilities.dp(18); } } float scale; if (currentBlock.w == 0) { scale = 1; } else { scale = width / (float) currentBlock.w; } if (exactWebViewHeight != 0) { height = AndroidUtilities.dp(exactWebViewHeight); } else { height = (int) (currentBlock.w == 0 ? AndroidUtilities.dp(currentBlock.h) * scale : currentBlock.h * scale); } if (height == 0) { height = AndroidUtilities.dp(10); } webView.measure(MeasureSpec.makeMeasureSpec(listWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); if (videoView.getParent() == this) { videoView.measure(MeasureSpec.makeMeasureSpec(listWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height + AndroidUtilities.dp(10), MeasureSpec.EXACTLY)); } textY = AndroidUtilities.dp(8) + height; captionLayout = createLayoutForText(this, null, currentBlock.caption.text, textWidth, textY, currentBlock, parentAdapter); if (captionLayout != null) { creditOffset = AndroidUtilities.dp(4) + captionLayout.getHeight(); height += creditOffset + AndroidUtilities.dp(4); } else { creditOffset = 0; } creditLayout = createLayoutForText(this, null, currentBlock.caption.credit, textWidth, textY + creditOffset, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (creditLayout != null) { height += AndroidUtilities.dp(4) + creditLayout.getHeight(); creditLayout.x = textX; creditLayout.y = creditOffset; } height += AndroidUtilities.dp(5); if (currentBlock.level > 0 && !currentBlock.bottom) { height += AndroidUtilities.dp(8); } else if (currentBlock.level == 0 && captionLayout != null) { height += AndroidUtilities.dp(8); } if (captionLayout != null) { captionLayout.x = textX; captionLayout.y = textY; } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { webView.layout(listX, 0, listX + webView.getMeasuredWidth(), webView.getMeasuredHeight()); if (videoView.getParent() == this) { videoView.layout(listX, 0, listX + videoView.getMeasuredWidth(), videoView.getMeasuredHeight()); } } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } int count = 0; if (captionLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this, count++); captionLayout.draw(canvas); canvas.restore(); } if (creditLayout != null) { canvas.save(); canvas.translate(textX, textY + creditOffset); drawTextSelection(canvas, this, count); creditLayout.draw(canvas); canvas.restore(); } if (currentBlock.level > 0) { canvas.drawRect(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(20), getMeasuredHeight() - (currentBlock.bottom ? AndroidUtilities.dp(6) : 0), quoteLinePaint); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (captionLayout != null) { blocks.add(captionLayout); } if (creditLayout != null) { blocks.add(creditLayout); } } } public class BlockTableCell extends FrameLayout implements TableLayout.TableLayoutDelegate, TextSelectionHelper.ArticleSelectableView { private HorizontalScrollView scrollView; private DrawingText titleLayout; private TableLayout tableLayout; private int listX; private int listY; private int textX; private int textY; private boolean firstLayout; private TLRPC.TL_pageBlockTable currentBlock; private WebpageAdapter parentAdapter; public BlockTableCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; scrollView = new HorizontalScrollView(context) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { boolean intercept = super.onInterceptTouchEvent(ev); if (tableLayout.getMeasuredWidth() > getMeasuredWidth() - AndroidUtilities.dp(36) && intercept) { windowView.requestDisallowInterceptTouchEvent(true); } return intercept; } @Override public boolean onTouchEvent(MotionEvent ev) { if (tableLayout.getMeasuredWidth() <= getMeasuredWidth() - AndroidUtilities.dp(36)) { return false; } return super.onTouchEvent(ev); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (pressedLinkOwnerLayout != null) { pressedLinkOwnerLayout = null; pressedLinkOwnerView = null; } updateChildTextPositions(); if (textSelectionHelper != null && textSelectionHelper.isSelectionMode()) { textSelectionHelper.invalidate(); } } @Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { removePressedLink(); return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { tableLayout.measure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight(), MeasureSpec.UNSPECIFIED), heightMeasureSpec); setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), tableLayout.getMeasuredHeight()); } }; scrollView.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); scrollView.setClipToPadding(false); addView(scrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); tableLayout = new TableLayout(context, this, textSelectionHelper); tableLayout.setOrientation(TableLayout.HORIZONTAL); tableLayout.setRowOrderPreserved(true); scrollView.addView(tableLayout, new HorizontalScrollView.LayoutParams(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT)); setWillNotDraw(false); } @Override public DrawingText createTextLayout(TLRPC.TL_pageTableCell cell, int maxWidth) { if (cell == null) { return null; } Layout.Alignment alignment; if (cell.align_right) { alignment = Layout.Alignment.ALIGN_OPPOSITE; } else if (cell.align_center) { alignment = Layout.Alignment.ALIGN_CENTER; } else { alignment = Layout.Alignment.ALIGN_NORMAL; } return createLayoutForText(this, null, cell.text, maxWidth, -1, currentBlock, alignment, 0, parentAdapter); } @Override public Paint getLinePaint() { return tableLinePaint; } @Override public Paint getHalfLinePaint() { return tableHalfLinePaint; } @Override public Paint getHeaderPaint() { return tableHeaderPaint; } @Override public Paint getStripPaint() { return tableStripPaint; } @Override public void onLayoutChild(DrawingText text, int x, int y) { if (text != null && !searchResults.isEmpty() && searchText != null) { String lowerString = text.textLayout.getText().toString().toLowerCase(); int startIndex = 0; int index; while ((index = lowerString.indexOf(searchText, startIndex)) >= 0) { startIndex = index + searchText.length(); if (index == 0 || AndroidUtilities.isPunctuationCharacter(lowerString.charAt(index - 1))) { adapter[0].searchTextOffset.put(searchText + currentBlock + text.parentText + index, y + text.textLayout.getLineTop(text.textLayout.getLineForOffset(index))); } } } } public void setBlock(TLRPC.TL_pageBlockTable block) { currentBlock = block; AndroidUtilities.setScrollViewEdgeEffectColor(scrollView, Theme.getColor(Theme.key_windowBackgroundWhite)); tableLayout.removeAllChildrens(); tableLayout.setDrawLines(currentBlock.bordered); tableLayout.setStriped(currentBlock.striped); tableLayout.setRtl(parentAdapter.isRtl); int maxCols = 0; if (!currentBlock.rows.isEmpty()) { TLRPC.TL_pageTableRow row = currentBlock.rows.get(0); for (int c = 0, size2 = row.cells.size(); c < size2; c++) { TLRPC.TL_pageTableCell cell = row.cells.get(c); maxCols += (cell.colspan != 0 ? cell.colspan : 1); } } for (int r = 0, size = currentBlock.rows.size(); r < size; r++) { TLRPC.TL_pageTableRow row = currentBlock.rows.get(r); int cols = 0; for (int c = 0, size2 = row.cells.size(); c < size2; c++) { TLRPC.TL_pageTableCell cell = row.cells.get(c); int colspan = (cell.colspan != 0 ? cell.colspan : 1); int rowspan = (cell.rowspan != 0 ? cell.rowspan : 1); if (cell.text != null) { tableLayout.addChild(cell, cols, r, colspan); } else { tableLayout.addChild(cols, r, colspan, rowspan); } cols += colspan; } } tableLayout.setColumnCount(maxCols); firstLayout = true; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { for (int i = 0, N = tableLayout.getChildCount(); i < N; i++) { TableLayout.Child c = tableLayout.getChildAt(i); if (checkLayoutForLinks(parentAdapter, event, this, c.textLayout, scrollView.getPaddingLeft() - scrollView.getScrollX() + listX + c.getTextX(), listY + c.getTextY())) { return true; } } return checkLayoutForLinks(parentAdapter, event, this, titleLayout, textX, textY) || super.onTouchEvent(event); } @Override public void invalidate() { super.invalidate(); tableLayout.invalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { int textWidth; if (currentBlock.level > 0) { listX = AndroidUtilities.dp(14 * currentBlock.level); textX = listX + AndroidUtilities.dp(18); textWidth = width - textX; } else { listX = 0; textX = AndroidUtilities.dp(18); textWidth = width - AndroidUtilities.dp(36); } titleLayout = createLayoutForText(this, null, currentBlock.title, textWidth, 0, currentBlock, Layout.Alignment.ALIGN_CENTER, 0, parentAdapter); if (titleLayout != null) { textY = 0; height += titleLayout.getHeight() + AndroidUtilities.dp(8); listY = height; titleLayout.x = textX; titleLayout.y = textY; } else { listY = AndroidUtilities.dp(8); } scrollView.measure(MeasureSpec.makeMeasureSpec(width - listX, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); height += scrollView.getMeasuredHeight() + AndroidUtilities.dp(8); if (currentBlock.level > 0 && !currentBlock.bottom) { height += AndroidUtilities.dp(8); } } else { height = 1; } setMeasuredDimension(width, height); updateChildTextPositions(); } private void updateChildTextPositions() { int count = titleLayout == null ? 0 : 1; for (int i = 0, N = tableLayout.getChildCount(); i < N; i++) { TableLayout.Child c = tableLayout.getChildAt(i); if (c.textLayout != null) { c.textLayout.x = c.getTextX() + listX + AndroidUtilities.dp(18) - scrollView.getScrollX(); c.textLayout.y = c.getTextY() + listY; c.textLayout.row = c.getRow(); c.setSelectionIndex(count++); } } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { scrollView.layout(listX, listY, listX + scrollView.getMeasuredWidth(), listY + scrollView.getMeasuredHeight()); if (firstLayout) { if (parentAdapter.isRtl) { scrollView.setScrollX(tableLayout.getMeasuredWidth() - scrollView.getMeasuredWidth() + AndroidUtilities.dp(36)); } else { scrollView.setScrollX(0); } firstLayout = false; } } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (titleLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this, 0); titleLayout.draw(canvas); canvas.restore(); } if (currentBlock.level > 0) { canvas.drawRect(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(20), getMeasuredHeight() - (currentBlock.bottom ? AndroidUtilities.dp(6) : 0), quoteLinePaint); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (titleLayout != null) { blocks.add(titleLayout); } for (int i = 0, N = tableLayout.getChildCount(); i < N; i++) { TableLayout.Child c = tableLayout.getChildAt(i); if (c.textLayout != null) { blocks.add(c.textLayout); } } } } private class BlockCollageCell extends FrameLayout implements TextSelectionHelper.ArticleSelectableView{ private RecyclerListView innerListView; private RecyclerView.Adapter innerAdapter; private DrawingText captionLayout; private DrawingText creditLayout; private int listX; private int textX; private int textY; private int creditOffset; private boolean inLayout; private TLRPC.TL_pageBlockCollage currentBlock; private GroupedMessages group = new GroupedMessages(); private WebpageAdapter parentAdapter; public class GroupedMessages { public long groupId; public boolean hasSibling; public ArrayList posArray = new ArrayList<>(); public HashMap positions = new HashMap<>(); private int maxSizeWidth = 1000; private class MessageGroupedLayoutAttempt { public int[] lineCounts; public float[] heights; public MessageGroupedLayoutAttempt(int i1, int i2, float f1, float f2) { lineCounts = new int[] {i1, i2}; heights = new float[] {f1, f2}; } public MessageGroupedLayoutAttempt(int i1, int i2, int i3, float f1, float f2, float f3) { lineCounts = new int[] {i1, i2, i3}; heights = new float[] {f1, f2, f3}; } public MessageGroupedLayoutAttempt(int i1, int i2, int i3, int i4, float f1, float f2, float f3, float f4) { lineCounts = new int[] {i1, i2, i3, i4}; heights = new float[] {f1, f2, f3, f4}; } } private float multiHeight(float[] array, int start, int end) { float sum = 0; for (int a = start; a < end; a++) { sum += array[a]; } return maxSizeWidth / sum; } public void calculate() { posArray.clear(); positions.clear(); int count = currentBlock.items.size(); if (count <= 1) { return; } float maxSizeHeight = 814.0f; StringBuilder proportions = new StringBuilder(); float averageAspectRatio = 1.0f; boolean forceCalc = false; hasSibling = false; for (int a = 0; a < count; a++) { TLRPC.PhotoSize photoSize; TLObject object = currentBlock.items.get(a); if (object instanceof TLRPC.TL_pageBlockPhoto) { TLRPC.TL_pageBlockPhoto pageBlockPhoto = (TLRPC.TL_pageBlockPhoto) object; TLRPC.Photo photo = parentAdapter.getPhotoWithId(pageBlockPhoto.photo_id); if (photo == null) { continue; } photoSize = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.getPhotoSize()); } else if (object instanceof TLRPC.TL_pageBlockVideo) { TLRPC.TL_pageBlockVideo pageBlockVideo = (TLRPC.TL_pageBlockVideo) object; TLRPC.Document document = parentAdapter.getDocumentWithId(pageBlockVideo.video_id); if (document == null) { continue; } photoSize = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90); } else { continue; } MessageObject.GroupedMessagePosition position = new MessageObject.GroupedMessagePosition(); position.last = a == count - 1; position.aspectRatio = photoSize == null ? 1.0f : photoSize.w / (float) photoSize.h; if (position.aspectRatio > 1.2f) { proportions.append("w"); } else if (position.aspectRatio < 0.8f) { proportions.append("n"); } else { proportions.append("q"); } averageAspectRatio += position.aspectRatio; if (position.aspectRatio > 2.0f) { forceCalc = true; } positions.put(object, position); posArray.add(position); } int minHeight = AndroidUtilities.dp(120); int minWidth = (int) (AndroidUtilities.dp(120) / (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) / (float) maxSizeWidth)); int paddingsWidth = (int) (AndroidUtilities.dp(40) / (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) / (float) maxSizeWidth)); float maxAspectRatio = maxSizeWidth / maxSizeHeight; averageAspectRatio = averageAspectRatio / count; if (!forceCalc && (count == 2 || count == 3 || count == 4)) { if (count == 2) { MessageObject.GroupedMessagePosition position1 = posArray.get(0); MessageObject.GroupedMessagePosition position2 = posArray.get(1); String pString = proportions.toString(); if (pString.equals("ww") && averageAspectRatio > 1.4 * maxAspectRatio && position1.aspectRatio - position2.aspectRatio < 0.2) { float height = Math.round(Math.min(maxSizeWidth / position1.aspectRatio, Math.min(maxSizeWidth / position2.aspectRatio, maxSizeHeight / 2.0f))) / maxSizeHeight; position1.set(0, 0, 0, 0, maxSizeWidth, height, POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT | POSITION_FLAG_TOP); position2.set(0, 0, 1, 1, maxSizeWidth, height, POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); } else if (pString.equals("ww") || pString.equals("qq")) { int width = maxSizeWidth / 2; float height = Math.round(Math.min(width / position1.aspectRatio, Math.min(width / position2.aspectRatio, maxSizeHeight))) / maxSizeHeight; position1.set(0, 0, 0, 0, width, height, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM | POSITION_FLAG_TOP); position2.set(1, 1, 0, 0, width, height, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM | POSITION_FLAG_TOP); } else { int secondWidth = (int) Math.max(0.4f * maxSizeWidth, Math.round((maxSizeWidth / position1.aspectRatio / (1.0f / position1.aspectRatio + 1.0f / position2.aspectRatio)))); int firstWidth = maxSizeWidth - secondWidth; if (firstWidth < minWidth) { int diff = minWidth - firstWidth; firstWidth = minWidth; secondWidth -= diff; } float height = Math.min(maxSizeHeight, Math.round(Math.min(firstWidth / position1.aspectRatio, secondWidth / position2.aspectRatio))) / maxSizeHeight; position1.set(0, 0, 0, 0, firstWidth, height, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM | POSITION_FLAG_TOP); position2.set(1, 1, 0, 0, secondWidth, height, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM | POSITION_FLAG_TOP); } } else if (count == 3) { MessageObject.GroupedMessagePosition position1 = posArray.get(0); MessageObject.GroupedMessagePosition position2 = posArray.get(1); MessageObject.GroupedMessagePosition position3 = posArray.get(2); if (proportions.charAt(0) == 'n') { float thirdHeight = Math.min(maxSizeHeight * 0.5f, Math.round(position2.aspectRatio * maxSizeWidth / (position3.aspectRatio + position2.aspectRatio))); float secondHeight = maxSizeHeight - thirdHeight; int rightWidth = (int) Math.max(minWidth, Math.min(maxSizeWidth * 0.5f, Math.round(Math.min(thirdHeight * position3.aspectRatio, secondHeight * position2.aspectRatio)))); int leftWidth = Math.round(Math.min(maxSizeHeight * position1.aspectRatio + paddingsWidth, maxSizeWidth - rightWidth)); position1.set(0, 0, 0, 1, leftWidth, 1.0f, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM | POSITION_FLAG_TOP); position2.set(1, 1, 0, 0, rightWidth, secondHeight / maxSizeHeight, POSITION_FLAG_RIGHT | POSITION_FLAG_TOP); position3.set(0, 1, 1, 1, rightWidth, thirdHeight / maxSizeHeight, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); position3.spanSize = maxSizeWidth; position1.siblingHeights = new float[] {thirdHeight / maxSizeHeight, secondHeight / maxSizeHeight}; position2.spanSize = maxSizeWidth - leftWidth; position3.leftSpanOffset = leftWidth; hasSibling = true; } else { float firstHeight = Math.round(Math.min(maxSizeWidth / position1.aspectRatio, (maxSizeHeight) * 0.66f)) / maxSizeHeight; position1.set(0, 1, 0, 0, maxSizeWidth, firstHeight, POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT | POSITION_FLAG_TOP); int width = maxSizeWidth / 2; float secondHeight = Math.min(maxSizeHeight - firstHeight, Math.round(Math.min(width / position2.aspectRatio, width / position3.aspectRatio))) / maxSizeHeight; position2.set(0, 0, 1, 1, width, secondHeight, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM); position3.set(1, 1, 1, 1, width, secondHeight, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); } } else if (count == 4) { MessageObject.GroupedMessagePosition position1 = posArray.get(0); MessageObject.GroupedMessagePosition position2 = posArray.get(1); MessageObject.GroupedMessagePosition position3 = posArray.get(2); MessageObject.GroupedMessagePosition position4 = posArray.get(3); if (proportions.charAt(0) == 'w') { float h0 = Math.round(Math.min(maxSizeWidth / position1.aspectRatio, maxSizeHeight * 0.66f)) / maxSizeHeight; position1.set(0, 2, 0, 0, maxSizeWidth, h0, POSITION_FLAG_LEFT | POSITION_FLAG_RIGHT | POSITION_FLAG_TOP); float h = Math.round(maxSizeWidth / (position2.aspectRatio + position3.aspectRatio + position4.aspectRatio)); int w0 = (int) Math.max(minWidth, Math.min(maxSizeWidth * 0.4f, h * position2.aspectRatio)); int w2 = (int) Math.max(Math.max(minWidth, maxSizeWidth * 0.33f), h * position4.aspectRatio); int w1 = maxSizeWidth - w0 - w2; h = Math.min(maxSizeHeight - h0, h); h /= maxSizeHeight; position2.set(0, 0, 1, 1, w0, h, POSITION_FLAG_LEFT | POSITION_FLAG_BOTTOM); position3.set(1, 1, 1, 1, w1, h, POSITION_FLAG_BOTTOM); position4.set(2, 2, 1, 1, w2, h, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); } else { int w = Math.max(minWidth, Math.round(maxSizeHeight / (1.0f / position2.aspectRatio + 1.0f / position3.aspectRatio + 1.0f / posArray.get(3).aspectRatio))); float h0 = Math.min(0.33f, Math.max(minHeight, w / position2.aspectRatio) / maxSizeHeight); float h1 = Math.min(0.33f, Math.max(minHeight, w / position3.aspectRatio) / maxSizeHeight); float h2 = 1.0f - h0 - h1; int w0 = Math.round(Math.min(maxSizeHeight * position1.aspectRatio + paddingsWidth, maxSizeWidth - w)); position1.set(0, 0, 0, 2, w0, h0 + h1 + h2, POSITION_FLAG_LEFT | POSITION_FLAG_TOP | POSITION_FLAG_BOTTOM); position2.set(1, 1, 0, 0, w, h0, POSITION_FLAG_RIGHT | POSITION_FLAG_TOP); position3.set(0, 1, 1, 1, w, h1, POSITION_FLAG_RIGHT); position3.spanSize = maxSizeWidth; position4.set(0, 1, 2, 2, w, h2, POSITION_FLAG_RIGHT | POSITION_FLAG_BOTTOM); position4.spanSize = maxSizeWidth; position2.spanSize = maxSizeWidth - w0; position3.leftSpanOffset = w0; position4.leftSpanOffset = w0; position1.siblingHeights = new float[] {h0, h1, h2}; hasSibling = true; } } } else { float[] croppedRatios = new float[posArray.size()]; for (int a = 0; a < count; a++) { if (averageAspectRatio > 1.1f) { croppedRatios[a] = Math.max(1.0f, posArray.get(a).aspectRatio); } else { croppedRatios[a] = Math.min(1.0f, posArray.get(a).aspectRatio); } croppedRatios[a] = Math.max(0.66667f, Math.min(1.7f, croppedRatios[a])); } int firstLine; int secondLine; int thirdLine; int fourthLine; ArrayList attempts = new ArrayList<>(); for (firstLine = 1; firstLine < croppedRatios.length; firstLine++) { secondLine = croppedRatios.length - firstLine; if (firstLine > 3 || secondLine > 3) { continue; } attempts.add(new MessageGroupedLayoutAttempt(firstLine, secondLine, multiHeight(croppedRatios, 0, firstLine), multiHeight(croppedRatios, firstLine, croppedRatios.length))); } for (firstLine = 1; firstLine < croppedRatios.length - 1; firstLine++) { for (secondLine = 1; secondLine < croppedRatios.length - firstLine; secondLine++) { thirdLine = croppedRatios.length - firstLine - secondLine; if (firstLine > 3 || secondLine > (averageAspectRatio < 0.85f ? 4 : 3) || thirdLine > 3) { continue; } attempts.add(new MessageGroupedLayoutAttempt(firstLine, secondLine, thirdLine, multiHeight(croppedRatios, 0, firstLine), multiHeight(croppedRatios, firstLine, firstLine + secondLine), multiHeight(croppedRatios, firstLine + secondLine, croppedRatios.length))); } } for (firstLine = 1; firstLine < croppedRatios.length - 2; firstLine++) { for (secondLine = 1; secondLine < croppedRatios.length - firstLine; secondLine++) { for (thirdLine = 1; thirdLine < croppedRatios.length - firstLine - secondLine; thirdLine++) { fourthLine = croppedRatios.length - firstLine - secondLine - thirdLine; if (firstLine > 3 || secondLine > 3 || thirdLine > 3 || fourthLine > 3) { continue; } attempts.add(new MessageGroupedLayoutAttempt(firstLine, secondLine, thirdLine, fourthLine, multiHeight(croppedRatios, 0, firstLine), multiHeight(croppedRatios, firstLine, firstLine + secondLine), multiHeight(croppedRatios, firstLine + secondLine, firstLine + secondLine + thirdLine), multiHeight(croppedRatios, firstLine + secondLine + thirdLine, croppedRatios.length))); } } } MessageGroupedLayoutAttempt optimal = null; float optimalDiff = 0.0f; float maxHeight = maxSizeWidth / 3 * 4; for (int a = 0; a < attempts.size(); a++) { MessageGroupedLayoutAttempt attempt = attempts.get(a); float height = 0; float minLineHeight = Float.MAX_VALUE; for (int b = 0; b < attempt.heights.length; b++){ height += attempt.heights[b]; if (attempt.heights[b] < minLineHeight) { minLineHeight = attempt.heights[b]; } } float diff = Math.abs(height - maxHeight); if (attempt.lineCounts.length > 1) { if (attempt.lineCounts[0] > attempt.lineCounts[1] || (attempt.lineCounts.length > 2 && attempt.lineCounts[1] > attempt.lineCounts[2]) || (attempt.lineCounts.length > 3 && attempt.lineCounts[2] > attempt.lineCounts[3])) { diff *= 1.2f; } } if (minLineHeight < minWidth) { diff *= 1.5f; } if (optimal == null || diff < optimalDiff) { optimal = attempt; optimalDiff = diff; } } if (optimal == null) { return; } int index = 0; float y = 0.0f; for (int i = 0; i < optimal.lineCounts.length; i++) { int c = optimal.lineCounts[i]; float lineHeight = optimal.heights[i]; int spanLeft = maxSizeWidth; MessageObject.GroupedMessagePosition posToFix = null; for (int k = 0; k < c; k++) { float ratio = croppedRatios[index]; int width = (int) (ratio * lineHeight); spanLeft -= width; MessageObject.GroupedMessagePosition pos = posArray.get(index); int flags = 0; if (i == 0) { flags |= POSITION_FLAG_TOP; } if (i == optimal.lineCounts.length - 1) { flags |= POSITION_FLAG_BOTTOM; } if (k == 0) { flags |= POSITION_FLAG_LEFT; } if (k == c - 1) { flags |= POSITION_FLAG_RIGHT; posToFix = pos; } pos.set(k, k, i, i, width, lineHeight / maxSizeHeight, flags); index++; } posToFix.pw += spanLeft; posToFix.spanSize += spanLeft; y += lineHeight; } } for (int a = 0; a < count; a++) { MessageObject.GroupedMessagePosition pos = posArray.get(a); if ((pos.flags & POSITION_FLAG_LEFT) != 0) { pos.edge = true; } } } } public BlockCollageCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; innerListView = new RecyclerListView(context) { @Override public void requestLayout() { if (inLayout) { return; } super.requestLayout(); } }; innerListView.addItemDecoration(new RecyclerView.ItemDecoration() { @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { outRect.bottom = 0; MessageObject.GroupedMessagePosition position; if (view instanceof BlockPhotoCell) { position = group.positions.get(((BlockPhotoCell) view).currentBlock); } else if (view instanceof BlockVideoCell) { position = group.positions.get(((BlockVideoCell) view).currentBlock); } else { position = null; } if (position != null && position.siblingHeights != null) { float maxHeight = Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f; int h = 0; for (int a = 0; a < position.siblingHeights.length; a++) { h += (int) Math.ceil(maxHeight * position.siblingHeights[a]); } h += (position.maxY - position.minY) * AndroidUtilities.dp2(11); int count = group.posArray.size(); for (int a = 0; a < count; a++) { MessageObject.GroupedMessagePosition pos = group.posArray.get(a); if (pos.minY != position.minY || pos.minX == position.minX && pos.maxX == position.maxX && pos.minY == position.minY && pos.maxY == position.maxY) { continue; } if (pos.minY == position.minY) { h -= (int) Math.ceil(maxHeight * pos.ph) - AndroidUtilities.dp(4); break; } } outRect.bottom = -h; } //outRect.top = outRect.left = 0; //outRect.bottom = outRect.right = AndroidUtilities.dp(2); } }); GridLayoutManager gridLayoutManager = new GridLayoutManagerFixed(context, 1000, LinearLayoutManager.VERTICAL, true) { @Override public boolean supportsPredictiveItemAnimations() { return false; } @Override public boolean shouldLayoutChildFromOpositeSide(View child) { return false; } @Override protected boolean hasSiblingChild(int position) { TLObject message = currentBlock.items.get(currentBlock.items.size() - position - 1); MessageObject.GroupedMessagePosition pos = group.positions.get(message); if (pos.minX == pos.maxX || pos.minY != pos.maxY || pos.minY == 0) { return false; } int count = group.posArray.size(); for (int a = 0; a < count; a++) { MessageObject.GroupedMessagePosition p = group.posArray.get(a); if (p == pos) { continue; } if (p.minY <= pos.minY && p.maxY >= pos.minY) { return true; } } return false; } }; gridLayoutManager.setSpanSizeLookup(new GridLayoutManagerFixed.SpanSizeLookup() { @Override public int getSpanSize(int position) { TLObject message = currentBlock.items.get(currentBlock.items.size() - position - 1); return group.positions.get(message).spanSize; } }); innerListView.setLayoutManager(gridLayoutManager); innerListView.setAdapter(innerAdapter = new RecyclerView.Adapter() { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view; switch (viewType) { case 0: { view = new BlockPhotoCell(getContext(), parentAdapter, 2); break; } case 1: default: { view = new BlockVideoCell(getContext(), parentAdapter, 2); break; } } return new RecyclerListView.Holder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { TLRPC.PageBlock pageBlock = currentBlock.items.get(currentBlock.items.size() - position - 1); switch (holder.getItemViewType()) { case 0: { BlockPhotoCell cell = (BlockPhotoCell) holder.itemView; cell.groupPosition = group.positions.get(pageBlock); cell.setBlock((TLRPC.TL_pageBlockPhoto) pageBlock, true, true); break; } case 1: default: { BlockVideoCell cell = (BlockVideoCell) holder.itemView; cell.groupPosition = group.positions.get(pageBlock); cell.setBlock((TLRPC.TL_pageBlockVideo) pageBlock, true, true); break; } } } @Override public int getItemCount() { if (currentBlock == null) { return 0; } return currentBlock.items.size(); } @Override public int getItemViewType(int position) { TLRPC.PageBlock block = currentBlock.items.get(currentBlock.items.size() - position - 1); if (block instanceof TLRPC.TL_pageBlockPhoto) { return 0; } else { return 1; } } }); addView(innerListView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); setWillNotDraw(false); } public void setBlock(TLRPC.TL_pageBlockCollage block) { if (currentBlock != block) { currentBlock = block; group.calculate(); } innerAdapter.notifyDataSetChanged(); innerListView.setGlowColor(Theme.getColor(Theme.key_windowBackgroundWhite)); requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, captionLayout, textX, textY) || checkLayoutForLinks(parentAdapter, event, this, creditLayout, textX, textY + creditOffset) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { inLayout = true; int width = MeasureSpec.getSize(widthMeasureSpec); int height; if (currentBlock != null) { int listWidth = width; int textWidth; if (currentBlock.level > 0) { textX = listX = AndroidUtilities.dp(14 * currentBlock.level) + AndroidUtilities.dp(18); listWidth -= listX + AndroidUtilities.dp(18); textWidth = listWidth; } else { listX = 0; textX = AndroidUtilities.dp(18); textWidth = width - AndroidUtilities.dp(36); } innerListView.measure(MeasureSpec.makeMeasureSpec(listWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); height = innerListView.getMeasuredHeight(); textY = height + AndroidUtilities.dp(8); captionLayout = createLayoutForText(this, null, currentBlock.caption.text, textWidth, textY, currentBlock, parentAdapter); if (captionLayout != null) { creditOffset = AndroidUtilities.dp(4) + captionLayout.getHeight(); height += creditOffset + AndroidUtilities.dp(4); captionLayout.x = textX; captionLayout.y = textY; } else { creditOffset = 0; } creditLayout = createLayoutForText(this, null, currentBlock.caption.credit, textWidth, textY + creditOffset, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (creditLayout != null) { height += AndroidUtilities.dp(4) + creditLayout.getHeight(); creditLayout.x = textX; creditLayout.y = textY + creditOffset; } height += AndroidUtilities.dp(16); if (currentBlock.level > 0 && !currentBlock.bottom) { height += AndroidUtilities.dp(8); } } else { height = 1; } setMeasuredDimension(width, height); inLayout = false; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { innerListView.layout(listX, AndroidUtilities.dp(8), listX + innerListView.getMeasuredWidth(), innerListView.getMeasuredHeight() + AndroidUtilities.dp(8)); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } int count = 0; if (captionLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this, count++); captionLayout.draw(canvas); canvas.restore(); } if (creditLayout != null) { canvas.save(); canvas.translate(textX, textY + creditOffset); drawTextSelection(canvas, this, count); creditLayout.draw(canvas); canvas.restore(); } if (currentBlock.level > 0) { canvas.drawRect(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(20), getMeasuredHeight() - (currentBlock.bottom ? AndroidUtilities.dp(6) : 0), quoteLinePaint); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (captionLayout != null) { blocks.add(captionLayout); } if (creditLayout != null) { blocks.add(creditLayout); } } } private static Paint dotsPaint; private class BlockSlideshowCell extends FrameLayout implements TextSelectionHelper.ArticleSelectableView { private ViewPager innerListView; private PagerAdapter innerAdapter; private View dotsContainer; private TLRPC.TL_pageBlockSlideshow currentBlock; private DrawingText captionLayout; private DrawingText creditLayout; private int textX = AndroidUtilities.dp(18); private int textY; private int creditOffset; private float pageOffset; private int currentPage; private WebpageAdapter parentAdapter; public BlockSlideshowCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; if (dotsPaint == null) { dotsPaint = new Paint(Paint.ANTI_ALIAS_FLAG); dotsPaint.setColor(0xffffffff); } innerListView = new ViewPager(context) { @Override public boolean onTouchEvent(MotionEvent ev) { return super.onTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { windowView.requestDisallowInterceptTouchEvent(true); cancelCheckLongPress(); return super.onInterceptTouchEvent(ev); } }; innerListView.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { float width = innerListView.getMeasuredWidth(); if (width == 0) { return; } pageOffset = (position * width + positionOffsetPixels - currentPage * width) / width; dotsContainer.invalidate(); } @Override public void onPageSelected(int position) { currentPage = position; dotsContainer.invalidate(); } @Override public void onPageScrollStateChanged(int state) { } }); innerListView.setAdapter(innerAdapter = new PagerAdapter() { class ObjectContainer { private TLRPC.PageBlock block; private View view; } @Override public int getCount() { if (currentBlock == null) { return 0; } return currentBlock.items.size(); } @Override public boolean isViewFromObject(View view, Object object) { return ((ObjectContainer) object).view == view; } @Override public int getItemPosition(Object object) { ObjectContainer objectContainer = (ObjectContainer) object; if (currentBlock.items.contains(objectContainer.block)) { return POSITION_UNCHANGED; } return POSITION_NONE; } @Override public Object instantiateItem(ViewGroup container, int position) { View view; TLRPC.PageBlock block = currentBlock.items.get(position); if (block instanceof TLRPC.TL_pageBlockPhoto) { view = new BlockPhotoCell(getContext(), parentAdapter, 1); ((BlockPhotoCell) view).setBlock((TLRPC.TL_pageBlockPhoto) block, true, true); } else { view = new BlockVideoCell(getContext(), parentAdapter, 1); ((BlockVideoCell) view).setBlock((TLRPC.TL_pageBlockVideo) block, true, true); } container.addView(view); ObjectContainer objectContainer = new ObjectContainer(); objectContainer.view = view; objectContainer.block = block; return objectContainer; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(((ObjectContainer) object).view); } @Override public void unregisterDataSetObserver(DataSetObserver observer) { if (observer != null) { super.unregisterDataSetObserver(observer); } } }); AndroidUtilities.setViewPagerEdgeEffectColor(innerListView, Theme.getColor(Theme.key_windowBackgroundWhite)); addView(innerListView); dotsContainer = new View(context) { @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } int count = innerAdapter.getCount(); int totalWidth = count * AndroidUtilities.dp(7) + (count - 1) * AndroidUtilities.dp(6) + AndroidUtilities.dp(4); int xOffset; if (totalWidth < getMeasuredWidth()) { xOffset = (getMeasuredWidth() - totalWidth) / 2; } else { xOffset = AndroidUtilities.dp(4); int size = AndroidUtilities.dp(13); int halfCount = (getMeasuredWidth() - AndroidUtilities.dp(8)) / 2 / size; if (currentPage == count - halfCount - 1 && pageOffset < 0) { xOffset -= (int) (pageOffset * size) + (count - halfCount * 2 - 1) * size; } else if (currentPage >= count - halfCount - 1) { xOffset -= (count - halfCount * 2 - 1) * size; } else if (currentPage > halfCount) { xOffset -= (int) (pageOffset * size) + (currentPage - halfCount) * size; } else if (currentPage == halfCount && pageOffset > 0) { xOffset -= (int) (pageOffset * size); } } for (int a = 0; a < currentBlock.items.size(); a++) { int cx = xOffset + AndroidUtilities.dp(4) + AndroidUtilities.dp(13) * a; Drawable drawable = currentPage == a ? slideDotBigDrawable : slideDotDrawable; drawable.setBounds(cx - AndroidUtilities.dp(5), 0, cx + AndroidUtilities.dp(5), AndroidUtilities.dp(10)); drawable.draw(canvas); } } }; addView(dotsContainer); setWillNotDraw(false); } public void setBlock(TLRPC.TL_pageBlockSlideshow block) { currentBlock = block; innerAdapter.notifyDataSetChanged(); innerListView.setCurrentItem(0, false); innerListView.forceLayout(); requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, captionLayout, textX, textY) || checkLayoutForLinks(parentAdapter, event, this, creditLayout, textX, textY + creditOffset) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height; if (currentBlock != null) { height = AndroidUtilities.dp(310); innerListView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)); int count = currentBlock.items.size(); dotsContainer.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(10), MeasureSpec.EXACTLY)); int textWidth = width - AndroidUtilities.dp(36); textY = height + AndroidUtilities.dp(16); captionLayout = createLayoutForText(this, null, currentBlock.caption.text, textWidth, textY, currentBlock, parentAdapter); if (captionLayout != null) { creditOffset = AndroidUtilities.dp(4) + captionLayout.getHeight(); height += creditOffset + AndroidUtilities.dp(4); captionLayout.x = textX; captionLayout.y = textY; } else { creditOffset = 0; } creditLayout = createLayoutForText(this, null, currentBlock.caption.credit, textWidth, textY + creditOffset, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (creditLayout != null) { height += AndroidUtilities.dp(4) + creditLayout.getHeight(); creditLayout.x = textX; creditLayout.y = textY + creditOffset; } height += AndroidUtilities.dp(16); } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { innerListView.layout(0, AndroidUtilities.dp(8), innerListView.getMeasuredWidth(), AndroidUtilities.dp(8) + innerListView.getMeasuredHeight()); int y = innerListView.getBottom() - AndroidUtilities.dp(7 + 16); dotsContainer.layout(0, y, dotsContainer.getMeasuredWidth(), y + dotsContainer.getMeasuredHeight()); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } int count = 0; if (captionLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this, count++); captionLayout.draw(canvas); canvas.restore(); } if (creditLayout != null) { canvas.save(); canvas.translate(textX, textY + creditOffset); drawTextSelection(canvas, this, count); creditLayout.draw(canvas); canvas.restore(); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (captionLayout != null) { blocks.add(captionLayout); } if (creditLayout != null) { blocks.add(creditLayout); } } } private class BlockListItemCell extends ViewGroup implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private RecyclerView.ViewHolder blockLayout; private int textX; private int textY; private int numOffsetY; private int blockX; private int blockY; private boolean verticalAlign; private int currentBlockType; private TL_pageBlockListItem currentBlock; private boolean drawDot; private WebpageAdapter parentAdapter; public BlockListItemCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; setWillNotDraw(false); } public void setBlock(TL_pageBlockListItem block) { if (currentBlock != block) { currentBlock = block; if (blockLayout != null) { removeView(blockLayout.itemView); blockLayout = null; } if (currentBlock.blockItem != null) { currentBlockType = parentAdapter.getTypeForBlock(currentBlock.blockItem); blockLayout = parentAdapter.onCreateViewHolder(this, currentBlockType); addView(blockLayout.itemView); } } if (currentBlock.blockItem != null) { parentAdapter.bindBlockToHolder(currentBlockType, blockLayout, currentBlock.blockItem, 0, 0); } requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { if (checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY)) { return true; } return super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { textLayout = null; textY = currentBlock.index == 0 && currentBlock.parent.level == 0 ? AndroidUtilities.dp(10) : 0; numOffsetY = 0; if (currentBlock.parent.lastMaxNumCalcWidth != width || currentBlock.parent.lastFontSize != SharedConfig.ivFontSize) { currentBlock.parent.lastMaxNumCalcWidth = width; currentBlock.parent.lastFontSize = SharedConfig.ivFontSize; currentBlock.parent.maxNumWidth = 0; for (int a = 0, size = currentBlock.parent.items.size(); a < size; a++) { TL_pageBlockListItem item = currentBlock.parent.items.get(a); if (item.num == null) { continue; } item.numLayout = createLayoutForText(this, item.num, null, width - AndroidUtilities.dp(36 + 18), textY, currentBlock, parentAdapter); currentBlock.parent.maxNumWidth = Math.max(currentBlock.parent.maxNumWidth, (int) Math.ceil(item.numLayout.getLineWidth(0))); } currentBlock.parent.maxNumWidth = Math.max(currentBlock.parent.maxNumWidth, (int) Math.ceil(listTextNumPaint.measureText("00."))); } drawDot = !currentBlock.parent.pageBlockList.ordered; if (parentAdapter.isRtl) { textX = AndroidUtilities.dp(18); } else { textX = AndroidUtilities.dp(18 + 6) + currentBlock.parent.maxNumWidth + currentBlock.parent.level * AndroidUtilities.dp(12); } int maxWidth = width - AndroidUtilities.dp(18) - textX; if (parentAdapter.isRtl) { maxWidth -= AndroidUtilities.dp(6) + currentBlock.parent.maxNumWidth + currentBlock.parent.level * AndroidUtilities.dp(12); } if (currentBlock.textItem != null) { textLayout = createLayoutForText(this, null, currentBlock.textItem, maxWidth, textY, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (textLayout != null && textLayout.getLineCount() > 0) { if (currentBlock.numLayout != null && currentBlock.numLayout.getLineCount() > 0) { int ascent = textLayout.getLineAscent(0); numOffsetY = (currentBlock.numLayout.getLineAscent(0) + AndroidUtilities.dp(2.5f)) - ascent; } height += textLayout.getHeight() + AndroidUtilities.dp(8); } } else if (currentBlock.blockItem != null) { blockX = textX; blockY = textY; if (blockLayout != null) { if (blockLayout.itemView instanceof BlockParagraphCell) { blockY -= AndroidUtilities.dp(8); if (!parentAdapter.isRtl) { blockX -= AndroidUtilities.dp(18); } maxWidth += AndroidUtilities.dp(18); height -= AndroidUtilities.dp(8); } else if (blockLayout.itemView instanceof BlockHeaderCell || blockLayout.itemView instanceof BlockSubheaderCell || blockLayout.itemView instanceof BlockTitleCell || blockLayout.itemView instanceof BlockSubtitleCell) { if (!parentAdapter.isRtl) { blockX -= AndroidUtilities.dp(18); } maxWidth += AndroidUtilities.dp(18); } else if (isListItemBlock(currentBlock.blockItem)) { blockX = 0; blockY = 0; textY = 0; if (currentBlock.index == 0 && currentBlock.parent.level == 0) { height -= AndroidUtilities.dp(10); } maxWidth = width; height -= AndroidUtilities.dp(8); } else if (blockLayout.itemView instanceof BlockTableCell) { blockX -= AndroidUtilities.dp(18); maxWidth += AndroidUtilities.dp(36); } blockLayout.itemView.measure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); if (blockLayout.itemView instanceof BlockParagraphCell && currentBlock.numLayout != null && currentBlock.numLayout.getLineCount() > 0) { BlockParagraphCell paragraphCell = (BlockParagraphCell) blockLayout.itemView; if (paragraphCell.textLayout != null && paragraphCell.textLayout.getLineCount() > 0) { int ascent = paragraphCell.textLayout.getLineAscent(0); numOffsetY = (currentBlock.numLayout.getLineAscent(0) + AndroidUtilities.dp(2.5f)) - ascent; } } if (currentBlock.blockItem instanceof TLRPC.TL_pageBlockDetails) { verticalAlign = true; blockY = 0; if (currentBlock.index == 0 && currentBlock.parent.level == 0) { height -= AndroidUtilities.dp(10); } height -= AndroidUtilities.dp(8); } else if (blockLayout.itemView instanceof BlockOrderedListItemCell) { verticalAlign = ((BlockOrderedListItemCell) blockLayout.itemView).verticalAlign; } else if (blockLayout.itemView instanceof BlockListItemCell) { verticalAlign = ((BlockListItemCell) blockLayout.itemView).verticalAlign; } if (verticalAlign && currentBlock.numLayout != null) { textY = (blockLayout.itemView.getMeasuredHeight() - currentBlock.numLayout.getHeight()) / 2 - AndroidUtilities.dp(4); drawDot = false; } height += blockLayout.itemView.getMeasuredHeight(); } height += AndroidUtilities.dp(8); } if (currentBlock.parent.items.get(currentBlock.parent.items.size() - 1) == currentBlock) { height += AndroidUtilities.dp(8); } if (currentBlock.index == 0 && currentBlock.parent.level == 0) { height += AndroidUtilities.dp(10); } if (textLayout != null) { textLayout.x = textX; textLayout.y = textY; } if (blockLayout != null && blockLayout.itemView instanceof TextSelectionHelper.ArticleSelectableView) { textSelectionHelper.arrayList.clear(); ((TextSelectionHelper.ArticleSelectableView) blockLayout.itemView).fillTextLayoutBlocks(textSelectionHelper.arrayList); for (TextSelectionHelper.TextLayoutBlock block : textSelectionHelper.arrayList) { if (block instanceof DrawingText) { ((DrawingText) block).x += blockX; ((DrawingText) block).y += blockY; } } } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (blockLayout != null) { blockLayout.itemView.layout(blockX, blockY, blockX + blockLayout.itemView.getMeasuredWidth(), blockY + blockLayout.itemView.getMeasuredHeight()); } } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } int width = getMeasuredWidth(); if (currentBlock.numLayout != null) { canvas.save(); if (parentAdapter.isRtl) { canvas.translate(width - AndroidUtilities.dp(15) - currentBlock.parent.maxNumWidth - currentBlock.parent.level * AndroidUtilities.dp(12), textY + numOffsetY - (drawDot ? AndroidUtilities.dp(1) : 0)); } else { canvas.translate(AndroidUtilities.dp(15) + currentBlock.parent.maxNumWidth - (int) Math.ceil(currentBlock.numLayout.getLineWidth(0)) + currentBlock.parent.level * AndroidUtilities.dp(12), textY + numOffsetY - (drawDot ? AndroidUtilities.dp(1) : 0)); } currentBlock.numLayout.draw(canvas); canvas.restore(); } if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas,this); textLayout.draw(canvas); canvas.restore(); } } @Override public void invalidate() { super.invalidate(); if (blockLayout != null) { blockLayout.itemView.invalidate(); } } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setEnabled(true); if (textLayout == null) return; info.setText(textLayout.getText()); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (blockLayout != null && blockLayout.itemView instanceof TextSelectionHelper.ArticleSelectableView) { ((TextSelectionHelper.ArticleSelectableView) blockLayout.itemView).fillTextLayoutBlocks(blocks); } if (textLayout != null) { blocks.add(textLayout); } } } private class BlockOrderedListItemCell extends ViewGroup implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private RecyclerView.ViewHolder blockLayout; private int textX; private int textY; private int numOffsetY; private int blockX; private int blockY; private int currentBlockType; private boolean verticalAlign; private TL_pageBlockOrderedListItem currentBlock; private WebpageAdapter parentAdapter; public BlockOrderedListItemCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; setWillNotDraw(false); } public void setBlock(TL_pageBlockOrderedListItem block) { if (currentBlock != block) { currentBlock = block; if (blockLayout != null) { removeView(blockLayout.itemView); blockLayout = null; } if (currentBlock.blockItem != null) { currentBlockType = parentAdapter.getTypeForBlock(currentBlock.blockItem); blockLayout = parentAdapter.onCreateViewHolder(this, currentBlockType); addView(blockLayout.itemView); } } if (currentBlock.blockItem != null) { parentAdapter.bindBlockToHolder(currentBlockType, blockLayout, currentBlock.blockItem, 0, 0); } requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { if (checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY)) { return true; } return super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { textLayout = null; textY = currentBlock.index == 0 && currentBlock.parent.level == 0 ? AndroidUtilities.dp(10) : 0; numOffsetY = 0; if (currentBlock.parent.lastMaxNumCalcWidth != width || currentBlock.parent.lastFontSize != SharedConfig.ivFontSize) { currentBlock.parent.lastMaxNumCalcWidth = width; currentBlock.parent.lastFontSize = SharedConfig.ivFontSize; currentBlock.parent.maxNumWidth = 0; for (int a = 0, size = currentBlock.parent.items.size(); a < size; a++) { TL_pageBlockOrderedListItem item = currentBlock.parent.items.get(a); if (item.num == null) { continue; } item.numLayout = createLayoutForText(this, item.num, null, width - AndroidUtilities.dp(36 + 18), textY, currentBlock, parentAdapter); currentBlock.parent.maxNumWidth = Math.max(currentBlock.parent.maxNumWidth, (int) Math.ceil(item.numLayout.getLineWidth(0))); } currentBlock.parent.maxNumWidth = Math.max(currentBlock.parent.maxNumWidth, (int) Math.ceil(listTextNumPaint.measureText("00."))); } if (parentAdapter.isRtl) { textX = AndroidUtilities.dp(18); } else { textX = AndroidUtilities.dp(18 + 6) + currentBlock.parent.maxNumWidth + currentBlock.parent.level * AndroidUtilities.dp(20); } verticalAlign = false; int maxWidth = width - AndroidUtilities.dp(18) - textX; if (parentAdapter.isRtl) { maxWidth -= AndroidUtilities.dp(6) + currentBlock.parent.maxNumWidth + currentBlock.parent.level * AndroidUtilities.dp(20); } if (currentBlock.textItem != null) { textLayout = createLayoutForText(this, null, currentBlock.textItem, maxWidth, textY, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (textLayout != null && textLayout.getLineCount() > 0) { if (currentBlock.numLayout != null && currentBlock.numLayout.getLineCount() > 0) { int ascent = textLayout.getLineAscent(0); numOffsetY = currentBlock.numLayout.getLineAscent(0) - ascent; } height += textLayout.getHeight() + AndroidUtilities.dp(8); } } else if (currentBlock.blockItem != null) { blockX = textX; blockY = textY; if (blockLayout != null) { if (blockLayout.itemView instanceof BlockParagraphCell) { blockY -= AndroidUtilities.dp(8); if (!parentAdapter.isRtl) { blockX -= AndroidUtilities.dp(18); } maxWidth += AndroidUtilities.dp(18); height -= AndroidUtilities.dp(8); } else if (blockLayout.itemView instanceof BlockHeaderCell || blockLayout.itemView instanceof BlockSubheaderCell || blockLayout.itemView instanceof BlockTitleCell || blockLayout.itemView instanceof BlockSubtitleCell) { if (!parentAdapter.isRtl) { blockX -= AndroidUtilities.dp(18); } maxWidth += AndroidUtilities.dp(18); } else if (isListItemBlock(currentBlock.blockItem)) { blockX = 0; blockY = 0; textY = 0; maxWidth = width; height -= AndroidUtilities.dp(8); } else if (blockLayout.itemView instanceof BlockTableCell) { blockX -= AndroidUtilities.dp(18); maxWidth += AndroidUtilities.dp(36); } blockLayout.itemView.measure(MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); if (blockLayout.itemView instanceof BlockParagraphCell && currentBlock.numLayout != null && currentBlock.numLayout.getLineCount() > 0) { BlockParagraphCell paragraphCell = (BlockParagraphCell) blockLayout.itemView; if (paragraphCell.textLayout != null && paragraphCell.textLayout.getLineCount() > 0) { int ascent = paragraphCell.textLayout.getLineAscent(0); numOffsetY = currentBlock.numLayout.getLineAscent(0) - ascent; } } if (currentBlock.blockItem instanceof TLRPC.TL_pageBlockDetails) { verticalAlign = true; blockY = 0; height -= AndroidUtilities.dp(8); } else if (blockLayout.itemView instanceof BlockOrderedListItemCell) { verticalAlign = ((BlockOrderedListItemCell) blockLayout.itemView).verticalAlign; } else if (blockLayout.itemView instanceof BlockListItemCell) { verticalAlign = ((BlockListItemCell) blockLayout.itemView).verticalAlign; } if (verticalAlign && currentBlock.numLayout != null) { textY = (blockLayout.itemView.getMeasuredHeight() - currentBlock.numLayout.getHeight()) / 2; } height += blockLayout.itemView.getMeasuredHeight(); } height += AndroidUtilities.dp(8); } if (currentBlock.parent.items.get(currentBlock.parent.items.size() - 1) == currentBlock) { height += AndroidUtilities.dp(8); } if (currentBlock.index == 0 && currentBlock.parent.level == 0) { height += AndroidUtilities.dp(10); } if (textLayout != null) { textLayout.x = textX; textLayout.y = textY; if (currentBlock.numLayout != null) { textLayout.prefix = currentBlock.numLayout.textLayout.getText(); } } if (blockLayout != null && blockLayout.itemView instanceof TextSelectionHelper.ArticleSelectableView) { textSelectionHelper.arrayList.clear(); ((TextSelectionHelper.ArticleSelectableView) blockLayout.itemView).fillTextLayoutBlocks(textSelectionHelper.arrayList); for (TextSelectionHelper.TextLayoutBlock block : textSelectionHelper.arrayList) { if (block instanceof DrawingText) { ((DrawingText)block).x += blockX; ((DrawingText)block).y += blockY; } } } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (blockLayout != null) { blockLayout.itemView.layout(blockX, blockY, blockX + blockLayout.itemView.getMeasuredWidth(), blockY + blockLayout.itemView.getMeasuredHeight()); } } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } int width = getMeasuredWidth(); if (currentBlock.numLayout != null) { canvas.save(); if (parentAdapter.isRtl) { canvas.translate(width - AndroidUtilities.dp(18) - currentBlock.parent.maxNumWidth - currentBlock.parent.level * AndroidUtilities.dp(20), textY + numOffsetY); } else { canvas.translate(AndroidUtilities.dp(18) + currentBlock.parent.maxNumWidth - (int) Math.ceil(currentBlock.numLayout.getLineWidth(0)) + currentBlock.parent.level * AndroidUtilities.dp(20), textY + numOffsetY); } currentBlock.numLayout.draw(canvas); canvas.restore(); } if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this); textLayout.draw(canvas); canvas.restore(); } } @Override public void invalidate() { super.invalidate(); if (blockLayout != null) { blockLayout.itemView.invalidate(); } } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setEnabled(true); if (textLayout == null) return; info.setText(textLayout.getText()); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (blockLayout != null && blockLayout.itemView instanceof TextSelectionHelper.ArticleSelectableView) { ((TextSelectionHelper.ArticleSelectableView) blockLayout.itemView).fillTextLayoutBlocks(blocks); } if (textLayout != null) { blocks.add(textLayout); } } } private class BlockDetailsCell extends View implements Drawable.Callback, TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private int textX = AndroidUtilities.dp(44 + 6); private int textY = AndroidUtilities.dp(11) + 1; private AnimatedArrowDrawable arrow; private TLRPC.TL_pageBlockDetails currentBlock; private WebpageAdapter parentAdapter; public BlockDetailsCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; arrow = new AnimatedArrowDrawable(getGrayTextColor(), true); } @Override public void invalidateDrawable(Drawable drawable) { invalidate(); } @Override public void scheduleDrawable(Drawable drawable, Runnable runnable, long l) { } @Override public void unscheduleDrawable(Drawable drawable, Runnable runnable) { } public void setBlock(TLRPC.TL_pageBlockDetails block) { currentBlock = block; arrow.setAnimationProgress(block.open ? 0.0f : 1.0f); arrow.setCallback(this); requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int h = AndroidUtilities.dp(39); if (currentBlock != null) { textLayout = createLayoutForText(this, null, currentBlock.title, width - AndroidUtilities.dp(36 + 16), 0, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (textLayout != null) { h = Math.max(h, AndroidUtilities.dp(21) + textLayout.getHeight()); textY = (textLayout.getHeight() + AndroidUtilities.dp(21) - textLayout.getHeight()) / 2; textLayout.x = textX; textLayout.y = textY; } } setMeasuredDimension(width, h + 1); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } canvas.save(); canvas.translate(AndroidUtilities.dp(18), (getMeasuredHeight() - AndroidUtilities.dp(13) - 1) / 2); arrow.draw(canvas); canvas.restore(); if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas,this); textLayout.draw(canvas); canvas.restore(); } int y = getMeasuredHeight() - 1; canvas.drawLine(0, y, getMeasuredWidth(), y, dividerPaint); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } } private static class BlockDetailsBottomCell extends View { private RectF rect = new RectF(); public BlockDetailsBottomCell(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), 1 + AndroidUtilities.dp(4)); } @Override protected void onDraw(Canvas canvas) { canvas.drawLine(0, 0, getMeasuredWidth(), 0, dividerPaint); } } private static class BlockRelatedArticlesShadowCell extends View { private CombinedDrawable shadowDrawable; public BlockRelatedArticlesShadowCell(Context context) { super(context); Drawable drawable = Theme.getThemedDrawable(context, R.drawable.greydivider_bottom, 0xff000000); shadowDrawable = new CombinedDrawable(new ColorDrawable(Theme.getColor(Theme.key_windowBackgroundGray)), drawable); shadowDrawable.setFullsize(true); setBackgroundDrawable(shadowDrawable); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.dp(12)); Theme.setCombinedDrawableColor(shadowDrawable, Theme.getColor(Theme.key_windowBackgroundGray), false); } } private class BlockRelatedArticlesHeaderCell extends View implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private int textX = AndroidUtilities.dp(18); private int textY; private TLRPC.TL_pageBlockRelatedArticles currentBlock; private WebpageAdapter parentAdapter; public BlockRelatedArticlesHeaderCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; } public void setBlock(TLRPC.TL_pageBlockRelatedArticles block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || super.onTouchEvent(event); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); if (currentBlock != null) { textLayout = createLayoutForText(this, null, currentBlock.title, width - AndroidUtilities.dp(36 + 16), 0, currentBlock, Layout.Alignment.ALIGN_NORMAL, 1, parentAdapter); if (textLayout != null) { textY = AndroidUtilities.dp(6) + (AndroidUtilities.dp(32) - textLayout.getHeight()) / 2; } } if (textLayout != null) { setMeasuredDimension(width, AndroidUtilities.dp(38)); textLayout.x = textX; textLayout.y = textY; } else { setMeasuredDimension(width, 1); } } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this); textLayout.draw(canvas); canvas.restore(); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } } private class BlockRelatedArticlesCell extends View implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private DrawingText textLayout2; private boolean divider; private boolean drawImage; private ImageReceiver imageView; private TL_pageBlockRelatedArticlesChild currentBlock; private int textX = AndroidUtilities.dp(18); private int textY = AndroidUtilities.dp(10); private int textOffset; private WebpageAdapter parentAdapter; public BlockRelatedArticlesCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; imageView = new ImageReceiver(this); imageView.setRoundRadius(AndroidUtilities.dp(6)); } public void setBlock(TL_pageBlockRelatedArticlesChild block) { currentBlock = block; requestLayout(); } @SuppressLint({"DrawAllocation", "NewApi"}) @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); divider = currentBlock.num != currentBlock.parent.articles.size() - 1; TLRPC.TL_pageRelatedArticle item = currentBlock.parent.articles.get(currentBlock.num); int additionalHeight = AndroidUtilities.dp(SharedConfig.ivFontSize - 16); TLRPC.Photo photo = item.photo_id != 0 ? parentAdapter.getPhotoWithId(item.photo_id) : null; if (photo != null) { drawImage = true; TLRPC.PhotoSize image = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.getPhotoSize()); TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, 80, true); if (image == thumb) { thumb = null; } imageView.setImage(ImageLocation.getForPhoto(image, photo), "64_64", ImageLocation.getForPhoto(thumb, photo), "64_64_b", image.size, null, parentAdapter.currentPage, 1); } else { drawImage = false; } int layoutHeight = AndroidUtilities.dp(16 + 44); int availableWidth = width - AndroidUtilities.dp(18 + 18); if (drawImage) { int imageWidth = AndroidUtilities.dp(44); imageView.setImageCoords(width - imageWidth - AndroidUtilities.dp(8), AndroidUtilities.dp(8), imageWidth, imageWidth); availableWidth -= imageView.getImageWidth() + AndroidUtilities.dp(6); } int height = AndroidUtilities.dp(18); boolean isTitleRtl = false; if (item.title != null) { textLayout = createLayoutForText(this, item.title, null, availableWidth, textY, currentBlock, Layout.Alignment.ALIGN_NORMAL, 3, parentAdapter); } int lineCount = 4; if (textLayout != null) { int count = textLayout.getLineCount(); lineCount -= count; textOffset = textLayout.getHeight() + AndroidUtilities.dp(6) + additionalHeight; height += textLayout.getHeight(); for (int a = 0; a < count; a++) { if (textLayout.getLineLeft(a) != 0) { isTitleRtl = true; break; } } textLayout.x = textX; textLayout.y = textY; } else { textOffset = 0; } String description; if (item.published_date != 0 && !TextUtils.isEmpty(item.author)) { description = LocaleController.formatString("ArticleDateByAuthor", R.string.ArticleDateByAuthor, LocaleController.getInstance().chatFullDate.format((long) item.published_date * 1000), item.author); } else if (!TextUtils.isEmpty(item.author)) { description = LocaleController.formatString("ArticleByAuthor", R.string.ArticleByAuthor, item.author); } else if (item.published_date != 0) { description = LocaleController.getInstance().chatFullDate.format((long) item.published_date * 1000); } else if (!TextUtils.isEmpty(item.description)) { description = item.description; } else { description = item.url; } textLayout2 = createLayoutForText(this, description, null, availableWidth, textY + textOffset, currentBlock, parentAdapter.isRtl || isTitleRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, lineCount, parentAdapter); if (textLayout2 != null) { height += textLayout2.getHeight(); if (textLayout != null) { height += AndroidUtilities.dp(6) + additionalHeight; } textLayout2.x = textX; textLayout2.y = textY + textOffset; } layoutHeight = Math.max(layoutHeight, height); setMeasuredDimension(width, layoutHeight + (divider ? 1 : 0)); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (drawImage) { imageView.draw(canvas); } int count = 0; canvas.save(); canvas.translate(textX, AndroidUtilities.dp(10)); if (textLayout != null) { drawTextSelection(canvas, this, count++); textLayout.draw(canvas); } if (textLayout2 != null) { canvas.translate(0, textOffset); drawTextSelection(canvas, this, count); textLayout2.draw(canvas); } canvas.restore(); if (divider) { canvas.drawLine(parentAdapter.isRtl ? 0 : AndroidUtilities.dp(17), getMeasuredHeight() - 1, getMeasuredWidth() - (parentAdapter.isRtl ? AndroidUtilities.dp(17) : 0), getMeasuredHeight() - 1, dividerPaint); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } if (textLayout2 != null) { blocks.add(textLayout2); } } } private class BlockHeaderCell extends View implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private int textX = AndroidUtilities.dp(18); private int textY = AndroidUtilities.dp(8); private TLRPC.TL_pageBlockHeader currentBlock; private WebpageAdapter parentAdapter; public BlockHeaderCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; } public void setBlock(TLRPC.TL_pageBlockHeader block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { textLayout = createLayoutForText(this, null, currentBlock.text, width - AndroidUtilities.dp(36), textY, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (textLayout != null) { height += AndroidUtilities.dp(8 + 8) + textLayout.getHeight(); textLayout.x = textX; textLayout.y = textY; } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this); textLayout.draw(canvas); canvas.restore(); } } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setEnabled(true); if (textLayout == null) return; info.setText(textLayout.getText() + ", " + LocaleController.getString("AccDescrIVHeading", R.string.AccDescrIVHeading)); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } } private static class BlockDividerCell extends View { private RectF rect = new RectF(); public BlockDividerCell(Context context) { super(context); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), AndroidUtilities.dp(2 + 16)); } @Override protected void onDraw(Canvas canvas) { int width = getMeasuredWidth() / 3; rect.set(width, AndroidUtilities.dp(8), width * 2, AndroidUtilities.dp(10)); canvas.drawRoundRect(rect, AndroidUtilities.dp(1), AndroidUtilities.dp(1), dividerPaint); } } private class BlockSubtitleCell extends View implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private int textX = AndroidUtilities.dp(18); private int textY = AndroidUtilities.dp(8); private TLRPC.TL_pageBlockSubtitle currentBlock; private WebpageAdapter parentAdapter; public BlockSubtitleCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; } public void setBlock(TLRPC.TL_pageBlockSubtitle block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { textLayout = createLayoutForText(this, null, currentBlock.text, width - AndroidUtilities.dp(36), textY, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (textLayout != null) { height += AndroidUtilities.dp(8 + 8) + textLayout.getHeight(); textLayout.x = textX; textLayout.y = textY; } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this); textLayout.draw(canvas); canvas.restore(); } } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setEnabled(true); if (textLayout == null) { return; } info.setText(textLayout.getText() + ", " + LocaleController.getString("AccDescrIVHeading", R.string.AccDescrIVHeading)); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } } private class BlockPullquoteCell extends View implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private DrawingText textLayout2; private int textY2; private int textX = AndroidUtilities.dp(18); private int textY = AndroidUtilities.dp(8); private TLRPC.TL_pageBlockPullquote currentBlock; private WebpageAdapter parentAdapter; public BlockPullquoteCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; } public void setBlock(TLRPC.TL_pageBlockPullquote block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || checkLayoutForLinks(parentAdapter, event, this, textLayout2, textX, textY2) || super.onTouchEvent(event); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { textLayout = createLayoutForText(this, null, currentBlock.text, width - AndroidUtilities.dp(36), textY, currentBlock, parentAdapter); if (textLayout != null) { height += AndroidUtilities.dp(8) + textLayout.getHeight(); textLayout.x = textX; textLayout.y = textY; } textY2 = height + AndroidUtilities.dp(2); textLayout2 = createLayoutForText(this, null, currentBlock.caption, width - AndroidUtilities.dp(36), textY2, currentBlock, parentAdapter); if (textLayout2 != null) { height += AndroidUtilities.dp(8) + textLayout2.getHeight(); textLayout2.x = textX; textLayout2.y = textY2; } if (height != 0) { height += AndroidUtilities.dp(8); } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } int count = 0; if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this, count++); textLayout.draw(canvas); canvas.restore(); } if (textLayout2 != null) { canvas.save(); canvas.translate(textX, textY2); drawTextSelection(canvas, this, count); textLayout2.draw(canvas); canvas.restore(); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } if (textLayout2 != null) { blocks.add(textLayout2); } } } private class BlockBlockquoteCell extends View implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private DrawingText textLayout2; private int textY2; private int textX; private int textY = AndroidUtilities.dp(8); private TLRPC.TL_pageBlockBlockquote currentBlock; private WebpageAdapter parentAdapter; public BlockBlockquoteCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; } public void setBlock(TLRPC.TL_pageBlockBlockquote block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || checkLayoutForLinks(parentAdapter, event, this, textLayout2, textX, textY2) || super.onTouchEvent(event); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { int textWidth = width - AndroidUtilities.dp(36 + 14); if (currentBlock.level > 0) { textWidth -= AndroidUtilities.dp(14 * currentBlock.level); } textLayout = createLayoutForText(this, null, currentBlock.text, textWidth, textY, currentBlock, parentAdapter); if (textLayout != null) { height += AndroidUtilities.dp(8) + textLayout.getHeight(); } if (currentBlock.level > 0) { if (parentAdapter.isRtl) { textX = AndroidUtilities.dp(14 + currentBlock.level * 14); } else { textX = AndroidUtilities.dp(14 * currentBlock.level) + AndroidUtilities.dp(18 + 14); } } else { if (parentAdapter.isRtl) { textX = AndroidUtilities.dp(14); } else { textX = AndroidUtilities.dp(18 + 14); } } textY2 = height + AndroidUtilities.dp(8); textLayout2 = createLayoutForText(this, null, currentBlock.caption, textWidth, textY2, currentBlock, parentAdapter); if (textLayout2 != null) { height += AndroidUtilities.dp(8) + textLayout2.getHeight(); } if (height != 0) { height += AndroidUtilities.dp(8); } if (textLayout != null) { textLayout.x = textX; textLayout.y = textY; } if (textLayout2 != null) { textLayout2.x = textX; textLayout2.y = textY2; } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } int counter = 0; if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this, counter++); textLayout.draw(canvas); canvas.restore(); } if (textLayout2 != null) { canvas.save(); canvas.translate(textX, textY2); drawTextSelection(canvas, this, counter); textLayout2.draw(canvas); canvas.restore(); } if (parentAdapter.isRtl) { int x = getMeasuredWidth() - AndroidUtilities.dp(20); canvas.drawRect(x, AndroidUtilities.dp(6), x + AndroidUtilities.dp(2), getMeasuredHeight() - AndroidUtilities.dp(6), quoteLinePaint); } else { canvas.drawRect(AndroidUtilities.dp(18 + currentBlock.level * 14), AndroidUtilities.dp(6), AndroidUtilities.dp(20 + currentBlock.level * 14), getMeasuredHeight() - AndroidUtilities.dp(6), quoteLinePaint); } if (currentBlock.level > 0) { canvas.drawRect(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(20), getMeasuredHeight() - (currentBlock.bottom ? AndroidUtilities.dp(6) : 0), quoteLinePaint); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } if (textLayout2 != null) { blocks.add(textLayout2); } } } private class BlockPhotoCell extends FrameLayout implements DownloadController.FileDownloadProgressListener, TextSelectionHelper.ArticleSelectableView { private DrawingText captionLayout; private DrawingText creditLayout; private ImageReceiver imageView; private RadialProgress2 radialProgress; private BlockChannelCell channelCell; private int currentType; private boolean isFirst; private int textX; private int textY; private int creditOffset; private int buttonX; private int buttonY; private boolean photoPressed; private int buttonState; private int buttonPressed; private TLRPC.PhotoSize currentPhotoObject; private String currentFilter; private TLRPC.PhotoSize currentPhotoObjectThumb; private String currentThumbFilter; private TLRPC.Photo currentPhoto; private int TAG; private TLRPC.TL_pageBlockPhoto currentBlock; private TLRPC.PageBlock parentBlock; private MessageObject.GroupedMessagePosition groupPosition; private Drawable linkDrawable; boolean autoDownload; private WebpageAdapter parentAdapter; public BlockPhotoCell(Context context, WebpageAdapter adapter, int type) { super(context); parentAdapter = adapter; setWillNotDraw(false); imageView = new ImageReceiver(this); channelCell = new BlockChannelCell(context, parentAdapter, 1); radialProgress = new RadialProgress2(this); radialProgress.setProgressColor(0xffffffff); radialProgress.setColors(0x66000000, 0x7f000000, 0xffffffff, 0xffd9d9d9); TAG = DownloadController.getInstance(currentAccount).generateObserverTag(); addView(channelCell, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); currentType = type; } public void setBlock(TLRPC.TL_pageBlockPhoto block, boolean first, boolean last) { parentBlock = null; currentBlock = block; isFirst = first; channelCell.setVisibility(INVISIBLE); if (!TextUtils.isEmpty(currentBlock.url)) { linkDrawable = getResources().getDrawable(R.drawable.instant_link); } if (currentBlock != null) { TLRPC.Photo photo = parentAdapter.getPhotoWithId(currentBlock.photo_id); if (photo != null) { currentPhotoObject = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.getPhotoSize()); } else { currentPhotoObject = null; } } else { currentPhotoObject = null; } updateButtonState(false); requestLayout(); } public void setParentBlock(TLRPC.PageBlock block) { parentBlock = block; if (parentAdapter.channelBlock != null && parentBlock instanceof TLRPC.TL_pageBlockCover) { channelCell.setBlock(parentAdapter.channelBlock); channelCell.setVisibility(VISIBLE); } } public View getChannelCell() { return channelCell; } @Override public boolean onTouchEvent(MotionEvent event) { if (pinchToZoomHelper.checkPinchToZoom(event, this, imageView, null)) { return true; } float x = event.getX(); float y = event.getY(); if (channelCell.getVisibility() == VISIBLE && y > channelCell.getTranslationY() && y < channelCell.getTranslationY() + AndroidUtilities.dp(39)) { if (parentAdapter.channelBlock != null && event.getAction() == MotionEvent.ACTION_UP) { MessagesController.getInstance(currentAccount).openByUserName(parentAdapter.channelBlock.channel.username, parentFragment, 2); close(false, true); } return true; } if (event.getAction() == MotionEvent.ACTION_DOWN && imageView.isInsideImage(x, y)) { if (buttonState != -1 && x >= buttonX && x <= buttonX + AndroidUtilities.dp(48) && y >= buttonY && y <= buttonY + AndroidUtilities.dp(48) || buttonState == 0) { buttonPressed = 1; invalidate(); } else { photoPressed = true; } } else if (event.getAction() == MotionEvent.ACTION_UP) { if (photoPressed) { photoPressed = false; openPhoto(currentBlock, parentAdapter); } else if (buttonPressed == 1) { buttonPressed = 0; playSoundEffect(SoundEffectConstants.CLICK); didPressedButton(true); invalidate(); } } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { photoPressed = false; buttonPressed = 0; } return photoPressed || buttonPressed != 0 || checkLayoutForLinks(parentAdapter, event, this, captionLayout, textX, textY) || checkLayoutForLinks(parentAdapter, event, this, creditLayout, textX, textY + creditOffset) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentType == 1) { width = ((View) getParent()).getMeasuredWidth(); height = ((View) getParent()).getMeasuredHeight(); } else if (currentType == 2) { height = (int) Math.ceil(groupPosition.ph * Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f); } if (currentBlock != null) { currentPhoto = parentAdapter.getPhotoWithId(currentBlock.photo_id); int size = AndroidUtilities.dp(48); int photoWidth = width; int photoHeight = height; int photoX; int textWidth; if (currentType == 0 && currentBlock.level > 0) { textX = photoX = AndroidUtilities.dp(14 * currentBlock.level) + AndroidUtilities.dp(18); photoWidth -= photoX + AndroidUtilities.dp(18); textWidth = photoWidth; } else { photoX = 0; textX = AndroidUtilities.dp(18); textWidth = width - AndroidUtilities.dp(36); } if (currentPhoto != null && currentPhotoObject != null) { currentPhotoObjectThumb = FileLoader.getClosestPhotoSizeWithSize(currentPhoto.sizes, 40, true); if (currentPhotoObject == currentPhotoObjectThumb) { currentPhotoObjectThumb = null; } if (currentType == 0) { float scale; scale = photoWidth / (float) currentPhotoObject.w; height = (int) (scale * currentPhotoObject.h); if (parentBlock instanceof TLRPC.TL_pageBlockCover) { height = Math.min(height, photoWidth); } else { int maxHeight = (int) ((Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(56)) * 0.9f); if (height > maxHeight) { height = maxHeight; scale = height / (float) currentPhotoObject.h; photoWidth = (int) (scale * currentPhotoObject.w); photoX += (width - photoX - photoWidth) / 2; } } photoHeight = height; } else if (currentType == 2) { if ((groupPosition.flags & POSITION_FLAG_RIGHT) == 0) { photoWidth -= AndroidUtilities.dp(2); } if ((groupPosition.flags & POSITION_FLAG_BOTTOM) == 0) { photoHeight -= AndroidUtilities.dp(2); } if (groupPosition.leftSpanOffset != 0) { int offset = (int) Math.ceil(width * groupPosition.leftSpanOffset / 1000.0f); photoWidth -= offset; photoX += offset; } } imageView.setImageCoords(photoX, (isFirst || currentType == 1 || currentType == 2 || currentBlock.level > 0) ? 0 : AndroidUtilities.dp(8), photoWidth, photoHeight); if (currentType == 0) { currentFilter = null; } else { currentFilter = String.format(Locale.US, "%d_%d", photoWidth, photoHeight); } currentThumbFilter = "80_80_b"; autoDownload = (DownloadController.getInstance(currentAccount).getCurrentDownloadMask() & DownloadController.AUTODOWNLOAD_TYPE_PHOTO) != 0; File path = FileLoader.getPathToAttach(currentPhotoObject, true); if (autoDownload || path.exists()) { imageView.setStrippedLocation(null); imageView.setImage(ImageLocation.getForPhoto(currentPhotoObject, currentPhoto), currentFilter, ImageLocation.getForPhoto(currentPhotoObjectThumb, currentPhoto), currentThumbFilter, currentPhotoObject.size, null, parentAdapter.currentPage, 1); } else { imageView.setStrippedLocation(ImageLocation.getForPhoto(currentPhotoObject, currentPhoto)); imageView.setImage(null, currentFilter, ImageLocation.getForPhoto(currentPhotoObjectThumb, currentPhoto), currentThumbFilter, currentPhotoObject.size, null, parentAdapter.currentPage, 1); } buttonX = (int) (imageView.getImageX() + (imageView.getImageWidth() - size) / 2.0f); buttonY = (int) (imageView.getImageY() + (imageView.getImageHeight() - size) / 2.0f); radialProgress.setProgressRect(buttonX, buttonY, buttonX + size, buttonY + size); } textY = (int) (imageView.getImageY() + imageView.getImageHeight() + AndroidUtilities.dp(8)); if (currentType == 0) { captionLayout = createLayoutForText(this, null, currentBlock.caption.text, textWidth, textY, currentBlock, parentAdapter); if (captionLayout != null) { creditOffset = AndroidUtilities.dp(4) + captionLayout.getHeight(); height += creditOffset + AndroidUtilities.dp(4); } creditLayout = createLayoutForText(this, null, currentBlock.caption.credit, textWidth, textY + creditOffset, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, 0, parentAdapter); if (creditLayout != null) { height += AndroidUtilities.dp(4) + creditLayout.getHeight(); } } if (!isFirst && currentType == 0 && currentBlock.level <= 0) { height += AndroidUtilities.dp(8); } boolean nextIsChannel = parentBlock instanceof TLRPC.TL_pageBlockCover && parentAdapter.blocks != null && parentAdapter.blocks.size() > 1 && parentAdapter.blocks.get(1) instanceof TLRPC.TL_pageBlockChannel; if (currentType != 2 && !nextIsChannel) { height += AndroidUtilities.dp(8); } if (captionLayout != null) { captionLayout.x = textX; captionLayout.y = textY; } if (creditLayout != null) { creditLayout.x = textX; creditLayout.y = textY + creditOffset; } } else { height = 1; } channelCell.measure(widthMeasureSpec, heightMeasureSpec); channelCell.setTranslationY(imageView.getImageHeight() - AndroidUtilities.dp(39)); setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (!imageView.hasBitmapImage() || imageView.getCurrentAlpha() != 1.0f) { canvas.drawRect(imageView.getImageX(), imageView.getImageY(), imageView.getImageX2(), imageView.getImageY2(), photoBackgroundPaint); } if (!pinchToZoomHelper.isInOverlayModeFor(this)) { imageView.draw(canvas); if (imageView.getVisible()) { radialProgress.draw(canvas); } } if (!TextUtils.isEmpty(currentBlock.url)) { int x = getMeasuredWidth() - AndroidUtilities.dp(11 + 24); int y = (int) (imageView.getImageY() + AndroidUtilities.dp(11)); linkDrawable.setBounds(x, y, x + AndroidUtilities.dp(24), y + AndroidUtilities.dp(24)); linkDrawable.draw(canvas); } int count = 0; if (captionLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas,this, count++); captionLayout.draw(canvas); canvas.restore(); } if (creditLayout != null) { canvas.save(); canvas.translate(textX, textY + creditOffset); drawTextSelection(canvas,this, count); creditLayout.draw(canvas); canvas.restore(); } if (currentBlock.level > 0) { canvas.drawRect(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(20), getMeasuredHeight() - (currentBlock.bottom ? AndroidUtilities.dp(6) : 0), quoteLinePaint); } } private int getIconForCurrentState() { if (buttonState == 0) { return MediaActionDrawable.ICON_DOWNLOAD; } else if (buttonState == 1) { return MediaActionDrawable.ICON_CANCEL; } return MediaActionDrawable.ICON_NONE; } private void didPressedButton(boolean animated) { if (buttonState == 0) { radialProgress.setProgress(0, animated); imageView.setImage(ImageLocation.getForPhoto(currentPhotoObject, currentPhoto), currentFilter, ImageLocation.getForPhoto(currentPhotoObjectThumb, currentPhoto), currentThumbFilter, currentPhotoObject.size, null, parentAdapter.currentPage, 1); buttonState = 1; radialProgress.setIcon(getIconForCurrentState(), true, animated); invalidate(); } else if (buttonState == 1) { imageView.cancelLoadImage(); buttonState = 0; radialProgress.setIcon(getIconForCurrentState(), false, animated); invalidate(); } } public void updateButtonState(boolean animated) { String fileName = FileLoader.getAttachFileName(currentPhotoObject); File path = FileLoader.getPathToAttach(currentPhotoObject, true); boolean fileExists = path.exists(); if (TextUtils.isEmpty(fileName)) { radialProgress.setIcon(MediaActionDrawable.ICON_NONE, false, false); return; } if (fileExists) { DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); buttonState = -1; radialProgress.setIcon(getIconForCurrentState(), false, animated); } else { DownloadController.getInstance(currentAccount).addLoadingFileObserver(fileName, null, this); float setProgress = 0; if (autoDownload || FileLoader.getInstance(currentAccount).isLoadingFile(fileName)) { buttonState = 1; Float progress = ImageLoader.getInstance().getFileProgress(fileName); setProgress = progress != null ? progress : 0; } else { buttonState = 0; } radialProgress.setIcon(getIconForCurrentState(), true, animated); radialProgress.setProgress(setProgress, false); } invalidate(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); imageView.onDetachedFromWindow(); DownloadController.getInstance(currentAccount).removeLoadingFileObserver(this); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); imageView.onAttachedToWindow(); updateButtonState(false); } @Override public void onFailedDownload(String fileName, boolean canceled) { updateButtonState(false); } @Override public void onSuccessDownload(String fileName) { radialProgress.setProgress(1, true); updateButtonState(true); } @Override public void onProgressUpload(String fileName, long uploadedSize, long totalSize, boolean isEncrypted) { } @Override public void onProgressDownload(String fileName, long downloadSize, long totalSize) { radialProgress.setProgress(Math.min(1f, downloadSize / (float) totalSize), true); if (buttonState != 1) { updateButtonState(true); } } @Override public int getObserverTag() { return TAG; } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setEnabled(true); StringBuilder sb = new StringBuilder(LocaleController.getString("AttachPhoto", R.string.AttachPhoto)); if (captionLayout != null) { sb.append(", "); sb.append(captionLayout.getText()); } info.setText(sb.toString()); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (captionLayout != null) { blocks.add(captionLayout); } if (creditLayout != null) { blocks.add(creditLayout); } } } private class BlockMapCell extends FrameLayout implements TextSelectionHelper.ArticleSelectableView { private DrawingText captionLayout; private DrawingText creditLayout; private ImageReceiver imageView; private int currentType; private boolean isFirst; private int textX; private int textY; private int creditOffset; private boolean photoPressed; private int currentMapProvider; private TLRPC.TL_pageBlockMap currentBlock; private WebpageAdapter parentAdapter; public BlockMapCell(Context context, WebpageAdapter adapter, int type) { super(context); parentAdapter = adapter; setWillNotDraw(false); imageView = new ImageReceiver(this); currentType = type; } public void setBlock(TLRPC.TL_pageBlockMap block, boolean first, boolean last) { currentBlock = block; isFirst = first; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); if (event.getAction() == MotionEvent.ACTION_DOWN && imageView.isInsideImage(x, y)) { photoPressed = true; } else if (event.getAction() == MotionEvent.ACTION_UP && photoPressed) { photoPressed = false; try { double lat = currentBlock.geo.lat; double lon = currentBlock.geo._long; parentActivity.startActivity(new Intent(android.content.Intent.ACTION_VIEW, Uri.parse("geo:" + lat + "," + lon + "?q=" + lat + "," + lon))); } catch (Exception e) { FileLog.e(e); } } else if (event.getAction() == MotionEvent.ACTION_CANCEL) { photoPressed = false; } return photoPressed || checkLayoutForLinks(parentAdapter, event, this, captionLayout, textX, textY) || checkLayoutForLinks(parentAdapter, event, this, creditLayout, textX, textY + creditOffset) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentType == 1) { width = ((View) getParent()).getMeasuredWidth(); height = ((View) getParent()).getMeasuredHeight(); } else if (currentType == 2) { height = width; } if (currentBlock != null) { int photoWidth = width; int photoX; int textWidth; if (currentType == 0 && currentBlock.level > 0) { textX = photoX = AndroidUtilities.dp(14 * currentBlock.level) + AndroidUtilities.dp(18); photoWidth -= photoX + AndroidUtilities.dp(18); textWidth = photoWidth; } else { photoX = 0; textX = AndroidUtilities.dp(18); textWidth = width - AndroidUtilities.dp(36); } if (currentType == 0) { float scale; scale = photoWidth / (float) currentBlock.w; height = (int) (scale * currentBlock.h); int maxHeight = (int) ((Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(56)) * 0.9f); if (height > maxHeight) { height = maxHeight; scale = height / (float) currentBlock.h; photoWidth = (int) (scale * currentBlock.w); photoX += (width - photoX - photoWidth) / 2; } } imageView.setImageCoords(photoX, (isFirst || currentType == 1 || currentType == 2 || currentBlock.level > 0) ? 0 : AndroidUtilities.dp(8), photoWidth, height); String currentUrl = AndroidUtilities.formapMapUrl(currentAccount, currentBlock.geo.lat, currentBlock.geo._long, (int) (photoWidth / AndroidUtilities.density), (int) (height / AndroidUtilities.density), true, 15, -1); WebFile currentWebFile = WebFile.createWithGeoPoint(currentBlock.geo, (int) (photoWidth / AndroidUtilities.density), (int) (height / AndroidUtilities.density), 15, Math.min(2, (int) Math.ceil(AndroidUtilities.density))); currentMapProvider = MessagesController.getInstance(currentAccount).mapProvider; if (currentMapProvider == 2) { if (currentWebFile != null) { imageView.setImage(ImageLocation.getForWebFile(currentWebFile), null, null, null, parentAdapter.currentPage, 0); } } else if (currentUrl != null) { imageView.setImage(currentUrl, null, null, null, 0); } textY = (int) (imageView.getImageY() + imageView.getImageHeight() + AndroidUtilities.dp(8)); if (currentType == 0) { captionLayout = createLayoutForText(this, null, currentBlock.caption.text, textWidth, textY, currentBlock, parentAdapter); if (captionLayout != null) { creditOffset = AndroidUtilities.dp(4) + captionLayout.getHeight(); height += creditOffset + AndroidUtilities.dp(4); captionLayout.x = textX; captionLayout.y = textY; } creditLayout = createLayoutForText(this, null, currentBlock.caption.credit, textWidth, textY + creditOffset, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (creditLayout != null) { height += AndroidUtilities.dp(4) + creditLayout.getHeight(); creditLayout.x = textX; creditLayout.y = textY + creditOffset; } } if (!isFirst && currentType == 0 && currentBlock.level <= 0) { height += AndroidUtilities.dp(8); } if (currentType != 2) { height += AndroidUtilities.dp(8); } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } Theme.chat_docBackPaint.setColor(Theme.getColor(Theme.key_chat_inLocationBackground)); canvas.drawRect(imageView.getImageX(), imageView.getImageY(), imageView.getImageX2(), imageView.getImageY2(), Theme.chat_docBackPaint); int left = (int) (imageView.getCenterX() - Theme.chat_locationDrawable[0].getIntrinsicWidth() / 2); int top = (int) (imageView.getCenterY() - Theme.chat_locationDrawable[0].getIntrinsicHeight() / 2); Theme.chat_locationDrawable[0].setBounds(left, top, left + Theme.chat_locationDrawable[0].getIntrinsicWidth(), top + Theme.chat_locationDrawable[0].getIntrinsicHeight()); Theme.chat_locationDrawable[0].draw(canvas); imageView.draw(canvas); if (currentMapProvider == 2 && imageView.hasNotThumb()) { int w = (int) (Theme.chat_redLocationIcon.getIntrinsicWidth() * 0.8f); int h = (int) (Theme.chat_redLocationIcon.getIntrinsicHeight() * 0.8f); int x = (int) (imageView.getImageX() + (imageView.getImageWidth() - w) / 2); int y = (int) (imageView.getImageY() + (imageView.getImageHeight() / 2 - h)); Theme.chat_redLocationIcon.setAlpha((int) (255 * imageView.getCurrentAlpha())); Theme.chat_redLocationIcon.setBounds(x, y, x + w, y + h); Theme.chat_redLocationIcon.draw(canvas); } int count = 0; if (captionLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this, count++); captionLayout.draw(canvas); canvas.restore(); } if (creditLayout != null) { canvas.save(); canvas.translate(textX, textY + creditOffset); drawTextSelection(canvas, this, count); creditLayout.draw(canvas); canvas.restore(); } if (currentBlock.level > 0) { canvas.drawRect(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(20), getMeasuredHeight() - (currentBlock.bottom ? AndroidUtilities.dp(6) : 0), quoteLinePaint); } } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setEnabled(true); StringBuilder sb = new StringBuilder(LocaleController.getString("Map", R.string.Map)); if (captionLayout != null) { sb.append(", "); sb.append(captionLayout.getText()); } info.setText(sb.toString()); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (captionLayout != null) { blocks.add(captionLayout); } if (creditLayout != null) { blocks.add(creditLayout); } } } private class BlockChannelCell extends FrameLayout implements TextSelectionHelper.ArticleSelectableView { private ContextProgressView progressView; private TextView textView; private ImageView imageView; private int currentState; private DrawingText textLayout; private int buttonWidth; private int textX = AndroidUtilities.dp(18); private int textY = AndroidUtilities.dp(11); private int textX2; private Paint backgroundPaint; private AnimatorSet currentAnimation; private int currentType; private TLRPC.TL_pageBlockChannel currentBlock; private WebpageAdapter parentAdapter; public BlockChannelCell(Context context, WebpageAdapter adapter, int type) { super(context); parentAdapter = adapter; setWillNotDraw(false); backgroundPaint = new Paint(); currentType = type; textView = new TextView(context); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14); textView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); textView.setText(LocaleController.getString("ChannelJoin", R.string.ChannelJoin)); textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT); addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 39, Gravity.RIGHT | Gravity.TOP)); textView.setOnClickListener(v -> { if (currentState != 0) { return; } setState(1, true); joinChannel(BlockChannelCell.this, loadedChannel); }); imageView = new ImageView(context); imageView.setImageResource(R.drawable.list_check); imageView.setScaleType(ImageView.ScaleType.CENTER); addView(imageView, LayoutHelper.createFrame(39, 39, Gravity.RIGHT | Gravity.TOP)); progressView = new ContextProgressView(context, 0); addView(progressView, LayoutHelper.createFrame(39, 39, Gravity.RIGHT | Gravity.TOP)); } public void setBlock(TLRPC.TL_pageBlockChannel block) { currentBlock = block; if (currentType == 0) { int color = Theme.getColor(Theme.key_switchTrack); int r = Color.red(color); int g = Color.green(color); int b = Color.blue(color); textView.setTextColor(getLinkTextColor()); backgroundPaint.setColor(Color.argb(34, r, g, b)); imageView.setColorFilter(new PorterDuffColorFilter(getGrayTextColor(), PorterDuff.Mode.MULTIPLY)); } else { textView.setTextColor(0xffffffff); backgroundPaint.setColor(0x7f000000); imageView.setColorFilter(new PorterDuffColorFilter(0xffffffff, PorterDuff.Mode.MULTIPLY)); } TLRPC.Chat channel = MessagesController.getInstance(currentAccount).getChat(block.channel.id); if (channel == null || channel.min) { loadChannel(this, parentAdapter, block.channel); setState(1, false); } else { loadedChannel = channel; if (channel.left && !channel.kicked) { setState(0, false); } else { setState(4, false); } } requestLayout(); } public void setState(int state, boolean animated) { if (currentAnimation != null) { currentAnimation.cancel(); } currentState = state; if (animated) { currentAnimation = new AnimatorSet(); currentAnimation.playTogether( ObjectAnimator.ofFloat(textView, View.ALPHA, state == 0 ? 1.0f : 0.0f), ObjectAnimator.ofFloat(textView, View.SCALE_X, state == 0 ? 1.0f : 0.1f), ObjectAnimator.ofFloat(textView, View.SCALE_Y, state == 0 ? 1.0f : 0.1f), ObjectAnimator.ofFloat(progressView, View.ALPHA, state == 1 ? 1.0f : 0.0f), ObjectAnimator.ofFloat(progressView, View.SCALE_X, state == 1 ? 1.0f : 0.1f), ObjectAnimator.ofFloat(progressView, View.SCALE_Y, state == 1 ? 1.0f : 0.1f), ObjectAnimator.ofFloat(imageView, View.ALPHA, state == 2 ? 1.0f : 0.0f), ObjectAnimator.ofFloat(imageView, View.SCALE_X, state == 2 ? 1.0f : 0.1f), ObjectAnimator.ofFloat(imageView, View.SCALE_Y, state == 2 ? 1.0f : 0.1f) ); currentAnimation.setDuration(150); currentAnimation.start(); } else { textView.setAlpha(state == 0 ? 1.0f : 0.0f); textView.setScaleX(state == 0 ? 1.0f : 0.1f); textView.setScaleY(state == 0 ? 1.0f : 0.1f); progressView.setAlpha(state == 1 ? 1.0f : 0.0f); progressView.setScaleX(state == 1 ? 1.0f : 0.1f); progressView.setScaleY(state == 1 ? 1.0f : 0.1f); imageView.setAlpha(state == 2 ? 1.0f : 0.0f); imageView.setScaleX(state == 2 ? 1.0f : 0.1f); imageView.setScaleY(state == 2 ? 1.0f : 0.1f); } } @Override public boolean onTouchEvent(MotionEvent event) { if (currentType != 0) { return super.onTouchEvent(event); } return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); setMeasuredDimension(width, AndroidUtilities.dp(39 + 9)); textView.measure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(39), MeasureSpec.EXACTLY)); buttonWidth = textView.getMeasuredWidth(); progressView.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(39), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(39), MeasureSpec.EXACTLY)); imageView.measure(MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(39), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(39), MeasureSpec.EXACTLY)); if (currentBlock != null) { textLayout = createLayoutForText(this, currentBlock.channel.title, null, width - AndroidUtilities.dp(36 + 16) - buttonWidth, textY, currentBlock, StaticLayoutEx.ALIGN_LEFT(), parentAdapter); if (parentAdapter.isRtl) { textX2 = textX; } else { textX2 = getMeasuredWidth() - textX - buttonWidth; } if (textLayout != null) { textLayout.x = textX; textLayout.y = textY; } } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { imageView.layout(textX2 + buttonWidth / 2 - AndroidUtilities.dp(19), 0, textX2 + buttonWidth / 2 + AndroidUtilities.dp(20), AndroidUtilities.dp(39)); progressView.layout(textX2 + buttonWidth / 2 - AndroidUtilities.dp(19), 0, textX2 + buttonWidth / 2 + AndroidUtilities.dp(20), AndroidUtilities.dp(39)); textView.layout(textX2, 0, textX2 + textView.getMeasuredWidth(), textView.getMeasuredHeight()); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } canvas.drawRect(0, 0, getMeasuredWidth(), AndroidUtilities.dp(39), backgroundPaint); if (textLayout != null && textLayout.getLineCount() > 0) { canvas.save(); if (parentAdapter.isRtl) { canvas.translate(getMeasuredWidth() - textLayout.getLineWidth(0) - textX, textY); } else { canvas.translate(textX, textY); } if (currentType == 0) { drawTextSelection(canvas, this); } textLayout.draw(canvas); canvas.restore(); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } } private class BlockAuthorDateCell extends View implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private int textX; private int textY = AndroidUtilities.dp(8); private TLRPC.TL_pageBlockAuthorDate currentBlock; private WebpageAdapter parentAdapter; public BlockAuthorDateCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; } public void setBlock(TLRPC.TL_pageBlockAuthorDate block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || super.onTouchEvent(event); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { CharSequence text; CharSequence author = getText(parentAdapter, this, currentBlock.author, currentBlock.author, currentBlock, width); Spannable spannableAuthor; MetricAffectingSpan[] spans; if (author instanceof Spannable) { spannableAuthor = (Spannable) author; spans = spannableAuthor.getSpans(0, author.length(), MetricAffectingSpan.class); } else { spannableAuthor = null; spans = null; } if (currentBlock.published_date != 0 && !TextUtils.isEmpty(author)) { text = LocaleController.formatString("ArticleDateByAuthor", R.string.ArticleDateByAuthor, LocaleController.getInstance().chatFullDate.format((long) currentBlock.published_date * 1000), author); } else if (!TextUtils.isEmpty(author)) { text = LocaleController.formatString("ArticleByAuthor", R.string.ArticleByAuthor, author); } else { text = LocaleController.getInstance().chatFullDate.format((long) currentBlock.published_date * 1000); } try { if (spans != null && spans.length > 0) { int idx = TextUtils.indexOf(text, author); if (idx != -1) { Spannable spannable = Spannable.Factory.getInstance().newSpannable(text); text = spannable; for (int a = 0; a < spans.length; a++) { spannable.setSpan(spans[a], idx + spannableAuthor.getSpanStart(spans[a]), idx + spannableAuthor.getSpanEnd(spans[a]), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } } } catch (Exception e) { FileLog.e(e); } textLayout = createLayoutForText(this, text, null, width - AndroidUtilities.dp(36), textY, currentBlock, parentAdapter); if (textLayout != null) { height += AndroidUtilities.dp(8 + 8) + textLayout.getHeight(); if (parentAdapter.isRtl) { textX = (int) Math.floor(width - textLayout.getLineWidth(0) - AndroidUtilities.dp(16)); } else { textX = AndroidUtilities.dp(18); } textLayout.x = textX; textLayout.y = textY; } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this); textLayout.draw(canvas); canvas.restore(); } } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setEnabled(true); if (textLayout == null) return; info.setText(textLayout.getText()); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } } private class BlockTitleCell extends View implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private TLRPC.TL_pageBlockTitle currentBlock; private int textX = AndroidUtilities.dp(18); private int textY; private WebpageAdapter parentAdapter; public BlockTitleCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; } public void setBlock(TLRPC.TL_pageBlockTitle block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { if (currentBlock.first) { height += AndroidUtilities.dp(8); textY = AndroidUtilities.dp(16); } else { textY = AndroidUtilities.dp(8); } textLayout = createLayoutForText(this, null, currentBlock.text, width - AndroidUtilities.dp(36), textY, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (textLayout != null) { height += AndroidUtilities.dp(8 + 8) + textLayout.getHeight(); textLayout.x = textX; textLayout.y = textY; } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas,this); textLayout.draw(canvas); canvas.restore(); } } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setEnabled(true); if (textLayout == null) return; info.setText(textLayout.getText() + ", " + LocaleController.getString("AccDescrIVTitle", R.string.AccDescrIVTitle)); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } } private class BlockKickerCell extends View implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private TLRPC.TL_pageBlockKicker currentBlock; private int textX = AndroidUtilities.dp(18); private int textY; private WebpageAdapter parentAdapter; public BlockKickerCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; } public void setBlock(TLRPC.TL_pageBlockKicker block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { if (currentBlock.first) { textY = AndroidUtilities.dp(16); height += AndroidUtilities.dp(8); } else { textY = AndroidUtilities.dp(8); } textLayout = createLayoutForText(this, null, currentBlock.text, width - AndroidUtilities.dp(36), textY, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (textLayout != null) { height += AndroidUtilities.dp(8 + 8) + textLayout.getHeight(); textLayout.x = textX; textLayout.y = textY; } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this); textLayout.draw(canvas); canvas.restore(); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } } private class BlockFooterCell extends View implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private int textX = AndroidUtilities.dp(18); private int textY = AndroidUtilities.dp(8); private TLRPC.TL_pageBlockFooter currentBlock; private WebpageAdapter parentAdapter; public BlockFooterCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; } public void setBlock(TLRPC.TL_pageBlockFooter block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { if (currentBlock.level == 0) { textY = AndroidUtilities.dp(8); textX = AndroidUtilities.dp(18); } else { textY = 0; textX = AndroidUtilities.dp(18 + 14 * currentBlock.level); } textLayout = createLayoutForText(this, null, currentBlock.text, width - AndroidUtilities.dp(18) - textX, textY, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (textLayout != null) { height = textLayout.getHeight(); if (currentBlock.level > 0) { height += AndroidUtilities.dp(8); } else { height += AndroidUtilities.dp(8 + 8); } textLayout.x = textX; textLayout.y = textY; } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this); textLayout.draw(canvas); canvas.restore(); } if (currentBlock.level > 0) { canvas.drawRect(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(20), getMeasuredHeight() - (currentBlock.bottom ? AndroidUtilities.dp(6) : 0), quoteLinePaint); } } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } } private class BlockPreformattedCell extends FrameLayout implements TextSelectionHelper.ArticleSelectableView { private DrawingText textLayout; private HorizontalScrollView scrollView; private View textContainer; private TLRPC.TL_pageBlockPreformatted currentBlock; private WebpageAdapter parentAdapter; public BlockPreformattedCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; scrollView = new HorizontalScrollView(context) { @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (textContainer.getMeasuredWidth() > getMeasuredWidth()) { windowView.requestDisallowInterceptTouchEvent(true); } return super.onInterceptTouchEvent(ev); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (pressedLinkOwnerLayout != null) { pressedLinkOwnerLayout = null; pressedLinkOwnerView = null; } } }; scrollView.setPadding(0, AndroidUtilities.dp(8), 0, AndroidUtilities.dp(8)); addView(scrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT)); textContainer = new View(context) { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = 0; int width = 1; if (currentBlock != null) { textLayout = createLayoutForText(this, null, currentBlock.text, AndroidUtilities.dp(5000), 0, currentBlock, parentAdapter); if (textLayout != null) { height += textLayout.getHeight(); for (int a = 0, count = textLayout.getLineCount(); a < count; a++) { width = Math.max((int) Math.ceil(textLayout.getLineWidth(a)), width); } } } else { height = 1; } setMeasuredDimension(width + AndroidUtilities.dp(32), height); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, BlockPreformattedCell.this, textLayout, 0, 0) || super.onTouchEvent(event); } @Override protected void onDraw(Canvas canvas) { if (textLayout != null) { canvas.save(); drawTextSelection(canvas, BlockPreformattedCell.this); textLayout.draw(canvas); canvas.restore(); textLayout.x = (int) getX(); textLayout.y = (int) getY(); } } }; HorizontalScrollView.LayoutParams layoutParams = new HorizontalScrollView.LayoutParams(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT); layoutParams.leftMargin = layoutParams.rightMargin = AndroidUtilities.dp(16); layoutParams.topMargin = layoutParams.bottomMargin = AndroidUtilities.dp(12); scrollView.addView(textContainer, layoutParams); if (Build.VERSION.SDK_INT >= 23) { scrollView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> { if (textSelectionHelper != null && textSelectionHelper.isSelectionMode()) { textSelectionHelper.invalidate(); } }); } setWillNotDraw(false); } public void setBlock(TLRPC.TL_pageBlockPreformatted block) { currentBlock = block; scrollView.setScrollX(0); textContainer.requestLayout(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); scrollView.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); setMeasuredDimension(width, scrollView.getMeasuredHeight()); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } canvas.drawRect(0, AndroidUtilities.dp(8), getMeasuredWidth(), getMeasuredHeight() - AndroidUtilities.dp(8), preformattedBackgroundPaint); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } @Override public void invalidate() { textContainer.invalidate(); super.invalidate(); } } private class BlockSubheaderCell extends View implements TextSelectionHelper.ArticleSelectableView{ private DrawingText textLayout; private int textX = AndroidUtilities.dp(18); private int textY = AndroidUtilities.dp(8); private TLRPC.TL_pageBlockSubheader currentBlock; private WebpageAdapter parentAdapter; public BlockSubheaderCell(Context context, WebpageAdapter adapter) { super(context); parentAdapter = adapter; } public void setBlock(TLRPC.TL_pageBlockSubheader block) { currentBlock = block; requestLayout(); } @Override public boolean onTouchEvent(MotionEvent event) { return checkLayoutForLinks(parentAdapter, event, this, textLayout, textX, textY) || super.onTouchEvent(event); } @SuppressLint("NewApi") @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int height = 0; if (currentBlock != null) { textLayout = createLayoutForText(this, null, currentBlock.text, width - AndroidUtilities.dp(36), textY, currentBlock, parentAdapter.isRtl ? StaticLayoutEx.ALIGN_RIGHT() : Layout.Alignment.ALIGN_NORMAL, parentAdapter); if (textLayout != null) { height += AndroidUtilities.dp(8 + 8) + textLayout.getHeight(); textLayout.x = textX; textLayout.y = textY; } } else { height = 1; } setMeasuredDimension(width, height); } @Override protected void onDraw(Canvas canvas) { if (currentBlock == null) { return; } if (textLayout != null) { canvas.save(); canvas.translate(textX, textY); drawTextSelection(canvas, this); textLayout.draw(canvas); canvas.restore(); } } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); info.setEnabled(true); if (textLayout == null) { return; } info.setText(textLayout.getText() + ", " + LocaleController.getString("AccDescrIVHeading", R.string.AccDescrIVHeading)); } @Override public void fillTextLayoutBlocks(ArrayList blocks) { if (textLayout != null) { blocks.add(textLayout); } } } private static class ReportCell extends FrameLayout { private TextView textView; private TextView viewsTextView; private boolean hasViews; public ReportCell(Context context) { super(context); setTag(90); textView = new TextView(context); textView.setText(LocaleController.getString("PreviewFeedback2", R.string.PreviewFeedback2)); textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); textView.setGravity(Gravity.CENTER); textView.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); addView(textView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 34, Gravity.LEFT | Gravity.TOP, 0, 10, 0, 0)); viewsTextView = new TextView(context); viewsTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12); viewsTextView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL); viewsTextView.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0); addView(viewsTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 34, Gravity.LEFT | Gravity.TOP, 0, 10, 0, 0)); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(44), MeasureSpec.EXACTLY)); } public void setViews(int count) { if (count == 0) { hasViews = false; viewsTextView.setVisibility(GONE); textView.setGravity(Gravity.CENTER); } else { hasViews = true; viewsTextView.setVisibility(VISIBLE); textView.setGravity(Gravity.RIGHT | Gravity.CENTER_VERTICAL); viewsTextView.setText(LocaleController.formatPluralStringComma("Views", count)); } int color = Theme.getColor(Theme.key_switchTrack); textView.setTextColor(getGrayTextColor()); viewsTextView.setTextColor(getGrayTextColor()); textView.setBackgroundColor(Color.argb(34, Color.red(color), Color.green(color), Color.blue(color))); } } private void drawTextSelection(Canvas canvas, TextSelectionHelper.ArticleSelectableView view) { drawTextSelection(canvas, view, 0); } private void drawTextSelection(Canvas canvas, TextSelectionHelper.ArticleSelectableView view, int i) { View v = (View) view; if (v.getTag() != null && v.getTag() == BOTTOM_SHEET_VIEW_TAG && textSelectionHelperBottomSheet != null) { textSelectionHelperBottomSheet.draw(canvas, view, i); } else { textSelectionHelper.draw(canvas, view, i); } } public boolean openPhoto(TLRPC.PageBlock block, WebpageAdapter adapter) { final int index; final List pageBlocks; if (!(block instanceof TLRPC.TL_pageBlockVideo) || WebPageUtils.isVideo(adapter.currentPage, block)) { pageBlocks = new ArrayList<>(adapter.photoBlocks); index = adapter.photoBlocks.indexOf(block); } else { pageBlocks = Collections.singletonList(block); index = 0; } final PhotoViewer photoViewer = PhotoViewer.getInstance(); photoViewer.setParentActivity(parentActivity); return photoViewer.openPhoto(index, new RealPageBlocksAdapter(adapter.currentPage, pageBlocks), new PageBlocksPhotoViewerProvider(pageBlocks)); } private class RealPageBlocksAdapter implements PhotoViewer.PageBlocksAdapter { private final TLRPC.WebPage page; private final List pageBlocks; private RealPageBlocksAdapter(TLRPC.WebPage page, List pageBlocks) { this.page = page; this.pageBlocks = pageBlocks; } @Override public int getItemsCount() { return pageBlocks.size(); } @Override public TLRPC.PageBlock get(int index) { return pageBlocks.get(index); } @Override public List getAll() { return pageBlocks; } @Override public boolean isVideo(int index) { return !(index >= pageBlocks.size() || index < 0) && WebPageUtils.isVideo(page, get(index)); } @Override public TLObject getMedia(int index) { if (index >= pageBlocks.size() || index < 0) { return null; } return WebPageUtils.getMedia(page, get(index)); } @Override public File getFile(int index) { if (index >= pageBlocks.size() || index < 0) { return null; } return WebPageUtils.getMediaFile(page, get(index)); } @Override public String getFileName(int index) { TLObject media = getMedia(index); if (media instanceof TLRPC.Photo) { media = FileLoader.getClosestPhotoSizeWithSize(((TLRPC.Photo) media).sizes, AndroidUtilities.getPhotoSize()); } return FileLoader.getAttachFileName(media); } @Override public CharSequence getCaption(int index) { CharSequence caption = null; final TLRPC.PageBlock pageBlock = get(index); if (pageBlock instanceof TLRPC.TL_pageBlockPhoto) { String url = ((TLRPC.TL_pageBlockPhoto) pageBlock).url; if (!TextUtils.isEmpty(url)) { SpannableStringBuilder stringBuilder = new SpannableStringBuilder(url); stringBuilder.setSpan(new URLSpan(url) { @Override public void onClick(View widget) { openWebpageUrl(getURL(), null); } }, 0, url.length(), Spanned.SPAN_EXCLUSIVE_INCLUSIVE); caption = stringBuilder; } } if (caption == null) { TLRPC.RichText captionRichText = getBlockCaption(pageBlock, 2); caption = getText(page, null, captionRichText, captionRichText, pageBlock, -AndroidUtilities.dp(100)); if (caption instanceof Spannable) { Spannable spannable = (Spannable) caption; TextPaintUrlSpan[] spans = spannable.getSpans(0, caption.length(), TextPaintUrlSpan.class); SpannableStringBuilder builder = new SpannableStringBuilder(caption.toString()); caption = builder; if (spans != null && spans.length > 0) { for (int a = 0; a < spans.length; a++) { builder.setSpan(new URLSpan(spans[a].getUrl()) { @Override public void onClick(View widget) { openWebpageUrl(getURL(), null); } }, spannable.getSpanStart(spans[a]), spannable.getSpanEnd(spans[a]), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); } } } } return caption; } @Override public TLRPC.PhotoSize getFileLocation(TLObject media, int[] size) { if (media instanceof TLRPC.Photo) { TLRPC.Photo photo = (TLRPC.Photo) media; TLRPC.PhotoSize sizeFull = FileLoader.getClosestPhotoSizeWithSize(photo.sizes, AndroidUtilities.getPhotoSize()); if (sizeFull != null) { size[0] = sizeFull.size; if (size[0] == 0) { size[0] = -1; } return sizeFull; } else { size[0] = -1; } } else if (media instanceof TLRPC.Document) { TLRPC.Document document = (TLRPC.Document) media; TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90); if (thumb != null) { size[0] = thumb.size; if (size[0] == 0) { size[0] = -1; } return thumb; } } return null; } @Override public void updateSlideshowCell(TLRPC.PageBlock currentPageBlock) { int count = listView[0].getChildCount(); for (int a = 0; a < count; a++) { View child = listView[0].getChildAt(a); if (child instanceof ArticleViewer.BlockSlideshowCell) { ArticleViewer.BlockSlideshowCell cell = (ArticleViewer.BlockSlideshowCell) child; int idx = cell.currentBlock.items.indexOf(currentPageBlock); if (idx != -1) { cell.innerListView.setCurrentItem(idx, false); break; } } } } @Override public Object getParentObject() { return page; } } private class PageBlocksPhotoViewerProvider extends PhotoViewer.EmptyPhotoViewerProvider { private final int[] tempArr = new int[2]; private final List pageBlocks; public PageBlocksPhotoViewerProvider(List pageBlocks) { this.pageBlocks = pageBlocks; } @Override public PhotoViewer.PlaceProviderObject getPlaceForPhoto(MessageObject messageObject, TLRPC.FileLocation fileLocation, int index, boolean needPreview) { if (index < 0 || index >= pageBlocks.size()) { return null; } ImageReceiver imageReceiver = getImageReceiverFromListView(listView[0], pageBlocks.get(index), tempArr); if (imageReceiver == null) { return null; } PhotoViewer.PlaceProviderObject object = new PhotoViewer.PlaceProviderObject(); object.viewX = tempArr[0]; object.viewY = tempArr[1]; object.parentView = listView[0]; object.imageReceiver = imageReceiver; object.thumb = imageReceiver.getBitmapSafe(); object.radius = imageReceiver.getRoundRadius(); object.clipTopAddition = currentHeaderHeight; return object; } private ImageReceiver getImageReceiverFromListView(ViewGroup listView, TLRPC.PageBlock pageBlock, int[] coords) { int count = listView.getChildCount(); for (int a = 0; a < count; a++) { ImageReceiver imageReceiver = getImageReceiverView(listView.getChildAt(a), pageBlock, coords); if (imageReceiver != null) { return imageReceiver; } } return null; } private ImageReceiver getImageReceiverView(View view, TLRPC.PageBlock pageBlock, int[] coords) { if (view instanceof BlockPhotoCell) { BlockPhotoCell cell = (BlockPhotoCell) view; if (cell.currentBlock == pageBlock) { view.getLocationInWindow(coords); return cell.imageView; } } else if (view instanceof BlockVideoCell) { BlockVideoCell cell = (BlockVideoCell) view; if (cell.currentBlock == pageBlock) { view.getLocationInWindow(coords); return cell.imageView; } } else if (view instanceof BlockCollageCell) { ImageReceiver imageReceiver = getImageReceiverFromListView(((BlockCollageCell) view).innerListView, pageBlock, coords); if (imageReceiver != null) { return imageReceiver; } } else if (view instanceof BlockSlideshowCell) { ImageReceiver imageReceiver = getImageReceiverFromListView(((BlockSlideshowCell) view).innerListView, pageBlock, coords); if (imageReceiver != null) { return imageReceiver; } } else if (view instanceof BlockListItemCell) { BlockListItemCell blockListItemCell = (BlockListItemCell) view; if (blockListItemCell.blockLayout != null) { ImageReceiver imageReceiver = getImageReceiverView(blockListItemCell.blockLayout.itemView, pageBlock, coords); if (imageReceiver != null) { return imageReceiver; } } } else if (view instanceof BlockOrderedListItemCell) { BlockOrderedListItemCell blockOrderedListItemCell = (BlockOrderedListItemCell) view; if (blockOrderedListItemCell.blockLayout != null) { ImageReceiver imageReceiver = getImageReceiverView(blockOrderedListItemCell.blockLayout.itemView, pageBlock, coords); if (imageReceiver != null) { return imageReceiver; } } } return null; } } }