Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,152 @@
apply plugin: 'com.android.application'
repositories {
mavenCentral()
google()
}
configurations {
compile.exclude module: 'support-v4'
}
configurations.all {
exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'androidx.recyclerview', module: 'recyclerview'
}
dependencies {
implementation project(':TMessagesProj')
implementation 'androidx.fragment:fragment:1.8.9'
implementation 'androidx.core:core:1.16.0'
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
implementation files('../TMessagesProj/libs/libgsaverification-client.aar')
}
android {
compileSdkVersion 35
buildToolsVersion '35.0.0'
defaultConfig.applicationId = APP_PACKAGE
sourceSets.main.jniLibs.srcDirs = ['../TMessagesProj/jni/']
lintOptions {
disable 'MissingTranslation'
disable 'ExtraTranslation'
disable 'BlockedPrivateApi'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
coreLibraryDesugaringEnabled true
}
signingConfigs {
debug {
storeFile file("../TMessagesProj/config/release.keystore")
storePassword RELEASE_STORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
}
release {
storeFile file("../TMessagesProj/config/release.keystore")
storePassword RELEASE_STORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
}
}
buildTypes {
debug {
debuggable true
jniDebuggable true
signingConfig signingConfigs.debug
applicationIdSuffix ".web"
minifyEnabled false
multiDexEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), '../TMessagesProj/proguard-rules.pro', '../TMessagesProj/proguard-rules-beta.pro'
ndk.debugSymbolLevel = 'FULL'
}
standalone {
debuggable false
jniDebuggable false
signingConfig signingConfigs.release
applicationIdSuffix ".web"
minifyEnabled true
multiDexEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), '../TMessagesProj/proguard-rules.pro'
ndk.debugSymbolLevel = 'FULL'
}
}
sourceSets.debug {
manifest.srcFile '../TMessagesProj/config/release/AndroidManifest_standalone.xml'
}
sourceSets.standalone {
manifest.srcFile '../TMessagesProj/config/release/AndroidManifest_standalone.xml'
}
flavorDimensions "minApi"
productFlavors {
afat {
ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
ext {
abiVersionCode = 9
}
sourceSets.standalone {
manifest.srcFile '../TMessagesProj/config/release/AndroidManifest_standalone.xml'
}
}
}
defaultConfig.versionCode = Integer.parseInt(APP_VERSION_CODE)
applicationVariants.all { variant ->
variant.outputs.all { output ->
outputFileName = "app.apk"
output.versionCodeOverride = defaultConfig.versionCode * 10 + variant.productFlavors.get(0).abiVersionCode
}
}
variantFilter { variant ->
def names = variant.flavors*.name
if (variant.buildType.name != "release" && !names.contains("afat")) {
setIgnore(true)
}
}
defaultConfig {
minSdkVersion 21
targetSdkVersion 35
versionName APP_VERSION_NAME
ndkVersion "21.4.7075529"
multiDexEnabled true
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
externalNativeBuild {
cmake {
version '3.10.2'
arguments '-DANDROID_STL=c++_static', '-DANDROID_PLATFORM=android-21' //, '-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON'
}
}
}
buildFeatures {
buildConfig = true
}
namespace 'org.telegram.messenger.web'
lintOptions {
checkReleaseBuilds false
}
}
apply plugin: 'com.google.gms.google-services'

View file

@ -0,0 +1,98 @@
{
"project_info": {
"project_number": "760348033671",
"firebase_url": "https://tmessages2.firebaseio.com",
"project_id": "tmessages2",
"storage_bucket": "tmessages2.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:760348033671:android:f6afd7b67eae3860",
"android_client_info": {
"package_name": "org.telegram.messenger"
}
},
"oauth_client": [
{
"client_id": "760348033671-2hh8ebmuflsnjoc0kldkfells9rhtfni.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyA-t0jLPjUt2FxrA8VPK2EiYHcYcboIR6k"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "760348033671-2hh8ebmuflsnjoc0kldkfells9rhtfni.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:760348033671:android:dc022572c167a16c",
"android_client_info": {
"package_name": "org.telegram.messenger.beta"
}
},
"oauth_client": [
{
"client_id": "760348033671-2hh8ebmuflsnjoc0kldkfells9rhtfni.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyA-t0jLPjUt2FxrA8VPK2EiYHcYcboIR6k"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "760348033671-2hh8ebmuflsnjoc0kldkfells9rhtfni.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:760348033671:android:7396e651423888c3f66e22",
"android_client_info": {
"package_name": "org.telegram.messenger.web"
}
},
"oauth_client": [
{
"client_id": "760348033671-2hh8ebmuflsnjoc0kldkfells9rhtfni.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyA-t0jLPjUt2FxrA8VPK2EiYHcYcboIR6k"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "760348033671-2hh8ebmuflsnjoc0kldkfells9rhtfni.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="auto">
<application android:name="org.telegram.messenger.ApplicationLoaderImpl" tools:replace="name">
<service
android:name="org.telegram.messenger.GoogleVoiceClientService"
android:exported="true"
android:foregroundServiceType="dataSync"
/>
<activity
android:name="org.telegram.messenger.GoogleVoiceClientActivity"
android:exported="true">
<intent-filter>
<action android:name="com.google.android.voicesearch.SEND_MESSAGE_TO_CONTACTS" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<service
android:name="org.telegram.messenger.SMSJobsNotification"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="dataSync|remoteMessaging"
/>
</application>
</manifest>

View file

@ -0,0 +1,298 @@
package org.telegram.messenger;
import static org.telegram.messenger.AndroidUtilities.isInAirplaneMode;
import static org.telegram.ui.PremiumPreviewFragment.applyNewSpan;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.text.SpannableStringBuilder;
import android.view.ViewGroup;
import androidx.core.content.FileProvider;
import org.json.JSONObject;
import org.telegram.messenger.web.BuildConfig;
import org.telegram.messenger.web.R;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TL_smsjobs;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.Adapters.DrawerLayoutAdapter;
import org.telegram.ui.Components.AlertsCreator;
import org.telegram.ui.Components.BulletinFactory;
import org.telegram.ui.Components.UpdateAppAlertDialog;
import org.telegram.ui.Components.UpdateButton;
import org.telegram.ui.Components.UpdateLayout;
import org.telegram.ui.IUpdateButton;
import org.telegram.ui.IUpdateLayout;
import org.telegram.ui.LaunchActivity;
import org.telegram.ui.SMSStatsActivity;
import org.telegram.ui.SMSSubscribeSheet;
import java.io.File;
import java.util.ArrayList;
public class ApplicationLoaderImpl extends ApplicationLoader {
@Override
protected String onGetApplicationId() {
return BuildConfig.APPLICATION_ID;
}
@Override
protected boolean isStandalone() {
return true;
}
@Override
protected void startAppCenterInternal(Activity context) {
}
@Override
protected void checkForUpdatesInternal() {
}
protected void appCenterLogInternal(Throwable e) {
}
protected void logDualCameraInternal(boolean success, boolean vendor) {
}
@Override
public boolean checkApkInstallPermissions(final Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !ApplicationLoader.applicationContext.getPackageManager().canRequestPackageInstalls()) {
AlertsCreator.createApkRestrictedDialog(context, null).show();
return false;
}
return true;
}
@Override
public boolean openApkInstall(Activity activity, TLRPC.Document document) {
boolean exists = false;
try {
String fileName = FileLoader.getAttachFileName(document);
File f = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(document, true);
if (exists = f.exists()) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (Build.VERSION.SDK_INT >= 24) {
intent.setDataAndType(FileProvider.getUriForFile(activity, ApplicationLoader.getApplicationId() + ".provider", f), "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(f), "application/vnd.android.package-archive");
}
try {
activity.startActivityForResult(intent, 500);
} catch (Exception e) {
FileLog.e(e);
}
}
} catch (Exception e) {
FileLog.e(e);
}
return exists;
}
@Override
public boolean showUpdateAppPopup(Context context, TLRPC.TL_help_appUpdate update, int account) {
try {
(new UpdateAppAlertDialog(context, update, account)).show();
} catch (Exception e) {
FileLog.e(e);
}
return true;
}
@Override
public IUpdateLayout takeUpdateLayout(Activity activity, ViewGroup sideMenu, ViewGroup sideMenuContainer) {
return new UpdateLayout(activity, sideMenu, sideMenuContainer);
}
@Override
public IUpdateButton takeUpdateButton(Context context) {
return new UpdateButton(context);
}
@Override
public TLRPC.Update parseTLUpdate(int constructor) {
if (constructor == TL_smsjobs.TL_updateSmsJob.constructor) {
return new TL_smsjobs.TL_updateSmsJob();
}
return super.parseTLUpdate(constructor);
}
@Override
public void processUpdate(int currentAccount, TLRPC.Update update) {
if (update instanceof TL_smsjobs.TL_updateSmsJob) {
SMSJobController.getInstance(currentAccount).processJobUpdate(((TL_smsjobs.TL_updateSmsJob) update).job_id);
}
}
@Override
public boolean extendDrawer(ArrayList<DrawerLayoutAdapter.Item> items) {
if (SMSJobController.getInstance(UserConfig.selectedAccount).isAvailable()) {
CharSequence text = LocaleController.getString(R.string.SmsJobsMenu);
if (MessagesController.getGlobalMainSettings().getBoolean("newppsms", true)) {
text = applyNewSpan(text.toString());
}
DrawerLayoutAdapter.Item item = new DrawerLayoutAdapter.Item(93, text, R.drawable.left_sms).onClick(v -> {
MessagesController.getGlobalMainSettings().edit().putBoolean("newppsms", false).apply();
SMSJobController controller = (SMSJobController) SMSJobController.getInstance(UserConfig.selectedAccount);
final int state = controller.currentState;
if (state == SMSJobController.STATE_NONE) {
SMSSubscribeSheet.show(LaunchActivity.instance, SMSJobController.getInstance(UserConfig.selectedAccount).isEligible, null, null);
return;
} else if (state == SMSJobController.STATE_NO_SIM) {
controller.checkSelectedSIMCard();
if (controller.getSelectedSIM() == null) {
new AlertDialog.Builder(LaunchActivity.instance)
.setTitle(LocaleController.getString(R.string.SmsNoSimTitle))
.setMessage(AndroidUtilities.replaceTags(LocaleController.getString(R.string.SmsNoSimMessage)))
.setPositiveButton(LocaleController.getString(R.string.OK), null)
.show();
return;
}
} else if (state == SMSJobController.STATE_ASKING_PERMISSION) {
SMSSubscribeSheet.requestSMSPermissions(LaunchActivity.instance, () -> {
controller.checkSelectedSIMCard();
if (controller.getSelectedSIM() == null) {
controller.setState(SMSJobController.STATE_NO_SIM);
new AlertDialog.Builder(LaunchActivity.instance)
.setTitle(LocaleController.getString(R.string.SmsNoSimTitle))
.setMessage(AndroidUtilities.replaceTags(LocaleController.getString(R.string.SmsNoSimMessage)))
.setPositiveButton(LocaleController.getString(R.string.OK), null)
.show();
return;
}
ConnectionsManager.getInstance(UserConfig.selectedAccount).sendRequest(new TL_smsjobs.TL_smsjobs_join(), (res, err) -> AndroidUtilities.runOnUIThread(() -> {
if (err != null) {
BulletinFactory.showError(err);
} else if (res instanceof TLRPC.TL_boolFalse) {
BulletinFactory.global().createErrorBulletin(LocaleController.getString(R.string.UnknownError)).show();
} else {
controller.setState(SMSJobController.STATE_JOINED);
controller.loadStatus(true);
SMSSubscribeSheet.showSubscribed(LaunchActivity.instance, null);
BaseFragment lastFragment = LaunchActivity.getLastFragment();
if (lastFragment != null) {
lastFragment.presentFragment(new SMSStatsActivity());
}
}
}));
}, false);
return;
}
BaseFragment lastFragment = LaunchActivity.getLastFragment();
if (lastFragment != null) {
lastFragment.presentFragment(new SMSStatsActivity());
}
});
if (isInAirplaneMode(LaunchActivity.instance) || SMSJobController.getInstance(UserConfig.selectedAccount).hasError()) {
item.withError();
}
items.add(item);
}
return true;
}
@Override
public boolean checkRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
if (SMSSubscribeSheet.checkSMSPermissions(requestCode, permissions, grantResults)) {
return true;
}
return super.checkRequestPermissionResult(requestCode, permissions, grantResults);
}
@Override
public boolean onSuggestionFill(String suggestion, CharSequence[] output, boolean[] closeable) {
if (suggestion == null && SMSJobController.getInstance(UserConfig.selectedAccount).hasError()) {
output[0] = new SpannableStringBuilder().append(SMSStatsActivity.error(17)).append(" ").append(LocaleController.getString(R.string.SmsJobsErrorHintTitle));
output[1] = LocaleController.getString(R.string.SmsJobsErrorHintMessage);
closeable[0] = false;
return true;
}
if ("PREMIUM_SMSJOBS".equals(suggestion) && SMSJobController.getInstance(UserConfig.selectedAccount).currentState != SMSJobController.STATE_JOINED) {
output[0] = LocaleController.getString(R.string.SmsJobsPremiumHintTitle);
output[1] = LocaleController.getString(R.string.SmsJobsPremiumHintMessage);
closeable[0] = true;
return true;
}
return super.onSuggestionFill(suggestion, output, closeable);
}
@Override
public boolean onSuggestionClick(String suggestion) {
if (suggestion == null) {
BaseFragment lastFragment = LaunchActivity.getLastFragment();
if (lastFragment != null) {
SMSJobController.getInstance(UserConfig.selectedAccount).seenError();
SMSStatsActivity fragment = new SMSStatsActivity();
lastFragment.presentFragment(fragment);
AndroidUtilities.runOnUIThread(() -> {
fragment.showDialog(new SMSStatsActivity.SMSHistorySheet(fragment));
}, 800);
}
return true;
} else if ("PREMIUM_SMSJOBS".equals(suggestion)) {
SMSJobController controller = SMSJobController.getInstance(UserConfig.selectedAccount);
if (controller.isEligible != null) {
SMSSubscribeSheet.show(LaunchActivity.instance, controller.isEligible, null, null);
} else {
controller.checkIsEligible(true, isEligible -> {
if (isEligible == null) {
MessagesController.getInstance(UserConfig.selectedAccount).removeSuggestion(0, "PREMIUM_SMSJOBS");
return;
}
SMSSubscribeSheet.show(LaunchActivity.instance, isEligible, null, null);
});
}
return true;
}
return false;
}
@Override
public boolean consumePush(int account, JSONObject json) {
try {
if (json != null && "SMSJOB".equals(json.getString("loc_key"))) {
JSONObject custom = json.getJSONObject("custom");
String job_id = custom.getString("job_id");
SMSJobController.getInstance(UserConfig.selectedAccount).processJobUpdate(job_id);
return true;
}
} catch (Exception e) {
FileLog.e(e);
}
return false;
}
@Override
public boolean onPause() {
super.onPause();
return SMSJobsNotification.check();
}
@Override
public void onResume() {
super.onResume();
SMSJobsNotification.check();
}
@Override
public BaseFragment openSettings(int n) {
if (n == 13) {
if (SMSJobController.getInstance(UserConfig.selectedAccount).getState() == SMSJobController.STATE_JOINED) {
return new SMSStatsActivity();
}
}
return null;
}
}

View file

@ -0,0 +1,19 @@
/*
* This is the source code of Telegram for Android v. 5.x.x
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2018.
*/
package org.telegram.messenger;
import com.google.android.search.verification.client.SearchActionVerificationClientActivity;
import com.google.android.search.verification.client.SearchActionVerificationClientService;
public class GoogleVoiceClientActivity extends SearchActionVerificationClientActivity {
public Class<? extends SearchActionVerificationClientService> getServiceClass() {
return GoogleVoiceClientService.class;
}
}

View file

@ -0,0 +1,22 @@
/*
* This is the source code of Telegram for Android v. 5.x.x
* It is licensed under GNU GPL v. 2 or later.
* You should have received a copy of the license in this archive (see LICENSE).
*
* Copyright Nikolai Kudashov, 2013-2018.
*/
package org.telegram.messenger;
import android.content.Intent;
import android.os.Bundle;
import com.google.android.search.verification.client.SearchActionVerificationClientService;
public class GoogleVoiceClientService extends SearchActionVerificationClientService {
@Override
public void performAction(Intent intent, boolean isVerified, Bundle options) {
AndroidUtilities.googleVoiceClientService_performAction(intent, isVerified, options);
}
}

View file

@ -0,0 +1,984 @@
package org.telegram.messenger;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.telecom.PhoneAccountHandle;
import android.telephony.CellInfo;
import android.telephony.SmsManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresPermission;
import org.telegram.PhoneFormat.PhoneFormat;
import org.telegram.messenger.web.R;
import org.telegram.tgnet.AbstractSerializedData;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.InputSerializedData;
import org.telegram.tgnet.OutputSerializedData;
import org.telegram.tgnet.SerializedData;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TL_smsjobs;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.Components.Bulletin;
import org.telegram.ui.Components.BulletinFactory;
import org.telegram.ui.LaunchActivity;
import org.telegram.ui.SMSSubscribeSheet;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
public class SMSJobController implements NotificationCenter.NotificationCenterDelegate {
private static volatile SMSJobController[] Instance = new SMSJobController[UserConfig.MAX_ACCOUNT_COUNT];
private static final Object[] lockObjects = new Object[UserConfig.MAX_ACCOUNT_COUNT];
static {
for (int i = 0; i < UserConfig.MAX_ACCOUNT_COUNT; i++) {
lockObjects[i] = new Object();
}
}
public static SMSJobController getInstance(int num) {
SMSJobController localInstance = Instance[num];
if (localInstance == null) {
synchronized (lockObjects[num]) {
localInstance = Instance[num];
if (localInstance == null) {
Instance[num] = localInstance = new SMSJobController(num);
}
}
}
return localInstance;
}
public final static int STATE_NONE = 0;
public final static int STATE_ASKING_PERMISSION = 1;
public final static int STATE_NO_SIM = 2;
public final static int STATE_JOINED = 3;
public final int currentAccount;
private int lastErrorId, seenErrorId;
public int currentState;
public TL_smsjobs.TL_smsjobs_status currentStatus;
private boolean loadingStatus, loadedStatus;
public boolean atStatisticsPage;
private boolean loadingIsEligible, loadedIsEligible;
public TL_smsjobs.TL_smsjobs_eligibleToJoin isEligible;
public SIM selectedSimCard;
public boolean isEligible() {
return isEligible != null || loadingIsEligible;
}
private SharedPreferences journalPrefs;
private SMSJobController(int account) {
this.currentAccount = account;
journalPrefs = ApplicationLoader.applicationContext.getSharedPreferences("smsjobs_journal_" + currentAccount, Context.MODE_PRIVATE);
loadCacheStatus();
AndroidUtilities.runOnUIThread(() -> {
readPending();
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.newSuggestionsAvailable);
});
}
@Override
public void didReceivedNotification(int id, int account, Object... args) {
if (id == NotificationCenter.newSuggestionsAvailable) {
if (currentState != STATE_NONE && currentState != STATE_JOINED) {
checkIsEligible(true, null);
}
invalidateStatus();
}
}
public boolean isAvailable() {
if (currentState != STATE_NONE && currentState != STATE_JOINED) {
checkIsEligible(false, null);
loadStatus(false);
}
return currentState != STATE_NONE && (isEligible != null || currentStatus != null);
}
public void checkIsEligible(boolean force, Utilities.Callback<TL_smsjobs.TL_smsjobs_eligibleToJoin> whenDone) {
if (loadedIsEligible && !force || loadingIsEligible && whenDone == null) return;
loadingIsEligible = true;
ConnectionsManager.getInstance(currentAccount).sendRequest(new TL_smsjobs.TL_smsjobs_isEligibleToJoin(), (res, err) -> AndroidUtilities.runOnUIThread(() -> {
loadingIsEligible = false;
loadedIsEligible = true;
if (res instanceof TL_smsjobs.TL_smsjobs_eligibleToJoin) {
isEligible = (TL_smsjobs.TL_smsjobs_eligibleToJoin) res;
} else if (err != null && "NOT_ELIGIBLE".equals(err.text)) {
isEligible = null;
} else if (err != null && "ALREADY_JOINED".equals(err.text)) {
isEligible = null;
} else if (err != null) {
BulletinFactory.showError(err);
}
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.mainUserInfoChanged);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.smsJobStatusUpdate);
if (whenDone != null) {
whenDone.run(isEligible);
}
}));
}
public void loadStatus(boolean force) {
if (loadingStatus || loadedStatus && !force)
return;
loadingStatus = true;
ConnectionsManager.getInstance(currentAccount).sendRequest(new TL_smsjobs.TL_smsjobs_getStatus(), (res, err) -> AndroidUtilities.runOnUIThread(() -> {
TL_smsjobs.TL_smsjobs_status lastStatus = currentStatus;
TL_smsjobs.TL_smsjobs_eligibleToJoin lastIsEligible = isEligible;
int lastState = currentState, state = currentState;
loadingStatus = false;
loadedStatus = true;
if (res instanceof TL_smsjobs.TL_smsjobs_status) {
state = STATE_JOINED;
currentStatus = (TL_smsjobs.TL_smsjobs_status) res;
saveCacheStatus();
} else if (err != null && "NOT_JOINED".equals(err.text)) {
if (state == STATE_JOINED) {
state = STATE_NONE;
}
currentStatus = null;
saveCacheStatus();
} else if (err != null && "NOT_ELIGIBLE".equals(err.text)) {
if (state == STATE_JOINED) {
state = STATE_NONE;
}
currentStatus = null;
isEligible = null;
saveCacheStatus();
} else {
BulletinFactory.showError(err);
}
if (lastStatus != currentStatus || lastIsEligible != isEligible || lastState != state) {
setState(state);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.mainUserInfoChanged);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.smsJobStatusUpdate);
SMSJobsNotification.check();
}
}));
}
public void invalidateStatus() {
loadedStatus = false;
if (atStatisticsPage || ApplicationLoader.mainInterfacePaused) {
loadStatus(false);
}
}
public void init() {
loadStatus(false);
checkSelectedSIMCard();
}
public void checkSelectedSIMCard() {
int selectedSimId = MessagesController.getMainSettings(currentAccount).getInt("smsjobs_sim", -1);
try {
ArrayList<SIM> sims = getSIMs();
if (sims.isEmpty()) {
selectedSimCard = null;
} else if (selectedSimId == -1) {
selectedSimCard = sims.get(0);
} else {
selectedSimCard = sims.get(0);
for (int i = 0; i < sims.size(); ++i) {
if (sims.get(i).id == selectedSimId) {
selectedSimCard = sims.get(i);
break;
}
}
}
} catch (Exception e) {
FileLog.e(e);
selectedSimCard = null;
}
if (selectedSimCard != null && selectedSimCard.id != selectedSimId) {
MessagesController.getMainSettings(currentAccount).edit().putInt("smsjobs_sim", selectedSimCard.id).apply();
}
if (currentState == STATE_NO_SIM && selectedSimCard != null) {
ConnectionsManager.getInstance(currentAccount).sendRequest(new TL_smsjobs.TL_smsjobs_join(), (res, err) -> AndroidUtilities.runOnUIThread(() -> {
if (err != null) {
BulletinFactory.showError(err);
} else if (res instanceof TLRPC.TL_boolFalse) {
BulletinFactory.global().createErrorBulletin(LocaleController.getString(R.string.UnknownError)).show();
} else {
setState(SMSJobController.STATE_JOINED);
loadStatus(true);
SMSSubscribeSheet.showSubscribed(LaunchActivity.instance == null ? ApplicationLoader.applicationContext : LaunchActivity.instance, null);
}
}));
}
}
public SIM getSelectedSIM() {
return selectedSimCard;
}
public void setSelectedSIM(SIM sim) {
if (sim == null) return;
selectedSimCard = sim;
MessagesController.getMainSettings(currentAccount).edit().putInt("smsjobs_sim", selectedSimCard.id).apply();
}
public int simsCount() {
try {
return getSIMs().size();
} catch (Exception e) {
}
return 0;
}
public ArrayList<SIM> getSIMs() {
return getSIMs(ApplicationLoader.applicationContext);
}
private void loadCacheStatus() {
currentState = MessagesController.getMainSettings(currentAccount).getInt("smsjobs_state", STATE_NONE);
lastErrorId = MessagesController.getMainSettings(currentAccount).getInt("smsjobs_error", 0);
seenErrorId = MessagesController.getMainSettings(currentAccount).getInt("smsjobs_seen_error", 0);
String string = MessagesController.getMainSettings(currentAccount).getString("smsjobs_status", null);
if (string != null) {
try {
SerializedData serializedData = new SerializedData(Utilities.hexToBytes(string));
int constructor = serializedData.readInt32(true);
if (constructor == TL_smsjobs.TL_smsjobs_status.constructor) {
currentStatus = new TL_smsjobs.TL_smsjobs_status();
currentStatus.readParams(serializedData, true);
}
} catch (Exception e) {
FileLog.e(e);
currentStatus = null;
}
}
readJournal();
}
public void setState(int state) {
MessagesController.getMainSettings(currentAccount).edit().putInt("smsjobs_state", this.currentState = state).apply();
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.smsJobStatusUpdate);
if (currentState == STATE_JOINED) {
MessagesController.getInstance(currentAccount).removeSuggestion(0, "PREMIUM_SMSJOBS");
}
SMSJobsNotification.check();
}
public int getState() {
return currentState;
}
private void saveCacheStatus() {
SharedPreferences.Editor edit = MessagesController.getMainSettings(currentAccount).edit();
if (currentStatus == null) {
edit.remove("smsjobs_status");
} else {
SerializedData data = new SerializedData(currentStatus.getObjectSize());
currentStatus.serializeToStream(data);
edit.putString("smsjobs_status", Utilities.bytesToHex(data.toByteArray()));
}
edit.apply();
}
private HashSet<String> completedJobs = new HashSet<>();
private HashSet<String> loadingJobs = new HashSet<>();
public void processJobUpdate(String job_id) {
if (currentState != STATE_JOINED) {
FileLog.d("[smsjob] received update on sms job " + job_id + ", but we did not join!!! currentState=" + currentState);
return;
}
if (completedJobs.contains(job_id) || loadingJobs.contains(job_id)) return;
loadingJobs.add(job_id);
FileLog.d("[smsjob] received update on sms job " + job_id + ", fetching");
TL_smsjobs.TL_smsjobs_getSmsJob req = new TL_smsjobs.TL_smsjobs_getSmsJob();
req.job_id = job_id;
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> {
if (res instanceof TL_smsjobs.TL_smsJob) {
runJob((TL_smsjobs.TL_smsJob) res);
} else {
FileLog.e("[smsjob] failed to fetch sms job by id " + job_id);
loadingJobs.remove(job_id);
}
}));
}
private void runJob(TL_smsjobs.TL_smsJob job) {
checkSelectedSIMCard();
final String phone_number = !job.phone_number.startsWith("+") ? "+" + job.phone_number : job.phone_number;
FileLog.d("[smsjob] running sms job " + job.job_id + (BuildVars.DEBUG_PRIVATE_VERSION ? ": " + job.text + " to " + phone_number : "") + ", selected sim: " + (selectedSimCard == null ? "null" : "{id=" + selectedSimCard.id + ", icc=" + selectedSimCard.iccId + ", name=" + selectedSimCard.name + ", slot=" + selectedSimCard.slot + "}"));
pushToJournal(job.job_id, 1, phone_number, null);
sendSMS(
ApplicationLoader.applicationContext,
currentAccount,
job.job_id,
selectedSimCard,
phone_number,
job.text
);
}
private void finishJob(String job_id, String phone_number, String error) {
FileLog.d("[smsjob] finished sms job " + job_id + ", error=" + error);
TL_smsjobs.TL_smsjobs_finishJob req = new TL_smsjobs.TL_smsjobs_finishJob();
req.job_id = job_id;
if (error != null) {
req.flags |= 1;
req.error = error;
}
completedJobs.add(job_id);
loadingJobs.remove(job_id);
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> {
if (res instanceof TLRPC.TL_boolTrue) {
FileLog.d("[smsjob] finished sms job " + job_id + ", received true");
} else if (res instanceof TLRPC.TL_boolFalse) {
FileLog.d("[smsjob] finished sms job " + job_id + ", received false");
} else if (err != null) {
FileLog.d("[smsjob] finished sms job " + job_id + ", received error " + err.code + " " + err.text);
}
pushToJournal(job_id, 0, phone_number, error);
invalidateStatus();
}));
}
private static class PendingSMS extends TLObject {
public int id;
public int currentAccount;
public String jobId;
public int simId;
public String phone, text;
public final boolean received[] = new boolean[2];
public boolean finished = false;
public int triesLeft = 2;
public long sentTime = System.currentTimeMillis();
private Runnable timerCallback;
private PendingSMS() {}
public PendingSMS(
int id,
int currentAccount,
String jobId,
SIM sim,
String phone,
String text
) {
this.id = id;
this.currentAccount = currentAccount;
this.jobId = jobId;
this.simId = sim == null ? -1 : sim.id;
this.phone = phone;
this.text = text;
}
public void setup() {
final long now = System.currentTimeMillis();
long timeout = sentTime + 2 * 60 * 1000 - now;
AndroidUtilities.runOnUIThread(timerCallback = () -> {
whenSent(false, "2MIN_TIMEOUT");
SMSJobController.pending.remove(id);
savePending();
}, Math.max(0, timeout));
}
public void whenSent(boolean success, String reason) {
if (received[0]) return;
received[0] = true;
AndroidUtilities.cancelRunOnUIThread(timerCallback);
FileLog.d("[smsjob] sms job " + jobId + " sent callback: success=" + success + ", reason=" + reason);
if (!finished) {
finished = true;
SMSJobController.getInstance(currentAccount).finishJob(jobId, phone, success ? null : reason);
}
}
public void whenDelivered(boolean success, String reason) {
if (received[1]) return;
received[1] = true;
AndroidUtilities.cancelRunOnUIThread(timerCallback);
FileLog.d("[smsjob] sms job " + jobId + " delivered callback: success=" + success + ", reason=" + reason);
if (!finished) {
finished = true;
SMSJobController.getInstance(currentAccount).finishJob(jobId, phone, success ? null : reason);
}
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(0x8384213);
stream.writeInt32(id);
stream.writeInt32(currentAccount);
stream.writeString(jobId);
stream.writeInt32(simId);
stream.writeString(phone);
stream.writeString(text);
int flags = 0;
flags |= (received[0] ? 1 : 0);
flags |= (received[1] ? 2 : 0);
flags |= (finished ? 4 : 0);
stream.writeInt32(flags);
stream.writeInt32(triesLeft);
stream.writeInt64(sentTime);
}
@Override
public void readParams(InputSerializedData stream, boolean exception) {
id = stream.readInt32(exception);
currentAccount = stream.readInt32(exception);
jobId = stream.readString(exception);
simId = stream.readInt32(exception);
phone = stream.readString(exception);
text = stream.readString(exception);
int flags = stream.readInt32(exception);
received[0] = (flags & 1) != 0;
received[1] = (flags & 2) != 0;
finished = (flags & 4) != 0;
triesLeft = stream.readInt32(exception);
sentTime = stream.readInt64(exception);
}
}
private static boolean readCachedPending = false;
private static HashMap<Integer, PendingSMS> pending = new HashMap<>();
private static void readPending() {
if (readCachedPending) return;
String pendingText = MessagesController.getGlobalMainSettings().getString("smsjobs_pending", null);
if (pendingText != null) {
try {
SerializedData serializedData = new SerializedData(Utilities.hexToBytes(pendingText));
int count = serializedData.readInt32(true);
for (int i = 0; i < count; ++i) {
int magic = serializedData.readInt32(true);
if (magic != 0x8384213) throw new RuntimeException("pending parse unknown magic " + magic);
PendingSMS pending = new PendingSMS();
pending.readParams(serializedData, true);
pending.setup();
SMSJobController.pending.put(pending.id, pending);
}
} catch (Exception e) {
FileLog.e(e);
}
}
readCachedPending = true;
}
private static void savePending() {
if (pending.isEmpty()) {
MessagesController.getGlobalMainSettings().edit().remove("smsjobs_pending").apply();
return;
}
try {
int size = pending.size() * 8;
for (PendingSMS pending : pending.values()) {
size += pending.getObjectSize();
}
SerializedData data = new SerializedData(size);
data.writeInt32(pending.size());
for (PendingSMS pending : pending.values()) {
pending.serializeToStream(data);
}
MessagesController.getGlobalMainSettings().edit().putString("smsjobs_pending", Utilities.bytesToHex(data.toByteArray())).apply();
} catch (Exception e) {
FileLog.e(e);
}
}
public static void receivedSMSIntent(Intent intent, final int resultCode) {
if (intent == null) return;
final int id = intent.getIntExtra("tg_sms_id", 0);
final boolean sent = intent.getBooleanExtra("sent", false);
final boolean delivered = intent.getBooleanExtra("delivered", false);
PendingSMS pending = SMSJobController.pending.get(id);
if (pending == null) {
FileLog.d("[smsjob] received sms callback with id " + id + ", "+ (sent ? "sent" : (delivered ? "delivered" : "null")) +": not found");
return;
}
FileLog.d("[smsjob] received sms callback with id " + id + ", "+ (sent ? "sent" : (delivered ? "delivered" : "null")));
if (resultCode == SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY && pending.triesLeft > 0) {
pending.triesLeft--;
resendPending(pending);
return;
}
boolean success = false;
String reason = null;
switch (resultCode) {
case Activity.RESULT_OK:
success = true;
break;
case SmsManager.RESULT_ERROR_NONE: reason = "RESULT_ERROR_NONE"; break;
case SmsManager.RESULT_ERROR_GENERIC_FAILURE: reason = "RESULT_ERROR_GENERIC_FAILURE"; break;
case SmsManager.RESULT_ERROR_RADIO_OFF: reason = "RESULT_ERROR_RADIO_OFF"; break;
case SmsManager.RESULT_ERROR_NULL_PDU: reason = "RESULT_ERROR_NULL_PDU"; break;
case SmsManager.RESULT_ERROR_NO_SERVICE: reason = "RESULT_ERROR_NO_SERVICE"; break;
case SmsManager.RESULT_ERROR_LIMIT_EXCEEDED: reason = "RESULT_ERROR_LIMIT_EXCEEDED"; break;
case SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE: reason = "RESULT_ERROR_FDN_CHECK_FAILURE"; break;
case SmsManager.RESULT_ERROR_SHORT_CODE_NOT_ALLOWED: reason = "RESULT_ERROR_SHORT_CODE_NOT_ALLOWED"; break;
case SmsManager.RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED: reason = "RESULT_ERROR_SHORT_CODE_NEVER_ALLOWED"; break;
case SmsManager.RESULT_RADIO_NOT_AVAILABLE: reason = "RESULT_RADIO_NOT_AVAILABLE"; break;
case SmsManager.RESULT_NETWORK_REJECT: reason = "RESULT_NETWORK_REJECT"; break;
case SmsManager.RESULT_INVALID_ARGUMENTS: reason = "RESULT_INVALID_ARGUMENTS"; break;
case SmsManager.RESULT_INVALID_STATE: reason = "RESULT_INVALID_STATE"; break;
case SmsManager.RESULT_NO_MEMORY: reason = "RESULT_NO_MEMORY"; break;
case SmsManager.RESULT_INVALID_SMS_FORMAT: reason = "RESULT_INVALID_SMS_FORMAT"; break;
case SmsManager.RESULT_SYSTEM_ERROR: reason = "RESULT_SYSTEM_ERROR"; break;
case SmsManager.RESULT_MODEM_ERROR: reason = "RESULT_MODEM_ERROR"; break;
case SmsManager.RESULT_NETWORK_ERROR: reason = "RESULT_NETWORK_ERROR"; break;
case SmsManager.RESULT_ENCODING_ERROR: reason = "RESULT_ENCODING_ERROR"; break;
case SmsManager.RESULT_INVALID_SMSC_ADDRESS: reason = "RESULT_INVALID_SMSC_ADDRESS"; break;
case SmsManager.RESULT_OPERATION_NOT_ALLOWED: reason = "RESULT_OPERATION_NOT_ALLOWED"; break;
case SmsManager.RESULT_INTERNAL_ERROR: reason = "RESULT_INTERNAL_ERROR"; break;
case SmsManager.RESULT_NO_RESOURCES: reason = "RESULT_NO_RESOURCES"; break;
case SmsManager.RESULT_CANCELLED: reason = "RESULT_CANCELLED"; break;
case SmsManager.RESULT_REQUEST_NOT_SUPPORTED: reason = "RESULT_REQUEST_NOT_SUPPORTED"; break;
case SmsManager.RESULT_NO_BLUETOOTH_SERVICE: reason = "RESULT_NO_BLUETOOTH_SERVICE"; break;
case SmsManager.RESULT_INVALID_BLUETOOTH_ADDRESS: reason = "RESULT_INVALID_BLUETOOTH_ADDRESS"; break;
case SmsManager.RESULT_BLUETOOTH_DISCONNECTED: reason = "RESULT_BLUETOOTH_DISCONNECTED"; break;
case SmsManager.RESULT_UNEXPECTED_EVENT_STOP_SENDING: reason = "RESULT_UNEXPECTED_EVENT_STOP_SENDING"; break;
case SmsManager.RESULT_SMS_BLOCKED_DURING_EMERGENCY: reason = "RESULT_SMS_BLOCKED_DURING_EMERGENCY"; break;
case SmsManager.RESULT_SMS_SEND_RETRY_FAILED: reason = "RESULT_SMS_SEND_RETRY_FAILED"; break;
case SmsManager.RESULT_REMOTE_EXCEPTION: reason = "RESULT_REMOTE_EXCEPTION"; break;
case SmsManager.RESULT_NO_DEFAULT_SMS_APP: reason = "RESULT_NO_DEFAULT_SMS_APP"; break;
case SmsManager.RESULT_RIL_RADIO_NOT_AVAILABLE: reason = "RESULT_RIL_RADIO_NOT_AVAILABLE"; break;
case SmsManager.RESULT_RIL_SMS_SEND_FAIL_RETRY: reason = "RESULT_RIL_SMS_SEND_FAIL_RETRY"; break;
case SmsManager.RESULT_RIL_NETWORK_REJECT: reason = "RESULT_RIL_NETWORK_REJECT"; break;
case SmsManager.RESULT_RIL_INVALID_STATE: reason = "RESULT_RIL_INVALID_STATE"; break;
case SmsManager.RESULT_RIL_INVALID_ARGUMENTS: reason = "RESULT_RIL_INVALID_ARGUMENTS"; break;
case SmsManager.RESULT_RIL_NO_MEMORY: reason = "RESULT_RIL_NO_MEMORY"; break;
case SmsManager.RESULT_RIL_REQUEST_RATE_LIMITED: reason = "RESULT_RIL_REQUEST_RATE_LIMITED"; break;
case SmsManager.RESULT_RIL_INVALID_SMS_FORMAT: reason = "RESULT_RIL_INVALID_SMS_FORMAT"; break;
case SmsManager.RESULT_RIL_SYSTEM_ERR: reason = "RESULT_RIL_SYSTEM_ERR"; break;
case SmsManager.RESULT_RIL_ENCODING_ERR: reason = "RESULT_RIL_ENCODING_ERR"; break;
case SmsManager.RESULT_RIL_INVALID_SMSC_ADDRESS: reason = "RESULT_RIL_INVALID_SMSC_ADDRESS"; break;
case SmsManager.RESULT_RIL_MODEM_ERR: reason = "RESULT_RIL_MODEM_ERR"; break;
case SmsManager.RESULT_RIL_NETWORK_ERR: reason = "RESULT_RIL_NETWORK_ERR"; break;
case SmsManager.RESULT_RIL_INTERNAL_ERR: reason = "RESULT_RIL_INTERNAL_ERR"; break;
case SmsManager.RESULT_RIL_REQUEST_NOT_SUPPORTED: reason = "RESULT_RIL_REQUEST_NOT_SUPPORTED"; break;
case SmsManager.RESULT_RIL_INVALID_MODEM_STATE: reason = "RESULT_RIL_INVALID_MODEM_STATE"; break;
case SmsManager.RESULT_RIL_NETWORK_NOT_READY: reason = "RESULT_RIL_NETWORK_NOT_READY"; break;
case SmsManager.RESULT_RIL_OPERATION_NOT_ALLOWED: reason = "RESULT_RIL_OPERATION_NOT_ALLOWED"; break;
case SmsManager.RESULT_RIL_NO_RESOURCES: reason = "RESULT_RIL_NO_RESOURCES"; break;
case SmsManager.RESULT_RIL_CANCELLED: reason = "RESULT_RIL_CANCELLED"; break;
case SmsManager.RESULT_RIL_SIM_ABSENT: reason = "RESULT_RIL_SIM_ABSENT"; break;
case SmsManager.RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED: reason = "RESULT_RIL_SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED"; break;
case SmsManager.RESULT_RIL_ACCESS_BARRED: reason = "RESULT_RIL_ACCESS_BARRED"; break;
case SmsManager.RESULT_RIL_BLOCKED_DUE_TO_CALL: reason = "RESULT_RIL_BLOCKED_DUE_TO_CALL"; break;
case SmsManager.RESULT_RIL_GENERIC_ERROR: reason = "RESULT_RIL_GENERIC_ERROR"; break;
case SmsManager.RESULT_RECEIVE_DISPATCH_FAILURE: reason = "RESULT_RECEIVE_DISPATCH_FAILURE"; break;
case SmsManager.RESULT_RECEIVE_INJECTED_NULL_PDU: reason = "RESULT_RECEIVE_INJECTED_NULL_PDU"; break;
case SmsManager.RESULT_RECEIVE_RUNTIME_EXCEPTION: reason = "RESULT_RECEIVE_RUNTIME_EXCEPTION"; break;
case SmsManager.RESULT_RECEIVE_NULL_MESSAGE_FROM_RIL: reason = "RESULT_RECEIVE_NULL_MESSAGE_FROM_RIL"; break;
case SmsManager.RESULT_RECEIVE_WHILE_ENCRYPTED: reason = "RESULT_RECEIVE_WHILE_ENCRYPTED"; break;
case SmsManager.RESULT_RECEIVE_SQL_EXCEPTION: reason = "RESULT_RECEIVE_SQL_EXCEPTION"; break;
case SmsManager.RESULT_RECEIVE_URI_EXCEPTION: reason = "RESULT_RECEIVE_URI_EXCEPTION"; break;
case 33: reason = "RESULT_USER_NOT_ALLOWED"; break;
default: reason = "UNKNOWN_EXCEPTION_" + resultCode; break;
}
int errorCode = intent.getIntExtra("errorCode", -1);
if (errorCode != -1) {
reason += "_" + errorCode;
}
if (sent) {
pending.whenSent(success, reason);
} else if (delivered) {
pending.whenDelivered(success, reason);
}
if (pending.received[0] || pending.received[1]) {
SMSJobController.pending.remove(id);
savePending();
}
}
private static void sendSMS(
Context context,
int currentAccount,
String jobId,
SIM sim,
String phone,
String text
) {
SmsManager smsManager;
if (sim != null && Build.VERSION.SDK_INT >= 31) {
smsManager = context.getSystemService(SmsManager.class).createForSubscriptionId(sim.id);
} else if (sim != null && Build.VERSION.SDK_INT >= 22) {
smsManager = SmsManager.getSmsManagerForSubscriptionId(sim.id);
} else {
smsManager = SmsManager.getDefault();
}
final int ID = (int) (Math.random() * 1_000_000);
final Intent sentIntent = new Intent(context, SMSResultService.class);
sentIntent.putExtra("sent", true);
sentIntent.putExtra("tg_sms_id", ID);
final PendingIntent sentPI = PendingIntent.getBroadcast(context, 0, sentIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
final Intent deliveredIntent = new Intent(context, SMSResultService.class);
deliveredIntent.putExtra("delivered", true);
deliveredIntent.putExtra("tg_sms_id", ID);
final PendingIntent deliveredPI = PendingIntent.getBroadcast(context, 0, deliveredIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
PendingSMS pendingSMS = new PendingSMS(ID, currentAccount, jobId, sim, phone, text);
pendingSMS.setup();
pending.put(ID, pendingSMS);
savePending();
FileLog.d("[smsjob] sending sms with id " + ID);
try {
smsManager.sendTextMessage(phone, null, text, sentPI, deliveredPI);
FileLog.d("[smsjob] sent sms with id " + ID);
} catch (Throwable e) {
FileLog.e("[smsjob] failed to send sms with id " + ID + ", caught error", e);
pendingSMS.whenSent(false, (e == null ? "CAUGHT_EXCEPTION" : e.getMessage()));
}
}
private static void resendPending(PendingSMS pending) {
Context context = ApplicationLoader.applicationContext;
if (context == null) {
context = LaunchActivity.instance;
}
if (context == null) {
FileLog.d("[smsjob] resending failed: no context; with id " + pending.id);
pending.whenSent(false, "RESENDING_NULL_CONTEXT");
return;
}
SmsManager smsManager;
if (pending.simId != -1 && Build.VERSION.SDK_INT >= 31) {
smsManager = context.getSystemService(SmsManager.class).createForSubscriptionId(pending.simId);
} else if (pending.simId != -1 && Build.VERSION.SDK_INT >= 22) {
smsManager = SmsManager.getSmsManagerForSubscriptionId(pending.simId);
} else {
smsManager = SmsManager.getDefault();
}
final Intent sentIntent = new Intent(context, SMSResultService.class);
sentIntent.putExtra("sent", true);
sentIntent.putExtra("tg_sms_id", pending.id);
final PendingIntent sentPI = PendingIntent.getBroadcast(context, 0, sentIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
final Intent deliveredIntent = new Intent(context, SMSResultService.class);
deliveredIntent.putExtra("delivered", true);
deliveredIntent.putExtra("tg_sms_id", pending.id);
final PendingIntent deliveredPI = PendingIntent.getBroadcast(context, 0, deliveredIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
FileLog.d("[smsjob] resending sms with id " + pending.id);
try {
smsManager.sendTextMessage(pending.phone, null, pending.text, sentPI, deliveredPI);
FileLog.d("[smsjob] resent sms with id " + pending.id);
} catch (Throwable e) {
FileLog.e("[smsjob] failed to resend sms with id " + pending.id + ", caught error", e);
pending.whenSent(false, (e == null ? "CAUGHT_EXCEPTION" : e.getMessage()));
}
}
public static class SIM {
public final int id;
public final int slot;
public final String name;
public final String iccId;
public final String country;
public final String carrier;
public final String phone_number;
public SIM(int id, int slot, String name, String country) {
this.id = id;
this.slot = slot;
this.name = name;
this.iccId = null;
this.country = country;
this.carrier = null;
this.phone_number = null;
}
public SIM(int id, int slot, String country) {
this.id = id;
this.slot = slot;
this.name = "SIM" + (1 + slot);
this.iccId = null;
this.country = country;
this.carrier = null;
this.phone_number = null;
}
public SIM(int id, int slot, String name, String iccId, String country, String carrier, String phoneNumber) {
this.id = id;
this.slot = slot;
this.name = name;
this.iccId = iccId;
this.country = country;
this.carrier = carrier;
this.phone_number = phoneNumber;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
@RequiresPermission(Manifest.permission.READ_PHONE_NUMBERS)
public static SIM from(SubscriptionManager subscriptionManager, SubscriptionInfo info) {
if (info == null) {
return null;
}
String phoneNumber;
if (android.os.Build.VERSION.SDK_INT >= 33) {
phoneNumber = subscriptionManager.getPhoneNumber(info.getSubscriptionId());
} else {
phoneNumber = info.getNumber();
}
return new SIM(
info.getSubscriptionId(),
info.getSimSlotIndex(),
info.getDisplayName() == null ? "" : info.getDisplayName().toString(),
info.getIccId(),
info.getCountryIso(),
info.getCarrierName() == null ? null : info.getCarrierName().toString(),
phoneNumber
);
}
@NonNull
@Override
public String toString() {
return (country != null ? "[" + country + "] " : "") + name + (carrier != null ? " (" + carrier + ")" : "");
}
}
private static ArrayList<SIM> getSIMs(Context context) {
final ArrayList<SIM> simInfoList = new ArrayList<>();
if (Build.VERSION.SDK_INT >= 22) {
final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
List<SubscriptionInfo> infos = null;
if (Build.VERSION.SDK_INT >= 30) {
infos = subscriptionManager.getCompleteActiveSubscriptionInfoList();
}
if ((infos == null || infos.isEmpty()) && Build.VERSION.SDK_INT >= 28) {
infos = subscriptionManager.getAccessibleSubscriptionInfoList();
}
if (infos == null || infos.isEmpty()) {
infos = subscriptionManager.getActiveSubscriptionInfoList();
}
if (infos != null) {
for (int i = 0; i < infos.size(); ++i) {
SIM sim = SIM.from(subscriptionManager, infos.get(i));
if (sim != null) {
simInfoList.add(sim);
}
}
}
} else {
final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
if (telephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY) {
SIM sim = new SIM(0, 0, telephonyManager.getSimCountryIso());
simInfoList.add(sim);
}
}
return simInfoList;
}
public void leave() {
currentStatus = null;
setState(STATE_NONE);
clearJournal();
ConnectionsManager.getInstance(currentAccount).sendRequest(new TL_smsjobs.TL_smsjobs_leave(), (res, err) -> AndroidUtilities.runOnUIThread(() -> {
if (err != null) {
BulletinFactory.showError(err);
} else if (res instanceof TLRPC.TL_boolFalse) {
BulletinFactory.global().createErrorBulletin(LocaleController.getString(R.string.UnknownError)).show();
} else {
SMSJobController.getInstance(currentAccount).loadStatus(true);
SMSJobController.getInstance(currentAccount).checkIsEligible(true, null);
}
}));
SMSJobsNotification.check();
}
private int updateSettingsReqId;
public void toggleAllowInternational(boolean value) {
if (currentStatus == null) return;
if (updateSettingsReqId != 0) {
ConnectionsManager.getInstance(currentAccount).cancelRequest(updateSettingsReqId, true);
}
if (currentStatus.allow_international == value) return;
TL_smsjobs.TL_smsjobs_updateSettings req = new TL_smsjobs.TL_smsjobs_updateSettings();
req.allow_international = value;
final int[] reqId = new int[1];
updateSettingsReqId = reqId[0] = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> {
if (reqId[0] != updateSettingsReqId) return;
updateSettingsReqId = 0;
if (currentStatus != null) {
currentStatus.allow_international = value;
}
}));
}
public ArrayList<JobEntry> journal = new ArrayList<>();
private void readJournal() {
journal.clear();
for (Object v : journalPrefs.getAll().values()) {
if (v instanceof String) {
JobEntry entry = JobEntry.fromString((String) v);
if (entry != null) {
journal.add(entry);
}
}
}
Collections.sort(journal, (a, b) -> b.date - a.date);
if (!MessagesController.getMainSettings(currentAccount).getBoolean("smsjobs_checked_journal", false)) {
for (int i = 0; i < journal.size(); ++i) {
if (!TextUtils.isEmpty(journal.get(i).error)) {
registerError();
break;
}
}
MessagesController.getMainSettings(currentAccount).edit().putBoolean("smsjobs_checked_journal", true).apply();
}
}
private void clearJournal() {
journal.clear();
journalPrefs.edit().clear().apply();
}
private void pushToJournal(String job_id, int state, String phone_number, String error) {
JobEntry entry = null;
for (int i = 0; i < journal.size(); ++i) {
if (TextUtils.equals(journal.get(i).job_id, job_id)) {
entry = journal.get(i);
break;
}
}
if (entry == null) {
journal.add(0, entry = new JobEntry());
}
entry.state = state;
entry.job_id = job_id;
entry.error = error;
entry.date = ConnectionsManager.getInstance(currentAccount).getCurrentTime();
entry.country = getCountryFromPhoneNumber(ApplicationLoader.applicationContext, phone_number);
journalPrefs.edit().putString(entry.job_id, entry.toString()).apply();
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.smsJobStatusUpdate);
if (!TextUtils.isEmpty(error)) {
registerError();
}
}
public static String getCountryFromPhoneNumber(Context context, String phone_number) {
if (phone_number == null) return null;
final String phone = PhoneFormat.stripExceptNumbers(phone_number);
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(context.getResources().getAssets().open("countries.txt")));
String line;
while ((line = reader.readLine()) != null) {
String[] args = line.split(";");
if (phone.startsWith(args[0])) {
return args[1];
}
}
reader.close();
} catch (Exception e) {
FileLog.e(e);
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (Exception e2) {
FileLog.e(e2);
}
}
return "";
}
public static class JobEntry {
public String job_id;
public String error;
public String country;
public int state;
public int date;
@NonNull
@Override
public String toString() {
return job_id + "," + (error == null ? "" : error) + "," + date + "," + country + "," + state;
}
public static JobEntry fromString(String string) {
String[] parts = string.split(",");
if (parts.length != 4 && parts.length != 5) return null;
JobEntry entry = new JobEntry();
entry.job_id = parts[0];
entry.error = TextUtils.isEmpty(parts[1]) ? null : parts[1];
entry.date = Utilities.parseInt(parts[2]);
entry.country = parts[3];
entry.state = parts.length >= 5 ? Utilities.parseInt(parts[4]) : 0;
return entry;
}
}
public boolean hasError() {
return lastErrorId > seenErrorId;
}
public void registerError() {
final boolean hadError = hasError();
MessagesController.getMainSettings(currentAccount).edit().putInt("smsjobs_error", ++lastErrorId).apply();
if (hasError() != hadError) {
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.mainUserInfoChanged);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.smsJobStatusUpdate);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.premiumPromoUpdated);
}
}
public void seenError() {
if (seenErrorId < lastErrorId) {
final boolean hadError = hasError();
MessagesController.getMainSettings(currentAccount).edit().putInt("smsjobs_seen_error", seenErrorId = lastErrorId).apply();
if (hasError() != hadError) {
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.mainUserInfoChanged);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.smsJobStatusUpdate);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.premiumPromoUpdated);
}
}
}
}

View file

@ -0,0 +1,152 @@
package org.telegram.messenger;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.app.ServiceCompat;
import org.telegram.messenger.web.R;
import org.telegram.tgnet.TL_smsjobs;
import org.telegram.ui.LaunchActivity;
public class SMSJobsNotification extends Service {
private static SMSJobsNotification[] instance = new SMSJobsNotification[UserConfig.MAX_ACCOUNT_COUNT];
private static Intent[] service = new Intent[UserConfig.MAX_ACCOUNT_COUNT];
public int currentAccount;
public boolean shown;
public SMSJobsNotification() {
super();
}
public static boolean check() {
boolean shown = false;
for (int i = 0; i < UserConfig.MAX_ACCOUNT_COUNT; ++i) {
shown = check(i) || shown;
}
return shown;
}
public static boolean check(int currentAccount) {
boolean showNotification = ApplicationLoader.mainInterfacePaused;
if (showNotification) {
showNotification = MessagesController.getInstance(currentAccount).smsjobsStickyNotificationEnabled;
}
if (showNotification) {
showNotification = (
SMSJobController.getInstance(currentAccount).getState() == SMSJobController.STATE_JOINED &&
SMSJobController.getInstance(currentAccount).currentStatus != null
);
}
final boolean shownNow = instance[currentAccount] != null && instance[currentAccount].shown;
if (shownNow != showNotification) {
if (showNotification) {
service[currentAccount] = new Intent(ApplicationLoader.applicationContext, SMSJobsNotification.class);
service[currentAccount].putExtra("account", currentAccount);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ApplicationLoader.applicationContext.startForegroundService(service[currentAccount]);
} else {
ApplicationLoader.applicationContext.startService(service[currentAccount]);
}
} else {
if (service[currentAccount] != null) {
ApplicationLoader.applicationContext.stopService(service[currentAccount]);
service[currentAccount] = null;
}
}
} else if (shownNow) {
instance[currentAccount].update();
}
return showNotification;
}
@Override
public void onDestroy() {
shown = false;
super.onDestroy();
try {
stopForeground(true);
} catch (Throwable ignore) {}
try {
NotificationManagerCompat.from(ApplicationLoader.applicationContext).cancel(38);
} catch (Throwable ignore) {}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private NotificationCompat.Builder builder;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
currentAccount = intent.getIntExtra("account", UserConfig.selectedAccount);
if (instance[currentAccount] != this && instance[currentAccount] != null) {
instance[currentAccount].stopSelf();
}
instance[currentAccount] = this;
shown = true;
if (builder == null) {
NotificationsController.checkOtherNotificationsChannel();
builder = new NotificationCompat.Builder(ApplicationLoader.applicationContext, NotificationsController.OTHER_NOTIFICATIONS_CHANNEL);
builder.setSmallIcon(R.drawable.left_sms);
builder.setWhen(System.currentTimeMillis());
builder.setChannelId(NotificationsController.OTHER_NOTIFICATIONS_CHANNEL);
Intent openIntent = new Intent(ApplicationLoader.applicationContext, LaunchActivity.class);
openIntent.setData(Uri.parse("tg://settings/premium_sms"));
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, openIntent, PendingIntent.FLAG_IMMUTABLE);
builder.setContentIntent(pendingIntent);
}
builder.setContentTitle(LocaleController.getString(R.string.SmsNotificationTitle));
TL_smsjobs.TL_smsjobs_status status = SMSJobController.getInstance(currentAccount).currentStatus;
final int sent = status != null ? status.recent_sent : 0;
final int all = status != null ? status.recent_sent + status.recent_remains : 100;
builder.setContentText(LocaleController.formatString(R.string.SmsNotificationSubtitle, sent, all));
builder.setProgress(all, sent, false);
try {
startForeground(38, builder.build());
} catch (Throwable e) {
FileLog.e(e);
}
AndroidUtilities.runOnUIThread(this::updateNotify);
return Service.START_NOT_STICKY;
}
public void update() {
if (builder != null) {
builder.setContentTitle(LocaleController.getString(R.string.SmsNotificationTitle));
TL_smsjobs.TL_smsjobs_status status = SMSJobController.getInstance(currentAccount).currentStatus;
final int sent = status != null ? status.recent_sent : 0;
final int all = status != null ? status.recent_sent + status.recent_remains : 100;
builder.setContentText(LocaleController.formatString(R.string.SmsNotificationSubtitle, sent, all));
builder.setProgress(all, sent, false);
}
updateNotify();
}
private void updateNotify() {
if (builder == null) return;
try {
NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(38, builder.build());
} catch (Throwable e) {
FileLog.e(e);
}
}
}

View file

@ -0,0 +1,12 @@
package org.telegram.messenger;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class SMSResultService extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
SMSJobController.receivedSMSIntent(intent, getResultCode());
}
}

View file

@ -0,0 +1,238 @@
package org.telegram.tgnet;
public class TL_smsjobs {
public static class TL_smsjobs_eligibleToJoin extends TLObject {
public static final int constructor = 0xdc8b44cf;
public String terms_of_use;
public int monthly_sent_sms;
@Override
public void readParams(InputSerializedData stream, boolean exception) {
terms_of_use = stream.readString(exception);
monthly_sent_sms = stream.readInt32(exception);
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(constructor);
stream.writeString(terms_of_use);
stream.writeInt32(monthly_sent_sms);
}
}
public static class TL_smsjobs_status extends TLObject {
public static final int constructor = 0x2aee9191;
public int flags;
public boolean allow_international;
public int recent_sent;
public int recent_since;
public int recent_remains;
public int total_sent;
public int total_since;
public String last_gift_slug;
public String terms_url;
@Override
public void readParams(InputSerializedData stream, boolean exception) {
flags = stream.readInt32(exception);
allow_international = (flags & 1) != 0;
recent_sent = stream.readInt32(exception);
recent_since = stream.readInt32(exception);
recent_remains = stream.readInt32(exception);
total_sent = stream.readInt32(exception);
total_since = stream.readInt32(exception);
if ((flags & 2) != 0) {
last_gift_slug = stream.readString(exception);
}
terms_url = stream.readString(exception);
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(constructor);
flags = allow_international ? flags | 1 : flags &~ 1;
stream.writeInt32(flags);
stream.writeInt32(recent_sent);
stream.writeInt32(recent_since);
stream.writeInt32(recent_remains);
stream.writeInt32(total_sent);
stream.writeInt32(total_since);
if ((flags & 2) != 0) {
stream.writeString(last_gift_slug);
}
stream.writeString(terms_url);
}
}
public static class TL_updateSmsJob extends TLRPC.Update {
public static final int constructor = 0xf16269d4;
public String job_id;
@Override
public void readParams(InputSerializedData stream, boolean exception) {
job_id = stream.readString(exception);
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(constructor);
stream.writeString(job_id);
}
}
public static class TL_smsJob extends TLObject {
public static final int constructor = 0xe6a1eeb8;
public String job_id;
public String phone_number;
public String text;
@Override
public void readParams(InputSerializedData stream, boolean exception) {
job_id = stream.readString(exception);
phone_number = stream.readString(exception);
text = stream.readString(exception);
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(constructor);
stream.writeString(job_id);
stream.writeString(phone_number);
stream.writeString(text);
}
}
public static class TL_smsjobs_isEligibleToJoin extends TLObject {
public static final int constructor = 0xedc39d0;
@Override
public TLObject deserializeResponse(InputSerializedData stream, int constructor, boolean exception) {
if (constructor == TL_smsjobs_eligibleToJoin.constructor) {
TL_smsjobs_eligibleToJoin result = new TL_smsjobs_eligibleToJoin();
result.readParams(stream, exception);
return result;
}
return null;
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_smsjobs_join extends TLObject {
public static final int constructor = 0xa74ece2d;
@Override
public TLObject deserializeResponse(InputSerializedData stream, int constructor, boolean exception) {
return TLRPC.Bool.TLdeserialize(stream, constructor, exception);
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_smsjobs_leave extends TLObject {
public static final int constructor = 0x9898ad73;
@Override
public TLObject deserializeResponse(InputSerializedData stream, int constructor, boolean exception) {
return TLRPC.Bool.TLdeserialize(stream, constructor, exception);
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_smsjobs_getStatus extends TLObject {
public static final int constructor = 0x10a698e8;
@Override
public TLObject deserializeResponse(InputSerializedData stream, int constructor, boolean exception) {
if (constructor == TL_smsjobs_status.constructor) {
TL_smsjobs_status result = new TL_smsjobs_status();
result.readParams(stream, exception);
return result;
}
return null;
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(constructor);
}
}
public static class TL_smsjobs_getSmsJob extends TLObject {
public static final int constructor = 0x778d902f;
public String job_id;
@Override
public TLObject deserializeResponse(InputSerializedData stream, int constructor, boolean exception) {
if (constructor == TL_smsJob.constructor) {
TL_smsJob result = new TL_smsJob();
result.readParams(stream, exception);
return result;
}
return null;
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(constructor);
stream.writeString(job_id);
}
}
public static class TL_smsjobs_finishJob extends TLObject {
public static final int constructor = 0x4f1ebf24;
public int flags;
public String job_id;
public String error;
@Override
public TLObject deserializeResponse(InputSerializedData stream, int constructor, boolean exception) {
return TLRPC.Bool.TLdeserialize(stream, constructor, exception);
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt32(flags);
stream.writeString(job_id);
if ((flags & 1) != 0) {
stream.writeString(error);
}
}
}
public static class TL_smsjobs_updateSettings extends TLObject {
public static final int constructor = 0x93fa0bf;
public int flags;
public boolean allow_international;
@Override
public TLObject deserializeResponse(InputSerializedData stream, int constructor, boolean exception) {
return TLRPC.Bool.TLdeserialize(stream, constructor, exception);
}
@Override
public void serializeToStream(OutputSerializedData stream) {
stream.writeInt32(constructor);
flags = allow_international ? flags | 1 : flags &~ 1;
stream.writeInt32(flags);
}
}
}

View file

@ -0,0 +1,358 @@
package org.telegram.ui.Components;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.core.widget.NestedScrollView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.DocumentObject;
import org.telegram.messenger.FileLoader;
import org.telegram.messenger.ImageLocation;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessageObject;
import org.telegram.messenger.R;
import org.telegram.messenger.SvgHelper;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.BottomSheet;
import org.telegram.ui.ActionBar.Theme;
public class UpdateAppAlertDialog extends BottomSheet {
private TLRPC.TL_help_appUpdate appUpdate;
private int accountNum;
private RadialProgress radialProgress;
private FrameLayout radialProgressView;
private AnimatorSet progressAnimation;
private Drawable shadowDrawable;
private TextView textView;
private TextView messageTextView;
private NestedScrollView scrollView;
private AnimatorSet shadowAnimation;
private View shadow;
private boolean ignoreLayout;
private LinearLayout linearLayout;
private int scrollOffsetY;
private int[] location = new int[2];
private boolean animationInProgress;
public class BottomSheetCell extends FrameLayout {
private View background;
private TextView[] textView = new TextView[2];
private boolean hasBackground;
public BottomSheetCell(Context context, boolean withoutBackground) {
super(context);
hasBackground = !withoutBackground;
setBackground(null);
background = new View(context);
if (hasBackground) {
background.setBackground(Theme.AdaptiveRipple.filledRectByKey(Theme.key_featuredStickers_addButton, 4));
}
addView(background, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, 0, 16, withoutBackground ? 0 : 16, 16, 16));
for (int a = 0; a < 2; a++) {
textView[a] = new TextView(context);
textView[a].setLines(1);
textView[a].setSingleLine(true);
textView[a].setGravity(Gravity.CENTER_HORIZONTAL);
textView[a].setEllipsize(TextUtils.TruncateAt.END);
textView[a].setGravity(Gravity.CENTER);
if (hasBackground) {
textView[a].setTextColor(Theme.getColor(Theme.key_featuredStickers_buttonText));
textView[a].setTypeface(AndroidUtilities.bold());
} else {
textView[a].setTextColor(Theme.getColor(Theme.key_featuredStickers_addButton));
}
textView[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
textView[a].setPadding(0, 0, 0, hasBackground ? 0 : AndroidUtilities.dp(13));
addView(textView[a], LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER));
if (a == 1) {
textView[a].setAlpha(0.0f);
}
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(hasBackground ? 80 : 50), MeasureSpec.EXACTLY));
}
public void setText(CharSequence text, boolean animated) {
if (!animated) {
textView[0].setText(text);
} else {
textView[1].setText(text);
animationInProgress = true;
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.setDuration(180);
animatorSet.setInterpolator(CubicBezierInterpolator.EASE_OUT);
animatorSet.playTogether(
ObjectAnimator.ofFloat(textView[0], View.ALPHA, 1.0f, 0.0f),
ObjectAnimator.ofFloat(textView[0], View.TRANSLATION_Y, 0, -AndroidUtilities.dp(10)),
ObjectAnimator.ofFloat(textView[1], View.ALPHA, 0.0f, 1.0f),
ObjectAnimator.ofFloat(textView[1], View.TRANSLATION_Y, AndroidUtilities.dp(10), 0)
);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
animationInProgress = false;
TextView temp = textView[0];
textView[0] = textView[1];
textView[1] = temp;
}
});
animatorSet.start();
}
}
}
public UpdateAppAlertDialog(Context context, TLRPC.TL_help_appUpdate update, int account) {
super(context, false);
appUpdate = update;
accountNum = account;
setCanceledOnTouchOutside(false);
setApplyTopPadding(false);
setApplyBottomPadding(false);
shadowDrawable = context.getResources().getDrawable(R.drawable.sheet_shadow_round).mutate();
shadowDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogBackground), PorterDuff.Mode.MULTIPLY));
FrameLayout container = new FrameLayout(context) {
@Override
public void setTranslationY(float translationY) {
super.setTranslationY(translationY);
updateLayout();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN && scrollOffsetY != 0 && ev.getY() < scrollOffsetY) {
dismiss();
return true;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent e) {
return !isDismissed() && super.onTouchEvent(e);
}
@Override
protected void onDraw(Canvas canvas) {
int top = (int) (scrollOffsetY - backgroundPaddingTop - getTranslationY());
shadowDrawable.setBounds(0, top, getMeasuredWidth(), getMeasuredHeight());
shadowDrawable.draw(canvas);
}
};
container.setWillNotDraw(false);
containerView = container;
scrollView = new NestedScrollView(context) {
private boolean ignoreLayout;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = MeasureSpec.getSize(heightMeasureSpec);
measureChildWithMargins(linearLayout, widthMeasureSpec, 0, heightMeasureSpec, 0);
int contentHeight = linearLayout.getMeasuredHeight();
int padding = (height / 5 * 2);
int visiblePart = height - padding;
if (contentHeight - visiblePart < AndroidUtilities.dp(90) || contentHeight < height / 2 + AndroidUtilities.dp(90)) {
padding = height - contentHeight;
}
if (padding < 0) {
padding = 0;
}
if (getPaddingTop() != padding) {
ignoreLayout = true;
setPadding(0, padding, 0, 0);
ignoreLayout = false;
}
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
updateLayout();
}
@Override
public void requestLayout() {
if (ignoreLayout) {
return;
}
super.requestLayout();
}
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
updateLayout();
}
};
scrollView.setFillViewport(true);
scrollView.setWillNotDraw(false);
scrollView.setClipToPadding(false);
scrollView.setVerticalScrollBarEnabled(false);
container.addView(scrollView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 0, 0, 130));
linearLayout = new LinearLayout(context);
linearLayout.setOrientation(LinearLayout.VERTICAL);
scrollView.addView(linearLayout, LayoutHelper.createScroll(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP));
if (appUpdate.sticker != null) {
BackupImageView imageView = new BackupImageView(context);
SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(appUpdate.sticker.thumbs, Theme.key_windowBackgroundGray, 1.0f);
TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(appUpdate.sticker.thumbs, 90);
ImageLocation imageLocation = ImageLocation.getForDocument(thumb, appUpdate.sticker);
if (svgThumb != null) {
imageView.setImage(ImageLocation.getForDocument(appUpdate.sticker), "250_250", svgThumb, 0, "update");
} else {
imageView.setImage(ImageLocation.getForDocument(appUpdate.sticker), "250_250", imageLocation, null, 0, "update");
}
linearLayout.addView(imageView, LayoutHelper.createLinear(160, 160, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 17, 8, 17, 0));
}
TextView textView = new TextView(context);
textView.setTypeface(AndroidUtilities.bold());
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack));
textView.setSingleLine(true);
textView.setEllipsize(TextUtils.TruncateAt.END);
textView.setText(LocaleController.getString(R.string.AppUpdate));
linearLayout.addView(textView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 23, 16, 23, 0));
TextView messageTextView = new TextView(getContext());
messageTextView.setTextColor(Theme.getColor(Theme.key_dialogTextGray3));
messageTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
messageTextView.setMovementMethod(new AndroidUtilities.LinkMovementMethodMy());
messageTextView.setLinkTextColor(Theme.getColor(Theme.key_dialogTextLink));
messageTextView.setText(LocaleController.formatString("AppUpdateVersionAndSize", R.string.AppUpdateVersionAndSize, appUpdate.version, AndroidUtilities.formatFileSize(appUpdate.document.size)));
messageTextView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.TOP);
linearLayout.addView(messageTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP | Gravity.CENTER_HORIZONTAL, 23, 0, 23, 5));
TextView changelogTextView = new TextView(getContext());
changelogTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack));
changelogTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
changelogTextView.setMovementMethod(new AndroidUtilities.LinkMovementMethodMy());
changelogTextView.setLinkTextColor(Theme.getColor(Theme.key_dialogTextLink));
if (TextUtils.isEmpty(appUpdate.text)) {
changelogTextView.setText(AndroidUtilities.replaceTags(LocaleController.getString(R.string.AppUpdateChangelogEmpty)));
} else {
SpannableStringBuilder builder = new SpannableStringBuilder(appUpdate.text);
MessageObject.addEntitiesToText(builder, update.entities, false, false, false, false);
changelogTextView.setText(builder);
}
changelogTextView.setGravity(Gravity.LEFT | Gravity.TOP);
linearLayout.addView(changelogTextView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP, 23, 15, 23, 0));
FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, AndroidUtilities.getShadowHeight(), Gravity.BOTTOM | Gravity.LEFT);
frameLayoutParams.bottomMargin = AndroidUtilities.dp(130);
shadow = new View(context);
shadow.setBackgroundColor(Theme.getColor(Theme.key_dialogShadowLine));
shadow.setAlpha(0.0f);
shadow.setTag(1);
container.addView(shadow, frameLayoutParams);
BottomSheetCell doneButton = new BottomSheetCell(context, false);
doneButton.setText(LocaleController.formatString("AppUpdateDownloadNow", R.string.AppUpdateDownloadNow), false);
doneButton.background.setOnClickListener(v -> {
FileLoader.getInstance(accountNum).loadFile(appUpdate.document, "update", FileLoader.PRIORITY_NORMAL, 1);
dismiss();
});
container.addView(doneButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 50, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 50));
BottomSheetCell scheduleButton = new BottomSheetCell(context, true);
scheduleButton.setText(LocaleController.getString(R.string.AppUpdateRemindMeLater), false);
scheduleButton.background.setOnClickListener(v -> dismiss());
container.addView(scheduleButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 50, Gravity.LEFT | Gravity.BOTTOM, 0, 0, 0, 0));
}
private void runShadowAnimation(final int num, final boolean show) {
if (show && shadow.getTag() != null || !show && shadow.getTag() == null) {
shadow.setTag(show ? null : 1);
if (show) {
shadow.setVisibility(View.VISIBLE);
}
if (shadowAnimation != null) {
shadowAnimation.cancel();
}
shadowAnimation = new AnimatorSet();
shadowAnimation.playTogether(ObjectAnimator.ofFloat(shadow, View.ALPHA, show ? 1.0f : 0.0f));
shadowAnimation.setDuration(150);
shadowAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (shadowAnimation != null && shadowAnimation.equals(animation)) {
if (!show) {
shadow.setVisibility(View.INVISIBLE);
}
shadowAnimation = null;
}
}
@Override
public void onAnimationCancel(Animator animation) {
if (shadowAnimation != null && shadowAnimation.equals(animation)) {
shadowAnimation = null;
}
}
});
shadowAnimation.start();
}
}
private void updateLayout() {
View child = linearLayout.getChildAt(0);
child.getLocationInWindow(location);
int top = location[1] - AndroidUtilities.dp(24);
int newOffset = Math.max(top, 0);
if (location[1] + linearLayout.getMeasuredHeight() <= container.getMeasuredHeight() - AndroidUtilities.dp(113) + containerView.getTranslationY()) {
runShadowAnimation(0, false);
} else {
runShadowAnimation(0, true);
}
if (scrollOffsetY != newOffset) {
scrollOffsetY = newOffset;
scrollView.invalidate();
}
}
@Override
protected boolean canDismissWithSwipe() {
return false;
}
}

View file

@ -0,0 +1,181 @@
package org.telegram.ui.Components;
import static org.telegram.messenger.AndroidUtilities.dp;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.os.Build;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.Keep;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.FileLoader;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.SharedConfig;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.IUpdateButton;
import java.io.File;
public class UpdateButton extends IUpdateButton {
private AnimatorSet animator;
private RadialProgress2 icon;
private TextView textView;
@Keep
public UpdateButton(Context context) {
super(context);
setWillNotDraw(false);
setVisibility(View.INVISIBLE);
setTranslationY(dp(48));
if (Build.VERSION.SDK_INT >= 21) {
setBackground(Theme.getSelectorDrawable(0x40ffffff, false));
}
setOnClickListener(v -> {
if (!SharedConfig.isAppUpdateAvailable()) return;
Activity activity = AndroidUtilities.findActivity(getContext());
if (activity == null) return;
AndroidUtilities.openForView(SharedConfig.pendingAppUpdate.document, true, activity);
});
icon = new RadialProgress2(this);
icon.setColors(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff);
icon.setCircleRadius(dp(11));
icon.setAsMini();
icon.setIcon(MediaActionDrawable.ICON_UPDATE, true, false);
textView = new TextView(context);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
textView.setTypeface(AndroidUtilities.bold());
textView.setText(LocaleController.getString(org.telegram.messenger.R.string.AppUpdateNow).toUpperCase());
textView.setTextColor(0xffffffff);
textView.setPadding(dp(30), 0, 0, 0);
addView(textView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER, 0, 0, 0, 0));
}
private Paint paint = new Paint();
private Matrix matrix = new Matrix();
private LinearGradient updateGradient;
private int lastGradientWidth;
@Override
public void draw(Canvas canvas) {
if (updateGradient != null) {
paint.setColor(0xffffffff);
paint.setShader(updateGradient);
updateGradient.setLocalMatrix(matrix);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
icon.setBackgroundGradientDrawable(updateGradient);
icon.draw(canvas);
}
super.draw(canvas);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
if (lastGradientWidth != width) {
updateGradient = new LinearGradient(0, 0, width, 0, new int[]{0xff69BF72, 0xff53B3AD}, new float[]{0.0f, 1.0f}, Shader.TileMode.CLAMP);
lastGradientWidth = width;
}
int x = (getMeasuredWidth() - textView.getMeasuredWidth()) / 2;
icon.setProgressRect(x, dp(13), x + dp(22), dp(13 + 22));
}
private Utilities.Callback<Float> onTranslationUpdate;
@Override
public void setTranslationY(float translationY) {
super.setTranslationY(translationY);
if (onTranslationUpdate != null) {
onTranslationUpdate.run(translationY);
}
}
@Keep
public void onTranslationUpdate(Utilities.Callback<Float> onTranslationUpdate) {
this.onTranslationUpdate = onTranslationUpdate;
}
@Keep
public void update(boolean animated) {
final boolean show;
if (SharedConfig.isAppUpdateAvailable()) {
final String fileName = FileLoader.getAttachFileName(SharedConfig.pendingAppUpdate.document);
final File path = FileLoader.getInstance(UserConfig.selectedAccount).getPathToAttach(SharedConfig.pendingAppUpdate.document, true);
show = path.exists();
} else {
show = false;
}
if (show) {
if (getTag() != null) {
return;
}
if (animator != null) {
animator.cancel();
}
setVisibility(View.VISIBLE);
setTag(1);
if (animated) {
animator = new AnimatorSet();
animator.setDuration(180);
animator.setInterpolator(CubicBezierInterpolator.EASE_OUT);
animator.playTogether(ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, 0));
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
animator = null;
}
});
animator.start();
} else {
setTranslationY(0);
}
} else {
if (getTag() == null) {
return;
}
setTag(null);
if (animated) {
animator = new AnimatorSet();
animator.setDuration(180);
animator.setInterpolator(CubicBezierInterpolator.EASE_OUT);
animator.playTogether(ObjectAnimator.ofFloat(this, View.TRANSLATION_Y, dp(48)));
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (getTag() == null) {
setVisibility(View.INVISIBLE);
}
animator = null;
}
});
animator.start();
} else {
setTranslationY(dp(48));
setVisibility(View.INVISIBLE);
}
}
}
}

View file

@ -0,0 +1,275 @@
package org.telegram.ui.Components;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.os.Build;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.FileLoader;
import org.telegram.messenger.ImageLoader;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
import org.telegram.ui.ActionBar.SimpleTextView;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.IUpdateLayout;
import java.io.File;
import java.util.ArrayList;
public class UpdateLayout extends IUpdateLayout {
private FrameLayout updateLayout;
private RadialProgress2 updateLayoutIcon;
private SimpleTextView[] updateTextViews;
private TextView updateSizeTextView;
private AnimatorSet updateTextAnimator;
private Activity activity;
private ViewGroup sideMenu;
private ViewGroup sideMenuContainer;
public UpdateLayout(Activity activity, ViewGroup sideMenu, ViewGroup sideMenuContainer) {
super(activity, sideMenu, sideMenuContainer);
this.activity = activity;
this.sideMenu = sideMenu;
this.sideMenuContainer = sideMenuContainer;
}
public void updateFileProgress(Object[] args) {
if (updateTextViews == null || args == null) return;
if (updateTextViews[0] != null && SharedConfig.isAppUpdateAvailable()) {
String location = (String) args[0];
String fileName = FileLoader.getAttachFileName(SharedConfig.pendingAppUpdate.document);
if (fileName != null && fileName.equals(location)) {
Long loadedSize = (Long) args[1];
Long totalSize = (Long) args[2];
float loadProgress = loadedSize / (float) totalSize;
updateLayoutIcon.setProgress(loadProgress, true);
updateTextViews[0].setText(LocaleController.formatString("AppUpdateDownloading", R.string.AppUpdateDownloading, (int) (loadProgress * 100)));
}
}
}
public void createUpdateUI(int currentAccount) {
if (sideMenuContainer == null || updateLayout != null) {
return;
}
updateLayout = new FrameLayout(activity) {
private Paint paint = new Paint();
private Matrix matrix = new Matrix();
private LinearGradient updateGradient;
private int lastGradientWidth;
@Override
public void draw(Canvas canvas) {
if (updateGradient != null) {
paint.setColor(0xffffffff);
paint.setShader(updateGradient);
updateGradient.setLocalMatrix(matrix);
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);
updateLayoutIcon.setBackgroundGradientDrawable(updateGradient);
updateLayoutIcon.draw(canvas);
}
super.draw(canvas);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
if (lastGradientWidth != width) {
updateGradient = new LinearGradient(0, 0, width, 0, new int[]{0xff69BF72, 0xff53B3AD}, new float[]{0.0f, 1.0f}, Shader.TileMode.CLAMP);
lastGradientWidth = width;
}
}
};
updateLayout.setWillNotDraw(false);
updateLayout.setVisibility(View.INVISIBLE);
updateLayout.setTranslationY(AndroidUtilities.dp(44));
if (Build.VERSION.SDK_INT >= 21) {
updateLayout.setBackground(Theme.getSelectorDrawable(0x40ffffff, false));
}
sideMenuContainer.addView(updateLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 44, Gravity.LEFT | Gravity.BOTTOM));
updateLayout.setOnClickListener(v -> {
if (!SharedConfig.isAppUpdateAvailable()) {
return;
}
if (updateLayoutIcon.getIcon() == MediaActionDrawable.ICON_DOWNLOAD) {
FileLoader.getInstance(currentAccount).loadFile(SharedConfig.pendingAppUpdate.document, "update", FileLoader.PRIORITY_NORMAL, 1);
updateAppUpdateViews(currentAccount, true);
} else if (updateLayoutIcon.getIcon() == MediaActionDrawable.ICON_CANCEL) {
FileLoader.getInstance(currentAccount).cancelLoadFile(SharedConfig.pendingAppUpdate.document);
updateAppUpdateViews(currentAccount, true);
} else {
AndroidUtilities.openForView(SharedConfig.pendingAppUpdate.document, true, activity);
}
});
updateLayoutIcon = new RadialProgress2(updateLayout);
updateLayoutIcon.setColors(0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff);
updateLayoutIcon.setProgressRect(AndroidUtilities.dp(22), AndroidUtilities.dp(11), AndroidUtilities.dp(22 + 22), AndroidUtilities.dp(11 + 22));
updateLayoutIcon.setCircleRadius(AndroidUtilities.dp(11));
updateLayoutIcon.setAsMini();
updateTextViews = new SimpleTextView[2];
for (int i = 0; i < 2; ++i) {
updateTextViews[i] = new SimpleTextView(activity);
updateTextViews[i].setTextSize(15);
updateTextViews[i].setTypeface(AndroidUtilities.bold());
updateTextViews[i].setTextColor(0xffffffff);
updateTextViews[i].setGravity(Gravity.LEFT);
updateLayout.addView(updateTextViews[i], LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL, 74, 0, 0, 0));
}
updateTextViews[0].setText(LocaleController.getString(R.string.AppUpdate));
updateTextViews[1].setAlpha(0f);
updateTextViews[1].setVisibility(View.GONE);
updateSizeTextView = new TextView(activity);
updateSizeTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
updateSizeTextView.setTypeface(AndroidUtilities.bold());
updateSizeTextView.setGravity(Gravity.RIGHT);
updateSizeTextView.setTextColor(0xffffffff);
updateLayout.addView(updateSizeTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_VERTICAL | Gravity.RIGHT, 0, 0, 17, 0));
}
public void updateAppUpdateViews(int currentAccount, boolean animated) {
if (sideMenuContainer == null) {
return;
}
if (SharedConfig.isAppUpdateAvailable()) {
createUpdateUI(currentAccount);
updateSizeTextView.setText(AndroidUtilities.formatFileSize(SharedConfig.pendingAppUpdate.document.size));
String fileName = FileLoader.getAttachFileName(SharedConfig.pendingAppUpdate.document);
File path = FileLoader.getInstance(currentAccount).getPathToAttach(SharedConfig.pendingAppUpdate.document, true);
boolean showSize;
if (path.exists()) {
updateLayoutIcon.setIcon(MediaActionDrawable.ICON_UPDATE, true, animated);
setUpdateText(LocaleController.getString(R.string.AppUpdateNow), animated);
showSize = false;
} else {
if (FileLoader.getInstance(currentAccount).isLoadingFile(fileName)) {
updateLayoutIcon.setIcon(MediaActionDrawable.ICON_CANCEL, true, animated);
updateLayoutIcon.setProgress(0, false);
Float p = ImageLoader.getInstance().getFileProgress(fileName);
setUpdateText(LocaleController.formatString("AppUpdateDownloading", R.string.AppUpdateDownloading, (int) ((p != null ? p : 0.0f) * 100)), animated);
showSize = false;
} else {
updateLayoutIcon.setIcon(MediaActionDrawable.ICON_DOWNLOAD, true, animated);
setUpdateText(LocaleController.getString(R.string.AppUpdate), animated);
showSize = true;
}
}
if (showSize) {
if (updateSizeTextView.getTag() != null) {
if (animated) {
updateSizeTextView.setTag(null);
updateSizeTextView.animate().alpha(1.0f).scaleX(1.0f).scaleY(1.0f).setDuration(180).start();
} else {
updateSizeTextView.setAlpha(1.0f);
updateSizeTextView.setScaleX(1.0f);
updateSizeTextView.setScaleY(1.0f);
}
}
} else {
if (updateSizeTextView.getTag() == null) {
if (animated) {
updateSizeTextView.setTag(1);
updateSizeTextView.animate().alpha(0.0f).scaleX(0.0f).scaleY(0.0f).setDuration(180).start();
} else {
updateSizeTextView.setAlpha(0.0f);
updateSizeTextView.setScaleX(0.0f);
updateSizeTextView.setScaleY(0.0f);
}
}
}
if (updateLayout.getTag() != null) {
return;
}
updateLayout.setVisibility(View.VISIBLE);
updateLayout.setTag(1);
if (animated) {
updateLayout.animate().translationY(0).setInterpolator(CubicBezierInterpolator.EASE_OUT).setListener(null).setDuration(180).start();
} else {
updateLayout.setTranslationY(0);
}
} else {
if (updateLayout == null || updateLayout.getTag() == null) {
return;
}
updateLayout.setTag(null);
if (animated) {
updateLayout.animate().translationY(AndroidUtilities.dp(44)).setInterpolator(CubicBezierInterpolator.EASE_OUT).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (updateLayout.getTag() == null) {
updateLayout.setVisibility(View.INVISIBLE);
}
}
}).setDuration(180).start();
} else {
updateLayout.setTranslationY(AndroidUtilities.dp(44));
updateLayout.setVisibility(View.INVISIBLE);
}
}
}
private void setUpdateText(String text, boolean animate) {
if (TextUtils.equals(updateTextViews[0].getText(), text)) {
return;
}
if (updateTextAnimator != null) {
updateTextAnimator.cancel();
updateTextAnimator = null;
}
if (animate) {
updateTextViews[1].setText(updateTextViews[0].getText());
updateTextViews[0].setText(text);
updateTextViews[0].setAlpha(0);
updateTextViews[1].setAlpha(1);
updateTextViews[0].setVisibility(View.VISIBLE);
updateTextViews[1].setVisibility(View.VISIBLE);
ArrayList<Animator> arrayList = new ArrayList<>();
arrayList.add(ObjectAnimator.ofFloat(updateTextViews[1], View.ALPHA, 0));
arrayList.add(ObjectAnimator.ofFloat(updateTextViews[0], View.ALPHA, 1));
updateTextAnimator = new AnimatorSet();
updateTextAnimator.playTogether(arrayList);
updateTextAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (updateTextAnimator == animation) {
updateTextViews[1].setVisibility(View.GONE);
updateTextAnimator = null;
}
}
});
updateTextAnimator.setDuration(320);
updateTextAnimator.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT);
updateTextAnimator.start();
} else {
updateTextViews[0].setText(text);
updateTextViews[0].setAlpha(1);
updateTextViews[0].setVisibility(View.VISIBLE);
updateTextViews[1].setVisibility(View.GONE);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,396 @@
package org.telegram.ui;
import static org.telegram.messenger.AndroidUtilities.dp;
import static org.telegram.messenger.LocaleController.formatPluralString;
import static org.telegram.messenger.LocaleController.formatString;
import static org.telegram.messenger.LocaleController.getString;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.LongSparseArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.BotWebViewVibrationEffect;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.NotificationCenter;
import org.telegram.messenger.SMSJobController;
import org.telegram.messenger.UserConfig;
import org.telegram.messenger.Utilities;
import org.telegram.messenger.browser.Browser;
import org.telegram.messenger.web.R;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC;
import org.telegram.tgnet.TL_smsjobs;
import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.BottomSheet;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.BulletinFactory;
import org.telegram.ui.Components.CheckBoxSquare;
import org.telegram.ui.Components.FireworksOverlay;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.LinkSpanDrawable;
import org.telegram.ui.Components.RLottieImageView;
import org.telegram.ui.Stories.recorder.ButtonWithCounterView;
import java.util.Locale;
public class SMSSubscribeSheet {
public static BottomSheet show(Context context, TL_smsjobs.TL_smsjobs_eligibleToJoin isEligible, Runnable onHide, Theme.ResourcesProvider resourcesProvider) {
BottomSheet sheet = new BottomSheet(context, false, resourcesProvider);
sheet.fixNavigationBar(Theme.getColor(Theme.key_dialogBackground, resourcesProvider));
LinearLayout layout = new LinearLayout(context);
layout.setOrientation(LinearLayout.VERTICAL);
ImageView imageView = new ImageView(context);
imageView.setScaleType(ImageView.ScaleType.CENTER);
imageView.setImageResource(R.drawable.large_sms_code);
imageView.setColorFilter(new PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_IN));
imageView.setBackground(Theme.createCircleDrawable(dp(80), Theme.getColor(Theme.key_featuredStickers_addButton, resourcesProvider)));
layout.addView(imageView, LayoutHelper.createLinear(80, 80, Gravity.CENTER_HORIZONTAL, 0, 24, 0, 12));
TextView textView = new TextView(context);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
textView.setGravity(Gravity.CENTER);
textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider));
textView.setTypeface(AndroidUtilities.bold());
textView.setText(getString(R.string.SmsSubscribeTitle));
layout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 30, 0, 30, 6));
textView = new TextView(context);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
textView.setGravity(Gravity.CENTER);
textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText4, resourcesProvider));
textView.setText(AndroidUtilities.replaceTags(getString(R.string.SmsSubscribeMessage)));
layout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 30, 0, 30, 14));
final Runnable openPremium = () -> {
BaseFragment fragment = LaunchActivity.getLastFragment();
if (fragment != null) {
BaseFragment.BottomSheetParams params = new BaseFragment.BottomSheetParams();
params.transitionFromLeft = true;
params.allowNestedScroll = false;
fragment.showAsSheet(new PremiumPreviewFragment("sms"), params);
}
};
layout.addView(new FeatureCell(context, R.drawable.menu_feature_sms, getString(R.string.SmsSubscribeFeature1Title), formatPluralString("SmsSubscribeFeature1Message", isEligible == null ? 100 : isEligible.monthly_sent_sms), resourcesProvider), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 30, 16, 30, 0));
layout.addView(new FeatureCell(context, R.drawable.menu_feature_premium, getString(R.string.SmsSubscribeFeature2Title), AndroidUtilities.replaceSingleTag(getString(R.string.SmsSubscribeFeature2Message), openPremium), resourcesProvider), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 30, 16, 30, 0));
layout.addView(new FeatureCell(context, R.drawable.menu_feature_gift, getString(R.string.SmsSubscribeFeature3Title), getString(R.string.SmsSubscribeFeature3Message), resourcesProvider), LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 30, 16, 30, 0));
final Runnable openTOS = () -> {
if (isEligible == null) return;
Browser.openUrl(context, isEligible.terms_of_use);
};
FrameLayout acceptLayout = new FrameLayout(context);
final CheckBoxSquare checkBox = new CheckBoxSquare(context, false);
checkBox.setDuplicateParentStateEnabled(false);
checkBox.setFocusable(false);
checkBox.setFocusableInTouchMode(false);
checkBox.setClickable(false);
acceptLayout.addView(checkBox, LayoutHelper.createFrame(18, 18, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL, 21, 0, 21, 0));
LinkSpanDrawable.LinksTextView linkTextView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider);
linkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText, resourcesProvider));
linkTextView.setLinkTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteLinkText, resourcesProvider));
linkTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
linkTextView.setMaxLines(2);
linkTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.CENTER_VERTICAL);
linkTextView.setEllipsize(TextUtils.TruncateAt.END);
linkTextView.setText(AndroidUtilities.replaceSingleTag(getString(R.string.SmsSubscribeAccept), openTOS));
acceptLayout.addView(linkTextView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, LocaleController.isRTL ? 16 : 58, 21, LocaleController.isRTL ? 58 : 16, 21));
layout.addView(acceptLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 9, 0, 9, 0));
ButtonWithCounterView button = new ButtonWithCounterView(context, resourcesProvider);
button.setText(getString(R.string.SmsSubscribeActivate), false);
button.setEnabled(false);
layout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 14, 0, 14, 0));
acceptLayout.setOnClickListener(v -> {
checkBox.setChecked(!checkBox.isChecked(), true);
button.setEnabled(checkBox.isChecked());
});
final float[] shiftDp = new float[] { 4 };
button.setOnClickListener(v -> {
final int currentAccount = UserConfig.selectedAccount;
if (!button.isEnabled()) {
AndroidUtilities.shakeViewSpring(acceptLayout, shiftDp[0] = -shiftDp[0]);
BotWebViewVibrationEffect.APP_ERROR.vibrate();
return;
}
SMSJobController.getInstance(currentAccount).setState(SMSJobController.STATE_ASKING_PERMISSION);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.mainUserInfoChanged);
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.smsJobStatusUpdate);
requestSMSPermissions(context, () -> {
SMSJobController.getInstance(currentAccount).checkSelectedSIMCard();
if (SMSJobController.getInstance(currentAccount).getSelectedSIM() == null) {
sheet.dismiss();
SMSJobController.getInstance(currentAccount).setState(SMSJobController.STATE_NO_SIM);
new AlertDialog.Builder(context, resourcesProvider)
.setTitle(LocaleController.getString(R.string.SmsNoSimTitle))
.setMessage(AndroidUtilities.replaceTags(LocaleController.getString(R.string.SmsNoSimMessage)))
.setPositiveButton(LocaleController.getString(R.string.OK), null)
.show();
return;
}
button.setLoading(true);
ConnectionsManager.getInstance(currentAccount).sendRequest(new TL_smsjobs.TL_smsjobs_join(), (res, err) -> AndroidUtilities.runOnUIThread(() -> {
if (err != null) {
button.setLoading(false);
BulletinFactory.showError(err);
} else if (res instanceof TLRPC.TL_boolFalse) {
button.setLoading(false);
BulletinFactory.global().createErrorBulletin(LocaleController.getString(R.string.UnknownError)).show();
} else {
SMSJobController.getInstance(currentAccount).setState(SMSJobController.STATE_JOINED);
sheet.dismiss();
SMSJobController.getInstance(currentAccount).loadStatus(true);
showSubscribed(context, resourcesProvider);
}
}));
}, false);
});
linkTextView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider);
linkTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText4, resourcesProvider));
linkTextView.setLinkTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteLinkText, resourcesProvider));
linkTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);
linkTextView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
linkTextView.setText(AndroidUtilities.replaceSingleTag(getString(R.string.SmsSubscribeActivateText), openTOS));
linkTextView.setGravity(Gravity.CENTER);
layout.addView(linkTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 30, 17, 30, 14));
sheet.setCustomView(layout);
sheet.show();
if (onHide != null) {
sheet.setOnHideListener(v -> onHide.run());
}
return sheet;
}
private static class FeatureCell extends LinearLayout {
public FeatureCell(Context context, int icon, CharSequence title, CharSequence message, Theme.ResourcesProvider resourcesProvider) {
super(context);
setOrientation(HORIZONTAL);
ImageView iconView = new ImageView(context);
iconView.setImageResource(icon);
iconView.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider), PorterDuff.Mode.SRC_IN));
addView(iconView, LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.TOP, 0, 4, 17, 0));
LinearLayout textLayout = new LinearLayout(context);
textLayout.setOrientation(VERTICAL);
addView(textLayout, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.FILL));
TextView titleView = new TextView(context);
titleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
titleView.setTypeface(AndroidUtilities.bold());
titleView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider));
titleView.setText(title);
textLayout.addView(titleView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, 0, 1));
LinkSpanDrawable.LinksTextView messageView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider);
messageView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
messageView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText4, resourcesProvider));
messageView.setLinkTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteLinkText, resourcesProvider));
messageView.setText(message);
textLayout.addView(messageView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(MeasureSpec.makeMeasureSpec(Math.min(dp(330), MeasureSpec.getSize(widthMeasureSpec)), MeasureSpec.EXACTLY), heightMeasureSpec);
}
}
public static BottomSheet showSubscribed(Context context, Theme.ResourcesProvider resourcesProvider) {
final int currentAccount = UserConfig.selectedAccount;
BottomSheet sheet = new BottomSheet(context, false, resourcesProvider);
sheet.fixNavigationBar(Theme.getColor(Theme.key_dialogBackground, resourcesProvider));
LinearLayout layout = new LinearLayout(context);
layout.setOrientation(LinearLayout.VERTICAL);
RLottieImageView imageView = new RLottieImageView(context);
imageView.setAnimation(R.raw.giveaway_results, 120, 120);
imageView.getAnimatedDrawable().multiplySpeed(1.8f);
imageView.playAnimation();
layout.addView(imageView, LayoutHelper.createLinear(120, 120, Gravity.CENTER_HORIZONTAL, 0, 24, 0, 12));
TextView textView = new TextView(context);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
textView.setGravity(Gravity.CENTER);
textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider));
textView.setTypeface(AndroidUtilities.bold());
textView.setText(getString(R.string.SmsPremiumActivated));
layout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 30, 0, 30, 14));
textView = new TextView(context);
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
textView.setGravity(Gravity.CENTER);
textView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
textView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider));
final TextView finalTextView2 = textView;
TLRPC.User user = UserConfig.getInstance(currentAccount).getCurrentUser();
String countryCode = null;
if (user != null) {
countryCode = SMSJobController.getCountryFromPhoneNumber(context, user.phone);
}
String country = null;
if (!TextUtils.isEmpty(countryCode)) {
try {
country = new Locale("", countryCode).getDisplayCountry();
} catch (Exception e) {
}
}
if (country != null) {
finalTextView2.setText(AndroidUtilities.replaceTags(formatString(R.string.SmsPremiumActivatedText, country)));
} else {
finalTextView2.setText(AndroidUtilities.replaceTags(getString(R.string.SmsPremiumActivatedTextUnknown)));
}
layout.addView(textView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 30, 0, 30, 16));
LinkSpanDrawable.LinksTextView linkTextView = new LinkSpanDrawable.LinksTextView(context, resourcesProvider);
linkTextView.setTextColor(Theme.getColor(Theme.key_dialogTextBlack, resourcesProvider));
linkTextView.setLinkTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteLinkText, resourcesProvider));
linkTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
linkTextView.setGravity(Gravity.CENTER);
linkTextView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
linkTextView.setText(AndroidUtilities.replaceSingleTag(getString(R.string.SmsPremiumActivatedText2), () -> {
sheet.dismiss();
BaseFragment lastFragment = LaunchActivity.getLastFragment();
if (lastFragment != null) {
lastFragment.presentFragment(new SMSStatsActivity());
}
}));
layout.addView(linkTextView, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, Gravity.CENTER_HORIZONTAL, 30, 0, 30, 24));
ButtonWithCounterView button = new ButtonWithCounterView(context, resourcesProvider);
button.setText(getString(R.string.OK), false);
layout.addView(button, LayoutHelper.createLinear(LayoutHelper.MATCH_PARENT, 48, 14, 0, 14, 0));
button.setOnClickListener(v -> {
sheet.dismiss();
});
sheet.setCustomView(layout);
sheet.show();
final FireworksOverlay fireworksOverlay = new FireworksOverlay(context);
sheet.getContainer().addView(fireworksOverlay, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT));
fireworksOverlay.postDelayed(() -> {
fireworksOverlay.start(true);
}, 720);
sheet.setOnHideListener(v -> {
fireworksOverlay.animate().alpha(0).start();
});
return sheet;
}
private static LongSparseArray<Utilities.Callback<Boolean>> permissionsCallbacks;
public static void requestSMSPermissions(Context context, Runnable whenDone, boolean forceOpenSettings) {
if (permissionsCallbacks == null) {
permissionsCallbacks = new LongSparseArray<>();
}
Activity _activity = AndroidUtilities.findActivity(context);
if (_activity == null) _activity = LaunchActivity.instance;
if (_activity == null || whenDone == null) return;
final Activity activity = _activity;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final boolean hasSMSPermission = activity.checkSelfPermission(Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_GRANTED;
final boolean hasPhonePermission = activity.checkSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED && activity.checkSelfPermission(Manifest.permission.READ_PHONE_NUMBERS) == PackageManager.PERMISSION_GRANTED;
if (hasSMSPermission && hasPhonePermission) {
whenDone.run();
return;
}
if (activity.shouldShowRequestPermissionRationale(Manifest.permission.SEND_SMS) || activity.shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_STATE) || activity.shouldShowRequestPermissionRationale(Manifest.permission.READ_PHONE_NUMBERS) || forceOpenSettings) {
int messageResId;
if (!hasSMSPermission && !hasPhonePermission) {
messageResId = R.string.SmsPermissionTextSMSPhoneSettings;
} else if (!hasSMSPermission) {
messageResId = R.string.SmsPermissionTextSMSSettings;
} else {
messageResId = R.string.SmsPermissionTextPhoneSettings;
}
new AlertDialog.Builder(activity)
.setMessage(AndroidUtilities.replaceTags(getString(messageResId)))
.setPositiveButton(LocaleController.getString(R.string.Settings), (dialog, which) -> {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
intent.setData(uri);
activity.startActivity(intent);
})
.setNegativeButton(LocaleController.getString(R.string.Cancel), null)
.setOnDismissListener(dialog -> {
})
.setTopImage(R.drawable.permissions_sms, Theme.getColor(Theme.key_dialogTopBackground))
.show();
} else {
new AlertDialog.Builder(activity)
.setMessage(AndroidUtilities.replaceTags(getString(R.string.SmsPermissionText)))
.setPositiveButton(LocaleController.getString(R.string.Next), (dialog, which) -> {
final int requestCode = (int) (1000 + Math.abs(Math.random() * (Integer.MAX_VALUE - 1000)));
permissionsCallbacks.put(requestCode, success -> {
if (!success) {
requestSMSPermissions(context, whenDone, true);
} else {
whenDone.run();
}
});
activity.requestPermissions(new String[] { Manifest.permission.SEND_SMS, Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_PHONE_NUMBERS }, requestCode);
})
.setNegativeButton(LocaleController.getString(R.string.Cancel), null)
.setOnDismissListener(dialog -> {
})
.setTopImage(R.drawable.permissions_sms, Theme.getColor(Theme.key_dialogTopBackground))
.show();
}
return;
}
whenDone.run();
}
public static boolean checkSMSPermissions(int requestCode, String[] permissions, int[] grantResults) {
if (permissionsCallbacks != null) {
Utilities.Callback<Boolean> callback = permissionsCallbacks.get(requestCode);
if (callback != null) {
permissionsCallbacks.remove(requestCode);
boolean granted = true;
for (int i = 0; i < grantResults.length; ++i) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
granted = false;
}
}
final boolean finalGranted = granted;
AndroidUtilities.runOnUIThread(() -> callback.run(finalGranted));
return true;
}
}
return false;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 793 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="72dp"
android:height="72dp"
android:viewportWidth="72"
android:viewportHeight="72">
<path
android:fillType="evenOdd"
android:strokeColor="#FFFFFF"
android:strokeWidth="5"
android:strokeLineCap="round"
android:pathData="M69.5605853,31.2093883 C69.6797173,31.827202 69.7971213,32.6765332 69.7971213,33.6861045 C69.5574623,49.6787327 54.8759472,62.5776013 36.8,62.5776013 C33.3445088,62.5776013 30.0130641,62.1062236 26.8828434,61.2319662 C25.0955474,62.3740206 23.5449316,63.2847733 22.2299811,63.9651378 C19.1670803,65.5499041 15.1987631,66.5454322 10.3250295,66.9517221 L9.71111336,66.9996905 C9.47537497,67.0149477 9.27190287,66.8362123 9.25664571,66.6004739 C9.25205149,66.5294887 9.26523328,66.4584784 9.29499778,66.3938712 C11.6302448,61.3873847 13.101106,57.3262026 13.7088435,54.2109065 C7.59377257,48.8958928 3.8,41.4855635 3.8,33.2888007 C3.8,17.6895362 17.5402549,4.93857082 34.8650379,4.04908133 C37.1376217,4.04908133 38.5457247,4.18503581 39.6778586,4.41806507" />
<path
android:fillColor="#FFFFFF"
android:fillType="evenOdd"
android:strokeWidth="1"
android:pathData="M58.3312776,20.322953 L53.5151599,23.2215677 C52.6606774,23.7358441 51.5510789,23.4600525 51.0368025,22.60557 C50.7867901,22.1901683 50.7143223,21.6916802 50.8357076,21.2222866 L51.4135939,18.9876156 C51.7483258,17.6932163 52.6412963,16.6146836 53.8505048,16.0443067 L60.1757816,13.0607088 C60.4754843,12.9193407 60.6038399,12.5617822 60.4624718,12.2620795 C60.3476141,12.0185794 60.0846218,11.8813998 59.8192041,11.9265441 L52.7172556,13.1345007 C51.2867167,13.3778182 49.8210353,12.9876584 48.7007879,12.0653295 L47.2323647,10.8563384 C46.5135994,10.2645602 46.4106561,9.20215491 47.0024342,8.48338968 C47.2939971,8.12926156 47.7172792,7.90923806 48.1746402,7.87407297 L54.0535125,7.42206443 C54.5376215,7.38484277 54.9603829,7.08031029 55.1490294,6.63291843 L57.3246624,1.47321169 C57.7148074,0.547948295 58.7811566,0.114149097 59.7064199,0.50429406 C60.1433931,0.688547415 60.4910842,1.03623851 60.6753376,1.47321169 L62.8509706,6.63291843 C63.0396171,7.08031029 63.4623785,7.38484277 63.9464875,7.42206443 L69.5516972,7.85303189 C70.5528963,7.93001103 71.3021246,8.8040472 71.2251454,9.8052463 C71.1876263,10.2932239 70.9549632,10.745469 70.5797643,11.059723 L66.3470911,14.6048681 C65.9681273,14.9222755 65.8025752,15.4275232 65.9202148,15.9076502 L67.2308088,21.2566278 C67.4697757,22.231933 66.8728557,23.2162951 65.8975505,23.4552621 C65.43123,23.5695188 64.9386462,23.4946941 64.5272893,23.2471161 L59.6687224,20.322953 C59.2572921,20.0753307 58.7427079,20.0753307 58.3312776,20.322953 Z" />
<path
android:fillType="evenOdd"
android:strokeColor="#FFFFFF"
android:strokeWidth="5.4"
android:strokeLineCap="round"
android:pathData="M 26.5 42.5 L 26.5 28.5" />
<path
android:fillType="evenOdd"
android:strokeColor="#FFFFFF"
android:strokeWidth="5.4"
android:strokeLineCap="round"
android:pathData="M 37.5 42.5 L 37.5 21.5" />
<path
android:fillType="evenOdd"
android:strokeColor="#FFFFFF"
android:strokeWidth="5.4"
android:strokeLineCap="round"
android:pathData="M 48.5 42.5 L 48.5 35.5" />
</vector>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/icon_01_background_sa" />
<foreground android:drawable="@mipmap/icon_01_foreground_sa" />
<monochrome android:drawable="@drawable/icon_plane" />
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@mipmap/icon_2_background_sa" />
<foreground android:drawable="@mipmap/icon_foreground_sa" />
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/icon_3_background_sa" />
<foreground android:drawable="@mipmap/icon_3_foreground_sa" />
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/icon_4_background_sa" />
<foreground android:drawable="@mipmap/icon_foreground_sa" />
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/icon_5_background_sa" />
<foreground android:drawable="@mipmap/icon_5_foreground_sa" />
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/icon_6_background_sa" />
<foreground android:drawable="@mipmap/icon_6_foreground_sa" />
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="SmsJobsMenu">SMS Statistics</string>
<string name="SmsJobsPremiumHintTitle">Premium Subscription **for free**</string>
<string name="SmsJobsPremiumHintMessage">Help Telegram send SMS codes and get a free premium subscription.</string>
<string name="SmsJobsErrorHintTitle">Some SMS were not sent successfully</string>
<string name="SmsJobsErrorHintMessage">Review SMS history to troubleshoot</string>
<string name="SmsSubscribeTitle">Premium for Free</string>
<string name="SmsSubscribeMessage">You are one of **0.01%** users eligible to take part in Telegram\'s **Premium for SMS** initiative!</string>
<string name="SmsSubscribeFeature1Title">Help Send Login SMS</string>
<string name="SmsSubscribeFeature1Message_other">Let Telegram use your phone to send %d login SMS per month.</string>
<string name="SmsSubscribeFeature2Title">Get Telegram Premium</string>
<string name="SmsSubscribeFeature2Message">Receive 1-month **Premium subscription** each month.</string>
<string name="SmsSubscribeFeature3Title">Share Premium with Friends</string>
<string name="SmsSubscribeFeature3Message">Send gift links granting Telegram subscriptions to your friends.</string>
<string name="SmsSubscribeAccept">I accept the **Terms of Service for P2PLP**</string>
<string name="SmsSubscribeActivate">Activate</string>
<string name="SmsSubscribeActivateText">Sending SMS can result in charges. Please read **Terms of Service for Peer-to-Peer Login Program.**</string>
<string name="SmsPremiumActivated">Premium for SMS activated!</string>
<string name="SmsPremiumActivatedText">Telegram will now try to use your device to automatically send SMS codes in your country (**%s**).</string>
<string name="SmsPremiumActivatedTextUnknown">Telegram will now try to use your device to automatically send SMS codes in your country.</string>
<string name="SmsPremiumActivatedText2">Check **SMS Statistics** in the left-side menu to see how many SMS have been sent or change settings for this Program.</string>
<string name="SmsPermissionText">To join the **Premium for SMS** program, please allow Telegram to send out SMS from your device</string>
<string name="SmsPermissionTextSMSSettings">To join the **Premium for SMS** program, please allow Telegram to send out SMS from your device.\n\nTap Settings -> Permissions, and turn **SMS** on.</string>
<string name="SmsPermissionTextPhoneSettings">To join the **Premium for SMS** program, please allow Telegram to read phone SIM card info.\n\nTap Settings -> Permissions, and turn **Phone** permission on.</string>
<string name="SmsPermissionTextSMSPhoneSettings">To join the **Premium for SMS** program, please allow Telegram to send out SMS and read phone SIM card info.\n\nTap Settings -> Permissions, and turn **SMS** and **Phone** permissions on.</string>
<string name="SmsStatusSending">%1$d of %2$d SMS Sent</string>
<string name="SmsStatusSendingSubtitle_other">Only **%d** SMS left to send to received your **Telegram Premium** gift link.</string>
<string name="SmsStatusFirst">Premium for SMS</string>
<string name="SmsStatusFirstSubtitle">Help Telegram send SMS login codes and get **Telegram Premium** gift links.</string>
<string name="SmsStatusNoSim">Insert a SIM card</string>
<string name="SmsStatusNoSimSubtitle">To join this program, please insert a SIM card into the device to start sending SMS.</string>
<string name="SmsStatusNoPermission">Access Required</string>
<string name="SmsStatusNoPermissionSubtitle">To join this program, please allow Telegram to send SMS in your phone\'s Settings.</string>
<string name="SmsStatusDone">All %d SMS Sent!</string>
<string name="SmsStatusDoneSubtitle">Telegram Premium gift-code is sent for you to activate.</string>
<string name="SmsNoSimTitle">No SIM card</string>
<string name="SmsNoSimMessage">Insert a SIM card into the device and come back to **SMS Statistics**.</string>
<string name="SmsTotalSent">Total SMS Sent</string>
<string name="SmsRemaining">SMS Remaining</string>
<string name="SmsSentSince">Sent Since</string>
<string name="SmsStartDate">Start Date</string>
<string name="SmsLastGiftLink">Last Gift Link Receipt</string>
<string name="SmsOverview">Overview</string>
<string name="SmsToS">Terms of Service</string>
<string name="SmsPremiumBenefits">Benefits of Premium</string>
<string name="SmsHistory">SMS History</string>
<string name="SmsActiveSim">Active SIM</string>
<string name="SmsSelectSim">Select SIM card</string>
<string name="SmsAllowInternational">Also send SMS outside my country</string>
<string name="SmsCostsInfo">Please refer to your mobile plan for any SMS costs that you may incur</string>
<string name="SmsDeactivate">Deactivate Premium for SMS</string>
<string name="SmsDeactivateTitle">Deactivate Premium for SMS</string>
<string name="SmsDeactivateMessage">Are you sure you want to leave the program?</string>
<string name="SmsHistoryTitle">Sent SMS History</string>
<string name="SmsHistorySubtitle">Here you can see all the SMS automatically sent from your device.</string>
<string name="SmsHistoryDateCountry">Date and Country</string>
<string name="SmsHistoryStatus">Status</string>
<string name="SmsHistoryStatusSuccess">Sent</string>
<string name="SmsHistoryStatusPending">Pending</string>
<string name="SmsHistoryStatusFailure">Failed</string>
<string name="SmsAirplaneMode">Airplane mode</string>
<string name="SmsDescrRESULT_ERROR_RADIO_OFF">Your device has been in airplane mode.</string>
<string name="SmsDescrRESULT_ERROR_NO_SERVICE">Your device hasn\'t had contact with cell phone network.</string>
<string name="SmsDescrRESULT_RIL_NETWORK_ERR">Network error. Make sure your SIM card is active.</string>
<string name="SmsNotificationTitle">Telegram Premium for SMS</string>
<string name="SmsNotificationSubtitle">%1$d of %2$d SMS Sent</string>
</resources>