diff --git a/.github/workflows/tandroid.yml b/.github/workflows/tandroid.yml
index 0870735d424..b273f8a911f 100644
--- a/.github/workflows/tandroid.yml
+++ b/.github/workflows/tandroid.yml
@@ -68,6 +68,8 @@ jobs:
echo "CHECK_UPDATES=${{ matrix.updates }}" >> $vars
echo "USER_ID_OWNER=${{ secrets.USER_ID_OWNER }}" >> $vars
echo "UPDATE_CHANNEL_USERNAME=${{ secrets.UPDATE_CHANNEL_USERNAME }}" >> $vars
+ echo "LASTFM_API_KEY=${{ secrets.LASTFM_API_KEY }}" >> $vars
+ echo "LASTFM_API_SECRET=${{ secrets.LASTFM_API_SECRET }}" >> $vars
###
echo $ADDITIONAL_BUILD_NUMBER
diff --git a/BUILDING.md b/BUILDING.md
new file mode 100644
index 00000000000..09201cef499
--- /dev/null
+++ b/BUILDING.md
@@ -0,0 +1,39 @@
+## Building instructions for Debian 13
+
+1. Install necessary software
+ - `sudo apt install cmake golang gperf meson ninja-build sdkmanager wget yasm`
+ - `sudo apt install google-android-platform-35-installer google-android-build-tools-35.0.0-installer google-android-ndk-r21e-installer google-android-ndk-r23c-installer openjdk-21-jdk-headless`
+
+2. Don't forget to include the submodules when you clone:
+ - `git clone --recursive https://github.com/forkgram/TelegramAndroid.git`
+
+3. Build native FFmpeg and BoringSSL dependencies:
+ - Go to the `TMessagesProj/jni` folder and execute the following (define the paths to your NDK and Ninja):
+
+ ```
+ export NDK=/usr/lib/android-sdk/ndk/21.4.7075529
+ export NINJA_PATH=/usr/bin/ninja
+ export ANDROID_SDK=/usr/lib/android-sdk
+ export ANDROID_HOME=/usr/lib/android-sdk
+ sudo sdkmanager "cmake;3.22.1" --sdk_root /usr/lib/android-sdk
+ ./build_libvpx_clang.sh
+ ./build_ffmpeg_clang.sh
+ ./patch_ffmpeg.sh
+ ./patch_boringssl.sh
+ ./build_boringssl.sh
+ ```
+
+4. If you want to publish a modified version of Telegram:
+ - You should get **your own API key** here: https://core.telegram.org/api/obtaining_api_id and edit a file called `gradle.properties` in the source root directory.
+ The contents should look like this:
+ ```
+ APP_ID = 12345
+ APP_HASH = aaaaaaaabbbbbbccccccfffffff001122
+ ```
+ - Do not use the name Telegram and the standard logo (white paper plane in a blue circle) for your app — or make sure your users understand that it is unofficial
+ - Take good care of your users' data and privacy
+ - **Please remember to publish your code too in order to comply with the licenses**
+
+The project can be built with Android Studio or from the command line with gradle:
+
+`./gradlew assembleAfatRelease`
\ No newline at end of file
diff --git a/README.md b/README.md
index d2ebd9af0fc..da7f091675e 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,10 @@ Fork Client is a fork of the official Telegram for Android application.
- Added ability to see unread count when you want to mark as read multiple dialogs.
- Option to directly open the archive on pulldown
- PiP mode for YouTube's in-app player
+- Added an option to show colored dots to quickly see when a person was last online
+ - Yellow dot: last seen 15 minutes ago or less
+ - Orange dot: last seen 30 minutes ago or less
+ - Red dot: last seen 60 minutes ago or less
### Privacy Features:
@@ -48,3 +52,6 @@ Fork Client is a fork of the official Telegram for Android application.
## Downloads:
You can download binaries from Releases or from my [Telegram channel Forkgram](https://t.me/forkgram).
+
+## Building instructions for Debian 13:
+[BUILDING.md](BUILDING.md)
\ No newline at end of file
diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle
index 3fca1b2bb69..47af7a82403 100644
--- a/TMessagesProj/build.gradle
+++ b/TMessagesProj/build.gradle
@@ -7,6 +7,12 @@ apply plugin: 'kotlin-android'
repositories {
mavenCentral()
google()
+ maven {
+ url 'https://www.jitpack.io'
+ content {
+ includeModule 'com.github.UnifiedPush', 'android-connector'
+ }
+ }
}
configurations {
@@ -18,6 +24,8 @@ configurations.all {
}
dependencies {
+ coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
+
implementation 'androidx.core:core:1.16.0'
implementation 'androidx.interpolator:interpolator:1.0.0'
implementation 'androidx.fragment:fragment:1.2.0'
@@ -55,6 +63,7 @@ dependencies {
because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib")
}
}
+ implementation 'com.github.UnifiedPush:android-connector:2.3.1'
implementation 'org.osmdroid:osmdroid-android:6.1.20'
}
@@ -83,8 +92,8 @@ android {
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_17
- targetCompatibility JavaVersion.VERSION_17
+ sourceCompatibility JavaVersion.VERSION_21
+ targetCompatibility JavaVersion.VERSION_21
coreLibraryDesugaringEnabled true
}
@@ -106,6 +115,8 @@ android {
buildConfigField 'String', 'USER_REPO', getStringForConfig(USER_REPO)
buildConfigField 'int', 'CHECK_UPDATES', CHECK_UPDATES
buildConfigField 'String', 'UPDATE_CHANNEL_USERNAME', getStringForConfig(UPDATE_CHANNEL_USERNAME)
+ buildConfigField 'String', 'LASTFM_API_KEY', getStringForConfig(LASTFM_API_KEY)
+ buildConfigField 'String', 'LASTFM_API_SECRET', getStringForConfig(LASTFM_API_SECRET)
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
@@ -116,6 +127,7 @@ android {
buildConfigField 'boolean', 'SKIP_DNS_RESOLVER', (Utils['isFdroid']() ? 'true' : 'false')
buildConfigField 'boolean', 'SKIP_INTERNAL_BROWSER_BY_DEFAULT', (Utils['isFdroid']() ? 'true' : 'false')
+ manifestPlaceholders = [appLabel: Utils['isFdroid']() ? "@string/AppNameFdroid" : "@string/AppName"]
externalNativeBuild {
cmake {
diff --git a/TMessagesProj/src/main/AndroidManifest.xml b/TMessagesProj/src/main/AndroidManifest.xml
index 3889435ce68..8d913f2748c 100644
--- a/TMessagesProj/src/main/AndroidManifest.xml
+++ b/TMessagesProj/src/main/AndroidManifest.xml
@@ -79,7 +79,6 @@
-
@@ -639,6 +638,15 @@ e
+
+
+
+
+
+
+
+
+
{
if (getPushProvider().hasServices()) {
@@ -426,7 +425,7 @@ private void initPushServices() {
}
}, 1000);
}
-
+/*
private boolean checkPlayServices() {
try {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageHeardReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageHeardReceiver.java
index 29f60ff7a30..bb63f0dcaf5 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageHeardReceiver.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/AutoMessageHeardReceiver.java
@@ -11,8 +11,11 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
import org.telegram.tgnet.TLRPC;
+import org.telegram.tgnet.tl.TL_account;
public class AutoMessageHeardReceiver extends BroadcastReceiver {
@@ -35,6 +38,7 @@ public void onReceive(Context context, Intent intent) {
accountInstance.getMessagesController().putUser(user1, true);
MessagesController.getInstance(currentAccount).markDialogAsRead(dialogId, maxId, maxId, 0, false, 0, 0, true, 0);
MessagesController.getInstance(currentAccount).markReactionsAsRead(dialogId, 0);
+ scheduleOfflineStatus(currentAccount);
});
});
return;
@@ -48,6 +52,7 @@ public void onReceive(Context context, Intent intent) {
accountInstance.getMessagesController().putChat(chat1, true);
MessagesController.getInstance(currentAccount).markDialogAsRead(dialogId, maxId, maxId, 0, false, 0, 0, true, 0);
MessagesController.getInstance(currentAccount).markReactionsAsRead(dialogId, 0);
+ scheduleOfflineStatus(currentAccount);
});
});
return;
@@ -55,5 +60,25 @@ public void onReceive(Context context, Intent intent) {
}
MessagesController.getInstance(currentAccount).markDialogAsRead(dialogId, maxId, maxId, 0, false, 0, 0, true, 0);
MessagesController.getInstance(currentAccount).markReactionsAsRead(dialogId, 0);
+ scheduleOfflineStatus(currentAccount);
+ }
+
+ private void scheduleOfflineStatus(int currentAccount) {
+ try {
+ if (!UserConfig.isValidAccount(currentAccount)) {
+ return;
+ }
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ try {
+ TL_account.updateStatus req = new TL_account.updateStatus();
+ req.offline = true;
+ org.telegram.tgnet.ConnectionsManager.getInstance(currentAccount).sendRequest(req, null);
+ } catch (Exception e) {
+ FileLog.e(e);
+ }
+ }, 5000);
+ } catch (Exception e) {
+ FileLog.e(e);
+ }
}
}
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java
index 5b914f3b595..37d7f90168d 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/BuildVars.java
@@ -36,6 +36,10 @@ public class BuildVars {
public static String HUAWEI_STORE_URL = "https://appgallery.huawei.com/app/C101184875";
public static String GOOGLE_AUTH_CLIENT_ID = "760348033671-81kmi3pi84p11ub8hp9a1funsv0rn2p9.apps.googleusercontent.com";
+ // Last.fm API constants
+ public static String LASTFM_API_KEY = BuildConfig.LASTFM_API_KEY;
+ public static String LASTFM_API_SECRET = BuildConfig.LASTFM_API_SECRET;
+ public static String LASTFM_API_URL = "https://ws.audioscrobbler.com/2.0/";
public static String UPDATE_CHANNEL_USERNAME = BuildConfig.UPDATE_CHANNEL_USERNAME;
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/LastFmHelper.java b/TMessagesProj/src/main/java/org/telegram/messenger/LastFmHelper.java
new file mode 100644
index 00000000000..17765a96fb9
--- /dev/null
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/LastFmHelper.java
@@ -0,0 +1,349 @@
+package org.telegram.messenger;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.security.MessageDigest;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class LastFmHelper {
+
+ private static LastFmHelper instance;
+ private SharedPreferences prefs;
+ private long lastApiCallTime = 0;
+ private static final long API_CALL_INTERVAL = 30000; // 30 seconds
+
+ public static LastFmHelper getInstance() {
+ if (instance == null) {
+ instance = new LastFmHelper();
+ }
+ return instance;
+ }
+
+ private LastFmHelper() {
+ if (ApplicationLoader.applicationContext != null) {
+ prefs = ApplicationLoader.applicationContext.getSharedPreferences("lastfm", Context.MODE_PRIVATE);
+ }
+ }
+
+ public boolean isLoggedIn() {
+ return prefs != null && prefs.getBoolean("logged_in", false);
+ }
+
+ public String getUsername() {
+ return prefs != null ? prefs.getString("username", "") : "";
+ }
+
+ public void logout() {
+ if (prefs != null) {
+ prefs.edit().clear().apply();
+ }
+ }
+
+ public void login(String username, String password, LoginCallback callback) {
+ Utilities.globalQueue.postRunnable(() -> {
+ try {
+ // Step 1: Get token
+ String token = getToken();
+ if (token == null) {
+ AndroidUtilities.runOnUIThread(() -> callback.onError("Failed to get token"));
+ return;
+ }
+
+ // Step 2: Get session
+ String sessionKey = getSession(username, password, token);
+ if (sessionKey != null) {
+ prefs.edit()
+ .putBoolean("logged_in", true)
+ .putString("username", username)
+ .putString("session_key", sessionKey)
+ .apply();
+ AndroidUtilities.runOnUIThread(() -> callback.onSuccess());
+ } else {
+ AndroidUtilities.runOnUIThread(() -> callback.onError("Invalid credentials"));
+ }
+ } catch (Exception e) {
+ FileLog.e(e);
+ AndroidUtilities.runOnUIThread(() -> callback.onError(e.getMessage()));
+ }
+ });
+ }
+
+ public interface LoginCallback {
+ void onSuccess();
+ void onError(String error);
+ }
+
+ public void scrobbleTrack(String artist, String track, String album, long timestamp) {
+ if (!isLoggedIn() || artist == null || track == null) {
+ return;
+ }
+
+ if (!canMakeApiCall()) {
+ return;
+ }
+
+ Map params = new HashMap<>();
+ params.put("method", "track.scrobble");
+ params.put("api_key", BuildVars.LASTFM_API_KEY);
+ params.put("artist", artist);
+ params.put("track", track);
+ if (album != null) {
+ params.put("album", album);
+ }
+ params.put("timestamp", String.valueOf(timestamp));
+ params.put("sk", getSessionKey());
+ params.put("api_sig", generateApiSignature(params));
+
+ Utilities.globalQueue.postRunnable(() -> {
+ try {
+ sendPostRequestVoid(params);
+ if (BuildVars.LOGS_ENABLED) {
+ FileLog.d("Last.fm scrobbled: " + artist + " - " + track);
+ }
+ } catch (Exception e) {
+ FileLog.e(e);
+ }
+ });
+ }
+
+ public void updateNowPlaying(String artist, String track, String album) {
+ if (!isLoggedIn() || artist == null || track == null) {
+ return;
+ }
+
+ if (!canMakeApiCall()) {
+ return;
+ }
+
+ Map params = new HashMap<>();
+ params.put("method", "track.updateNowPlaying");
+ params.put("api_key", BuildVars.LASTFM_API_KEY);
+ params.put("artist", artist);
+ params.put("track", track);
+ if (album != null) {
+ params.put("album", album);
+ }
+ params.put("sk", getSessionKey());
+ params.put("api_sig", generateApiSignature(params));
+
+ Utilities.globalQueue.postRunnable(() -> {
+ try {
+ sendPostRequestVoid(params);
+ if (BuildVars.LOGS_ENABLED) {
+ FileLog.d("Last.fm now playing: " + artist + " - " + track);
+ }
+ } catch (Exception e) {
+ FileLog.e(e);
+ }
+ });
+ }
+
+ private String getSessionKey() {
+ return prefs != null ? prefs.getString("session_key", "") : "";
+ }
+
+ private boolean canMakeApiCall() {
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - lastApiCallTime >= API_CALL_INTERVAL) {
+ lastApiCallTime = currentTime;
+ return true;
+ }
+ return false;
+ }
+
+ public String generateApiSignature(Map params) {
+ TreeMap sortedParams = new TreeMap<>(params);
+ StringBuilder signature = new StringBuilder();
+
+ for (Map.Entry entry : sortedParams.entrySet()) {
+ signature.append(entry.getKey()).append(entry.getValue());
+ }
+ signature.append(BuildVars.LASTFM_API_SECRET);
+
+ return md5(signature.toString());
+ }
+
+ private String md5(String input) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ byte[] messageDigest = md.digest(input.getBytes());
+ StringBuilder hexString = new StringBuilder();
+ for (byte b : messageDigest) {
+ String hex = Integer.toHexString(0xff & b);
+ if (hex.length() == 1) {
+ hexString.append('0');
+ }
+ hexString.append(hex);
+ }
+ return hexString.toString();
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+ private String getToken() {
+ try {
+ Map params = new HashMap<>();
+ params.put("method", "auth.getToken");
+ params.put("api_key", BuildVars.LASTFM_API_KEY);
+ params.put("api_sig", generateApiSignature(params));
+
+ String response = sendGetRequest(params);
+ // Parse XML response to get token
+ // Simplified implementation - real project needs XML parser
+ if (response != null && response.contains("")) {
+ int start = response.indexOf("") + 7;
+ int end = response.indexOf("");
+ if (start > 6 && end > start) {
+ return response.substring(start, end);
+ }
+ }
+ } catch (Exception e) {
+ FileLog.e(e);
+ }
+ return null;
+ }
+
+ private String getSession(String username, String password, String token) {
+ try {
+ Map params = new HashMap<>();
+ params.put("method", "auth.getMobileSession");
+ params.put("api_key", BuildVars.LASTFM_API_KEY);
+ params.put("username", username);
+ params.put("password", password);
+ params.put("api_sig", generateApiSignature(params));
+
+ String response = sendPostRequest(params);
+ // Parse XML response to get session key
+ if (response != null && response.contains("")) {
+ int start = response.indexOf("") + 5;
+ int end = response.indexOf("");
+ if (start > 4 && end > start) {
+ return response.substring(start, end);
+ }
+ }
+ } catch (Exception e) {
+ FileLog.e(e);
+ }
+ return null;
+ }
+
+ private String sendGetRequest(Map params) {
+ try {
+ StringBuilder urlBuilder = new StringBuilder(BuildVars.LASTFM_API_URL + "?");
+ for (Map.Entry entry : params.entrySet()) {
+ if (urlBuilder.length() > BuildVars.LASTFM_API_URL.length() + 1) {
+ urlBuilder.append('&');
+ }
+ urlBuilder.append(java.net.URLEncoder.encode(entry.getKey(), "UTF-8"));
+ urlBuilder.append('=');
+ urlBuilder.append(java.net.URLEncoder.encode(entry.getValue(), "UTF-8"));
+ }
+
+ java.net.URL url = new java.net.URL(urlBuilder.toString());
+ java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+
+ int responseCode = connection.getResponseCode();
+ if (responseCode == 200) {
+ java.io.BufferedReader reader = new java.io.BufferedReader(
+ new java.io.InputStreamReader(connection.getInputStream()));
+ StringBuilder response = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ response.append(line);
+ }
+ reader.close();
+ connection.disconnect();
+ return response.toString();
+ }
+ connection.disconnect();
+ } catch (Exception e) {
+ FileLog.e(e);
+ }
+ return null;
+ }
+
+ private String sendPostRequest(Map params) {
+ try {
+ java.net.URL url = new java.net.URL(BuildVars.LASTFM_API_URL);
+ java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(true);
+ connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+
+ StringBuilder postData = new StringBuilder();
+ for (Map.Entry entry : params.entrySet()) {
+ if (postData.length() != 0) {
+ postData.append('&');
+ }
+ postData.append(java.net.URLEncoder.encode(entry.getKey(), "UTF-8"));
+ postData.append('=');
+ postData.append(java.net.URLEncoder.encode(entry.getValue(), "UTF-8"));
+ }
+
+ byte[] postDataBytes = postData.toString().getBytes("UTF-8");
+ connection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
+ connection.getOutputStream().write(postDataBytes);
+
+ int responseCode = connection.getResponseCode();
+ if (responseCode == 200) {
+ java.io.BufferedReader reader = new java.io.BufferedReader(
+ new java.io.InputStreamReader(connection.getInputStream()));
+ StringBuilder response = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ response.append(line);
+ }
+ reader.close();
+ connection.disconnect();
+ return response.toString();
+ }
+
+ if (BuildVars.LOGS_ENABLED) {
+ FileLog.d("Last.fm API response code: " + responseCode);
+ }
+
+ connection.disconnect();
+ } catch (Exception e) {
+ FileLog.e(e);
+ }
+ return null;
+ }
+
+ private void sendPostRequestVoid(Map params) {
+ try {
+ java.net.URL url = new java.net.URL(BuildVars.LASTFM_API_URL);
+ java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(true);
+ connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
+
+ StringBuilder postData = new StringBuilder();
+ for (Map.Entry entry : params.entrySet()) {
+ if (postData.length() != 0) {
+ postData.append('&');
+ }
+ postData.append(java.net.URLEncoder.encode(entry.getKey(), "UTF-8"));
+ postData.append('=');
+ postData.append(java.net.URLEncoder.encode(entry.getValue(), "UTF-8"));
+ }
+
+ byte[] postDataBytes = postData.toString().getBytes("UTF-8");
+ connection.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
+ connection.getOutputStream().write(postDataBytes);
+
+ int responseCode = connection.getResponseCode();
+ if (BuildVars.LOGS_ENABLED) {
+ FileLog.d("Last.fm API response code: " + responseCode);
+ }
+
+ connection.disconnect();
+ } catch (Exception e) {
+ FileLog.e(e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java
index 142be2944c8..3003e97bfd8 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaController.java
@@ -3808,6 +3808,18 @@ public void onStateChanged(boolean playWhenReady, int playbackState) {
if (playbackState == ExoPlayer.STATE_ENDED || (playbackState == ExoPlayer.STATE_IDLE || playbackState == ExoPlayer.STATE_BUFFERING) && playWhenReady && messageObject.audioProgress >= 0.999f) {
messageObject.audioProgress = 1f;
NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingProgressDidChanged, messageObject.getId(), 0);
+
+ if (messageObject.isMusic() && messageObject.getDuration() > 30) {
+ String artist = messageObject.getMusicAuthor();
+ String track = messageObject.getMusicTitle();
+ String album = null;
+ if (audioInfo != null && !TextUtils.isEmpty(audioInfo.getAlbum())) {
+ album = audioInfo.getAlbum();
+ }
+ long timestamp = System.currentTimeMillis() / 1000;
+ LastFmHelper.getInstance().scrobbleTrack(artist, track, album, timestamp);
+ }
+
final boolean restored = restoreMusicPlaylistState();
if (!restored) {
if (!playlist.isEmpty() && (playlist.size() > 1 || !messageObject.isVoice())) {
@@ -3967,6 +3979,16 @@ public boolean needUpdate() {
}
startProgressTimer(playingMessageObject);
NotificationCenter.getInstance(messageObject.currentAccount).postNotificationName(NotificationCenter.messagePlayingDidStart, messageObject, oldMessageObject);
+
+ if (messageObject.isMusic()) {
+ String artist = messageObject.getMusicAuthor();
+ String track = messageObject.getMusicTitle();
+ String album = null;
+ if (audioInfo != null && !TextUtils.isEmpty(audioInfo.getAlbum())) {
+ album = audioInfo.getAlbum();
+ }
+ LastFmHelper.getInstance().updateNowPlaying(artist, track, album);
+ }
if (videoPlayer != null) {
try {
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java
index 357ad2ebbc5..adb93f88191 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/MediaDataController.java
@@ -4933,15 +4933,17 @@ public void buildShortcuts() {
if (Build.VERSION.SDK_INT < 23) {
return;
}
- int maxShortcuts = ShortcutManagerCompat.getMaxShortcutCountPerActivity(ApplicationLoader.applicationContext) - 2;
+ int maxShortcuts = ShortcutManagerCompat.getMaxShortcutCountPerActivity(ApplicationLoader.applicationContext) - 1;
if (maxShortcuts <= 0) {
maxShortcuts = 5;
}
- ArrayList hintsFinal = new ArrayList<>();
- if (SharedConfig.passcodeHash.length() <= 0) {
- for (int a = 0; a < hints.size(); a++) {
- hintsFinal.add(hints.get(a));
- if (hintsFinal.size() == maxShortcuts - 2) {
+
+ // Get available accounts instead of hints
+ ArrayList accountsList = new ArrayList<>();
+ for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) {
+ if (UserConfig.getInstance(a).isClientActivated()) {
+ accountsList.add(a);
+ if (accountsList.size() >= maxShortcuts) {
break;
}
}
@@ -4963,10 +4965,8 @@ public void buildShortcuts() {
} else {
List currentShortcuts = ShortcutManagerCompat.getDynamicShortcuts(ApplicationLoader.applicationContext);
if (currentShortcuts != null && !currentShortcuts.isEmpty()) {
- newShortcutsIds.add("compose");
- for (int a = 0; a < hintsFinal.size(); a++) {
- TLRPC.TL_topPeer hint = hintsFinal.get(a);
- newShortcutsIds.add("did3_" + MessageObject.getPeerId(hint.peer));
+ for (int a = 0; a < accountsList.size(); a++) {
+ newShortcutsIds.add("account_" + accountsList.get(a));
}
for (int a = 0; a < currentShortcuts.size(); a++) {
String id = currentShortcuts.get(a).getId();
@@ -4985,71 +4985,26 @@ public void buildShortcuts() {
}
}
- Intent intent = new Intent(ApplicationLoader.applicationContext, LaunchActivity.class);
- intent.setAction("new_dialog");
ArrayList arrayList = new ArrayList<>();
- ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(ApplicationLoader.applicationContext, "compose")
- .setShortLabel(LocaleController.getString(R.string.NewConversationShortcut))
- .setLongLabel(LocaleController.getString(R.string.NewConversationShortcut))
- .setIcon(IconCompat.createWithResource(ApplicationLoader.applicationContext, R.drawable.shortcut_compose))
- .setRank(0)
- .setIntent(intent)
- .build();
- if (recreateShortcuts) {
- ShortcutManagerCompat.pushDynamicShortcut(ApplicationLoader.applicationContext, shortcut);
- } else {
- arrayList.add(shortcut);
- if (shortcutsToUpdate.contains("compose")) {
- ShortcutManagerCompat.updateShortcuts(ApplicationLoader.applicationContext, arrayList);
- } else {
- ShortcutManagerCompat.addDynamicShortcuts(ApplicationLoader.applicationContext, arrayList);
- }
- arrayList.clear();
- }
-
- HashSet category = new HashSet<>(1);
- category.add(SHORTCUT_CATEGORY);
-
- for (int a = 0; a < hintsFinal.size(); a++) {
- Intent shortcutIntent = new Intent(ApplicationLoader.applicationContext, OpenChatReceiver.class);
- TLRPC.TL_topPeer hint = hintsFinal.get(a);
+ for (int a = 0; a < accountsList.size(); a++) {
+ int accountNum = accountsList.get(a);
+ Intent shortcutIntent = new Intent(ApplicationLoader.applicationContext, LaunchActivity.class);
+ shortcutIntent.setAction("switch_account");
+ shortcutIntent.putExtra("currentAccount", accountNum);
+ shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- TLRPC.User user = null;
- TLRPC.Chat chat = null;
- long peerId = MessageObject.getPeerId(hint.peer);
- if (DialogObject.isUserDialog(peerId)) {
- shortcutIntent.putExtra("userId", peerId);
- user = getMessagesController().getUser(peerId);
- } else {
- chat = getMessagesController().getChat(-peerId);
- shortcutIntent.putExtra("chatId", -peerId);
- }
- if ((user == null || UserObject.isDeleted(user)) && chat == null) {
+ TLRPC.User user = UserConfig.getInstance(accountNum).getCurrentUser();
+ if (user == null) {
continue;
}
- String name;
+ String name = ContactsController.formatName(user.first_name, user.last_name);
TLRPC.FileLocation photo = null;
-
- if (user != null) {
- name = ContactsController.formatName(user.first_name, user.last_name);
- if (user.photo != null) {
- photo = user.photo.photo_small;
- }
- } else {
- name = chat.title;
- if (chat.photo != null) {
- photo = chat.photo.photo_small;
- }
+ if (user.photo != null) {
+ photo = user.photo.photo_small;
}
- shortcutIntent.putExtra("currentAccount", currentAccount);
- shortcutIntent.setAction("com.tmessages.openchat" + peerId);
- shortcutIntent.putExtra("dialogId", peerId);
- shortcutIntent.putExtra("hash", SharedConfig.directShareHash);
- shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
Bitmap bitmap = null;
if (photo != null) {
try {
@@ -5083,18 +5038,16 @@ public void buildShortcuts() {
}
}
- String id = "did3_" + peerId;
+ String id = "account_" + accountNum;
if (TextUtils.isEmpty(name)) {
- name = " ";
+ name = "Account " + (accountNum + 1);
}
ShortcutInfoCompat.Builder builder = new ShortcutInfoCompat.Builder(ApplicationLoader.applicationContext, id)
.setShortLabel(name)
.setLongLabel(name)
- .setRank(1 + a)
+ .setRank(a)
.setIntent(shortcutIntent);
- if (SharedConfig.directShare) {
- builder.setCategories(category);
- }
+
if (bitmap != null) {
builder.setIcon(IconCompat.createWithBitmap(bitmap));
} else {
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java b/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java
index 780c255e1bc..38f68653f88 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/PushListenerController.java
@@ -20,21 +20,26 @@
import org.telegram.tgnet.SerializedData;
import org.telegram.tgnet.TLRPC;
+import org.unifiedpush.android.connector.UnifiedPush;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
import java.util.concurrent.CountDownLatch;
@Keep
public class PushListenerController {
public static final int PUSH_TYPE_FIREBASE = 2,
+ PUSH_TYPE_SIMPLE = 4,
PUSH_TYPE_HUAWEI = 13;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
PUSH_TYPE_FIREBASE,
+ PUSH_TYPE_SIMPLE,
PUSH_TYPE_HUAWEI
})
public @interface PushType {}
@@ -62,7 +67,7 @@ public static void sendRegistrationToServer(@PushType int pushType, String token
if (userConfig.getClientUserId() != 0) {
final int currentAccount = a;
if (sendStat) {
- String tag = pushType == PUSH_TYPE_FIREBASE ? "fcm" : "hcm";
+ String tag = pushType == PUSH_TYPE_FIREBASE ? "fcm" : (pushType == PUSH_TYPE_HUAWEI ? "hcm" : "up");
TLRPC.TL_help_saveAppLog req = new TLRPC.TL_help_saveAppLog();
TLRPC.TL_inputAppEvent event = new TLRPC.TL_inputAppEvent();
event.time = SharedConfig.pushStringGetTimeStart;
@@ -90,7 +95,7 @@ public static void sendRegistrationToServer(@PushType int pushType, String token
}
public static void processRemoteMessage(@PushType int pushType, String data, long time) {
- String tag = pushType == PUSH_TYPE_FIREBASE ? "FCM" : "HCM";
+ String tag = pushType == PUSH_TYPE_FIREBASE ? "FCM" : (pushType == PUSH_TYPE_HUAWEI ? "HCM" : "UP");
if (BuildVars.LOGS_ENABLED) {
FileLog.d(tag + " PRE START PROCESSING");
}
@@ -1689,4 +1694,64 @@ public boolean hasServices() {
return hasServices;*/
}
}
+ public final static class UnifiedPushListenerServiceProvider implements IPushListenerServiceProvider {
+ public final static UnifiedPushListenerServiceProvider INSTANCE = new UnifiedPushListenerServiceProvider();
+ private final static UnifiedPushReceiver mReceiver = new UnifiedPushReceiver();
+
+ private UnifiedPushListenerServiceProvider(){};
+
+ @Override
+ public boolean hasServices() {
+ return !UnifiedPush.getDistributors(ApplicationLoader.applicationContext, new ArrayList()).isEmpty();
+ }
+
+ @Override
+ public String getLogTitle() {
+ return "UnifiedPush";
+ }
+
+ @Override
+ public void onRequestPushToken() {
+ if (SharedConfig.disableUnifiedPush) {
+ UnifiedPush.unregisterApp(ApplicationLoader.applicationContext, "default");
+ } else {
+ String currentPushString = SharedConfig.pushString;
+ if (!TextUtils.isEmpty(currentPushString)) {
+ if (BuildVars.DEBUG_PRIVATE_VERSION && BuildVars.LOGS_ENABLED) {
+ FileLog.d("UnifiedPush endpoint = " + currentPushString);
+ }
+ } else {
+ if (BuildVars.LOGS_ENABLED) {
+ FileLog.d("No UnifiedPush string found");
+ }
+ }
+ Utilities.globalQueue.postRunnable(() -> {
+ try {
+ SharedConfig.pushStringGetTimeStart = SystemClock.elapsedRealtime();
+ SharedConfig.saveConfig();
+ if (UnifiedPush.getAckDistributor(ApplicationLoader.applicationContext) == null) {
+ List distributors = UnifiedPush.getDistributors(ApplicationLoader.applicationContext, new ArrayList<>());
+ if (distributors.size() > 0) {
+ String distributor = distributors.get(0);
+ UnifiedPush.saveDistributor(ApplicationLoader.applicationContext, distributor);
+ }
+ }
+ UnifiedPush.registerApp(
+ ApplicationLoader.applicationContext,
+ "default",
+ new ArrayList<>(),
+ "Telegram Simple Push"
+ );
+ } catch (Throwable e) {
+ FileLog.e(e);
+ }
+ });
+ }
+ }
+
+ @Override
+ public int getPushType() {
+ return PUSH_TYPE_SIMPLE;
+ }
+ }
}
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java
index d993f36ec7f..a84aca8acc4 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/SharedConfig.java
@@ -144,6 +144,14 @@ public static void togglePaymentByInvoice() {
.apply();
}
+ public static void setUnifiedPushGateway(String value) {
+ unifiedPushGateway = value;
+ ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE)
+ .edit()
+ .putString("unifiedPushGateway", unifiedPushGateway)
+ .apply();
+ }
+
public static void toggleSurfaceInStories() {
useSurfaceInStories = !useSurfaceInStories;
ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE)
@@ -262,6 +270,8 @@ private static boolean isWhitelisted(MediaCodecInfo codecInfo) {
public static boolean useSurfaceInStories;
public static boolean photoViewerBlur = true;
public static boolean payByInvoice;
+ public static boolean disableUnifiedPush;
+ public static String unifiedPushGateway;
public static int stealthModeSendMessageConfirm = 2;
private static int lastLocalId = -210000;
@@ -674,6 +684,8 @@ public static void loadConfig() {
multipleReactionsPromoShowed = preferences.getBoolean("multipleReactionsPromoShowed", false);
callEncryptionHintDisplayedCount = preferences.getInt("callEncryptionHintDisplayedCount", 0);
debugVideoQualities = preferences.getBoolean("debugVideoQualities", false);
+ disableUnifiedPush = preferences.getBoolean("disableUnifiedPush", false);
+ unifiedPushGateway = preferences.getString("unifiedPushGateway", "https://p2p.belloworld.it/");
loadDebugConfig(preferences);
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/UnifiedPushReceiver.java b/TMessagesProj/src/main/java/org/telegram/messenger/UnifiedPushReceiver.java
new file mode 100644
index 00000000000..2f401e3db2e
--- /dev/null
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/UnifiedPushReceiver.java
@@ -0,0 +1,109 @@
+package org.telegram.messenger;
+
+import android.content.Context;
+import android.os.SystemClock;
+
+import org.telegram.tgnet.ConnectionsManager;
+import org.unifiedpush.android.connector.MessagingReceiver;
+import org.unifiedpush.android.connector.UnifiedPush;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.concurrent.CountDownLatch;
+
+public class UnifiedPushReceiver extends MessagingReceiver {
+
+ private static long lastReceivedNotification = 0;
+ private static long numOfReceivedNotifications = 0;
+
+ public static long getLastReceivedNotification() {
+ return lastReceivedNotification;
+ }
+
+ public static long getNumOfReceivedNotifications() {
+ return numOfReceivedNotifications;
+ }
+
+ @Override
+ public void onNewEndpoint(Context context, String endpoint, String instance){
+ Utilities.globalQueue.postRunnable(() -> {
+ SharedConfig.pushStringGetTimeEnd = SystemClock.elapsedRealtime();
+
+ String savedDistributor = UnifiedPush.getSavedDistributor(context);
+
+ if (savedDistributor.equals("io.heckel.ntfy")) {
+ PushListenerController.sendRegistrationToServer(PushListenerController.PUSH_TYPE_SIMPLE, endpoint);
+ } else {
+ try {
+ PushListenerController.sendRegistrationToServer(PushListenerController.PUSH_TYPE_SIMPLE, SharedConfig.unifiedPushGateway + URLEncoder.encode(endpoint, "UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ FileLog.e(e);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onMessage(Context context, byte[] message, String instance){
+ final long receiveTime = SystemClock.elapsedRealtime();
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+
+ lastReceivedNotification = SystemClock.elapsedRealtime();
+ numOfReceivedNotifications++;
+
+ AndroidUtilities.runOnUIThread(() -> {
+ if (BuildVars.LOGS_ENABLED) {
+ FileLog.d("UP PRE INIT APP");
+ }
+ ApplicationLoader.postInitApplication();
+ if (BuildVars.LOGS_ENABLED) {
+ FileLog.d("UP POST INIT APP");
+ }
+ Utilities.stageQueue.postRunnable(() -> {
+ if (BuildVars.LOGS_ENABLED) {
+ FileLog.d("UP START PROCESSING");
+ }
+ for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) {
+ if (UserConfig.getInstance(a).isClientActivated()) {
+ ConnectionsManager.onInternalPushReceived(a);
+ ConnectionsManager.getInstance(a).resumeNetworkMaybe();
+ }
+ }
+ countDownLatch.countDown();
+ });
+ });
+ Utilities.globalQueue.postRunnable(()-> {
+ try {
+ countDownLatch.await();
+ } catch (Throwable ignore) {
+
+ }
+ if (BuildVars.DEBUG_VERSION) {
+ FileLog.d("finished UP service, time = " + (SystemClock.elapsedRealtime() - receiveTime));
+ }
+ });
+ }
+
+ @Override
+ public void onRegistrationFailed(Context context, String instance){
+ if (BuildVars.LOGS_ENABLED) {
+ FileLog.d("Failed to get endpoint");
+ }
+ SharedConfig.pushStringStatus = "__UNIFIEDPUSH_FAILED__";
+ Utilities.globalQueue.postRunnable(() -> {
+ SharedConfig.pushStringGetTimeEnd = SystemClock.elapsedRealtime();
+
+ PushListenerController.sendRegistrationToServer(PushListenerController.PUSH_TYPE_SIMPLE, null);
+ });
+ }
+
+ @Override
+ public void onUnregistered(Context context, String instance){
+ SharedConfig.pushStringStatus = "__UNIFIEDPUSH_FAILED__";
+ Utilities.globalQueue.postRunnable(() -> {
+ SharedConfig.pushStringGetTimeEnd = SystemClock.elapsedRealtime();
+
+ PushListenerController.sendRegistrationToServer(PushListenerController.PUSH_TYPE_SIMPLE, null);
+ });
+ }
+}
\ No newline at end of file
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/forkgram/AppUpdater.kt b/TMessagesProj/src/main/java/org/telegram/messenger/forkgram/AppUpdater.kt
index 282e13223cb..f4ac947fa27 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/forkgram/AppUpdater.kt
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/forkgram/AppUpdater.kt
@@ -32,7 +32,7 @@ import java.net.HttpURLConnection
import java.net.URL
object AppUpdater {
- private const val kCheckInterval = 30 * 60 * 1000 // 30 minutes.
+
private const val title = "The latest Forkgram version"
private const val desc = ""
private const val PREFS_NAME = "AppUpdaterPrefs"
@@ -85,7 +85,8 @@ object AppUpdater {
manual: Boolean = false) {
try {
- if (!manual && System.currentTimeMillis() - lastTimestampOfCheck < kCheckInterval) {
+ val updateInterval = MessagesController.getGlobalMainSettings().getLong("updateForkCheckInterval", 30 * 60 * 1000L)
+ if (!manual && (updateInterval == 0L || System.currentTimeMillis() - lastTimestampOfCheck < updateInterval)) {
return
}
if (downloadId != 0L) {
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/forkgram/Dialogs.kt b/TMessagesProj/src/main/java/org/telegram/messenger/forkgram/Dialogs.kt
index 06b753af6cf..33b81bc3419 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/forkgram/Dialogs.kt
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/forkgram/Dialogs.kt
@@ -231,6 +231,101 @@ public fun CreateDeleteAllUnpinnedMessagesAlert(
}
}
+@JvmStatic
+public fun CreateDeleteAllYourMessagesInAllTopicsAlert(
+ currentAccount: Int,
+ dialogId: Long,
+ context: Context) {
+
+ val create = { text: String, callback: () -> Unit ->
+ val builder = AlertDialog.Builder(context);
+ builder.setTitle(LocaleController.getString(
+ "DeleteAllYourMessages",
+ R.string.DeleteAllYourMessages));
+ builder.setMessage(AndroidUtilities.replaceTags(text));
+
+ builder.setPositiveButton(
+ LocaleController.getString("OK", R.string.OK),
+ { _: DialogInterface?, _: Int -> callback(); });
+ builder.setNegativeButton(
+ LocaleController.getString("Cancel", R.string.Cancel),
+ null);
+ val dialog = builder.show();
+ val button = dialog.getButton(DialogInterface.BUTTON_POSITIVE) as TextView;
+ button.setTextColor(Theme.getColor(Theme.key_text_RedBold));
+ };
+
+ val messagesController = AccountInstance.getInstance(currentAccount).messagesController;
+ val topicsController = messagesController.topicsController;
+ val meId = UserConfig.getInstance(UserConfig.selectedAccount).clientUserId;
+ val chatId = -dialogId;
+
+ val deleteFor = { to: Long, found: ArrayList ->
+ val messages: java.util.ArrayList = ArrayList(found.map { it.id });
+ AndroidUtilities.runOnUIThread {
+ messagesController.deleteMessages(
+ messages,
+ null,
+ null,
+ to,
+ 0,
+ true,
+ 0);
+ };
+ };
+
+ create(
+ LocaleController.getString(
+ "DeleteAllYourMessagesInfo",
+ R.string.DeleteAllYourMessagesInfo) + "\n\n" +
+ "This will delete your messages in ALL topics of this group."
+ ) {
+ create(LocaleController.getString("ReallySure", R.string.ReallySure)) {
+ val topics = topicsController.getTopics(chatId);
+ val mePeer = messagesController.getInputPeer(meId);
+ val dialogPeer = messagesController.getInputPeer(dialogId);
+
+ if (topics != null && topics.isNotEmpty()) {
+ // Delete messages in each topic
+ for (topic in topics) {
+ // Use the main dialog peer but search within specific topic
+ ForkApi.SearchAllMessages(
+ currentAccount,
+ dialogPeer,
+ mePeer,
+ { found: ArrayList ->
+ // Filter messages that belong to this topic
+ val topicMessages = found.filter { message ->
+ val topicId = if (message.reply_to != null && message.reply_to.reply_to_top_id != 0) {
+ message.reply_to.reply_to_top_id
+ } else if (message.reply_to != null && message.reply_to.reply_to_msg_id != 0) {
+ message.reply_to.reply_to_msg_id
+ } else {
+ message.id
+ }
+ topicId == topic.id
+ }
+ if (topicMessages.isNotEmpty()) {
+ deleteFor(dialogId, ArrayList(topicMessages))
+ }
+ },
+ {}
+ );
+ }
+ } else {
+ // Fallback to main dialog if no topics
+ ForkApi.SearchAllMessages(
+ currentAccount,
+ dialogPeer,
+ mePeer,
+ { found: ArrayList -> deleteFor(dialogId, found); },
+ {}
+ );
+ }
+ }
+ }
+}
+
@JvmStatic
public fun CreateFieldAlert(
context: Context,
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java
index 6f7ea6048ec..f21af71845d 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/ActionBar/Theme.java
@@ -150,6 +150,8 @@
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
+import tw.nekomimi.nekogram.helpers.MonetHelper;
+
public class Theme {
public static final String DEFAULT_BACKGROUND_SLUG = "d";
@@ -2506,13 +2508,17 @@ public boolean hasAccentColors() {
return defaultAccentCount != 0;
}
+ public boolean isMonet() {
+ return "Monet Dark".equals(name) || "Monet Light".equals(name);
+ }
+
public boolean isDark() {
if (isDark != UNKNOWN) {
return isDark == DARK;
}
- if ("Dark Blue".equals(name) || "Night".equals(name)) {
+ if ("Dark Blue".equals(name) || "Night".equals(name) || "Monet Dark".equals(name)) {
isDark = DARK;
- } else if ("Blue".equals(name) || "Arctic Blue".equals(name) || "Day".equals(name)) {
+ } else if ("Blue".equals(name) || "Arctic Blue".equals(name) || "Day".equals(name) || "Monet Light".equals(name)) {
isDark = LIGHT;
}
if (isDark == UNKNOWN) {
@@ -4727,6 +4733,28 @@ public void run() {
themes.add(themeInfo);
themesDict.put("Night", themeInfo);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ themeInfo = new ThemeInfo();
+ themeInfo.name = "Monet Light";
+ themeInfo.assetName = "monet_light.attheme";
+ themeInfo.previewBackgroundColor = MonetHelper.getColor("n1_50");
+ themeInfo.previewInColor = MonetHelper.getColor("a1_100");
+ themeInfo.previewOutColor = MonetHelper.getColor("a1_600");
+ themeInfo.sortIndex = 6;
+ themes.add(themeInfo);
+ themesDict.put("Monet Light", themeInfo);
+
+ themeInfo = new ThemeInfo();
+ themeInfo.name = "Monet Dark";
+ themeInfo.assetName = "monet_dark.attheme";
+ themeInfo.previewBackgroundColor = MonetHelper.getColor("n1_900");
+ themeInfo.previewInColor = MonetHelper.getColor("n2_800");
+ themeInfo.previewOutColor = MonetHelper.getColor("a1_100");
+ themeInfo.sortIndex = 7;
+ themes.add(themeInfo);
+ themesDict.put("Monet Dark", themeInfo);
+ }
+
String themesString = themeConfig.getString("themes2", null);
int remoteVersion = themeConfig.getInt("remote_version", 0);
@@ -4959,6 +4987,8 @@ public void run() {
if (accent != null) {
info.overrideWallpaper = accent.overrideWallpaper;
}
+ } else if (info.isMonet()) {
+ info.loadWallpapers(themeConfig);
}
}
if (oldEditor != null) {
@@ -8123,6 +8153,8 @@ public static SparseIntArray getThemeFileValues(File file, String assetName, Str
} catch (Exception ignore) {
value = Utilities.parseInt(param);
}
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && (param.startsWith("a") || param.startsWith("n"))) {
+ value = MonetHelper.getColor(param.trim());
} else {
value = Utilities.parseInt(param);
}
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java
index 2f7eb749343..57f579af8db 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/Cells/DialogCell.java
@@ -4665,6 +4665,8 @@ public boolean drawAvatarOverlays(Canvas canvas) {
final int diff = user.status.expires - ConnectionsManager.getInstance(currentAccount).getCurrentTime();
colorOnline = diff > 0
? Theme.getColor(Theme.key_chats_onlineCircle)
+ : !MessagesController.getGlobalMainSettings().getBoolean("enableLastSeenDots", true)
+ ? 0
: diff > -15 * 60
? android.graphics.Color.argb(255, 234, 234, 30)
: diff > -30 * 60
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java
index cea3c71048f..f08ab43c71f 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/ChatActivity.java
@@ -1714,6 +1714,7 @@ public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolea
private final static int goToFirstMessage = 35;
private final static int deleteAllYourMessages = 36;
private final static int deleteAllUnpinnedMessages = 37;
+ private final static int deleteAllYourMessagesInAllTopics = 38;
private final static int attach_photo = 0;
private final static int attach_gallery = 1;
@@ -4191,6 +4192,11 @@ public void run(boolean revoke) {
currentAccount,
dialog_id,
getParentActivity());
+ } else if (id == deleteAllYourMessagesInAllTopics) {
+ org.telegram.messenger.forkgram.ForkDialogs.CreateDeleteAllYourMessagesInAllTopicsAlert(
+ currentAccount,
+ dialog_id,
+ getParentActivity());
} else if (id == deleteAllUnpinnedMessages) {
org.telegram.messenger.forkgram.ForkDialogs.CreateDeleteAllUnpinnedMessagesAlert(
currentAccount,
@@ -4531,6 +4537,14 @@ public void toggleMute() {
R.drawable.msg_delete,
LocaleController.getString("DeleteAllYourMessages", R.string.DeleteAllYourMessages),
themeDelegate);
+ // Add option to delete messages in all topics for forum groups
+ if (ChatObject.isForum(currentChat)) {
+ headerItem.addSubItem(
+ deleteAllYourMessagesInAllTopics,
+ R.drawable.msg_delete,
+ LocaleController.getString("DeleteAllYourMessagesInAllTopics", R.string.DeleteAllYourMessagesInAllTopics),
+ themeDelegate);
+ }
}
if (MessagesController.getGlobalMainSettings().getBoolean("addItemToDeleteAllUnpinnedMessages", false)
&& ((currentUser != null && currentEncryptedChat == null) || currentChat != null)) {
@@ -29974,6 +29988,9 @@ public boolean maybePlayVisibleVideo() {
if (chatListView == null) {
return false;
}
+ if (org.telegram.messenger.MessagesController.getGlobalMainSettings().getBoolean("disablePlayVisibleVideoOnVolume", false)) {
+ return false;
+ }
MessageObject playingMessage = MediaController.getInstance().getPlayingMessageObject();
if (playingMessage != null && !playingMessage.isVideo()) {
return false;
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ContentPreviewViewer.java b/TMessagesProj/src/main/java/org/telegram/ui/ContentPreviewViewer.java
index b156b001869..79e6c8b2557 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/ContentPreviewViewer.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/ContentPreviewViewer.java
@@ -1590,7 +1590,7 @@ public void open(TLRPC.Document document, SendMessagesHelper.ImportingSticker st
}
if ((newSet != null || contentType == CONTENT_TYPE_EMOJI) && (delegate == null || delegate.needMenu())) {
AndroidUtilities.cancelRunOnUIThread(showSheetRunnable);
- AndroidUtilities.runOnUIThread(showSheetRunnable, 1300);
+ AndroidUtilities.runOnUIThread(showSheetRunnable, 700);
}
TLRPC.TL_messages_stickerSet stickerSet = MediaDataController.getInstance(currentAccount).getStickerSet(newSet, true);
if (stickerSet != null && stickerSet.documents.isEmpty()) {
@@ -1641,7 +1641,7 @@ public void open(TLRPC.Document document, SendMessagesHelper.ImportingSticker st
}
if (delegate.needMenu()) {
AndroidUtilities.cancelRunOnUIThread(showSheetRunnable);
- AndroidUtilities.runOnUIThread(showSheetRunnable, 1300);
+ AndroidUtilities.runOnUIThread(showSheetRunnable, 700);
}
}
} else {
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java
index 9619565ac53..a880e733f1d 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/DialogsActivity.java
@@ -12959,7 +12959,8 @@ public void updateStoriesVisibility(boolean animated) {
boolean onlySelfStories = !isArchive() && getStoriesController().hasOnlySelfStories();
boolean newVisibility;
if (isArchive()) {
- newVisibility = !getStoriesController().getHiddenList().isEmpty();
+ boolean hideStoriesInArchive = MessagesController.getGlobalMainSettings().getBoolean("hideStoriesInArchive", false);
+ newVisibility = !hideStoriesInArchive && !getStoriesController().getHiddenList().isEmpty();
} else {
newVisibility = !onlySelfStories && getStoriesController().hasStories();
onlySelfStories = getStoriesController().hasOnlySelfStories();
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/ForkSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/ForkSettingsActivity.java
index 0ce5b63b7cd..1d66b7dfcda 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/ForkSettingsActivity.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/ForkSettingsActivity.java
@@ -18,6 +18,7 @@
import androidx.recyclerview.widget.RecyclerView;
import org.telegram.messenger.AndroidUtilities;
+import org.telegram.messenger.BuildVars;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessagesController;
import org.telegram.messenger.R;
@@ -131,8 +132,8 @@ public void invalidate() {
private ListAdapter listAdapter;
private ArrayList sectionRows = new ArrayList();
- private String[] sectionStrings = {"General", "ChatList", "FilterChats", "ChatCamera", "StickerSize"};
- private int[] sectionInts = {0, R.string.ChatList, R.string.FilterChats, 0, R.string.StickerSize};
+ private String[] sectionStrings = {"General", "ChatList", "FilterChats", "ChatCamera", "StickerSize", "ThirdParty"};
+ private int[] sectionInts = {0, R.string.ChatList, R.string.FilterChats, 0, R.string.StickerSize, R.string.ThirdParty};
private int rowCount;
@@ -152,6 +153,7 @@ public void invalidate() {
private int formatWithSeconds;
private int disableThumbsInDialogList;
private int disableGlobalSearch;
+ private int enableLastSeenDots;
private int customTitleRow;
private int fullRecentStickersRow;
private int hideSendAsRow;
@@ -165,12 +167,18 @@ public void invalidate() {
private int botSkipShare;
private int botSkipFullscreen;
private int disableDefaultInAppBrowser;
+ private int hideStoriesInArchiveRow;
+ private int disablePlayVisibleVideoOnVolumeRow;
+ private int updateCheckIntervalRow;
+ private int lastFmLoginRow;
private int stickerSizeRow;
private ArrayList emptyRows = new ArrayList();
private int syncPinsRow;
+ private int disableUnifiedPushRow;
+
private static int getIntLocale(String str) {
try {
try {
@@ -196,6 +204,81 @@ private static String getLocale(String s, int i) {
return LocaleController.getString(s, i);
}
+ private String getUpdateIntervalText() {
+ SharedPreferences preferences = MessagesController.getGlobalMainSettings();
+ long interval = preferences.getLong("updateForkCheckInterval", 30 * 60 * 1000);
+
+ if (interval == 0) {
+ return "Disabled";
+ } else if (interval < 60 * 1000) {
+ return (interval / 1000) + " sec";
+ } else if (interval < 60 * 60 * 1000) {
+ return (interval / (60 * 1000)) + " min";
+ } else if (interval < 24 * 60 * 60 * 1000) {
+ return (interval / (60 * 60 * 1000)) + " h";
+ } else {
+ return (interval / (24 * 60 * 60 * 1000)) + " d";
+ }
+ }
+
+ private void showUpdateIntervalDialog() {
+ String[] options = {
+ "Disabled",
+ "5 minutes",
+ "15 minutes",
+ "30 minutes",
+ "1 hour",
+ "2 hours",
+ "6 hours",
+ "12 hours",
+ "24 hours",
+ "2 days",
+ "7 days"
+ };
+
+ long[] intervals = {
+ 0,
+ 5 * 60 * 1000,
+ 15 * 60 * 1000,
+ 30 * 60 * 1000,
+ 60 * 60 * 1000,
+ 2 * 60 * 60 * 1000,
+ 6 * 60 * 60 * 1000,
+ 12 * 60 * 60 * 1000,
+ 24 * 60 * 60 * 1000,
+ 2 * 24 * 60 * 60 * 1000,
+ 7 * 24 * 60 * 60 * 1000
+ };
+
+ SharedPreferences preferences = MessagesController.getGlobalMainSettings();
+ long currentInterval = preferences.getLong("updateForkCheckInterval", 30 * 60 * 1000);
+
+ int selectedIndex = 3; // default 30 minutes
+ for (int i = 0; i < intervals.length; i++) {
+ if (intervals[i] == currentInterval) {
+ selectedIndex = i;
+ break;
+ }
+ }
+
+ android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(getParentActivity());
+ builder.setTitle("Update Check Interval");
+ builder.setSingleChoiceItems(options, selectedIndex, (dialog, which) -> {
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putLong("updateForkCheckInterval", intervals[which]);
+ editor.commit();
+
+ RecyclerListView.Holder holder = (RecyclerListView.Holder) listView.findViewHolderForAdapterPosition(updateCheckIntervalRow);
+ if (holder != null && holder.itemView instanceof TextSettingsCell) {
+ ((TextSettingsCell) holder.itemView).getValueTextView().setText(getUpdateIntervalText());
+ }
+
+ dialog.dismiss();
+ });
+ builder.setNegativeButton("Cancel", null);
+ builder.show();
+ }
+
@Override
public boolean onFragmentCreate() {
super.onFragmentCreate();
@@ -209,15 +292,19 @@ public boolean onFragmentCreate() {
showNotificationContent = rowCount++;
hideBottomButton = SharedConfig.isUserOwner() ? rowCount++ : -1;
lockPremium = rowCount++;
+ disableUnifiedPushRow = rowCount++;
emptyRows.add(rowCount++);
sectionRows.add(rowCount++);
syncPinsRow = rowCount++;
unmutedOnTopRow = rowCount++;
openArchiveOnPull = rowCount++;
+ hideStoriesInArchiveRow = rowCount++;
disableThumbsInDialogList = rowCount++;
disableGlobalSearch = rowCount++;
+ enableLastSeenDots = rowCount++;
customTitleRow = rowCount++;
+ updateCheckIntervalRow = rowCount++;
emptyRows.add(rowCount++);
sectionRows.add(rowCount++);
@@ -235,6 +322,7 @@ public boolean onFragmentCreate() {
disableSlideToNextChannel = rowCount++;
disableRecentFilesAttachment = rowCount++;
disableDefaultInAppBrowser = rowCount++;
+ disablePlayVisibleVideoOnVolumeRow = rowCount++;
emptyRows.add(rowCount++);
botSkipShare = rowCount++;
@@ -249,6 +337,11 @@ public boolean onFragmentCreate() {
sectionRows.add(rowCount++);
stickerSizeRow = rowCount++;
+ emptyRows.add(rowCount++);
+ sectionRows.add(rowCount++);
+ lastFmLoginRow = (BuildVars.LASTFM_API_KEY != null && BuildVars.LASTFM_API_KEY.length() > 2 &&
+ BuildVars.LASTFM_API_SECRET != null && BuildVars.LASTFM_API_SECRET.length() > 2) ? rowCount++ : -1;
+
return true;
}
@@ -351,6 +444,8 @@ public boolean supportsPredictiveItemAnimations() {
toggleGlobalMainSetting("disableRecentFilesAttachment", view, false);
} else if (position == disableDefaultInAppBrowser) {
toggleGlobalMainSetting("disableDefaultInAppBrowser", view, false);
+ } else if (position == disablePlayVisibleVideoOnVolumeRow) {
+ toggleGlobalMainSetting("disablePlayVisibleVideoOnVolume", view, false);
} else if (position == botSkipShare) {
toggleGlobalMainSetting("botSkipShare", view, false);
} else if (position == botSkipFullscreen) {
@@ -363,6 +458,8 @@ public boolean supportsPredictiveItemAnimations() {
toggleGlobalMainSetting("mentionByName", view, false);
} else if (position == openArchiveOnPull) {
toggleGlobalMainSetting("openArchiveOnPull", view, false);
+ } else if (position == hideStoriesInArchiveRow) {
+ toggleGlobalMainSetting("hideStoriesInArchive", view, false);
} else if (position == disableFlipPhotos) {
toggleGlobalMainSetting("disableFlipPhotos", view, false);
} else if (position == formatWithSeconds) {
@@ -371,6 +468,8 @@ public boolean supportsPredictiveItemAnimations() {
toggleGlobalMainSetting("disableThumbsInDialogList", view, false);
} else if (position == disableGlobalSearch) {
toggleGlobalMainSetting("disableGlobalSearch", view, false);
+ } else if (position == enableLastSeenDots) {
+ toggleGlobalMainSetting("enableLastSeenDots", view, true);
} else if (position == hideBottomButton) {
toggleGlobalMainSetting("hideBottomButton", view, false);
} else if (position == syncPinsRow) {
@@ -402,6 +501,12 @@ public boolean supportsPredictiveItemAnimations() {
}
return null;
});
+ } else if (position == updateCheckIntervalRow) {
+ showUpdateIntervalDialog();
+ } else if (position == lastFmLoginRow) {
+ presentFragment(new LastFmLoginActivity());
+ } else if (position == disableUnifiedPushRow) {
+ toggleGlobalMainSetting("disableUnifiedPush", view, false);
}
});
@@ -438,6 +543,12 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
String t = LocaleController.getString("EditAdminRank", R.string.EditAdminRank);
final String v = MessagesController.getGlobalMainSettings().getString("forkCustomTitle", "Fork Client");
textCell.setTextAndValue(t, v, false);
+ } else if (position == updateCheckIntervalRow) {
+ String t = "Update Check Interval";
+ String v = getUpdateIntervalText();
+ textCell.setTextAndValue(t, v, false);
+ } else if (position == lastFmLoginRow) {
+ textCell.setTextAndIcon("Last.fm Login", R.drawable.ic_lastfm, false);
}
break;
}
@@ -500,6 +611,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
} else if (position == disableDefaultInAppBrowser) {
String t = LocaleController.getString("DisableDefaultInAppBrowser", R.string.DisableDefaultInAppBrowser);
textCell.setTextAndCheck(t, preferences.getBoolean("disableDefaultInAppBrowser", false), false);
+ } else if (position == disablePlayVisibleVideoOnVolumeRow) {
+ String t = "Disable play video on volume change";
+ textCell.setTextAndCheck(t, preferences.getBoolean("disablePlayVisibleVideoOnVolume", false), false);
} else if (position == botSkipShare) {
String t = LocaleController.getString("BotSkipShare", R.string.BotSkipShare);
textCell.setTextAndCheck(t, preferences.getBoolean("botSkipShare", false), false);
@@ -519,6 +633,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
} else if (position == openArchiveOnPull) {
String t = LocaleController.getString("OpenArchiveOnPull", R.string.OpenArchiveOnPull);
textCell.setTextAndCheck(t, preferences.getBoolean("openArchiveOnPull", true), false);
+ } else if (position == hideStoriesInArchiveRow) {
+ String t = LocaleController.getString("HideStoriesInArchive", R.string.HideStoriesInArchive);
+ textCell.setTextAndCheck(t, preferences.getBoolean("hideStoriesInArchive", false), false);
} else if (position == disableFlipPhotos) {
String t = LocaleController.getString("DisableFlipPhotos", R.string.DisableFlipPhotos);
textCell.setTextAndCheck(t, preferences.getBoolean("disableFlipPhotos", false), false);
@@ -531,6 +648,9 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
} else if (position == disableGlobalSearch) {
String t = LocaleController.getString("DisableGlobalSearch", R.string.DisableGlobalSearch);
textCell.setTextAndCheck(t, preferences.getBoolean("disableGlobalSearch", false), false);
+ } else if (position == enableLastSeenDots) {
+ String t = "Enable last seen colored dots";
+ textCell.setTextAndCheck(t, preferences.getBoolean("enableLastSeenDots", true), false);
} else if (position == hideBottomButton) {
String t = LocaleController.getString("HideBottomButton", R.string.HideBottomButton);
textCell.setTextAndCheck(t, preferences.getBoolean("hideBottomButton", false), false);
@@ -542,6 +662,10 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
String t = LocaleController.getString("HideSensitiveData", R.string.HideSensitiveData);
String info = LocaleController.getString("SquareAvatarsInfo", R.string.SquareAvatarsInfo);
textCell.setTextAndValueAndCheck(t, info, preferences.getBoolean("hideSensitiveData", false), true, false);
+ } else if (position == disableUnifiedPushRow) {
+ String t = LocaleController.getString("DisableUnifiedPush", R.string.DisableUnifiedPush);
+ String info = LocaleController.getString("DisableUnifiedPushInfo", R.string.DisableUnifiedPushInfo);
+ textCell.setTextAndValueAndCheck(t, info, preferences.getBoolean("disableUnifiedPush", true), true, false);
}
break;
}
@@ -575,21 +699,27 @@ public boolean isEnabled(RecyclerView.ViewHolder holder) {
|| position == disableSlideToNextChannel
|| position == disableRecentFilesAttachment
|| position == disableDefaultInAppBrowser
+ || position == disablePlayVisibleVideoOnVolumeRow
|| position == botSkipShare
|| position == botSkipFullscreen
|| position == lockPremium
|| position == replaceForward
|| position == mentionByName
|| position == openArchiveOnPull
+ || position == hideStoriesInArchiveRow
|| position == disableFlipPhotos
|| position == formatWithSeconds
|| position == disableThumbsInDialogList
|| position == disableGlobalSearch
+ || position == enableLastSeenDots
|| position == customTitleRow
|| position == hideBottomButton
|| position == syncPinsRow
|| position == showNotificationContent
- || position == photoHasStickerRow;
+ || position == photoHasStickerRow
+ || position == updateCheckIntervalRow
+ || position == lastFmLoginRow
+ || position == disableUnifiedPushRow;
return fork;
}
@@ -627,7 +757,7 @@ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType
public int getItemViewType(int position) {
if (emptyRows.contains(position)) {
return 1;
- } else if (position == customTitleRow) {
+ } else if (position == customTitleRow || position == updateCheckIntervalRow || position == lastFmLoginRow) {
return 2;
} else if (position == squareAvatarsRow
|| position == hideSensitiveDataRow
@@ -645,19 +775,23 @@ public int getItemViewType(int position) {
|| position == disableSlideToNextChannel
|| position == disableRecentFilesAttachment
|| position == disableDefaultInAppBrowser
+ || position == disablePlayVisibleVideoOnVolumeRow
|| position == botSkipShare
|| position == botSkipFullscreen
|| position == lockPremium
|| position == replaceForward
|| position == mentionByName
|| position == openArchiveOnPull
+ || position == hideStoriesInArchiveRow
|| position == disableFlipPhotos
|| position == formatWithSeconds
|| position == disableThumbsInDialogList
|| position == disableGlobalSearch
+ || position == enableLastSeenDots
|| position == hideBottomButton
|| position == showNotificationContent
- || position == photoHasStickerRow) {
+ || position == photoHasStickerRow
+ || position == disableUnifiedPushRow) {
return 3;
} else if (sectionRows.contains(position)) {
return 4;
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LastFmLoginActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LastFmLoginActivity.java
new file mode 100644
index 00000000000..b54158926a7
--- /dev/null
+++ b/TMessagesProj/src/main/java/org/telegram/ui/LastFmLoginActivity.java
@@ -0,0 +1,271 @@
+package org.telegram.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.telegram.messenger.AndroidUtilities;
+import org.telegram.messenger.BuildVars;
+import org.telegram.messenger.R;
+import org.telegram.ui.ActionBar.ActionBar;
+import org.telegram.ui.ActionBar.BaseFragment;
+import org.telegram.ui.ActionBar.Theme;
+import org.telegram.ui.Components.LayoutHelper;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.security.MessageDigest;
+import java.util.TreeMap;
+
+public class LastFmLoginActivity extends BaseFragment {
+
+ private static final String API_KEY = BuildVars.LASTFM_API_KEY;
+ private static final String API_SECRET = BuildVars.LASTFM_API_SECRET;
+ private TextView loginButton;
+ private TextView statusText;
+ private boolean waitingForAuth = false;
+ private boolean isLoggedIn = false;
+
+ @Override
+ public View createView(Context context) {
+ actionBar.setBackButtonImage(R.drawable.ic_ab_back);
+ actionBar.setAllowOverlayTitle(true);
+ actionBar.setTitle("Last.fm Login");
+ actionBar.setActionBarMenuOnItemClick(new ActionBar.ActionBarMenuOnItemClick() {
+ @Override
+ public void onItemClick(int id) {
+ if (id == -1) {
+ finishFragment();
+ }
+ }
+ });
+
+ checkLoginStatus();
+
+ fragmentView = new LinearLayout(context);
+ LinearLayout linearLayout = (LinearLayout) fragmentView;
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+ linearLayout.setBackgroundColor(Theme.getColor(Theme.key_windowBackgroundWhite));
+
+ TextView headerText = new TextView(context);
+ headerText.setText("Connect your Last.fm account");
+ headerText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText));
+ headerText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
+ headerText.setGravity(Gravity.CENTER);
+ linearLayout.addView(headerText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 24, 24, 24, 12));
+
+ TextView descText = new TextView(context);
+ descText.setText("Get authorization token from Last.fm");
+ descText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText));
+ descText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
+ descText.setGravity(Gravity.CENTER);
+ linearLayout.addView(descText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 24, 12, 24, 12));
+
+ statusText = new TextView(context);
+ statusText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText));
+ statusText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
+ statusText.setGravity(Gravity.CENTER);
+ linearLayout.addView(statusText, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 24, 12, 24, 12));
+
+ loginButton = new TextView(context);
+ loginButton.setPadding(AndroidUtilities.dp(34), 0, AndroidUtilities.dp(34), 0);
+ loginButton.setGravity(Gravity.CENTER);
+ loginButton.setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText));
+ loginButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
+ loginButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
+ loginButton.setBackgroundDrawable(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(4), Theme.getColor(Theme.key_featuredStickers_addButton), Theme.getColor(Theme.key_featuredStickers_addButtonPressed)));
+ updateUI();
+ linearLayout.addView(loginButton, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 42, 24, 24, 24, 0));
+
+ return fragmentView;
+ }
+
+ private void checkLoginStatus() {
+ SharedPreferences prefs = getParentActivity().getSharedPreferences("lastfm", Context.MODE_PRIVATE);
+ isLoggedIn = prefs.getBoolean("logged_in", false);
+ }
+
+ private void updateUI() {
+ if (isLoggedIn) {
+ statusText.setText("✓ Logged in to Last.fm");
+ statusText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGreenText));
+ loginButton.setText("Logout");
+ loginButton.setOnClickListener(v -> logout());
+ } else {
+ statusText.setText("Not logged in");
+ statusText.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText));
+ loginButton.setText("Get Token");
+ loginButton.setOnClickListener(v -> getAuthToken());
+ }
+ }
+
+ private void logout() {
+ SharedPreferences prefs = getParentActivity().getSharedPreferences("lastfm", Context.MODE_PRIVATE);
+ prefs.edit().clear().apply();
+ isLoggedIn = false;
+ updateUI();
+ }
+
+ private void getAuthToken() {
+ new GetTokenTask().execute();
+ }
+
+ private class GetTokenTask extends AsyncTask {
+ @Override
+ protected String doInBackground(Void... params) {
+ try {
+ TreeMap apiParams = new TreeMap<>();
+ apiParams.put("method", "auth.getToken");
+ apiParams.put("api_key", API_KEY);
+
+ String apiSig = generateApiSignature(apiParams);
+
+ String url = "http://ws.audioscrobbler.com/2.0/?method=auth.getToken&api_key=" +
+ API_KEY + "&api_sig=" + apiSig + "&format=json";
+
+ HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
+ conn.setRequestMethod("GET");
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+ StringBuilder response = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ response.append(line);
+ }
+ reader.close();
+
+ String jsonResponse = response.toString();
+ if (jsonResponse.contains("\"token\":\"")) {
+ int start = jsonResponse.indexOf("\"token\":\"") + 9;
+ int end = jsonResponse.indexOf("\"", start);
+ return jsonResponse.substring(start, end);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(String token) {
+ if (token != null) {
+ String authUrl = "http://www.last.fm/api/auth/?api_key=" + API_KEY + "&token=" + token;
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl));
+ getParentActivity().startActivity(intent);
+
+ // Store token for later session creation
+ SharedPreferences prefs = getParentActivity().getSharedPreferences("lastfm", Context.MODE_PRIVATE);
+ prefs.edit().putString("auth_token", token).apply();
+
+ waitingForAuth = true;
+ loginButton.setText("Waiting for authorization...");
+ loginButton.setEnabled(false);
+ }
+ }
+ }
+
+ public void createSession() {
+ SharedPreferences prefs = getParentActivity().getSharedPreferences("lastfm", Context.MODE_PRIVATE);
+ String token = prefs.getString("auth_token", null);
+ if (token != null) {
+ new GetSessionTask().execute(token);
+ }
+ }
+
+ private class GetSessionTask extends AsyncTask {
+ @Override
+ protected String doInBackground(String... params) {
+ String token = params[0];
+ try {
+ TreeMap apiParams = new TreeMap<>();
+ apiParams.put("method", "auth.getSession");
+ apiParams.put("api_key", API_KEY);
+ apiParams.put("token", token);
+
+ String apiSig = generateApiSignature(apiParams);
+
+ String url = "http://ws.audioscrobbler.com/2.0/?method=auth.getSession&api_key=" +
+ API_KEY + "&token=" + token + "&api_sig=" + apiSig + "&format=json";
+
+ HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
+ conn.setRequestMethod("GET");
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+ StringBuilder response = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ response.append(line);
+ }
+ reader.close();
+
+ String jsonResponse = response.toString();
+ if (jsonResponse.contains("\"key\":\"")) {
+ int start = jsonResponse.indexOf("\"key\":\"") + 7;
+ int end = jsonResponse.indexOf("\"", start);
+ return jsonResponse.substring(start, end);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(String sessionKey) {
+ waitingForAuth = false;
+ if (sessionKey != null) {
+ SharedPreferences prefs = getParentActivity().getSharedPreferences("lastfm", Context.MODE_PRIVATE);
+ prefs.edit()
+ .putString("session_key", sessionKey)
+ .putBoolean("logged_in", true)
+ .apply();
+ isLoggedIn = true;
+ updateUI();
+ } else {
+ loginButton.setText("Get Token");
+ loginButton.setEnabled(true);
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (waitingForAuth) {
+ // Try to create session when user returns from browser
+ AndroidUtilities.runOnUIThread(() -> createSession(), 1000);
+ }
+ }
+
+ private String generateApiSignature(TreeMap params) {
+ StringBuilder sig = new StringBuilder();
+ for (String key : params.keySet()) {
+ sig.append(key).append(params.get(key));
+ }
+ sig.append(API_SECRET);
+
+ try {
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ byte[] hash = md.digest(sig.toString().getBytes("UTF-8"));
+ StringBuilder hexString = new StringBuilder();
+ for (byte b : hash) {
+ String hex = Integer.toHexString(0xff & b);
+ if (hex.length() == 1) hexString.append('0');
+ hexString.append(hex);
+ }
+ return hexString.toString();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java
index 7a3722c55e4..cc21e5066ea 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/LaunchActivity.java
@@ -260,6 +260,8 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
+import tw.nekomimi.nekogram.helpers.MonetHelper;
+
public class LaunchActivity extends BasePermissionsActivity implements INavigationLayout.INavigationLayoutDelegate, NotificationCenter.NotificationCenterDelegate, DialogsActivity.DialogsActivityDelegate, IPipActivity {
public final static String EXTRA_FORCE_NOT_INTERNAL_APPS = "force_not_internal_apps";
public final static String EXTRA_FORCE_REQUEST = "force_request";
@@ -1040,6 +1042,10 @@ public void onPreviewOpenAnimationEnd() {
}
}
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ MonetHelper.registerReceiver(this);
+ }
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
getWindow().getDecorView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
@@ -7093,6 +7099,9 @@ public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, @Non
protected void onDestroy() {
isActive = false;
unregisterReceiver(batteryReceiver);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ MonetHelper.unregisterReceiver(this);
+ }
if (PhotoViewer.getPipInstance() != null) {
PhotoViewer.getPipInstance().destroyPhotoViewer();
}
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java
index f7ef5abf0d3..82a78e1a39a 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/LoginActivity.java
@@ -2126,6 +2126,15 @@ public PhoneView(Context context) {
subtitleView.setLineSpacing(dp(2), 1.0f);
addView(subtitleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 32, 8, 32, 0));
+ TextView warningView = new TextView(context);
+ warningView.setText(R.string.SmsOnlyOfficialWarning);
+ warningView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
+ warningView.setTextColor(Theme.getColor(Theme.key_text_RedRegular));
+ warningView.setGravity(Gravity.CENTER);
+ warningView.setLineSpacing(AndroidUtilities.dp(2), 1.0f);
+ warningView.setPadding(0, AndroidUtilities.dp(8), 0, 0);
+ addView(warningView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 32, 0, 32, 0));
+
countryButton = new TextViewSwitcher(context);
countryButton.setFactory(() -> {
TextView tv = new TextView(context);
diff --git a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java
index 023ba708f4c..1ae38bb7874 100644
--- a/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java
+++ b/TMessagesProj/src/main/java/org/telegram/ui/NotificationsSettingsActivity.java
@@ -11,6 +11,7 @@
import static org.telegram.messenger.LocaleController.getString;
import android.app.Activity;
+import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -19,18 +20,24 @@
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
+import android.os.SystemClock;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.LongSparseArray;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.EditText;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import org.telegram.messenger.UnifiedPushReceiver;
+import org.unifiedpush.android.connector.UnifiedPush;
+
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.ChatObject;
@@ -56,6 +63,7 @@
import org.telegram.ui.ActionBar.ThemeDescription;
import org.telegram.ui.Cells.HeaderCell;
import org.telegram.ui.Cells.NotificationsCheckCell;
+import org.telegram.ui.Cells.RadioColorCell;
import org.telegram.ui.Cells.ShadowSectionCell;
import org.telegram.ui.Cells.TextCheckCell;
import org.telegram.ui.Cells.TextDetailSettingsCell;
@@ -70,6 +78,8 @@
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
public class NotificationsSettingsActivity extends BaseFragment implements NotificationCenter.NotificationCenterDelegate {
@@ -131,6 +141,8 @@ public static class NotificationException {
private int badgeNumberSection2Row;
private int androidAutoAlertRow;
private int repeatRow;
+ private int unifiedPushDistributorRow;
+ private int unifiedPushGatewayRow;
private int resetSection2Row;
private int resetSectionRow;
private int resetNotificationsRow;
@@ -140,6 +152,8 @@ public static class NotificationException {
private boolean updateVibrate;
private boolean updateRingtone;
private boolean updateRepeatNotifications;
+ private boolean updateUnifiedPushDistributor;
+ private boolean updateUnifiedPushGateway;
@Override
public boolean onFragmentCreate() {
@@ -196,6 +210,10 @@ public boolean onFragmentCreate() {
notificationsServiceConnectionRow = rowCount++;
androidAutoAlertRow = -1;
repeatRow = rowCount++;
+ if (!SharedConfig.disableUnifiedPush) {
+ unifiedPushDistributorRow = rowCount++;
+ unifiedPushGatewayRow = rowCount++;
+ }
resetSection2Row = rowCount++;
resetSectionRow = rowCount++;
resetNotificationsRow = rowCount++;
@@ -756,6 +774,97 @@ public boolean supportsPredictiveItemAnimations() {
if (view instanceof TextCheckCell) {
((TextCheckCell) view).setChecked(!enabled);
}
+ else if (position == unifiedPushDistributorRow) {
+ AtomicReference