package org.telegram.ui.Components; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Shader; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.util.TypedValue; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearSmoothScroller; import androidx.recyclerview.widget.RecyclerView; import org.telegram.messenger.AndroidUtilities; import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ChatThemeController; import org.telegram.messenger.FileLoader; import org.telegram.messenger.FileLog; import org.telegram.messenger.LocaleController; import org.telegram.messenger.MediaDataController; import org.telegram.messenger.NotificationCenter; import org.telegram.messenger.R; import org.telegram.messenger.SharedConfig; import org.telegram.messenger.Utilities; import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.ResultCallback; import org.telegram.tgnet.TLRPC; import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.BottomSheet; import org.telegram.ui.ActionBar.EmojiThemes; import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.ThemeDescription; import org.telegram.ui.Cells.DrawerProfileCell; import org.telegram.ui.Cells.ThemesHorizontalListCell; import org.telegram.ui.ChatActivity; import java.io.File; import java.io.FileInputStream; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Objects; public class ChatThemeBottomSheet extends BottomSheet implements NotificationCenter.NotificationCenterDelegate { private final Adapter adapter; private final ChatActivity.ThemeDelegate themeDelegate; private final EmojiThemes originalTheme; private final boolean originalIsDark; private final ChatActivity chatActivity; private final RecyclerListView recyclerView; private final LinearLayoutManager layoutManager; private final FlickerLoadingView progressView; private final TextView titleView; private final RLottieDrawable darkThemeDrawable; private final RLottieImageView darkThemeView; private final LinearSmoothScroller scroller; private final View applyButton; private TextView applyTextView; private TextView resetTextView; private ChatThemeItem selectedItem; private boolean forceDark; private boolean isApplyClicked; private boolean isLightDarkChangeAnimation; private int prevSelectedPosition = -1; private View changeDayNightView; private float changeDayNightViewProgress; private ValueAnimator changeDayNightViewAnimator; HintView hintView; public ChatThemeBottomSheet(final ChatActivity chatActivity, ChatActivity.ThemeDelegate themeDelegate) { super(chatActivity.getParentActivity(), true, themeDelegate); this.chatActivity = chatActivity; this.themeDelegate = themeDelegate; this.originalTheme = themeDelegate.getCurrentTheme(); this.originalIsDark = Theme.getActiveTheme().isDark(); adapter = new Adapter(currentAccount, themeDelegate, ThemeSmallPreviewView.TYPE_DEFAULT); setDimBehind(false); setCanDismissWithSwipe(false); setApplyBottomPadding(false); FrameLayout rootLayout = new FrameLayout(getContext()); setCustomView(rootLayout); titleView = new TextView(getContext()); titleView.setEllipsize(TextUtils.TruncateAt.MIDDLE); titleView.setLines(1); titleView.setSingleLine(true); titleView.setText(LocaleController.getString("SelectTheme", R.string.SelectTheme)); titleView.setTextColor(getThemedColor(Theme.key_dialogTextBlack)); titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20); titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); titleView.setPadding(AndroidUtilities.dp(21), AndroidUtilities.dp(6), AndroidUtilities.dp(21), AndroidUtilities.dp(8)); rootLayout.addView(titleView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.START, 0, 0, 62, 0)); int drawableColor = getThemedColor(Theme.key_featuredStickers_addButton); int drawableSize = AndroidUtilities.dp(28); darkThemeDrawable = new RLottieDrawable(R.raw.sun_outline, "" + R.raw.sun_outline, drawableSize, drawableSize, true, null); darkThemeDrawable.setPlayInDirectionOfCustomEndFrame(true); darkThemeDrawable.beginApplyLayerColors(); setDarkButtonColor(drawableColor); darkThemeDrawable.commitApplyLayerColors(); darkThemeView = new RLottieImageView(getContext()); darkThemeView.setAnimation(darkThemeDrawable); darkThemeView.setScaleType(ImageView.ScaleType.CENTER); darkThemeView.setOnClickListener(view -> { if (changeDayNightViewAnimator != null) { return; } setupLightDarkTheme(!forceDark); }); rootLayout.addView(darkThemeView, LayoutHelper.createFrame(44, 44, Gravity.TOP | Gravity.END, 0, -2, 7, 0)); forceDark = !Theme.getActiveTheme().isDark(); setForceDark(Theme.getActiveTheme().isDark(), false); scroller = new LinearSmoothScroller(getContext()) { @Override protected int calculateTimeForScrolling(int dx) { return super.calculateTimeForScrolling(dx) * 6; } }; recyclerView = new RecyclerListView(getContext()); recyclerView.setAdapter(adapter); recyclerView.setClipChildren(false); recyclerView.setClipToPadding(false); recyclerView.setHasFixedSize(true); recyclerView.setItemAnimator(null); recyclerView.setNestedScrollingEnabled(false); recyclerView.setLayoutManager(layoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false)); recyclerView.setPadding(AndroidUtilities.dp(12), 0, AndroidUtilities.dp(12), 0); recyclerView.setOnItemClickListener((view, position) -> { if (adapter.items.get(position) == selectedItem || changeDayNightView != null) { return; } selectedItem = adapter.items.get(position); isLightDarkChangeAnimation = false; if (selectedItem.chatTheme == null || selectedItem.chatTheme.showAsDefaultStub) { applyTextView.animate().alpha(0f).setDuration(300).start(); resetTextView.animate().alpha(1f).setDuration(300).start(); } else { resetTextView.animate().alpha(0f).setDuration(300).start(); applyTextView.animate().alpha(1f).setDuration(300).start(); } if (selectedItem.chatTheme.showAsDefaultStub) { themeDelegate.setCurrentTheme(null, true, forceDark); } else { themeDelegate.setCurrentTheme(selectedItem.chatTheme, true, forceDark); } adapter.setSelectedItem(position); containerView.postDelayed(new Runnable() { @Override public void run() { RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); if (layoutManager != null) { final int targetPosition = position > prevSelectedPosition ? Math.min(position + 1, adapter.items.size() - 1) : Math.max(position - 1, 0); scroller.setTargetPosition(targetPosition); layoutManager.startSmoothScroll(scroller); } prevSelectedPosition = position; } }, 100); for (int i = 0; i < recyclerView.getChildCount(); i++) { ThemeSmallPreviewView child = (ThemeSmallPreviewView) recyclerView.getChildAt(i); if (child != view) { child.cancelAnimation(); } } if (!adapter.items.get(position).chatTheme.showAsDefaultStub) { ((ThemeSmallPreviewView) view).playEmojiAnimation(); } }); progressView = new FlickerLoadingView(getContext(), resourcesProvider); progressView.setViewType(FlickerLoadingView.CHAT_THEMES_TYPE); progressView.setVisibility(View.VISIBLE); rootLayout.addView(progressView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 104, Gravity.START, 0, 44, 0, 0)); rootLayout.addView(recyclerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 104, Gravity.START, 0, 44, 0, 0)); applyButton = new View(getContext()); applyButton.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), getThemedColor(Theme.key_featuredStickers_addButton), getThemedColor(Theme.key_featuredStickers_addButtonPressed))); applyButton.setEnabled(false); applyButton.setOnClickListener((view) -> applySelectedTheme()); rootLayout.addView(applyButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 16, 162, 16, 16)); resetTextView = new TextView(getContext()); resetTextView.setAlpha(0f); resetTextView.setEllipsize(TextUtils.TruncateAt.END); resetTextView.setGravity(Gravity.CENTER); resetTextView.setLines(1); resetTextView.setSingleLine(true); resetTextView.setText(themeDelegate.getCurrentTheme() == null ? LocaleController.getString("DoNoSetTheme", R.string.DoNoSetTheme) : LocaleController.getString("ChatResetTheme", R.string.ChatResetTheme)); resetTextView.setTextColor(getThemedColor(Theme.key_featuredStickers_buttonText)); resetTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); resetTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); resetTextView.setVisibility(View.INVISIBLE); rootLayout.addView(resetTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 16, 162, 16, 16)); applyTextView = new TextView(getContext()); applyTextView.setEllipsize(TextUtils.TruncateAt.END); applyTextView.setGravity(Gravity.CENTER); applyTextView.setLines(1); applyTextView.setSingleLine(true); applyTextView.setText(LocaleController.getString("ChatApplyTheme", R.string.ChatApplyTheme)); applyTextView.setTextColor(getThemedColor(Theme.key_featuredStickers_buttonText)); applyTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15); applyTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf")); applyTextView.setVisibility(View.INVISIBLE); rootLayout.addView(applyTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.START, 16, 162, 16, 16)); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ChatThemeController.preloadAllWallpaperThumbs(true); ChatThemeController.preloadAllWallpaperThumbs(false); ChatThemeController.preloadAllWallpaperImages(true); ChatThemeController.preloadAllWallpaperImages(false); NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded); isApplyClicked = false; List cachedThemes = themeDelegate.getCachedThemes(); if (cachedThemes == null || cachedThemes.isEmpty()) { ChatThemeController.requestAllChatThemes(new ResultCallback>() { @Override public void onComplete(List result) { if (result != null && !result.isEmpty()) { themeDelegate.setCachedThemes(result); } onDataLoaded(result); } @Override public void onError(TLRPC.TL_error error) { Toast.makeText(getContext(), error.text, Toast.LENGTH_SHORT).show(); } }, true); } else { onDataLoaded(cachedThemes); } if (chatActivity.getCurrentUser() != null && SharedConfig.dayNightThemeSwitchHintCount > 0 && !chatActivity.getCurrentUser().self) { SharedConfig.updateDayNightThemeSwitchHintCount(SharedConfig.dayNightThemeSwitchHintCount - 1); hintView = new HintView(getContext(), 9, chatActivity.getResourceProvider()); hintView.setVisibility(View.INVISIBLE); hintView.setShowingDuration(5000); hintView.setBottomOffset(-AndroidUtilities.dp(8)); hintView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("ChatThemeDayNightSwitchTooltip", R.string.ChatThemeDayNightSwitchTooltip, chatActivity.getCurrentUser().first_name))); AndroidUtilities.runOnUIThread(() -> { hintView.showForView(darkThemeView, true); }, 1500); container.addView(hintView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 10, 0, 10, 0)); } } @Override public void onContainerTranslationYChanged(float y) { if (hintView != null) { hintView.hide(); } } @Override public void onBackPressed() { close(); } @Override public void dismiss() { NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.emojiLoaded); super.dismiss(); if (!isApplyClicked) { themeDelegate.setCurrentTheme(originalTheme, true, originalIsDark); } } public void close() { if (hasChanges()) { AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), resourcesProvider); builder.setTitle(LocaleController.getString("ChatThemeSaveDialogTitle", R.string.ChatThemeSaveDialogTitle)); builder.setSubtitle(LocaleController.getString("ChatThemeSaveDialogText", R.string.ChatThemeSaveDialogText)); builder.setPositiveButton(LocaleController.getString("ChatThemeSaveDialogApply", R.string.ChatThemeSaveDialogApply), (dialogInterface, i) -> applySelectedTheme()); builder.setNegativeButton(LocaleController.getString("ChatThemeSaveDialogDiscard", R.string.ChatThemeSaveDialogDiscard), (dialogInterface, i) -> dismiss()); builder.show(); } else { dismiss(); } } @SuppressLint("NotifyDataSetChanged") @Override public void didReceivedNotification(int id, int account, Object... args) { if (id == NotificationCenter.emojiLoaded) { adapter.notifyDataSetChanged(); } } @Override public ArrayList getThemeDescriptions() { ThemeDescription.ThemeDescriptionDelegate descriptionDelegate = new ThemeDescription.ThemeDescriptionDelegate() { private boolean isAnimationStarted = false; @Override public void onAnimationProgress(float progress) { if (progress == 0f && !isAnimationStarted) { onAnimationStart(); isAnimationStarted = true; } setDarkButtonColor(getThemedColor(Theme.key_featuredStickers_addButton)); setOverlayNavBarColor(getThemedColor(Theme.key_dialogBackground)); if (isLightDarkChangeAnimation) { setItemsAnimationProgress(progress); } if (progress == 1f && isAnimationStarted) { isLightDarkChangeAnimation = false; onAnimationEnd(); isAnimationStarted = false; } } @Override public void didSetColor() { } }; ArrayList themeDescriptions = new ArrayList<>(); themeDescriptions.add(new ThemeDescription(null, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, new Drawable[]{shadowDrawable}, descriptionDelegate, Theme.key_dialogBackground)); themeDescriptions.add(new ThemeDescription(titleView, ThemeDescription.FLAG_TEXTCOLOR, null, null, null, null, Theme.key_dialogTextBlack)); themeDescriptions.add(new ThemeDescription(recyclerView, ThemeDescription.FLAG_CELLBACKGROUNDCOLOR, new Class[]{ThemeSmallPreviewView.class}, null, null, null, Theme.key_dialogBackgroundGray)); themeDescriptions.add(new ThemeDescription(applyButton, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_featuredStickers_addButton)); themeDescriptions.add(new ThemeDescription(applyButton, ThemeDescription.FLAG_BACKGROUNDFILTER | ThemeDescription.FLAG_DRAWABLESELECTEDSTATE, null, null, null, null, Theme.key_featuredStickers_addButtonPressed)); for (ThemeDescription description : themeDescriptions) { description.resourcesProvider = themeDelegate; } return themeDescriptions; } @SuppressLint("NotifyDataSetChanged") public void setupLightDarkTheme(boolean isDark) { if (changeDayNightViewAnimator != null) { changeDayNightViewAnimator.cancel(); } FrameLayout decorView1 = (FrameLayout) chatActivity.getParentActivity().getWindow().getDecorView(); FrameLayout decorView2 = (FrameLayout) getWindow().getDecorView(); Bitmap bitmap = Bitmap.createBitmap(decorView2.getWidth(), decorView2.getHeight(), Bitmap.Config.ARGB_8888); Canvas bitmapCanvas = new Canvas(bitmap); darkThemeView.setAlpha(0f); decorView1.draw(bitmapCanvas); decorView2.draw(bitmapCanvas); darkThemeView.setAlpha(1f); Paint xRefPaint = new Paint(Paint.ANTI_ALIAS_FLAG); xRefPaint.setColor(0xff000000); xRefPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); Paint bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); bitmapPaint.setFilterBitmap(true); int[] position = new int[2]; darkThemeView.getLocationInWindow(position); float x = position[0]; float y = position[1]; float cx = x + darkThemeView.getMeasuredWidth() / 2f; float cy = y + darkThemeView.getMeasuredHeight() / 2f; float r = Math.max(bitmap.getHeight(), bitmap.getWidth()) * 0.9f; Shader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); bitmapPaint.setShader(bitmapShader); changeDayNightView = new View(getContext()) { @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (isDark) { if (changeDayNightViewProgress > 0f) { bitmapCanvas.drawCircle(cx, cy, r * changeDayNightViewProgress, xRefPaint); } canvas.drawBitmap(bitmap, 0, 0, bitmapPaint); } else { canvas.drawCircle(cx, cy, r * (1f - changeDayNightViewProgress), bitmapPaint); } canvas.save(); canvas.translate(x, y); darkThemeView.draw(canvas); canvas.restore(); } }; changeDayNightViewProgress = 0f; changeDayNightViewAnimator = ValueAnimator.ofFloat(0, 1f); changeDayNightViewAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { changeDayNightViewProgress = (float) valueAnimator.getAnimatedValue(); changeDayNightView.invalidate(); } }); changeDayNightViewAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (changeDayNightView != null) { if (changeDayNightView.getParent() != null) { ((ViewGroup) changeDayNightView.getParent()).removeView(changeDayNightView); } changeDayNightView = null; } changeDayNightViewAnimator = null; super.onAnimationEnd(animation); } }); changeDayNightViewAnimator.setDuration(400); changeDayNightViewAnimator.setInterpolator(Easings.easeInOutQuad); changeDayNightViewAnimator.start(); decorView2.addView(changeDayNightView, new ViewGroup.LayoutParams(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT)); AndroidUtilities.runOnUIThread(() -> { if (adapter == null || adapter.items == null) { return; } setForceDark(isDark, true); if (selectedItem != null) { isLightDarkChangeAnimation = true; if (selectedItem.chatTheme.showAsDefaultStub) { themeDelegate.setCurrentTheme(null, false, isDark); } else { themeDelegate.setCurrentTheme(selectedItem.chatTheme, false, isDark); } } if (adapter != null && adapter.items != null) { for (int i = 0; i < adapter.items.size(); i++) { adapter.items.get(i).themeIndex = isDark ? 1 : 0; } adapter.notifyDataSetChanged(); } }); } @Override protected boolean onContainerTouchEvent(MotionEvent event) { if (event == null || !hasChanges()) { return false; } int x = (int) event.getX(); int y = (int) event.getY(); boolean touchInsideContainer = y >= containerView.getTop() && x >= containerView.getLeft() && x <= containerView.getRight(); if (touchInsideContainer) { return false; } else { chatActivity.getFragmentView().dispatchTouchEvent(event); return true; } } private void onDataLoaded(List result) { if (result == null || result.isEmpty()) { return; } ChatThemeItem noThemeItem = new ChatThemeItem(result.get(0)); List items = new ArrayList<>(result.size()); EmojiThemes currentTheme = themeDelegate.getCurrentTheme(); items.add(0, noThemeItem); selectedItem = noThemeItem; for (int i = 1; i < result.size(); ++i) { EmojiThemes chatTheme = result.get(i); ChatThemeItem item = new ChatThemeItem(chatTheme); chatTheme.loadPreviewColors(currentAccount); item.themeIndex = forceDark ? 1 : 0; items.add(item); } adapter.setItems(items); applyButton.setEnabled(true); applyTextView.setAlpha(0f); resetTextView.setAlpha(0f); recyclerView.setAlpha(0f); applyTextView.setVisibility(View.VISIBLE); resetTextView.setVisibility(View.VISIBLE); darkThemeView.setVisibility(View.VISIBLE); boolean showRestText = false; if (currentTheme != null) { int selectedPosition = -1; for (int i = 0; i != items.size(); ++i) { if (items.get(i).chatTheme.getEmoticon().equals(currentTheme.getEmoticon())) { selectedItem = items.get(i); selectedPosition = i; break; } } if (selectedPosition != -1) { prevSelectedPosition = selectedPosition; adapter.setSelectedItem(selectedPosition); if (selectedPosition > 0 && selectedPosition < items.size() / 2) { selectedPosition -= 1; } int finalSelectedPosition = Math.min(selectedPosition, adapter.items.size() - 1); layoutManager.scrollToPositionWithOffset(finalSelectedPosition, 0); } } else { showRestText = true; adapter.setSelectedItem(0); layoutManager.scrollToPositionWithOffset(0, 0); } recyclerView.animate().alpha(1f).setDuration(150).start(); resetTextView.animate().alpha(showRestText ? 1f : 0).setDuration(150).start(); applyTextView.animate().alpha(showRestText ? 0f : 1).setDuration(150).start(); progressView.animate().alpha(0f).setListener(new HideViewAfterAnimation(progressView)).setDuration(150).start(); } private void onAnimationStart() { if (adapter != null && adapter.items != null) { for (ChatThemeItem item : adapter.items) { item.themeIndex = forceDark ? 1 : 0; } } if (!isLightDarkChangeAnimation) { setItemsAnimationProgress(1.0f); } } private void onAnimationEnd() { isLightDarkChangeAnimation = false; } private void setDarkButtonColor(int color) { darkThemeDrawable.setLayerColor("Sunny.**", color); darkThemeDrawable.setLayerColor("Path.**", color); darkThemeDrawable.setLayerColor("Path 10.**", color); darkThemeDrawable.setLayerColor("Path 11.**", color); } private void setForceDark(boolean isDark, boolean playAnimation) { useLightNavBar = isDark; useLightStatusBar = isDark; if (forceDark == isDark) { return; } forceDark = isDark; if (playAnimation) { darkThemeDrawable.setCustomEndFrame(isDark ? darkThemeDrawable.getFramesCount() : 0); darkThemeView.playAnimation(); } else { darkThemeDrawable.setCurrentFrame(isDark ? darkThemeDrawable.getFramesCount() - 1 : 0, false, true); darkThemeView.invalidate(); } } private void setItemsAnimationProgress(float progress) { for (int i = 0; i < adapter.getItemCount(); ++i) { adapter.items.get(i).animationProgress = progress; } } private void applySelectedTheme() { Bulletin bulletin = null; EmojiThemes newTheme = selectedItem.chatTheme; if (newTheme.showAsDefaultStub) { newTheme = null; } if (selectedItem != null && newTheme != originalTheme) { EmojiThemes chatTheme = selectedItem.chatTheme; String emoticon = (chatTheme != null && !chatTheme.showAsDefaultStub) ? chatTheme.getEmoticon() : null; ChatThemeController.getInstance(currentAccount).setDialogTheme(chatActivity.getDialogId(), emoticon, true); if (chatTheme != null && !chatTheme.showAsDefaultStub) { themeDelegate.setCurrentTheme(chatTheme, true, originalIsDark); } else { themeDelegate.setCurrentTheme(null, true, originalIsDark); } isApplyClicked = true; TLRPC.User user = chatActivity.getCurrentUser(); if (user != null && !user.self) { boolean themeDisabled = false; if (TextUtils.isEmpty(emoticon)) { themeDisabled = true; emoticon = "❌"; } TLRPC.Document document = emoticon != null ? MediaDataController.getInstance(currentAccount).getEmojiAnimatedSticker(emoticon) : null; StickerSetBulletinLayout layout = new StickerSetBulletinLayout(getContext(), null, StickerSetBulletinLayout.TYPE_EMPTY, document, chatActivity.getResourceProvider()); layout.subtitleTextView.setVisibility(View.GONE); if (themeDisabled) { layout.titleTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("ThemeAlsoDisabledForHint", R.string.ThemeAlsoDisabledForHint, user.first_name))); } else { layout.titleTextView.setText(AndroidUtilities.replaceTags(LocaleController.formatString("ThemeAlsoAppliedForHint", R.string.ThemeAlsoAppliedForHint, user.first_name))); } layout.titleTextView.setTypeface(null); bulletin = Bulletin.make(chatActivity, layout, Bulletin.DURATION_LONG); } } dismiss(); if (bulletin != null) { bulletin.show(); } } private boolean hasChanges() { if (selectedItem == null) { return false; } else { String oldEmoticon = originalTheme != null ? originalTheme.getEmoticon() : null; if (TextUtils.isEmpty(oldEmoticon)) { oldEmoticon = "❌"; } String newEmoticon = selectedItem.chatTheme != null ? selectedItem.chatTheme.getEmoticon() : null; if (TextUtils.isEmpty(newEmoticon)) { newEmoticon = "❌"; } return !Objects.equals(oldEmoticon, newEmoticon); } } @SuppressLint("NotifyDataSetChanged") public static class Adapter extends RecyclerView.Adapter { private final Theme.ResourcesProvider resourcesProvider; public List items; private WeakReference selectedViewRef; private int selectedItemPosition = -1; private final int currentAccount; private final int currentViewType; private HashMap loadingThemes = new HashMap<>(); private HashMap loadingWallpapers = new HashMap<>(); public Adapter(int currentAccount, Theme.ResourcesProvider resourcesProvider, int type) { this.currentViewType = type; this.resourcesProvider = resourcesProvider; this.currentAccount = currentAccount; } @NonNull @Override public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new RecyclerListView.Holder(new ThemeSmallPreviewView(parent.getContext(), currentAccount, resourcesProvider, currentViewType)); } @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { ThemeSmallPreviewView view = (ThemeSmallPreviewView) holder.itemView; Theme.ThemeInfo themeInfo = items.get(position).chatTheme.getThemeInfo(items.get(position).themeIndex); if (themeInfo != null && themeInfo.pathToFile != null && !themeInfo.previewParsed) { File file = new File(themeInfo.pathToFile); boolean fileExists = file.exists(); if (fileExists) { parseTheme(themeInfo); } } boolean animated = true; ChatThemeItem newItem = items.get(position); if (view.chatThemeItem == null || !view.chatThemeItem.chatTheme.getEmoticon().equals(newItem.chatTheme.getEmoticon()) || DrawerProfileCell.switchingTheme || view.lastThemeIndex != newItem.themeIndex) { animated = false; } view.setItem(newItem, animated); view.setSelected(position == selectedItemPosition, animated); if (position == selectedItemPosition) { selectedViewRef = new WeakReference<>(view); } } private boolean parseTheme(Theme.ThemeInfo themeInfo) { if (themeInfo == null || themeInfo.pathToFile == null) { return false; } boolean finished = false; File file = new File(themeInfo.pathToFile); try (FileInputStream stream = new FileInputStream(file)) { int currentPosition = 0; int idx; int read; int linesRead = 0; while ((read = stream.read(ThemesHorizontalListCell.bytes)) != -1) { int previousPosition = currentPosition; int start = 0; for (int a = 0; a < read; a++) { if (ThemesHorizontalListCell.bytes[a] == '\n') { linesRead++; int len = a - start + 1; String line = new String(ThemesHorizontalListCell.bytes, start, len - 1, "UTF-8"); if (line.startsWith("WLS=")) { String wallpaperLink = line.substring(4); Uri uri = Uri.parse(wallpaperLink); themeInfo.slug = uri.getQueryParameter("slug"); themeInfo.pathToWallpaper = new File(ApplicationLoader.getFilesDirFixed(), Utilities.MD5(wallpaperLink) + ".wp").getAbsolutePath(); String mode = uri.getQueryParameter("mode"); if (mode != null) { mode = mode.toLowerCase(); String[] modes = mode.split(" "); if (modes != null && modes.length > 0) { for (int b = 0; b < modes.length; b++) { if ("blur".equals(modes[b])) { themeInfo.isBlured = true; break; } } } } String pattern = uri.getQueryParameter("pattern"); if (!TextUtils.isEmpty(pattern)) { try { String bgColor = uri.getQueryParameter("bg_color"); if (!TextUtils.isEmpty(bgColor)) { themeInfo.patternBgColor = Integer.parseInt(bgColor.substring(0, 6), 16) | 0xff000000; if (bgColor.length() >= 13 && AndroidUtilities.isValidWallChar(bgColor.charAt(6))) { themeInfo.patternBgGradientColor1 = Integer.parseInt(bgColor.substring(7, 13), 16) | 0xff000000; } if (bgColor.length() >= 20 && AndroidUtilities.isValidWallChar(bgColor.charAt(13))) { themeInfo.patternBgGradientColor2 = Integer.parseInt(bgColor.substring(14, 20), 16) | 0xff000000; } if (bgColor.length() == 27 && AndroidUtilities.isValidWallChar(bgColor.charAt(20))) { themeInfo.patternBgGradientColor3 = Integer.parseInt(bgColor.substring(21), 16) | 0xff000000; } } } catch (Exception ignore) { } try { String rotation = uri.getQueryParameter("rotation"); if (!TextUtils.isEmpty(rotation)) { themeInfo.patternBgGradientRotation = Utilities.parseInt(rotation); } } catch (Exception ignore) { } String intensity = uri.getQueryParameter("intensity"); if (!TextUtils.isEmpty(intensity)) { themeInfo.patternIntensity = Utilities.parseInt(intensity); } if (themeInfo.patternIntensity == 0) { themeInfo.patternIntensity = 50; } } } else if (line.startsWith("WPS")) { themeInfo.previewWallpaperOffset = currentPosition + len; finished = true; break; } else { if ((idx = line.indexOf('=')) != -1) { String key = line.substring(0, idx); if (key.equals(Theme.key_chat_inBubble) || key.equals(Theme.key_chat_outBubble) || key.equals(Theme.key_chat_wallpaper) || key.equals(Theme.key_chat_wallpaper_gradient_to1) || key.equals(Theme.key_chat_wallpaper_gradient_to2) || key.equals(Theme.key_chat_wallpaper_gradient_to3)) { String param = line.substring(idx + 1); int value; if (param.length() > 0 && param.charAt(0) == '#') { try { value = Color.parseColor(param); } catch (Exception ignore) { value = Utilities.parseInt(param); } } else { value = Utilities.parseInt(param); } switch (key) { case Theme.key_chat_inBubble: themeInfo.setPreviewInColor(value); break; case Theme.key_chat_outBubble: themeInfo.setPreviewOutColor(value); break; case Theme.key_chat_wallpaper: themeInfo.setPreviewBackgroundColor(value); break; case Theme.key_chat_wallpaper_gradient_to1: themeInfo.previewBackgroundGradientColor1 = value; break; case Theme.key_chat_wallpaper_gradient_to2: themeInfo.previewBackgroundGradientColor2 = value; break; case Theme.key_chat_wallpaper_gradient_to3: themeInfo.previewBackgroundGradientColor3 = value; break; } } } } start += len; currentPosition += len; } } if (finished || previousPosition == currentPosition) { break; } stream.getChannel().position(currentPosition); } } catch (Throwable e) { FileLog.e(e); } if (themeInfo.pathToWallpaper != null && !themeInfo.badWallpaper) { file = new File(themeInfo.pathToWallpaper); if (!file.exists()) { if (!loadingWallpapers.containsKey(themeInfo)) { loadingWallpapers.put(themeInfo, themeInfo.slug); TLRPC.TL_account_getWallPaper req = new TLRPC.TL_account_getWallPaper(); TLRPC.TL_inputWallPaperSlug inputWallPaperSlug = new TLRPC.TL_inputWallPaperSlug(); inputWallPaperSlug.slug = themeInfo.slug; req.wallpaper = inputWallPaperSlug; ConnectionsManager.getInstance(themeInfo.account).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { if (response instanceof TLRPC.TL_wallPaper) { TLRPC.WallPaper wallPaper = (TLRPC.WallPaper) response; String name = FileLoader.getAttachFileName(wallPaper.document); if (!loadingThemes.containsKey(name)) { loadingThemes.put(name, themeInfo); FileLoader.getInstance(themeInfo.account).loadFile(wallPaper.document, wallPaper, 1, 1); } } else { themeInfo.badWallpaper = true; } })); } return false; } } themeInfo.previewParsed = true; return true; } @Override public int getItemCount() { return items == null ? 0 : items.size(); } public void setItems(List newItems) { items = newItems; notifyDataSetChanged(); } public void setSelectedItem(int position) { if (selectedItemPosition == position) { return; } if (selectedItemPosition >= 0) { notifyItemChanged(selectedItemPosition); ThemeSmallPreviewView view = selectedViewRef == null ? null : selectedViewRef.get(); if (view != null) { view.setSelected(false); } } selectedItemPosition = position; notifyItemChanged(selectedItemPosition); } } public static class ChatThemeItem { public final EmojiThemes chatTheme; public Drawable previewDrawable; public int themeIndex; public boolean isSelected; public float animationProgress = 1f; public Bitmap icon; public ChatThemeItem(EmojiThemes chatTheme) { this.chatTheme = chatTheme; } } @Override public void show() { super.show(); resetTextView.setText(themeDelegate.getCurrentTheme() == null ? LocaleController.getString("DoNoSetTheme", R.string.DoNoSetTheme) : LocaleController.getString("ChatResetTheme", R.string.ChatResetTheme)); } }