update to 10.0.8 (3867)

This commit is contained in:
xaxtix 2023-09-13 21:27:29 +04:00
parent 36067f8b38
commit dcad7b4375
311 changed files with 17775 additions and 6757 deletions

View file

@ -435,7 +435,7 @@ target_include_directories(breakpad PUBLIC
#voip
include(${CMAKE_HOME_DIRECTORY}/voip/CMakeLists.txt)
set(NATIVE_LIB "tmessages.45")
set(NATIVE_LIB "tmessages.46")
#tmessages
add_library(${NATIVE_LIB} SHARED

View file

@ -126,7 +126,7 @@ void sendRequest(JNIEnv *env, jclass c, jint instanceNum, jlong object, jobject
}
}
if (onComplete != nullptr) {
jniEnv[instanceNum]->CallVoidMethod(onComplete, jclass_RequestDelegateInternal_run, ptr, errorCode, errorText, networkType, responseTime);
jniEnv[instanceNum]->CallVoidMethod(onComplete, jclass_RequestDelegateInternal_run, ptr, errorCode, errorText, networkType, responseTime, msgId);
}
if (errorText != nullptr) {
jniEnv[instanceNum]->DeleteLocalRef(errorText);

View file

@ -7,6 +7,7 @@
#include <opusfile.h>
#include <math.h>
#include "c_utils.h"
#include "libavformat/avformat.h"
typedef struct {
int version;
@ -687,3 +688,143 @@ JNIEXPORT jbyteArray Java_org_telegram_messenger_MediaController_getWaveform(JNI
return result;
}
JNIEXPORT void JNICALL Java_org_telegram_ui_Stories_recorder_FfmpegAudioWaveformLoader_init(JNIEnv *env, jobject obj, jstring pathJStr, jint count) {
const char *path = (*env)->GetStringUTFChars(env, pathJStr, 0);
// Initialize FFmpeg components
av_register_all();
AVFormatContext *formatContext = avformat_alloc_context();
if (!formatContext) {
// Handle error
return;
}
int res;
if ((res = avformat_open_input(&formatContext, path, NULL, NULL)) != 0) {
LOGD("avformat_open_input error %s", av_err2str(res));
// Handle error
avformat_free_context(formatContext);
return;
}
if (avformat_find_stream_info(formatContext, NULL) < 0) {
// Handle error
avformat_close_input(&formatContext);
return;
}
AVCodec *codec = NULL;
int audioStreamIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
if (audioStreamIndex < 0) {
LOGD("av_find_best_stream error %s", av_err2str(audioStreamIndex));
// Handle error
avformat_close_input(&formatContext);
return;
}
AVCodecContext *codecContext = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(codecContext, formatContext->streams[audioStreamIndex]->codecpar);
int64_t duration_in_microseconds = formatContext->duration;
double duration_in_seconds = (double)duration_in_microseconds / AV_TIME_BASE;
if (avcodec_open2(codecContext, codec, NULL) < 0) {
// Handle error
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
return;
}
// Obtain the class and method to callback
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID mid = (*env)->GetMethodID(env, cls, "receiveChunk", "([SI)V");
AVFrame *frame = av_frame_alloc();
AVPacket packet;
int sampleRate = codecContext->sample_rate; // Sample rate from FFmpeg's codec context
int skip = 4;
int barWidth = (int) round((double) duration_in_seconds * sampleRate / count / (1 + skip)); // Assuming you have 'duration' and 'count' defined somewhere
short peak = 0;
int currentCount = 0;
int index = 0;
int chunkIndex = 0;
short waveformChunkData[32]; // Allocate the chunk array
while (av_read_frame(formatContext, &packet) >= 0) {
if (packet.stream_index == audioStreamIndex) {
// Decode the audio packet
int response = avcodec_send_packet(codecContext, &packet);
while (response >= 0) {
response = avcodec_receive_frame(codecContext, frame);
if (response == AVERROR(EAGAIN) || response == AVERROR_EOF) {
break;
} else if (response < 0) {
// Handle error
break;
}
int16_t* samples = (int16_t*) frame->data[0];
for (int i = 0; i < frame->nb_samples; i++) {
short value = samples[i]; // Read the 16-bit PCM sample
if (currentCount >= barWidth) {
waveformChunkData[index - chunkIndex] = peak;
index++;
if (index - chunkIndex >= sizeof(waveformChunkData) / sizeof(short) || index >= count) {
jshortArray waveformData = (*env)->NewShortArray(env, sizeof(waveformChunkData) / sizeof(short));
(*env)->SetShortArrayRegion(env, waveformData, 0, sizeof(waveformChunkData) / sizeof(short), waveformChunkData);
(*env)->CallVoidMethod(env, obj, mid, waveformData, sizeof(waveformChunkData) / sizeof(short));
// Reset the chunk data
memset(waveformChunkData, 0, sizeof(waveformChunkData));
chunkIndex = index;
// Delete local reference to avoid memory leak
(*env)->DeleteLocalRef(env, waveformData);
}
peak = 0;
currentCount = 0;
if (index >= count) {
break;
}
}
if (peak < value) {
peak = value;
}
currentCount++;
// Skip logic
i += skip;
if (i >= frame->nb_samples) {
break;
}
}
}
}
av_packet_unref(&packet);
if (index >= count) {
break;
}
// Check for stopping flag
jfieldID fid = (*env)->GetFieldID(env, cls, "running", "Z");
jboolean running = (*env)->GetBooleanField(env, obj, fid);
if (running == JNI_FALSE) {
break;
}
}
av_frame_free(&frame);
avcodec_free_context(&codecContext);
avformat_close_input(&formatContext);
(*env)->ReleaseStringUTFChars(env, pathJStr, path);
}

2
TMessagesProj/jni/ffmpeg/build_ffmpeg/build_ffmpeg.sh Normal file → Executable file
View file

@ -87,10 +87,12 @@ LIBS=" -L${PREFIX}/lib"
--enable-decoder=gif \
--enable-decoder=alac \
--enable-decoder=opus \
--enable-decoder=mp3 \
--enable-demuxer=mov \
--enable-demuxer=gif \
--enable-demuxer=ogg \
--enable-demuxer=matroska \
--enable-demuxer=mp3 \
--enable-hwaccels \
--enable-runtime-cpudetect \
$ADDITIONAL_CONFIGURE_FLAG

View file

@ -18,6 +18,7 @@
#include <openssl/rand.h>
#include <openssl/hmac.h>
#include <algorithm>
#include <utility>
#include <openssl/bn.h>
#include "ByteStream.h"
#include "ConnectionSocket.h"
@ -29,6 +30,7 @@
#include "NativeByteBuffer.h"
#include "BuffersStorage.h"
#include "Connection.h"
#include <random>
#ifndef EPOLLRDHUP
#define EPOLLRDHUP 0x2000
@ -134,7 +136,7 @@ public:
grease[a] = (uint8_t) ((grease[a] & 0xf0) + 0x0A);
}
for (size_t i = 1; i < MAX_GREASE; i += 2) {
if (grease[i] == grease[i - 1]) {
if (grease[i] == grease[i + 1]) {
grease[i] ^= 0x10;
}
}
@ -142,12 +144,13 @@ public:
struct Op {
enum class Type {
String, Random, K, Zero, Domain, Grease, BeginScope, EndScope
String, Random, K, Zero, Domain, Grease, BeginScope, EndScope, Permutation
};
Type type;
size_t length;
int seed;
std::string data;
std::vector<std::vector<Op>> entities;
static Op string(const char str[], size_t len) {
Op res;
@ -201,6 +204,14 @@ public:
res.type = Type::EndScope;
return res;
}
static Op permutation(std::vector<std::vector<Op>> entities) {
Op res;
res.type = Type::Permutation;
res.entities = std::move(entities);
return res;
}
};
static const TlsHello &getDefault() {
@ -213,32 +224,81 @@ public:
Op::random(32),
Op::string("\x00\x20", 2),
Op::grease(0),
Op::string("\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c"
"\x00\x9d\x00\x2f\x00\x35\x01\x00\x01\x93", 34),
Op::string("\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00\x2f\x00\x35\x01\x00\x01\x93", 34),
Op::grease(2),
Op::string("\x00\x00\x00\x00", 4),
Op::begin_scope(),
Op::begin_scope(),
Op::string("\x00", 1),
Op::begin_scope(),
Op::domain(),
Op::end_scope(),
Op::end_scope(),
Op::end_scope(),
Op::string("\x00\x17\x00\x00\xff\x01\x00\x01\x00\x00\x0a\x00\x0a\x00\x08", 15),
Op::grease(4),
Op::string(
"\x00\x1d\x00\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08"
"\x68\x74\x74\x70\x2f\x31\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x12\x00\x10\x04\x03\x08"
"\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29", 75),
Op::grease(4),
Op::string("\x00\x01\x00\x00\x1d\x00\x20", 7),
Op::K(),
Op::string("\x00\x2d\x00\x02\x01\x01\x00\x2b\x00\x0b\x0a", 11),
Op::grease(6),
Op::string("\x03\x04\x03\x03\x03\x02\x03\x01\x00\x1b\x00\x03\x02\x00\x02", 15),
Op::string("\x00\x00", 2),
Op::permutation({
{
Op::string("\x00\x00", 2),
Op::begin_scope(),
Op::begin_scope(),
Op::string("\x00", 1),
Op::begin_scope(),
Op::domain(),
Op::end_scope(),
Op::end_scope(),
Op::end_scope()
},
{
Op::string(
"\x00\x05\x00\x05\x01\x00\x00\x00\x00",
9)
},
{
Op::string("\x00\x0a\x00\x0a\x00\x08", 6),
Op::grease(4),
Op::string("\x00\x1d\x00\x17\x00\x18", 6)
},
{
Op::string("\x00\x0b\x00\x02\x01\x00", 6),
},
{
Op::string(
"\x00\x0d\x00\x12\x00\x10\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01",
22),
},
{
Op::string(
"\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70\x2f\x31\x2e\x31",
18),
},
{
Op::string("\x00\x12\x00\x00", 4)
},
{
Op::string("\x00\x17\x00\x00", 4)
},
{
Op::string("\x00\x1b\x00\x03\x02\x00\x02", 7)
},
{
Op::string("\x00\x23\x00\x00", 4)
},
{
Op::string("\x00\x2b\x00\x07\x06", 5),
Op::grease(6),
Op::string("\x03\x04\x03\x03", 4)
},
{
Op::string("\x00\x2d\x00\x02\x01\x01", 6)
},
{
Op::string("\x00\x33\x00\x2b\x00\x29", 6),
Op::grease(4),
Op::string("\x00\x01\x00\x00\x1d\x00\x20", 7),
Op::K()
},
{
Op::string("\x44\x69\x00\x05\x00\x03\x02\x68\x32",9),
},
{
Op::string("\xff\x01\x00\x01\x00", 5),
}
}),
Op::grease(3),
Op::string("\x00\x01\x00\x00\x15", 5)};
Op::string("\x00\x01\x00\x00\x15", 5)
};
return res;
}();
return result;
@ -319,6 +379,25 @@ private:
data[begin_offset + 1] = static_cast<uint8_t>(size & 0xff);
break;
}
case Type::Permutation: {
std::vector<std::vector<Op>> list = {};
for (const auto &part : op.entities) {
list.push_back(part);
}
size_t size = list.size();
for (int i = 0; i < size - 1; i++) {
int j = i + rand() % (size - i);
if (i != j) {
std::swap(list[i], list[j]);
}
}
for (const auto &part : list) {
for (const auto &op_local: part) {
writeOp(op_local, data, offset);
}
}
break;
}
}
}
};

View file

@ -970,7 +970,7 @@ void ConnectionsManager::onConnectionDataReceived(Connection *connection, Native
}
} else {
if (delegate != nullptr) {
delegate->onUnparsedMessageReceived(0, data, connection->getConnectionType(), instanceNum);
delegate->onUnparsedMessageReceived(messageId, data, connection->getConnectionType(), instanceNum);
}
}
} else {
@ -1128,7 +1128,7 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag
if (innerMessage->unparsedBody != nullptr) {
if (LOGS_ENABLED) DEBUG_D("inner message %d id 0x%" PRIx64 " is unparsed", a, innerMessageId);
if (delegate != nullptr) {
delegate->onUnparsedMessageReceived(0, innerMessage->unparsedBody.get(), connection->getConnectionType(), instanceNum);
delegate->onUnparsedMessageReceived(innerMessageId, innerMessage->unparsedBody.get(), connection->getConnectionType(), instanceNum);
}
} else {
if (LOGS_ENABLED) DEBUG_D("inner message %d id 0x%" PRIx64 " process", a, innerMessageId);
@ -1643,7 +1643,7 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag
NativeByteBuffer *data = BuffersStorage::getInstance().getFreeBuffer(message->getObjectSize());
message->serializeToStream(data);
data->position(0);
delegate->onUnparsedMessageReceived(0, data, connection->getConnectionType(), instanceNum);
delegate->onUnparsedMessageReceived(messageId, data, connection->getConnectionType(), instanceNum);
data->reuse();
}
}

View file

@ -26,6 +26,7 @@
-keep class com.google.android.exoplayer2.extractor.FlacStreamMetadata { *; }
-keep class com.google.android.exoplayer2.metadata.flac.PictureFrame { *; }
-keep class com.google.android.exoplayer2.decoder.SimpleDecoderOutputBuffer { *; }
-keep class org.telegram.ui.Stories.recorder.FfmpegAudioWaveformLoader { *; }
# https://developers.google.com/ml-kit/known-issues#android_issues
-keep class com.google.mlkit.nl.languageid.internal.LanguageIdentificationJni { *; }

View file

@ -130,7 +130,7 @@ chat_outViewsSelected=-1258291201
chat_outInstant=-1
actionBarDefaultSearchPlaceholder=-2005173381
chat_outForwardedNameText=-1
dialogRoundCheckBox=-13653259
dialogRoundCheckBox=-15033089
actionBarTabLine=-13655305
chats_nameMessageArchived_threeLines=-7237231
chat_outSiteNameText=-1
@ -249,7 +249,7 @@ inappPlayerClose=-7563878
chat_outMediaIcon=-13332255
chat_outAudioCacheSeekbar=738197503
chats_sentClock=2066650878
dialogFloatingButton=-12081419
dialogFloatingButton=-15033089
chats_archiveBackground=-11294989
chat_inPreviewInstantText=-15299362
chat_outLoaderSelected=-1

View file

@ -200,7 +200,7 @@ profile_tabSelector=350681087
actionBarDefaultSearchPlaceholder=2027746559
actionBarActionModeDefaultSelector=433780735
chat_outForwardedNameText=-7551233
dialogRoundCheckBox=-10177041
dialogRoundCheckBox=-15033089
chat_emojiPanelTrendingTitle=-1
actionBarTabLine=-10179073
chat_stickersHintPanel=-13484721

View file

@ -141,7 +141,7 @@ chat_outViewsSelected=-3676417
chat_outInstant=-1
actionBarDefaultSearchPlaceholder=-2005173381
chat_outForwardedNameText=-1
dialogRoundCheckBox=-13653259
dialogRoundCheckBox=-15033089
actionBarTabLine=-13655305
chats_nameMessageArchived_threeLines=-7237231
chat_outSiteNameText=-1
@ -271,7 +271,7 @@ chat_outMediaIcon=-14707997
chat_outAudioCacheSeekbar=738197503
chats_sentClock=2073474246
chat_inAudioSeekbar=-2762017
dialogFloatingButton=-12081419
dialogFloatingButton=-15033089
chats_archiveBackground=-11294989
chat_inPreviewInstantText=-15300135
chat_inViews=-274882397

Binary file not shown.

View file

@ -213,7 +213,7 @@ actionBarDefaultSearchPlaceholder=-2097152001
profile_tabSelector=268435455
actionBarActionModeDefaultSelector=520093695
chat_outForwardedNameText=-5448193
dialogRoundCheckBox=-10177041
dialogRoundCheckBox=-15033089
chat_emojiPanelTrendingTitle=-1
actionBarTabLine=-12339201
chat_stickersHintPanel=-14606046

View file

@ -945,8 +945,8 @@ public class AndroidUtilities {
}
}
public static void fillStatusBarHeight(Context context) {
if (context == null || AndroidUtilities.statusBarHeight > 0) {
public static void fillStatusBarHeight(Context context, boolean force) {
if (context == null || (AndroidUtilities.statusBarHeight > 0 && !force)) {
return;
}
AndroidUtilities.statusBarHeight = getStatusBarHeight(context);
@ -2291,10 +2291,8 @@ public class AndroidUtilities {
}
roundMessageInset = dp(2);
}
fillStatusBarHeight(context, true);
if (BuildVars.LOGS_ENABLED) {
if (statusBarHeight == 0) {
fillStatusBarHeight(context);
}
FileLog.e("density = " + density + " display size = " + displaySize.x + " " + displaySize.y + " " + displayMetrics.xdpi + "x" + displayMetrics.ydpi + ", screen layout: " + configuration.screenLayout + ", statusbar height: " + statusBarHeight + ", navbar height: " + navigationBarHeight);
}
ViewConfiguration vc = ViewConfiguration.get(context);
@ -5379,6 +5377,18 @@ public class AndroidUtilities {
return new Pair<>(0, 0);
}
public static void forEachViews(View view, Consumer<View> consumer) {
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++) {
consumer.accept(view);
forEachViews(viewGroup.getChildAt(i), consumer);
}
} else {
consumer.accept(view);
}
}
public static void forEachViews(RecyclerView recyclerView, Consumer<View> consumer) {
for (int i = 0; i < recyclerView.getChildCount(); i++) {
consumer.accept(recyclerView.getChildAt(i));

View file

@ -150,6 +150,7 @@ public class ApplicationLoader extends Application {
return;
}
applicationInited = true;
NativeLoader.initNativeLibs(ApplicationLoader.applicationContext);
try {
LocaleController.getInstance(); //TODO improve
@ -228,7 +229,6 @@ public class ApplicationLoader extends Application {
ContactsController.getInstance(a).checkAppAccount();
DownloadController.getInstance(a);
}
ChatThemeController.init();
BillingController.getInstance().startConnection();
}

View file

@ -24,8 +24,8 @@ public class BuildVars {
public static boolean USE_CLOUD_STRINGS = true;
public static boolean CHECK_UPDATES = true;
public static boolean NO_SCOPED_STORAGE = Build.VERSION.SDK_INT <= 29;
public static int BUILD_VERSION = 3804;
public static String BUILD_VERSION_STRING = "10.0.5";
public static int BUILD_VERSION = 3867;
public static String BUILD_VERSION_STRING = "10.0.8";
public static int APP_ID = 4;
public static String APP_HASH = "014b35b6184100b085b0d0572f9b5103";

View file

@ -1665,6 +1665,16 @@ public class ChatObject {
return chat == null || chat instanceof TLRPC.TL_chatEmpty || chat instanceof TLRPC.TL_chatForbidden || chat instanceof TLRPC.TL_channelForbidden || chat.left || chat.kicked || chat.deactivated;
}
public static boolean isInChat(TLRPC.Chat chat) {
if (chat == null || chat instanceof TLRPC.TL_chatEmpty || chat instanceof TLRPC.TL_chatForbidden || chat instanceof TLRPC.TL_channelForbidden) {
return false;
}
if (chat.left || chat.kicked || chat.deactivated) {
return false;
}
return true;
}
public static boolean canSendAsPeers(TLRPC.Chat chat) {
return ChatObject.isChannel(chat) && chat.megagroup && (ChatObject.isPublic(chat) || chat.has_geo || chat.has_link);
}

View file

@ -24,15 +24,20 @@ import java.util.Locale;
public class ChatThemeController extends BaseController {
private static final long reloadTimeoutMs = 2 * 60 * 60 * 1000;
private final long reloadTimeoutMs = 2 * 60 * 60 * 1000;
public static volatile DispatchQueue chatThemeQueue = new DispatchQueue("chatThemeQueue");
private static final HashMap<Long, Bitmap> themeIdWallpaperThumbMap = new HashMap<>();
private static List<EmojiThemes> allChatThemes;
private static volatile long themesHash;
private static volatile long lastReloadTimeMs;
private final HashMap<Long, Bitmap> themeIdWallpaperThumbMap = new HashMap<>();
private List<EmojiThemes> allChatThemes;
private volatile long themesHash;
private volatile long lastReloadTimeMs;
public static void init() {
private ChatThemeController(int num) {
super(num);
init();
}
private void init() {
SharedPreferences preferences = getSharedPreferences();
themesHash = 0;
lastReloadTimeMs = 0;
@ -53,20 +58,20 @@ public class ChatThemeController extends BaseController {
}
}
private static void preloadSticker(String emojicon) {
private void preloadSticker(String emojicon) {
ImageReceiver imageReceiver = new ImageReceiver();
TLRPC.Document document = MediaDataController.getInstance(UserConfig.selectedAccount).getEmojiAnimatedSticker(emojicon);
imageReceiver.setImage(ImageLocation.getForDocument(document), "50_50", null, null, null, 0);
Emoji.preloadEmoji(emojicon);
}
public static void requestAllChatThemes(final ResultCallback<List<EmojiThemes>> callback, boolean withDefault) {
public void requestAllChatThemes(final ResultCallback<List<EmojiThemes>> callback, boolean withDefault) {
if (themesHash == 0 || lastReloadTimeMs == 0) {
init();
}
boolean needReload = System.currentTimeMillis() - lastReloadTimeMs > reloadTimeoutMs;
if (true || allChatThemes == null || allChatThemes.isEmpty() || needReload) {
if (allChatThemes == null || allChatThemes.isEmpty() || needReload) {
TLRPC.TL_account_getChatThemes request = new TLRPC.TL_account_getChatThemes();
request.hash = themesHash;
ConnectionsManager.getInstance(UserConfig.selectedAccount).sendRequest(request, (response, error) -> chatThemeQueue.postRunnable(() -> {
@ -89,7 +94,7 @@ public class ChatThemeController extends BaseController {
SerializedData data = new SerializedData(tlChatTheme.getObjectSize());
tlChatTheme.serializeToStream(data);
editor.putString("theme_" + i, Utilities.bytesToHex(data.toByteArray()));
EmojiThemes chatTheme = new EmojiThemes(tlChatTheme, false);
EmojiThemes chatTheme = new EmojiThemes(currentAccount, tlChatTheme, false);
chatTheme.preloadWallpaper();
chatThemes.add(chatTheme);
}
@ -107,7 +112,7 @@ public class ChatThemeController extends BaseController {
}
if (!isError) {
if (withDefault && !chatThemes.get(0).showAsDefaultStub) {
chatThemes.add(0, EmojiThemes.createChatThemesDefault());
chatThemes.add(0, EmojiThemes.createChatThemesDefault(currentAccount));
}
for (EmojiThemes theme : chatThemes) {
theme.initColors();
@ -122,7 +127,7 @@ public class ChatThemeController extends BaseController {
if (allChatThemes != null && !allChatThemes.isEmpty()) {
List<EmojiThemes> chatThemes = new ArrayList<>(allChatThemes);
if (withDefault && !chatThemes.get(0).showAsDefaultStub) {
chatThemes.add(0, EmojiThemes.createChatThemesDefault());
chatThemes.add(0, EmojiThemes.createChatThemesDefault(currentAccount));
}
for (EmojiThemes theme : chatThemes) {
theme.initColors();
@ -131,15 +136,15 @@ public class ChatThemeController extends BaseController {
}
}
private static SharedPreferences getSharedPreferences() {
return ApplicationLoader.applicationContext.getSharedPreferences("chatthemeconfig", Context.MODE_PRIVATE);
private SharedPreferences getSharedPreferences() {
return ApplicationLoader.applicationContext.getSharedPreferences("chatthemeconfig_" + currentAccount, Context.MODE_PRIVATE);
}
private static SharedPreferences getEmojiSharedPreferences() {
private SharedPreferences getEmojiSharedPreferences() {
return ApplicationLoader.applicationContext.getSharedPreferences("chatthemeconfig_emoji", Context.MODE_PRIVATE);
}
private static List<EmojiThemes> getAllChatThemesFromPrefs() {
private List<EmojiThemes> getAllChatThemesFromPrefs() {
SharedPreferences preferences = getSharedPreferences();
int count = preferences.getInt("count", 0);
List<EmojiThemes> themes = new ArrayList<>(count);
@ -149,7 +154,7 @@ public class ChatThemeController extends BaseController {
try {
TLRPC.TL_theme chatTheme = TLRPC.Theme.TLdeserialize(serializedData, serializedData.readInt32(true), true);
if (chatTheme != null) {
themes.add(new EmojiThemes(chatTheme, false));
themes.add(new EmojiThemes(currentAccount, chatTheme, false));
}
} catch (Throwable e) {
FileLog.e(e);
@ -158,7 +163,7 @@ public class ChatThemeController extends BaseController {
return themes;
}
public static void requestChatTheme(final String emoticon, final ResultCallback<EmojiThemes> callback) {
public void requestChatTheme(final String emoticon, final ResultCallback<EmojiThemes> callback) {
if (TextUtils.isEmpty(emoticon)) {
callback.onComplete(null);
return;
@ -201,10 +206,6 @@ public class ChatThemeController extends BaseController {
private final LongSparseArray<String> dialogEmoticonsMap = new LongSparseArray<>();
public ChatThemeController(int num) {
super(num);
}
public static boolean equals(TLRPC.WallPaper wallPaper, TLRPC.WallPaper oldWallpaper) {
if (wallPaper == null && oldWallpaper == null) {
return true;
@ -311,7 +312,7 @@ public class ChatThemeController extends BaseController {
return null;
}
public static void preloadAllWallpaperImages(boolean isDark) {
public void preloadAllWallpaperImages(boolean isDark) {
for (EmojiThemes chatTheme : allChatThemes) {
TLRPC.TL_theme theme = chatTheme.getTlTheme(isDark ? 1 : 0);
if (theme == null) {
@ -325,7 +326,7 @@ public class ChatThemeController extends BaseController {
}
}
public static void preloadAllWallpaperThumbs(boolean isDark) {
public void preloadAllWallpaperThumbs(boolean isDark) {
for (EmojiThemes chatTheme : allChatThemes) {
TLRPC.TL_theme theme = chatTheme.getTlTheme(isDark ? 1 : 0);
if (theme == null) {
@ -343,15 +344,15 @@ public class ChatThemeController extends BaseController {
}
}
public static void clearWallpaperImages() {
public void clearWallpaperImages() {
}
public static void clearWallpaperThumbImages() {
public void clearWallpaperThumbImages() {
themeIdWallpaperThumbMap.clear();
}
public static void getWallpaperBitmap(long themeId, ResultCallback<Bitmap> callback) {
public void getWallpaperBitmap(long themeId, ResultCallback<Bitmap> callback) {
if (themesHash == 0) {
callback.onComplete(null);
return;
@ -375,11 +376,11 @@ public class ChatThemeController extends BaseController {
});
}
private static File getPatternFile(long themeId) {
private File getPatternFile(long themeId) {
return new File(ApplicationLoader.getFilesDirFixed(), String.format(Locale.US, "%d_%d.jpg", themeId, themesHash));
}
public static void saveWallpaperBitmap(Bitmap bitmap, long themeId) {
public void saveWallpaperBitmap(Bitmap bitmap, long themeId) {
File file = getPatternFile(themeId);
chatThemeQueue.postRunnable(() -> {
try {
@ -392,7 +393,7 @@ public class ChatThemeController extends BaseController {
});
}
public static Bitmap getWallpaperThumbBitmap(long themeId) {
public Bitmap getWallpaperThumbBitmap(long themeId) {
return themeIdWallpaperThumbMap.get(themeId);
}

View file

@ -1340,6 +1340,36 @@ public class DatabaseMigrationHelper {
version = 129;
}
if (version == 129) {
database.executeFast("CREATE INDEX IF NOT EXISTS stickers_featured_emoji_index ON stickers_featured(emoji);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 130").stepThis().dispose();
version = 130;
}
if (version == 130) {
database.executeFast("DROP TABLE archived_stories").stepThis().dispose();
database.executeFast("ALTER TABLE profile_stories ADD COLUMN type INTEGER default 0").stepThis().dispose();
database.executeFast("PRAGMA user_version = 131").stepThis().dispose();
version = 131;
}
if (version == 131) {
database.executeFast("ALTER TABLE stories DROP COLUMN local_path").stepThis().dispose();
database.executeFast("ALTER TABLE stories DROP COLUMN local_thumb_path").stepThis().dispose();
database.executeFast("PRAGMA user_version = 132").stepThis().dispose();
version = 132;
}
if (version == 132) {
database.executeFast("CREATE TABLE unconfirmed_auth (data BLOB);").stepThis().dispose();
database.executeFast("PRAGMA user_version = 133").stepThis().dispose();
version = 133;
}
return version;
}

View file

@ -269,6 +269,7 @@ public class FileLoadOperation {
void didChangedLoadProgress(FileLoadOperation operation, long uploadedSize, long totalSize);
void saveFilePath(FilePathDatabase.PathData pathSaveData, File cacheFileFinal);
boolean hasAnotherRefOnFile(String path);
boolean isLocallyCreatedFile(String path);
}
private void updateParams() {
@ -948,7 +949,7 @@ public class FileLoadOperation {
}
}
boolean finalFileExist = cacheFileFinal.exists();
if (finalFileExist && (parentObject instanceof TLRPC.TL_theme || (totalBytesCount != 0 && !ungzip && totalBytesCount != cacheFileFinal.length()))) {
if (finalFileExist && (parentObject instanceof TLRPC.TL_theme || (totalBytesCount != 0 && !ungzip && totalBytesCount != cacheFileFinal.length())) && !delegate.isLocallyCreatedFile(cacheFileFinal.toString())) {
if (BuildVars.LOGS_ENABLED) {
FileLog.d("debug_loading: delete existing file cause file size mismatch " + cacheFileFinal.getName() + " totalSize=" + totalBytesCount + " existingFileSize=" + cacheFileFinal.length());
}

View file

@ -11,8 +11,6 @@ package org.telegram.messenger;
import android.text.TextUtils;
import android.util.SparseArray;
import com.google.android.exoplayer2.util.Log;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.LaunchActivity;
@ -123,6 +121,41 @@ public class FileLoader extends BaseController {
return fileLoaderQueue;
}
public void setLocalPathTo(TLObject attach, String attachPath) {
long documentId = 0;
int dcId = 0;
int type = 0;
if (attach instanceof TLRPC.Document) {
TLRPC.Document document = (TLRPC.Document) attach;
if (document.key != null) {
type = MEDIA_DIR_CACHE;
} else {
if (MessageObject.isVoiceDocument(document)) {
type = MEDIA_DIR_AUDIO;
} else if (MessageObject.isVideoDocument(document)) {
type = MEDIA_DIR_VIDEO;
} else {
type = MEDIA_DIR_DOCUMENT;
}
}
documentId = document.id;
dcId = document.dc_id;
filePathDatabase.putPath(documentId, dcId, type, FilePathDatabase.FLAG_LOCALLY_CREATED, attachPath);
} else if (attach instanceof TLRPC.PhotoSize) {
TLRPC.PhotoSize photoSize = (TLRPC.PhotoSize) attach;
if (photoSize instanceof TLRPC.TL_photoStrippedSize || photoSize instanceof TLRPC.TL_photoPathSize) {
return;
} else if (photoSize.location == null || photoSize.location.key != null || photoSize.location.volume_id == Integer.MIN_VALUE && photoSize.location.local_id < 0 || photoSize.size < 0) {
type = MEDIA_DIR_CACHE;
} else {
type = MEDIA_DIR_IMAGE;
}
documentId = photoSize.location.volume_id;
dcId = photoSize.location.dc_id + (photoSize.location.local_id << 16);
filePathDatabase.putPath(documentId, dcId, type, FilePathDatabase.FLAG_LOCALLY_CREATED, attachPath);
}
}
public interface FileLoaderDelegate {
void fileUploadProgressChanged(FileUploadOperation operation, String location, long uploadedSize, long totalSize, boolean isEncrypted);
@ -924,13 +957,18 @@ public class FileLoader extends BaseController {
@Override
public void saveFilePath(FilePathDatabase.PathData pathSaveData, File cacheFileFinal) {
getFileDatabase().putPath(pathSaveData.id, pathSaveData.dc, pathSaveData.type, cacheFileFinal != null ? cacheFileFinal.toString() : null);
getFileDatabase().putPath(pathSaveData.id, pathSaveData.dc, pathSaveData.type, 0, cacheFileFinal != null ? cacheFileFinal.toString() : null);
}
@Override
public boolean hasAnotherRefOnFile(String path) {
return getFileDatabase().hasAnotherRefOnFile(path);
}
@Override
public boolean isLocallyCreatedFile(String path) {
return getFileDatabase().isLocallyCreated(path);
}
};
operation.setDelegate(fileLoadOperationDelegate);

View file

@ -17,6 +17,8 @@ import java.util.concurrent.CountDownLatch;
public class FilePathDatabase {
public final static int FLAG_LOCALLY_CREATED = 1; //file is locally created, skip file size check in FileLoader
private DispatchQueue dispatchQueue;
private final int currentAccount;
@ -24,7 +26,7 @@ public class FilePathDatabase {
private File cacheFile;
private File shmCacheFile;
private final static int LAST_DB_VERSION = 4;
private final static int LAST_DB_VERSION = 7;
private final static String DATABASE_NAME = "file_to_path";
private final static String DATABASE_BACKUP_NAME = "file_to_path_backup";
@ -58,7 +60,7 @@ public class FilePathDatabase {
database.executeFast("PRAGMA temp_store = MEMORY").stepThis().dispose();
if (createTable) {
database.executeFast("CREATE TABLE paths(document_id INTEGER, dc_id INTEGER, type INTEGER, path TEXT, PRIMARY KEY(document_id, dc_id, type));").stepThis().dispose();
database.executeFast("CREATE TABLE paths(document_id INTEGER, dc_id INTEGER, type INTEGER, path TEXT, flags INTEGER, PRIMARY KEY(document_id, dc_id, type));").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS path_in_paths ON paths(path);").stepThis().dispose();
database.executeFast("CREATE TABLE paths_by_dialog_id(path TEXT PRIMARY KEY, dialog_id INTEGER, message_id INTEGER, message_type INTEGER);").stepThis().dispose();
@ -111,6 +113,15 @@ public class FilePathDatabase {
database.executeFast("ALTER TABLE paths_by_dialog_id ADD COLUMN message_id INTEGER default 0").stepThis().dispose();
database.executeFast("ALTER TABLE paths_by_dialog_id ADD COLUMN message_type INTEGER default 0").stepThis().dispose();
database.executeFast("PRAGMA user_version = " + 4).stepThis().dispose();
version = 4;
}
if (version == 4 || version == 5 || version == 6) {
try {
database.executeFast("ALTER TABLE paths ADD COLUMN flags INTEGER default 0").stepThis().dispose();
} catch (Throwable ignore) {
FileLog.e(ignore);
}
database.executeFast("PRAGMA user_version = " + 7).stepThis().dispose();
}
}
@ -231,7 +242,7 @@ public class FilePathDatabase {
}
}
public void putPath(long id, int dc, int type, String path) {
public void putPath(long id, int dc, int type, int flags, String path) {
postRunnable(() -> {
if (BuildVars.DEBUG_VERSION) {
FileLog.d("put file path id=" + id + " dc=" + dc + " type=" + type + " path=" + path);
@ -248,12 +259,13 @@ public class FilePathDatabase {
deleteState.bindString(1, path);
deleteState.step();
state = database.executeFast("REPLACE INTO paths VALUES(?, ?, ?, ?)");
state = database.executeFast("REPLACE INTO paths VALUES(?, ?, ?, ?, ?)");
state.requery();
state.bindLong(1, id);
state.bindInteger(2, dc);
state.bindInteger(3, type);
state.bindString(4, path);
state.bindInteger(5, flags);
state.step();
state.dispose();
} else {
@ -485,6 +497,31 @@ public class FilePathDatabase {
}
}
public boolean isLocallyCreated(String path) {
CountDownLatch syncLatch = new CountDownLatch(1);
boolean[] res = new boolean[]{false};
postRunnable(() -> {
ensureDatabaseCreated();
try {
SQLiteCursor cursor = database.queryFinalized("SELECT flags FROM paths WHERE path = '" + path + "'");
if (cursor.next()) {
res[0] = (cursor.intValue(0) & FLAG_LOCALLY_CREATED) != 0;
}
} catch (Exception e) {
FileLog.e(e);
} finally {
syncLatch.countDown();
}
});
try {
syncLatch.await();
} catch (InterruptedException e) {
FileLog.e(e);
}
return res[0];
}
public static class PathData {
public final long id;
public final int dc;

View file

@ -911,7 +911,35 @@ public class FileRefController extends BaseController {
}
} else if (response instanceof TLRPC.TL_help_appUpdate) {
TLRPC.TL_help_appUpdate appUpdate = (TLRPC.TL_help_appUpdate) response;
result = getFileReference(appUpdate.document, requester.location, needReplacement, locationReplacement);
try {
SharedConfig.pendingAppUpdate = appUpdate;
SharedConfig.saveConfig();
} catch (Exception e) {
FileLog.e(e);
}
try {
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.appUpdateAvailable);
} catch (Exception e) {
FileLog.e(e);
}
try {
if (appUpdate.document != null) {
result = appUpdate.document.file_reference;
TLRPC.TL_inputDocumentFileLocation location = new TLRPC.TL_inputDocumentFileLocation();
location.id = appUpdate.document.id;
location.access_hash = appUpdate.document.access_hash;
location.file_reference = appUpdate.document.file_reference;
location.thumb_size = "";
locationReplacement = new TLRPC.InputFileLocation[1];
locationReplacement[0] = location;
}
} catch (Exception e) {
result = null;
FileLog.e(e);
}
if (result == null) {
result = getFileReference(appUpdate.document, requester.location, needReplacement, locationReplacement);
}
if (result == null) {
result = getFileReference(appUpdate.sticker, requester.location, needReplacement, locationReplacement);
}

View file

@ -104,6 +104,9 @@ public class FileStreamLoadOperation extends BaseDataSource implements FileLoadO
try {
file = new RandomAccessFile(currentFile, "r");
file.seek(currentOffset);
if (loadOperation.isFinished()) {
bytesRemaining = currentFile.length() - currentOffset;
}
} catch (Throwable e) {
}
}
@ -163,6 +166,9 @@ public class FileStreamLoadOperation extends BaseDataSource implements FileLoadO
try {
file = new RandomAccessFile(currentFile, "r");
file.seek(currentOffset);
if (loadOperation.isFinished()) {
bytesRemaining = currentFile.length() - currentOffset;
}
} catch (Throwable e) {
}

View file

@ -127,6 +127,10 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
recycleOnRelease = true;
}
public String getKey() {
return key;
}
public int getWidth() {
return bitmap != null ? bitmap.getWidth() : 0;
}
@ -3090,7 +3094,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
public void setFileLoadingPriority(int fileLoadingPriority) {
if (this.fileLoadingPriority != fileLoadingPriority) {
this.fileLoadingPriority = fileLoadingPriority;
if (attachedToWindow) {
if (attachedToWindow && hasImageSet()) {
ImageLoader.getInstance().changeFileLoadingPriorityForImageReceiver(this);
}
}

View file

@ -120,6 +120,8 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
public native byte[] getWaveform2(short[] array, int length);
public static native byte[] getMp3Waveform(String path, int samplesCount);
public boolean isBuffering() {
if (audioPlayer != null) {
return audioPlayer.isBuffering();
@ -293,8 +295,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
Math.abs(vignetteValue) < 0.1f &&
Math.abs(grainValue) < 0.1f &&
blurType == 0 &&
Math.abs(sharpenValue) < 0.1f &&
Math.abs(blurExcludeSize) < 0.1f
Math.abs(sharpenValue) < 0.1f
);
}
}
@ -5278,7 +5279,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
}
boolean needCompress = avatarStartTime != -1 || info.cropState != null || info.mediaEntities != null || info.paintPath != null || info.filterState != null ||
resultWidth != originalWidth || resultHeight != originalHeight || rotationValue != 0 || info.roundVideo || startTime != -1;
resultWidth != originalWidth || resultHeight != originalHeight || rotationValue != 0 || info.roundVideo || startTime != -1 || !info.mixedSoundInfos.isEmpty();
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("videoconvert", Activity.MODE_PRIVATE);
@ -5315,7 +5316,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
info.videoConvertFirstWrite = true;
MediaCodecVideoConvertor videoConvertor = new MediaCodecVideoConvertor();
boolean error = videoConvertor.convertVideo(videoPath, cacheFile,
MediaCodecVideoConvertor.ConvertVideoParams convertVideoParams = MediaCodecVideoConvertor.ConvertVideoParams.of(videoPath, cacheFile,
rotationValue, isSecret,
originalWidth, originalHeight,
resultWidth, resultHeight,
@ -5324,6 +5325,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
needCompress, duration,
info.filterState,
info.paintPath,
info.blurPath,
info.mediaEntities,
info.isPhoto,
info.cropState,
@ -5334,8 +5336,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
info.muted,
info.isStory,
info.hdrInfo,
info.parts
);
info.parts);
convertVideoParams.soundInfos.addAll(info.mixedSoundInfos);
boolean error = videoConvertor.convertVideo(convertVideoParams);
boolean canceled = info.canceled;

View file

@ -34,7 +34,6 @@ import android.text.TextUtils;
import android.text.style.CharacterStyle;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@ -99,7 +98,8 @@ public class MediaDataController extends BaseController {
ATTACH_MENU_BOT_COLOR_LIGHT_ICON = "light_icon",
ATTACH_MENU_BOT_COLOR_LIGHT_TEXT = "light_text",
ATTACH_MENU_BOT_COLOR_DARK_ICON = "dark_icon",
ATTACH_MENU_BOT_COLOR_DARK_TEXT = "dark_text";
ATTACH_MENU_BOT_COLOR_DARK_TEXT = "dark_text",
ATTACH_MENU_BOT_SIDE_MENU = "android_side_menu_static";
private static Pattern BOLD_PATTERN = Pattern.compile("\\*\\*(.+?)\\*\\*"),
ITALIC_PATTERN = Pattern.compile("__(.+?)__"),
@ -183,6 +183,8 @@ public class MediaDataController extends BaseController {
loadAvatarConstructor(false);
loadAvatarConstructor(true);
ringtoneDataStore = new RingtoneDataStore(currentAccount);
menuBotsUpdateDate = getMessagesController().getMainSettings().getInt("menuBotsUpdateDate", 0);
}
public static final int TYPE_IMAGE = 0;
@ -199,6 +201,7 @@ public class MediaDataController extends BaseController {
private long menuBotsUpdateHash;
private TLRPC.TL_attachMenuBots attachMenuBots = new TLRPC.TL_attachMenuBots();
private boolean isLoadingMenuBots;
private boolean menuBotsUpdatedLocal;
private int menuBotsUpdateDate;
private int reactionsUpdateHash;
@ -375,8 +378,8 @@ public class MediaDataController extends BaseController {
}
}
public void checkMenuBots() {
if (!isLoadingMenuBots && Math.abs(System.currentTimeMillis() / 1000 - menuBotsUpdateDate) >= 60 * 60) {
public void checkMenuBots(boolean atStart) {
if (!isLoadingMenuBots && (atStart && !menuBotsUpdatedLocal || Math.abs(System.currentTimeMillis() / 1000 - menuBotsUpdateDate) >= 60 * 60)) {
loadAttachMenuBots(true, false);
}
}
@ -460,6 +463,9 @@ public class MediaDataController extends BaseController {
}
public void loadAttachMenuBots(boolean cache, boolean force) {
loadAttachMenuBots(cache, force, null);
}
public void loadAttachMenuBots(boolean cache, boolean force, Runnable onDone) {
isLoadingMenuBots = true;
if (cache) {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
@ -481,6 +487,13 @@ public class MediaDataController extends BaseController {
hash = c.longValue(1);
date = c.intValue(2);
}
if (bots != null) {
ArrayList<Long> usersToLoad = new ArrayList<>();
for (int i = 0; i < bots.bots.size(); i++) {
usersToLoad.add(bots.bots.get(i).bot_id);
}
bots.users.addAll(getMessagesStorage().getUsers(usersToLoad));
}
} catch (Exception e) {
FileLog.e(e, false);
} finally {
@ -501,6 +514,9 @@ public class MediaDataController extends BaseController {
TLRPC.TL_attachMenuBots r = (TLRPC.TL_attachMenuBots) response;
processLoadedMenuBots(r, r.hash, date, false);
}
if (onDone != null) {
AndroidUtilities.runOnUIThread(onDone);
}
});
}
}
@ -510,19 +526,35 @@ public class MediaDataController extends BaseController {
attachMenuBots = bots;
menuBotsUpdateHash = hash;
}
menuBotsUpdateDate = date;
getMessagesController().getMainSettings().edit().putInt("menuBotsUpdateDate", menuBotsUpdateDate = date).commit();
menuBotsUpdatedLocal = true;
boolean forceReload = false;
if (bots != null) {
if (!cache) {
getMessagesStorage().putUsersAndChats(bots.users, null, true, false);
}
getMessagesController().putUsers(bots.users, cache);
AndroidUtilities.runOnUIThread(() -> NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.attachMenuBotsDidLoad));
for (int i = 0; i < bots.bots.size(); i++) {
if (bots.bots.get(i) instanceof TLRPC.TL_attachMenuBot_layer162) {
bots.bots.get(i).show_in_attach_menu = true;
forceReload = true;
}
}
}
if (!cache) {
putMenuBotsToCache(bots, hash, date);
} else if (Math.abs(System.currentTimeMillis() / 1000 - date) >= 60 * 60) {
} else if (forceReload || Math.abs(System.currentTimeMillis() / 1000 - date) >= 60 * 60) {
loadAttachMenuBots(false, true);
}
}
public void updateAttachMenuBotsInCache() {
if (getAttachMenuBots() != null) {
putMenuBotsToCache(getAttachMenuBots(), menuBotsUpdateHash, menuBotsUpdateDate);
}
}
private void putMenuBotsToCache(TLRPC.TL_attachMenuBots bots, long hash, int date) {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
try {
@ -731,18 +763,18 @@ public class MediaDataController extends BaseController {
int N = Math.min(arrayList.size(), 10);
for (int i = 0; i < N; i++) {
TLRPC.TL_availableReaction reaction = arrayList.get(i);
preloadImage(ImageLocation.getForDocument(reaction.activate_animation));
preloadImage(ImageLocation.getForDocument(reaction.appear_animation));
preloadImage(ImageLocation.getForDocument(reaction.activate_animation), FileLoader.PRIORITY_LOW);
preloadImage(ImageLocation.getForDocument(reaction.appear_animation), FileLoader.PRIORITY_LOW);
}
for (int i = 0; i < N; i++) {
TLRPC.TL_availableReaction reaction = arrayList.get(i);
preloadImage(ImageLocation.getForDocument(reaction.effect_animation));
preloadImage(ImageLocation.getForDocument(reaction.effect_animation), FileLoader.PRIORITY_LOW);
}
}
private void preloadImage(ImageLocation location) {
getFileLoader().loadFile(location, null, null, FileLoader.PRIORITY_LOW, FileLoader.PRELOAD_CACHE_TYPE);
public void preloadImage(ImageLocation location, int priority) {
getFileLoader().loadFile(location, null, null, priority, FileLoader.PRELOAD_CACHE_TYPE);
}
public void preloadImage(ImageReceiver imageReceiver, ImageLocation location, String filter) {
@ -1283,14 +1315,21 @@ public class MediaDataController extends BaseController {
});
}
private final HashSet<TLRPC.InputStickerSet> loadingStickerSets = new HashSet<>();
private void fetchStickerSetInternal(TLRPC.InputStickerSet inputStickerSet, Utilities.Callback2<Boolean, TLRPC.TL_messages_stickerSet> onDone) {
if (onDone == null) {
return;
}
if (loadingStickerSets.contains(inputStickerSet)) {
return;
}
loadingStickerSets.add(inputStickerSet);
TLRPC.TL_messages_getStickerSet req = new TLRPC.TL_messages_getStickerSet();
req.stickerset = inputStickerSet;
getConnectionsManager().sendRequest(req, (response, error) -> {
AndroidUtilities.runOnUIThread(() -> {
loadingStickerSets.remove(inputStickerSet);
// if (error != null && "".equals(error.text)) {
// onDone.run(true, null);
// } else
@ -1509,6 +1548,16 @@ public class MediaDataController extends BaseController {
return null;
}
@Nullable
public static TLRPC.TL_attachMenuBotIcon getSideMenuBotIcon(@NonNull TLRPC.TL_attachMenuBot bot) {
for (TLRPC.TL_attachMenuBotIcon icon : bot.icons) {
if (icon.name.equals(ATTACH_MENU_BOT_SIDE_MENU)) {
return icon;
}
}
return null;
}
@Nullable
public static TLRPC.TL_attachMenuBotIcon getStaticAttachMenuBotIcon(@NonNull TLRPC.TL_attachMenuBot bot) {
for (TLRPC.TL_attachMenuBotIcon icon : bot.icons) {
@ -2360,7 +2409,7 @@ public class MediaDataController extends BaseController {
processLoadedDiceStickers(getUserConfig().genericAnimationsStickerPack, false, stickerSet, false, (int) (System.currentTimeMillis() / 1000));
for (int i = 0; i < stickerSet.documents.size(); i++) {
if (currentAccount == UserConfig.selectedAccount) {
preloadImage(ImageLocation.getForDocument(stickerSet.documents.get(i)));
preloadImage(ImageLocation.getForDocument(stickerSet.documents.get(i)), FileLoader.PRIORITY_LOW);
}
}
}
@ -7143,7 +7192,7 @@ public class MediaDataController extends BaseController {
}));
}
public void chekAllMedia(boolean force) {
public void checkAllMedia(boolean force) {
if (force) {
reactionsUpdateDate = 0;
loadFeaturedDate[0] = 0;
@ -7155,7 +7204,7 @@ public class MediaDataController extends BaseController {
checkFeaturedStickers();
checkFeaturedEmoji();
checkReactions();
checkMenuBots();
checkMenuBots(true);
checkPremiumPromo();
checkPremiumGiftStickers();
checkGenericAnimations();
@ -7788,13 +7837,13 @@ public class MediaDataController extends BaseController {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("emojithemes_config_" + currentAccount, Context.MODE_PRIVATE);
int count = preferences.getInt("count", 0);
ArrayList<ChatThemeBottomSheet.ChatThemeItem> previewItems = new ArrayList<>();
previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(EmojiThemes.createHomePreviewTheme()));
previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(EmojiThemes.createHomePreviewTheme(currentAccount)));
for (int i = 0; i < count; ++i) {
String value = preferences.getString("theme_" + i, "");
SerializedData serializedData = new SerializedData(Utilities.hexToBytes(value));
try {
TLRPC.TL_theme theme = TLRPC.Theme.TLdeserialize(serializedData, serializedData.readInt32(true), true);
EmojiThemes fullTheme = EmojiThemes.createPreviewFullTheme(theme);
EmojiThemes fullTheme = EmojiThemes.createPreviewFullTheme(currentAccount, theme);
if (fullTheme.items.size() >= 4) {
previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(fullTheme));
}
@ -7833,10 +7882,10 @@ public class MediaDataController extends BaseController {
if (!emojiPreviewThemes.isEmpty()) {
final ArrayList<ChatThemeBottomSheet.ChatThemeItem> previewItems = new ArrayList<>();
previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(EmojiThemes.createHomePreviewTheme()));
previewItems.add(new ChatThemeBottomSheet.ChatThemeItem(EmojiThemes.createHomePreviewTheme(currentAccount)));
for (int i = 0; i < emojiPreviewThemes.size(); i++) {
TLRPC.TL_theme theme = emojiPreviewThemes.get(i);
EmojiThemes chatTheme = EmojiThemes.createPreviewFullTheme(theme);
EmojiThemes chatTheme = EmojiThemes.createPreviewFullTheme(currentAccount, theme);
ChatThemeBottomSheet.ChatThemeItem item = new ChatThemeBottomSheet.ChatThemeItem(chatTheme);
if (chatTheme.items.size() >= 4) {
previewItems.add(item);

View file

@ -41,6 +41,7 @@ import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Cells.ChatMessageCell;
import org.telegram.ui.Components.AnimatedEmojiDrawable;
import org.telegram.ui.Components.AnimatedEmojiSpan;
import org.telegram.ui.Components.ColoredImageSpan;
import org.telegram.ui.Components.Forum.ForumBubbleDrawable;
import org.telegram.ui.Components.Forum.ForumUtilities;
import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble;
@ -173,6 +174,7 @@ public class MessageObject {
public StringBuilder botButtonsLayout;
public boolean isRestrictedMessage;
public long loadedFileSize;
public boolean forceExpired;
public boolean isSpoilersRevealed;
public boolean isMediaSpoilersRevealed;
@ -231,6 +233,7 @@ public class MessageObject {
public int lastLineWidth;
public int textWidth;
public int textHeight;
public int captionHeight;
public boolean hasRtl;
public float textXOffset;
@ -403,7 +406,7 @@ public class MessageObject {
}
public boolean hasMediaSpoilers() {
return messageOwner.media != null && messageOwner.media.spoiler;
return messageOwner.media != null && messageOwner.media.spoiler || needDrawBluredPreview();
}
public boolean shouldDrawReactionsInLayout() {
@ -3593,8 +3596,11 @@ public class MessageObject {
messageText = AndroidUtilities.replaceTags(LocaleController.formatString("AutoDeleteGlobalActionFromYou", R.string.AutoDeleteGlobalActionFromYou, LocaleController.formatTTLString(action.period)));
} else {
TLObject object = null;
if (users != null) {
users.get(action.auto_setting_from);
if (sUsers != null) {
object = sUsers.get(action.auto_setting_from);
}
if (object == null && users != null) {
object = users.get(action.auto_setting_from);
}
if (object == null && chats != null) {
object = chats.get(action.auto_setting_from);
@ -3960,7 +3966,7 @@ public class MessageObject {
} else {
messageText = LocaleController.getString("AttachPhoto", R.string.AttachPhoto);
}
} else if (isVideo() || getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDocument && getDocument() instanceof TLRPC.TL_documentEmpty && getMedia(messageOwner).ttl_seconds != 0) {
} else if (isVideo() || getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDocument && (getDocument() instanceof TLRPC.TL_documentEmpty || getDocument() == null) && getMedia(messageOwner).ttl_seconds != 0) {
if (getMedia(messageOwner).ttl_seconds != 0 && !(messageOwner instanceof TLRPC.TL_message_secret)) {
messageText = LocaleController.getString("AttachDestructingVideo", R.string.AttachDestructingVideo);
} else {
@ -4076,7 +4082,7 @@ public class MessageObject {
}
} else if (hasExtendedMediaPreview()) {
type = TYPE_EXTENDED_MEDIA_PREVIEW;
} else if (getMedia(messageOwner).ttl_seconds != 0 && (getMedia(messageOwner).photo instanceof TLRPC.TL_photoEmpty || getDocument() instanceof TLRPC.TL_documentEmpty)) {
} else if (getMedia(messageOwner).ttl_seconds != 0 && (getMedia(messageOwner).photo instanceof TLRPC.TL_photoEmpty || getDocument() instanceof TLRPC.TL_documentEmpty || getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDocument && getDocument() == null || forceExpired)) {
contentType = 1;
type = TYPE_DATE;
} else if (getMedia(messageOwner) instanceof TLRPC.TL_messageMediaDice) {
@ -4858,6 +4864,28 @@ public class MessageObject {
caption = Emoji.replaceEmoji(text, Theme.chat_msgTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
caption = replaceAnimatedEmoji(caption, entities, Theme.chat_msgTextPaint.getFontMetricsInt(), false);
int maxWidth = getMaxMessageTextWidth();
final float lineSpacing = 1f;
final float lineAdd = 0;
Layout.Alignment align = Layout.Alignment.ALIGN_NORMAL;
StaticLayout captionLayout = null;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
StaticLayout.Builder builder =
StaticLayout.Builder.obtain(caption, 0, caption.length(), Theme.chat_msgTextPaint, maxWidth)
.setLineSpacing(lineAdd, lineSpacing)
.setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY)
.setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE)
.setAlignment(align);
captionLayout = builder.build();
} else {
captionLayout = new StaticLayout(caption, Theme.chat_msgTextPaint, maxWidth, align, lineSpacing, lineAdd, false);
}
} catch (Exception e) {
FileLog.e(e);
}
captionHeight = captionLayout == null ? 0 : captionLayout.getHeight();
boolean hasEntities;
if (messageOwner.send_state != MESSAGE_SEND_STATE_SENT) {
hasEntities = false;
@ -6275,10 +6303,23 @@ public class MessageObject {
return secondsLeft;
}
public String getSecretTimeString() {
private CharSequence secretOnceSpan;
private CharSequence secretPlaySpan;
public CharSequence getSecretTimeString() {
if (!isSecretMedia()) {
return null;
}
if (messageOwner.ttl == 0x7FFFFFFF) {
if (secretOnceSpan == null) {
secretOnceSpan = new SpannableString("v");
ColoredImageSpan span = new ColoredImageSpan(R.drawable.mini_viewonce);
span.setTranslateX(-AndroidUtilities.dp(3));
span.setWidth(AndroidUtilities.dp(13));
((Spannable) secretOnceSpan).setSpan(span, 0, secretOnceSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return TextUtils.concat(secretOnceSpan, "1");
}
int secondsLeft = getSecretTimeLeft();
String str;
if (secondsLeft < 60) {
@ -6286,7 +6327,14 @@ public class MessageObject {
} else {
str = secondsLeft / 60 + "m";
}
return str;
if (secretPlaySpan == null) {
secretPlaySpan = new SpannableString("p");
ColoredImageSpan span = new ColoredImageSpan(R.drawable.play_mini_video);
span.setTranslateX(AndroidUtilities.dp(1));
span.setWidth(AndroidUtilities.dp(13));
((Spannable) secretPlaySpan).setSpan(span, 0, secretPlaySpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return TextUtils.concat(secretPlaySpan, str);
}
public String getDocumentName() {
@ -6843,6 +6891,11 @@ public class MessageObject {
}
photoHeight = h;
}
if (caption != null && !TextUtils.isEmpty(caption)) {
photoHeight += captionHeight;
}
return photoHeight + AndroidUtilities.dp(14);
}
}

View file

@ -72,6 +72,7 @@ import org.telegram.ui.EditWidgetActivity;
import org.telegram.ui.LaunchActivity;
import org.telegram.ui.PremiumPreviewFragment;
import org.telegram.ui.ProfileActivity;
import org.telegram.ui.SecretMediaViewer;
import org.telegram.ui.Stories.StoriesController;
import org.telegram.ui.TopicsFragment;
@ -141,6 +142,7 @@ public class MessagesController extends BaseController implements NotificationCe
public int stealthModePast;
public int stealthModeCooldown;
public StoriesController storiesController;
public UnconfirmedAuthController unconfirmedAuthController;
private boolean hasArchivedChats;
private boolean hasStories;
public long storiesChangelogUserId = 777000;
@ -489,6 +491,7 @@ public class MessagesController extends BaseController implements NotificationCe
public int ringtoneDurationMax;
public int ringtoneSizeMax;
public boolean storiesExportNopublicLink;
public int authorizationAutoconfirmPeriod;
public int channelsLimitDefault;
public int channelsLimitPremium;
@ -1382,6 +1385,7 @@ public class MessagesController extends BaseController implements NotificationCe
storiesPosting = mainPreferences.getString("storiesPosting", "enabled");
storiesEntities = mainPreferences.getString("storiesEntities", "premium");
storiesExportNopublicLink = mainPreferences.getBoolean("storiesExportNopublicLink", false);
authorizationAutoconfirmPeriod = mainPreferences.getInt("authorization_autoconfirm_period", 604800);
BuildVars.GOOGLE_AUTH_CLIENT_ID = mainPreferences.getString("googleAuthClientId", BuildVars.GOOGLE_AUTH_CLIENT_ID);
if (mainPreferences.contains("dcDomainName2")) {
dcDomainName = mainPreferences.getString("dcDomainName2", "apv3.stel.com");
@ -3319,6 +3323,7 @@ public class MessagesController extends BaseController implements NotificationCe
changed = storiesChanged = true;
}
}
break;
}
case "stories_entities": {
if (value.value instanceof TLRPC.TL_jsonString) {
@ -3329,6 +3334,7 @@ public class MessagesController extends BaseController implements NotificationCe
changed = true;
}
}
break;
}
case "stories_export_nopublic_link": {
if (value.value instanceof TLRPC.TL_jsonBool) {
@ -3339,6 +3345,7 @@ public class MessagesController extends BaseController implements NotificationCe
changed = true;
}
}
break;
}
case "stories_venue_search_username": {
if (value.value instanceof TLRPC.TL_jsonString) {
@ -3349,6 +3356,18 @@ public class MessagesController extends BaseController implements NotificationCe
changed = true;
}
}
break;
}
case "authorization_autoconfirm_period": {
if (value.value instanceof TLRPC.TL_jsonNumber) {
TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value;
if (authorizationAutoconfirmPeriod != num.value) {
authorizationAutoconfirmPeriod = (int) num.value;
editor.putInt("authorizationAutoconfirmPeriod", authorizationAutoconfirmPeriod);
changed = true;
}
}
break;
}
}
}
@ -4112,6 +4131,9 @@ public class MessagesController extends BaseController implements NotificationCe
if (storiesController != null) {
storiesController.cleanup();
}
if (unconfirmedAuthController != null) {
unconfirmedAuthController.cleanup();
}
showFiltersTooltip = false;
@ -5626,8 +5648,22 @@ public class MessagesController extends BaseController implements NotificationCe
}
}
if (taskMedia != null) {
final boolean checkViewer = SecretMediaViewer.hasInstance() && SecretMediaViewer.getInstance().isVisible();
final MessageObject viewerObject = checkViewer ? SecretMediaViewer.getInstance().getCurrentMessageObject() : null;
for (int a = 0, N = taskMedia.size(); a < N; a++) {
getMessagesStorage().emptyMessagesMedia(taskMedia.keyAt(a), taskMedia.valueAt(a));
long dialogId = taskMedia.keyAt(a);
ArrayList<Integer> mids = taskMedia.valueAt(a);
if (checkViewer && viewerObject != null && viewerObject.currentAccount == currentAccount && viewerObject.getDialogId() == dialogId && mids.contains(viewerObject.getId())) {
final int id = viewerObject.getId();
mids.remove((Integer) id);
viewerObject.forceExpired = true;
final long taskId = createDeleteShowOnceTask(dialogId, id);
SecretMediaViewer.getInstance().setOnClose(() -> doDeleteShowOnceTask(taskId, dialogId, id));
getNotificationCenter().postNotificationName(NotificationCenter.updateMessageMedia, viewerObject.messageOwner);
}
if (!mids.isEmpty()) {
getMessagesStorage().emptyMessagesMedia(dialogId, mids);
}
}
}
Utilities.stageQueue.postRunnable(() -> {
@ -5839,7 +5875,7 @@ public class MessagesController extends BaseController implements NotificationCe
AndroidUtilities.runOnUIThread(() -> {
BaseFragment lastFragment = LaunchActivity.getLastFragment();
if (lastFragment != null && lastFragment.getParentActivity() != null) {
LimitReachedBottomSheet restricterdUsersBottomSheet = new LimitReachedBottomSheet(lastFragment, lastFragment.getParentActivity(), LimitReachedBottomSheet.TYPE_ADD_MEMBERS_RESTRICTED, currentAccount);
LimitReachedBottomSheet restricterdUsersBottomSheet = new LimitReachedBottomSheet(lastFragment, lastFragment.getParentActivity(), LimitReachedBottomSheet.TYPE_ADD_MEMBERS_RESTRICTED, currentAccount, null);
ArrayList<TLRPC.User> users = new ArrayList<TLRPC.User>();
users.add(user);
restricterdUsersBottomSheet.setRestrictedUsers(chat, users);
@ -10664,7 +10700,7 @@ public class MessagesController extends BaseController implements NotificationCe
}
arrayList.add(messageObject.getId());
long dialogId = messageObject.getDialogId();
getMessagesStorage().markMessagesContentAsRead(dialogId, arrayList, 0);
getMessagesStorage().markMessagesContentAsRead(dialogId, arrayList, 0, 0);
getNotificationCenter().postNotificationName(NotificationCenter.messagesReadContent, dialogId, arrayList);
if (messageObject.getId() < 0) {
markMessageAsRead(messageObject.getDialogId(), messageObject.messageOwner.random_id, Integer.MIN_VALUE);
@ -10716,8 +10752,32 @@ public class MessagesController extends BaseController implements NotificationCe
}
}
public long createDeleteShowOnceTask(long dialogId, int mid) {
NativeByteBuffer data = null;
try {
data = new NativeByteBuffer(16);
data.writeInt32(102);
data.writeInt64(dialogId);
data.writeInt32(mid);
} catch (Exception e) {
FileLog.e(e);
}
return getMessagesStorage().createPendingTask(data);
}
public void doDeleteShowOnceTask(long taskId, long dialogId, int mid) {
getMessagesStorage().removePendingTask(taskId);
ArrayList<Integer> mids = new ArrayList<>();
mids.add(mid);
getMessagesStorage().emptyMessagesMedia(dialogId, mids);
}
public void markMessageAsRead2(long dialogId, int mid, TLRPC.InputChannel inputChannel, int ttl, long taskId) {
if (mid == 0 || ttl <= 0) {
markMessageAsRead2(dialogId, mid, inputChannel, ttl, taskId, true);
}
public void markMessageAsRead2(long dialogId, int mid, TLRPC.InputChannel inputChannel, int ttl, long taskId, boolean createDeleteTask) {
if (mid == 0 || ttl < 0) {
return;
}
if (DialogObject.isChatDialog(dialogId) && inputChannel == null) {
@ -10731,7 +10791,7 @@ public class MessagesController extends BaseController implements NotificationCe
NativeByteBuffer data = null;
try {
data = new NativeByteBuffer(20 + (inputChannel != null ? inputChannel.getObjectSize() : 0));
data.writeInt32(23);
data.writeInt32(createDeleteTask ? 23 : 101);
data.writeInt64(dialogId);
data.writeInt32(mid);
data.writeInt32(ttl);
@ -10746,7 +10806,9 @@ public class MessagesController extends BaseController implements NotificationCe
newTaskId = taskId;
}
int time = getConnectionsManager().getCurrentTime();
getMessagesStorage().createTaskForMid(dialogId, mid, time, time, ttl, false);
if (createDeleteTask) {
getMessagesStorage().createTaskForMid(dialogId, mid, time, time, ttl, false);
}
if (inputChannel != null) {
TLRPC.TL_channels_readMessageContents req = new TLRPC.TL_channels_readMessageContents();
req.channel = inputChannel;
@ -11485,7 +11547,7 @@ public class MessagesController extends BaseController implements NotificationCe
BaseFragment lastFragment = LaunchActivity.getLastFragment();
if (lastFragment != null && lastFragment.getParentActivity() != null && !lastFragment.getParentActivity().isFinishing()) {
// if (ChatObject.canUserDoAdminAction(currentChat, ChatObject.ACTION_INVITE)) {
LimitReachedBottomSheet restricterdUsersBottomSheet = new LimitReachedBottomSheet(lastFragment, lastFragment.getParentActivity(), LimitReachedBottomSheet.TYPE_ADD_MEMBERS_RESTRICTED, currentAccount);
LimitReachedBottomSheet restricterdUsersBottomSheet = new LimitReachedBottomSheet(lastFragment, lastFragment.getParentActivity(), LimitReachedBottomSheet.TYPE_ADD_MEMBERS_RESTRICTED, currentAccount, null);
restricterdUsersBottomSheet.setRestrictedUsers(currentChat, userRestrictedPrivacy);
restricterdUsersBottomSheet.show();
// } else {
@ -14210,6 +14272,7 @@ public class MessagesController extends BaseController implements NotificationCe
LongSparseIntArray markAsReadMessagesInbox = null;
LongSparseIntArray stillUnreadMessagesCount = null;
LongSparseIntArray markAsReadMessagesOutbox = null;
int markContentAsReadMessagesDate = 0;
LongSparseArray<ArrayList<Integer>> markContentAsReadMessages = null;
SparseIntArray markAsReadEncrypted = null;
LongSparseArray<ArrayList<Integer>> deletedMessages = null;
@ -14464,6 +14527,7 @@ public class MessagesController extends BaseController implements NotificationCe
}
} else if (baseUpdate instanceof TLRPC.TL_updateReadMessagesContents) {
TLRPC.TL_updateReadMessagesContents update = (TLRPC.TL_updateReadMessagesContents) baseUpdate;
markContentAsReadMessagesDate = update.date;
if (markContentAsReadMessages == null) {
markContentAsReadMessages = new LongSparseArray<>();
}
@ -14861,6 +14925,11 @@ public class MessagesController extends BaseController implements NotificationCe
updatesOnMainThread = new ArrayList<>();
}
updatesOnMainThread.add(baseUpdate);
} else if (baseUpdate instanceof TLRPC.TL_updateNewAuthorization) {
if (updatesOnMainThread == null) {
updatesOnMainThread = new ArrayList<>();
}
updatesOnMainThread.add(baseUpdate);
} else if (baseUpdate instanceof TLRPC.TL_updateServiceNotification) {
TLRPC.TL_updateServiceNotification update = (TLRPC.TL_updateServiceNotification) baseUpdate;
if (update.popup && update.message != null && update.message.length() > 0) {
@ -15827,6 +15896,8 @@ public class MessagesController extends BaseController implements NotificationCe
}
getMessagesStorage().updateMutedDialogsFiltersCounters();
}
} else if (baseUpdate instanceof TLRPC.TL_updateNewAuthorization) {
getUnconfirmedAuthController().processUpdate((TLRPC.TL_updateNewAuthorization) baseUpdate);
} else if (baseUpdate instanceof TLRPC.TL_updateChannel) {
TLRPC.TL_updateChannel update = (TLRPC.TL_updateChannel) baseUpdate;
TLRPC.Dialog dialog = dialogs_dict.get(-update.channel_id);
@ -16591,11 +16662,11 @@ public class MessagesController extends BaseController implements NotificationCe
getTopicsController().updateReadOutbox(topicsReadOutbox);
}
if (markContentAsReadMessages != null) {
int time = getConnectionsManager().getCurrentTime();
int currentTime2 = getConnectionsManager().getCurrentTime();
for (int a = 0, size = markContentAsReadMessages.size(); a < size; a++) {
long key = markContentAsReadMessages.keyAt(a);
ArrayList<Integer> arrayList = markContentAsReadMessages.valueAt(a);
getMessagesStorage().markMessagesContentAsRead(key, arrayList, time);
getMessagesStorage().markMessagesContentAsRead(key, arrayList, currentTime2, markContentAsReadMessagesDate);
}
}
if (deletedMessages != null) {
@ -18372,6 +18443,19 @@ public class MessagesController extends BaseController implements NotificationCe
return storiesController;
}
public UnconfirmedAuthController getUnconfirmedAuthController() {
if (unconfirmedAuthController != null) {
return unconfirmedAuthController;
}
synchronized (lockObjects[currentAccount]) {
if (unconfirmedAuthController != null) {
return unconfirmedAuthController;
}
unconfirmedAuthController = new UnconfirmedAuthController(currentAccount);
}
return unconfirmedAuthController;
}
public boolean storiesEnabled() {
switch (storiesPosting) {
case "premium":

View file

@ -15,6 +15,7 @@ import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseIntArray;
@ -95,7 +96,7 @@ public class MessagesStorage extends BaseController {
}
}
public final static int LAST_DB_VERSION = 129;
public final static int LAST_DB_VERSION = 133;
private boolean databaseMigrationInProgress;
public boolean showClearDatabaseAlert;
private LongSparseIntArray dialogIsForum = new LongSparseIntArray();
@ -606,6 +607,8 @@ public class MessagesStorage extends BaseController {
database.executeFast("CREATE TABLE requested_holes(uid INTEGER, seq_out_start INTEGER, seq_out_end INTEGER, PRIMARY KEY (uid, seq_out_start, seq_out_end));").stepThis().dispose();
database.executeFast("CREATE TABLE sharing_locations(uid INTEGER PRIMARY KEY, mid INTEGER, date INTEGER, period INTEGER, message BLOB, proximity INTEGER);").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS stickers_featured_emoji_index ON stickers_featured(emoji);").stepThis().dispose();
database.executeFast("CREATE TABLE shortcut_widget(id INTEGER, did INTEGER, ord INTEGER, PRIMARY KEY (id, did));").stepThis().dispose();
database.executeFast("CREATE INDEX IF NOT EXISTS shortcut_widget_did ON shortcut_widget(did);").stepThis().dispose();
@ -672,16 +675,17 @@ public class MessagesStorage extends BaseController {
database.executeFast("CREATE TABLE emoji_groups(type INTEGER PRIMARY KEY, data BLOB)").stepThis().dispose();
database.executeFast("CREATE TABLE app_config(data BLOB)").stepThis().dispose();
database.executeFast("CREATE TABLE stories (dialog_id INTEGER, story_id INTEGER, data BLOB, local_path TEXT, local_thumb_path TEXT, custom_params BLOB, PRIMARY KEY (dialog_id, story_id));").stepThis().dispose();
database.executeFast("CREATE TABLE stories (dialog_id INTEGER, story_id INTEGER, data BLOB, custom_params BLOB, PRIMARY KEY (dialog_id, story_id));").stepThis().dispose();
database.executeFast("CREATE TABLE stories_counter (dialog_id INTEGER PRIMARY KEY, count INTEGER, max_read INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE profile_stories (dialog_id INTEGER, story_id INTEGER, data BLOB, PRIMARY KEY(dialog_id, story_id));").stepThis().dispose();
database.executeFast("CREATE TABLE archived_stories (story_id INTEGER PRIMARY KEY, data BLOB);").stepThis().dispose();
database.executeFast("CREATE TABLE profile_stories (dialog_id INTEGER, story_id INTEGER, data BLOB, type INTEGER, PRIMARY KEY(dialog_id, story_id));").stepThis().dispose();
database.executeFast("CREATE TABLE story_drafts (id INTEGER PRIMARY KEY, date INTEGER, data BLOB, type INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE story_pushes (uid INTEGER, sid INTEGER, date INTEGER, localName TEXT, flags INTEGER, expire_date INTEGER, PRIMARY KEY(uid, sid));").stepThis().dispose();
database.executeFast("CREATE TABLE unconfirmed_auth (data BLOB);").stepThis().dispose();
database.executeFast("PRAGMA user_version = " + MessagesStorage.LAST_DB_VERSION).stepThis().dispose();
}
@ -1033,6 +1037,7 @@ public class MessagesStorage extends BaseController {
AndroidUtilities.runOnUIThread(() -> getMessagesController().markMessageAsRead2(-channelId, mid, inputChannel, ttl, taskId));
break;
}
case 101:
case 23: {
TLRPC.InputChannel inputChannel;
long dialogId = data.readInt64(false);
@ -1043,7 +1048,7 @@ public class MessagesStorage extends BaseController {
} else {
inputChannel = null;
}
AndroidUtilities.runOnUIThread(() -> getMessagesController().markMessageAsRead2(dialogId, mid, inputChannel, ttl, taskId));
AndroidUtilities.runOnUIThread(() -> getMessagesController().markMessageAsRead2(dialogId, mid, inputChannel, ttl, taskId, type == 23));
break;
}
case 12:
@ -1126,6 +1131,12 @@ public class MessagesStorage extends BaseController {
AndroidUtilities.runOnUIThread(() -> getSecretChatHelper().declineSecretChat(chatId, revoke, taskId));
break;
}
case 102: {
long dialogId = data.readInt64(false);
int mid = data.readInt32(false);
AndroidUtilities.runOnUIThread(() -> getMessagesController().doDeleteShowOnceTask(taskId, dialogId, mid));
break;
}
}
data.reuse();
}
@ -1319,7 +1330,6 @@ public class MessagesStorage extends BaseController {
database.executeFast("DELETE FROM chat_pinned_v2").stepThis().dispose();
database.executeFast("DELETE FROM chat_pinned_count").stepThis().dispose();
database.executeFast("DELETE FROM profile_stories").stepThis().dispose();
database.executeFast("DELETE FROM archived_stories").stepThis().dispose();
database.executeFast("DELETE FROM story_pushes").stepThis().dispose();
cursor = database.queryFinalized("SELECT did FROM dialogs WHERE 1");
@ -5024,7 +5034,7 @@ public class MessagesStorage extends BaseController {
AndroidUtilities.runOnUIThread(() -> {
if (!inner) {
markMessagesContentAsRead(dialogId, midsArray, 0);
markMessagesContentAsRead(dialogId, midsArray, 0, 0);
}
getNotificationCenter().postNotificationName(NotificationCenter.messagesReadContent, dialogId, midsArray);
});
@ -5056,6 +5066,59 @@ public class MessagesStorage extends BaseController {
});
}
private void createTaskForSecretMedia(long dialogId, SparseArray<ArrayList<Integer>> messages) {
SQLiteCursor cursor = null;
SQLitePreparedStatement state = null;
try {
int minDate = Integer.MAX_VALUE;
// if (random_ids != null) {
// AndroidUtilities.runOnUIThread(() -> {
// markMessagesContentAsRead(dialogId, mids, 0, 0);
// getNotificationCenter().postNotificationName(NotificationCenter.messagesReadContent, dialogId, mids);
// });
// }
ArrayList<Integer> mids = new ArrayList<>();
if (messages.size() != 0) {
database.beginTransaction();
state = database.executeFast("REPLACE INTO enc_tasks_v4 VALUES(?, ?, ?, ?)");
for (int a = 0; a < messages.size(); a++) {
int key = messages.keyAt(a);
ArrayList<Integer> arr = messages.get(key);
for (int b = 0; b < arr.size(); b++) {
int date = arr.get(b);
state.requery();
state.bindInteger(1, date);
state.bindLong(2, dialogId);
state.bindInteger(3, key);
state.bindInteger(4, 1);
minDate = Math.min(minDate, date);
state.step();
mids.add(arr.get(b));
}
}
state.dispose();
state = null;
database.commitTransaction();
database.executeFast(String.format(Locale.US, "UPDATE messages_v2 SET ttl = 0 WHERE uid = %d AND mid IN(%s)", dialogId, TextUtils.join(", ", mids))).stepThis().dispose();
getMessagesController().didAddedNewTask(minDate, dialogId, messages);
}
} catch (Exception e) {
checkSQLException(e);
} finally {
if (database != null) {
database.commitTransaction();
}
if (state != null) {
state.dispose();
}
if (cursor != null) {
cursor.dispose();
}
}
}
public void createTaskForSecretChat(int chatId, int time, int readTime, int isOut, ArrayList<Long> random_ids) {
storageQueue.postRunnable(() -> {
SQLiteCursor cursor = null;
@ -5099,7 +5162,7 @@ public class MessagesStorage extends BaseController {
if (random_ids != null) {
AndroidUtilities.runOnUIThread(() -> {
markMessagesContentAsRead(dialogId, midsArray, 0);
markMessagesContentAsRead(dialogId, midsArray, 0, 0);
getNotificationCenter().postNotificationName(NotificationCenter.messagesReadContent, dialogId, midsArray);
});
}
@ -9769,10 +9832,10 @@ public class MessagesStorage extends BaseController {
data.reuse();
if (messageMedia.document != null) {
downloadObject.object = messageMedia.document;
downloadObject.secret = MessageObject.isVideoDocument(messageMedia.document) && messageMedia.ttl_seconds > 0 && messageMedia.ttl_seconds <= 60;
downloadObject.secret = MessageObject.isVideoDocument(messageMedia.document) && (messageMedia.ttl_seconds > 0 && messageMedia.ttl_seconds <= 60 || messageMedia.ttl_seconds == 0x7FFFFFFF);
} else if (messageMedia.photo != null) {
downloadObject.object = messageMedia.photo;
downloadObject.secret = messageMedia.ttl_seconds > 0 && messageMedia.ttl_seconds <= 60;
downloadObject.secret = messageMedia.ttl_seconds > 0 && messageMedia.ttl_seconds <= 60 || messageMedia.ttl_seconds == 0x7FFFFFFF;
}
downloadObject.forceCache = (messageMedia.flags & 0x80000000) != 0;
}
@ -12061,7 +12124,7 @@ public class MessagesStorage extends BaseController {
}
}
public void markMessagesContentAsRead(long dialogId, ArrayList<Integer> mids, int date) {
public void markMessagesContentAsRead(long dialogId, ArrayList<Integer> mids, int currentDate, int readDate) {
if (isEmpty(mids)) {
return;
}
@ -12070,21 +12133,39 @@ public class MessagesStorage extends BaseController {
if (dialogId == 0) {
SQLiteCursor cursor = null;
try {
LongSparseArray<ArrayList<Integer>> sparseArray = new LongSparseArray<>();
cursor = database.queryFinalized(String.format(Locale.US, "SELECT uid, mid FROM messages_v2 WHERE mid IN (%s) AND is_channel = 0", TextUtils.join(",", mids)));
LongSparseArray<ArrayList<Integer>> toDelete = new LongSparseArray<>();
LongSparseArray<SparseArray<ArrayList<Integer>>> toTask = new LongSparseArray<>();
cursor = database.queryFinalized(String.format(Locale.US, "SELECT uid, mid, ttl FROM messages_v2 WHERE mid IN (%s) AND is_channel = 0", TextUtils.join(",", mids)));
while (cursor.next()) {
long did = cursor.longValue(0);
ArrayList<Integer> arrayList = sparseArray.get(did);
if (arrayList == null) {
arrayList = new ArrayList<>();
sparseArray.put(did, arrayList);
int mid = cursor.intValue(1);
int ttl = cursor.intValue(2);
if (ttl <= 0 || ttl == 0x7FFFFFFF || readDate == 0 || readDate + ttl < currentDate) {
ArrayList<Integer> arrayList = toDelete.get(did);
if (arrayList == null) {
toDelete.put(did, arrayList = new ArrayList<>());
}
arrayList.add(mid);
} else {
int date = readDate + ttl;
SparseArray<ArrayList<Integer>> array = toTask.get(did);
if (array == null) {
toTask.put(did, array = new SparseArray<>());
}
ArrayList<Integer> msgs = array.get(date);
if (msgs == null) {
array.put(date, msgs = new ArrayList<>());
}
msgs.add(mid);
}
arrayList.add(cursor.intValue(1));
}
cursor.dispose();
cursor = null;
for (int a = 0, N = sparseArray.size(); a < N; a++) {
markMessagesContentAsReadInternal(sparseArray.keyAt(a), sparseArray.valueAt(a), date);
for (int a = 0, N = toDelete.size(); a < N; a++) {
markMessagesContentAsReadInternal(toDelete.keyAt(a), toDelete.valueAt(a), currentDate);
}
for (int a = 0, N = toTask.size(); a < N; a++) {
createTaskForSecretMedia(toTask.keyAt(a), toTask.valueAt(a));
}
} catch (Exception e) {
checkSQLException(e);
@ -12094,7 +12175,7 @@ public class MessagesStorage extends BaseController {
}
}
} else {
markMessagesContentAsReadInternal(dialogId, mids, date);
markMessagesContentAsReadInternal(dialogId, mids, currentDate);
}
});
}
@ -15347,6 +15428,17 @@ public class MessagesStorage extends BaseController {
return users;
}
public ArrayList<TLRPC.Chat> getChats(ArrayList<Long> dids) {
ArrayList<TLRPC.Chat> chats = new ArrayList<>();
try {
getChatsInternal(TextUtils.join(",", dids), chats);
} catch (Exception e) {
chats.clear();
checkSQLException(e);
}
return chats;
}
public TLRPC.Chat getChat(long chatId) {
TLRPC.Chat chat = null;
try {

View file

@ -22,7 +22,7 @@ import java.util.zip.ZipFile;
public class NativeLoader {
private final static int LIB_VERSION = 45;
private final static int LIB_VERSION = 46;
private final static String LIB_NAME = "tmessages." + LIB_VERSION;
private final static String LIB_SO_NAME = "lib" + LIB_NAME + ".so";
private final static String LOCALE_LIB_SO_NAME = "lib" + LIB_NAME + "loc.so";
@ -125,7 +125,7 @@ public class NativeLoader {
return;
} catch (Error e) {
FileLog.e(e);
log.append("129: ").append(e).append("\n");
log.append("128: ").append(e).append("\n");
}
String folder = getAbiFolder();
@ -173,7 +173,7 @@ public class NativeLoader {
}
} catch (Throwable e) {
e.printStackTrace();
log.append("177: ").append(e).append("\n");
log.append("176: ").append(e).append("\n");
}
try {
@ -181,7 +181,7 @@ public class NativeLoader {
nativeLoaded = true;
} catch (Error e) {
FileLog.e(e);
log.append("185: ").append(e).append("\n");
log.append("184: ").append(e).append("\n");
}
}

View file

@ -214,6 +214,8 @@ public class NotificationCenter {
public static final int storiesEnabledUpdate = totalEvents++;
public static final int storiesBlocklistUpdate = totalEvents++;
public static final int storiesLimitUpdate = totalEvents++;
public static final int storiesSendAsUpdate = totalEvents++;
public static final int unconfirmedAuthUpdate = totalEvents++;
//global
public static final int pushMessagesUpdated = totalEvents++;

View file

@ -934,8 +934,6 @@ public class NotificationsController extends BaseController {
storyPushMessagesDict.put(dialogId, notification);
getMessagesStorage().putStoryPushMessage(notification);
}
TLRPC.TL_updateStory updateStory = new TLRPC.TL_updateStory();
updateStory.story = new TLRPC.TL_storyItemSkipped();
Collections.sort(storyPushMessages, Comparator.comparingLong(n -> n.date));
continue;

View file

@ -7808,7 +7808,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
accountInstance.getUserConfig().saveConfig(false);
TLRPC.TL_documentAttributeVideo attributeVideo;
if (isEncrypted) {
attributeVideo = new TLRPC.TL_documentAttributeVideo();
attributeVideo = new TLRPC.TL_documentAttributeVideo_layer159();
} else {
attributeVideo = new TLRPC.TL_documentAttributeVideo();
attributeVideo.supports_streaming = true;
@ -8481,7 +8481,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
if (encryptedChat == null) {
return;
}
attributeVideo = new TLRPC.TL_documentAttributeVideo();
attributeVideo = new TLRPC.TL_documentAttributeVideo_layer159();
} else {
attributeVideo = new TLRPC.TL_documentAttributeVideo();
attributeVideo.supports_streaming = true;

View file

@ -144,6 +144,14 @@ public class SharedConfig {
.apply();
}
public static void togglePhotoViewerBlur() {
photoViewerBlur = !photoViewerBlur;
ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE)
.edit()
.putBoolean("photoViewerBlur", photoViewerBlur)
.apply();
}
private static String goodHevcEncoder;
private static HashSet<String> hevcEncoderWhitelist = new HashSet<>();
static {
@ -237,6 +245,7 @@ public class SharedConfig {
public static boolean updateStickersOrderOnSend = true;
public static boolean bigCameraForRound;
public static boolean useSurfaceInStories;
public static boolean photoViewerBlur = true;
public static int stealthModeSendMessageConfirm = 2;
private static int lastLocalId = -210000;
@ -615,6 +624,9 @@ public class SharedConfig {
dayNightWallpaperSwitchHint = preferences.getInt("dayNightWallpaperSwitchHint", 0);
bigCameraForRound = preferences.getBoolean("bigCameraForRound", false);
useSurfaceInStories = preferences.getBoolean("useSurfaceInStories", Build.VERSION.SDK_INT >= 30);
photoViewerBlur = preferences.getBoolean("photoViewerBlur", true);
loadDebugConfig(preferences);
preferences = ApplicationLoader.applicationContext.getSharedPreferences("Notifications", Activity.MODE_PRIVATE);
showNotificationsForAllAccounts = preferences.getBoolean("AllAccounts", true);
@ -1681,4 +1693,20 @@ public class SharedConfig {
}
return legacyDevicePerformanceClass;
}
//DEBUG
public static boolean drawActionBarShadow = true;
private static void loadDebugConfig(SharedPreferences preferences) {
drawActionBarShadow = preferences.getBoolean("drawActionBarShadow", true);
}
public static void saveDebugConfig() {
SharedPreferences pref = ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE);
pref.edit().putBoolean("drawActionBarShadow", drawActionBarShadow);
}
}

View file

@ -0,0 +1,332 @@
package org.telegram.messenger;
import android.content.Context;
import org.telegram.SQLite.SQLiteCursor;
import org.telegram.SQLite.SQLiteDatabase;
import org.telegram.SQLite.SQLitePreparedStatement;
import org.telegram.tgnet.AbstractSerializedData;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.NativeByteBuffer;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.BottomSheet;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Locale;
public class UnconfirmedAuthController {
private final int currentAccount;
public UnconfirmedAuthController(int currentAccount) {
this.currentAccount = currentAccount;
readCache();
}
public final ArrayList<UnconfirmedAuth> auths = new ArrayList<>();
private boolean fetchedCache, fetchingCache;
private boolean saveAfterFetch;
public void readCache() {
if (fetchedCache || fetchingCache) {
return;
}
fetchingCache = true;
MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> {
final HashSet<Long> hashes = new HashSet<>();
final ArrayList<UnconfirmedAuth> result = new ArrayList<>();
SQLiteDatabase database = MessagesStorage.getInstance(currentAccount).getDatabase();
SQLiteCursor cursor = null;
try {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM unconfirmed_auth"));
while (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
try {
UnconfirmedAuth auth = new UnconfirmedAuth(data);
result.add(auth);
hashes.add(auth.hash);
} catch (Exception e) {
FileLog.e(e);
}
}
}
} catch (Exception e) {
FileLog.e(e);
} finally {
if (cursor != null) {
cursor.dispose();
cursor = null;
}
}
AndroidUtilities.runOnUIThread(() -> {
boolean wasEmpty = auths.isEmpty();
for (int i = 0; i < auths.size(); ++i) {
UnconfirmedAuth existingAuth = auths.get(i);
if (existingAuth == null || existingAuth.expired() || hashes.contains(existingAuth.hash)) {
auths.remove(i);
i--;
}
}
auths.addAll(result);
boolean isEmpty = auths.isEmpty();
fetchedCache = true;
fetchingCache = false;
if (wasEmpty != isEmpty) {
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.unconfirmedAuthUpdate);
}
scheduleAuthExpireCheck();
if (saveAfterFetch) {
saveAfterFetch = false;
saveCache();
}
});
});
}
private void scheduleAuthExpireCheck() {
AndroidUtilities.cancelRunOnUIThread(checkExpiration);
if (auths.isEmpty()) {
return;
}
long minTime = Long.MAX_VALUE;
for (UnconfirmedAuth auth : auths) {
minTime = Math.min(minTime, auth.expiresAfter());
}
if (minTime == Long.MAX_VALUE) {
return;
}
AndroidUtilities.runOnUIThread(checkExpiration, Math.max(0, minTime * 1000L));
}
private final Runnable checkExpiration = () -> {
for (int i = 0; i < auths.size(); ++i) {
UnconfirmedAuth auth = auths.get(i);
if (auth.expired()) {
auths.remove(i);
i--;
}
}
saveCache();
};
private boolean debug = false;
public void putDebug() {
debug = true;
TLRPC.TL_updateNewAuthorization update = new TLRPC.TL_updateNewAuthorization();
update.unconfirmed = true;
update.device = "device";
update.location = "location";
update.hash = 123;
processUpdate(update);
}
public void processUpdate(TLRPC.TL_updateNewAuthorization update) {
for (int i = 0; i < auths.size(); ++i) {
UnconfirmedAuth auth = auths.get(i);
if (auth != null && auth.hash == update.hash) {
auths.remove(i);
i--;
}
}
if (update.unconfirmed) {
auths.add(new UnconfirmedAuth(update));
}
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.unconfirmedAuthUpdate);
scheduleAuthExpireCheck();
saveCache();
}
private boolean savingCache;
public void saveCache() {
if (savingCache) {
return;
}
if (fetchingCache) {
saveAfterFetch = true;
return;
}
savingCache = true;
MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> {
SQLiteDatabase database = MessagesStorage.getInstance(currentAccount).getDatabase();
SQLitePreparedStatement state = null;
try {
database.executeFast("DELETE FROM unconfirmed_auth WHERE 1").stepThis().dispose();
state = database.executeFast("REPLACE INTO unconfirmed_auth VALUES(?)");
for (UnconfirmedAuth auth : auths) {
state.requery();
NativeByteBuffer buffer = new NativeByteBuffer(auth.getObjectSize());
auth.serializeToStream(buffer);
state.bindByteBuffer(1, buffer);
state.step();
}
} catch (Exception e) {
FileLog.e(e);
} finally {
if (state != null) {
state.dispose();
state = null;
}
}
AndroidUtilities.runOnUIThread(() -> {
savingCache = false;
});
});
}
public void cleanup() {
auths.clear();
saveCache();
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.unconfirmedAuthUpdate);
scheduleAuthExpireCheck();
}
private void updateList(boolean confirm, ArrayList<UnconfirmedAuth> _list, Utilities.Callback<ArrayList<UnconfirmedAuth>> whenDone) {
final ArrayList<UnconfirmedAuth> list = new ArrayList<>(_list);
final boolean[] results = new boolean[list.size()];
final Utilities.Callback<Runnable>[] callbacks = new Utilities.Callback[list.size()];
for (int i = 0; i < list.size(); ++i) {
final int a = i;
final UnconfirmedAuth auth = list.get(a);
callbacks[a] = finish -> {
Utilities.Callback<Boolean> next = success -> { results[a] = success; finish.run(); };
if (confirm) {
auth.confirm(next);
} else {
auth.deny(next);
}
};
}
Utilities.raceCallbacks(
() -> {
final HashSet<Long> hashes = new HashSet<>();
final ArrayList<UnconfirmedAuth> success = new ArrayList<>();
for (int i = 0; i < results.length; ++i) {
if (results[i]) {
UnconfirmedAuth auth = list.get(i);
success.add(auth);
hashes.add(auth.hash);
}
}
if (!confirm) {
for (int i = 0; i < auths.size(); ++i) {
if (hashes.contains(auths.get(i).hash)) {
auths.remove(i);
i--;
}
}
if (!hashes.isEmpty()) {
saveCache();
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.unconfirmedAuthUpdate);
scheduleAuthExpireCheck();
}
}
whenDone.run(success);
},
callbacks
);
if (confirm) {
final HashSet<Long> hashes = new HashSet<>();
for (int i = 0; i < list.size(); ++i) {
hashes.add(list.get(i).hash);
}
for (int i = 0; i < auths.size(); ++i) {
if (hashes.contains(auths.get(i).hash)) {
auths.remove(i);
i--;
}
}
if (!hashes.isEmpty()) {
saveCache();
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.unconfirmedAuthUpdate);
scheduleAuthExpireCheck();
}
}
}
public void confirm(ArrayList<UnconfirmedAuth> list, Utilities.Callback<ArrayList<UnconfirmedAuth>> whenDone) {
updateList(true, list, whenDone);
}
public void deny(ArrayList<UnconfirmedAuth> list, Utilities.Callback<ArrayList<UnconfirmedAuth>> whenDone) {
updateList(false, list, whenDone);
}
public class UnconfirmedAuth extends TLObject {
public long hash;
public int date;
public String device;
public String location;
public UnconfirmedAuth(AbstractSerializedData stream) {
int magic = stream.readInt32(true);
if (magic != 0x7ab6618c) {
throw new RuntimeException("UnconfirmedAuth can't parse magic " + Integer.toHexString(magic));
}
hash = stream.readInt64(true);
date = stream.readInt32(true);
device = stream.readString(true);
location = stream.readString(true);
}
public UnconfirmedAuth(TLRPC.TL_updateNewAuthorization update) {
hash = update.hash;
date = update.date;
device = update.device;
location = update.location;
}
@Override
public void serializeToStream(AbstractSerializedData stream) {
stream.writeInt32(0x7ab6618c);
stream.writeInt64(hash);
stream.writeInt32(date);
stream.writeString(device);
stream.writeString(location);
}
public long expiresAfter() {
return ConnectionsManager.getInstance(currentAccount).getCurrentTime() + MessagesController.getInstance(currentAccount).authorizationAutoconfirmPeriod - date;
}
public boolean expired() {
return expiresAfter() <= 0;
}
public void confirm(Utilities.Callback<Boolean> whenDone) {
TLRPC.TL_account_changeAuthorizationSettings req = new TLRPC.TL_account_changeAuthorizationSettings();
req.hash = hash;
req.confirmed = true;
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> {
AndroidUtilities.runOnUIThread(() -> {
if (whenDone != null) {
whenDone.run(res instanceof TLRPC.TL_boolTrue && err == null || debug);
debug = false;
}
});
});
}
public void deny(Utilities.Callback<Boolean> whenDone) {
TLRPC.TL_account_resetAuthorization req = new TLRPC.TL_account_resetAuthorization();
req.hash = hash;
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (res, err) -> {
AndroidUtilities.runOnUIThread(() -> {
if (whenDone != null) {
whenDone.run(res instanceof TLRPC.TL_boolTrue && err == null || debug);
debug = false;
}
});
});
}
}
}

View file

@ -22,7 +22,9 @@ import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -516,10 +518,22 @@ public class Utilities {
public void run(T arg);
}
public static interface CallbackVoidReturn<ReturnType> {
public ReturnType run();
}
public static interface CallbackReturn<Arg, ReturnType> {
public ReturnType run(Arg arg);
}
public static interface Callback2Return<T1, T2, ReturnType> {
public ReturnType run(T1 arg, T2 arg2);
}
public static interface Callback3Return<T1, T2, T3, ReturnType> {
public ReturnType run(T1 arg, T2 arg2, T3 arg3);
}
public static interface Callback2<T, T2> {
public void run(T arg, T2 arg2);
}
@ -531,6 +545,9 @@ public class Utilities {
public static interface Callback4<T, T2, T3, T4> {
public void run(T arg, T2 arg2, T3 arg3, T4 arg4);
}
public static interface Callback5<T, T2, T3, T4, T5> {
public void run(T arg, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
}
public static <Key, Value> Value getOrDefault(HashMap<Key, Value> map, Key key, Value defaultValue) {
Value v = map.get(key);
@ -576,4 +593,9 @@ public class Utilities {
}
return videoPlayerQueue;
}
public static boolean isNullOrEmpty(final Collection<?> list) {
return list == null || list.isEmpty();
}
}

View file

@ -13,6 +13,7 @@ import android.graphics.Canvas;
import android.text.TextUtils;
import android.view.View;
import org.telegram.messenger.video.MediaCodecVideoConvertor;
import org.telegram.tgnet.AbstractSerializedData;
import org.telegram.tgnet.SerializedData;
import org.telegram.tgnet.TLRPC;
@ -20,6 +21,8 @@ import org.telegram.ui.Components.AnimatedFileDrawable;
import org.telegram.ui.Components.Paint.PaintTypeface;
import org.telegram.ui.Components.PhotoFilterView;
import org.telegram.ui.Components.Point;
import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble;
import org.telegram.ui.Components.Reactions.ReactionsUtils;
import org.telegram.ui.Stories.recorder.StoryEntry;
import java.util.ArrayList;
@ -52,7 +55,7 @@ public class VideoEditedInfo {
public byte[] key;
public byte[] iv;
public MediaController.SavedFilterState filterState;
public String paintPath;
public String paintPath, blurPath;
public ArrayList<MediaEntity> mediaEntities;
public MediaController.CropState cropState;
public boolean isPhoto;
@ -72,6 +75,8 @@ public class VideoEditedInfo {
public boolean tryUseHevc = false;
public boolean fromCamera;
public ArrayList<MediaCodecVideoConvertor.MixedSoundInfo> mixedSoundInfos = new ArrayList<>();
public static class EmojiEntity extends TLRPC.TL_messageEntityCustomEmoji {
public String documentAbsolutePath;
@ -108,6 +113,7 @@ public class VideoEditedInfo {
public static final int TYPE_TEXT = 1;
public static final int TYPE_PHOTO = 2;
public static final int TYPE_LOCATION = 3;
public static final byte TYPE_REACTION = 4;
public byte type;
public byte subType;
@ -117,7 +123,7 @@ public class VideoEditedInfo {
public float width;
public float height;
public float additionalWidth, additionalHeight;
public String text;
public String text = "";
public ArrayList<EmojiEntity> entities = new ArrayList<>();
public int color;
public int fontSize;
@ -153,6 +159,7 @@ public class VideoEditedInfo {
public float density;
public int W, H;
public ReactionsLayoutInBubble.VisibleReaction visibleReaction;
public MediaEntity() {
@ -207,6 +214,9 @@ public class VideoEditedInfo {
}
}
}
if (type == TYPE_REACTION) {
mediaArea = TLRPC.MediaArea.TLdeserialize(data, data.readInt32(false), false);
}
}
public void serializeTo(AbstractSerializedData data, boolean full) {
@ -260,6 +270,9 @@ public class VideoEditedInfo {
data.writeInt32(TLRPC.TL_null.constructor);
}
}
if (type == TYPE_REACTION) {
mediaArea.serializeToStream(data);
}
}
public MediaEntity copy() {
@ -290,7 +303,7 @@ public class VideoEditedInfo {
public String getString() {
String filters;
if (avatarStartTime != -1 || filterState != null || paintPath != null || mediaEntities != null && !mediaEntities.isEmpty() || cropState != null) {
if (avatarStartTime != -1 || filterState != null || paintPath != null || blurPath != null || mediaEntities != null && !mediaEntities.isEmpty() || cropState != null) {
int len = 10;
if (filterState != null) {
len += 160;
@ -302,8 +315,15 @@ public class VideoEditedInfo {
} else {
paintPathBytes = null;
}
byte[] blurPathBytes;
if (blurPath != null) {
blurPathBytes = blurPath.getBytes();
len += blurPathBytes.length;
} else {
blurPathBytes = null;
}
SerializedData serializedData = new SerializedData(len);
serializedData.writeInt32(7);
serializedData.writeInt32(8);
serializedData.writeInt64(avatarStartTime);
serializedData.writeInt32(originalBitrate);
if (filterState != null) {
@ -395,6 +415,12 @@ public class VideoEditedInfo {
}
serializedData.writeBool(isStory);
serializedData.writeBool(fromCamera);
if (blurPathBytes != null) {
serializedData.writeByte(1);
serializedData.writeByteArray(blurPathBytes);
} else {
serializedData.writeByte(0);
}
filters = Utilities.bytesToHex(serializedData.toByteArray());
serializedData.cleanup();
} else {
@ -519,6 +545,13 @@ public class VideoEditedInfo {
isStory = serializedData.readBool(false);
fromCamera = serializedData.readBool(false);
}
if (version >= 8) {
has = serializedData.readByte(false);
if (has != 0) {
byte[] bytes = serializedData.readByteArray(false);
blurPath = new String(bytes);
}
}
serializedData.cleanup();
}
} else {
@ -545,9 +578,9 @@ public class VideoEditedInfo {
if (!fromCamera) {
return true;
}
return mediaEntities != null || paintPath != null || filterState != null || (cropState != null && !cropState.isEmpty()) || startTime > 0 || endTime != -1 && endTime != estimatedDuration || originalHeight != resultHeight || originalWidth != resultWidth;
return !mixedSoundInfos.isEmpty() || mediaEntities != null || paintPath != null || blurPath != null || filterState != null || (cropState != null && !cropState.isEmpty()) || startTime > 0 || endTime != -1 && endTime != estimatedDuration || originalHeight != resultHeight || originalWidth != resultWidth;
}
return mediaEntities != null || paintPath != null || filterState != null || cropState != null || !roundVideo || startTime > 0 || endTime != -1 && endTime != estimatedDuration;
return !mixedSoundInfos.isEmpty() || mediaEntities != null || paintPath != null || blurPath != null || filterState != null || cropState != null || !roundVideo || startTime > 0 || endTime != -1 && endTime != estimatedDuration;
}
public boolean canAutoPlaySourceVideo() {

View file

@ -0,0 +1,88 @@
package org.telegram.messenger.video;
import androidx.annotation.NonNull;
import org.telegram.messenger.video.remix.AudioRemixer;
import org.telegram.messenger.video.remix.DefaultAudioRemixer;
import org.telegram.messenger.video.resample.AudioResampler;
import org.telegram.messenger.video.resample.DefaultAudioResampler;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
public class AudioBufferConverter {
private static final String TAG = AudioBufferConverter.class.getSimpleName();
private static final int BYTES_PER_SHORT = 2;
private final AudioRemixer mRemixer;
private final AudioResampler mResampler;
public AudioBufferConverter() {
// Create remixer and resampler.
mRemixer = new DefaultAudioRemixer();
mResampler = new DefaultAudioResampler();
}
public int calculateRequiredOutputSize(int inputSize, int inputSampleRate, int inputChannelCount,
int outputSampleRate, int outputChannelCount){
checkChannels(inputChannelCount, outputChannelCount);
int requiredOutputSize = inputSize;
// Ask remixer how much space it needs for the given input
requiredOutputSize = mRemixer.getRemixedSize(requiredOutputSize, inputChannelCount, outputChannelCount);
// After remixing we'll resample.
// Resampling will change the input size based on the sample rate ratio.
requiredOutputSize = (int) Math.ceil((double) requiredOutputSize *
outputSampleRate / (double)inputSampleRate);
return requiredOutputSize;
}
public ShortBuffer convert(@NonNull ShortBuffer inputBuffer, int inputSampleRate, int inputChannelCount,
int outputSampleRate, int outputChannelCount) {
checkChannels(inputChannelCount, outputChannelCount);
final int inputSize = inputBuffer.remaining();
// Do the remixing.
int remixSize = mRemixer.getRemixedSize(inputSize, inputChannelCount, outputChannelCount);
ShortBuffer remixedBuffer = createBuffer(remixSize);
mRemixer.remix(inputBuffer, inputChannelCount, remixedBuffer, outputChannelCount);
remixedBuffer.rewind();
// Do the resampling.
int resampleSize = (int) Math.ceil((double) remixSize * outputSampleRate / (double)inputSampleRate);
// We add some extra values to avoid BufferOverflowException.
// Problem may occur for calculation.
// To be safe we add 10 but 1 is enough may be. Not sure.
resampleSize += 10;
ShortBuffer outputBuffer = createBuffer(resampleSize);
mResampler.resample(remixedBuffer, inputSampleRate, outputBuffer, outputSampleRate, inputChannelCount);
outputBuffer.limit(outputBuffer.position());
outputBuffer.rewind();
return outputBuffer;
}
private void checkChannels(int inputChannelCount, int outputChannelCount){
// Check channel count.
if (inputChannelCount != 1 && inputChannelCount != 2) {
throw new UnsupportedOperationException("Input channel count (" + inputChannelCount + ") not supported.");
}
if (outputChannelCount != 1 && outputChannelCount != 2) {
throw new UnsupportedOperationException("Output channel count (" + outputChannelCount + ") not supported.");
}
}
private ShortBuffer createBuffer(int capacity) {
ShortBuffer buffer = ByteBuffer.allocateDirect(capacity * BYTES_PER_SHORT)
.order(ByteOrder.nativeOrder())
.asShortBuffer();
buffer.clear();
buffer.limit(capacity);
return buffer;
}
}

View file

@ -0,0 +1,34 @@
package org.telegram.messenger.video;
public class AudioConversions {
private static final int BYTES_PER_SAMPLE_PER_CHANNEL = 2; // Assuming 16bit audio, so 2
private static final long MICROSECONDS_PER_SECOND = 1000000L;
private static final int BYTES_PER_SHORT = 2;
@SuppressWarnings("WeakerAccess")
public static long bytesToUs(
int bytes /* bytes */,
int sampleRate /* samples/sec */,
int channels /* channel */
) {
int byteRatePerChannel = sampleRate * BYTES_PER_SAMPLE_PER_CHANNEL; // bytes/sec/channel
int byteRate = byteRatePerChannel * channels; // bytes/sec
return MICROSECONDS_PER_SECOND * bytes / byteRate; // usec
}
@SuppressWarnings("WeakerAccess")
public static int usToBytes(long us, int sampleRate, int channels) {
int byteRatePerChannel = sampleRate * BYTES_PER_SAMPLE_PER_CHANNEL;
int byteRate = byteRatePerChannel * channels;
return (int) Math.ceil((double) us * byteRate / MICROSECONDS_PER_SECOND);
}
public static long shortsToUs(int shorts, int sampleRate, int channels) {
return bytesToUs(shorts * BYTES_PER_SHORT, sampleRate, channels);
}
public static int usToShorts(long us, int sampleRate, int channels) {
return usToBytes(us, sampleRate, channels) / BYTES_PER_SHORT;
}
}

View file

@ -0,0 +1,284 @@
package org.telegram.messenger.video;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.MediaCodec;
import android.media.MediaDataSource;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.net.Uri;
import android.os.Build;
import org.telegram.messenger.FileLog;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Map;
public class AudioDecoder {
private static final int TIMEOUT_USEC = 0000;
private final MediaExtractor extractor;
private MediaCodec decoder;
private int trackIndex;
private long startTimeUs;
private long endTimeUs;
private boolean loopingEnabled;
private boolean allInputExtracted;
private boolean decodingDone;
private int audioIndex = -1;
public AudioDecoder(String sourcePath) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(sourcePath);
init();
}
public AudioDecoder(String sourcePath, int audioIndex) throws IOException {
extractor = new MediaExtractor();
extractor.setDataSource(sourcePath);
this.audioIndex = audioIndex;
init();
}
private void init() throws IOException {
selectTrack();
MediaFormat inputFormat = extractor.getTrackFormat(trackIndex);
decoder = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME));
decoder.configure(inputFormat, null, null, 0);
startTimeUs = 0;
endTimeUs = getDurationUs();
}
private void selectTrack() {
trackIndex = audioIndex;
if (trackIndex == -1) {
int numTracks = extractor.getTrackCount();
for (int i = 0; i < numTracks; i++) {
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime != null && mime.startsWith("audio/")) {
trackIndex = i;
break;
}
}
}
if (trackIndex < 0) {
throw new RuntimeException("No audio track found in source");
}
extractor.selectTrack(trackIndex);
}
public MediaFormat getMediaFormat() {
try {
return extractor.getTrackFormat(trackIndex);
} catch (Exception e) {
FileLog.e(e);
}
return null;
}
public long getDurationUs() {
try {
return getMediaFormat().getLong(MediaFormat.KEY_DURATION);
} catch (Exception e) {
FileLog.e(e);
}
return -1;
}
public int getSampleRate() {
try {
return getMediaFormat().getInteger(MediaFormat.KEY_SAMPLE_RATE);
} catch (Exception e) {
FileLog.e(e);
}
return -1;
}
public int getBitrateRate() {
try {
return getMediaFormat().getInteger(MediaFormat.KEY_BIT_RATE);
} catch (Exception e) {
}
return -1;
}
public int getChannelCount() {
try {
return getMediaFormat().getInteger(MediaFormat.KEY_CHANNEL_COUNT);
} catch (Exception e) {
FileLog.e(e);
}
return -1;
}
public long getStartTimeUs() {
return startTimeUs;
}
public long getEndTimeUs() {
return endTimeUs;
}
public boolean isLoopingEnabled() {
return loopingEnabled;
}
public boolean isDecodingDone() {
return decodingDone;
}
public void setStartTimeUs(long startTimeUs) {
this.startTimeUs = startTimeUs;
long durationUs = getDurationUs();
if (startTimeUs < 0) this.startTimeUs = 0;
else if (startTimeUs > durationUs) this.startTimeUs = durationUs;
}
public void setEndTimeUs(long endTimeUs) {
this.endTimeUs = endTimeUs;
long durationUs = getDurationUs();
if (endTimeUs < 0) {
this.endTimeUs = 0;
} else {
if (endTimeUs > durationUs) this.endTimeUs = durationUs;
}
}
public void setLoopingEnabled(boolean loopingEnabled) {
this.loopingEnabled = loopingEnabled;
}
public void start() {
if (startTimeUs > endTimeUs) {
throw new RuntimeException("StartTimeUs(" + startTimeUs + ") must be less than or equal to EndTimeUs(" + endTimeUs + ")");
}
extractor.seekTo(startTimeUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
decoder.start();
allInputExtracted = false;
decodingDone = false;
}
public DecodedBufferData decode() {
DecodedBufferData data = new DecodedBufferData();
boolean currentOutputDone = false;
while (!currentOutputDone && !decodingDone) {
if (!allInputExtracted) {
int inBufferId = decoder.dequeueInputBuffer(TIMEOUT_USEC);
if (inBufferId >= 0) {
ByteBuffer buffer;
if (Build.VERSION.SDK_INT >= 21) {
buffer = decoder.getInputBuffer(inBufferId);
} else {
buffer = decoder.getInputBuffers()[inBufferId];
}
int sampleSize = extractor.readSampleData(buffer, 0);
if (sampleSize >= 0 && extractor.getSampleTime() <= endTimeUs) {
decoder.queueInputBuffer(inBufferId, 0, sampleSize, extractor.getSampleTime(), extractor.getSampleFlags());
extractor.advance();
} else {
if (loopingEnabled) {
decoder.flush();
extractor.seekTo(startTimeUs, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
} else {
decoder.queueInputBuffer(inBufferId, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
allInputExtracted = true;
}
}
}
}
MediaCodec.BufferInfo outputBufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = decoder.dequeueOutputBuffer(outputBufferInfo, TIMEOUT_USEC);
if (outputBufferIndex >= 0) {
if (Build.VERSION.SDK_INT >= 21) {
data.byteBuffer = decoder.getOutputBuffer(outputBufferIndex);
} else {
data.byteBuffer = decoder.getOutputBuffers()[outputBufferIndex];
}
data.index = outputBufferIndex;
data.size = outputBufferInfo.size;
data.presentationTimeUs = outputBufferInfo.presentationTimeUs;
data.flags = outputBufferInfo.flags;
data.offset = outputBufferInfo.offset;
// Adjusting start time
if (data.presentationTimeUs < startTimeUs) {
long timeDiff = startTimeUs - data.presentationTimeUs;
int bytesForTimeDiff = AudioConversions.usToBytes(timeDiff, getSampleRate(), getChannelCount());
int position = data.byteBuffer.position() + bytesForTimeDiff;
if (position <= data.byteBuffer.limit()) {
data.byteBuffer.position(position);
}
}
// Adjusting end time
long nextTime = data.presentationTimeUs + AudioConversions.bytesToUs(data.size, getSampleRate(), getChannelCount());
if (nextTime > endTimeUs) {
int bytesToRemove = AudioConversions.usToBytes(nextTime - endTimeUs, getSampleRate(), getChannelCount());
if (bytesToRemove > 0) {
int limit = data.byteBuffer.limit() - bytesToRemove;
if (limit >= data.byteBuffer.position()) {
data.byteBuffer.limit(limit);
}
}
}
// Did we get all output from decoder?
if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0)
decodingDone = true;
if (data.byteBuffer.remaining() > 0) currentOutputDone = true;
}
}
return data;
}
/**
* @param index last decoded output buffer's index
* <p>
* This method must be called each time after decoding and and using the ByteBuffer sample
*/
public void releaseOutputBuffer(int index) {
decoder.releaseOutputBuffer(index, false);
}
public void stop() {
decoder.stop();
decodingDone = true;
}
public void release() {
stop();
decoder.release();
extractor.release();
}
public static class DecodedBufferData {
public ByteBuffer byteBuffer = null;
public int index = -1;
public int size = 0;
public long presentationTimeUs = 0;
public int flags = 0;
public int offset = 0;
}
}

View file

@ -3,191 +3,128 @@ package org.telegram.messenger.video;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Build;
import com.google.android.exoplayer2.util.Log;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.MediaController;
import org.telegram.messenger.video.audio_input.AudioInput;
import org.telegram.messenger.video.audio_input.GeneralAudioInput;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
import java.util.ArrayList;
public class AudioRecoder {
private ByteBuffer[] decoderInputBuffers;
private ByteBuffer[] decoderOutputBuffers;
private static final int BYTES_PER_SHORT = 2;
private final int TIMEOUT_USEC = 2500;
private final int DEFAULT_SAMPLE_RATE = 44100;
private final int DEFAULT_BIT_RATE = 128000;
private final int DEFAULT_CHANNEL_COUNT = 2;
private ByteBuffer[] encoderInputBuffers;
private ByteBuffer[] encoderOutputBuffers;
private final MediaCodec.BufferInfo decoderOutputBufferInfo = new MediaCodec.BufferInfo();
private final MediaCodec.BufferInfo encoderOutputBufferInfo = new MediaCodec.BufferInfo();
private final MediaCodec decoder;
private final MediaCodec encoder;
private final MediaExtractor extractor;
private boolean extractorDone = false;
private boolean decoderDone = false;
private boolean encoderDone = false;
private boolean encoderInputDone = false;
private int pendingAudioDecoderOutputBufferIndex = -1;
private final int trackIndex;
private final int TIMEOUT_USEC = 2500;
public long startTime = 0;
public long endTime = 0;
private int sampleRate = DEFAULT_SAMPLE_RATE;
private int channelCount = DEFAULT_CHANNEL_COUNT;
public final MediaFormat format;
public AudioRecoder(MediaFormat inputAudioFormat, MediaExtractor extractor, int trackIndex) throws IOException {
this.extractor = extractor;
this.trackIndex = trackIndex;
AudioInput mainInput;
ArrayList<AudioInput> audioInputs;
private long encoderInputPresentationTimeUs = 0;
private boolean encoderDone;
private long totalDurationUs;
decoder = MediaCodec.createDecoderByType(inputAudioFormat.getString(MediaFormat.KEY_MIME));
decoder.configure(inputAudioFormat, null, null, 0);
decoder.start();
public AudioRecoder(ArrayList<AudioInput> audioInputs, long totalDurationUs) throws IOException {
this.audioInputs = audioInputs;
this.totalDurationUs = totalDurationUs;
mainInput = audioInputs.get(0);
for (int i = 0; i < audioInputs.size(); i++) {
if (audioInputs.get(i).getSampleRate() > sampleRate) {
sampleRate = audioInputs.get(i).getSampleRate();
}
}
encoder = MediaCodec.createEncoderByType(MediaController.AUIDO_MIME_TYPE);
format = MediaFormat.createAudioFormat(MediaController.AUIDO_MIME_TYPE,
inputAudioFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE),
inputAudioFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
sampleRate,
channelCount
);
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
encoder.start();
decoderInputBuffers = decoder.getInputBuffers();
decoderOutputBuffers = decoder.getOutputBuffers();
encoderInputBuffers = encoder.getInputBuffers();
encoderOutputBuffers = encoder.getOutputBuffers();
for (int i = 0; i < audioInputs.size(); i++) {
audioInputs.get(i).start(sampleRate, channelCount);
}
}
public void release() {
try {
encoder.stop();
decoder.stop();
extractor.unselectTrack(trackIndex);
extractor.release();
for (int i = 0; i < audioInputs.size(); i++) {
audioInputs.get(i).release();
}
} catch (Exception e) {
FileLog.e(e);
}
}
public boolean step(MP4Builder muxer, int audioTrackIndex) throws Exception {
while (!extractorDone) {
int decoderInputBufferIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
if (decoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
break;
}
if (!encoderInputDone) {
int encoderBufferIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
if (encoderBufferIndex >= 0) {
if (isInputAvailable()) {
ShortBuffer encoderBuffer;
if (Build.VERSION.SDK_INT >= 21) {
encoderBuffer = encoder.getInputBuffer(encoderBufferIndex).asShortBuffer();
} else {
encoderBuffer = encoder.getInputBuffers()[encoderBufferIndex].asShortBuffer();
}
// mix the audios and add to encoder input buffer
mix(encoderBuffer);
ByteBuffer decoderInputBuffer;
if (android.os.Build.VERSION.SDK_INT >= 21) {
decoderInputBuffer = decoder.getInputBuffer(decoderInputBufferIndex);
} else {
decoderInputBuffer = decoderInputBuffers[decoderInputBufferIndex];
encoder.queueInputBuffer(encoderBufferIndex,
0,
encoderBuffer.position() * BYTES_PER_SHORT,
encoderInputPresentationTimeUs,
MediaCodec.BUFFER_FLAG_KEY_FRAME);
encoderInputPresentationTimeUs += AudioConversions.shortsToUs(encoderBuffer.position(), sampleRate, channelCount);
} else {
encoder.queueInputBuffer(encoderBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
encoderInputDone = true;
}
}
int size = extractor.readSampleData(decoderInputBuffer, 0);
long presentationTime = extractor.getSampleTime();
if (endTime > 0 && presentationTime >= endTime) {
encoderDone = true;
decoderOutputBufferInfo.flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
}
if (size >= 0) {
decoder.queueInputBuffer(
decoderInputBufferIndex,
0,
size,
extractor.getSampleTime(),
extractor.getSampleFlags());
}
extractorDone = !extractor.advance();
if (extractorDone) {
decoderInputBufferIndex = decoder.dequeueInputBuffer(TIMEOUT_USEC);
decoder.queueInputBuffer(
decoderInputBufferIndex,
0,
0,
0L,
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}
break;
}
while (!decoderDone && pendingAudioDecoderOutputBufferIndex == -1) {
int decoderOutputBufferIndex =
decoder.dequeueOutputBuffer(
decoderOutputBufferInfo, TIMEOUT_USEC);
if (decoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
break;
}
if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
decoderOutputBuffers = decoder.getOutputBuffers();
break;
}
if (decoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
break;
}
if ((decoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG)
!= 0) {
decoder.releaseOutputBuffer(decoderOutputBufferIndex, false);
break;
}
pendingAudioDecoderOutputBufferIndex = decoderOutputBufferIndex;
break;
}
while (pendingAudioDecoderOutputBufferIndex != -1) {
int encoderInputBufferIndex = encoder.dequeueInputBuffer(TIMEOUT_USEC);
if (encoderInputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
break;
}
ByteBuffer encoderInputBuffer = encoderInputBuffers[encoderInputBufferIndex];
int size = decoderOutputBufferInfo.size;
long presentationTime = decoderOutputBufferInfo.presentationTimeUs;
if (size >= 0) {
ByteBuffer decoderOutputBuffer =
decoderOutputBuffers[pendingAudioDecoderOutputBufferIndex]
.duplicate();
decoderOutputBuffer.position(decoderOutputBufferInfo.offset);
decoderOutputBuffer.limit(decoderOutputBufferInfo.offset + size);
encoderInputBuffer.position(0);
encoderInputBuffer.put(decoderOutputBuffer);
encoder.queueInputBuffer(
encoderInputBufferIndex,
0,
size,
presentationTime,
decoderOutputBufferInfo.flags);
}
decoder.releaseOutputBuffer(pendingAudioDecoderOutputBufferIndex, false);
pendingAudioDecoderOutputBufferIndex = -1;
if ((decoderOutputBufferInfo.flags
& MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
decoderDone = true;
}
break;
}
while (!encoderDone) {
int encoderOutputBufferIndex = encoder.dequeueOutputBuffer(
encoderOutputBufferInfo, TIMEOUT_USEC);
if (!encoderDone) {
int encoderOutputBufferIndex = encoder.dequeueOutputBuffer(encoderOutputBufferInfo, TIMEOUT_USEC);
if (encoderOutputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
break;
return encoderDone;
}
if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
encoderOutputBuffers = encoder.getOutputBuffers();
break;
}
if (encoderOutputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
break;
return encoderDone;
}
ByteBuffer encoderOutputBuffer =
@ -195,20 +132,50 @@ public class AudioRecoder {
if ((encoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG)
!= 0) {
encoder.releaseOutputBuffer(encoderOutputBufferIndex, false);
break;
return encoderDone;
}
if (encoderOutputBufferInfo.size != 0) {
muxer.writeSampleData(audioTrackIndex, encoderOutputBuffer, encoderOutputBufferInfo, false);
}
if ((encoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM)
!= 0) {
if ((encoderOutputBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
encoderDone = true;
}
encoder.releaseOutputBuffer(encoderOutputBufferIndex, false);
break;
}
return encoderDone;
}
private void mix(ShortBuffer inputBuffer) {
final int size = inputBuffer.remaining();
for (int i = 0; i < size; i++) {
if (!isInputAvailable()) break;
boolean put = false;
short result = 0;
for (int j = 0; j < audioInputs.size(); j++) {
if (!isInputAvailable()) break;
AudioInput input = audioInputs.get(j);
if (input.hasRemaining()) {
short value = input.getNext();
//controlling volume
value = (short) (value * input.getVolume());
result += value / audioInputs.size();
put = true;
}
}
if (put) inputBuffer.put(result);
}
}
private boolean isInputAvailable() {
if (encoderInputPresentationTimeUs > totalDurationUs) {
return false;
}
return mainInput.hasRemaining();
}
}

View file

@ -3,15 +3,12 @@ package org.telegram.messenger.video;
import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Build;
import androidx.annotation.NonNull;
import com.google.android.exoplayer2.util.Log;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.BuildVars;
import org.telegram.messenger.FileLog;
@ -20,6 +17,9 @@ import org.telegram.messenger.R;
import org.telegram.messenger.SharedConfig;
import org.telegram.messenger.Utilities;
import org.telegram.messenger.VideoEditedInfo;
import org.telegram.messenger.video.audio_input.AudioInput;
import org.telegram.messenger.video.audio_input.BlankAudioInput;
import org.telegram.messenger.video.audio_input.GeneralAudioInput;
import org.telegram.ui.Components.RLottieDrawable;
import org.telegram.ui.Stories.recorder.StoryEntry;
@ -48,26 +48,9 @@ public class MediaCodecVideoConvertor {
private static final int MEDIACODEC_TIMEOUT_INCREASED = 22000;
private String outputMimeType;
public boolean convertVideo(String videoPath, File cacheFile,
int rotationValue, boolean isSecret,
int originalWidth, int originalHeight,
int resultWidth, int resultHeight,
int framerate, int bitrate, int originalBitrate,
long startTime, long endTime, long avatarStartTime,
boolean needCompress, long duration,
MediaController.SavedFilterState savedFilterState,
String paintPath,
ArrayList<VideoEditedInfo.MediaEntity> mediaEntities,
boolean isPhoto,
MediaController.CropState cropState,
boolean isRound,
MediaController.VideoConvertorListener callback,
Integer gradientTopColor, Integer gradientBottomColor,
boolean muted, boolean isStory, StoryEntry.HDRInfo hdrInfo,
ArrayList<StoryEntry.Part> parts) {
this.callback = callback;
return convertVideoInternal(videoPath, cacheFile, rotationValue, isSecret, originalWidth, originalHeight,
resultWidth, resultHeight, framerate, bitrate, originalBitrate, startTime, endTime, avatarStartTime, duration, needCompress, false, savedFilterState, paintPath, mediaEntities, isPhoto, cropState, isRound, gradientTopColor, gradientBottomColor, muted, isStory, hdrInfo, parts, 0);
public boolean convertVideo(ConvertVideoParams convertStoryVideoParams) {
this.callback = convertStoryVideoParams.callback;
return convertVideoInternal(convertStoryVideoParams, false, 0);
}
public long getLastFrameTimestamp() {
@ -75,24 +58,38 @@ public class MediaCodecVideoConvertor {
}
@TargetApi(18)
private boolean convertVideoInternal(String videoPath, File cacheFile,
int rotationValue, boolean isSecret,
int originalWidth, int originalHeight,
int resultWidth, int resultHeight,
int framerate, int bitrate, int originalBitrate,
long startTime, long endTime, long avatarStartTime,
long duration,
boolean needCompress, boolean increaseTimeout,
MediaController.SavedFilterState savedFilterState,
String paintPath,
ArrayList<VideoEditedInfo.MediaEntity> mediaEntities,
boolean isPhoto,
MediaController.CropState cropState,
boolean isRound,
Integer gradientTopColor, Integer gradientBottomColor, boolean muted, boolean isStory,
StoryEntry.HDRInfo hdrInfo,
ArrayList<StoryEntry.Part> parts,
private boolean convertVideoInternal(ConvertVideoParams convertVideoParams,
boolean increaseTimeout,
int triesCount) {
String videoPath = convertVideoParams.videoPath;
File cacheFile = convertVideoParams.cacheFile;
int rotationValue = convertVideoParams.rotationValue;
boolean isSecret = convertVideoParams.isSecret;
int originalWidth = convertVideoParams.originalWidth;
int originalHeight = convertVideoParams.originalHeight;
int resultWidth = convertVideoParams.resultWidth;
int resultHeight = convertVideoParams.resultHeight;
int framerate = convertVideoParams.framerate;
int bitrate = convertVideoParams.bitrate;
int originalBitrate = convertVideoParams.originalBitrate;
long startTime = convertVideoParams.startTime;
long endTime = convertVideoParams.endTime;
long avatarStartTime = convertVideoParams.avatarStartTime;
boolean needCompress = convertVideoParams.needCompress;
long duration = convertVideoParams.duration;
MediaController.SavedFilterState savedFilterState = convertVideoParams.savedFilterState;
String paintPath = convertVideoParams.paintPath;
String blurPath = convertVideoParams.blurPath;
ArrayList<VideoEditedInfo.MediaEntity> mediaEntities = convertVideoParams.mediaEntities;
boolean isPhoto = convertVideoParams.isPhoto;
MediaController.CropState cropState = convertVideoParams.cropState;
boolean isRound = convertVideoParams.isRound;
Integer gradientTopColor = convertVideoParams.gradientTopColor;
Integer gradientBottomColor = convertVideoParams.gradientBottomColor;
boolean muted = convertVideoParams.muted;
boolean isStory = convertVideoParams.isStory;
StoryEntry.HDRInfo hdrInfo = convertVideoParams.hdrInfo;
ArrayList<StoryEntry.Part> parts = convertVideoParams.parts;
FileLog.d("convertVideoInternal original=" + originalWidth + "x" + originalHeight + " result=" + resultWidth + "x" + resultHeight + " " + avatarStartTime);
long time = System.currentTimeMillis();
@ -123,6 +120,7 @@ public class MediaCodecVideoConvertor {
int prependHeaderSize = 0;
endPresentationTime = duration * 1000;
checkConversionCanceled();
AudioRecoder audioRecoder = null;
if (isPhoto) {
try {
@ -180,7 +178,7 @@ public class MediaCodecVideoConvertor {
inputSurface.makeCurrent();
encoder.start();
outputSurface = new OutputSurface(savedFilterState, videoPath, paintPath, mediaEntities, cropState != null && cropState.useMatrix != null ? cropState : null, resultWidth, resultHeight, originalWidth, originalHeight, rotationValue, framerate, true, gradientTopColor, gradientBottomColor, null, parts);
outputSurface = new OutputSurface(savedFilterState, videoPath, paintPath, blurPath, mediaEntities, cropState != null && cropState.useMatrix != null ? cropState : null, resultWidth, resultHeight, originalWidth, originalHeight, rotationValue, framerate, true, gradientTopColor, gradientBottomColor, null, parts);
ByteBuffer[] encoderOutputBuffers = null;
ByteBuffer[] encoderInputBuffers = null;
@ -193,9 +191,27 @@ public class MediaCodecVideoConvertor {
checkConversionCanceled();
mediaMuxer = new MP4Builder().createMovie(movie, isSecret, outputMimeType.equals("video/hevc"));
while (!outputDone) {
int audioTrackIndex = -1;
boolean audioEncoderDone = true;
if (!convertVideoParams.soundInfos.isEmpty()) {
audioEncoderDone = false;
ArrayList<AudioInput> audioInputs = new ArrayList<>();
long totalDuration = duration * 1000;
BlankAudioInput mainInput = new BlankAudioInput(totalDuration);
audioInputs.add(mainInput);
applyAudioInputs(convertVideoParams.soundInfos, audioInputs);
audioRecoder = new AudioRecoder(audioInputs, totalDuration);
audioTrackIndex = mediaMuxer.addTrack(audioRecoder.format, true);
}
while (!outputDone || !audioEncoderDone) {
checkConversionCanceled();
if (audioRecoder != null) {
audioEncoderDone = audioRecoder.step(mediaMuxer, audioTrackIndex);
}
boolean decoderOutputAvailable = !decoderDone;
boolean encoderOutputAvailable = true;
while (decoderOutputAvailable || encoderOutputAvailable) {
@ -330,6 +346,9 @@ public class MediaCodecVideoConvertor {
encoder.release();
encoder = null;
}
if (audioRecoder != null) {
audioRecoder.release();
}
checkConversionCanceled();
} else {
extractor = new MediaExtractor();
@ -343,7 +362,6 @@ public class MediaCodecVideoConvertor {
}
if (needCompress || needConvertVideo) {
AudioRecoder audioRecoder = null;
ByteBuffer audioBuffer = null;
boolean copyAudioBuffer = true;
long lastFramePts = -1;
@ -439,7 +457,7 @@ public class MediaCodecVideoConvertor {
// prevent case when result video max 2MB
outputFormat.setInteger(MediaFormat.KEY_BITRATE_MODE, MediaCodecInfo.EncoderCapabilities.BITRATE_MODE_CBR);
}
outputFormat.setInteger( "max-bitrate", bitrate);
outputFormat.setInteger("max-bitrate", bitrate);
outputFormat.setInteger(MediaFormat.KEY_FRAME_RATE, framerate);
outputFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
@ -475,7 +493,7 @@ public class MediaCodecVideoConvertor {
inputSurface.makeCurrent();
encoder.start();
outputSurface = new OutputSurface(savedFilterState, null, paintPath, mediaEntities, cropState, resultWidth, resultHeight, originalWidth, originalHeight, rotationValue, framerate, false, gradientTopColor, gradientBottomColor, hdrInfo, parts);
outputSurface = new OutputSurface(savedFilterState, null, paintPath, blurPath, mediaEntities, cropState, resultWidth, resultHeight, originalWidth, originalHeight, rotationValue, framerate, false, gradientTopColor, gradientBottomColor, hdrInfo, parts);
if (hdrInfo == null && outputSurface.supportsEXTYUV() && hasHDR) {
hdrInfo = new StoryEntry.HDRInfo();
hdrInfo.colorTransfer = colorTransfer;
@ -487,15 +505,15 @@ public class MediaCodecVideoConvertor {
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && hdrInfo != null && hdrInfo.getHDRType() != 0 && outputSurface.supportsEXTYUV()) {
outputSurface.changeFragmentShader(
hdrFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, true, hdrInfo),
hdrFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, false, hdrInfo),
true
hdrFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, true, hdrInfo),
hdrFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, false, hdrInfo),
true
);
} else if (!isRound && Math.max(resultHeight, resultHeight) / (float) Math.max(originalHeight, originalWidth) < 0.9f) {
outputSurface.changeFragmentShader(
createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, true, isStory ? 0 : 3),
createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, false, isStory ? 0 : 3),
false
createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, true, isStory ? 0 : 3),
createFragmentShader(originalWidth, originalHeight, resultWidth, resultHeight, false, isStory ? 0 : 3),
false
);
}
decoder = getDecoderByFormat(videoFormat);
@ -514,7 +532,7 @@ public class MediaCodecVideoConvertor {
mediaMuxer = new MP4Builder().createMovie(movie, isSecret, outputMimeType.equals("video/hevc"));
if (audioIndex >= 0) {
MediaFormat audioFormat = extractor.getTrackFormat(audioIndex);
copyAudioBuffer = audioFormat.getString(MediaFormat.KEY_MIME).equals(MediaController.AUIDO_MIME_TYPE) || audioFormat.getString(MediaFormat.KEY_MIME).equals("audio/mpeg");
copyAudioBuffer = convertVideoParams.soundInfos.isEmpty() && audioFormat.getString(MediaFormat.KEY_MIME).equals(MediaController.AUIDO_MIME_TYPE) || audioFormat.getString(MediaFormat.KEY_MIME).equals("audio/mpeg");
if (audioFormat.getString(MediaFormat.KEY_MIME).equals("audio/unknown")) {
audioIndex = -1;
@ -540,25 +558,33 @@ public class MediaCodecVideoConvertor {
extractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
}
} else {
MediaExtractor audioExtractor = new MediaExtractor();
audioExtractor.setDataSource(videoPath);
audioExtractor.selectTrack(audioIndex);
if (startTime > 0) {
audioExtractor.seekTo(startTime, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
} else {
audioExtractor.seekTo(0, MediaExtractor.SEEK_TO_PREVIOUS_SYNC);
ArrayList<AudioInput> audioInputs = new ArrayList<>();
GeneralAudioInput mainInput = new GeneralAudioInput(videoPath, audioIndex);
if (endTime > 0) {
mainInput.setEndTimeUs(endTime);
}
if (startTime > 0) {
mainInput.setStartTimeUs(startTime);
}
audioInputs.add(mainInput);
applyAudioInputs(convertVideoParams.soundInfos, audioInputs);
audioRecoder = new AudioRecoder(audioFormat, audioExtractor, audioIndex);
audioRecoder.startTime = startTime;
audioRecoder.endTime = endTime;
audioRecoder = new AudioRecoder(audioInputs, duration);
audioTrackIndex = mediaMuxer.addTrack(audioRecoder.format, true);
}
}
} else if (!convertVideoParams.soundInfos.isEmpty()) {
copyAudioBuffer = false;
ArrayList<AudioInput> audioInputs = new ArrayList<>();
BlankAudioInput mainInput = new BlankAudioInput(duration);
audioInputs.add(mainInput);
applyAudioInputs(convertVideoParams.soundInfos, audioInputs);
audioRecoder = new AudioRecoder(audioInputs, duration);
audioTrackIndex = mediaMuxer.addTrack(audioRecoder.format, true);
}
boolean audioEncoderDone = audioIndex < 0;
boolean audioEncoderDone = audioRecoder == null;
boolean firstEncode = true;
@ -568,7 +594,7 @@ public class MediaCodecVideoConvertor {
while (!outputDone || (!copyAudioBuffer && !audioEncoderDone)) {
checkConversionCanceled();
if (!copyAudioBuffer && audioRecoder != null) {
if (audioRecoder != null) {
audioEncoderDone = audioRecoder.step(mediaMuxer, audioTrackIndex);
}
@ -879,43 +905,39 @@ public class MediaCodecVideoConvertor {
if (encoder != null) {
try {
encoder.release();
} catch (Exception ignore) {}
} catch (Exception ignore) {
}
encoder = null;
}
if (decoder != null) {
try {
decoder.release();
} catch (Exception ignore) {}
} catch (Exception ignore) {
}
decoder = null;
}
if (outputSurface != null) {
try {
outputSurface.release();
} catch (Exception ignore) {}
} catch (Exception ignore) {
}
outputSurface = null;
}
if (inputSurface != null) {
try {
inputSurface.release();
} catch (Exception ignore) {}
} catch (Exception ignore) {
}
inputSurface = null;
}
}
if (repeatWithIncreasedTimeout) {
return convertVideoInternal(videoPath, cacheFile, rotationValue, isSecret,
originalWidth, originalHeight,
resultWidth, resultHeight, framerate, bitrate, originalBitrate, startTime, endTime, avatarStartTime, duration,
needCompress, true, savedFilterState, paintPath, mediaEntities,
isPhoto, cropState, isRound, gradientTopColor, gradientBottomColor, muted, isStory, hdrInfo, parts, triesCount + 1);
return convertVideoInternal(convertVideoParams, true, triesCount + 1);
}
if (error && canBeBrokenEncoder && triesCount < 3) {
return convertVideoInternal(videoPath, cacheFile, rotationValue, isSecret,
originalWidth, originalHeight,
resultWidth, resultHeight, framerate, bitrate, originalBitrate, startTime, endTime, avatarStartTime, duration,
needCompress, increaseTimeout, savedFilterState, paintPath, mediaEntities,
isPhoto, cropState, isRound, gradientTopColor, gradientBottomColor, muted, isStory, hdrInfo, parts, triesCount + 1);
return convertVideoInternal(convertVideoParams, increaseTimeout, triesCount + 1);
}
long timeLeft = System.currentTimeMillis() - time;
@ -926,6 +948,25 @@ public class MediaCodecVideoConvertor {
return error;
}
private static void applyAudioInputs(ArrayList<MixedSoundInfo> soundInfos, ArrayList<AudioInput> audioInputs) throws IOException {
for (int i = 0; i < soundInfos.size(); i++) {
MixedSoundInfo soundInfo = soundInfos.get(i);
GeneralAudioInput secondAudio = new GeneralAudioInput(soundInfo.audioFile);
secondAudio.setVolume(soundInfo.volume);
long startTimeLocal = 0;
if (soundInfo.startTime > 0) {
secondAudio.setStartOffsetUs(soundInfo.startTime);
}
if (soundInfo.audioOffset > 0) {
secondAudio.setStartTimeUs(startTimeLocal = soundInfo.audioOffset);
}
if (soundInfo.duration > 0) {
secondAudio.setEndTimeUs(startTimeLocal + soundInfo.duration);
}
audioInputs.add(secondAudio);
}
}
private MediaCodec createEncoderForMimeType() throws IOException {
MediaCodec encoder = null;//MediaCodec.createEncoderByType(outputMimeType);
if (outputMimeType.equals("video/hevc") && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
@ -1169,11 +1210,11 @@ public class MediaCodecVideoConvertor {
shaderCode = shaderCode.replace("$dstHeight", dstHeight + ".0");
// TODO(@dkaraush): use minlum/maxlum
return shaderCode + "\n" +
"in vec2 vTextureCoord;\n" +
"out vec4 fragColor;\n" +
"void main() {\n" +
" fragColor = TEX(vTextureCoord);\n" +
"}";
"in vec2 vTextureCoord;\n" +
"out vec4 fragColor;\n" +
"void main() {\n" +
" fragColor = TEX(vTextureCoord);\n" +
"}";
} else {
return "#version 320 es\n" +
"precision mediump float;\n" +
@ -1239,6 +1280,7 @@ public class MediaCodecVideoConvertor {
}
}
public class ConversionCanceledException extends RuntimeException {
public ConversionCanceledException() {
@ -1273,4 +1315,104 @@ public class MediaCodecVideoConvertor {
throw new RuntimeException(exception);
}
public static class ConvertVideoParams {
String videoPath;
File cacheFile;
int rotationValue;
boolean isSecret;
int originalWidth, originalHeight;
int resultWidth, resultHeight;
int framerate;
int bitrate;
int originalBitrate;
long startTime;
long endTime;
long avatarStartTime;
boolean needCompress;
long duration;
MediaController.SavedFilterState savedFilterState;
String paintPath;
String blurPath;
ArrayList<VideoEditedInfo.MediaEntity> mediaEntities;
boolean isPhoto;
MediaController.CropState cropState;
boolean isRound;
MediaController.VideoConvertorListener callback;
Integer gradientTopColor;
Integer gradientBottomColor;
boolean muted;
boolean isStory;
StoryEntry.HDRInfo hdrInfo;
ArrayList<StoryEntry.Part> parts;
public ArrayList<MixedSoundInfo> soundInfos = new ArrayList<MixedSoundInfo>();
private ConvertVideoParams() {
}
public static ConvertVideoParams of(String videoPath, File cacheFile,
int rotationValue, boolean isSecret,
int originalWidth, int originalHeight,
int resultWidth, int resultHeight,
int framerate, int bitrate, int originalBitrate,
long startTime, long endTime, long avatarStartTime,
boolean needCompress, long duration,
MediaController.SavedFilterState savedFilterState,
String paintPath, String blurPath,
ArrayList<VideoEditedInfo.MediaEntity> mediaEntities,
boolean isPhoto,
MediaController.CropState cropState,
boolean isRound,
MediaController.VideoConvertorListener callback,
Integer gradientTopColor, Integer gradientBottomColor,
boolean muted, boolean isStory, StoryEntry.HDRInfo hdrInfo,
ArrayList<StoryEntry.Part> parts) {
ConvertVideoParams params = new ConvertVideoParams();
params.videoPath = videoPath;
params.cacheFile = cacheFile;
params.rotationValue = rotationValue;
params.isSecret = isSecret;
params.originalWidth = originalWidth;
params.originalHeight = originalHeight;
params.resultWidth = resultWidth;
params.resultHeight = resultHeight;
params.framerate = framerate;
params.bitrate = bitrate;
params.originalBitrate = originalBitrate;
params.startTime = startTime;
params.endTime = endTime;
params.avatarStartTime = avatarStartTime;
params.needCompress = needCompress;
params.duration = duration;
params.savedFilterState = savedFilterState;
params.paintPath = paintPath;
params.blurPath = blurPath;
params.mediaEntities = mediaEntities;
params.isPhoto = isPhoto;
params.cropState = cropState;
params.isRound = isRound;
params.callback = callback;
params.gradientTopColor = gradientTopColor;
params.gradientBottomColor = gradientBottomColor;
params.muted = muted;
params.isStory = isStory;
params.hdrInfo = hdrInfo;
params.parts = parts;
return params;
}
}
public static class MixedSoundInfo {
final String audioFile;
public float volume = 1f;
public long audioOffset;
public long startTime;
public long duration;
public MixedSoundInfo(String file) {
this.audioFile = file;
}
}
}

View file

@ -39,8 +39,8 @@ public class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
private boolean mFrameAvailable;
private TextureRenderer mTextureRender;
public OutputSurface(MediaController.SavedFilterState savedFilterState, String imagePath, String paintPath, ArrayList<VideoEditedInfo.MediaEntity> mediaEntities, MediaController.CropState cropState, int w, int h, int originalW, int originalH, int rotation, float fps, boolean photo, Integer gradientTopColor, Integer gradientBottomColor, StoryEntry.HDRInfo hdrInfo, ArrayList<StoryEntry.Part> parts) {
mTextureRender = new TextureRenderer(savedFilterState, imagePath, paintPath, mediaEntities, cropState, w, h, originalW, originalH, rotation, fps, photo, gradientTopColor, gradientBottomColor, hdrInfo, parts);
public OutputSurface(MediaController.SavedFilterState savedFilterState, String imagePath, String paintPath, String blurPath, ArrayList<VideoEditedInfo.MediaEntity> mediaEntities, MediaController.CropState cropState, int w, int h, int originalW, int originalH, int rotation, float fps, boolean photo, Integer gradientTopColor, Integer gradientBottomColor, StoryEntry.HDRInfo hdrInfo, ArrayList<StoryEntry.Part> parts) {
mTextureRender = new TextureRenderer(savedFilterState, imagePath, paintPath, blurPath, mediaEntities, cropState, w, h, originalW, originalH, rotation, fps, photo, gradientTopColor, gradientBottomColor, hdrInfo, parts);
mTextureRender.surfaceCreated();
mSurfaceTexture = new SurfaceTexture(mTextureRender.getTextureId());
mSurfaceTexture.setOnFrameAvailableListener(this);

View file

@ -61,6 +61,7 @@ import org.telegram.tgnet.TLRPC;
import org.telegram.ui.Components.AnimatedEmojiDrawable;
import org.telegram.ui.Components.AnimatedEmojiSpan;
import org.telegram.ui.Components.AnimatedFileDrawable;
import org.telegram.ui.Components.BlurringShader;
import org.telegram.ui.Components.EditTextEffects;
import org.telegram.ui.Components.FilterShaders;
import org.telegram.ui.Components.Paint.Views.EditTextOutline;
@ -89,6 +90,8 @@ public class TextureRenderer {
private FloatBuffer renderTextureBuffer;
private FloatBuffer bitmapVerticesBuffer;
private FloatBuffer blurVerticesBuffer;
private FloatBuffer partsVerticesBuffer[];
private FloatBuffer partsTextureBuffer;
private ArrayList<StoryEntry.Part> parts;
@ -105,7 +108,9 @@ public class TextureRenderer {
private FilterShaders filterShaders;
private String paintPath;
private String blurPath;
private String imagePath;
private int imageWidth, imageHeight;
private ArrayList<VideoEditedInfo.MediaEntity> mediaEntities;
private ArrayList<AnimatedEmojiDrawable> emojiDrawables;
private int originalWidth;
@ -113,6 +118,8 @@ public class TextureRenderer {
private int transformedWidth;
private int transformedHeight;
private BlurringShader blur;
private static final String VERTEX_SHADER =
"uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uSTMatrix;\n" +
@ -187,6 +194,12 @@ public class TextureRenderer {
private int simpleInputTexCoordHandle;
private int simpleSourceImageHandle;
private int blurShaderProgram;
private int blurPositionHandle;
private int blurInputTexCoordHandle;
private int blurBlurImageHandle;
private int blurMaskImageHandle;
private int[] paintTexture;
private int[] stickerTexture;
private Bitmap stickerBitmap;
@ -204,12 +217,16 @@ public class TextureRenderer {
Paint xRefPaint;
Paint textColorPaint;
private final MediaController.CropState cropState;
private int[] blurTexture;
private int gradientTopColor, gradientBottomColor;
public TextureRenderer(
MediaController.SavedFilterState savedFilterState,
String image,
String paint,
String blurtex,
ArrayList<VideoEditedInfo.MediaEntity> entities,
MediaController.CropState cropState,
int w, int h,
@ -253,9 +270,7 @@ public class TextureRenderer {
if (savedFilterState != null) {
filterShaders = new FilterShaders(true, hdrInfo);
if (savedFilterState != null) {
filterShaders.setDelegate(FilterShaders.getFilterShadersDelegate(savedFilterState));
}
filterShaders.setDelegate(FilterShaders.getFilterShadersDelegate(savedFilterState));
}
transformedWidth = w;
transformedHeight = h;
@ -263,8 +278,10 @@ public class TextureRenderer {
this.originalHeight = originalHeight;
imagePath = image;
paintPath = paint;
blurPath = blurtex;
mediaEntities = entities;
videoFps = fps == 0 ? 30 : fps;
this.cropState = cropState;
int count = 0;
NUM_EXTERNAL_SHADER = count++;
@ -457,6 +474,7 @@ public class TextureRenderer {
}
public void drawFrame(SurfaceTexture st) {
boolean blurred = false;
if (isPhoto) {
drawGradient();
} else {
@ -487,7 +505,7 @@ public class TextureRenderer {
filterShaders.drawEnhancePass();
filterShaders.drawSharpenPass();
filterShaders.drawCustomParamsPass();
boolean blurred = filterShaders.drawBlurPass();
blurred = filterShaders.drawBlurPass();
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
if (transformedWidth != originalWidth || transformedHeight != originalHeight) {
@ -524,6 +542,47 @@ public class TextureRenderer {
GLES20.glUniformMatrix4fv(muMVPMatrixHandle[index], 1, false, mMVPMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
}
if (blur != null) {
if (!blendEnabled) {
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
blendEnabled = true;
}
int tex = -1, w = 1, h = 1;
if (imagePath != null && paintTexture != null) {
tex = paintTexture[0];
w = imageWidth;
h = imageHeight;
} else if (filterShaders != null) {
tex = filterShaders.getRenderTexture(blurred ? 0 : 1);
w = filterShaders.getRenderBufferWidth();
h = filterShaders.getRenderBufferHeight();
}
if (tex != -1) {
blur.draw(null, tex, w, h);
GLES20.glViewport(0, 0, transformedWidth, transformedHeight);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glUseProgram(blurShaderProgram);
GLES20.glEnableVertexAttribArray(blurInputTexCoordHandle);
GLES20.glVertexAttribPointer(blurInputTexCoordHandle, 2, GLES20.GL_FLOAT, false, 8, gradientTextureBuffer);
GLES20.glEnableVertexAttribArray(blurPositionHandle);
GLES20.glVertexAttribPointer(blurPositionHandle, 2, GLES20.GL_FLOAT, false, 8, blurVerticesBuffer);
GLES20.glUniform1i(blurBlurImageHandle, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, blur.getTexture());
GLES20.glUniform1i(blurMaskImageHandle, 1);
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, blurTexture[0]);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
}
}
if (isPhoto || paintTexture != null || stickerTexture != null || partsTexture != null) {
GLES20.glUseProgram(simpleShaderProgram);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
@ -762,6 +821,82 @@ public class TextureRenderer {
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
if (blurPath != null && cropState != null && cropState.useMatrix != null) {
blur = new BlurringShader();
if (!blur.setup(transformedWidth / (float) transformedHeight, true, 0)) {
blur = null;
} else {
blur.updateGradient(gradientTopColor, gradientBottomColor);
android.graphics.Matrix matrix = new android.graphics.Matrix();
matrix.postScale(originalWidth, originalHeight);
matrix.postConcat(cropState.useMatrix);
matrix.postScale(1f / transformedWidth, 1f / transformedHeight);
android.graphics.Matrix imatrix = new android.graphics.Matrix();
matrix.invert(imatrix);
blur.updateTransform(imatrix);
}
Bitmap bitmap = BitmapFactory.decodeFile(blurPath);
if (bitmap != null) {
blurTexture = new int[1];
GLES20.glGenTextures(1, blurTexture, 0);
GLES20.glBindTexture(GL10.GL_TEXTURE_2D, blurTexture[0]);
GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
} else {
blur = null;
}
if (blur != null) {
final String fragShader =
"varying highp vec2 vTextureCoord;" +
"uniform sampler2D blurImage;" +
"uniform sampler2D maskImage;" +
"void main() {" +
"gl_FragColor = texture2D(blurImage, vTextureCoord) * texture2D(maskImage, vTextureCoord).a;" +
"}";
int vertexShader = FilterShaders.loadShader(GLES20.GL_VERTEX_SHADER, FilterShaders.simpleVertexShaderCode);
int fragmentShader = FilterShaders.loadShader(GLES20.GL_FRAGMENT_SHADER, fragShader);
if (vertexShader != 0 && fragmentShader != 0) {
blurShaderProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(blurShaderProgram, vertexShader);
GLES20.glAttachShader(blurShaderProgram, fragmentShader);
GLES20.glBindAttribLocation(blurShaderProgram, 0, "position");
GLES20.glBindAttribLocation(blurShaderProgram, 1, "inputTexCoord");
GLES20.glLinkProgram(blurShaderProgram);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(blurShaderProgram, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] == 0) {
GLES20.glDeleteProgram(blurShaderProgram);
blurShaderProgram = 0;
} else {
blurPositionHandle = GLES20.glGetAttribLocation(blurShaderProgram, "position");
blurInputTexCoordHandle = GLES20.glGetAttribLocation(blurShaderProgram, "inputTexCoord");
blurBlurImageHandle = GLES20.glGetUniformLocation(blurShaderProgram, "blurImage");
blurMaskImageHandle = GLES20.glGetUniformLocation(blurShaderProgram, "maskImage");
float[] verticesData = {
-1.0f, 1.0f,
1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f,
};
blurVerticesBuffer = ByteBuffer.allocateDirect(verticesData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
blurVerticesBuffer.put(verticesData).position(0);
}
} else {
blur = null;
}
}
}
if (filterShaders != null || imagePath != null || paintPath != null || mediaEntities != null || parts != null) {
int vertexShader = FilterShaders.loadShader(GLES20.GL_VERTEX_SHADER, FilterShaders.simpleVertexShaderCode);
int fragmentShader = FilterShaders.loadShader(GLES20.GL_FRAGMENT_SHADER, FilterShaders.simpleFragmentShaderCode);
@ -781,7 +916,7 @@ public class TextureRenderer {
} else {
simplePositionHandle = GLES20.glGetAttribLocation(simpleShaderProgram, "position");
simpleInputTexCoordHandle = GLES20.glGetAttribLocation(simpleShaderProgram, "inputTexCoord");
simpleSourceImageHandle = GLES20.glGetUniformLocation(simpleShaderProgram, "sourceImage");
simpleSourceImageHandle = GLES20.glGetUniformLocation(simpleShaderProgram, "sTexture");
}
}
}
@ -827,6 +962,11 @@ public class TextureRenderer {
bitmap = newBitmap;
}
if (a == 0 && imagePath != null) {
imageWidth = bitmap.getWidth();
imageHeight = bitmap.getHeight();
}
GLES20.glBindTexture(GL10.GL_TEXTURE_2D, paintTexture[a]);
GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameteri(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
@ -905,7 +1045,6 @@ public class TextureRenderer {
} else if (entity.type == VideoEditedInfo.MediaEntity.TYPE_TEXT) {
EditTextOutline editText = new EditTextOutline(ApplicationLoader.applicationContext);
editText.getPaint().setAntiAlias(true);
editText.betterFraming = useMatrixForImagePath;
editText.drawAnimatedEmojiDrawables = false;
editText.setBackgroundColor(Color.TRANSPARENT);
editText.setPadding(AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7), AndroidUtilities.dp(7));
@ -928,7 +1067,7 @@ public class TextureRenderer {
super.draw(canvas, charSequence, start, end, x, top, y, bottom, paint);
float tcx = entity.x + (editText.getPaddingLeft() + x + measuredSize / 2f) / entity.viewWidth * entity.width;
float tcy = entity.y + ((editText.betterFraming ? editText.getPaddingTop() : 0) + top + (bottom - top) / 2f) / entity.viewHeight * entity.height;
float tcy = entity.y + (editText.getPaddingTop() + top + (bottom - top) / 2f) / entity.viewHeight * entity.height;
if (entity.rotation != 0) {
float mx = entity.x + entity.width / 2f;

View file

@ -188,7 +188,7 @@ public class VideoPlayerHolderBase {
onReadyListener.run();
onReadyListener = null;
}
}, 16);
}, surfaceView == null ? 16 : 32);
}
@Override

View file

@ -0,0 +1,47 @@
package org.telegram.messenger.video.audio_input;
public abstract class AudioInput {
private boolean loopingEnabled;
private float volume = 1f;
public boolean isLoopingEnabled() {
return loopingEnabled;
}
public float getVolume() {
return volume;
}
public void setLoopingEnabled(boolean loopingEnabled) {
this.loopingEnabled = loopingEnabled;
}
public void setVolume(float volume) {
this.volume = Math.max(0f, Math.min(volume, 1f));
}
public abstract long getStartTimeUs();
public abstract long getEndTimeUs();
public abstract long getDurationUs();
public abstract int getSampleRate();
public abstract int getBitrate();
public abstract int getChannelCount();
public abstract boolean hasRemaining();
public abstract void setStartTimeUs(long timeUs);
public abstract void setEndTimeUs(long timeUs);
public abstract void start(int outputSampleRate, int outputChannelCount);
public abstract short getNext();
public abstract void release();
}

View file

@ -0,0 +1,78 @@
package org.telegram.messenger.video.audio_input;
import org.telegram.messenger.video.AudioConversions;
public class BlankAudioInput extends AudioInput {
public final long durationUs;
private int requiredShortsForDuration;
private int remainingShorts;
public BlankAudioInput(long durationUs){
this.durationUs = durationUs;
}
@Override
public long getStartTimeUs() {
return 0;
}
@Override
public long getEndTimeUs() {
return durationUs;
}
@Override
public long getDurationUs() {
return durationUs;
}
@Override
public int getSampleRate() {
return -1;
}
@Override
public int getBitrate() {
return -1;
}
@Override
public int getChannelCount() {
return -1;
}
@Override
public void setStartTimeUs(long timeUs) { }
@Override
public void setEndTimeUs(long timeUs) { }
@Override
public boolean hasRemaining() {
return remainingShorts > 0;
}
@Override
public void start(int outputSampleRate, int outputChannelCount) {
requiredShortsForDuration = AudioConversions.usToShorts(
durationUs, outputSampleRate, outputChannelCount);
remainingShorts = requiredShortsForDuration;
}
@Override
public short getNext() {
if(!hasRemaining()) throw new RuntimeException("Audio input has no remaining value.");
remainingShorts--;
if(isLoopingEnabled() && remainingShorts == 0){
remainingShorts = requiredShortsForDuration;
}
return 0;
}
@Override
public void release() {
remainingShorts = 0;
}
}

View file

@ -0,0 +1,154 @@
package org.telegram.messenger.video.audio_input;
import org.telegram.messenger.video.AudioBufferConverter;
import org.telegram.messenger.video.AudioConversions;
import org.telegram.messenger.video.AudioDecoder;
import java.io.IOException;
import java.nio.ShortBuffer;
public class GeneralAudioInput extends AudioInput {
private final AudioDecoder decoder;
private AudioBufferConverter audioBufferConverter;
private long startOffsetUs;
private int requiredShortsForStartOffset;
private int startOffsetShortsCounter;
private int outputSampleRate;
private int outputChannelCount;
private ShortBuffer buffer;
private boolean hasRemaining;
public GeneralAudioInput(String sourcePath) throws IOException {
decoder = new AudioDecoder(sourcePath);
init();
}
public GeneralAudioInput(String sourcePath, int audioIndex) throws IOException {
decoder = new AudioDecoder(sourcePath, audioIndex);
init();
}
private void init() {
audioBufferConverter = new AudioBufferConverter();
}
@Override
public void setLoopingEnabled(boolean loopingEnabled) {
super.setLoopingEnabled(loopingEnabled);
decoder.setLoopingEnabled(loopingEnabled);
}
public long getStartOffsetUs() {
return startOffsetUs;
}
@Override
public long getStartTimeUs() {
return decoder.getStartTimeUs();
}
@Override
public long getEndTimeUs() {
return decoder.getEndTimeUs();
}
@Override
public long getDurationUs() {
return getEndTimeUs() - getStartTimeUs() + getStartOffsetUs();
}
@Override
public int getSampleRate() {
return decoder.getSampleRate();
}
@Override
public int getBitrate() {
return decoder.getBitrateRate();
}
@Override
public int getChannelCount() {
return decoder.getChannelCount();
}
public void setStartOffsetUs(long startOffsetUs) {
this.startOffsetUs = startOffsetUs < 0 ? 0 : startOffsetUs;
}
@Override
public void setStartTimeUs(long timeUs) {
decoder.setStartTimeUs(timeUs);
}
@Override
public void setEndTimeUs(long timeUs) {
decoder.setEndTimeUs(timeUs);
}
@Override
public boolean hasRemaining() {
return hasRemaining;
}
@Override
public void start(int outputSampleRate, int outputChannelCount) {
this.outputSampleRate = outputSampleRate;
this.outputChannelCount = outputChannelCount;
hasRemaining = true;
decoder.start();
requiredShortsForStartOffset = AudioConversions.usToShorts(getStartOffsetUs(), this.outputSampleRate, this.outputChannelCount);
startOffsetShortsCounter = 0;
}
@Override
public short getNext() {
if (!hasRemaining()) throw new RuntimeException("Audio input has no remaining value.");
if (startOffsetShortsCounter < requiredShortsForStartOffset) {
startOffsetShortsCounter++;
return 0;
}
decode();
short value = 0;
if (buffer != null && buffer.remaining() > 0) {
value = buffer.get();
}
decode();
if (buffer == null || buffer.remaining() < 1) {
hasRemaining = false;
}
return value;
}
private void decode() {
if (buffer == null || buffer.remaining() <= 0) {
AudioDecoder.DecodedBufferData audioData = decoder.decode();
if (audioData.index >= 0) {
buffer = audioBufferConverter.convert(audioData.byteBuffer.asShortBuffer(),
decoder.getSampleRate(), decoder.getChannelCount(),
outputSampleRate, outputChannelCount);
decoder.releaseOutputBuffer(audioData.index);
} else {
buffer = null;
}
}
}
@Override
public void release() {
buffer = null;
hasRemaining = false;
decoder.stop();
decoder.release();
}
}

View file

@ -0,0 +1,39 @@
package org.telegram.messenger.video.remix;
import androidx.annotation.NonNull;
import java.nio.Buffer;
import java.nio.ShortBuffer;
/**
* Remixes audio data. See {@link DownMixAudioRemixer},
* {@link UpMixAudioRemixer} or {@link PassThroughAudioRemixer}
* for concrete implementations.
*/
public interface AudioRemixer {
/**
* Remixes input audio from input buffer into the output buffer.
* The output buffer is guaranteed to have a {@link Buffer#remaining()} size that is
* consistent with {@link #getRemixedSize(int,int,int)}.
*
* @param inputBuffer the input buffer
* @param outputBuffer the output buffer
*/
void remix(@NonNull final ShortBuffer inputBuffer, int inputChannelCount,
@NonNull final ShortBuffer outputBuffer, int outputChannelCount);
/**
* Returns the output size (in shorts) needed to process an input buffer of the
* given size (in shorts).
* @param inputSize input size in shorts
* @return output size in shorts
*/
int getRemixedSize(int inputSize, int inputChannelCount, int outputChannelCount);
AudioRemixer DOWNMIX = new DownMixAudioRemixer();
AudioRemixer UPMIX = new UpMixAudioRemixer();
AudioRemixer PASSTHROUGH = new PassThroughAudioRemixer();
}

View file

@ -0,0 +1,34 @@
package org.telegram.messenger.video.remix;
import androidx.annotation.NonNull;
import java.nio.ShortBuffer;
public class DefaultAudioRemixer implements AudioRemixer {
@Override
public void remix(@NonNull ShortBuffer inputBuffer, int inputChannelCount, @NonNull ShortBuffer outputBuffer, int outputChannelCount) {
AudioRemixer remixer;
if (inputChannelCount > outputChannelCount) {
remixer = DOWNMIX;
} else if (inputChannelCount < outputChannelCount) {
remixer = AudioRemixer.UPMIX;
} else {
remixer = AudioRemixer.PASSTHROUGH;
}
remixer.remix(inputBuffer, inputChannelCount, outputBuffer, outputChannelCount);
}
@Override
public int getRemixedSize(int inputSize, int inputChannelCount, int outputChannelCount) {
AudioRemixer remixer;
if (inputChannelCount > outputChannelCount) {
remixer = DOWNMIX;
} else if (inputChannelCount < outputChannelCount) {
remixer = AudioRemixer.UPMIX;
} else {
remixer = AudioRemixer.PASSTHROUGH;
}
return remixer.getRemixedSize(inputSize, inputChannelCount, outputChannelCount);
}
}

View file

@ -0,0 +1,49 @@
package org.telegram.messenger.video.remix;
import androidx.annotation.NonNull;
import java.nio.ShortBuffer;
/**
* A {@link AudioRemixer} that downmixes stereo audio to mono.
*/
public class DownMixAudioRemixer implements AudioRemixer {
private static final int SIGNED_SHORT_LIMIT = 32768;
private static final int UNSIGNED_SHORT_MAX = 65535;
@Override
public void remix(@NonNull ShortBuffer inputBuffer, int inputChannelCount, @NonNull ShortBuffer outputBuffer, int outputChannelCount) {
// Down-mix stereo to mono
// Viktor Toth's algorithm -
// See: http://www.vttoth.com/CMS/index.php/technical-notes/68
// http://stackoverflow.com/a/25102339
final int inRemaining = inputBuffer.remaining() / 2;
final int outSpace = outputBuffer.remaining();
final int samplesToBeProcessed = Math.min(inRemaining, outSpace);
for (int i = 0; i < samplesToBeProcessed; ++i) {
// Convert to unsigned
final int a = inputBuffer.get() + SIGNED_SHORT_LIMIT;
final int b = inputBuffer.get() + SIGNED_SHORT_LIMIT;
int m;
// Pick the equation
if ((a < SIGNED_SHORT_LIMIT) || (b < SIGNED_SHORT_LIMIT)) {
// Viktor's first equation when both sources are "quiet"
// (i.e. less than middle of the dynamic range)
m = a * b / SIGNED_SHORT_LIMIT;
} else {
// Viktor's second equation when one or both sources are loud
m = 2 * (a + b) - (a * b) / SIGNED_SHORT_LIMIT - UNSIGNED_SHORT_MAX;
}
// Convert output back to signed short
if (m == UNSIGNED_SHORT_MAX + 1) m = UNSIGNED_SHORT_MAX;
outputBuffer.put((short) (m - SIGNED_SHORT_LIMIT));
}
}
@Override
public int getRemixedSize(int inputSize, int inputChannelCount, int outputChannelCount) {
return inputSize / 2;
}
}

View file

@ -0,0 +1,21 @@
package org.telegram.messenger.video.remix;
import androidx.annotation.NonNull;
import java.nio.ShortBuffer;
/**
* The simplest {@link AudioRemixer} that does nothing.
*/
public class PassThroughAudioRemixer implements AudioRemixer {
@Override
public void remix(@NonNull ShortBuffer inputBuffer, int inputChannelCount, @NonNull ShortBuffer outputBuffer, int outputChannelCount) {
outputBuffer.put(inputBuffer);
}
@Override
public int getRemixedSize(int inputSize, int inputChannelCount, int outputChannelCount) {
return inputSize;
}
}

View file

@ -0,0 +1,30 @@
package org.telegram.messenger.video.remix;
import androidx.annotation.NonNull;
import java.nio.ShortBuffer;
/**
* A {@link AudioRemixer} that upmixes mono audio to stereo.
*/
public class UpMixAudioRemixer implements AudioRemixer {
@Override
public void remix(@NonNull ShortBuffer inputBuffer, int inputChannelCount, @NonNull ShortBuffer outputBuffer, int outputChannelCount) {
// Up-mix mono to stereo
final int inRemaining = inputBuffer.remaining();
final int outSpace = outputBuffer.remaining() / 2;
final int samplesToBeProcessed = Math.min(inRemaining, outSpace);
for (int i = 0; i < samplesToBeProcessed; ++i) {
final short inSample = inputBuffer.get();
outputBuffer.put(inSample);
outputBuffer.put(inSample);
}
}
@Override
public int getRemixedSize(int inputSize, int inputChannelCount, int outputChannelCount) {
return inputSize * 2;
}
}

View file

@ -0,0 +1,29 @@
package org.telegram.messenger.video.resample;
import androidx.annotation.NonNull;
import java.nio.ShortBuffer;
/**
* Resamples audio data. See {@link UpsampleAudioResampler} or
* {@link DownsampleAudioResampler} for concrete implementations.
*/
public interface AudioResampler {
/**
* Resamples input audio from input buffer into the output buffer.
*
* @param inputBuffer the input buffer
* @param inputSampleRate the input sample rate
* @param outputBuffer the output buffer
* @param outputSampleRate the output sample rate
* @param channels the number of channels
*/
void resample(@NonNull final ShortBuffer inputBuffer, int inputSampleRate, @NonNull final ShortBuffer outputBuffer, int outputSampleRate, int channels);
AudioResampler DOWNSAMPLE = new DownsampleAudioResampler();
AudioResampler UPSAMPLE = new UpsampleAudioResampler();
AudioResampler PASSTHROUGH = new PassThroughAudioResampler();
}

View file

@ -0,0 +1,23 @@
package org.telegram.messenger.video.resample;
import androidx.annotation.NonNull;
import java.nio.ShortBuffer;
/**
* An {@link AudioResampler} that delegates to appropriate classes
* based on input and output size.
*/
public class DefaultAudioResampler implements AudioResampler {
@Override
public void resample(@NonNull ShortBuffer inputBuffer, int inputSampleRate, @NonNull ShortBuffer outputBuffer, int outputSampleRate, int channels) {
if (inputSampleRate < outputSampleRate) {
UPSAMPLE.resample(inputBuffer, inputSampleRate, outputBuffer, outputSampleRate, channels);
} else if (inputSampleRate > outputSampleRate) {
DOWNSAMPLE.resample(inputBuffer, inputSampleRate, outputBuffer, outputSampleRate, channels);
} else {
PASSTHROUGH.resample(inputBuffer, inputSampleRate, outputBuffer, outputSampleRate, channels);
}
}
}

View file

@ -0,0 +1,47 @@
package org.telegram.messenger.video.resample;
import androidx.annotation.NonNull;
import java.nio.ShortBuffer;
/**
* An {@link AudioResampler} that downsamples from a higher sample rate to a lower sample rate.
*/
public class DownsampleAudioResampler implements AudioResampler {
private static float ratio(int remaining, int all) {
return (float) remaining / all;
}
@Override
public void resample(@NonNull ShortBuffer inputBuffer, int inputSampleRate, @NonNull ShortBuffer outputBuffer, int outputSampleRate, int channels) {
if (inputSampleRate < outputSampleRate) {
throw new IllegalArgumentException("Illegal use of DownsampleAudioResampler");
}
if (channels != 1 && channels != 2) {
throw new IllegalArgumentException("Illegal use of DownsampleAudioResampler. Channels:" + channels);
}
final int inputSamples = inputBuffer.remaining() / channels;
final int outputSamples = (int) Math.ceil(inputSamples * ((double) outputSampleRate / inputSampleRate));
final int dropSamples = inputSamples - outputSamples;
int remainingOutputSamples = outputSamples;
int remainingDropSamples = dropSamples;
float remainingOutputSamplesRatio = ratio(remainingOutputSamples, outputSamples);
float remainingDropSamplesRatio = ratio(remainingDropSamples, dropSamples);
while (remainingOutputSamples > 0 && remainingDropSamples > 0) {
// Will this be an input sample or a drop sample?
// Choose the one with the bigger ratio.
if (remainingOutputSamplesRatio >= remainingDropSamplesRatio) {
outputBuffer.put(inputBuffer.get());
if (channels == 2) outputBuffer.put(inputBuffer.get());
remainingOutputSamples--;
remainingOutputSamplesRatio = ratio(remainingOutputSamples, outputSamples);
} else {
// Drop this - read from input without writing.
inputBuffer.position(inputBuffer.position() + channels);
remainingDropSamples--;
remainingDropSamplesRatio = ratio(remainingDropSamples, dropSamples);
}
}
}
}

View file

@ -0,0 +1,21 @@
package org.telegram.messenger.video.resample;
import androidx.annotation.NonNull;
import java.nio.ShortBuffer;
/**
* An {@link AudioResampler} that does nothing, meant to be used when sample
* rates are identical.
*/
public class PassThroughAudioResampler implements AudioResampler {
@Override
public void resample(@NonNull ShortBuffer inputBuffer, int inputSampleRate,
@NonNull ShortBuffer outputBuffer, int outputSampleRate, int channels) {
if (inputSampleRate != outputSampleRate) {
throw new IllegalArgumentException("Illegal use of PassThroughAudioResampler");
}
outputBuffer.put(inputBuffer);
}
}

View file

@ -0,0 +1,72 @@
package org.telegram.messenger.video.resample;
import androidx.annotation.NonNull;
import java.nio.ShortBuffer;
/**
* An {@link AudioResampler} that upsamples from a lower sample rate to a higher sample rate.
*/
public class UpsampleAudioResampler implements AudioResampler {
private static float ratio(int remaining, int all) {
return (float) remaining / all;
}
@Override
public void resample(@NonNull ShortBuffer inputBuffer, int inputSampleRate, @NonNull ShortBuffer outputBuffer, int outputSampleRate, int channels) {
if (inputSampleRate > outputSampleRate) {
throw new IllegalArgumentException("Illegal use of UpsampleAudioResampler");
}
if (channels != 1 && channels != 2) {
throw new IllegalArgumentException("Illegal use of UpsampleAudioResampler. Channels:" + channels);
}
final int inputSamples = inputBuffer.remaining() / channels;
final int outputSamples = (int) Math.ceil(inputSamples * ((double) outputSampleRate / inputSampleRate));
final int fakeSamples = outputSamples - inputSamples;
int remainingInputSamples = inputSamples;
int remainingFakeSamples = fakeSamples;
float remainingInputSamplesRatio = ratio(remainingInputSamples, inputSamples);
float remainingFakeSamplesRatio = ratio(remainingFakeSamples, fakeSamples);
while (remainingInputSamples > 0 && remainingFakeSamples > 0) {
// Will this be an input sample or a fake sample?
// Choose the one with the bigger ratio.
if (remainingInputSamplesRatio >= remainingFakeSamplesRatio) {
outputBuffer.put(inputBuffer.get());
if (channels == 2) outputBuffer.put(inputBuffer.get());
remainingInputSamples--;
remainingInputSamplesRatio = ratio(remainingInputSamples, inputSamples);
} else {
outputBuffer.put(fakeSample(outputBuffer, inputBuffer, 1, channels));
if (channels == 2) outputBuffer.put(fakeSample(outputBuffer, inputBuffer, 2, channels));
remainingFakeSamples--;
remainingFakeSamplesRatio = ratio(remainingFakeSamples, fakeSamples);
}
}
}
/**
* We have different options here.
* 1. Return a 0 sample.
* 2. Return a noise sample.
* 3. Return the previous sample for this channel.
* 4. Return an interpolated value between previous and next sample for this channel.
*
* None of this provides a real quality improvement, since the fundamental issue is that we
* can not achieve a higher quality by simply inserting fake samples each X input samples.
* A real upsampling algorithm should do something more intensive like interpolating between
* all values, not just the spare one.
*
* However this is probably beyond the scope of this library.
*
* @param output output buffer
* @param input output buffer
* @param channel current channel
* @param channels number of channels
* @return a fake sample
*/
@SuppressWarnings("unused")
private static short fakeSample(@NonNull ShortBuffer output, @NonNull ShortBuffer input, int channel, int channels) {
return output.get(output.position() - channels);
}
}

View file

@ -74,7 +74,7 @@ public class TLRPC {
public static final int MESSAGE_FLAG_HAS_BOT_ID = 0x00000800;
public static final int MESSAGE_FLAG_EDITED = 0x00008000;
public static final int LAYER = 162;
public static final int LAYER = 163;
public static class TL_stats_megagroupStats extends TLObject {
public static int constructor = 0xef7ff916;
@ -5691,6 +5691,7 @@ public class TLRPC {
public boolean password_pending;
public boolean encrypted_requests_disabled;
public boolean call_requests_disabled;
public boolean unconfirmed;
public long hash;
public String device_model;
public String platform;
@ -5724,6 +5725,7 @@ public class TLRPC {
password_pending = (flags & 4) != 0;
encrypted_requests_disabled = (flags & 8) != 0;
call_requests_disabled = (flags & 16) != 0;
unconfirmed = (flags & 32) != 0;
hash = stream.readInt64(exception);
device_model = stream.readString(exception);
platform = stream.readString(exception);
@ -5745,6 +5747,7 @@ public class TLRPC {
flags = password_pending ? (flags | 4) : (flags &~ 4);
flags = encrypted_requests_disabled ? (flags | 8) : (flags &~ 8);
flags = call_requests_disabled ? (flags | 16) : (flags &~ 16);
flags = unconfirmed ? (flags | 32) : (flags &~ 32);
stream.writeInt32(flags);
stream.writeInt64(hash);
stream.writeString(device_model);
@ -6525,6 +6528,9 @@ public class TLRPC {
public ArrayList<User> participants = new ArrayList<>();
public Chat chat;
public int expires;
public boolean verified;
public boolean scam;
public boolean fake;
public static ChatInvite TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) {
ChatInvite result = null;
@ -6559,6 +6565,9 @@ public class TLRPC {
isPublic = (flags & 4) != 0;
megagroup = (flags & 8) != 0;
request_needed = (flags & 64) != 0;
verified = (flags & 128) != 0;
scam = (flags & 256) != 0;
fake = (flags & 512) != 0;
title = stream.readString(exception);
boolean hasAbout = (flags & 32) != 0;
if (hasAbout) {
@ -6593,6 +6602,9 @@ public class TLRPC {
flags = megagroup ? (flags | 8) : (flags &~ 8);
flags = about != null ? (flags | 32) : (flags &~ 32);
flags = request_needed ? (flags | 64) : (flags &~ 64);
flags = verified ? (flags | 128) : (flags &~ 128);
flags = scam ? (flags | 256) : (flags &~ 256);
flags = fake ? (flags | 512) : (flags &~ 512);
stream.writeInt32(flags);
stream.writeString(title);
if (about != null) {
@ -12495,7 +12507,7 @@ public class TLRPC {
}
}
}
public static class WebPageAttribute extends TLObject {
public int flags;
@ -12521,7 +12533,7 @@ public class TLRPC {
public static class TL_webPageAttributeStory extends WebPageAttribute {
public static final int constructor = 0x939a4671;
public long user_id;
public int id;
public TLRPC.StoryItem storyItem;
@ -21120,6 +21132,7 @@ public class TLRPC {
public Page cached_page;
public int date;
public ArrayList<WebPageAttribute> attributes = new ArrayList<>();
public String displayedText;//custom
public static WebPage TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) {
WebPage result = null;
@ -32931,6 +32944,9 @@ public class TLRPC {
case 0x1b49ec6d:
result = new TL_updateDraftMessage();
break;
case 0x8951abef:
result = new TL_updateNewAuthorization();
break;
case 0xa7848924:
result = new TL_updateUserName();
break;
@ -33051,7 +33067,7 @@ public class TLRPC {
case 0x922e6e10:
result = new TL_updateReadChannelInbox();
break;
case 0x68c13933:
case 0xf8227181:
result = new TL_updateReadMessagesContents();
break;
case 0x7761198:
@ -34250,7 +34266,7 @@ public class TLRPC {
public long user_id;
public EmojiStatus emoji_status;
public void readParams(AbstractSerializedData stream, boolean exception) {
user_id = stream.readInt64(exception);
emoji_status = EmojiStatus.TLdeserialize(stream, stream.readInt32(exception), exception);
@ -34458,6 +34474,28 @@ public class TLRPC {
}
}
public static class TL_updateNewAuthorization extends Update {
public static int constructor = 0x8951abef;
public int flags;
public boolean unconfirmed;
public long hash;
public int date;
public String device;
public String location;
public void readParams(AbstractSerializedData stream, boolean exception) {
flags = stream.readInt32(exception);
unconfirmed = (flags & 1) != 0;
hash = stream.readInt64(exception);
if ((flags & 1) != 0) {
date = stream.readInt32(exception);
device = stream.readString(exception);
location = stream.readString(exception);
}
}
}
public static class TL_updateUserName extends Update {
public static int constructor = 0xa7848924;
@ -35060,7 +35098,7 @@ public class TLRPC {
stream.writeInt32(constructor);
}
}
public static class TL_updateRecentEmojiStatuses extends Update {
public static int constructor = 0x30f443db;
@ -35310,13 +35348,16 @@ public class TLRPC {
}
public static class TL_updateReadMessagesContents extends Update {
public static int constructor = 0x68c13933;
public static int constructor = 0xf8227181;
public int flags;
public ArrayList<Integer> messages = new ArrayList<>();
public int pts;
public int pts_count;
public int date;
public void readParams(AbstractSerializedData stream, boolean exception) {
flags = stream.readInt32(exception);
int magic = stream.readInt32(exception);
if (magic != 0x1cb5c415) {
if (exception) {
@ -35330,10 +35371,14 @@ public class TLRPC {
}
pts = stream.readInt32(exception);
pts_count = stream.readInt32(exception);
if ((flags & 1) != 0) {
date = stream.readInt32(exception);
}
}
public void serializeToStream(AbstractSerializedData stream) {
stream.writeInt32(constructor);
stream.writeInt32(flags);
stream.writeInt32(0x1cb5c415);
int count = messages.size();
stream.writeInt32(count);
@ -35342,6 +35387,9 @@ public class TLRPC {
}
stream.writeInt32(pts);
stream.writeInt32(pts_count);
if ((flags & 1) != 0) {
stream.writeInt32(date);
}
}
}
@ -48590,7 +48638,7 @@ public class TLRPC {
}
public static abstract class ReactionCount extends TLObject {
public int flags;
public int chosen_order;
public boolean chosen; //custom
@ -53277,6 +53325,7 @@ public class TLRPC {
public static int constructor = 0x40f48462;
public int flags;
public boolean confirmed;
public long hash;
public boolean encrypted_requests_disabled;
public boolean call_requests_disabled;
@ -53287,6 +53336,7 @@ public class TLRPC {
public void serializeToStream(AbstractSerializedData stream) {
stream.writeInt32(constructor);
flags = confirmed ? (flags | 8) : (flags &~ 8);
stream.writeInt32(flags);
stream.writeInt64(hash);
if ((flags & 1) != 0) {
@ -65936,18 +65986,9 @@ public class TLRPC {
}
}
public static class TL_attachMenuBot extends AttachMenuBot {
public static class TL_attachMenuBot_layer162 extends TL_attachMenuBot {
public static int constructor = 0xc8aa2cd2;
public int flags;
public boolean inactive;
public boolean has_settings;
public boolean request_write_access;
public long bot_id;
public String short_name;
public ArrayList<AttachMenuPeerType> peer_types = new ArrayList<>();
public ArrayList<TL_attachMenuBotIcon> icons = new ArrayList<>();
public void readParams(AbstractSerializedData stream, boolean exception) {
flags = stream.readInt32(exception);
inactive = (flags & 1) != 0;
@ -66010,16 +66051,102 @@ public class TLRPC {
}
}
public static class TL_attachMenuBot extends AttachMenuBot {
public static int constructor = 0xd90d8dfe;
public int flags;
public boolean inactive;
public boolean has_settings;
public boolean request_write_access;
public boolean show_in_attach_menu;
public boolean show_in_side_menu;
public boolean side_menu_disclaimer_needed;
public long bot_id;
public String short_name;
public ArrayList<AttachMenuPeerType> peer_types = new ArrayList<>();
public ArrayList<TL_attachMenuBotIcon> icons = new ArrayList<>();
public void readParams(AbstractSerializedData stream, boolean exception) {
flags = stream.readInt32(exception);
inactive = (flags & 1) != 0;
has_settings = (flags & 2) != 0;
request_write_access = (flags & 4) != 0;
show_in_attach_menu = (flags & 8) != 0;
show_in_side_menu = (flags & 16) != 0;
side_menu_disclaimer_needed = (flags & 32) != 0;
bot_id = stream.readInt64(exception);
short_name = stream.readString(exception);
int magic = stream.readInt32(exception);
if (magic != 0x1cb5c415) {
if (exception) {
throw new RuntimeException(String.format("wrong Vector magic, got %x", magic));
}
return;
}
int count = stream.readInt32(exception);
for (int a = 0; a < count; a++) {
AttachMenuPeerType object = AttachMenuPeerType.TLdeserialize(stream, stream.readInt32(exception), exception);
if (object == null) {
return;
}
peer_types.add(object);
}
magic = stream.readInt32(exception);
if (magic != 0x1cb5c415) {
if (exception) {
throw new RuntimeException(String.format("wrong Vector magic, got %x", magic));
}
return;
}
count = stream.readInt32(exception);
for (int a = 0; a < count; a++) {
TL_attachMenuBotIcon object = TL_attachMenuBotIcon.TLdeserialize(stream, stream.readInt32(exception), exception);
if (object == null) {
return;
}
icons.add(object);
}
}
public void serializeToStream(AbstractSerializedData stream) {
stream.writeInt32(constructor);
flags = inactive ? (flags | 1) : (flags &~ 1);
flags = has_settings ? (flags | 2) : (flags &~ 2);
flags = request_write_access ? (flags | 4) : (flags &~ 4);
flags = show_in_attach_menu ? (flags | 8) : (flags &~ 8);
flags = show_in_side_menu ? (flags | 16) : (flags &~ 16);
flags = side_menu_disclaimer_needed ? (flags | 32) : (flags &~ 32);
stream.writeInt32(flags);
stream.writeInt64(bot_id);
stream.writeString(short_name);
stream.writeInt32(0x1cb5c415);
int count = peer_types.size();
stream.writeInt32(count);
for (int a = 0; a < count; a++) {
peer_types.get(a).serializeToStream(stream);
}
stream.writeInt32(0x1cb5c415);
count = icons.size();
stream.writeInt32(count);
for (int a = 0; a < count; a++) {
icons.get(a).serializeToStream(stream);
}
}
}
public static abstract class AttachMenuBot extends TLObject {
public static TL_attachMenuBot TLdeserialize(AbstractSerializedData stream, int constructor, boolean exception) {
TL_attachMenuBot result = null;
switch (constructor) {
case 0xe93cb772:
result = new TL_attachMenuBot_layer140();
case 0xd90d8dfe:
result = new TL_attachMenuBot();
break;
case 0xc8aa2cd2:
result = new TL_attachMenuBot();
result = new TL_attachMenuBot_layer162();
break;
case 0xe93cb772:
result = new TL_attachMenuBot_layer140();
break;
}
if (result == null && exception) {
@ -66552,12 +66679,14 @@ public class TLRPC {
}
public static class TL_messages_requestSimpleWebView extends TLObject {
public static int constructor = 0x299bec8e;
public static int constructor = 0x1a46500a;
public int flags;
public boolean from_switch_webview;
public boolean from_side_menu;
public InputUser bot;
public String url;
public String start_param;
public TL_dataJSON theme_params;
public String platform;
@ -66568,9 +66697,15 @@ public class TLRPC {
public void serializeToStream(AbstractSerializedData stream) {
stream.writeInt32(constructor);
flags = from_switch_webview ? (flags | 2) : (flags &~ 2);
flags = from_side_menu ? (flags | 4) : (flags &~ 4);
stream.writeInt32(flags);
bot.serializeToStream(stream);
stream.writeString(url);
if ((flags & 8) != 0) {
stream.writeString(url);
}
if ((flags & 16) != 0) {
stream.writeString(start_param);
}
if ((flags & 1) != 0) {
theme_params.serializeToStream(stream);
}
@ -68696,10 +68831,10 @@ public class TLRPC {
stream.writeBool(enabled);
}
}
public static class TL_channels_clickSponsoredMessage extends TLObject {
public static int constructor = 0x18afbc93;
public InputChannel channel;
public byte[] random_id;
@ -69686,10 +69821,10 @@ public class TLRPC {
stream.writeInt32(expire_date);
}
}
public static class TL_mediaAreaCoordinates extends TLObject {
public static final int constructor = 0x3d1ea4e;
public double x;
public double y;
public double w;
@ -69728,7 +69863,7 @@ public class TLRPC {
stream.writeDouble(rotation);
}
}
public static class MediaArea extends TLObject {
public TL_mediaAreaCoordinates coordinates;
@ -69754,10 +69889,10 @@ public class TLRPC {
return result;
}
}
public static class TL_mediaAreaVenue extends MediaArea {
public static final int constructor = 0xbe82db9c;
public GeoPoint geo;
public String title;
public String address;
@ -70170,7 +70305,7 @@ public class TLRPC {
stealth_mode.serializeToStream(stream);
}
}
public static class TL_stories_canSendStory extends TLObject {
public static int constructor = 0xb100d45d;
@ -70382,15 +70517,15 @@ public class TLRPC {
stream.writeBool(hidden);
}
}
public static class TL_contacts_setBlocked extends TLObject {
public static int constructor = 0x94c65c76;
public int flags;
public boolean my_stories_from;
public ArrayList<InputPeer> id = new ArrayList<>();
public int limit;
public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) {
return Bool.TLdeserialize(stream, constructor, exception);
}
@ -70702,12 +70837,12 @@ public class TLRPC {
stream.writeInt32(limit);
}
}
public static class TL_editCloseFriends extends TLObject {
public static int constructor = 0xba6705f0;
public ArrayList<Long> id = new ArrayList<>();
public TLObject deserializeResponse(AbstractSerializedData stream, int constructor, boolean exception) {
return Bool.TLdeserialize(stream, constructor, exception);
}

View file

@ -417,12 +417,7 @@ public class ActionBar extends FrameLayout {
if (titleTextView[i] != null) {
return;
}
titleTextView[i] = new SimpleTextView(getContext()) {
@Override
public void setAlpha(float alpha) {
super.setAlpha(alpha);
}
};
titleTextView[i] = new SimpleTextView(getContext());
titleTextView[i].setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
if (titleColorToSet != 0) {
titleTextView[i].setTextColor(titleColorToSet);

View file

@ -490,7 +490,8 @@ public class ActionBarLayout extends FrameLayout implements INavigationLayout, F
@Override
public void drawHeaderShadow(Canvas canvas, int alpha, int y) {
if (headerShadowDrawable != null) {
if (headerShadowDrawable != null && SharedConfig.drawActionBarShadow) {
alpha = alpha / 2;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (headerShadowDrawable.getAlpha() != alpha) {
headerShadowDrawable.setAlpha(alpha);

View file

@ -346,6 +346,7 @@ public class ActionBarPopupWindow extends PopupWindow {
}
}
@Override
public void setBackgroundDrawable(Drawable drawable) {
backgroundColor = Color.WHITE;
backgroundDrawable = drawable;

View file

@ -195,7 +195,7 @@ public class AlertDialog extends Dialog implements Drawable.Callback, Notificati
this.blurredBackground = blurBackground;
}
private boolean supportsNativeBlur() {
protected boolean supportsNativeBlur() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && LaunchActivity.systemBlurEnabled;
}
@ -305,10 +305,7 @@ public class AlertDialog extends Dialog implements Drawable.Callback, Notificati
shownAt = System.currentTimeMillis();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
protected View inflateContent(boolean setContent) {
LinearLayout containerView = new LinearLayout(getContext()) {
private boolean inLayout;
@ -594,7 +591,9 @@ public class AlertDialog extends Dialog implements Drawable.Callback, Notificati
}
}
containerView.setFitsSystemWindows(Build.VERSION.SDK_INT >= 21);
setContentView(containerView);
if (setContent) {
setContentView(containerView);
}
final boolean hasButtons = positiveButtonText != null || negativeButtonText != null || neutralButtonText != null;
@ -1111,6 +1110,13 @@ public class AlertDialog extends Dialog implements Drawable.Callback, Notificati
window.setAttributes(params);
return containerView;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
inflateContent(true);
NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.emojiLoaded);
}
@ -1464,7 +1470,11 @@ public class AlertDialog extends Dialog implements Drawable.Callback, Notificati
}
public Builder(Context context, int progressViewStyle, Theme.ResourcesProvider resourcesProvider) {
alertDialog = new AlertDialog(context, progressViewStyle, resourcesProvider);
alertDialog = createAlertDialog(context, progressViewStyle, resourcesProvider);
}
protected AlertDialog createAlertDialog(Context context, int progressViewStyle, Theme.ResourcesProvider resourcesProvider) {
return new AlertDialog(context, progressViewStyle, resourcesProvider);
}
public Context getContext() {

View file

@ -0,0 +1,233 @@
package org.telegram.ui.ActionBar;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Build;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import org.telegram.messenger.AndroidUtilities;
public class AlertDialogDecor extends AlertDialog {
private static final int[] ATTRS = new int[]{
android.R.attr.windowEnterAnimation,
android.R.attr.windowExitAnimation
};
private static final int DIM_DURATION = 300;
private int resEnterAnimation;
private int resExitAnimation;
private View rootView;
private View contentView;
private View dimView;
private OnShowListener onShowListener;
private OnDismissListener onDismissListener;
private boolean isDismissed = false;
private long openDelay = 0;
private final Runnable showRunnable = () -> {
rootView.setVisibility(View.VISIBLE);
dimView.setAlpha(0);
contentView.startAnimation(AnimationUtils.loadAnimation(getContext(), resEnterAnimation));
dimView.animate().setDuration(DIM_DURATION).alpha(1f).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (onShowListener != null) {
onShowListener.onShow(AlertDialogDecor.this);
}
}
}).start();
};
public AlertDialogDecor(Context context, int progressStyle) {
super(context, progressStyle, null);
}
public AlertDialogDecor(Context context, int progressStyle, Theme.ResourcesProvider resourcesProvider) {
super(context, progressStyle, resourcesProvider);
}
private ViewGroup getDecorView() {
return ((ViewGroup) getActivity(getContext()).getWindow().getDecorView());
}
private void extractAnimations() {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = getContext().getTheme();
theme.resolveAttribute(android.R.attr.windowAnimationStyle, typedValue, true);
TypedArray array = getContext().obtainStyledAttributes(typedValue.resourceId, ATTRS);
resEnterAnimation = array.getResourceId(0, -1);
resExitAnimation = array.getResourceId(1, -1);
array.recycle();
}
@Override
protected boolean supportsNativeBlur() {
return false;
}
@Override
public void show() {
extractAnimations();
setDismissDialogByButtons(true);
contentView = inflateContent(false);
contentView.setClickable(true);
contentView.setFitsSystemWindows(false);
WindowManager.LayoutParams params = getWindow().getAttributes();
FrameLayout container = new FrameLayout(getContext());
container.setOnClickListener(v -> dismiss());
dimView = new View(getContext());
dimView.setBackgroundColor(Theme.multAlpha(Color.BLACK, params.dimAmount));
container.addView(dimView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
final FrameLayout contentWrapper = new FrameLayout(getContext());
contentWrapper.addView(contentView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT));
FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams(params.width, FrameLayout.LayoutParams.WRAP_CONTENT);
containerParams.gravity = Gravity.CENTER;
container.addView(contentWrapper, containerParams);
rootView = container;
getDecorView().addView(rootView);
ViewCompat.requestApplyInsets(rootView);
ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
final Rect rect = new Rect();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
final Insets r = insets.getInsets(WindowInsetsCompat.Type.ime() | WindowInsetsCompat.Type.systemBars());
rect.set(r.left, r.top, r.right, r.bottom);
} else {
rect.set(insets.getStableInsetLeft(), insets.getStableInsetTop(), insets.getStableInsetRight(), insets.getStableInsetBottom());
}
contentWrapper.setPadding(rect.left, rect.top, rect.right, rect.bottom);
contentWrapper.requestLayout();
return insets;
});
rootView.setVisibility(View.INVISIBLE);
if (openDelay == 0) {
showRunnable.run();
} else {
AndroidUtilities.runOnUIThread(showRunnable, openDelay);
}
}
@Override
public void showDelayed(long delay) {
if (isShowing()) {
return;
}
openDelay = delay;
show();
}
@Override
public boolean isShowing() {
return getDecorView().indexOfChild(rootView) != -1 && !isDismissed;
}
@Override
public void setOnShowListener(@Nullable OnShowListener listener) {
onShowListener = listener;
}
@Override
public void setOnDismissListener(@Nullable OnDismissListener listener) {
onDismissListener = listener;
}
@Override
public void dismiss() {
if (!isShowing()) {
return;
}
if (isDismissed) {
return;
}
isDismissed = true;
AndroidUtilities.cancelRunOnUIThread(showRunnable);
if (rootView.getVisibility() != View.VISIBLE) {
getDecorView().removeView(rootView);
return;
}
Animation animation = AnimationUtils.loadAnimation(getContext(), resExitAnimation);
animation.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
contentView.setAlpha(0f);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
contentView.clearAnimation();
contentView.startAnimation(animation);
dimView.animate().setListener(null).cancel();
dimView.animate().setDuration(DIM_DURATION).alpha(0f).setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
getDecorView().removeView(rootView);
if (onDismissListener != null) {
onDismissListener.onDismiss(AlertDialogDecor.this);
}
}
}).start();
}
private Activity getActivity(Context context) {
if (context instanceof Activity) {
return (Activity) context;
} else if (context instanceof ContextThemeWrapper) {
return getActivity(((ContextThemeWrapper) context).getBaseContext());
}
return null;
}
public static class Builder extends AlertDialog.Builder {
protected Builder(AlertDialogDecor alert) {
super(alert);
}
public Builder(Context context) {
super(context, null);
}
public Builder(Context context, Theme.ResourcesProvider resourcesProvider) {
super(context, 0, resourcesProvider);
}
@Override
protected AlertDialog createAlertDialog(Context context, int progressViewStyle, Theme.ResourcesProvider resourcesProvider) {
return new AlertDialogDecor(context, progressViewStyle, resourcesProvider);
}
}
}

View file

@ -123,6 +123,7 @@ public class BottomSheet extends Dialog {
protected int behindKeyboardColor;
private boolean canDismissWithSwipe = true;
private boolean canDismissWithTouchOutside = true;
private boolean allowCustomAnimation = true;
private boolean showWithoutAnimation;
@ -980,6 +981,23 @@ public class BottomSheet extends Dialog {
container.requestApplyInsets();
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Bulletin.addDelegate(this, new Bulletin.Delegate() {
@Override
public int getTopOffset(int tag) {
return AndroidUtilities.statusBarHeight;
}
});
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Bulletin.removeDelegate(this);
}
};
container.setBackgroundDrawable(backDrawable);
focusable = needFocus;
@ -1321,7 +1339,11 @@ public class BottomSheet extends Dialog {
}
protected boolean canDismissWithTouchOutside() {
return true;
return canDismissWithTouchOutside;
}
public void setCanDismissWithTouchOutside(boolean value) {
canDismissWithTouchOutside = value;
}
public TextView getTitleView() {

View file

@ -25,6 +25,7 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.util.Log;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.MotionEvent;

View file

@ -31,6 +31,7 @@ public class EmojiThemes {
public TLRPC.WallPaper wallpaper;
int currentIndex = 0;
public ArrayList<ThemeItem> items = new ArrayList<>();
private final int currentAccount;
private static final int[] previewColorKeys = new int[]{
Theme.key_chat_inBubble,
@ -43,10 +44,12 @@ public class EmojiThemes {
Theme.key_chat_wallpaper_gradient_rotation
};
public EmojiThemes() {
public EmojiThemes(int currentAccount) {
this.currentAccount = currentAccount;
}
public EmojiThemes(TLRPC.TL_theme chatThemeObject, boolean isDefault) {
public EmojiThemes(int currentAccount, TLRPC.TL_theme chatThemeObject, boolean isDefault) {
this.currentAccount = currentAccount;
this.showAsDefaultStub = isDefault;
this.emoji = chatThemeObject.emoticon;
if (!isDefault) {
@ -62,8 +65,8 @@ public class EmojiThemes {
}
}
public static EmojiThemes createPreviewFullTheme(TLRPC.TL_theme tl_theme) {
EmojiThemes chatTheme = new EmojiThemes();
public static EmojiThemes createPreviewFullTheme(int currentAccount, TLRPC.TL_theme tl_theme) {
EmojiThemes chatTheme = new EmojiThemes(currentAccount);
chatTheme.emoji = tl_theme.emoticon;
for (int i = 0; i < tl_theme.settings.size(); i++) {
@ -76,9 +79,9 @@ public class EmojiThemes {
}
public static EmojiThemes createChatThemesDefault() {
public static EmojiThemes createChatThemesDefault(int currentAccount) {
EmojiThemes themeItem = new EmojiThemes();
EmojiThemes themeItem = new EmojiThemes(currentAccount);
themeItem.emoji = "";
themeItem.showAsDefaultStub = true;
@ -93,8 +96,8 @@ public class EmojiThemes {
return themeItem;
}
public static EmojiThemes createPreviewCustom() {
EmojiThemes themeItem = new EmojiThemes();
public static EmojiThemes createPreviewCustom(int currentAccount) {
EmojiThemes themeItem = new EmojiThemes(currentAccount);
themeItem.emoji = "\uD83C\uDFA8";
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("themeconfig", Activity.MODE_PRIVATE);
@ -159,8 +162,8 @@ public class EmojiThemes {
return themeItem;
}
public static EmojiThemes createHomePreviewTheme() {
EmojiThemes themeItem = new EmojiThemes();
public static EmojiThemes createHomePreviewTheme(int currentAccount) {
EmojiThemes themeItem = new EmojiThemes(currentAccount);
themeItem.emoji = "\uD83C\uDFE0";
ThemeItem blue = new ThemeItem();
@ -185,8 +188,8 @@ public class EmojiThemes {
return themeItem;
}
public static EmojiThemes createHomeQrTheme() {
EmojiThemes themeItem = new EmojiThemes();
public static EmojiThemes createHomeQrTheme(int currentAccount) {
EmojiThemes themeItem = new EmojiThemes(currentAccount);
themeItem.emoji = "\uD83C\uDFE0";
ThemeItem blue = new ThemeItem();
@ -370,11 +373,11 @@ public class EmojiThemes {
}
long themeId = getTlTheme(index).id;
loadWallpaperImage(themeId, wallPaper, callback);
loadWallpaperImage(currentAccount, themeId, wallPaper, callback);
}
public static void loadWallpaperImage(long hash, TLRPC.WallPaper wallPaper, ResultCallback<Pair<Long, Bitmap>> callback) {
ChatThemeController.getWallpaperBitmap(hash, cachedBitmap -> {
public static void loadWallpaperImage(int currentAccount, long hash, TLRPC.WallPaper wallPaper, ResultCallback<Pair<Long, Bitmap>> callback) {
ChatThemeController.getInstance(currentAccount).getWallpaperBitmap(hash, cachedBitmap -> {
if (cachedBitmap != null && callback != null) {
callback.onComplete(new Pair<>(hash, cachedBitmap));
return;
@ -401,7 +404,7 @@ public class EmojiThemes {
if (callback != null) {
callback.onComplete(new Pair<>(hash, bitmap));
}
ChatThemeController.saveWallpaperBitmap(bitmap, hash);
ChatThemeController.getInstance(currentAccount).saveWallpaperBitmap(bitmap, hash);
});
ImageLoader.getInstance().loadImageForImageReceiver(imageReceiver);
});
@ -417,7 +420,7 @@ public class EmojiThemes {
}
long themeId = getTlTheme(index).id;
Bitmap bitmap = ChatThemeController.getWallpaperThumbBitmap(themeId);
Bitmap bitmap = ChatThemeController.getInstance(currentAccount).getWallpaperThumbBitmap(themeId);
File file = getWallpaperThumbFile(themeId);
if (bitmap == null && file.exists() && file.length() > 0) {
try {

View file

@ -15,15 +15,9 @@ import org.telegram.messenger.AndroidUtilities;
public class RoundVideoShadow extends Drawable {
Paint eraserPaint;
Paint paint;
public RoundVideoShadow() {
// eraserPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
// eraserPaint.setColor(0);
// eraserPaint.setStyle(Paint.Style.FILL);
// eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShadowLayer(AndroidUtilities.dp(4), 0, 0, 0x5f000000);
}
@ -32,9 +26,6 @@ public class RoundVideoShadow extends Drawable {
public void draw(@NonNull Canvas canvas) {
float cx = getBounds().centerX();
float cy = getBounds().centerY();
// for (int a = 0; a < 2; a++) {
// canvas.drawCircle(cx, cy, AndroidUtilities.roundMessageSize / 2f - AndroidUtilities.dp(1), a == 0 ? paint : eraserPaint);
// }
float r = (getBounds().width() - AndroidUtilities.dp(8)) / 2f;
canvas.drawCircle(cx, cy - AndroidUtilities.dp(1), r, paint);
}
@ -42,7 +33,6 @@ public class RoundVideoShadow extends Drawable {
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
// eraserPaint.setAlpha(alpha);
}
@Override

View file

@ -99,6 +99,7 @@ import org.telegram.messenger.time.SunDate;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.SerializedData;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.BlurSettingsBottomSheet;
import org.telegram.ui.ChatActivity;
import org.telegram.ui.Components.AudioVisualizerDrawable;
import org.telegram.ui.Components.BackgroundGradientDrawable;
@ -3112,6 +3113,7 @@ public class Theme {
public static TextPaint chat_msgTextPaintTwoEmoji;
public static TextPaint chat_msgTextPaintThreeEmoji;
public static TextPaint chat_infoPaint;
public static TextPaint chat_infoBoldPaint;
public static TextPaint chat_stickerCommentCountPaint;
public static TextPaint chat_livePaint;
public static TextPaint chat_docNamePaint;
@ -6226,6 +6228,7 @@ public class Theme {
} catch (Exception e) {
FileLog.e(e);
}
BlurSettingsBottomSheet.onThemeApplyed();
if (previousTheme == null && save && !switchingNightTheme) {
MessagesController.getInstance(themeInfo.account).saveTheme(themeInfo, themeInfo.getAccent(false), nightTheme, false);
}
@ -8301,12 +8304,16 @@ public class Theme {
if (chat_infoPaint == null) {
chat_infoPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
chat_infoBoldPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
chat_infoBoldPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
chat_stickerCommentCountPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
chat_stickerCommentCountPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
chat_docNamePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
chat_docNamePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
chat_docBackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
chat_deleteProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
chat_deleteProgressPaint.setStyle(Paint.Style.STROKE);
chat_deleteProgressPaint.setStrokeCap(Paint.Cap.ROUND);
chat_locationTitlePaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
chat_locationTitlePaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
chat_locationAddressPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
@ -8538,7 +8545,7 @@ public class Theme {
chat_filePath[1].lineTo(AndroidUtilities.dp(14), AndroidUtilities.dp(10));
chat_filePath[1].close();
chat_flameIcon = resources.getDrawable(R.drawable.burn).mutate();
chat_flameIcon = resources.getDrawable(R.drawable.filled_fire).mutate();
chat_gifIcon = resources.getDrawable(R.drawable.msg_round_gif_m).mutate();
chat_fileStatesDrawable[0][0] = createCircleDrawableWithIcon(AndroidUtilities.dp(44), R.drawable.msg_round_play_m);
@ -8618,6 +8625,7 @@ public class Theme {
if (!fontsOnly && chat_infoPaint != null) {
chat_infoPaint.setTextSize(AndroidUtilities.dp(12));
chat_infoBoldPaint.setTextSize(AndroidUtilities.dp(12));
chat_stickerCommentCountPaint.setTextSize(AndroidUtilities.dp(11));
chat_docNamePaint.setTextSize(AndroidUtilities.dp(15));
chat_locationTitlePaint.setTextSize(AndroidUtilities.dp(15));
@ -8700,7 +8708,6 @@ public class Theme {
chat_botButtonPaint.setColor(getColor(key_chat_botButtonText));
chat_urlPaint.setColor(getColor(key_chat_linkSelectBackground));
chat_outUrlPaint.setColor(getColor(key_chat_outLinkSelectBackground));
chat_deleteProgressPaint.setColor(getColor(key_chat_secretTimeText));
chat_textSearchSelectionPaint.setColor(getColor(key_chat_textSelectBackground));
chat_msgErrorPaint.setColor(getColor(key_chat_sentError));
chat_statusPaint.setColor(getColor(key_chat_status));

View file

@ -47,7 +47,7 @@ public class ThemeColors {
defaultColors[key_dialogButton] = 0xff4991cc;
defaultColors[key_dialogButtonSelector] = 0x0f000000;
defaultColors[key_dialogScrollGlow] = 0xfff5f6f7;
defaultColors[key_dialogRoundCheckBox] = 0xff4cb4f5;
defaultColors[key_dialogRoundCheckBox] = 0xff1A9CFF;
defaultColors[key_dialogRoundCheckBoxCheck] = 0xffffffff;
defaultColors[key_dialogCameraIcon] = 0xffffffff;
defaultColors[key_dialog_inlineProgressBackground] = 0xf6f0f2f5;
@ -56,7 +56,7 @@ public class ThemeColors {
defaultColors[key_dialogSearchHint] = 0xff98a0a7;
defaultColors[key_dialogSearchIcon] = 0xffa1a8af;
defaultColors[key_dialogSearchText] = 0xff222222;
defaultColors[key_dialogFloatingButton] = 0xff4cb4f5;
defaultColors[key_dialogFloatingButton] = 0xff1A9CFF;
defaultColors[key_dialogFloatingButtonPressed] = 0x0f000000;
defaultColors[key_dialogFloatingIcon] = 0xffffffff;
defaultColors[key_dialogShadowLine] = 0x12000000;

View file

@ -22,6 +22,7 @@ import androidx.recyclerview.widget.RecyclerView;
import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.ContactsController;
import org.telegram.messenger.DialogObject;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessagesController;

View file

@ -948,7 +948,7 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter implements
}
TextView textView = cell.getTextView();
textView.setCompoundDrawablePadding(AndroidUtilities.dp(4));
textView.setCompoundDrawablesWithIntrinsicBounds(null, null, arrowDrawable, null);
textView.setCompoundDrawablesWithIntrinsicBounds(null, null, parentFragment != null && parentFragment.storiesEnabled ? null : arrowDrawable, null);
textView.getLayoutParams().width = LayoutHelper.WRAP_CONTENT;
break;
}

Some files were not shown because too many files have changed in this diff Show more