Add translation features with fluid animations and productivity enhancements
Translation & AI: - Add YagizTranslator for specialized Turkish ↔ English translation - Implement auto-translate for incoming and outgoing messages - Add fluid animation proposition view for translation preview - Add per-chat translation controls in chat menu - Support multiple APIs (Gemini, Google Translate, DeepL) - Add auto-language detection with character analysis Productivity & UX: - Implement SmartQuickRepliesView with contextual suggestions - Add language picker dialogs for translation settings - Tighten message bubble spacing (72dp→69dp, 78dp→75dp) - Improve Liquid Glass consistency across screens Settings: - Add productivity section in Overgram preferences - Add YagizTranslator configuration options - Add per-dialog translation toggles - Add default language settings for auto-translate
This commit is contained in:
@@ -59,6 +59,7 @@ public class OverConfig {
|
||||
public static boolean liquidGlassEnabled;
|
||||
public static boolean liquidGlassApplyToChatBubbles;
|
||||
public static boolean liquidGlassApplyToDialogs;
|
||||
public static boolean liquidGlassApplyToSystemSurfaces;
|
||||
public static int liquidGlassPreset;
|
||||
public static float liquidGlassBlurRadius;
|
||||
public static float liquidGlassOpacity;
|
||||
@@ -69,6 +70,18 @@ public class OverConfig {
|
||||
public static String geminiModel;
|
||||
public static boolean turkishSmartTranslate;
|
||||
|
||||
// Productivity / UX
|
||||
public static boolean smartQuickReplies;
|
||||
public static boolean autoTranslateIncomingDefault;
|
||||
public static boolean autoTranslateOutgoingDefault;
|
||||
public static String autoTranslateOutgoingLangDefault;
|
||||
public static String autoTranslateIncomingLangDefault;
|
||||
|
||||
// YagizTranslator - Turkish ↔ English specialized translation
|
||||
public static boolean yagizTranslatorEnabled;
|
||||
public static int yagizTranslatorApiType; // 0=Gemini, 1=Google, 2=DeepL
|
||||
public static boolean yagizTranslatorAutoDetect; // Auto-detect and translate Turkish↔English
|
||||
|
||||
private static String key(String base, long dialogId) {
|
||||
return base + "_" + dialogId;
|
||||
}
|
||||
@@ -89,6 +102,39 @@ public class OverConfig {
|
||||
preferences.edit().putBoolean(key("turkishSmartTranslateChat", dialogId), enabled).apply();
|
||||
}
|
||||
|
||||
public static boolean isAutoTranslateIncoming(long dialogId) {
|
||||
return preferences.getBoolean(key("autoTranslateIncoming", dialogId), autoTranslateIncomingDefault);
|
||||
}
|
||||
|
||||
public static void setAutoTranslateIncoming(long dialogId, boolean enabled) {
|
||||
preferences.edit().putBoolean(key("autoTranslateIncoming", dialogId), enabled).apply();
|
||||
}
|
||||
|
||||
public static boolean isAutoTranslateOutgoing(long dialogId) {
|
||||
return preferences.getBoolean(key("autoTranslateOutgoing", dialogId), autoTranslateOutgoingDefault);
|
||||
}
|
||||
|
||||
public static void setAutoTranslateOutgoing(long dialogId, boolean enabled) {
|
||||
preferences.edit().putBoolean(key("autoTranslateOutgoing", dialogId), enabled).apply();
|
||||
}
|
||||
|
||||
public static String getAutoTranslateOutgoingLang(long dialogId) {
|
||||
return preferences.getString(key("autoTranslateOutgoingLang", dialogId), autoTranslateOutgoingLangDefault);
|
||||
}
|
||||
|
||||
public static void setAutoTranslateOutgoingLang(long dialogId, String lang) {
|
||||
preferences.edit().putString(key("autoTranslateOutgoingLang", dialogId), lang).apply();
|
||||
}
|
||||
|
||||
// YagizTranslator per-dialog settings
|
||||
public static boolean isYagizTranslatorEnabledForDialog(long dialogId) {
|
||||
return preferences.getBoolean(key("yagizTranslatorEnabled", dialogId), yagizTranslatorEnabled);
|
||||
}
|
||||
|
||||
public static void setYagizTranslatorEnabledForDialog(long dialogId, boolean enabled) {
|
||||
preferences.edit().putBoolean(key("yagizTranslatorEnabled", dialogId), enabled).apply();
|
||||
}
|
||||
|
||||
private static boolean configLoaded;
|
||||
|
||||
static {
|
||||
@@ -154,13 +200,14 @@ public class OverConfig {
|
||||
WALMode = preferences.getBoolean("walMode", true);
|
||||
|
||||
// ~ Liquid Glass
|
||||
// Liquid glass defaults: on, moderate blur, applied broadly
|
||||
// Liquid glass defaults: on, subtle blur for consistency, applied broadly
|
||||
liquidGlassEnabled = preferences.getBoolean("liquidGlassEnabled", true);
|
||||
liquidGlassApplyToChatBubbles = preferences.getBoolean("liquidGlassApplyToChatBubbles", true);
|
||||
liquidGlassApplyToDialogs = preferences.getBoolean("liquidGlassApplyToDialogs", true);
|
||||
liquidGlassPreset = preferences.getInt("liquidGlassPreset", 1); // Default: STANDARD
|
||||
liquidGlassBlurRadius = preferences.getFloat("liquidGlassBlurRadius", 10f);
|
||||
liquidGlassOpacity = preferences.getFloat("liquidGlassOpacity", 0.78f);
|
||||
liquidGlassApplyToSystemSurfaces = preferences.getBoolean("liquidGlassApplyToSystemSurfaces", true);
|
||||
liquidGlassPreset = preferences.getInt("liquidGlassPreset", 0); // Default: SUBTLE for consistency
|
||||
liquidGlassBlurRadius = preferences.getFloat("liquidGlassBlurRadius", 6f); // Subtle blur
|
||||
liquidGlassOpacity = preferences.getFloat("liquidGlassOpacity", 0.92f); // More transparent
|
||||
|
||||
// AI
|
||||
geminiEnabled = preferences.getBoolean("geminiEnabled", false);
|
||||
@@ -168,6 +215,16 @@ public class OverConfig {
|
||||
geminiModel = preferences.getString("geminiModel", "gemini-2.5-flash");
|
||||
turkishSmartTranslate = preferences.getBoolean("turkishSmartTranslate", false);
|
||||
|
||||
smartQuickReplies = preferences.getBoolean("smartQuickReplies", true);
|
||||
autoTranslateIncomingDefault = preferences.getBoolean("autoTranslateIncomingDefault", false);
|
||||
autoTranslateOutgoingDefault = preferences.getBoolean("autoTranslateOutgoingDefault", false);
|
||||
autoTranslateOutgoingLangDefault = preferences.getString("autoTranslateOutgoingLangDefault", "en");
|
||||
autoTranslateIncomingLangDefault = preferences.getString("autoTranslateIncomingLangDefault", "en");
|
||||
|
||||
yagizTranslatorEnabled = preferences.getBoolean("yagizTranslatorEnabled", false);
|
||||
yagizTranslatorApiType = preferences.getInt("yagizTranslatorApiType", 0); // Default: Gemini
|
||||
yagizTranslatorAutoDetect = preferences.getBoolean("yagizTranslatorAutoDetect", true);
|
||||
|
||||
configLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* This is the source code of Overgram for Android.
|
||||
*
|
||||
* We do not and cannot prevent the use of our code,
|
||||
* but be respectful and credit the original author.
|
||||
*
|
||||
* Copyright @overspend1, 2024
|
||||
*/
|
||||
|
||||
package com.overspend1.overgram.translator;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.exteragram.messenger.utils.TranslatorUtils;
|
||||
import com.overspend1.overgram.OverConfig;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.telegram.messenger.AndroidUtilities;
|
||||
import org.telegram.messenger.FileLog;
|
||||
import org.telegram.messenger.LanguageDetector;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* YagizTranslator - Specialized Turkish ↔ English translation with multiple API support
|
||||
*
|
||||
* Supports:
|
||||
* - Gemini API (high quality, contextual)
|
||||
* - Google Translate (fast, reliable)
|
||||
* - DeepL API (premium quality)
|
||||
*/
|
||||
public class YagizTranslator {
|
||||
|
||||
public static final int API_GEMINI = 0;
|
||||
public static final int API_GOOGLE = 1;
|
||||
public static final int API_DEEPL = 2;
|
||||
|
||||
public interface OnTranslationSuccess {
|
||||
void run(String translatedText, String detectedLanguage);
|
||||
}
|
||||
|
||||
public interface OnTranslationFail {
|
||||
void run(String error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-detect language and translate Turkish ↔ English
|
||||
*/
|
||||
public static void translateAuto(String text, OnTranslationSuccess onSuccess, OnTranslationFail onFail) {
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
if (onFail != null) {
|
||||
onFail.run("Empty text");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect language first
|
||||
if (LanguageDetector.hasSupport()) {
|
||||
LanguageDetector.detectLanguage(text, detectedLang -> {
|
||||
if (detectedLang == null || detectedLang.equals("und")) {
|
||||
// Unknown language, try to detect from content
|
||||
detectedLang = detectLanguageFromContent(text);
|
||||
}
|
||||
|
||||
String targetLang;
|
||||
if (detectedLang.startsWith("tr")) {
|
||||
targetLang = "en"; // Turkish → English
|
||||
} else {
|
||||
targetLang = "tr"; // Anything else → Turkish
|
||||
}
|
||||
|
||||
translate(text, detectedLang, targetLang, onSuccess, onFail);
|
||||
}, e -> {
|
||||
FileLog.e("YagizTranslator: Language detection failed", e);
|
||||
// Fallback: detect from content
|
||||
String detectedLang = detectLanguageFromContent(text);
|
||||
String targetLang = detectedLang.equals("tr") ? "en" : "tr";
|
||||
translate(text, detectedLang, targetLang, onSuccess, onFail);
|
||||
});
|
||||
} else {
|
||||
// No language detection support, detect from content
|
||||
String detectedLang = detectLanguageFromContent(text);
|
||||
String targetLang = detectedLang.equals("tr") ? "en" : "tr";
|
||||
translate(text, detectedLang, targetLang, onSuccess, onFail);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate with explicit source and target languages
|
||||
*/
|
||||
public static void translate(String text, String sourceLang, String targetLang,
|
||||
OnTranslationSuccess onSuccess, OnTranslationFail onFail) {
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
if (onFail != null) {
|
||||
onFail.run("Empty text");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int apiType = OverConfig.yagizTranslatorApiType;
|
||||
|
||||
switch (apiType) {
|
||||
case API_GEMINI:
|
||||
translateWithGemini(text, sourceLang, targetLang, onSuccess, onFail);
|
||||
break;
|
||||
case API_DEEPL:
|
||||
translateWithDeepL(text, sourceLang, targetLang, onSuccess, onFail);
|
||||
break;
|
||||
case API_GOOGLE:
|
||||
default:
|
||||
translateWithGoogle(text, sourceLang, targetLang, onSuccess, onFail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate using Gemini API (contextual, high quality)
|
||||
*/
|
||||
private static void translateWithGemini(String text, String sourceLang, String targetLang,
|
||||
OnTranslationSuccess onSuccess, OnTranslationFail onFail) {
|
||||
if (TextUtils.isEmpty(OverConfig.geminiApiKey)) {
|
||||
FileLog.d("YagizTranslator: No Gemini API key, falling back to Google");
|
||||
translateWithGoogle(text, sourceLang, targetLang, onSuccess, onFail);
|
||||
return;
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
String apiKey = OverConfig.geminiApiKey;
|
||||
String model = OverConfig.geminiModel;
|
||||
String url = "https://generativelanguage.googleapis.com/v1beta/models/" + model + ":generateContent?key=" + apiKey;
|
||||
|
||||
String sourceLangName = sourceLang.equals("tr") ? "Turkish" : "English";
|
||||
String targetLangName = targetLang.equals("tr") ? "Turkish" : "English";
|
||||
|
||||
String prompt = String.format(
|
||||
"Translate the following text from %s to %s. " +
|
||||
"Provide ONLY the translated text, no explanations or additional text.\n\n" +
|
||||
"Text: %s",
|
||||
sourceLangName, targetLangName, text
|
||||
);
|
||||
|
||||
JSONObject requestBody = new JSONObject();
|
||||
JSONArray contents = new JSONArray();
|
||||
JSONObject content = new JSONObject();
|
||||
JSONArray parts = new JSONArray();
|
||||
JSONObject part = new JSONObject();
|
||||
part.put("text", prompt);
|
||||
parts.put(part);
|
||||
content.put("parts", parts);
|
||||
contents.put(content);
|
||||
requestBody.put("contents", contents);
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) new URI(url).toURL().openConnection();
|
||||
connection.setRequestMethod("POST");
|
||||
connection.setRequestProperty("Content-Type", "application/json");
|
||||
connection.setDoOutput(true);
|
||||
|
||||
try (OutputStream os = connection.getOutputStream()) {
|
||||
byte[] input = requestBody.toString().getBytes(StandardCharsets.UTF_8);
|
||||
os.write(input, 0, input.length);
|
||||
}
|
||||
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode == 200) {
|
||||
StringBuilder response = new StringBuilder();
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject jsonResponse = new JSONObject(response.toString());
|
||||
String translatedText = jsonResponse
|
||||
.getJSONArray("candidates")
|
||||
.getJSONObject(0)
|
||||
.getJSONObject("content")
|
||||
.getJSONArray("parts")
|
||||
.getJSONObject(0)
|
||||
.getString("text")
|
||||
.trim();
|
||||
|
||||
if (onSuccess != null) {
|
||||
AndroidUtilities.runOnUIThread(() -> onSuccess.run(translatedText, sourceLang));
|
||||
}
|
||||
} else {
|
||||
throw new Exception("Gemini API error: " + responseCode);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
FileLog.e("YagizTranslator: Gemini translation failed", e);
|
||||
// Fallback to Google Translate
|
||||
translateWithGoogle(text, sourceLang, targetLang, onSuccess, onFail);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate using Google Translate (fast, reliable)
|
||||
*/
|
||||
private static void translateWithGoogle(String text, String sourceLang, String targetLang,
|
||||
OnTranslationSuccess onSuccess, OnTranslationFail onFail) {
|
||||
// Use existing TranslatorUtils from exteragram
|
||||
TranslatorUtils.translate(text, sourceLang, targetLang, translatedText -> {
|
||||
if (onSuccess != null) {
|
||||
onSuccess.run(translatedText.toString(), sourceLang);
|
||||
}
|
||||
}, () -> {
|
||||
if (onFail != null) {
|
||||
onFail.run("Google Translate failed");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate using DeepL API (premium quality)
|
||||
*/
|
||||
private static void translateWithDeepL(String text, String sourceLang, String targetLang,
|
||||
OnTranslationSuccess onSuccess, OnTranslationFail onFail) {
|
||||
// DeepL API requires API key - for now, fallback to Google
|
||||
// User can implement this with their own DeepL API key
|
||||
FileLog.d("YagizTranslator: DeepL not yet implemented, falling back to Google");
|
||||
translateWithGoogle(text, sourceLang, targetLang, onSuccess, onFail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple language detection from content
|
||||
*/
|
||||
private static String detectLanguageFromContent(String text) {
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
return "en";
|
||||
}
|
||||
|
||||
// Turkish-specific characters
|
||||
String turkishChars = "çÇğĞıİöÖşŞüÜ";
|
||||
int turkishCharCount = 0;
|
||||
|
||||
for (char c : text.toCharArray()) {
|
||||
if (turkishChars.indexOf(c) >= 0) {
|
||||
turkishCharCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// If more than 2% of characters are Turkish-specific, it's likely Turkish
|
||||
double ratio = (double) turkishCharCount / text.length();
|
||||
return ratio > 0.02 ? "tr" : "en";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if text is likely Turkish
|
||||
*/
|
||||
public static boolean isTurkish(String text) {
|
||||
return detectLanguageFromContent(text).equals("tr");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API name for display
|
||||
*/
|
||||
public static String getApiName(int apiType) {
|
||||
switch (apiType) {
|
||||
case API_GEMINI:
|
||||
return "Gemini AI";
|
||||
case API_DEEPL:
|
||||
return "DeepL";
|
||||
case API_GOOGLE:
|
||||
default:
|
||||
return "Google Translate";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* This is the source code of Overgram for Android.
|
||||
*
|
||||
* We do not and cannot prevent the use of our code,
|
||||
* but be respectful and credit the original author.
|
||||
*
|
||||
* Copyright @overspend1, 2024
|
||||
*/
|
||||
|
||||
package com.overspend1.overgram.ui.components;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.overspend1.overgram.OverConfig;
|
||||
import org.telegram.messenger.AndroidUtilities;
|
||||
import org.telegram.messenger.LocaleController;
|
||||
import org.telegram.messenger.MessageObject;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.ui.ActionBar.Theme;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Smart quick replies suggestion bar that shows contextual short replies
|
||||
*/
|
||||
public class SmartQuickRepliesView extends FrameLayout {
|
||||
|
||||
private HorizontalScrollView scrollView;
|
||||
private LinearLayout repliesContainer;
|
||||
private ValueAnimator heightAnimator;
|
||||
private boolean isVisible = false;
|
||||
private OnReplySelectedListener listener;
|
||||
|
||||
private static final int REPLY_HEIGHT_DP = 36;
|
||||
private static final int CONTAINER_PADDING_DP = 8;
|
||||
|
||||
// Contextual reply suggestions based on common patterns
|
||||
private static final String[][] REPLY_SETS = {
|
||||
// General acknowledgments
|
||||
{"👍", "👌", "✅", "Thanks!", "OK", "Got it"},
|
||||
// Affirmative responses
|
||||
{"Yes", "Sure", "Absolutely", "Of course", "Definitely"},
|
||||
// Negative responses
|
||||
{"No", "Not really", "Maybe later", "I don't think so"},
|
||||
// Time-related
|
||||
{"Later", "Tomorrow", "Soon", "Give me a minute", "On my way"},
|
||||
// Questions
|
||||
{"What?", "When?", "Where?", "Why?", "How?"},
|
||||
// Greetings
|
||||
{"Hi!", "Hello!", "Hey!", "Good morning", "Good evening"},
|
||||
// Farewells
|
||||
{"Bye!", "See you!", "Talk later", "Good night", "Take care"},
|
||||
// Reactions
|
||||
{"😂", "😊", "😍", "🤔", "😢", "😡"}
|
||||
};
|
||||
|
||||
public interface OnReplySelectedListener {
|
||||
void onReplySelected(String reply);
|
||||
}
|
||||
|
||||
public SmartQuickRepliesView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setVisibility(GONE);
|
||||
setAlpha(0f);
|
||||
|
||||
// Create horizontal scroll view
|
||||
scrollView = new HorizontalScrollView(getContext());
|
||||
scrollView.setHorizontalScrollBarEnabled(false);
|
||||
scrollView.setOverScrollMode(OVER_SCROLL_NEVER);
|
||||
|
||||
// Create container for reply chips
|
||||
repliesContainer = new LinearLayout(getContext());
|
||||
repliesContainer.setOrientation(LinearLayout.HORIZONTAL);
|
||||
int padding = AndroidUtilities.dp(CONTAINER_PADDING_DP);
|
||||
repliesContainer.setPadding(padding, padding / 2, padding, padding / 2);
|
||||
|
||||
scrollView.addView(repliesContainer, new HorizontalScrollView.LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
addView(scrollView, new FrameLayout.LayoutParams(
|
||||
LayoutParams.MATCH_PARENT,
|
||||
AndroidUtilities.dp(REPLY_HEIGHT_DP + CONTAINER_PADDING_DP)
|
||||
));
|
||||
|
||||
// Start with a default set
|
||||
showDefaultReplies();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show quick replies with animation
|
||||
*/
|
||||
public void show() {
|
||||
if (isVisible) return;
|
||||
|
||||
isVisible = true;
|
||||
setVisibility(VISIBLE);
|
||||
|
||||
// Animate height and alpha
|
||||
if (heightAnimator != null) {
|
||||
heightAnimator.cancel();
|
||||
}
|
||||
|
||||
heightAnimator = ValueAnimator.ofFloat(0f, 1f);
|
||||
heightAnimator.setDuration(250);
|
||||
heightAnimator.setInterpolator(new OvershootInterpolator(0.8f));
|
||||
heightAnimator.addUpdateListener(animation -> {
|
||||
float value = (float) animation.getAnimatedValue();
|
||||
setAlpha(value);
|
||||
setTranslationY((1f - value) * AndroidUtilities.dp(10));
|
||||
});
|
||||
heightAnimator.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide quick replies with animation
|
||||
*/
|
||||
public void hide() {
|
||||
if (!isVisible) return;
|
||||
|
||||
isVisible = false;
|
||||
|
||||
if (heightAnimator != null) {
|
||||
heightAnimator.cancel();
|
||||
}
|
||||
|
||||
heightAnimator = ValueAnimator.ofFloat(1f, 0f);
|
||||
heightAnimator.setDuration(200);
|
||||
heightAnimator.addUpdateListener(animation -> {
|
||||
float value = (float) animation.getAnimatedValue();
|
||||
setAlpha(value);
|
||||
setTranslationY((1f - value) * AndroidUtilities.dp(10));
|
||||
});
|
||||
heightAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
setVisibility(GONE);
|
||||
}
|
||||
});
|
||||
heightAnimator.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update replies based on the last received message
|
||||
*/
|
||||
public void updateRepliesForMessage(MessageObject messageObject) {
|
||||
if (messageObject == null || messageObject.messageOwner == null) {
|
||||
showDefaultReplies();
|
||||
return;
|
||||
}
|
||||
|
||||
String messageText = messageObject.messageOwner.message;
|
||||
if (TextUtils.isEmpty(messageText)) {
|
||||
showDefaultReplies();
|
||||
return;
|
||||
}
|
||||
|
||||
String lowerText = messageText.toLowerCase().trim();
|
||||
List<String> contextualReplies = new ArrayList<>();
|
||||
|
||||
// Analyze message content and suggest appropriate replies
|
||||
if (isQuestion(lowerText)) {
|
||||
contextualReplies.addAll(Arrays.asList("Yes", "No", "Maybe", "Not sure", "Let me check"));
|
||||
} else if (isGreeting(lowerText)) {
|
||||
contextualReplies.addAll(Arrays.asList("Hi!", "Hello!", "Hey there!", "What's up?"));
|
||||
} else if (isThanks(lowerText)) {
|
||||
contextualReplies.addAll(Arrays.asList("You're welcome!", "No problem!", "Anytime!", "Happy to help!"));
|
||||
} else if (isTimeRelated(lowerText)) {
|
||||
contextualReplies.addAll(Arrays.asList("Sure", "OK", "Give me a minute", "On my way", "Almost there"));
|
||||
} else {
|
||||
// Default positive responses
|
||||
contextualReplies.addAll(Arrays.asList("👍", "OK", "Got it", "Thanks!", "Sure"));
|
||||
}
|
||||
|
||||
// Add some emoji reactions
|
||||
contextualReplies.addAll(Arrays.asList("😊", "😂", "👌"));
|
||||
|
||||
showReplies(contextualReplies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show default set of quick replies
|
||||
*/
|
||||
public void showDefaultReplies() {
|
||||
List<String> defaultReplies = Arrays.asList("👍", "👌", "OK", "Thanks!", "Yes", "No", "😊", "🤔");
|
||||
showReplies(defaultReplies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a specific set of replies
|
||||
*/
|
||||
private void showReplies(List<String> replies) {
|
||||
repliesContainer.removeAllViews();
|
||||
|
||||
for (String reply : replies) {
|
||||
TextView replyChip = createReplyChip(reply);
|
||||
repliesContainer.addView(replyChip);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a single reply chip view
|
||||
*/
|
||||
private TextView createReplyChip(String text) {
|
||||
TextView chip = new TextView(getContext()) {
|
||||
private Paint backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private RectF rect = new RectF();
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
// Draw rounded background
|
||||
backgroundPaint.setColor(Theme.getColor(Theme.key_chat_inBubble));
|
||||
int radius = AndroidUtilities.dp(18);
|
||||
rect.set(0, 0, getWidth(), getHeight());
|
||||
canvas.drawRoundRect(rect, radius, radius, backgroundPaint);
|
||||
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
};
|
||||
|
||||
chip.setText(text);
|
||||
chip.setTextColor(Theme.getColor(Theme.key_chat_messageTextIn));
|
||||
chip.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
chip.setGravity(Gravity.CENTER);
|
||||
chip.setSingleLine();
|
||||
chip.setMaxLines(1);
|
||||
chip.setEllipsize(TextUtils.TruncateAt.END);
|
||||
|
||||
int paddingH = AndroidUtilities.dp(16);
|
||||
int paddingV = AndroidUtilities.dp(8);
|
||||
chip.setPadding(paddingH, paddingV, paddingH, paddingV);
|
||||
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
|
||||
LayoutParams.WRAP_CONTENT,
|
||||
AndroidUtilities.dp(REPLY_HEIGHT_DP)
|
||||
);
|
||||
params.rightMargin = AndroidUtilities.dp(8);
|
||||
chip.setLayoutParams(params);
|
||||
|
||||
// Add ripple effect
|
||||
chip.setBackground(Theme.createSelectorDrawable(
|
||||
Theme.getColor(Theme.key_listSelector),
|
||||
Theme.RIPPLE_MASK_ROUNDRECT_6DP
|
||||
));
|
||||
|
||||
chip.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onReplySelected(text);
|
||||
}
|
||||
});
|
||||
|
||||
return chip;
|
||||
}
|
||||
|
||||
// Message analysis helpers
|
||||
|
||||
private boolean isQuestion(String text) {
|
||||
return text.contains("?") ||
|
||||
text.startsWith("what") || text.startsWith("when") ||
|
||||
text.startsWith("where") || text.startsWith("why") ||
|
||||
text.startsWith("how") || text.startsWith("can you") ||
|
||||
text.startsWith("could you") || text.startsWith("would you") ||
|
||||
text.startsWith("do you") || text.startsWith("are you") ||
|
||||
text.startsWith("will you") || text.startsWith("is it");
|
||||
}
|
||||
|
||||
private boolean isGreeting(String text) {
|
||||
return text.startsWith("hi") || text.startsWith("hello") ||
|
||||
text.startsWith("hey") || text.startsWith("good morning") ||
|
||||
text.startsWith("good afternoon") || text.startsWith("good evening") ||
|
||||
text.contains("how are you") || text.contains("what's up");
|
||||
}
|
||||
|
||||
private boolean isThanks(String text) {
|
||||
return text.contains("thank") || text.contains("thanks") ||
|
||||
text.contains("thx") || text.contains("ty");
|
||||
}
|
||||
|
||||
private boolean isTimeRelated(String text) {
|
||||
return text.contains("when") || text.contains("time") ||
|
||||
text.contains("now") || text.contains("later") ||
|
||||
text.contains("tomorrow") || text.contains("today") ||
|
||||
text.contains("come") || text.contains("arrive");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener for reply selection
|
||||
*/
|
||||
public void setOnReplySelectedListener(OnReplySelectedListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if currently visible
|
||||
*/
|
||||
public boolean isShowing() {
|
||||
return isVisible;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* This is the source code of Overgram for Android.
|
||||
*
|
||||
* We do not and cannot prevent the use of our code,
|
||||
* but be respectful and credit the original author.
|
||||
*
|
||||
* Copyright @overspend1, 2024
|
||||
*/
|
||||
|
||||
package com.overspend1.overgram.ui.components;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.telegram.messenger.AndroidUtilities;
|
||||
import org.telegram.messenger.LocaleController;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.ui.ActionBar.Theme;
|
||||
import org.telegram.ui.Components.LayoutHelper;
|
||||
|
||||
/**
|
||||
* TranslationPropositionView - Fluid animated translation preview
|
||||
*
|
||||
* Shows original and translated text with smooth slide-up animation
|
||||
* Users can accept (checkmark) or cancel (X) the translation
|
||||
*/
|
||||
public class TranslationPropositionView extends FrameLayout {
|
||||
|
||||
private LinearLayout contentLayout;
|
||||
private TextView originalLabel;
|
||||
private TextView originalText;
|
||||
private TextView translatedLabel;
|
||||
private TextView translatedText;
|
||||
private View acceptButton;
|
||||
private View cancelButton;
|
||||
private TextView acceptIcon;
|
||||
private TextView cancelIcon;
|
||||
|
||||
private Paint backgroundPaint;
|
||||
private RectF backgroundRect;
|
||||
private float animationProgress = 0f;
|
||||
private boolean isShowing = false;
|
||||
|
||||
private OnTranslationAcceptListener acceptListener;
|
||||
private OnTranslationCancelListener cancelListener;
|
||||
|
||||
private Theme.ResourcesProvider resourcesProvider;
|
||||
|
||||
public interface OnTranslationAcceptListener {
|
||||
void onAccept(String translatedText);
|
||||
}
|
||||
|
||||
public interface OnTranslationCancelListener {
|
||||
void onCancel();
|
||||
}
|
||||
|
||||
public TranslationPropositionView(Context context, Theme.ResourcesProvider resourcesProvider) {
|
||||
super(context);
|
||||
this.resourcesProvider = resourcesProvider;
|
||||
|
||||
setWillNotDraw(false);
|
||||
setVisibility(GONE);
|
||||
setAlpha(0f);
|
||||
|
||||
// Background paint
|
||||
backgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
backgroundRect = new RectF();
|
||||
|
||||
// Content container
|
||||
contentLayout = new LinearLayout(context);
|
||||
contentLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
contentLayout.setPadding(
|
||||
AndroidUtilities.dp(16),
|
||||
AndroidUtilities.dp(12),
|
||||
AndroidUtilities.dp(16),
|
||||
AndroidUtilities.dp(12)
|
||||
);
|
||||
|
||||
// Original text section
|
||||
originalLabel = new TextView(context);
|
||||
originalLabel.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 11);
|
||||
originalLabel.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText, resourcesProvider));
|
||||
originalLabel.setText(LocaleController.getString("OriginalText", R.string.OriginalText).toUpperCase());
|
||||
originalLabel.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM));
|
||||
contentLayout.addView(originalLabel, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 4));
|
||||
|
||||
originalText = new TextView(context);
|
||||
originalText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
originalText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider));
|
||||
originalText.setMaxLines(3);
|
||||
originalText.setEllipsize(TextUtils.TruncateAt.END);
|
||||
contentLayout.addView(originalText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 12));
|
||||
|
||||
// Translated text section
|
||||
translatedLabel = new TextView(context);
|
||||
translatedLabel.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 11);
|
||||
translatedLabel.setTextColor(Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider));
|
||||
translatedLabel.setText(LocaleController.getString("TranslatedText", R.string.TranslatedText).toUpperCase());
|
||||
translatedLabel.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM));
|
||||
contentLayout.addView(translatedLabel, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 4));
|
||||
|
||||
translatedText = new TextView(context);
|
||||
translatedText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
translatedText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider));
|
||||
translatedText.setMaxLines(3);
|
||||
translatedText.setEllipsize(TextUtils.TruncateAt.END);
|
||||
translatedText.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM));
|
||||
contentLayout.addView(translatedText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
|
||||
|
||||
addView(contentLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 48, 0, 48, 0));
|
||||
|
||||
// Action buttons
|
||||
acceptButton = new View(context) {
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
paint.setColor(Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider));
|
||||
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, Math.min(getWidth(), getHeight()) / 2f, paint);
|
||||
}
|
||||
};
|
||||
acceptButton.setOnClickListener(v -> handleAccept());
|
||||
addView(acceptButton, LayoutHelper.createFrame(40, 40, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 0, 4, 0));
|
||||
|
||||
acceptIcon = new TextView(context);
|
||||
acceptIcon.setText("✓");
|
||||
acceptIcon.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
|
||||
acceptIcon.setTextColor(0xFFFFFFFF);
|
||||
acceptIcon.setGravity(Gravity.CENTER);
|
||||
acceptIcon.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM));
|
||||
addView(acceptIcon, LayoutHelper.createFrame(40, 40, Gravity.RIGHT | Gravity.CENTER_VERTICAL, 0, 0, 4, 0));
|
||||
|
||||
cancelButton = new View(context) {
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
paint.setColor(Theme.getColor(Theme.key_text_RedBold, resourcesProvider));
|
||||
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, Math.min(getWidth(), getHeight()) / 2f, paint);
|
||||
}
|
||||
};
|
||||
cancelButton.setOnClickListener(v -> handleCancel());
|
||||
addView(cancelButton, LayoutHelper.createFrame(40, 40, Gravity.LEFT | Gravity.CENTER_VERTICAL, 4, 0, 0, 0));
|
||||
|
||||
cancelIcon = new TextView(context);
|
||||
cancelIcon.setText("✕");
|
||||
cancelIcon.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
|
||||
cancelIcon.setTextColor(0xFFFFFFFF);
|
||||
cancelIcon.setGravity(Gravity.CENTER);
|
||||
cancelIcon.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM));
|
||||
addView(cancelIcon, LayoutHelper.createFrame(40, 40, Gravity.LEFT | Gravity.CENTER_VERTICAL, 4, 0, 0, 0));
|
||||
|
||||
// Add ripple effect to buttons
|
||||
acceptButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector, resourcesProvider), 3));
|
||||
cancelButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_listSelector, resourcesProvider), 3));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
// Draw rounded background
|
||||
backgroundPaint.setColor(Theme.getColor(Theme.key_chat_inBubble, resourcesProvider));
|
||||
backgroundPaint.setAlpha((int) (255 * animationProgress * 0.95f));
|
||||
|
||||
backgroundRect.set(0, 0, getWidth(), getHeight());
|
||||
canvas.drawRoundRect(backgroundRect, AndroidUtilities.dp(12), AndroidUtilities.dp(12), backgroundPaint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show translation proposition with fluid animation
|
||||
*/
|
||||
public void showProposition(String original, String translated,
|
||||
OnTranslationAcceptListener acceptListener,
|
||||
OnTranslationCancelListener cancelListener) {
|
||||
if (isShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.acceptListener = acceptListener;
|
||||
this.cancelListener = cancelListener;
|
||||
|
||||
// Set text
|
||||
originalText.setText(original);
|
||||
translatedText.setText(translated);
|
||||
|
||||
// Prepare for animation
|
||||
setVisibility(VISIBLE);
|
||||
isShowing = true;
|
||||
|
||||
// Slide up + fade in animation
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
|
||||
animator.setDuration(400);
|
||||
animator.setInterpolator(new OvershootInterpolator(0.8f));
|
||||
animator.addUpdateListener(animation -> {
|
||||
animationProgress = (float) animation.getAnimatedValue();
|
||||
|
||||
// Slide up
|
||||
setTranslationY(AndroidUtilities.dp(20) * (1f - animationProgress));
|
||||
|
||||
// Fade in
|
||||
setAlpha(animationProgress);
|
||||
|
||||
// Scale buttons
|
||||
acceptButton.setScaleX(animationProgress);
|
||||
acceptButton.setScaleY(animationProgress);
|
||||
acceptIcon.setScaleX(animationProgress);
|
||||
acceptIcon.setScaleY(animationProgress);
|
||||
cancelButton.setScaleX(animationProgress);
|
||||
cancelButton.setScaleY(animationProgress);
|
||||
cancelIcon.setScaleX(animationProgress);
|
||||
cancelIcon.setScaleY(animationProgress);
|
||||
|
||||
invalidate();
|
||||
});
|
||||
animator.start();
|
||||
|
||||
// Add subtle bounce to text
|
||||
animateTextEntrance(originalText, 100);
|
||||
animateTextEntrance(translatedText, 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide with fluid animation
|
||||
*/
|
||||
public void hideProposition() {
|
||||
if (!isShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
ValueAnimator animator = ValueAnimator.ofFloat(1f, 0f);
|
||||
animator.setDuration(300);
|
||||
animator.addUpdateListener(animation -> {
|
||||
animationProgress = (float) animation.getAnimatedValue();
|
||||
|
||||
// Slide down
|
||||
setTranslationY(AndroidUtilities.dp(20) * (1f - animationProgress));
|
||||
|
||||
// Fade out
|
||||
setAlpha(animationProgress);
|
||||
|
||||
invalidate();
|
||||
});
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
setVisibility(GONE);
|
||||
isShowing = false;
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private void animateTextEntrance(TextView textView, long delay) {
|
||||
textView.setAlpha(0f);
|
||||
textView.setTranslationY(AndroidUtilities.dp(10));
|
||||
|
||||
textView.animate()
|
||||
.alpha(1f)
|
||||
.translationY(0)
|
||||
.setDuration(300)
|
||||
.setStartDelay(delay)
|
||||
.setInterpolator(new OvershootInterpolator(0.8f))
|
||||
.start();
|
||||
}
|
||||
|
||||
private void handleAccept() {
|
||||
// Pulse animation
|
||||
acceptButton.animate()
|
||||
.scaleX(1.2f)
|
||||
.scaleY(1.2f)
|
||||
.setDuration(100)
|
||||
.withEndAction(() -> {
|
||||
acceptButton.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setDuration(100)
|
||||
.start();
|
||||
})
|
||||
.start();
|
||||
|
||||
acceptIcon.animate()
|
||||
.scaleX(1.2f)
|
||||
.scaleY(1.2f)
|
||||
.setDuration(100)
|
||||
.withEndAction(() -> {
|
||||
acceptIcon.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setDuration(100)
|
||||
.start();
|
||||
})
|
||||
.start();
|
||||
|
||||
// Callback and hide
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
if (acceptListener != null) {
|
||||
acceptListener.onAccept(translatedText.getText().toString());
|
||||
}
|
||||
hideProposition();
|
||||
}, 150);
|
||||
}
|
||||
|
||||
private void handleCancel() {
|
||||
// Pulse animation
|
||||
cancelButton.animate()
|
||||
.scaleX(1.2f)
|
||||
.scaleY(1.2f)
|
||||
.setDuration(100)
|
||||
.withEndAction(() -> {
|
||||
cancelButton.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setDuration(100)
|
||||
.start();
|
||||
})
|
||||
.start();
|
||||
|
||||
cancelIcon.animate()
|
||||
.scaleX(1.2f)
|
||||
.scaleY(1.2f)
|
||||
.setDuration(100)
|
||||
.withEndAction(() -> {
|
||||
cancelIcon.animate()
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setDuration(100)
|
||||
.start();
|
||||
})
|
||||
.start();
|
||||
|
||||
// Callback and hide
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
if (cancelListener != null) {
|
||||
cancelListener.onCancel();
|
||||
}
|
||||
hideProposition();
|
||||
}, 150);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// Consume all touch events to prevent click-through
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return isShowing;
|
||||
}
|
||||
}
|
||||
@@ -10,13 +10,25 @@
|
||||
package com.overspend1.overgram.ui.preferences;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.exteragram.messenger.preferences.BasePreferencesActivity;
|
||||
import com.overspend1.overgram.OverConfig;
|
||||
import com.overspend1.overgram.ui.liquidglass.GlassParameters;
|
||||
import com.overspend1.overgram.ui.liquidglass.LiquidGlassPreset;
|
||||
import com.overspend1.overgram.ui.liquidglass.LiquidGlassEffect;
|
||||
import org.telegram.messenger.AndroidUtilities;
|
||||
import org.telegram.messenger.LocaleController;
|
||||
import org.telegram.messenger.R;
|
||||
import org.telegram.ui.ActionBar.AlertDialog;
|
||||
@@ -32,6 +44,7 @@ public class LiquidGlassPreferencesActivity extends BasePreferencesActivity {
|
||||
|
||||
private int applyToChatBubblesRow;
|
||||
private int applyToDialogsRow;
|
||||
private int applyToSystemRow;
|
||||
private int divider2Row;
|
||||
|
||||
private int presetRow;
|
||||
@@ -54,6 +67,7 @@ public class LiquidGlassPreferencesActivity extends BasePreferencesActivity {
|
||||
if (OverConfig.liquidGlassEnabled) {
|
||||
applyToChatBubblesRow = newRow();
|
||||
applyToDialogsRow = newRow();
|
||||
applyToSystemRow = newRow();
|
||||
divider2Row = newRow();
|
||||
|
||||
presetRow = newRow();
|
||||
@@ -66,6 +80,7 @@ public class LiquidGlassPreferencesActivity extends BasePreferencesActivity {
|
||||
} else {
|
||||
applyToChatBubblesRow = -1;
|
||||
applyToDialogsRow = -1;
|
||||
applyToSystemRow = -1;
|
||||
divider2Row = -1;
|
||||
presetRow = -1;
|
||||
blurRadiusRow = -1;
|
||||
@@ -84,11 +99,7 @@ public class LiquidGlassPreferencesActivity extends BasePreferencesActivity {
|
||||
((TextCheckCell) view).setChecked(OverConfig.liquidGlassEnabled);
|
||||
|
||||
updateRowsId();
|
||||
if (OverConfig.liquidGlassEnabled) {
|
||||
listAdapter.notifyItemRangeInserted(divider1Row + 1, 11);
|
||||
} else {
|
||||
listAdapter.notifyItemRangeRemoved(divider1Row + 1, 11);
|
||||
}
|
||||
listAdapter.notifyDataSetChanged();
|
||||
} else if (position == applyToChatBubblesRow) {
|
||||
OverConfig.liquidGlassApplyToChatBubbles ^= true;
|
||||
OverConfig.editor.putBoolean("liquidGlassApplyToChatBubbles", OverConfig.liquidGlassApplyToChatBubbles).apply();
|
||||
@@ -97,6 +108,10 @@ public class LiquidGlassPreferencesActivity extends BasePreferencesActivity {
|
||||
OverConfig.liquidGlassApplyToDialogs ^= true;
|
||||
OverConfig.editor.putBoolean("liquidGlassApplyToDialogs", OverConfig.liquidGlassApplyToDialogs).apply();
|
||||
((TextCheckCell) view).setChecked(OverConfig.liquidGlassApplyToDialogs);
|
||||
} else if (position == applyToSystemRow) {
|
||||
OverConfig.liquidGlassApplyToSystemSurfaces ^= true;
|
||||
OverConfig.editor.putBoolean("liquidGlassApplyToSystemSurfaces", OverConfig.liquidGlassApplyToSystemSurfaces).apply();
|
||||
((TextCheckCell) view).setChecked(OverConfig.liquidGlassApplyToSystemSurfaces);
|
||||
} else if (position == presetRow) {
|
||||
showPresetSelector();
|
||||
} else if (position == blurRadiusRow) {
|
||||
@@ -122,6 +137,15 @@ public class LiquidGlassPreferencesActivity extends BasePreferencesActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private GlassParameters buildGlassParameters() {
|
||||
LiquidGlassPreset preset = LiquidGlassPreset.fromId(OverConfig.liquidGlassPreset);
|
||||
GlassParameters params = preset.toParameters();
|
||||
params.blurRadius = OverConfig.liquidGlassBlurRadius;
|
||||
params.opacity = OverConfig.liquidGlassOpacity;
|
||||
params.clamp();
|
||||
return params;
|
||||
}
|
||||
|
||||
private void showPresetSelector() {
|
||||
LiquidGlassPreset[] presets = LiquidGlassPreset.values();
|
||||
String[] names = new String[presets.length];
|
||||
@@ -149,12 +173,68 @@ public class LiquidGlassPreferencesActivity extends BasePreferencesActivity {
|
||||
}
|
||||
|
||||
private void showSlider(String title, int min, int max, int current, SliderCallback callback) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
|
||||
Context context = getParentActivity();
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int initial = current;
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
builder.setTitle(title);
|
||||
|
||||
// Simple implementation - can be enhanced with actual slider view
|
||||
builder.setMessage("Current value: " + current + "\nUse custom slider implementation here");
|
||||
LinearLayout container = new LinearLayout(context);
|
||||
container.setOrientation(LinearLayout.VERTICAL);
|
||||
int pad = AndroidUtilities.dp(20);
|
||||
container.setPadding(pad, pad, pad, pad);
|
||||
|
||||
TextView valueView = new TextView(context);
|
||||
valueView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack));
|
||||
valueView.setTextSize(16);
|
||||
valueView.setText(String.valueOf(current));
|
||||
container.addView(valueView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
SeekBar seekBar = new SeekBar(context);
|
||||
seekBar.setMax(max - min);
|
||||
seekBar.setProgress(current - min);
|
||||
container.addView(seekBar, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
// Glass preview
|
||||
GlassPreviewView previewView = new GlassPreviewView(context);
|
||||
previewView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, AndroidUtilities.dp(120)));
|
||||
previewView.setEffect(new LiquidGlassEffect(buildGlassParameters()));
|
||||
container.addView(previewView);
|
||||
|
||||
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
float value = min + progress;
|
||||
valueView.setText(String.valueOf((int) value));
|
||||
callback.onValueChanged(value);
|
||||
// Refresh preview using current config
|
||||
if (previewView.getEffect() != null) {
|
||||
previewView.getEffect().setParameters(buildGlassParameters());
|
||||
previewView.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) { }
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) { }
|
||||
});
|
||||
|
||||
builder.setView(container);
|
||||
builder.setPositiveButton(LocaleController.getString("OK", R.string.OK), null);
|
||||
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), (dialog, which) -> {
|
||||
// revert if cancelled
|
||||
callback.onValueChanged(initial);
|
||||
if (previewView.getEffect() != null) {
|
||||
previewView.getEffect().setParameters(buildGlassParameters());
|
||||
previewView.invalidate();
|
||||
}
|
||||
});
|
||||
showDialog(builder.create());
|
||||
}
|
||||
|
||||
@@ -243,6 +323,12 @@ public class LiquidGlassPreferencesActivity extends BasePreferencesActivity {
|
||||
OverConfig.liquidGlassApplyToDialogs,
|
||||
false
|
||||
);
|
||||
} else if (position == applyToSystemRow) {
|
||||
textCheckCell.setTextAndCheck(
|
||||
LocaleController.getString(R.string.LiquidGlassApplyToSystem),
|
||||
OverConfig.liquidGlassApplyToSystemSurfaces,
|
||||
false
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -262,4 +348,65 @@ public class LiquidGlassPreferencesActivity extends BasePreferencesActivity {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Small preview surface that renders the current glass parameters.
|
||||
*/
|
||||
private static class GlassPreviewView extends View {
|
||||
private LiquidGlassEffect effect;
|
||||
private Bitmap background;
|
||||
private final RectF rect = new RectF();
|
||||
private final Paint bgPaint = new Paint();
|
||||
|
||||
public GlassPreviewView(Context context) {
|
||||
super(context);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
public void setEffect(LiquidGlassEffect effect) {
|
||||
this.effect = effect;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public LiquidGlassEffect getEffect() {
|
||||
return effect;
|
||||
}
|
||||
|
||||
private void ensureBackground() {
|
||||
int w = Math.max(1, getWidth());
|
||||
int h = Math.max(1, getHeight());
|
||||
if (background != null && !background.isRecycled() && background.getWidth() == w && background.getHeight() == h) {
|
||||
return;
|
||||
}
|
||||
if (background != null && !background.isRecycled()) {
|
||||
background.recycle();
|
||||
}
|
||||
background = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(background);
|
||||
int topColor = Theme.getColor(Theme.key_windowBackgroundWhite);
|
||||
int bottomColor = Theme.getColor(Theme.key_actionBarDefault);
|
||||
LinearGradient gradient = new LinearGradient(0, 0, w, h, topColor, bottomColor, Shader.TileMode.CLAMP);
|
||||
bgPaint.setShader(gradient);
|
||||
canvas.drawRect(0, 0, w, h, bgPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
ensureBackground();
|
||||
if (effect != null && background != null && !background.isRecycled()) {
|
||||
rect.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
|
||||
effect.apply(canvas, rect, background);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
if (background != null && !background.isRecycled()) {
|
||||
background.recycle();
|
||||
}
|
||||
background = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.overspend1.overgram.ui.preferences.utils.OverUi;
|
||||
import com.overspend1.overgram.utils.OverState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.telegram.messenger.*;
|
||||
import org.telegram.ui.ActionBar.AlertDialog;
|
||||
import org.telegram.ui.ActionBar.Theme;
|
||||
import org.telegram.ui.Cells.*;
|
||||
import org.telegram.ui.Components.BulletinFactory;
|
||||
@@ -58,6 +59,7 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
private int disableAdsRow;
|
||||
private int localPremiumRow;
|
||||
private int filtersRow;
|
||||
private int quickActionsRow;
|
||||
private int qolDividerRow;
|
||||
|
||||
private int customizationHeaderRow;
|
||||
@@ -75,6 +77,20 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
private int aiSettingsRow;
|
||||
private int aiDividerRow;
|
||||
|
||||
private int productivityHeaderRow;
|
||||
private int smartRepliesRow;
|
||||
private int autoTranslateIncomingRow;
|
||||
private int autoTranslateOutgoingRow;
|
||||
private int autoTranslateLangRow;
|
||||
private int autoTranslateIncomingLangRow;
|
||||
private int productivityDividerRow;
|
||||
|
||||
private int yagizTranslatorHeaderRow;
|
||||
private int yagizTranslatorEnabledRow;
|
||||
private int yagizTranslatorApiTypeRow;
|
||||
private int yagizTranslatorAutoDetectRow;
|
||||
private int yagizTranslatorDividerRow;
|
||||
|
||||
private int ayuSyncHeaderRow;
|
||||
private int ayuSyncStatusBtnRow;
|
||||
private int ayuSyncDividerRow;
|
||||
@@ -120,6 +136,7 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
disableAdsRow = newRow();
|
||||
localPremiumRow = newRow();
|
||||
filtersRow = newRow();
|
||||
quickActionsRow = newRow();
|
||||
qolDividerRow = newRow();
|
||||
|
||||
customizationHeaderRow = newRow();
|
||||
@@ -137,6 +154,20 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
aiSettingsRow = newRow();
|
||||
aiDividerRow = newRow();
|
||||
|
||||
productivityHeaderRow = newRow();
|
||||
smartRepliesRow = newRow();
|
||||
autoTranslateIncomingRow = newRow();
|
||||
autoTranslateOutgoingRow = newRow();
|
||||
autoTranslateLangRow = newRow();
|
||||
autoTranslateIncomingLangRow = newRow();
|
||||
productivityDividerRow = newRow();
|
||||
|
||||
yagizTranslatorHeaderRow = newRow();
|
||||
yagizTranslatorEnabledRow = newRow();
|
||||
yagizTranslatorApiTypeRow = newRow();
|
||||
yagizTranslatorAutoDetectRow = newRow();
|
||||
yagizTranslatorDividerRow = newRow();
|
||||
|
||||
ayuSyncHeaderRow = newRow();
|
||||
ayuSyncStatusBtnRow = newRow();
|
||||
ayuSyncDividerRow = newRow();
|
||||
@@ -285,6 +316,8 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
} else {
|
||||
presentFragment(new RegexFiltersPreferencesActivity());
|
||||
}
|
||||
} else if (position == quickActionsRow) {
|
||||
showQuickActions();
|
||||
} else if (position == showGhostToggleInDrawerRow) {
|
||||
OverConfig.editor.putBoolean("showGhostToggleInDrawer", OverConfig.showGhostToggleInDrawer ^= true).apply();
|
||||
((TextCheckCell) view).setChecked(OverConfig.showGhostToggleInDrawer);
|
||||
@@ -317,6 +350,27 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
presentFragment(new LiquidGlassPreferencesActivity());
|
||||
} else if (position == aiSettingsRow) {
|
||||
presentFragment(new AiPreferencesActivity());
|
||||
} else if (position == smartRepliesRow) {
|
||||
OverConfig.editor.putBoolean("smartQuickReplies", OverConfig.smartQuickReplies ^= true).apply();
|
||||
((TextCheckCell) view).setChecked(OverConfig.smartQuickReplies);
|
||||
} else if (position == autoTranslateIncomingRow) {
|
||||
OverConfig.editor.putBoolean("autoTranslateIncomingDefault", OverConfig.autoTranslateIncomingDefault ^= true).apply();
|
||||
((TextCheckCell) view).setChecked(OverConfig.autoTranslateIncomingDefault);
|
||||
} else if (position == autoTranslateOutgoingRow) {
|
||||
OverConfig.editor.putBoolean("autoTranslateOutgoingDefault", OverConfig.autoTranslateOutgoingDefault ^= true).apply();
|
||||
((TextCheckCell) view).setChecked(OverConfig.autoTranslateOutgoingDefault);
|
||||
} else if (position == autoTranslateLangRow) {
|
||||
showLanguagePicker(false); // false = outgoing language
|
||||
} else if (position == autoTranslateIncomingLangRow) {
|
||||
showLanguagePicker(true); // true = incoming language
|
||||
} else if (position == yagizTranslatorEnabledRow) {
|
||||
OverConfig.editor.putBoolean("yagizTranslatorEnabled", OverConfig.yagizTranslatorEnabled ^= true).apply();
|
||||
((TextCheckCell) view).setChecked(OverConfig.yagizTranslatorEnabled);
|
||||
} else if (position == yagizTranslatorApiTypeRow) {
|
||||
showYagizTranslatorApiPicker();
|
||||
} else if (position == yagizTranslatorAutoDetectRow) {
|
||||
OverConfig.editor.putBoolean("yagizTranslatorAutoDetect", OverConfig.yagizTranslatorAutoDetect ^= true).apply();
|
||||
((TextCheckCell) view).setChecked(OverConfig.yagizTranslatorAutoDetect);
|
||||
} else if (position == ayuSyncStatusBtnRow) {
|
||||
presentFragment(new OverSyncPreferencesActivity());
|
||||
} else if (position == WALModeRow) {
|
||||
@@ -391,6 +445,10 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
textCell.setTextAndValue(LocaleController.getString(R.string.DeletedMarkText), OverConfig.getDeletedMark(), true);
|
||||
} else if (position == editedMarkTextRow) {
|
||||
textCell.setTextAndValue(LocaleController.getString(R.string.EditedMarkText), OverConfig.getEditedMark(), true);
|
||||
} else if (position == quickActionsRow) {
|
||||
textCell.setTextAndValue(LocaleController.getString(R.string.OvergramQuickActions),
|
||||
LocaleController.getString(R.string.OvergramQuickActionsHint),
|
||||
true);
|
||||
} else if (position == liquidGlassBtnRow) {
|
||||
textCell.setTextAndValue(LocaleController.getString(R.string.LiquidGlassHeader),
|
||||
OverConfig.liquidGlassEnabled ? LocaleController.getString("NotificationsOn", R.string.NotificationsOn) : LocaleController.getString("NotificationsOff", R.string.NotificationsOff),
|
||||
@@ -399,6 +457,17 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
textCell.setTextAndValue(LocaleController.getString(R.string.OvergramAiSettings),
|
||||
OverConfig.geminiEnabled ? LocaleController.getString("NotificationsOn", R.string.NotificationsOn) : LocaleController.getString("NotificationsOff", R.string.NotificationsOff),
|
||||
true);
|
||||
} else if (position == autoTranslateLangRow) {
|
||||
String langCode = OverConfig.autoTranslateOutgoingLangDefault;
|
||||
String langName = getLanguageDisplayName(langCode);
|
||||
textCell.setTextAndValue(LocaleController.getString(R.string.AutoTranslateOutgoingLang), langName, true);
|
||||
} else if (position == autoTranslateIncomingLangRow) {
|
||||
String langCode = OverConfig.autoTranslateIncomingLangDefault;
|
||||
String langName = getLanguageDisplayName(langCode);
|
||||
textCell.setTextAndValue(LocaleController.getString(R.string.AutoTranslateIncomingLang), langName, true);
|
||||
} else if (position == yagizTranslatorApiTypeRow) {
|
||||
String apiName = com.overspend1.overgram.translator.YagizTranslator.getApiName(OverConfig.yagizTranslatorApiType);
|
||||
textCell.setTextAndValue(LocaleController.getString(R.string.YagizTranslatorApiType), apiName, true);
|
||||
} else if (position == ayuSyncStatusBtnRow) {
|
||||
var status = OverSyncState.getConnectionStateString();
|
||||
|
||||
@@ -428,6 +497,10 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
headerCell.setText(LocaleController.getString(R.string.LiquidGlassHeader));
|
||||
} else if (position == aiHeaderRow) {
|
||||
headerCell.setText(LocaleController.getString(R.string.OvergramAiHeader));
|
||||
} else if (position == productivityHeaderRow) {
|
||||
headerCell.setText(LocaleController.getString(R.string.ProductivityHeader));
|
||||
} else if (position == yagizTranslatorHeaderRow) {
|
||||
headerCell.setText(LocaleController.getString(R.string.YagizTranslatorHeader));
|
||||
} else if (position == ayuSyncHeaderRow) {
|
||||
headerCell.setText(LocaleController.getString(R.string.AyuSyncHeader));
|
||||
} else if (position == debugHeaderRow) {
|
||||
@@ -457,6 +530,16 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
textCheckCell.setTextAndCheck(LocaleController.getString(R.string.ShowKllButtonInDrawer), OverConfig.showKillButtonInDrawer, false);
|
||||
} else if (position == WALModeRow) {
|
||||
textCheckCell.setTextAndCheck(LocaleController.getString(R.string.WALMode), OverConfig.WALMode, false);
|
||||
} else if (position == smartRepliesRow) {
|
||||
textCheckCell.setTextAndCheck(LocaleController.getString(R.string.SmartQuickReplies), OverConfig.smartQuickReplies, true);
|
||||
} else if (position == autoTranslateIncomingRow) {
|
||||
textCheckCell.setTextAndCheck(LocaleController.getString(R.string.AutoTranslateIncoming), OverConfig.autoTranslateIncomingDefault, true);
|
||||
} else if (position == autoTranslateOutgoingRow) {
|
||||
textCheckCell.setTextAndCheck(LocaleController.getString(R.string.AutoTranslateOutgoing), OverConfig.autoTranslateOutgoingDefault, true);
|
||||
} else if (position == yagizTranslatorEnabledRow) {
|
||||
textCheckCell.setTextAndCheck(LocaleController.getString(R.string.YagizTranslatorEnabled), OverConfig.yagizTranslatorEnabled, true);
|
||||
} else if (position == yagizTranslatorAutoDetectRow) {
|
||||
textCheckCell.setTextAndCheck(LocaleController.getString(R.string.YagizTranslatorAutoDetect), OverConfig.yagizTranslatorAutoDetect, false);
|
||||
}
|
||||
break;
|
||||
case 18:
|
||||
@@ -517,16 +600,22 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
position == customizationDividerRow ||
|
||||
position == liquidGlassDividerRow ||
|
||||
position == aiDividerRow ||
|
||||
position == productivityDividerRow ||
|
||||
position == yagizTranslatorDividerRow ||
|
||||
position == ayuSyncDividerRow ||
|
||||
position == buttonsDividerRow
|
||||
) {
|
||||
return 1;
|
||||
} else if (
|
||||
position == messageSavingBtnRow ||
|
||||
position == messageSavingBtnRow ||
|
||||
position == deletedMarkTextRow ||
|
||||
position == editedMarkTextRow ||
|
||||
position == quickActionsRow ||
|
||||
position == liquidGlassBtnRow ||
|
||||
position == aiSettingsRow ||
|
||||
position == autoTranslateLangRow ||
|
||||
position == autoTranslateIncomingLangRow ||
|
||||
position == yagizTranslatorApiTypeRow ||
|
||||
position == ayuSyncStatusBtnRow ||
|
||||
position == clearAyuDatabaseBtnRow ||
|
||||
position == eraseLocalDatabaseBtnRow
|
||||
@@ -539,6 +628,8 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
position == customizationHeaderRow ||
|
||||
position == liquidGlassHeaderRow ||
|
||||
position == aiHeaderRow ||
|
||||
position == productivityHeaderRow ||
|
||||
position == yagizTranslatorHeaderRow ||
|
||||
position == ayuSyncHeaderRow ||
|
||||
position == debugHeaderRow
|
||||
) {
|
||||
@@ -555,8 +646,168 @@ public class OvergramPreferencesActivity extends BasePreferencesActivity impleme
|
||||
position == filtersRow
|
||||
) {
|
||||
return TOGGLE_BUTTON_VIEW;
|
||||
} else if (
|
||||
position == smartRepliesRow ||
|
||||
position == autoTranslateIncomingRow ||
|
||||
position == autoTranslateOutgoingRow ||
|
||||
position == yagizTranslatorEnabledRow ||
|
||||
position == yagizTranslatorAutoDetectRow
|
||||
) {
|
||||
return 5;
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
private void showQuickActions() {
|
||||
if (getParentActivity() == null) {
|
||||
return;
|
||||
}
|
||||
String[] options = new String[] {
|
||||
LocaleController.getString(R.string.OvergramQuickActionToggleGlass),
|
||||
LocaleController.getString(R.string.OvergramQuickActionToggleGhost),
|
||||
LocaleController.getString(R.string.OvergramQuickActionOpenGlass),
|
||||
LocaleController.getString(R.string.OvergramQuickActionOpenAi)
|
||||
};
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
|
||||
builder.setTitle(LocaleController.getString(R.string.OvergramQuickActions));
|
||||
builder.setItems(options, (dialog, which) -> {
|
||||
switch (which) {
|
||||
case 0: {
|
||||
OverConfig.liquidGlassEnabled = !OverConfig.liquidGlassEnabled;
|
||||
OverConfig.editor.putBoolean("liquidGlassEnabled", OverConfig.liquidGlassEnabled).apply();
|
||||
listAdapter.notifyItemChanged(liquidGlassBtnRow, payload);
|
||||
BulletinFactory.of(this).createSimpleBulletin(
|
||||
OverConfig.liquidGlassEnabled ? R.raw.done : R.raw.deactivate,
|
||||
OverConfig.liquidGlassEnabled ? LocaleController.getString("NotificationsOn", R.string.NotificationsOn) : LocaleController.getString("NotificationsOff", R.string.NotificationsOff)
|
||||
).show();
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
OverConfig.toggleGhostMode();
|
||||
updateGhostViews();
|
||||
BulletinFactory.of(this).createSimpleBulletin(
|
||||
OverConfig.isGhostModeActive() ? R.raw.done : R.raw.deactivate,
|
||||
OverConfig.isGhostModeActive() ? LocaleController.getString(R.string.GhostModeEnabled) : LocaleController.getString(R.string.GhostModeDisabled)
|
||||
).show();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
presentFragment(new LiquidGlassPreferencesActivity());
|
||||
break;
|
||||
case 3:
|
||||
presentFragment(new AiPreferencesActivity());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
|
||||
showDialog(builder.create());
|
||||
}
|
||||
|
||||
private String getLanguageDisplayName(String langCode) {
|
||||
if (langCode == null || langCode.isEmpty()) {
|
||||
langCode = "en";
|
||||
}
|
||||
|
||||
// Common language names
|
||||
switch (langCode.toLowerCase()) {
|
||||
case "en": return "English";
|
||||
case "es": return "Spanish";
|
||||
case "fr": return "French";
|
||||
case "de": return "German";
|
||||
case "it": return "Italian";
|
||||
case "pt": return "Portuguese";
|
||||
case "ru": return "Russian";
|
||||
case "ja": return "Japanese";
|
||||
case "ko": return "Korean";
|
||||
case "zh": return "Chinese";
|
||||
case "ar": return "Arabic";
|
||||
case "hi": return "Hindi";
|
||||
case "tr": return "Turkish";
|
||||
case "pl": return "Polish";
|
||||
case "uk": return "Ukrainian";
|
||||
case "nl": return "Dutch";
|
||||
case "sv": return "Swedish";
|
||||
case "no": return "Norwegian";
|
||||
case "da": return "Danish";
|
||||
case "fi": return "Finnish";
|
||||
default: return langCode.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
private void showLanguagePicker(boolean isIncoming) {
|
||||
if (getParentActivity() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[][] languages = {
|
||||
{"en", "English"},
|
||||
{"es", "Spanish"},
|
||||
{"fr", "French"},
|
||||
{"de", "German"},
|
||||
{"it", "Italian"},
|
||||
{"pt", "Portuguese"},
|
||||
{"ru", "Russian"},
|
||||
{"ja", "Japanese"},
|
||||
{"ko", "Korean"},
|
||||
{"zh", "Chinese"},
|
||||
{"ar", "Arabic"},
|
||||
{"hi", "Hindi"},
|
||||
{"tr", "Turkish"},
|
||||
{"pl", "Polish"},
|
||||
{"uk", "Ukrainian"},
|
||||
{"nl", "Dutch"},
|
||||
{"sv", "Swedish"},
|
||||
{"no", "Norwegian"},
|
||||
{"da", "Danish"},
|
||||
{"fi", "Finnish"}
|
||||
};
|
||||
|
||||
String[] options = new String[languages.length];
|
||||
for (int i = 0; i < languages.length; i++) {
|
||||
options[i] = languages[i][1];
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
|
||||
builder.setTitle(LocaleController.getString(isIncoming ? R.string.AutoTranslateIncomingLang : R.string.AutoTranslateOutgoingLang));
|
||||
builder.setItems(options, (dialog, which) -> {
|
||||
String selectedLang = languages[which][0];
|
||||
if (isIncoming) {
|
||||
OverConfig.autoTranslateIncomingLangDefault = selectedLang;
|
||||
OverConfig.editor.putString("autoTranslateIncomingLangDefault", selectedLang).apply();
|
||||
listAdapter.notifyItemChanged(autoTranslateIncomingLangRow);
|
||||
} else {
|
||||
OverConfig.autoTranslateOutgoingLangDefault = selectedLang;
|
||||
OverConfig.editor.putString("autoTranslateOutgoingLangDefault", selectedLang).apply();
|
||||
listAdapter.notifyItemChanged(autoTranslateLangRow);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
|
||||
showDialog(builder.create());
|
||||
}
|
||||
|
||||
private void showYagizTranslatorApiPicker() {
|
||||
if (getParentActivity() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] apiNames = {
|
||||
com.overspend1.overgram.translator.YagizTranslator.getApiName(com.overspend1.overgram.translator.YagizTranslator.API_GEMINI),
|
||||
com.overspend1.overgram.translator.YagizTranslator.getApiName(com.overspend1.overgram.translator.YagizTranslator.API_GOOGLE),
|
||||
com.overspend1.overgram.translator.YagizTranslator.getApiName(com.overspend1.overgram.translator.YagizTranslator.API_DEEPL)
|
||||
};
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
|
||||
builder.setTitle(LocaleController.getString(R.string.YagizTranslatorApiType));
|
||||
builder.setItems(apiNames, (dialog, which) -> {
|
||||
OverConfig.yagizTranslatorApiType = which;
|
||||
OverConfig.editor.putInt("yagizTranslatorApiType", which).apply();
|
||||
listAdapter.notifyItemChanged(yagizTranslatorApiTypeRow);
|
||||
});
|
||||
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
|
||||
showDialog(builder.create());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -51,6 +52,9 @@ import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import com.overspend1.overgram.OverFilter;
|
||||
import com.overspend1.overgram.OverUtils;
|
||||
import com.overspend1.overgram.OverConfig;
|
||||
import com.overspend1.overgram.ui.liquidglass.GlassParameters;
|
||||
import com.overspend1.overgram.ui.liquidglass.LiquidGlassEffect;
|
||||
import org.telegram.messenger.AndroidUtilities;
|
||||
import org.telegram.messenger.ApplicationLoader;
|
||||
import org.telegram.messenger.ChatObject;
|
||||
@@ -132,8 +136,8 @@ public class DialogCell extends BaseCell {
|
||||
public static final int SENT_STATE_READ = 2;
|
||||
public boolean drawAvatar = true;
|
||||
public int messagePaddingStart = 72;
|
||||
public int heightDefault = 72;
|
||||
public int heightThreeLines = 78;
|
||||
public int heightDefault = 69; // Overgram: Reduced from 72 to reduce crowding
|
||||
public int heightThreeLines = 75; // Overgram: Reduced from 78 to reduce crowding
|
||||
public TLRPC.TL_forumTopic forumTopic;
|
||||
public boolean useFromUserAsAvatar;
|
||||
private boolean isTopic;
|
||||
@@ -166,6 +170,12 @@ public class DialogCell extends BaseCell {
|
||||
private Path thumbPath = new Path();
|
||||
private SpoilerEffect thumbSpoiler = new SpoilerEffect();
|
||||
|
||||
// Liquid glass overlay support for dialog list
|
||||
private LiquidGlassEffect glassEffect;
|
||||
private Bitmap glassBackgroundCache;
|
||||
private long lastGlassCaptureTime;
|
||||
private final Rect glassTempRect = new Rect();
|
||||
|
||||
public void setMoving(boolean moving) {
|
||||
this.moving = moving;
|
||||
}
|
||||
@@ -174,6 +184,91 @@ public class DialogCell extends BaseCell {
|
||||
return moving;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
clearGlassResources();
|
||||
}
|
||||
|
||||
private boolean shouldUseLiquidGlass() {
|
||||
return OverConfig.liquidGlassEnabled && (OverConfig.liquidGlassApplyToDialogs || OverConfig.liquidGlassApplyToSystemSurfaces);
|
||||
}
|
||||
|
||||
private GlassParameters buildGlassParameters() {
|
||||
com.overspend1.overgram.ui.liquidglass.LiquidGlassPreset preset = com.overspend1.overgram.ui.liquidglass.LiquidGlassPreset.fromId(OverConfig.liquidGlassPreset);
|
||||
GlassParameters params = preset.toParameters();
|
||||
params.blurRadius = OverConfig.liquidGlassBlurRadius;
|
||||
params.opacity = OverConfig.liquidGlassOpacity;
|
||||
params.clamp();
|
||||
return params;
|
||||
}
|
||||
|
||||
private void ensureGlassEffect() {
|
||||
if (glassEffect == null) {
|
||||
glassEffect = new LiquidGlassEffect(buildGlassParameters());
|
||||
} else {
|
||||
glassEffect.setParameters(buildGlassParameters());
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap captureGlassBackground() {
|
||||
View parent = (View) getParent();
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
int w = getMeasuredWidth();
|
||||
int h = getMeasuredHeight();
|
||||
if (w <= 0 || h <= 0) {
|
||||
return null;
|
||||
}
|
||||
long now = System.currentTimeMillis();
|
||||
if (glassBackgroundCache != null && !glassBackgroundCache.isRecycled() && now - lastGlassCaptureTime < 100) {
|
||||
return glassBackgroundCache;
|
||||
}
|
||||
try {
|
||||
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
Drawable bg = parent.getBackground();
|
||||
if (bg != null) {
|
||||
bg.setBounds(0, 0, parent.getWidth(), parent.getHeight());
|
||||
bg.draw(canvas);
|
||||
} else {
|
||||
canvas.drawColor(Color.TRANSPARENT);
|
||||
}
|
||||
if (glassBackgroundCache != null && !glassBackgroundCache.isRecycled()) {
|
||||
glassBackgroundCache.recycle();
|
||||
}
|
||||
glassBackgroundCache = bitmap;
|
||||
lastGlassCaptureTime = now;
|
||||
return bitmap;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void drawLiquidGlass(Canvas canvas) {
|
||||
ensureGlassEffect();
|
||||
Bitmap background = captureGlassBackground();
|
||||
if (glassEffect != null && background != null && !background.isRecycled()) {
|
||||
glassTempRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||
canvas.save();
|
||||
glassEffect.apply(canvas, new RectF(glassTempRect), background);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
private void clearGlassResources() {
|
||||
if (glassBackgroundCache != null && !glassBackgroundCache.isRecycled()) {
|
||||
glassBackgroundCache.recycle();
|
||||
}
|
||||
glassBackgroundCache = null;
|
||||
lastGlassCaptureTime = 0;
|
||||
if (glassEffect != null) {
|
||||
glassEffect.recycle();
|
||||
glassEffect = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setForumTopic(TLRPC.TL_forumTopic topic, long dialog_id, MessageObject messageObject, boolean showTopicIconInName, boolean animated) {
|
||||
forumTopic = topic;
|
||||
isTopic = forumTopic != null;
|
||||
@@ -3038,6 +3133,9 @@ public class DialogCell extends BaseCell {
|
||||
}
|
||||
|
||||
int backgroundColor = 0;
|
||||
if (!drawingForBlur && shouldUseLiquidGlass()) {
|
||||
drawLiquidGlass(canvas);
|
||||
}
|
||||
if (translationX != 0 || cornerProgress != 0.0f) {
|
||||
canvas.save();
|
||||
canvas.translate(0, -translateY);
|
||||
|
||||
@@ -83,6 +83,7 @@ import android.widget.FrameLayout;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.Space;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
@@ -1326,6 +1327,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||
private final static int open_forum = 61;
|
||||
|
||||
private final static int translate = 62;
|
||||
private final static int auto_translate_incoming = 63;
|
||||
private final static int auto_translate_outgoing = 64;
|
||||
private final static int auto_translate_lang = 65;
|
||||
private final static int yagiz_translator = 66;
|
||||
|
||||
private final static int permissions = 100;
|
||||
private final static int administrators = 101;
|
||||
@@ -3149,6 +3154,38 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||
if (!getMessagesController().getTranslateController().toggleTranslatingDialog(getDialogId(), true)) {
|
||||
updateTopPanel(true);
|
||||
}
|
||||
} else if (id == auto_translate_incoming) {
|
||||
// Overgram: Toggle auto-translate incoming for this chat
|
||||
boolean currentState = OverConfig.isAutoTranslateIncoming(dialog_id);
|
||||
OverConfig.setAutoTranslateIncoming(dialog_id, !currentState);
|
||||
BulletinFactory.of(ChatActivity.this).createSimpleBulletin(
|
||||
!currentState ? R.raw.done : R.raw.deactivate,
|
||||
!currentState ? LocaleController.getString(R.string.AutoTranslateEnabled) : LocaleController.getString(R.string.AutoTranslateDisabled)
|
||||
).show();
|
||||
if (!currentState && !getMessagesController().getTranslateController().isTranslatingDialog(dialog_id)) {
|
||||
getMessagesController().getTranslateController().toggleTranslatingDialog(dialog_id, true);
|
||||
}
|
||||
} else if (id == auto_translate_outgoing) {
|
||||
// Overgram: Toggle auto-translate outgoing for this chat
|
||||
boolean currentState = OverConfig.isAutoTranslateOutgoing(dialog_id);
|
||||
OverConfig.setAutoTranslateOutgoing(dialog_id, !currentState);
|
||||
BulletinFactory.of(ChatActivity.this).createSimpleBulletin(
|
||||
!currentState ? R.raw.done : R.raw.deactivate,
|
||||
!currentState ? LocaleController.getString(R.string.AutoTranslateEnabled) : LocaleController.getString(R.string.AutoTranslateDisabled)
|
||||
).show();
|
||||
} else if (id == auto_translate_lang) {
|
||||
// Overgram: Show language picker for this chat
|
||||
showAutoTranslateLanguagePicker();
|
||||
} else if (id == yagiz_translator) {
|
||||
// Overgram: Toggle YagizTranslator for this chat
|
||||
boolean currentState = OverConfig.isYagizTranslatorEnabledForDialog(dialog_id);
|
||||
OverConfig.setYagizTranslatorEnabledForDialog(dialog_id, !currentState);
|
||||
BulletinFactory.of(ChatActivity.this).createSimpleBulletin(
|
||||
!currentState ? R.raw.done : R.raw.deactivate,
|
||||
!currentState ?
|
||||
LocaleController.getString(R.string.YagizTranslatorEnabled) + " - " + com.overspend1.overgram.translator.YagizTranslator.getApiName(OverConfig.yagizTranslatorApiType) :
|
||||
LocaleController.getString(R.string.YagizTranslatorPerChat) + " " + LocaleController.getString(R.string.AutoTranslateDisabled)
|
||||
).show();
|
||||
} else if (id == call || id == video_call) {
|
||||
if (currentUser != null && getParentActivity() != null) {
|
||||
VoIPHelper.startCall(currentUser, id == video_call, userInfo != null && userInfo.video_calls_available, getParentActivity(), getMessagesController().getUserFull(currentUser.id), getAccountInstance());
|
||||
@@ -3490,6 +3527,15 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||
}
|
||||
translateItem = headerItem.lazilyAddSubItem(translate, R.drawable.msg_translate, LocaleController.getString("TranslateMessage", R.string.TranslateMessage));
|
||||
updateTranslateItemVisibility();
|
||||
|
||||
// Overgram: Per-chat auto-translate controls
|
||||
headerItem.lazilyAddSubItem(auto_translate_incoming, R.drawable.msg_translate, LocaleController.getString(R.string.AutoTranslatePerChatIncoming));
|
||||
headerItem.lazilyAddSubItem(auto_translate_outgoing, R.drawable.msg_translate, LocaleController.getString(R.string.AutoTranslatePerChatOutgoing));
|
||||
headerItem.lazilyAddSubItem(auto_translate_lang, R.drawable.msg_language, LocaleController.getString(R.string.AutoTranslatePerChatLang));
|
||||
|
||||
// Overgram: YagizTranslator toggle
|
||||
headerItem.lazilyAddSubItem(yagiz_translator, R.drawable.msg_translate, LocaleController.getString(R.string.YagizTranslatorPerChat));
|
||||
|
||||
if (currentChat != null && !currentChat.creator && !ChatObject.hasAdminRights(currentChat)) {
|
||||
headerItem.lazilyAddSubItem(report, R.drawable.msg_report, LocaleController.getString("ReportChat", R.string.ReportChat));
|
||||
}
|
||||
@@ -5566,6 +5612,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||
outRect.bottom = -h;
|
||||
}
|
||||
}
|
||||
if (outRect.bottom == 0) {
|
||||
// Add a bit of breathing room between standalone messages
|
||||
outRect.bottom = AndroidUtilities.dp(6);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -16672,6 +16722,19 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Overgram: Update smart quick replies when new messages arrive
|
||||
if (OverConfig.smartQuickReplies && chatActivityEnterView != null && !arr.isEmpty()) {
|
||||
// Get the last message that's not from current user
|
||||
for (int i = arr.size() - 1; i >= 0; i--) {
|
||||
MessageObject msg = arr.get(i);
|
||||
if (!msg.isOut() && !msg.isService()) {
|
||||
chatActivityEnterView.updateSmartQuickReplies(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processNewMessages(arr);
|
||||
} else if (ChatObject.isChannel(currentChat) && !currentChat.megagroup && chatInfo != null && did == -chatInfo.linked_chat_id) {
|
||||
for (int a = 0, N = arr.size(); a < N; a++) {
|
||||
@@ -19247,6 +19310,44 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||
LongSparseArray<Long> scheduledGroupReplacement = null;
|
||||
for (int a = 0, N = arr.size(); a < N; a++) {
|
||||
MessageObject messageObject = arr.get(a);
|
||||
|
||||
// Overgram: Auto-translate incoming messages for full two-way conversation
|
||||
boolean shouldUseYagizTranslator = OverConfig.isYagizTranslatorEnabledForDialog(dialog_id) &&
|
||||
OverConfig.yagizTranslatorAutoDetect;
|
||||
|
||||
if (shouldUseYagizTranslator && !messageObject.isOut() && !messageObject.isService()) {
|
||||
// YagizTranslator: Turkish ↔ English auto-detection
|
||||
String messageText = messageObject.messageOwner.message;
|
||||
if (messageText != null && !messageText.isEmpty()) {
|
||||
com.overspend1.overgram.translator.YagizTranslator.translateAuto(
|
||||
messageText,
|
||||
(translatedText, detectedLang) -> {
|
||||
// Enable translation UI and show translated text
|
||||
if (!getMessagesController().getTranslateController().isTranslatingDialog(dialog_id)) {
|
||||
getMessagesController().getTranslateController().toggleTranslatingDialog(dialog_id, true);
|
||||
}
|
||||
},
|
||||
error -> FileLog.d("YagizTranslator: Failed to translate incoming message - " + error)
|
||||
);
|
||||
}
|
||||
} else if (OverConfig.isAutoTranslateIncoming(dialog_id) && !messageObject.isOut() && !messageObject.isService()) {
|
||||
// Regular auto-translate incoming
|
||||
if (!getMessagesController().getTranslateController().isTranslatingDialog(dialog_id)) {
|
||||
getMessagesController().getTranslateController().toggleTranslatingDialog(dialog_id, true);
|
||||
}
|
||||
|
||||
// Get user's language preference
|
||||
String targetLang = OverConfig.autoTranslateIncomingLangDefault;
|
||||
if (targetLang == null || targetLang.isEmpty()) {
|
||||
targetLang = java.util.Locale.getDefault().getLanguage();
|
||||
if (targetLang == null || targetLang.isEmpty()) {
|
||||
targetLang = "en";
|
||||
}
|
||||
}
|
||||
|
||||
// Set the target language for this dialog in TranslateController
|
||||
getMessagesController().getTranslateController().setDialogTranslateTo(dialog_id, targetLang);
|
||||
}
|
||||
if (!isAd) {
|
||||
isAd = messageObject.isSponsored();
|
||||
}
|
||||
@@ -32682,7 +32783,8 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||
}
|
||||
|
||||
private void requestGemini(CharSequence messageText, boolean translateToTurkish) {
|
||||
if (getParentActivity() == null || messageText == null || messageText.length() == 0) {
|
||||
if (getParentActivity() == null || messageText == null || TextUtils.isEmpty(messageText.toString().trim())) {
|
||||
BulletinFactory.of(this).createSimpleBulletin(R.raw.error, LocaleController.getString(R.string.OvergramGeminiNoInput)).show();
|
||||
return;
|
||||
}
|
||||
if (TextUtils.isEmpty(OverConfig.geminiApiKey)) {
|
||||
@@ -32709,6 +32811,10 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||
if (progressDialog.isShowing()) {
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
BulletinFactory.of(ChatActivity.this).createSimpleBulletin(R.raw.error, LocaleController.getString(R.string.OvergramGeminiEmptyResponse)).show();
|
||||
return;
|
||||
}
|
||||
showGeminiResult(translateToTurkish ? LocaleController.getString(R.string.OvergramTranslateTurkish) : LocaleController.getString(R.string.OvergramGeminiAsk), text);
|
||||
});
|
||||
}
|
||||
@@ -32729,21 +32835,133 @@ public class ChatActivity extends BaseFragment implements NotificationCenter.Not
|
||||
if (getParentActivity() == null) {
|
||||
return;
|
||||
}
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity(), themeDelegate);
|
||||
builder.setTitle(title);
|
||||
builder.setMessage(body);
|
||||
builder.setPositiveButton(LocaleController.getString("Copy", R.string.Copy), (dialog, which) -> {
|
||||
if (!TextUtils.isEmpty(body)) {
|
||||
AndroidUtilities.addToClipboard(body);
|
||||
BulletinFactory.of(this).createSimpleBulletin(R.raw.copy, LocaleController.getString("TextCopied", R.string.TextCopied)).show();
|
||||
}
|
||||
});
|
||||
builder.setNeutralButton(LocaleController.getString("Paste", R.string.Paste), (dialog, which) -> {
|
||||
Context ctx = getParentActivity();
|
||||
BottomSheet.Builder builder = new BottomSheet.Builder(ctx, false, themeDelegate);
|
||||
builder.setApplyTopPadding(false);
|
||||
|
||||
LinearLayout container = new LinearLayout(ctx);
|
||||
container.setOrientation(LinearLayout.VERTICAL);
|
||||
int pad = AndroidUtilities.dp(20);
|
||||
container.setPadding(pad, pad, pad, pad);
|
||||
|
||||
TextView titleView = new TextView(ctx);
|
||||
titleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack));
|
||||
titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
|
||||
titleView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||
titleView.setText(title);
|
||||
container.addView(titleView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
ScrollView scrollView = new ScrollView(ctx);
|
||||
scrollView.setFillViewport(true);
|
||||
TextView bodyView = new TextView(ctx);
|
||||
bodyView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack));
|
||||
bodyView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
|
||||
bodyView.setText(body);
|
||||
bodyView.setTextIsSelectable(true);
|
||||
bodyView.setLineSpacing(AndroidUtilities.dp(2), 1.1f);
|
||||
int bodyPad = AndroidUtilities.dp(4);
|
||||
bodyView.setPadding(bodyPad, bodyPad, bodyPad, bodyPad);
|
||||
scrollView.addView(bodyView, new ScrollView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
container.addView(scrollView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, AndroidUtilities.dp(260)));
|
||||
|
||||
LinearLayout actions = new LinearLayout(ctx);
|
||||
actions.setOrientation(LinearLayout.HORIZONTAL);
|
||||
actions.setGravity(Gravity.END);
|
||||
actions.setPadding(0, AndroidUtilities.dp(12), 0, 0);
|
||||
|
||||
actions.addView(createGeminiActionButton(ctx, LocaleController.getString("Copy", R.string.Copy), () -> {
|
||||
AndroidUtilities.addToClipboard(body);
|
||||
BulletinFactory.of(this).createSimpleBulletin(R.raw.copy, LocaleController.getString("TextCopied", R.string.TextCopied)).show();
|
||||
}));
|
||||
|
||||
actions.addView(createGeminiActionButton(ctx, LocaleController.getString("Paste", R.string.Paste), () -> {
|
||||
if (chatActivityEnterView != null && !TextUtils.isEmpty(body)) {
|
||||
chatActivityEnterView.setFieldText(body, true);
|
||||
}
|
||||
}));
|
||||
|
||||
actions.addView(createGeminiActionButton(ctx, LocaleController.getString("ShareFile", R.string.ShareFile), () -> {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
intent.putExtra(Intent.EXTRA_TEXT, body);
|
||||
ctx.startActivity(Intent.createChooser(intent, LocaleController.getString("ShareFile", R.string.ShareFile)));
|
||||
} catch (Exception e) {
|
||||
FileLog.e(e);
|
||||
}
|
||||
}));
|
||||
|
||||
container.addView(actions, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
builder.setCustomView(container);
|
||||
showDialog(builder.create());
|
||||
}
|
||||
|
||||
private View createGeminiActionButton(Context context, String text, Runnable onClick) {
|
||||
TextView button = new TextView(context);
|
||||
button.setText(text);
|
||||
button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||
button.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||
button.setTextColor(Theme.getColor(Theme.key_dialogTextBlack));
|
||||
int padH = AndroidUtilities.dp(14);
|
||||
int padV = AndroidUtilities.dp(10);
|
||||
button.setPadding(padH, padV, padH, padV);
|
||||
button.setBackground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(8), Theme.getColor(Theme.key_windowBackgroundWhite), Theme.getColor(Theme.key_dialogButtonSelector)));
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.leftMargin = AndroidUtilities.dp(8);
|
||||
button.setLayoutParams(params);
|
||||
button.setOnClickListener(v -> {
|
||||
if (onClick != null) {
|
||||
onClick.run();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(LocaleController.getString("Close", R.string.Close), null);
|
||||
return button;
|
||||
}
|
||||
|
||||
private void showAutoTranslateLanguagePicker() {
|
||||
if (getParentActivity() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[][] languages = {
|
||||
{"en", "English"},
|
||||
{"es", "Spanish"},
|
||||
{"fr", "French"},
|
||||
{"de", "German"},
|
||||
{"it", "Italian"},
|
||||
{"pt", "Portuguese"},
|
||||
{"ru", "Russian"},
|
||||
{"ja", "Japanese"},
|
||||
{"ko", "Korean"},
|
||||
{"zh", "Chinese"},
|
||||
{"ar", "Arabic"},
|
||||
{"hi", "Hindi"},
|
||||
{"tr", "Turkish"},
|
||||
{"pl", "Polish"},
|
||||
{"uk", "Ukrainian"},
|
||||
{"nl", "Dutch"},
|
||||
{"sv", "Swedish"},
|
||||
{"no", "Norwegian"},
|
||||
{"da", "Danish"},
|
||||
{"fi", "Finnish"}
|
||||
};
|
||||
|
||||
String[] options = new String[languages.length];
|
||||
for (int i = 0; i < languages.length; i++) {
|
||||
options[i] = languages[i][1];
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getParentActivity());
|
||||
builder.setTitle(LocaleController.getString(R.string.AutoTranslatePerChatLang));
|
||||
builder.setItems(options, (dialog, which) -> {
|
||||
String selectedLang = languages[which][0];
|
||||
OverConfig.setAutoTranslateOutgoingLang(dialog_id, selectedLang);
|
||||
BulletinFactory.of(ChatActivity.this).createSimpleBulletin(
|
||||
R.raw.done,
|
||||
LocaleController.getString(R.string.AutoTranslatePerChatLang) + ": " + languages[which][1]
|
||||
).show();
|
||||
});
|
||||
builder.setNegativeButton(LocaleController.getString("Cancel", R.string.Cancel), null);
|
||||
showDialog(builder.create());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -493,6 +493,8 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
||||
public ValueAnimator currentTopViewAnimation;
|
||||
@Nullable
|
||||
private ReplaceableIconDrawable botButtonDrawable;
|
||||
private com.overspend1.overgram.ui.components.SmartQuickRepliesView smartQuickRepliesView;
|
||||
private com.overspend1.overgram.ui.components.TranslationPropositionView translationPropositionView;
|
||||
|
||||
private CharSequence draftMessage;
|
||||
private boolean draftSearchWebpage;
|
||||
@@ -2615,6 +2617,23 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
||||
checkChannelRights();
|
||||
|
||||
createMessageEditText();
|
||||
|
||||
// Overgram: Initialize smart quick replies view
|
||||
if (OverConfig.smartQuickReplies) {
|
||||
smartQuickRepliesView = new com.overspend1.overgram.ui.components.SmartQuickRepliesView(context);
|
||||
smartQuickRepliesView.setOnReplySelectedListener(reply -> {
|
||||
if (messageEditText != null) {
|
||||
messageEditText.setText(reply);
|
||||
messageEditText.setSelection(reply.length());
|
||||
}
|
||||
smartQuickRepliesView.hide();
|
||||
});
|
||||
addView(smartQuickRepliesView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM, 0, 0, 0, 50));
|
||||
}
|
||||
|
||||
// Overgram: Initialize translation proposition view
|
||||
translationPropositionView = new com.overspend1.overgram.ui.components.TranslationPropositionView(context, resourcesProvider);
|
||||
addView(translationPropositionView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.BOTTOM, 8, 0, 8, 58));
|
||||
}
|
||||
|
||||
private void createCaptionLimitView() {
|
||||
@@ -3354,6 +3373,21 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
||||
return parentFragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overgram: Update smart quick replies based on the last received message
|
||||
*/
|
||||
public void updateSmartQuickReplies(MessageObject lastMessage) {
|
||||
if (OverConfig.smartQuickReplies && smartQuickRepliesView != null && lastMessage != null) {
|
||||
// Only show/update if input is empty
|
||||
if (messageEditText == null || messageEditText.length() == 0) {
|
||||
smartQuickRepliesView.updateRepliesForMessage(lastMessage);
|
||||
if (!smartQuickRepliesView.isShowing()) {
|
||||
smartQuickRepliesView.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBotMenu() {
|
||||
final boolean shouldBeExpanded = (messageEditText == null || TextUtils.isEmpty(messageEditText.getText())) && !(keyboardVisible || waitingForKeyboardOpen || isPopupShowing());
|
||||
if (shouldBeExpanded) {
|
||||
@@ -4208,6 +4242,21 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
||||
}
|
||||
delegate.onTextChanged(charSequence, before > count + 1 || (count - before) > 2);
|
||||
}
|
||||
|
||||
// Overgram: Handle smart quick replies visibility
|
||||
if (OverConfig.smartQuickReplies && smartQuickRepliesView != null) {
|
||||
if (charSequence.length() > 0) {
|
||||
// Hide quick replies when user starts typing
|
||||
if (smartQuickRepliesView.isShowing()) {
|
||||
smartQuickRepliesView.hide();
|
||||
}
|
||||
} else {
|
||||
// Show quick replies when input is empty
|
||||
if (!smartQuickRepliesView.isShowing()) {
|
||||
smartQuickRepliesView.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (innerTextChange != 2 && (count - before) > 1) {
|
||||
processChange = true;
|
||||
@@ -5374,6 +5423,39 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessageAfterTranslate(CharSequence message, boolean notify, int scheduleDate) {
|
||||
if (checkPremiumAnimatedEmoji(currentAccount, dialog_id, parentFragment, null, message)) {
|
||||
return;
|
||||
}
|
||||
if (processSendingText(message, notify, scheduleDate)) {
|
||||
if (delegate.hasForwardingMessages() || (scheduleDate != 0 && !isInScheduleMode()) || isInScheduleMode()) {
|
||||
if (messageEditText != null) {
|
||||
messageEditText.setText("");
|
||||
}
|
||||
if (delegate != null) {
|
||||
delegate.onMessageSend(message, notify, scheduleDate);
|
||||
}
|
||||
} else {
|
||||
messageTransitionIsRunning = false;
|
||||
AndroidUtilities.runOnUIThread(moveToSendStateRunnable = () -> {
|
||||
moveToSendStateRunnable = null;
|
||||
hideTopView(true);
|
||||
if (messageEditText != null) {
|
||||
messageEditText.setText("");
|
||||
}
|
||||
if (delegate != null) {
|
||||
delegate.onMessageSend(message, notify, scheduleDate);
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
lastTypingTimeSend = 0;
|
||||
} else if (forceShowSendButton) {
|
||||
if (delegate != null) {
|
||||
delegate.onMessageSend(null, notify, scheduleDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean premiumEmojiBulletin = true;
|
||||
private void sendMessageInternal(boolean notify, int scheduleDate) {
|
||||
if (slowModeTimer == Integer.MAX_VALUE && !isInScheduleMode()) {
|
||||
@@ -5430,6 +5512,75 @@ public class ChatActivityEnterView extends BlurredFrameLayout implements Notific
|
||||
if (checkPremiumAnimatedEmoji(currentAccount, dialog_id, parentFragment, null, message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Overgram: Check YagizTranslator first (Turkish ↔ English specialized)
|
||||
boolean shouldUseYagizTranslator = OverConfig.isYagizTranslatorEnabledForDialog(dialog_id) &&
|
||||
OverConfig.yagizTranslatorAutoDetect;
|
||||
|
||||
if (shouldUseYagizTranslator && message.length() > 0 && translationPropositionView != null && !translationPropositionView.isShowing()) {
|
||||
final CharSequence originalMessage = message;
|
||||
com.overspend1.overgram.translator.YagizTranslator.translateAuto(
|
||||
message.toString(),
|
||||
(translatedText, detectedLang) -> AndroidUtilities.runOnUIThread(() -> {
|
||||
if (translationPropositionView != null && !originalMessage.toString().equals(translatedText)) {
|
||||
translationPropositionView.showProposition(
|
||||
originalMessage.toString(),
|
||||
translatedText,
|
||||
accepted -> {
|
||||
if (messageEditText != null) {
|
||||
messageEditText.setText(accepted);
|
||||
}
|
||||
sendMessageAfterTranslate(accepted, notify, scheduleDate);
|
||||
},
|
||||
() -> {
|
||||
sendMessageAfterTranslate(originalMessage, notify, scheduleDate);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// No translation needed, send as-is
|
||||
sendMessageAfterTranslate(originalMessage, notify, scheduleDate);
|
||||
}
|
||||
}),
|
||||
error -> AndroidUtilities.runOnUIThread(() -> {
|
||||
BulletinFactory.of(parentFragment).createSimpleBulletin(R.raw.error, LocaleController.getString(R.string.AutoTranslateFailed)).show();
|
||||
sendMessageAfterTranslate(originalMessage, notify, scheduleDate);
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Overgram: Regular auto-translate outgoing messages
|
||||
if (OverConfig.isAutoTranslateOutgoing(dialog_id) && message.length() > 0 && translationPropositionView != null && !translationPropositionView.isShowing()) {
|
||||
String targetLang = OverConfig.getAutoTranslateOutgoingLang(dialog_id);
|
||||
if (targetLang != null && !targetLang.isEmpty()) {
|
||||
final CharSequence originalMessage = message;
|
||||
TranslatorUtils.translate(message, targetLang, translatedText -> AndroidUtilities.runOnUIThread(() -> {
|
||||
if (translationPropositionView != null && !originalMessage.toString().equals(translatedText.toString())) {
|
||||
translationPropositionView.showProposition(
|
||||
originalMessage.toString(),
|
||||
translatedText.toString(),
|
||||
accepted -> {
|
||||
if (messageEditText != null) {
|
||||
messageEditText.setText(accepted);
|
||||
}
|
||||
sendMessageAfterTranslate(accepted, notify, scheduleDate);
|
||||
},
|
||||
() -> {
|
||||
sendMessageAfterTranslate(originalMessage, notify, scheduleDate);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// No translation needed, send as-is
|
||||
sendMessageAfterTranslate(originalMessage, notify, scheduleDate);
|
||||
}
|
||||
}), () -> AndroidUtilities.runOnUIThread(() -> {
|
||||
BulletinFactory.of(parentFragment).createSimpleBulletin(R.raw.error, LocaleController.getString(R.string.AutoTranslateFailed)).show();
|
||||
sendMessageAfterTranslate(originalMessage, notify, scheduleDate);
|
||||
}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (processSendingText(message, notify, scheduleDate)) {
|
||||
if (delegate.hasForwardingMessages() || (scheduleDate != 0 && !isInScheduleMode()) || isInScheduleMode()) {
|
||||
if (messageEditText != null) {
|
||||
|
||||
@@ -36,6 +36,8 @@ import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.RenderEffect;
|
||||
import android.graphics.Shader.TileMode;
|
||||
import android.location.LocationManager;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
@@ -99,6 +101,7 @@ import com.overspend1.overgram.OverConfig;
|
||||
import com.overspend1.overgram.OverConstants;
|
||||
import com.overspend1.overgram.OverCustomHandlers;
|
||||
import com.overspend1.overgram.OverUtils;
|
||||
import one.overgram.messenger.ui.liquidglass.LiquidGlassHelper;
|
||||
|
||||
import org.telegram.PhoneFormat.PhoneFormat;
|
||||
import org.telegram.messenger.AccountInstance;
|
||||
@@ -491,6 +494,7 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati
|
||||
sideMenu.setItemAnimator(itemAnimator);
|
||||
sideMenu.setBackgroundColor(Theme.getColor(Theme.key_chats_menuBackground));
|
||||
sideMenuContainer.setBackgroundColor(Theme.getColor(Theme.key_chats_menuBackground));
|
||||
applyDrawerBlurIfNeeded();
|
||||
sideMenu.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
|
||||
sideMenu.setAllowItemsInteractionDuringAnimation(false);
|
||||
sideMenu.setAdapter(drawerLayoutAdapter = new DrawerLayoutAdapter(this, itemAnimator, drawerLayoutContainer));
|
||||
@@ -5647,6 +5651,9 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
LiquidGlassHelper.removeWindowBlur(getWindow());
|
||||
}
|
||||
isResumed = false;
|
||||
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.stopAllHeavyOperations, 4096);
|
||||
ApplicationLoader.mainInterfacePaused = true;
|
||||
@@ -5780,6 +5787,11 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (OverConfig.liquidGlassEnabled && OverConfig.liquidGlassApplyToSystemSurfaces && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
LiquidGlassHelper.applyWindowBlur(getWindow(), (int) Math.max(12, OverConfig.liquidGlassBlurRadius * 2));
|
||||
}
|
||||
applyDrawerBlurIfNeeded();
|
||||
applyChromeBlurIfNeeded();
|
||||
isResumed = true;
|
||||
if (onResumeStaticCallback != null) {
|
||||
onResumeStaticCallback.run();
|
||||
@@ -5856,6 +5868,40 @@ public class LaunchActivity extends BasePermissionsActivity implements INavigati
|
||||
invalidateTabletMode();
|
||||
}
|
||||
|
||||
private void applyDrawerBlurIfNeeded() {
|
||||
if (sideMenuContainer == null) {
|
||||
return;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && OverConfig.liquidGlassEnabled && OverConfig.liquidGlassApplyToSystemSurfaces) {
|
||||
float radius = Math.max(8f, OverConfig.liquidGlassBlurRadius * 1.5f);
|
||||
sideMenuContainer.setRenderEffect(RenderEffect.createBlurEffect(radius, radius, TileMode.CLAMP));
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
sideMenuContainer.setRenderEffect(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyChromeBlurIfNeeded() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
return;
|
||||
}
|
||||
RenderEffect effect = null;
|
||||
if (OverConfig.liquidGlassEnabled && OverConfig.liquidGlassApplyToSystemSurfaces) {
|
||||
float radius = Math.max(6f, OverConfig.liquidGlassBlurRadius * 1.2f);
|
||||
effect = RenderEffect.createBlurEffect(radius, radius, TileMode.CLAMP);
|
||||
}
|
||||
setRenderEffectSafe(actionBarLayout, effect);
|
||||
if (AndroidUtilities.isTablet()) {
|
||||
setRenderEffectSafe(rightActionBarLayout, effect);
|
||||
setRenderEffectSafe(layersActionBarLayout, effect);
|
||||
}
|
||||
}
|
||||
|
||||
private void setRenderEffectSafe(View view, RenderEffect effect) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && view != null) {
|
||||
view.setRenderEffect(effect);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidateTabletMode() {
|
||||
Boolean wasTablet = AndroidUtilities.getWasTablet();
|
||||
if (wasTablet == null) {
|
||||
|
||||
@@ -47,6 +47,24 @@
|
||||
<string name="RegexFiltersAddDescription">You can use site <![CDATA[<a href="https://regex101.com">regex101.com</a>]]> to fully test your regular expression.
|
||||
You can also use plain text, but don\'t forget to escape brackets.</string>
|
||||
<string name="RegexFiltersAddError">Regex syntax error</string>
|
||||
<string name="ProductivityHeader">Productivity & UX</string>
|
||||
<string name="SmartQuickReplies">Smart quick replies</string>
|
||||
<string name="SmartQuickRepliesHint">Show AI-suggested short replies in chat</string>
|
||||
<string name="AutoTranslateIncoming">Auto-translate incoming (default)</string>
|
||||
<string name="AutoTranslateIncomingHint">Automatically translate messages you receive</string>
|
||||
<string name="AutoTranslateOutgoing">Auto-translate outgoing (default)</string>
|
||||
<string name="AutoTranslateOutgoingHint">Automatically translate before sending</string>
|
||||
<string name="AutoTranslateOutgoingLang">Default outgoing language</string>
|
||||
<string name="AutoTranslateOutgoingLangHint">Target language for outgoing messages</string>
|
||||
<string name="AutoTranslateIncomingLang">Your language (for incoming)</string>
|
||||
<string name="AutoTranslateIncomingLangHint">Your preferred language for reading messages</string>
|
||||
<string name="AutoTranslatePerChatIncoming">Auto-translate incoming for this chat</string>
|
||||
<string name="AutoTranslatePerChatOutgoing">Auto-translate outgoing for this chat</string>
|
||||
<string name="AutoTranslatePerChatLang">Set outgoing language for this chat</string>
|
||||
<string name="AutoTranslateEnabled">Auto-translate enabled</string>
|
||||
<string name="AutoTranslateDisabled">Auto-translate disabled</string>
|
||||
<string name="AutoTranslatingMessage">Translating...</string>
|
||||
<string name="AutoTranslateFailed">Translation failed. Sending original message.</string>
|
||||
<string name="AyuSyncHeader">OvergramSync</string>
|
||||
<string name="AyuSyncStatusTitle">Sync status</string>
|
||||
<string name="AyuSyncStatusOk">connected</string>
|
||||
@@ -112,6 +130,7 @@
|
||||
<string name="LiquidGlassEnableHint">Apply glassmorphism effects throughout the app</string>
|
||||
<string name="LiquidGlassApplyToChatBubbles">Chat bubbles</string>
|
||||
<string name="LiquidGlassApplyToDialogs">Dialog list</string>
|
||||
<string name="LiquidGlassApplyToSystem">System surfaces (headers, drawers)</string>
|
||||
<string name="LiquidGlassPreset">Preset style</string>
|
||||
<string name="LiquidGlassPresetSubtle">Subtle</string>
|
||||
<string name="LiquidGlassPresetStandard">Standard</string>
|
||||
@@ -124,5 +143,14 @@
|
||||
<string name="LiquidGlassCustomization">Advanced customization</string>
|
||||
<string name="LiquidGlassPerformance">Performance</string>
|
||||
<string name="LiquidGlassPerformanceHint">Liquid glass effects are GPU-accelerated for smooth 60 FPS</string>
|
||||
<string name="OvergramQuickActions">Quick actions</string>
|
||||
<string name="OvergramQuickActionsHint">Fast access to AI and glass settings</string>
|
||||
<string name="OvergramQuickActionToggleGlass">Toggle liquid glass</string>
|
||||
<string name="OvergramQuickActionToggleGhost">Toggle ghost mode</string>
|
||||
<string name="OvergramQuickActionOpenGlass">Open liquid glass settings</string>
|
||||
<string name="OvergramQuickActionOpenAi">Open AI & translation settings</string>
|
||||
<string name="OriginalText">Original</string>
|
||||
<string name="TranslatedText">Translated</string>
|
||||
<string name="AcceptTranslation">Use translation</string>
|
||||
<string name="CancelTranslation">Keep original</string>
|
||||
</resources>
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
<string name="OvergramGeminiModelTitle">Model</string>
|
||||
<string name="OvergramGeminiMissingKey">Add your Gemini API key in AI settings first</string>
|
||||
<string name="OvergramGeminiWorking">Contacting Gemini...</string>
|
||||
<string name="OvergramGeminiNoInput">Select a message or type something first</string>
|
||||
<string name="OvergramGeminiEmptyResponse">Gemini did not return a reply. Try again.</string>
|
||||
<string name="OvergramAiEnableChat">Enable AI for this chat</string>
|
||||
<string name="OvergramAiDisableChat">Disable AI for this chat</string>
|
||||
<string name="OvergramAiEnabledChat">AI enabled for this chat</string>
|
||||
|
||||
Reference in New Issue
Block a user