update to 10.2.0 (4056)

This commit is contained in:
dkaraush 2023-10-29 00:52:06 +04:00
parent fea5ca949a
commit b4dbcd1639
469 changed files with 40273 additions and 12680 deletions

View file

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

View file

@ -202,6 +202,10 @@ void setUserId(JNIEnv *env, jclass c, jint instanceNum, int64_t id) {
ConnectionsManager::getInstance(instanceNum).setUserId(id); ConnectionsManager::getInstance(instanceNum).setUserId(id);
} }
void setUserPremium(JNIEnv *env, jclass c, jint instanceNum, bool premium) {
ConnectionsManager::getInstance(instanceNum).setUserPremium(premium);
}
void switchBackend(JNIEnv *env, jclass c, jint instanceNum, jboolean restart) { void switchBackend(JNIEnv *env, jclass c, jint instanceNum, jboolean restart) {
ConnectionsManager::getInstance(instanceNum).switchBackend(restart); ConnectionsManager::getInstance(instanceNum).switchBackend(restart);
} }
@ -384,7 +388,7 @@ void setSystemLangCode(JNIEnv *env, jclass c, jint instanceNum, jstring langCode
} }
} }
void init(JNIEnv *env, jclass c, jint instanceNum, jint version, jint layer, jint apiId, jstring deviceModel, jstring systemVersion, jstring appVersion, jstring langCode, jstring systemLangCode, jstring configPath, jstring logPath, jstring regId, jstring cFingerprint, jstring installerId, jstring packageId, jint timezoneOffset, jlong userId, jboolean enablePushConnection, jboolean hasNetwork, jint networkType, jint performanceClass) { void init(JNIEnv *env, jclass c, jint instanceNum, jint version, jint layer, jint apiId, jstring deviceModel, jstring systemVersion, jstring appVersion, jstring langCode, jstring systemLangCode, jstring configPath, jstring logPath, jstring regId, jstring cFingerprint, jstring installerId, jstring packageId, jint timezoneOffset, jlong userId, jboolean userPremium, jboolean enablePushConnection, jboolean hasNetwork, jint networkType, jint performanceClass) {
const char *deviceModelStr = env->GetStringUTFChars(deviceModel, 0); const char *deviceModelStr = env->GetStringUTFChars(deviceModel, 0);
const char *systemVersionStr = env->GetStringUTFChars(systemVersion, 0); const char *systemVersionStr = env->GetStringUTFChars(systemVersion, 0);
const char *appVersionStr = env->GetStringUTFChars(appVersion, 0); const char *appVersionStr = env->GetStringUTFChars(appVersion, 0);
@ -397,7 +401,7 @@ void init(JNIEnv *env, jclass c, jint instanceNum, jint version, jint layer, jin
const char *installerIdStr = env->GetStringUTFChars(installerId, 0); const char *installerIdStr = env->GetStringUTFChars(installerId, 0);
const char *packageIdStr = env->GetStringUTFChars(packageId, 0); const char *packageIdStr = env->GetStringUTFChars(packageId, 0);
ConnectionsManager::getInstance(instanceNum).init((uint32_t) version, layer, apiId, std::string(deviceModelStr), std::string(systemVersionStr), std::string(appVersionStr), std::string(langCodeStr), std::string(systemLangCodeStr), std::string(configPathStr), std::string(logPathStr), std::string(regIdStr), std::string(cFingerprintStr), std::string(installerIdStr), std::string(packageIdStr), timezoneOffset, userId, true, enablePushConnection, hasNetwork, networkType, performanceClass); ConnectionsManager::getInstance(instanceNum).init((uint32_t) version, layer, apiId, std::string(deviceModelStr), std::string(systemVersionStr), std::string(appVersionStr), std::string(langCodeStr), std::string(systemLangCodeStr), std::string(configPathStr), std::string(logPathStr), std::string(regIdStr), std::string(cFingerprintStr), std::string(installerIdStr), std::string(packageIdStr), timezoneOffset, userId, userPremium, true, enablePushConnection, hasNetwork, networkType, performanceClass);
if (deviceModelStr != 0) { if (deviceModelStr != 0) {
env->ReleaseStringUTFChars(deviceModel, deviceModelStr); env->ReleaseStringUTFChars(deviceModel, deviceModelStr);
@ -457,7 +461,7 @@ static JNINativeMethod ConnectionsManagerMethods[] = {
{"native_setProxySettings", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", (void *) setProxySettings}, {"native_setProxySettings", "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", (void *) setProxySettings},
{"native_getConnectionState", "(I)I", (void *) getConnectionState}, {"native_getConnectionState", "(I)I", (void *) getConnectionState},
{"native_setUserId", "(IJ)V", (void *) setUserId}, {"native_setUserId", "(IJ)V", (void *) setUserId},
{"native_init", "(IIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IJZZII)V", (void *) init}, {"native_init", "(IIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IJZZZII)V", (void *) init},
{"native_setLangCode", "(ILjava/lang/String;)V", (void *) setLangCode}, {"native_setLangCode", "(ILjava/lang/String;)V", (void *) setLangCode},
{"native_setRegId", "(ILjava/lang/String;)V", (void *) setRegId}, {"native_setRegId", "(ILjava/lang/String;)V", (void *) setRegId},
{"native_setSystemLangCode", "(ILjava/lang/String;)V", (void *) setSystemLangCode}, {"native_setSystemLangCode", "(ILjava/lang/String;)V", (void *) setSystemLangCode},

View file

@ -483,19 +483,19 @@ void TL_user::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &er
lang_code = stream->readString(&error); lang_code = stream->readString(&error);
} }
if ((flags & 1073741824) != 0) { if ((flags & 1073741824) != 0) {
uint32_t magic = stream->readUint32(&error); emojiStatusMagic = stream->readUint32(&error);
if (magic == 0x2de11aae) { if (emojiStatusMagic == 0x2de11aae) {
// emojiStatusEmpty // emojiStatusEmpty
} else if (magic == 0x929b619d) { } else if (emojiStatusMagic == 0x929b619d) {
// emojiStatus // emojiStatus
int64_t document_id = stream->readInt64(&error); emojiStatusDocumentId = stream->readInt64(&error);
} else if (magic == 0xfa30a8c7) { } else if (emojiStatusMagic == 0xfa30a8c7) {
// emojiStatusUntil // emojiStatusUntil
int64_t document_id = stream->readInt64(&error); emojiStatusDocumentId = stream->readInt64(&error);
int until = stream->readInt32(&error); emojiStatusUntil = stream->readInt32(&error);
} else { } else {
error = true; error = true;
if (LOGS_ENABLED) DEBUG_FATAL("wrong EmojiStatus magic, got %x", magic); if (LOGS_ENABLED) DEBUG_FATAL("wrong EmojiStatus magic, got %x", emojiStatusMagic);
return; return;
} }
} }
@ -518,6 +518,12 @@ void TL_user::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &er
if ((flags2 & 32) != 0) { if ((flags2 & 32) != 0) {
stories_max_id = stream->readInt32(&error); stories_max_id = stream->readInt32(&error);
} }
if ((flags2 & 128) != 0) {
color = stream->readInt32(&error);
}
if ((flags2 & 64) != 0) {
background_emoji_id = stream->readInt64(&error);
}
} }
void TL_user::serializeToStream(NativeByteBuffer *stream) { void TL_user::serializeToStream(NativeByteBuffer *stream) {
@ -563,6 +569,34 @@ void TL_user::serializeToStream(NativeByteBuffer *stream) {
if ((flags & 4194304) != 0) { if ((flags & 4194304) != 0) {
stream->writeString(lang_code); stream->writeString(lang_code);
} }
if ((flags & 1073741824) != 0) {
stream->writeInt32(emojiStatusMagic);
if (emojiStatusMagic == 0x929b619d) {
// emojiStatus
stream->writeInt64(emojiStatusDocumentId);
} else if (emojiStatusMagic == 0xfa30a8c7) {
// emojiStatusUntil
stream->writeInt64(emojiStatusDocumentId);
stream->writeInt32(emojiStatusUntil);
}
}
if ((flags2 & 1) != 0) {
stream->writeInt32(0x1cb5c415);
int32_t count = (int32_t) usernames.size();
stream->writeInt32(count);
for (int a = 0; a < count; a++) {
usernames[a]->serializeToStream(stream);
}
}
if ((flags2 & 32) != 0) {
stream->writeInt32(stories_max_id);
}
if ((flags2 & 128) != 0) {
stream->writeInt32(color);
}
if ((flags2 & 64) != 0) {
stream->writeInt64(background_emoji_id);
}
} }
InputPeer *InputPeer::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) { InputPeer *InputPeer::TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error) {

View file

@ -336,6 +336,11 @@ public:
std::string lang_code; std::string lang_code;
std::vector<std::unique_ptr<TL_username>> usernames; std::vector<std::unique_ptr<TL_username>> usernames;
int32_t stories_max_id; int32_t stories_max_id;
int32_t emojiStatusMagic;
int64_t emojiStatusDocumentId;
int32_t emojiStatusUntil;
int32_t color;
int64_t background_emoji_id;
static User *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error); static User *TLdeserialize(NativeByteBuffer *stream, uint32_t constructor, int32_t instanceNum, bool &error);
}; };
@ -352,7 +357,7 @@ public:
class TL_user : public User { class TL_user : public User {
public: public:
static const uint32_t constructor = 0xabb5f120; static const uint32_t constructor = 0xeb602f25;
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error); void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
void serializeToStream(NativeByteBuffer *stream); void serializeToStream(NativeByteBuffer *stream);

View file

@ -666,6 +666,7 @@ void ConnectionsManager::cleanUp(bool resetKeys, int32_t datacenterId) {
if (datacenterId == -1) { if (datacenterId == -1) {
sessionsToDestroy.clear(); sessionsToDestroy.clear();
currentUserId = 0; currentUserId = 0;
currentUserPremium = false;
registeredForInternalPush = false; registeredForInternalPush = false;
} }
saveConfig(); saveConfig();
@ -1389,6 +1390,7 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag
} else if (datacenter->getDatacenterId() == currentDatacenterId || datacenter->getDatacenterId() == movingToDatacenterId) { } else if (datacenter->getDatacenterId() == currentDatacenterId || datacenter->getDatacenterId() == movingToDatacenterId) {
if (request->connectionType & ConnectionTypeGeneric && currentUserId) { if (request->connectionType & ConnectionTypeGeneric && currentUserId) {
currentUserId = 0; currentUserId = 0;
currentUserPremium = false;
if (delegate != nullptr) { if (delegate != nullptr) {
delegate->onLogout(instanceNum); delegate->onLogout(instanceNum);
} }
@ -1889,6 +1891,12 @@ void ConnectionsManager::bindRequestToGuid(int32_t requestToken, int32_t guid) {
}); });
} }
void ConnectionsManager::setUserPremium(bool premium) {
scheduleTask([&, premium] {
currentUserPremium = premium;
});
}
void ConnectionsManager::setUserId(int64_t userId) { void ConnectionsManager::setUserId(int64_t userId) {
scheduleTask([&, userId] { scheduleTask([&, userId] {
int32_t oldUserId = currentUserId; int32_t oldUserId = currentUserId;
@ -1989,7 +1997,7 @@ bool ConnectionsManager::cancelRequestInternal(int32_t token, int64_t messageId,
if (notifyServer) { if (notifyServer) {
auto dropAnswer = new TL_rpc_drop_answer(); auto dropAnswer = new TL_rpc_drop_answer();
dropAnswer->req_msg_id = request->messageId; dropAnswer->req_msg_id = request->messageId;
sendRequest(dropAnswer, nullptr, nullptr, RequestFlagEnableUnauthorized | RequestFlagWithoutLogin | RequestFlagFailOnServerErrors, request->datacenterId, request->connectionType, true); sendRequest(dropAnswer, nullptr, nullptr, RequestFlagEnableUnauthorized | RequestFlagWithoutLogin | RequestFlagFailOnServerErrors | RequestFlagIsCancel, request->datacenterId, request->connectionType, true);
} }
request->cancelled = true; request->cancelled = true;
if (LOGS_ENABLED) DEBUG_D("cancelled running rpc request %p - %s", request->rawRequest, typeid(*request->rawRequest).name()); if (LOGS_ENABLED) DEBUG_D("cancelled running rpc request %p - %s", request->rawRequest, typeid(*request->rawRequest).name());
@ -2214,6 +2222,7 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
neededDatacenters.clear(); neededDatacenters.clear();
unauthorizedDatacenters.clear(); unauthorizedDatacenters.clear();
downloadRunningRequestCount.clear(); downloadRunningRequestCount.clear();
downloadCancelRunningRequestCount.clear();
int64_t currentTimeMillis = getCurrentTimeMonotonicMillis(); int64_t currentTimeMillis = getCurrentTimeMonotonicMillis();
auto currentTime = (int32_t) (currentTimeMillis / 1000); auto currentTime = (int32_t) (currentTimeMillis / 1000);
@ -2250,14 +2259,15 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
genericRunningRequestCount++; genericRunningRequestCount++;
break; break;
case ConnectionTypeDownload: { case ConnectionTypeDownload: {
auto map = request->isCancelRequest() ? downloadCancelRunningRequestCount : downloadRunningRequestCount;
uint32_t currentCount; uint32_t currentCount;
auto dcIter = downloadRunningRequestCount.find(datacenterId); auto dcIter = map.find(datacenterId);
if (dcIter != downloadRunningRequestCount.end()) { if (dcIter != map.end()) {
currentCount = dcIter->second; currentCount = dcIter->second;
} else { } else {
currentCount = 0; currentCount = 0;
} }
downloadRunningRequestCount[datacenterId] = currentCount + 1; map[datacenterId] = currentCount + 1;
break; break;
} }
case ConnectionTypeUpload: case ConnectionTypeUpload:
@ -2473,6 +2483,8 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
Request *request = iter->get(); Request *request = iter->get();
if (request->cancelled) { if (request->cancelled) {
iter = requestsQueue.erase(iter); iter = requestsQueue.erase(iter);
if (LOGS_ENABLED)
DEBUG_D("skip queue, token = %d: cancelled", request->requestToken);
continue; continue;
} }
if (hasInvokeWaitMessage && (request->requestFlags & RequestFlagInvokeAfter) != 0 && (request->requestFlags & RequestFlagResendAfter) == 0) { if (hasInvokeWaitMessage && (request->requestFlags & RequestFlagInvokeAfter) != 0 && (request->requestFlags & RequestFlagResendAfter) == 0) {
@ -2490,6 +2502,8 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
if (datacenterId == DEFAULT_DATACENTER_ID) { if (datacenterId == DEFAULT_DATACENTER_ID) {
if (movingToDatacenterId != DEFAULT_DATACENTER_ID) { if (movingToDatacenterId != DEFAULT_DATACENTER_ID) {
iter++; iter++;
if (LOGS_ENABLED)
DEBUG_D("skip queue, token = %d: moving dc", request->requestToken);
continue; continue;
} }
datacenterId = currentDatacenterId; datacenterId = currentDatacenterId;
@ -2535,6 +2549,8 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
if (std::find(unknownDatacenterIds.begin(), unknownDatacenterIds.end(), datacenterId) == unknownDatacenterIds.end()) { if (std::find(unknownDatacenterIds.begin(), unknownDatacenterIds.end(), datacenterId) == unknownDatacenterIds.end()) {
unknownDatacenterIds.push_back(datacenterId); unknownDatacenterIds.push_back(datacenterId);
} }
if (LOGS_ENABLED)
DEBUG_D("skip queue, token = %d: unknown dc", request->requestToken);
iter++; iter++;
continue; continue;
} else { } else {
@ -2548,12 +2564,16 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
if (std::find(neededDatacenters.begin(), neededDatacenters.end(), pair) == neededDatacenters.end()) { if (std::find(neededDatacenters.begin(), neededDatacenters.end(), pair) == neededDatacenters.end()) {
neededDatacenters.push_back(pair); neededDatacenters.push_back(pair);
} }
if (LOGS_ENABLED)
DEBUG_D("skip queue, token = %d: no authkey for dc", request->requestToken);
iter++; iter++;
continue; continue;
} else if (!(request->requestFlags & RequestFlagEnableUnauthorized) && !requestDatacenter->authorized && request->datacenterId != DEFAULT_DATACENTER_ID && request->datacenterId != currentDatacenterId) { } else if (!(request->requestFlags & RequestFlagEnableUnauthorized) && !requestDatacenter->authorized && request->datacenterId != DEFAULT_DATACENTER_ID && request->datacenterId != currentDatacenterId) {
if (std::find(unauthorizedDatacenters.begin(), unauthorizedDatacenters.end(), requestDatacenter) == unauthorizedDatacenters.end()) { if (std::find(unauthorizedDatacenters.begin(), unauthorizedDatacenters.end(), requestDatacenter) == unauthorizedDatacenters.end()) {
unauthorizedDatacenters.push_back(requestDatacenter); unauthorizedDatacenters.push_back(requestDatacenter);
} }
if (LOGS_ENABLED)
DEBUG_D("skip queue, token = %d: dc is unauthorized", request->requestToken);
iter++; iter++;
continue; continue;
} }
@ -2563,6 +2583,8 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
if (request->connectionType & ConnectionTypeGeneric && connection->getConnectionToken() == 0) { if (request->connectionType & ConnectionTypeGeneric && connection->getConnectionToken() == 0) {
iter++; iter++;
if (LOGS_ENABLED)
DEBUG_D("skip queue, token = %d: generic && connectionToken == 0", request->requestToken);
continue; continue;
} }
@ -2571,20 +2593,38 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
case ConnectionTypeGenericMedia: case ConnectionTypeGenericMedia:
if (!canUseUnboundKey && genericRunningRequestCount >= 60) { if (!canUseUnboundKey && genericRunningRequestCount >= 60) {
iter++; iter++;
DEBUG_D("skip queue, token = %d: generic type: running generic requests >= 60", request->requestToken);
continue; continue;
} }
genericRunningRequestCount++; genericRunningRequestCount++;
break; break;
case ConnectionTypeDownload: { case ConnectionTypeDownload: {
uint32_t currentCount; uint32_t currentCount;
auto dcIter = downloadRunningRequestCount.find(datacenterId); auto map = request->isCancelRequest() ? downloadCancelRunningRequestCount : downloadRunningRequestCount;
if (dcIter != downloadRunningRequestCount.end()) { auto dcIter = map.find(datacenterId);
if (dcIter != map.end()) {
currentCount = dcIter->second; currentCount = dcIter->second;
} else { } else {
currentCount = 0; currentCount = 0;
} }
if (!networkAvailable || currentCount >= 16) { if (!networkAvailable) {
iter++; iter++;
if (LOGS_ENABLED)
DEBUG_D("skip queue, token = %d: download type: network unavailable", request->requestToken);
continue;
}
int max;
if (request->isCancelRequest()) {
max = 24;
} else if (currentUserPremium) {
max = 32;
} else {
max = 16;
}
if (currentCount >= max) {
iter++;
if (LOGS_ENABLED)
DEBUG_D("skip queue, token = %d: download type: running download requests >= %d", request->requestToken, max);
continue; continue;
} }
downloadRunningRequestCount[datacenterId] = currentCount + 1; downloadRunningRequestCount[datacenterId] = currentCount + 1;
@ -2594,12 +2634,22 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
case ConnectionTypeTemp: case ConnectionTypeTemp:
if (!networkAvailable) { if (!networkAvailable) {
iter++; iter++;
if (LOGS_ENABLED)
DEBUG_D("skip queue, token = %d: proxy/temp type: network unavailable", request->requestToken);
continue; continue;
} }
break; break;
case ConnectionTypeUpload: case ConnectionTypeUpload:
if (!networkAvailable || uploadRunningRequestCount >= 10) { if (!networkAvailable) {
iter++; iter++;
if (LOGS_ENABLED)
DEBUG_D("skip queue, token = %d: upload type: network unavailable", request->requestToken);
continue;
}
if (uploadRunningRequestCount >= 10) {
iter++;
if (LOGS_ENABLED)
DEBUG_D("skip queue, token = %d: upload type: running upload requests >= 10", request->requestToken);
continue; continue;
} }
uploadRunningRequestCount++; uploadRunningRequestCount++;
@ -3057,14 +3107,13 @@ void ConnectionsManager::updateDcSettings(uint32_t dcNum, bool workaround, bool
if ((!workaround && !updatingDcSettings) || (workaround && !updatingDcSettingsWorkaround)) { if ((!workaround && !updatingDcSettings) || (workaround && !updatingDcSettingsWorkaround)) {
return; return;
} }
if (!workaround && updatingDcSettingsAgain && updatingDcSettingsAgainDcNum == dcNum) { if (!workaround && updatingDcSettingsAgain) {
updatingDcSettings = false;
updatingDcSettingsAgain = false; updatingDcSettingsAgain = false;
for (auto & datacenter : datacenters) { for (auto & datacenter : datacenters) {
if (datacenter.first == dcNum) { datacenter.second->resetInitVersion();
datacenter.second->resetInitVersion();
}
} }
updateDcSettings(updatingDcSettingsAgainDcNum, false, false); updateDcSettings(0, false, false);
return; return;
} }
@ -3362,7 +3411,7 @@ void ConnectionsManager::applyDnsConfig(NativeByteBuffer *buffer, std::string ph
}); });
} }
void ConnectionsManager::init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel, std::string systemVersion, std::string appVersion, std::string langCode, std::string systemLangCode, std::string configPath, std::string logPath, std::string regId, std::string cFingerpting, std::string installerId, std::string packageId, int32_t timezoneOffset, int64_t userId, bool isPaused, bool enablePushConnection, bool hasNetwork, int32_t networkType, int32_t performanceClass) { void ConnectionsManager::init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel, std::string systemVersion, std::string appVersion, std::string langCode, std::string systemLangCode, std::string configPath, std::string logPath, std::string regId, std::string cFingerpting, std::string installerId, std::string packageId, int32_t timezoneOffset, int64_t userId, bool userPremium, bool isPaused, bool enablePushConnection, bool hasNetwork, int32_t networkType, int32_t performanceClass) {
currentVersion = version; currentVersion = version;
currentLayer = layer; currentLayer = layer;
currentApiId = apiId; currentApiId = apiId;
@ -3378,6 +3427,7 @@ void ConnectionsManager::init(uint32_t version, int32_t layer, int32_t apiId, st
currentDeviceTimezone = timezoneOffset; currentDeviceTimezone = timezoneOffset;
currentSystemLangCode = systemLangCode; currentSystemLangCode = systemLangCode;
currentUserId = userId; currentUserId = userId;
currentUserPremium = userPremium;
currentLogPath = logPath; currentLogPath = logPath;
pushConnectionEnabled = enablePushConnection; pushConnectionEnabled = enablePushConnection;
currentNetworkType = networkType; currentNetworkType = networkType;
@ -3675,9 +3725,8 @@ void ConnectionsManager::reconnect(int32_t dcId, int32_t connectionType) {
Connection *connection = datacenter->getConnectionByType(connectionType, false, Connection *connection = datacenter->getConnectionByType(connectionType, false,
0); 0);
if (connection != nullptr) { if (connection != nullptr) {
if (LOGS_ENABLED) DEBUG_D("discard connection dcId=%d connectionType=%d", dcId,
DEBUG_D("discard connection dcId=%d connectionType=%d", dcId, connectionType);
connectionType);
connection->suspendConnection(true); connection->suspendConnection(true);
} }
} }

View file

@ -59,12 +59,13 @@ public:
void setDelegate(ConnectiosManagerDelegate *connectiosManagerDelegate); void setDelegate(ConnectiosManagerDelegate *connectiosManagerDelegate);
ConnectionState getConnectionState(); ConnectionState getConnectionState();
void setUserId(int64_t userId); void setUserId(int64_t userId);
void setUserPremium(bool premium);
void switchBackend(bool restart); void switchBackend(bool restart);
void resumeNetwork(bool partial); void resumeNetwork(bool partial);
void pauseNetwork(); void pauseNetwork();
void setNetworkAvailable(bool value, int32_t type, bool slow); void setNetworkAvailable(bool value, int32_t type, bool slow);
void setIpStrategy(uint8_t value); void setIpStrategy(uint8_t value);
void init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel, std::string systemVersion, std::string appVersion, std::string langCode, std::string systemLangCode, std::string configPath, std::string logPath, std::string regId, std::string cFingerprint, std::string installerId, std::string packageId, int32_t timezoneOffset, int64_t userId, bool isPaused, bool enablePushConnection, bool hasNetwork, int32_t networkType, int32_t performanceClass); void init(uint32_t version, int32_t layer, int32_t apiId, std::string deviceModel, std::string systemVersion, std::string appVersion, std::string langCode, std::string systemLangCode, std::string configPath, std::string logPath, std::string regId, std::string cFingerprint, std::string installerId, std::string packageId, int32_t timezoneOffset, int64_t userId, bool userPremium, bool isPaused, bool enablePushConnection, bool hasNetwork, int32_t networkType, int32_t performanceClass);
void setProxySettings(std::string address, uint16_t port, std::string username, std::string password, std::string secret); void setProxySettings(std::string address, uint16_t port, std::string username, std::string password, std::string secret);
void setLangCode(std::string langCode); void setLangCode(std::string langCode);
void setRegId(std::string regId); void setRegId(std::string regId);
@ -231,6 +232,7 @@ private:
std::string currentConfigPath; std::string currentConfigPath;
std::string currentLogPath; std::string currentLogPath;
int64_t currentUserId = 0; int64_t currentUserId = 0;
bool currentUserPremium = false;
bool registeredForInternalPush = false; bool registeredForInternalPush = false;
bool pushConnectionEnabled = true; bool pushConnectionEnabled = true;
int32_t currentPerformanceClass = -1; int32_t currentPerformanceClass = -1;
@ -241,6 +243,7 @@ private:
std::vector<uint32_t> unknownDatacenterIds; std::vector<uint32_t> unknownDatacenterIds;
std::vector<std::pair<Datacenter *, ConnectionType>> neededDatacenters; std::vector<std::pair<Datacenter *, ConnectionType>> neededDatacenters;
std::map<uint32_t, uint32_t> downloadRunningRequestCount; std::map<uint32_t, uint32_t> downloadRunningRequestCount;
std::map<uint32_t, uint32_t> downloadCancelRunningRequestCount;
std::vector<Datacenter *> unauthorizedDatacenters; std::vector<Datacenter *> unauthorizedDatacenters;
NativeByteBuffer *sizeCalculator; NativeByteBuffer *sizeCalculator;

View file

@ -170,7 +170,8 @@ enum RequestFlag {
RequestFlagNeedQuickAck = 128, RequestFlagNeedQuickAck = 128,
RequestFlagUseUnboundKey = 256, RequestFlagUseUnboundKey = 256,
RequestFlagResendAfter = 512, RequestFlagResendAfter = 512,
RequestFlagIgnoreFloodWait = 1024 RequestFlagIgnoreFloodWait = 1024,
RequestFlagIsCancel = 32768
}; };
inline std::string to_string_int32(int32_t value) { inline std::string to_string_int32(int32_t value) {

View file

@ -85,6 +85,10 @@ bool Request::isMediaRequest() {
return Connection::isMediaConnectionType(connectionType); return Connection::isMediaConnectionType(connectionType);
} }
bool Request::isCancelRequest() {
return (requestFlags & RequestFlagIsCancel) != 0;
}
bool Request::needInitRequest(Datacenter *datacenter, uint32_t currentVersion) { bool Request::needInitRequest(Datacenter *datacenter, uint32_t currentVersion) {
bool media = PFS_ENABLED && datacenter != nullptr && isMediaRequest() && datacenter->hasMediaAddress(); bool media = PFS_ENABLED && datacenter != nullptr && isMediaRequest() && datacenter->hasMediaAddress();
return !media && datacenter->lastInitVersion != currentVersion || media && datacenter->lastInitMediaVersion != currentVersion; return !media && datacenter->lastInitVersion != currentVersion || media && datacenter->lastInitMediaVersion != currentVersion;

View file

@ -63,6 +63,7 @@ public:
void onQuickAck(); void onQuickAck();
void onWriteToSocket(); void onWriteToSocket();
bool isMediaRequest(); bool isMediaRequest();
bool isCancelRequest();
bool hasInitFlag(); bool hasInitFlag();
bool needInitRequest(Datacenter *datacenter, uint32_t currentVersion); bool needInitRequest(Datacenter *datacenter, uint32_t currentVersion);
TLObject *getRpcRequest(); TLObject *getRpcRequest();

View file

@ -0,0 +1,21 @@
#ifndef TGCALLS_DIRECT_CONNECTION_CHANNEL_H
#define TGCALLS_DIRECT_CONNECTION_CHANNEL_H
#include <functional>
#include <memory>
#include <vector>
namespace tgcalls {
class DirectConnectionChannel {
public:
virtual ~DirectConnectionChannel() = default;
virtual std::vector<uint8_t> addOnIncomingPacket(std::function<void(std::shared_ptr<std::vector<uint8_t>>)> &&) = 0;
virtual void removeOnIncomingPacket(std::vector<uint8_t> &token) = 0;
virtual void sendPacket(std::unique_ptr<std::vector<uint8_t>> &&packet) = 0;
};
} // namespace tgcalls
#endif

View file

@ -8,6 +8,7 @@
#include <map> #include <map>
#include "Stats.h" #include "Stats.h"
#include "DirectConnectionChannel.h"
namespace rtc { namespace rtc {
template <typename VideoFrameT> template <typename VideoFrameT>
@ -186,7 +187,7 @@ public:
virtual void setEchoCancellationStrength(int strength) = 0; virtual void setEchoCancellationStrength(int strength) = 0;
virtual bool supportsVideo() = 0; virtual bool supportsVideo() = 0;
virtual void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) = 0; virtual void setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) = 0;
virtual void setAudioInputDevice(std::string id) = 0; virtual void setAudioInputDevice(std::string id) = 0;
virtual void setAudioOutputDevice(std::string id) = 0; virtual void setAudioOutputDevice(std::string id) = 0;
@ -237,6 +238,7 @@ struct Descriptor {
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> createAudioDeviceModule; std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> createAudioDeviceModule;
std::string initialInputDeviceId; std::string initialInputDeviceId;
std::string initialOutputDeviceId; std::string initialOutputDeviceId;
std::shared_ptr<DirectConnectionChannel> directConnectionChannel;
std::shared_ptr<PlatformContext> platformContext; std::shared_ptr<PlatformContext> platformContext;
}; };

View file

@ -93,7 +93,7 @@ void InstanceImpl::setMuteMicrophone(bool muteMicrophone) {
}); });
} }
void InstanceImpl::setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) { void InstanceImpl::setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
_manager->perform([sink](Manager *manager) { _manager->perform([sink](Manager *manager) {
manager->setIncomingVideoOutput(sink); manager->setIncomingVideoOutput(sink);
}); });
@ -166,6 +166,8 @@ PersistentState InstanceImpl::getPersistentState() {
} }
void InstanceImpl::stop(std::function<void(FinalState)> completion) { void InstanceImpl::stop(std::function<void(FinalState)> completion) {
RTC_LOG(LS_INFO) << "Stopping InstanceImpl";
std::string debugLog = _logSink->result(); std::string debugLog = _logSink->result();
_manager->perform([completion, debugLog = std::move(debugLog)](Manager *manager) { _manager->perform([completion, debugLog = std::move(debugLog)](Manager *manager) {

View file

@ -28,7 +28,7 @@ public:
bool supportsVideo() override { bool supportsVideo() override {
return true; return true;
} }
void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override; void setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override;
void setAudioOutputGainControlEnabled(bool enabled) override; void setAudioOutputGainControlEnabled(bool enabled) override;
void setEchoCancellationStrength(int strength) override; void setEchoCancellationStrength(int strength) override;
void setAudioInputDevice(std::string id) override; void setAudioInputDevice(std::string id) override;

View file

@ -98,7 +98,7 @@ _remoteBatteryLevelIsLowUpdated(std::move(descriptor.remoteBatteryLevelIsLowUpda
_remotePrefferedAspectRatioUpdated(std::move(descriptor.remotePrefferedAspectRatioUpdated)), _remotePrefferedAspectRatioUpdated(std::move(descriptor.remotePrefferedAspectRatioUpdated)),
_signalingDataEmitted(std::move(descriptor.signalingDataEmitted)), _signalingDataEmitted(std::move(descriptor.signalingDataEmitted)),
_signalBarsUpdated(std::move(descriptor.signalBarsUpdated)), _signalBarsUpdated(std::move(descriptor.signalBarsUpdated)),
_audioLevelsUpdated(std::move(descriptor.audioLevelsUpdated)), _audioLevelUpdated(std::move(descriptor.audioLevelsUpdated)),
_createAudioDeviceModule(std::move(descriptor.createAudioDeviceModule)), _createAudioDeviceModule(std::move(descriptor.createAudioDeviceModule)),
_enableHighBitrateVideo(descriptor.config.enableHighBitrateVideo), _enableHighBitrateVideo(descriptor.config.enableHighBitrateVideo),
_dataSaving(descriptor.config.dataSaving), _dataSaving(descriptor.config.dataSaving),
@ -223,7 +223,7 @@ void Manager::start() {
}); });
})); }));
bool isOutgoing = _encryptionKey.isOutgoing; bool isOutgoing = _encryptionKey.isOutgoing;
_mediaManager.reset(new ThreadLocalObject<MediaManager>(StaticThreads::getMediaThread(), [weak, isOutgoing, protocolVersion = _protocolVersion, thread, sendSignalingMessage, videoCapture = _videoCapture, mediaDevicesConfig = _mediaDevicesConfig, enableHighBitrateVideo = _enableHighBitrateVideo, signalBarsUpdated = _signalBarsUpdated, audioLevelsUpdated = _audioLevelsUpdated, preferredCodecs = _preferredCodecs, createAudioDeviceModule = _createAudioDeviceModule, platformContext = _platformContext]() { _mediaManager.reset(new ThreadLocalObject<MediaManager>(StaticThreads::getMediaThread(), [weak, isOutgoing, protocolVersion = _protocolVersion, thread, sendSignalingMessage, videoCapture = _videoCapture, mediaDevicesConfig = _mediaDevicesConfig, enableHighBitrateVideo = _enableHighBitrateVideo, signalBarsUpdated = _signalBarsUpdated, audioLevelsUpdated = _audioLevelUpdated, preferredCodecs = _preferredCodecs, createAudioDeviceModule = _createAudioDeviceModule, platformContext = _platformContext]() {
return new MediaManager( return new MediaManager(
StaticThreads::getMediaThread(), StaticThreads::getMediaThread(),
isOutgoing, isOutgoing,

View file

@ -69,7 +69,7 @@ private:
std::function<void(float)> _remotePrefferedAspectRatioUpdated; std::function<void(float)> _remotePrefferedAspectRatioUpdated;
std::function<void(const std::vector<uint8_t> &)> _signalingDataEmitted; std::function<void(const std::vector<uint8_t> &)> _signalingDataEmitted;
std::function<void(int)> _signalBarsUpdated; std::function<void(int)> _signalBarsUpdated;
std::function<void(float, float)> _audioLevelsUpdated; std::function<void(float, float)> _audioLevelUpdated;
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule; std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule;
std::function<uint32_t(const Message &)> _sendSignalingMessage; std::function<uint32_t(const Message &)> _sendSignalingMessage;
std::function<void(Message&&)> _sendTransportMessage; std::function<void(Message&&)> _sendTransportMessage;

View file

@ -256,7 +256,7 @@ _taskQueueFactory(webrtc::CreateDefaultTaskQueueFactory()),
_sendSignalingMessage(std::move(sendSignalingMessage)), _sendSignalingMessage(std::move(sendSignalingMessage)),
_sendTransportMessage(std::move(sendTransportMessage)), _sendTransportMessage(std::move(sendTransportMessage)),
_signalBarsUpdated(std::move(signalBarsUpdated)), _signalBarsUpdated(std::move(signalBarsUpdated)),
_audioLevelsUpdated(std::move(audioLevelsUpdated)), _audioLevelUpdated(std::move(audioLevelsUpdated)),
_createAudioDeviceModule(std::move(createAudioDeviceModule)), _createAudioDeviceModule(std::move(createAudioDeviceModule)),
_protocolVersion(protocolVersion), _protocolVersion(protocolVersion),
_outgoingVideoState(videoCapture ? VideoState::Active : VideoState::Inactive), _outgoingVideoState(videoCapture ? VideoState::Active : VideoState::Inactive),
@ -467,7 +467,7 @@ void MediaManager::start() {
} }
beginStatsTimer(3000); beginStatsTimer(3000);
if (_audioLevelsUpdated != nullptr) { if (_audioLevelUpdated != nullptr) {
beginLevelsTimer(100); beginLevelsTimer(100);
} }
} }
@ -595,7 +595,7 @@ void MediaManager::beginLevelsTimer(int timeoutMs) {
return; return;
} }
strong->_audioLevelsUpdated(strong->_currentMyAudioLevel, strong->_currentAudioLevel); strong->_audioLevelUpdated(strong->_currentMyAudioLevel, strong->_currentAudioLevel);
strong->beginLevelsTimer(100); strong->beginLevelsTimer(100);
}, webrtc::TimeDelta::Millis(timeoutMs)); }, webrtc::TimeDelta::Millis(timeoutMs));
@ -689,7 +689,7 @@ void MediaManager::setSendVideo(std::shared_ptr<VideoCaptureInterface> videoCapt
const auto object = GetVideoCaptureAssumingSameThread(_videoCapture.get()); const auto object = GetVideoCaptureAssumingSameThread(_videoCapture.get());
_isScreenCapture = object->isScreenCapture(); _isScreenCapture = object->isScreenCapture();
_videoCaptureGuard = std::make_shared<bool>(true); _videoCaptureGuard = std::make_shared<bool>(true);
const auto guard = std::weak_ptr<bool>{_videoCaptureGuard}; const auto guard = std::weak_ptr<bool>{ _videoCaptureGuard };
object->setStateUpdated([=](VideoState state) { object->setStateUpdated([=](VideoState state) {
thread->PostTask([=] { thread->PostTask([=] {
// Checking this special guard instead of weak_ptr(this) // Checking this special guard instead of weak_ptr(this)
@ -1040,7 +1040,7 @@ void MediaManager::fillCallStats(CallStats &callStats) {
if (_videoCodecOut.has_value()) { if (_videoCodecOut.has_value()) {
callStats.outgoingCodec = _videoCodecOut->name; callStats.outgoingCodec = _videoCodecOut->name;
} }
callStats.bitrateRecords = std::move(_bitrateRecords); callStats.bitrateRecords = _bitrateRecords;
} }
void MediaManager::setAudioInputDevice(std::string id) { void MediaManager::setAudioInputDevice(std::string id) {

View file

@ -47,7 +47,7 @@ public:
std::function<void(Message &&)> sendSignalingMessage, std::function<void(Message &&)> sendSignalingMessage,
std::function<void(Message &&)> sendTransportMessage, std::function<void(Message &&)> sendTransportMessage,
std::function<void(int)> signalBarsUpdated, std::function<void(int)> signalBarsUpdated,
std::function<void(float, float)> audioLevelsUpdated, std::function<void(float, float)> audioLevelUpdated,
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> createAudioDeviceModule, std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> createAudioDeviceModule,
bool enableHighBitrateVideo, bool enableHighBitrateVideo,
std::vector<std::string> preferredCodecs, std::vector<std::string> preferredCodecs,
@ -130,7 +130,7 @@ private:
std::function<void(Message &&)> _sendSignalingMessage; std::function<void(Message &&)> _sendSignalingMessage;
std::function<void(Message &&)> _sendTransportMessage; std::function<void(Message &&)> _sendTransportMessage;
std::function<void(int)> _signalBarsUpdated; std::function<void(int)> _signalBarsUpdated;
std::function<void(float, float)> _audioLevelsUpdated; std::function<void(float, float)> _audioLevelUpdated;
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule; std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule;
SSRC _ssrcAudio; SSRC _ssrcAudio;

View file

@ -269,7 +269,7 @@ TrafficStats NetworkManager::getNetworkStats() {
} }
void NetworkManager::fillCallStats(CallStats &callStats) { void NetworkManager::fillCallStats(CallStats &callStats) {
callStats.networkRecords = std::move(_networkRecords); callStats.networkRecords = _networkRecords;
} }
void NetworkManager::logCurrentNetworkState() { void NetworkManager::logCurrentNetworkState() {

View file

@ -7,7 +7,7 @@
namespace tgcalls { namespace tgcalls {
SctpDataChannelProviderInterfaceImpl::SctpDataChannelProviderInterfaceImpl( SctpDataChannelProviderInterfaceImpl::SctpDataChannelProviderInterfaceImpl(
cricket::DtlsTransport *transportChannel, rtc::PacketTransportInternal *transportChannel,
bool isOutgoing, bool isOutgoing,
std::function<void(bool)> onStateChanged, std::function<void(bool)> onStateChanged,
std::function<void()> onTerminated, std::function<void()> onTerminated,

View file

@ -19,7 +19,7 @@ namespace tgcalls {
class SctpDataChannelProviderInterfaceImpl : public sigslot::has_slots<>, public webrtc::SctpDataChannelControllerInterface, public webrtc::DataChannelObserver, public webrtc::DataChannelSink { class SctpDataChannelProviderInterfaceImpl : public sigslot::has_slots<>, public webrtc::SctpDataChannelControllerInterface, public webrtc::DataChannelObserver, public webrtc::DataChannelSink {
public: public:
SctpDataChannelProviderInterfaceImpl( SctpDataChannelProviderInterfaceImpl(
cricket::DtlsTransport *transportChannel, rtc::PacketTransportInternal *transportChannel,
bool isOutgoing, bool isOutgoing,
std::function<void(bool)> onStateChanged, std::function<void(bool)> onStateChanged,
std::function<void()> onTerminated, std::function<void()> onTerminated,

View file

@ -199,7 +199,7 @@ _avIoContext(std::move(fileData)) {
AudioStreamingPartInternal::~AudioStreamingPartInternal() { AudioStreamingPartInternal::~AudioStreamingPartInternal() {
if (_frame) { if (_frame) {
av_frame_unref(_frame); av_frame_free(&_frame);
} }
if (_inputFormatContext) { if (_inputFormatContext) {
avformat_close_input(&_inputFormatContext); avformat_close_input(&_inputFormatContext);

View file

@ -55,6 +55,7 @@ public:
~AudioStreamingPartPersistentDecoderState() { ~AudioStreamingPartPersistentDecoderState() {
if (_codecContext) { if (_codecContext) {
avcodec_close(_codecContext);
avcodec_free_context(&_codecContext); avcodec_free_context(&_codecContext);
} }
} }
@ -85,6 +86,10 @@ AudioStreamingPartPersistentDecoder::AudioStreamingPartPersistentDecoder() {
} }
AudioStreamingPartPersistentDecoder::~AudioStreamingPartPersistentDecoder() { AudioStreamingPartPersistentDecoder::~AudioStreamingPartPersistentDecoder() {
if (_state) {
delete _state;
_state = nullptr;
}
} }
void AudioStreamingPartPersistentDecoder::maybeReset(AVCodecParameters const *codecParameters, AVRational timeBase) { void AudioStreamingPartPersistentDecoder::maybeReset(AVCodecParameters const *codecParameters, AVRational timeBase) {

View file

@ -103,15 +103,11 @@ static int stringToInt(std::string const &string) {
} }
static std::string intToString(int value) { static std::string intToString(int value) {
std::ostringstream stringStream; return std::to_string(value);
stringStream << value;
return stringStream.str();
} }
static std::string uint32ToString(uint32_t value) { static std::string uint32ToString(uint32_t value) {
std::ostringstream stringStream; return std::to_string(value);
stringStream << value;
return stringStream.str();
} }
static uint32_t stringToUInt32(std::string const &string) { static uint32_t stringToUInt32(std::string const &string) {
@ -130,6 +126,7 @@ static uint16_t stringToUInt16(std::string const &string) {
static std::string formatTimestampMillis(int64_t timestamp) { static std::string formatTimestampMillis(int64_t timestamp) {
std::ostringstream stringStream; std::ostringstream stringStream;
stringStream.imbue(std::locale::classic());
stringStream << std::fixed << std::setprecision(3) << (double)timestamp / 1000.0; stringStream << std::fixed << std::setprecision(3) << (double)timestamp / 1000.0;
return stringStream.str(); return stringStream.str();
} }
@ -2845,9 +2842,9 @@ public:
void setAudioInputDevice(const std::string &id) { void setAudioInputDevice(const std::string &id) {
#if not defined(WEBRTC_IOS) && not defined(WEBRTC_ANDROID) #if not defined(WEBRTC_IOS) && not defined(WEBRTC_ANDROID)
_threads->getWorkerThread()->BlockingCall([&] { _threads->getWorkerThread()->BlockingCall([&] {
SetAudioInputDeviceById(_audioDeviceModule.get(), id); SetAudioInputDeviceById(_audioDeviceModule.get(), id);
}); });
#endif // WEBRTC_IOS #endif // WEBRTC_IOS
} }

View file

@ -302,8 +302,20 @@ public:
for (auto &videoSegment : segment->video) { for (auto &videoSegment : segment->video) {
videoSegment->isPlaying = true; videoSegment->isPlaying = true;
cancelPendingVideoQualityUpdate(videoSegment); cancelPendingVideoQualityUpdate(videoSegment);
auto frame = videoSegment->part->getFrameAtRelativeTimestamp(relativeTimestamp); std::shared_ptr<VideoStreamingSharedState> sharedVideoState;
auto endpointId = videoSegment->part->getActiveEndpointId();
if (endpointId.has_value()) {
auto it = _sharedVideoStateByEndpointId.find(endpointId.value());
if (it != _sharedVideoStateByEndpointId.end()) {
sharedVideoState = it->second;
} else {
sharedVideoState = std::make_shared<VideoStreamingSharedState>();
_sharedVideoStateByEndpointId.insert(std::make_pair(endpointId.value(), sharedVideoState));
}
}
auto frame = videoSegment->part->getFrameAtRelativeTimestamp(sharedVideoState.get(), relativeTimestamp);
if (frame) { if (frame) {
if (videoSegment->lastFramePts != frame->pts) { if (videoSegment->lastFramePts != frame->pts) {
videoSegment->lastFramePts = frame->pts; videoSegment->lastFramePts = frame->pts;
@ -324,8 +336,20 @@ public:
for (auto &videoSegment : segment->unified) { for (auto &videoSegment : segment->unified) {
videoSegment->isPlaying = true; videoSegment->isPlaying = true;
absl::optional<std::string> endpointId = "unified";
std::shared_ptr<VideoStreamingSharedState> sharedVideoState;
if (endpointId.has_value()) {
auto it = _sharedVideoStateByEndpointId.find(endpointId.value());
if (it != _sharedVideoStateByEndpointId.end()) {
sharedVideoState = it->second;
} else {
sharedVideoState = std::make_shared<VideoStreamingSharedState>();
_sharedVideoStateByEndpointId.insert(std::make_pair(endpointId.value(), sharedVideoState));
}
}
auto frame = videoSegment->videoPart->getFrameAtRelativeTimestamp(relativeTimestamp); auto frame = videoSegment->videoPart->getFrameAtRelativeTimestamp(sharedVideoState.get(), relativeTimestamp);
if (frame) { if (frame) {
if (videoSegment->lastFramePts != frame->pts) { if (videoSegment->lastFramePts != frame->pts) {
videoSegment->lastFramePts = frame->pts; videoSegment->lastFramePts = frame->pts;
@ -1039,6 +1063,7 @@ private:
std::map<uint32_t, double> _volumeBySsrc; std::map<uint32_t, double> _volumeBySsrc;
std::vector<StreamingMediaContext::VideoChannel> _activeVideoChannels; std::vector<StreamingMediaContext::VideoChannel> _activeVideoChannels;
std::map<std::string, std::shared_ptr<VideoStreamingSharedState>> _sharedVideoStateByEndpointId;
std::map<std::string, std::vector<std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>>>> _videoSinks; std::map<std::string, std::vector<std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>>>> _videoSinks;
std::map<std::string, int32_t> _currentEndpointMapping; std::map<std::string, int32_t> _currentEndpointMapping;

View file

@ -5,6 +5,7 @@
#include "api/video/i420_buffer.h" #include "api/video/i420_buffer.h"
#include "AVIOContextImpl.h" #include "AVIOContextImpl.h"
#include "platform/PlatformInterface.h"
#include <string> #include <string>
#include <set> #include <set>
@ -79,7 +80,7 @@ public:
~Frame() { ~Frame() {
if (_frame) { if (_frame) {
av_frame_unref(_frame); av_frame_free(&_frame);
} }
} }
@ -265,6 +266,214 @@ absl::optional<VideoStreamInfo> consumeVideoStreamInfo(std::vector<uint8_t> &dat
return info; return info;
} }
bool areCodecParametersEqual(AVCodecParameters const &lhs, AVCodecParameters const &rhs) {
if (lhs.codec_id != rhs.codec_id) {
return false;
}
if (lhs.extradata_size != rhs.extradata_size) {
return false;
}
if (lhs.extradata_size != 0) {
if (memcmp(lhs.extradata, rhs.extradata, lhs.extradata_size)) {
return false;
}
}
if (lhs.format != rhs.format) {
return false;
}
if (lhs.profile != rhs.profile) {
return false;
}
if (lhs.level != rhs.level) {
return false;
}
if (lhs.width != rhs.width) {
return false;
}
if (lhs.height != rhs.height) {
return false;
}
if (lhs.sample_aspect_ratio.num != rhs.sample_aspect_ratio.num) {
return false;
}
if (lhs.sample_aspect_ratio.den != rhs.sample_aspect_ratio.den) {
return false;
}
if (lhs.field_order != rhs.field_order) {
return false;
}
if (lhs.color_range != rhs.color_range) {
return false;
}
if (lhs.color_primaries != rhs.color_primaries) {
return false;
}
if (lhs.color_trc != rhs.color_trc) {
return false;
}
if (lhs.color_space != rhs.color_space) {
return false;
}
if (lhs.chroma_location != rhs.chroma_location) {
return false;
}
return true;
}
class VideoStreamingDecoderState {
public:
static std::unique_ptr<VideoStreamingDecoderState> create(
AVCodecParameters const *codecParameters,
AVRational pktTimebase
) {
AVCodec const *codec = nullptr;
if (!codec) {
codec = avcodec_find_decoder(codecParameters->codec_id);
}
if (!codec) {
return nullptr;
}
AVCodecContext *codecContext = avcodec_alloc_context3(codec);
int ret = avcodec_parameters_to_context(codecContext, codecParameters);
if (ret < 0) {
avcodec_free_context(&codecContext);
return nullptr;
} else {
codecContext->pkt_timebase = pktTimebase;
PlatformInterface::SharedInstance()->setupVideoDecoding(codecContext);
ret = avcodec_open2(codecContext, codec, nullptr);
if (ret < 0) {
avcodec_free_context(&codecContext);
return nullptr;
}
}
return std::make_unique<VideoStreamingDecoderState>(
codecContext,
codecParameters,
pktTimebase
);
}
public:
VideoStreamingDecoderState(
AVCodecContext *codecContext,
AVCodecParameters const *codecParameters,
AVRational pktTimebase
) {
_codecContext = codecContext;
_codecParameters = avcodec_parameters_alloc();
avcodec_parameters_copy(_codecParameters, codecParameters);
_pktTimebase = pktTimebase;
}
~VideoStreamingDecoderState() {
if (_codecContext) {
avcodec_close(_codecContext);
avcodec_free_context(&_codecContext);
}
if (_codecParameters) {
avcodec_parameters_free(&_codecParameters);
}
}
bool supportsDecoding(
AVCodecParameters const *codecParameters,
AVRational pktTimebase
) const {
if (!areCodecParametersEqual(*_codecParameters, *codecParameters)) {
return false;
}
if (_pktTimebase.num != pktTimebase.num) {
return false;
}
if (_pktTimebase.den != pktTimebase.den) {
return false;
}
return true;
}
int sendFrame(std::shared_ptr<DecodableFrame> frame) {
if (frame) {
int status = avcodec_send_packet(_codecContext, frame->packet().packet());
return status;
} else {
int status = avcodec_send_packet(_codecContext, nullptr);
return status;
}
}
int receiveFrame(Frame &frame) {
int status = avcodec_receive_frame(_codecContext, frame.frame());
return status;
}
void reset() {
avcodec_flush_buffers(_codecContext);
}
private:
AVCodecContext *_codecContext = nullptr;
AVCodecParameters *_codecParameters = nullptr;
AVRational _pktTimebase;
};
}
class VideoStreamingSharedStateInternal {
public:
VideoStreamingSharedStateInternal() {
}
~VideoStreamingSharedStateInternal() {
}
void updateDecoderState(
AVCodecParameters const *codecParameters,
AVRational pktTimebase
) {
if (_decoderState && _decoderState->supportsDecoding(codecParameters, pktTimebase)) {
return;
}
_decoderState.reset();
_decoderState = VideoStreamingDecoderState::create(codecParameters, pktTimebase);
}
int sendFrame(std::shared_ptr<DecodableFrame> frame) {
if (!_decoderState) {
return AVERROR(EIO);
}
return _decoderState->sendFrame(frame);
}
int receiveFrame(Frame &frame) {
if (!_decoderState) {
return AVERROR(EIO);
}
return _decoderState->receiveFrame(frame);
}
void reset() {
if (!_decoderState) {
return;
}
_decoderState->reset();
}
private:
std::unique_ptr<VideoStreamingDecoderState> _decoderState;
};
VideoStreamingSharedState::VideoStreamingSharedState() {
_impl = new VideoStreamingSharedStateInternal();
}
VideoStreamingSharedState::~VideoStreamingSharedState() {
delete _impl;
} }
class VideoStreamingPartInternal { class VideoStreamingPartInternal {
@ -322,7 +531,11 @@ public:
} }
if (videoCodecParameters && videoStream) { if (videoCodecParameters && videoStream) {
const AVCodec *codec = avcodec_find_decoder(videoCodecParameters->codec_id); _videoCodecParameters = avcodec_parameters_alloc();
avcodec_parameters_copy(_videoCodecParameters, videoCodecParameters);
_videoStream = videoStream;
/*const AVCodec *codec = avcodec_find_decoder(videoCodecParameters->codec_id);
if (codec) { if (codec) {
_codecContext = avcodec_alloc_context3(codec); _codecContext = avcodec_alloc_context3(codec);
ret = avcodec_parameters_to_context(_codecContext, videoCodecParameters); ret = avcodec_parameters_to_context(_codecContext, videoCodecParameters);
@ -344,14 +557,13 @@ public:
_videoStream = videoStream; _videoStream = videoStream;
} }
} }
} }*/
} }
} }
~VideoStreamingPartInternal() { ~VideoStreamingPartInternal() {
if (_codecContext) { if (_videoCodecParameters) {
avcodec_close(_codecContext); avcodec_parameters_free(&_videoCodecParameters);
avcodec_free_context(&_codecContext);
} }
if (_inputFormatContext) { if (_inputFormatContext) {
avformat_close_input(&_inputFormatContext); avformat_close_input(&_inputFormatContext);
@ -393,32 +605,47 @@ public:
} }
absl::optional<VideoStreamingPartFrame> convertCurrentFrame() { absl::optional<VideoStreamingPartFrame> convertCurrentFrame() {
rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = webrtc::I420Buffer::Copy( auto platformFrameBuffer = PlatformInterface::SharedInstance()->createPlatformFrameFromData(_frame.frame());
_frame.frame()->width, if (platformFrameBuffer) {
_frame.frame()->height,
_frame.frame()->data[0],
_frame.frame()->linesize[0],
_frame.frame()->data[1],
_frame.frame()->linesize[1],
_frame.frame()->data[2],
_frame.frame()->linesize[2]
);
if (i420Buffer) {
auto videoFrame = webrtc::VideoFrame::Builder() auto videoFrame = webrtc::VideoFrame::Builder()
.set_video_frame_buffer(i420Buffer) .set_video_frame_buffer(platformFrameBuffer)
.set_rotation(_rotation) .set_rotation(_rotation)
.build(); .build();
return VideoStreamingPartFrame(_endpointId, videoFrame, _frame.pts(_videoStream, _firstFramePts), _frameIndex); return VideoStreamingPartFrame(_endpointId, videoFrame, _frame.pts(_videoStream, _firstFramePts), _frameIndex);
} else { } else {
return absl::nullopt; rtc::scoped_refptr<webrtc::I420Buffer> i420Buffer = webrtc::I420Buffer::Copy(
_frame.frame()->width,
_frame.frame()->height,
_frame.frame()->data[0],
_frame.frame()->linesize[0],
_frame.frame()->data[1],
_frame.frame()->linesize[1],
_frame.frame()->data[2],
_frame.frame()->linesize[2]
);
if (i420Buffer) {
auto videoFrame = webrtc::VideoFrame::Builder()
.set_video_frame_buffer(i420Buffer)
.set_rotation(_rotation)
.build();
return VideoStreamingPartFrame(_endpointId, videoFrame, _frame.pts(_videoStream, _firstFramePts), _frameIndex);
} else {
return absl::nullopt;
}
} }
} }
absl::optional<VideoStreamingPartFrame> getNextFrame() { absl::optional<VideoStreamingPartFrame> getNextFrame(VideoStreamingSharedState const *sharedState) {
if (!_codecContext) { if (!_videoStream) {
return {}; return {};
} }
if (!_videoCodecParameters) {
return {};
}
sharedState->impl()->updateDecoderState(_videoCodecParameters, _videoStream->time_base);
while (true) { while (true) {
if (_didReadToEnd) { if (_didReadToEnd) {
@ -432,42 +659,50 @@ public:
} else { } else {
const auto frame = readNextDecodableFrame(); const auto frame = readNextDecodableFrame();
if (frame) { if (frame) {
auto status = avcodec_send_packet(_codecContext, frame->packet().packet()); int sendStatus = sharedState->impl()->sendFrame(frame);
if (status == 0) { if (sendStatus == 0) {
auto status = avcodec_receive_frame(_codecContext, _frame.frame()); int receiveStatus = sharedState->impl()->receiveFrame(_frame);
if (status == 0) { if (receiveStatus == 0) {
auto convertedFrame = convertCurrentFrame(); auto convertedFrame = convertCurrentFrame();
if (convertedFrame) { if (convertedFrame) {
_frameIndex++; _frameIndex++;
return convertedFrame; return convertedFrame;
} }
} else if (status == AVERROR(EAGAIN)) { } else if (receiveStatus == AVERROR(EAGAIN)) {
// more data needed // more data needed
} else { } else {
RTC_LOG(LS_ERROR) << "avcodec_receive_frame failed with result: " << receiveStatus;
_didReadToEnd = true; _didReadToEnd = true;
break; break;
} }
} else { } else {
RTC_LOG(LS_ERROR) << "avcodec_send_packet failed with result: " << sendStatus;
_didReadToEnd = true; _didReadToEnd = true;
return {}; return {};
} }
} else { } else {
_didReadToEnd = true; _didReadToEnd = true;
int status = avcodec_send_packet(_codecContext, nullptr); int sendStatus = sharedState->impl()->sendFrame(nullptr);
if (status == 0) { if (sendStatus == 0) {
while (true) { while (true) {
auto status = avcodec_receive_frame(_codecContext, _frame.frame()); int receiveStatus = sharedState->impl()->receiveFrame(_frame);
if (status == 0) { if (receiveStatus == 0) {
auto convertedFrame = convertCurrentFrame(); auto convertedFrame = convertCurrentFrame();
if (convertedFrame) { if (convertedFrame) {
_frameIndex++; _frameIndex++;
_finalFrames.push_back(convertedFrame.value()); _finalFrames.push_back(convertedFrame.value());
} }
} else { } else {
if (receiveStatus != AVERROR_EOF) {
RTC_LOG(LS_ERROR) << "avcodec_receive_frame (drain) failed with result: " << receiveStatus;
}
break; break;
} }
} }
} else {
RTC_LOG(LS_ERROR) << "avcodec_send_packet (drain) failed with result: " << sendStatus;
} }
sharedState->impl()->reset();
} }
} }
} }
@ -482,10 +717,11 @@ private:
std::unique_ptr<AVIOContextImpl> _avIoContext; std::unique_ptr<AVIOContextImpl> _avIoContext;
AVFormatContext *_inputFormatContext = nullptr; AVFormatContext *_inputFormatContext = nullptr;
AVCodecContext *_codecContext = nullptr;
AVStream *_videoStream = nullptr; AVStream *_videoStream = nullptr;
Frame _frame; Frame _frame;
AVCodecParameters *_videoCodecParameters = nullptr;
std::vector<VideoStreamingPartFrame> _finalFrames; std::vector<VideoStreamingPartFrame> _finalFrames;
int _frameIndex = 0; int _frameIndex = 0;
@ -564,7 +800,7 @@ public:
~VideoStreamingPartState() { ~VideoStreamingPartState() {
} }
absl::optional<VideoStreamingPartFrame> getFrameAtRelativeTimestamp(double timestamp) { absl::optional<VideoStreamingPartFrame> getFrameAtRelativeTimestamp(VideoStreamingSharedState const *sharedState, double timestamp) {
while (true) { while (true) {
while (_availableFrames.size() >= 2) { while (_availableFrames.size() >= 2) {
if (timestamp >= _availableFrames[1].pts) { if (timestamp >= _availableFrames[1].pts) {
@ -576,7 +812,7 @@ public:
if (_availableFrames.size() < 2) { if (_availableFrames.size() < 2) {
if (!_parsedVideoParts.empty()) { if (!_parsedVideoParts.empty()) {
auto result = _parsedVideoParts[0]->getNextFrame(); auto result = _parsedVideoParts[0]->getNextFrame(sharedState);
if (result) { if (result) {
_availableFrames.push_back(result.value()); _availableFrames.push_back(result.value());
} else { } else {
@ -622,7 +858,7 @@ public:
} }
return 0; return 0;
} }
std::vector<AudioStreamingPart::StreamingPartChannel> getAudio10msPerChannel(AudioStreamingPartPersistentDecoder &persistentDecoder) { std::vector<AudioStreamingPart::StreamingPartChannel> getAudio10msPerChannel(AudioStreamingPartPersistentDecoder &persistentDecoder) {
while (!_parsedAudioParts.empty()) { while (!_parsedAudioParts.empty()) {
auto firstPartResult = _parsedAudioParts[0]->get10msPerChannel(persistentDecoder); auto firstPartResult = _parsedAudioParts[0]->get10msPerChannel(persistentDecoder);
@ -655,9 +891,9 @@ VideoStreamingPart::~VideoStreamingPart() {
} }
} }
absl::optional<VideoStreamingPartFrame> VideoStreamingPart::getFrameAtRelativeTimestamp(double timestamp) { absl::optional<VideoStreamingPartFrame> VideoStreamingPart::getFrameAtRelativeTimestamp(VideoStreamingSharedState const *sharedState, double timestamp) {
return _state return _state
? _state->getFrameAtRelativeTimestamp(timestamp) ? _state->getFrameAtRelativeTimestamp(sharedState, timestamp)
: absl::nullopt; : absl::nullopt;
} }

View file

@ -14,6 +14,7 @@
namespace tgcalls { namespace tgcalls {
class VideoStreamingPartState; class VideoStreamingPartState;
class VideoStreamingSharedStateInternal;
struct VideoStreamingPartFrame { struct VideoStreamingPartFrame {
std::string endpointId; std::string endpointId;
@ -29,6 +30,19 @@ struct VideoStreamingPartFrame {
} }
}; };
class VideoStreamingSharedState {
public:
VideoStreamingSharedState();
~VideoStreamingSharedState();
VideoStreamingSharedStateInternal *impl() const {
return _impl;
}
private:
VideoStreamingSharedStateInternal *_impl = nullptr;
};
class VideoStreamingPart { class VideoStreamingPart {
public: public:
enum class ContentType { enum class ContentType {
@ -48,7 +62,7 @@ public:
VideoStreamingPart& operator=(const VideoStreamingPart&) = delete; VideoStreamingPart& operator=(const VideoStreamingPart&) = delete;
VideoStreamingPart& operator=(VideoStreamingPart&&) = delete; VideoStreamingPart& operator=(VideoStreamingPart&&) = delete;
absl::optional<VideoStreamingPartFrame> getFrameAtRelativeTimestamp(double timestamp); absl::optional<VideoStreamingPartFrame> getFrameAtRelativeTimestamp(VideoStreamingSharedState const *sharedState, double timestamp);
absl::optional<std::string> getActiveEndpointId() const; absl::optional<std::string> getActiveEndpointId() const;
bool hasRemainingFrames() const; bool hasRemainingFrames() const;

View file

@ -215,7 +215,7 @@ void InstanceImplLegacy::sendVideoDeviceUpdated() {
void InstanceImplLegacy::setRequestedVideoAspect(float aspect) { void InstanceImplLegacy::setRequestedVideoAspect(float aspect) {
} }
void InstanceImplLegacy::setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) { void InstanceImplLegacy::setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
} }
void InstanceImplLegacy::setAudioOutputGainControlEnabled(bool enabled) { void InstanceImplLegacy::setAudioOutputGainControlEnabled(bool enabled) {

View file

@ -25,7 +25,7 @@ public:
bool supportsVideo() override { bool supportsVideo() override {
return false; return false;
} }
void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override; void setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override;
void setAudioOutputGainControlEnabled(bool enabled) override; void setAudioOutputGainControlEnabled(bool enabled) override;
void setEchoCancellationStrength(int strength) override; void setEchoCancellationStrength(int strength) override;
void setAudioInputDevice(std::string id) override; void setAudioInputDevice(std::string id) override;

View file

@ -10,6 +10,9 @@
#include "rtc_base/ref_counted_object.h" #include "rtc_base/ref_counted_object.h"
#include <string> #include <string>
struct AVFrame;
struct AVCodecContext;
namespace tgcalls { namespace tgcalls {
enum class VideoState; enum class VideoState;
@ -292,6 +295,14 @@ private:
rtc::scoped_refptr<webrtc::AudioDeviceModule> _impl; rtc::scoped_refptr<webrtc::AudioDeviceModule> _impl;
}; };
class PlatformVideoFrame {
public:
PlatformVideoFrame() {
}
virtual ~PlatformVideoFrame() = default;
};
class PlatformInterface { class PlatformInterface {
public: public:
static PlatformInterface *SharedInstance(); static PlatformInterface *SharedInstance();
@ -313,6 +324,11 @@ public:
virtual rtc::scoped_refptr<WrappedAudioDeviceModule> wrapAudioDeviceModule(rtc::scoped_refptr<webrtc::AudioDeviceModule> module) { virtual rtc::scoped_refptr<WrappedAudioDeviceModule> wrapAudioDeviceModule(rtc::scoped_refptr<webrtc::AudioDeviceModule> module) {
return rtc::make_ref_counted<DefaultWrappedAudioDeviceModule>(module); return rtc::make_ref_counted<DefaultWrappedAudioDeviceModule>(module);
} }
virtual void setupVideoDecoding(AVCodecContext *codecContext) {
}
virtual rtc::scoped_refptr<webrtc::VideoFrameBuffer> createPlatformFrameFromData(AVFrame const *frame) {
return nullptr;
}
public: public:
bool preferX264 = false; bool preferX264 = false;

View file

@ -6,12 +6,9 @@
namespace tgcalls { namespace tgcalls {
AndroidContext::AndroidContext(JNIEnv *env, jobject instance, bool screencast) { AndroidContext::AndroidContext(JNIEnv *env, jobject instance, bool screencast) {
DEBUG_REF("VideoCapturerDevice");
VideoCapturerDeviceClass = (jclass) env->NewGlobalRef(env->FindClass("org/telegram/messenger/voip/VideoCapturerDevice")); VideoCapturerDeviceClass = (jclass) env->NewGlobalRef(env->FindClass("org/telegram/messenger/voip/VideoCapturerDevice"));
jmethodID initMethodId = env->GetMethodID(VideoCapturerDeviceClass, "<init>", "(Z)V"); jmethodID initMethodId = env->GetMethodID(VideoCapturerDeviceClass, "<init>", "(Z)V");
DEBUG_REF("VideoCapturerDevice javaCapturer");
javaCapturer = env->NewGlobalRef(env->NewObject(VideoCapturerDeviceClass, initMethodId, screencast)); javaCapturer = env->NewGlobalRef(env->NewObject(VideoCapturerDeviceClass, initMethodId, screencast));
DEBUG_REF("VideoCapturerDevice javaInstance");
javaInstance = env->NewGlobalRef(instance); javaInstance = env->NewGlobalRef(instance);
} }
@ -20,21 +17,17 @@ AndroidContext::~AndroidContext() {
jmethodID onDestroyMethodId = env->GetMethodID(VideoCapturerDeviceClass, "onDestroy", "()V"); jmethodID onDestroyMethodId = env->GetMethodID(VideoCapturerDeviceClass, "onDestroy", "()V");
env->CallVoidMethod(javaCapturer, onDestroyMethodId); env->CallVoidMethod(javaCapturer, onDestroyMethodId);
DEBUG_DELREF("javaCapturer");
env->DeleteGlobalRef(javaCapturer); env->DeleteGlobalRef(javaCapturer);
javaCapturer = nullptr; javaCapturer = nullptr;
DEBUG_DELREF("VideoCapturerDeviceClass");
env->DeleteGlobalRef(VideoCapturerDeviceClass); env->DeleteGlobalRef(VideoCapturerDeviceClass);
if (javaInstance) { if (javaInstance) {
DEBUG_DELREF("javaInstance");
env->DeleteGlobalRef(javaInstance); env->DeleteGlobalRef(javaInstance);
} }
} }
void AndroidContext::setJavaInstance(JNIEnv *env, jobject instance) { void AndroidContext::setJavaInstance(JNIEnv *env, jobject instance) {
DEBUG_REF("setJavaInstance");
javaInstance = env->NewGlobalRef(instance); javaInstance = env->NewGlobalRef(instance);
} }

View file

@ -11,60 +11,60 @@ namespace {
signaling::MediaContent convertContentInfoToSingalingContent(cricket::ContentInfo const &content) { signaling::MediaContent convertContentInfoToSingalingContent(cricket::ContentInfo const &content) {
signaling::MediaContent mappedContent; signaling::MediaContent mappedContent;
switch (content.media_description()->type()) { switch (content.media_description()->type()) {
case cricket::MediaType::MEDIA_TYPE_AUDIO: { case cricket::MediaType::MEDIA_TYPE_AUDIO: {
mappedContent.type = signaling::MediaContent::Type::Audio; mappedContent.type = signaling::MediaContent::Type::Audio;
for (const auto &codec : content.media_description()->as_audio()->codecs()) { for (const auto &codec : content.media_description()->as_audio()->codecs()) {
signaling::PayloadType mappedPayloadType; signaling::PayloadType mappedPayloadType;
mappedPayloadType.id = codec.id; mappedPayloadType.id = codec.id;
mappedPayloadType.name = codec.name; mappedPayloadType.name = codec.name;
mappedPayloadType.clockrate = codec.clockrate; mappedPayloadType.clockrate = codec.clockrate;
mappedPayloadType.channels = (uint32_t)codec.channels; mappedPayloadType.channels = (uint32_t)codec.channels;
for (const auto &feedbackType : codec.feedback_params.params()) { for (const auto &feedbackType : codec.feedback_params.params()) {
signaling::FeedbackType mappedFeedbackType; signaling::FeedbackType mappedFeedbackType;
mappedFeedbackType.type = feedbackType.id(); mappedFeedbackType.type = feedbackType.id();
mappedFeedbackType.subtype = feedbackType.param(); mappedFeedbackType.subtype = feedbackType.param();
mappedPayloadType.feedbackTypes.push_back(std::move(mappedFeedbackType)); mappedPayloadType.feedbackTypes.push_back(std::move(mappedFeedbackType));
} }
for (const auto &parameter : codec.params) { for (const auto &parameter : codec.params) {
mappedPayloadType.parameters.push_back(std::make_pair(parameter.first, parameter.second)); mappedPayloadType.parameters.push_back(std::make_pair(parameter.first, parameter.second));
} }
std::sort(mappedPayloadType.parameters.begin(), mappedPayloadType.parameters.end(), [](std::pair<std::string, std::string> const &lhs, std::pair<std::string, std::string> const &rhs) -> bool { std::sort(mappedPayloadType.parameters.begin(), mappedPayloadType.parameters.end(), [](std::pair<std::string, std::string> const &lhs, std::pair<std::string, std::string> const &rhs) -> bool {
return lhs.first < rhs.first; return lhs.first < rhs.first;
}); });
mappedContent.payloadTypes.push_back(std::move(mappedPayloadType)); mappedContent.payloadTypes.push_back(std::move(mappedPayloadType));
} }
break; break;
} }
case cricket::MediaType::MEDIA_TYPE_VIDEO: { case cricket::MediaType::MEDIA_TYPE_VIDEO: {
mappedContent.type = signaling::MediaContent::Type::Video; mappedContent.type = signaling::MediaContent::Type::Video;
for (const auto &codec : content.media_description()->as_video()->codecs()) { for (const auto &codec : content.media_description()->as_video()->codecs()) {
signaling::PayloadType mappedPayloadType; signaling::PayloadType mappedPayloadType;
mappedPayloadType.id = codec.id; mappedPayloadType.id = codec.id;
mappedPayloadType.name = codec.name; mappedPayloadType.name = codec.name;
mappedPayloadType.clockrate = codec.clockrate; mappedPayloadType.clockrate = codec.clockrate;
mappedPayloadType.channels = 0; mappedPayloadType.channels = 0;
for (const auto &feedbackType : codec.feedback_params.params()) { for (const auto &feedbackType : codec.feedback_params.params()) {
signaling::FeedbackType mappedFeedbackType; signaling::FeedbackType mappedFeedbackType;
mappedFeedbackType.type = feedbackType.id(); mappedFeedbackType.type = feedbackType.id();
mappedFeedbackType.subtype = feedbackType.param(); mappedFeedbackType.subtype = feedbackType.param();
mappedPayloadType.feedbackTypes.push_back(std::move(mappedFeedbackType)); mappedPayloadType.feedbackTypes.push_back(std::move(mappedFeedbackType));
} }
for (const auto &parameter : codec.params) { for (const auto &parameter : codec.params) {
mappedPayloadType.parameters.push_back(std::make_pair(parameter.first, parameter.second)); mappedPayloadType.parameters.push_back(std::make_pair(parameter.first, parameter.second));
} }
std::sort(mappedPayloadType.parameters.begin(), mappedPayloadType.parameters.end(), [](std::pair<std::string, std::string> const &lhs, std::pair<std::string, std::string> const &rhs) -> bool { std::sort(mappedPayloadType.parameters.begin(), mappedPayloadType.parameters.end(), [](std::pair<std::string, std::string> const &lhs, std::pair<std::string, std::string> const &rhs) -> bool {
return lhs.first < rhs.first; return lhs.first < rhs.first;
}); });
mappedContent.payloadTypes.push_back(std::move(mappedPayloadType)); mappedContent.payloadTypes.push_back(std::move(mappedPayloadType));
} }
break; break;
@ -74,7 +74,7 @@ signaling::MediaContent convertContentInfoToSingalingContent(cricket::ContentInf
break; break;
} }
} }
if (!content.media_description()->streams().empty()) { if (!content.media_description()->streams().empty()) {
mappedContent.ssrc = content.media_description()->streams()[0].first_ssrc(); mappedContent.ssrc = content.media_description()->streams()[0].first_ssrc();
for (const auto &ssrcGroup : content.media_description()->streams()[0].ssrc_groups) { for (const auto &ssrcGroup : content.media_description()->streams()[0].ssrc_groups) {
@ -84,21 +84,21 @@ signaling::MediaContent convertContentInfoToSingalingContent(cricket::ContentInf
mappedContent.ssrcGroups.push_back(std::move(mappedSsrcGroup)); mappedContent.ssrcGroups.push_back(std::move(mappedSsrcGroup));
} }
} }
for (const auto &extension : content.media_description()->rtp_header_extensions()) { for (const auto &extension : content.media_description()->rtp_header_extensions()) {
mappedContent.rtpExtensions.push_back(extension); mappedContent.rtpExtensions.push_back(extension);
} }
return mappedContent; return mappedContent;
} }
cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &contentId, signaling::MediaContent const &content, webrtc::RtpTransceiverDirection direction) { cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &contentId, signaling::MediaContent const &content, webrtc::RtpTransceiverDirection direction) {
std::unique_ptr<cricket::MediaContentDescription> contentDescription; std::unique_ptr<cricket::MediaContentDescription> contentDescription;
switch (content.type) { switch (content.type) {
case signaling::MediaContent::Type::Audio: { case signaling::MediaContent::Type::Audio: {
auto audioDescription = std::make_unique<cricket::AudioContentDescription>(); auto audioDescription = std::make_unique<cricket::AudioContentDescription>();
for (const auto &payloadType : content.payloadTypes) { for (const auto &payloadType : content.payloadTypes) {
cricket::AudioCodec mappedCodec((int)payloadType.id, payloadType.name, (int)payloadType.clockrate, 0, payloadType.channels); cricket::AudioCodec mappedCodec((int)payloadType.id, payloadType.name, (int)payloadType.clockrate, 0, payloadType.channels);
for (const auto &parameter : payloadType.parameters) { for (const auto &parameter : payloadType.parameters) {
@ -109,14 +109,14 @@ cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &con
} }
audioDescription->AddCodec(mappedCodec); audioDescription->AddCodec(mappedCodec);
} }
contentDescription = std::move(audioDescription); contentDescription = std::move(audioDescription);
break; break;
} }
case signaling::MediaContent::Type::Video: { case signaling::MediaContent::Type::Video: {
auto videoDescription = std::make_unique<cricket::VideoContentDescription>(); auto videoDescription = std::make_unique<cricket::VideoContentDescription>();
for (const auto &payloadType : content.payloadTypes) { for (const auto &payloadType : content.payloadTypes) {
cricket::VideoCodec mappedCodec((int)payloadType.id, payloadType.name); cricket::VideoCodec mappedCodec((int)payloadType.id, payloadType.name);
for (const auto &parameter : payloadType.parameters) { for (const auto &parameter : payloadType.parameters) {
@ -127,9 +127,9 @@ cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &con
} }
videoDescription->AddCodec(mappedCodec); videoDescription->AddCodec(mappedCodec);
} }
contentDescription = std::move(videoDescription); contentDescription = std::move(videoDescription);
break; break;
} }
default: { default: {
@ -137,7 +137,7 @@ cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &con
break; break;
} }
} }
cricket::StreamParams streamParams; cricket::StreamParams streamParams;
streamParams.id = contentId; streamParams.id = contentId;
streamParams.set_stream_ids({ contentId }); streamParams.set_stream_ids({ contentId });
@ -151,47 +151,43 @@ cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &con
} }
} }
contentDescription->AddStream(streamParams); contentDescription->AddStream(streamParams);
for (const auto &extension : content.rtpExtensions) { for (const auto &extension : content.rtpExtensions) {
contentDescription->AddRtpHeaderExtension(extension); contentDescription->AddRtpHeaderExtension(extension);
} }
contentDescription->set_direction(direction); contentDescription->set_direction(direction);
contentDescription->set_rtcp_mux(true); contentDescription->set_rtcp_mux(true);
cricket::ContentInfo mappedContentInfo(cricket::MediaProtocolType::kRtp); cricket::ContentInfo mappedContentInfo(cricket::MediaProtocolType::kRtp);
mappedContentInfo.name = contentId; mappedContentInfo.name = contentId;
mappedContentInfo.rejected = false; mappedContentInfo.rejected = false;
mappedContentInfo.bundle_only = false; mappedContentInfo.bundle_only = false;
mappedContentInfo.set_media_description(std::move(contentDescription)); mappedContentInfo.set_media_description(std::move(contentDescription));
return mappedContentInfo; return mappedContentInfo;
} }
cricket::ContentInfo createInactiveContentInfo(std::string const &contentId) { cricket::ContentInfo createInactiveContentInfo(std::string const &contentId) {
std::unique_ptr<cricket::MediaContentDescription> contentDescription; std::unique_ptr<cricket::MediaContentDescription> contentDescription;
auto audioDescription = std::make_unique<cricket::AudioContentDescription>(); auto audioDescription = std::make_unique<cricket::AudioContentDescription>();
contentDescription = std::move(audioDescription); contentDescription = std::move(audioDescription);
contentDescription->set_direction(webrtc::RtpTransceiverDirection::kInactive); contentDescription->set_direction(webrtc::RtpTransceiverDirection::kInactive);
contentDescription->set_rtcp_mux(true); contentDescription->set_rtcp_mux(true);
cricket::ContentInfo mappedContentInfo(cricket::MediaProtocolType::kRtp); cricket::ContentInfo mappedContentInfo(cricket::MediaProtocolType::kRtp);
mappedContentInfo.name = contentId; mappedContentInfo.name = contentId;
mappedContentInfo.rejected = false; mappedContentInfo.rejected = false;
mappedContentInfo.bundle_only = false; mappedContentInfo.bundle_only = false;
mappedContentInfo.set_media_description(std::move(contentDescription)); mappedContentInfo.set_media_description(std::move(contentDescription));
return mappedContentInfo; return mappedContentInfo;
} }
std::string contentIdBySsrc(uint32_t ssrc) { std::string contentIdBySsrc(uint32_t ssrc) {
std::ostringstream contentIdString; return std::to_string(ssrc);
contentIdString << ssrc;
return contentIdString.str();
} }
} }
@ -200,19 +196,19 @@ ContentNegotiationContext::ContentNegotiationContext(const webrtc::WebRtcKeyValu
_isOutgoing(isOutgoing), _isOutgoing(isOutgoing),
_uniqueRandomIdGenerator(uniqueRandomIdGenerator) { _uniqueRandomIdGenerator(uniqueRandomIdGenerator) {
_transportDescriptionFactory = std::make_unique<cricket::TransportDescriptionFactory>(fieldTrials); _transportDescriptionFactory = std::make_unique<cricket::TransportDescriptionFactory>(fieldTrials);
// tempCertificate is only used to fill in the local SDP // tempCertificate is only used to fill in the local SDP
auto tempCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt); auto tempCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
_transportDescriptionFactory->set_secure(cricket::SecurePolicy::SEC_REQUIRED); _transportDescriptionFactory->set_secure(cricket::SecurePolicy::SEC_REQUIRED);
_transportDescriptionFactory->set_certificate(tempCertificate); _transportDescriptionFactory->set_certificate(tempCertificate);
_sessionDescriptionFactory = std::make_unique<cricket::MediaSessionDescriptionFactory>(_transportDescriptionFactory.get(), uniqueRandomIdGenerator); _sessionDescriptionFactory = std::make_unique<cricket::MediaSessionDescriptionFactory>(_transportDescriptionFactory.get(), uniqueRandomIdGenerator);
_needNegotiation = true; _needNegotiation = true;
} }
ContentNegotiationContext::~ContentNegotiationContext() { ContentNegotiationContext::~ContentNegotiationContext() {
} }
void ContentNegotiationContext::copyCodecsFromChannelManager(cricket::MediaEngineInterface *mediaEngine, bool randomize) { void ContentNegotiationContext::copyCodecsFromChannelManager(cricket::MediaEngineInterface *mediaEngine, bool randomize) {
@ -220,7 +216,7 @@ void ContentNegotiationContext::copyCodecsFromChannelManager(cricket::MediaEngin
cricket::AudioCodecs audioRecvCodecs = mediaEngine->voice().recv_codecs(); cricket::AudioCodecs audioRecvCodecs = mediaEngine->voice().recv_codecs();
cricket::VideoCodecs videoSendCodecs = mediaEngine->video().send_codecs(); cricket::VideoCodecs videoSendCodecs = mediaEngine->video().send_codecs();
cricket::VideoCodecs videoRecvCodecs = mediaEngine->video().recv_codecs(); cricket::VideoCodecs videoRecvCodecs = mediaEngine->video().recv_codecs();
for (const auto &codec : audioSendCodecs) { for (const auto &codec : audioSendCodecs) {
if (codec.name == "opus") { if (codec.name == "opus") {
audioSendCodecs = { codec }; audioSendCodecs = { codec };
@ -228,7 +224,7 @@ void ContentNegotiationContext::copyCodecsFromChannelManager(cricket::MediaEngin
break; break;
} }
} }
if (randomize) { if (randomize) {
for (auto &codec : audioSendCodecs) { for (auto &codec : audioSendCodecs) {
codec.id += 3; codec.id += 3;
@ -243,23 +239,23 @@ void ContentNegotiationContext::copyCodecsFromChannelManager(cricket::MediaEngin
codec.id += 3; codec.id += 3;
} }
} }
_sessionDescriptionFactory->set_audio_codecs(audioSendCodecs, audioRecvCodecs); _sessionDescriptionFactory->set_audio_codecs(audioSendCodecs, audioRecvCodecs);
_sessionDescriptionFactory->set_video_codecs(videoSendCodecs, videoRecvCodecs); _sessionDescriptionFactory->set_video_codecs(videoSendCodecs, videoRecvCodecs);
int absSendTimeUriId = 2; int absSendTimeUriId = 2;
int transportSequenceNumberUriId = 3; int transportSequenceNumberUriId = 3;
int videoRotationUri = 13; int videoRotationUri = 13;
if (randomize) { if (randomize) {
absSendTimeUriId = 3; absSendTimeUriId = 3;
transportSequenceNumberUriId = 2; transportSequenceNumberUriId = 2;
videoRotationUri = 4; videoRotationUri = 4;
} }
_rtpAudioExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, absSendTimeUriId); _rtpAudioExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, absSendTimeUriId);
_rtpAudioExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, transportSequenceNumberUriId); _rtpAudioExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, transportSequenceNumberUriId);
_rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, absSendTimeUriId); _rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, absSendTimeUriId);
_rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, transportSequenceNumberUriId); _rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, transportSequenceNumberUriId);
_rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kVideoRotationUri, videoRotationUri); _rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kVideoRotationUri, videoRotationUri);
@ -267,7 +263,7 @@ void ContentNegotiationContext::copyCodecsFromChannelManager(cricket::MediaEngin
std::string ContentNegotiationContext::addOutgoingChannel(signaling::MediaContent::Type mediaType) { std::string ContentNegotiationContext::addOutgoingChannel(signaling::MediaContent::Type mediaType) {
std::string channelId = takeNextOutgoingChannelId(); std::string channelId = takeNextOutgoingChannelId();
cricket::MediaType mappedMediaType; cricket::MediaType mappedMediaType;
std::vector<webrtc::RtpHeaderExtensionCapability> rtpExtensions; std::vector<webrtc::RtpHeaderExtensionCapability> rtpExtensions;
switch (mediaType) { switch (mediaType) {
@ -288,7 +284,7 @@ std::string ContentNegotiationContext::addOutgoingChannel(signaling::MediaConten
} }
cricket::MediaDescriptionOptions offerDescription(mappedMediaType, channelId, webrtc::RtpTransceiverDirection::kSendOnly, false); cricket::MediaDescriptionOptions offerDescription(mappedMediaType, channelId, webrtc::RtpTransceiverDirection::kSendOnly, false);
offerDescription.header_extensions = rtpExtensions; offerDescription.header_extensions = rtpExtensions;
switch (mediaType) { switch (mediaType) {
case signaling::MediaContent::Type::Audio: { case signaling::MediaContent::Type::Audio: {
offerDescription.AddAudioSender(channelId, { channelId }); offerDescription.AddAudioSender(channelId, { channelId });
@ -304,10 +300,10 @@ std::string ContentNegotiationContext::addOutgoingChannel(signaling::MediaConten
break; break;
} }
} }
_outgoingChannelDescriptions.emplace_back(std::move(offerDescription)); _outgoingChannelDescriptions.emplace_back(std::move(offerDescription));
_needNegotiation = true; _needNegotiation = true;
return channelId; return channelId;
} }
@ -315,9 +311,9 @@ void ContentNegotiationContext::removeOutgoingChannel(std::string const &id) {
for (size_t i = 0; i < _outgoingChannels.size(); i++) { for (size_t i = 0; i < _outgoingChannels.size(); i++) {
if (_outgoingChannelDescriptions[i].description.mid == id) { if (_outgoingChannelDescriptions[i].description.mid == id) {
_outgoingChannelDescriptions.erase(_outgoingChannelDescriptions.begin() + i); _outgoingChannelDescriptions.erase(_outgoingChannelDescriptions.begin() + i);
_needNegotiation = true; _needNegotiation = true;
break; break;
} }
} }
@ -327,66 +323,66 @@ std::unique_ptr<cricket::SessionDescription> ContentNegotiationContext::currentS
if (_channelIdOrder.empty()) { if (_channelIdOrder.empty()) {
return nullptr; return nullptr;
} }
auto sessionDescription = std::make_unique<cricket::SessionDescription>(); auto sessionDescription = std::make_unique<cricket::SessionDescription>();
for (const auto &id : _channelIdOrder) { for (const auto &id : _channelIdOrder) {
bool found = false; bool found = false;
for (const auto &channel : _incomingChannels) { for (const auto &channel : _incomingChannels) {
if (contentIdBySsrc(channel.ssrc) == id) { if (contentIdBySsrc(channel.ssrc) == id) {
found = true; found = true;
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(channel.ssrc), channel, webrtc::RtpTransceiverDirection::kRecvOnly); auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(channel.ssrc), channel, webrtc::RtpTransceiverDirection::kRecvOnly);
cricket::TransportDescription transportDescription; cricket::TransportDescription transportDescription;
cricket::TransportInfo transportInfo(contentIdBySsrc(channel.ssrc), transportDescription); cricket::TransportInfo transportInfo(contentIdBySsrc(channel.ssrc), transportDescription);
sessionDescription->AddTransportInfo(transportInfo); sessionDescription->AddTransportInfo(transportInfo);
sessionDescription->AddContent(std::move(mappedContent)); sessionDescription->AddContent(std::move(mappedContent));
break; break;
} }
} }
for (const auto &channel : _outgoingChannels) { for (const auto &channel : _outgoingChannels) {
if (channel.id == id) { if (channel.id == id) {
found = true; found = true;
auto mappedContent = convertSingalingContentToContentInfo(channel.id, channel.content, webrtc::RtpTransceiverDirection::kSendOnly); auto mappedContent = convertSingalingContentToContentInfo(channel.id, channel.content, webrtc::RtpTransceiverDirection::kSendOnly);
cricket::TransportDescription transportDescription; cricket::TransportDescription transportDescription;
cricket::TransportInfo transportInfo(mappedContent.name, transportDescription); cricket::TransportInfo transportInfo(mappedContent.name, transportDescription);
sessionDescription->AddTransportInfo(transportInfo); sessionDescription->AddTransportInfo(transportInfo);
sessionDescription->AddContent(std::move(mappedContent)); sessionDescription->AddContent(std::move(mappedContent));
break; break;
} }
} }
if (!found) { if (!found) {
auto mappedContent = createInactiveContentInfo("_" + id); auto mappedContent = createInactiveContentInfo("_" + id);
cricket::TransportDescription transportDescription; cricket::TransportDescription transportDescription;
cricket::TransportInfo transportInfo(mappedContent.name, transportDescription); cricket::TransportInfo transportInfo(mappedContent.name, transportDescription);
sessionDescription->AddTransportInfo(transportInfo); sessionDescription->AddTransportInfo(transportInfo);
sessionDescription->AddContent(std::move(mappedContent)); sessionDescription->AddContent(std::move(mappedContent));
} }
} }
return sessionDescription; return sessionDescription;
} }
static cricket::MediaDescriptionOptions getIncomingContentDescription(signaling::MediaContent const &content) { static cricket::MediaDescriptionOptions getIncomingContentDescription(signaling::MediaContent const &content) {
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly); auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly);
cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kRecvOnly, false); cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kRecvOnly, false);
for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) { for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) {
contentDescription.header_extensions.emplace_back(extension.uri, extension.id); contentDescription.header_extensions.emplace_back(extension.uri, extension.id);
} }
return contentDescription; return contentDescription;
} }
@ -397,71 +393,73 @@ std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiati
if (_pendingOutgoingOffer) { if (_pendingOutgoingOffer) {
return nullptr; return nullptr;
} }
_needNegotiation = false;
_pendingOutgoingOffer = std::make_unique<PendingOutgoingOffer>(); _pendingOutgoingOffer = std::make_unique<PendingOutgoingOffer>();
_pendingOutgoingOffer->exchangeId = _uniqueRandomIdGenerator->GenerateId(); _pendingOutgoingOffer->exchangeId = _uniqueRandomIdGenerator->GenerateId();
auto currentSessionDescription = currentSessionDescriptionFromCoordinatedState(); auto currentSessionDescription = currentSessionDescriptionFromCoordinatedState();
cricket::MediaSessionOptions offerOptions; cricket::MediaSessionOptions offerOptions;
offerOptions.offer_extmap_allow_mixed = true; offerOptions.offer_extmap_allow_mixed = true;
offerOptions.bundle_enabled = true; offerOptions.bundle_enabled = true;
for (const auto &id : _channelIdOrder) { for (const auto &id : _channelIdOrder) {
bool found = false; bool found = false;
for (const auto &channel : _outgoingChannelDescriptions) { for (const auto &channel : _outgoingChannelDescriptions) {
if (channel.description.mid == id) { if (channel.description.mid == id) {
found = true; found = true;
offerOptions.media_description_options.push_back(channel.description); offerOptions.media_description_options.push_back(channel.description);
break; break;
} }
} }
for (const auto &content : _incomingChannels) { for (const auto &content : _incomingChannels) {
if (contentIdBySsrc(content.ssrc) == id) { if (contentIdBySsrc(content.ssrc) == id) {
found = true; found = true;
offerOptions.media_description_options.push_back(getIncomingContentDescription(content)); offerOptions.media_description_options.push_back(getIncomingContentDescription(content));
break; break;
} }
} }
if (!found) { if (!found) {
cricket::MediaDescriptionOptions contentDescription(cricket::MediaType::MEDIA_TYPE_AUDIO, "_" + id, webrtc::RtpTransceiverDirection::kInactive, false); cricket::MediaDescriptionOptions contentDescription(cricket::MediaType::MEDIA_TYPE_AUDIO, "_" + id, webrtc::RtpTransceiverDirection::kInactive, false);
offerOptions.media_description_options.push_back(contentDescription); offerOptions.media_description_options.push_back(contentDescription);
} }
} }
for (const auto &channel : _outgoingChannelDescriptions) { for (const auto &channel : _outgoingChannelDescriptions) {
if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), channel.description.mid) == _channelIdOrder.end()) { if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), channel.description.mid) == _channelIdOrder.end()) {
_channelIdOrder.push_back(channel.description.mid); _channelIdOrder.push_back(channel.description.mid);
offerOptions.media_description_options.push_back(channel.description); offerOptions.media_description_options.push_back(channel.description);
} }
for (const auto &content : _incomingChannels) { for (const auto &content : _incomingChannels) {
if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), contentIdBySsrc(content.ssrc)) == _channelIdOrder.end()) { if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), contentIdBySsrc(content.ssrc)) == _channelIdOrder.end()) {
_channelIdOrder.push_back(contentIdBySsrc(content.ssrc)); _channelIdOrder.push_back(contentIdBySsrc(content.ssrc));
offerOptions.media_description_options.push_back(getIncomingContentDescription(content)); offerOptions.media_description_options.push_back(getIncomingContentDescription(content));
} }
} }
} }
std::unique_ptr<cricket::SessionDescription> offer = _sessionDescriptionFactory->CreateOffer(offerOptions, currentSessionDescription.get()); std::unique_ptr<cricket::SessionDescription> offer = _sessionDescriptionFactory->CreateOffer(offerOptions, currentSessionDescription.get());
auto mappedOffer = std::make_unique<ContentNegotiationContext::NegotiationContents>(); auto mappedOffer = std::make_unique<ContentNegotiationContext::NegotiationContents>();
mappedOffer->exchangeId = _pendingOutgoingOffer->exchangeId; mappedOffer->exchangeId = _pendingOutgoingOffer->exchangeId;
for (const auto &content : offer->contents()) { for (const auto &content : offer->contents()) {
auto mappedContent = convertContentInfoToSingalingContent(content); auto mappedContent = convertContentInfoToSingalingContent(content);
if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kSendOnly) { if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kSendOnly) {
mappedOffer->contents.push_back(std::move(mappedContent)); mappedOffer->contents.push_back(std::move(mappedContent));
for (auto &channel : _outgoingChannelDescriptions) { for (auto &channel : _outgoingChannelDescriptions) {
if (channel.description.mid == content.mid()) { if (channel.description.mid == content.mid()) {
channel.ssrc = mappedContent.ssrc; channel.ssrc = mappedContent.ssrc;
@ -470,7 +468,7 @@ std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiati
} }
} }
} }
return mappedOffer; return mappedOffer;
} }
@ -478,7 +476,7 @@ std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiati
if (!remoteNegotiationContent) { if (!remoteNegotiationContent) {
return nullptr; return nullptr;
} }
if (_pendingOutgoingOffer) { if (_pendingOutgoingOffer) {
if (remoteNegotiationContent->exchangeId == _pendingOutgoingOffer->exchangeId) { if (remoteNegotiationContent->exchangeId == _pendingOutgoingOffer->exchangeId) {
setAnswer(std::move(remoteNegotiationContent)); setAnswer(std::move(remoteNegotiationContent));
@ -499,118 +497,118 @@ std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiati
std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiationContext::getAnswer(std::unique_ptr<ContentNegotiationContext::NegotiationContents> &&offer) { std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiationContext::getAnswer(std::unique_ptr<ContentNegotiationContext::NegotiationContents> &&offer) {
auto currentSessionDescription = currentSessionDescriptionFromCoordinatedState(); auto currentSessionDescription = currentSessionDescriptionFromCoordinatedState();
auto mappedOffer = std::make_unique<cricket::SessionDescription>(); auto mappedOffer = std::make_unique<cricket::SessionDescription>();
cricket::MediaSessionOptions answerOptions; cricket::MediaSessionOptions answerOptions;
answerOptions.offer_extmap_allow_mixed = true; answerOptions.offer_extmap_allow_mixed = true;
answerOptions.bundle_enabled = true; answerOptions.bundle_enabled = true;
for (const auto &id : _channelIdOrder) { for (const auto &id : _channelIdOrder) {
bool found = false; bool found = false;
for (const auto &channel : _outgoingChannels) { for (const auto &channel : _outgoingChannels) {
if (channel.id == id) { if (channel.id == id) {
found = true; found = true;
auto mappedContent = convertSingalingContentToContentInfo(channel.id, channel.content, webrtc::RtpTransceiverDirection::kRecvOnly); auto mappedContent = convertSingalingContentToContentInfo(channel.id, channel.content, webrtc::RtpTransceiverDirection::kRecvOnly);
cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kSendOnly, false); cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kSendOnly, false);
for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) { for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) {
contentDescription.header_extensions.emplace_back(extension.uri, extension.id); contentDescription.header_extensions.emplace_back(extension.uri, extension.id);
} }
answerOptions.media_description_options.push_back(contentDescription); answerOptions.media_description_options.push_back(contentDescription);
cricket::TransportDescription transportDescription; cricket::TransportDescription transportDescription;
cricket::TransportInfo transportInfo(channel.id, transportDescription); cricket::TransportInfo transportInfo(channel.id, transportDescription);
mappedOffer->AddTransportInfo(transportInfo); mappedOffer->AddTransportInfo(transportInfo);
mappedOffer->AddContent(std::move(mappedContent)); mappedOffer->AddContent(std::move(mappedContent));
break; break;
} }
} }
for (const auto &content : offer->contents) { for (const auto &content : offer->contents) {
if (contentIdBySsrc(content.ssrc) == id) { if (contentIdBySsrc(content.ssrc) == id) {
found = true; found = true;
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly); auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly);
cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kRecvOnly, false); cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kRecvOnly, false);
for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) { for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) {
contentDescription.header_extensions.emplace_back(extension.uri, extension.id); contentDescription.header_extensions.emplace_back(extension.uri, extension.id);
} }
answerOptions.media_description_options.push_back(contentDescription); answerOptions.media_description_options.push_back(contentDescription);
cricket::TransportDescription transportDescription; cricket::TransportDescription transportDescription;
cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription); cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription);
mappedOffer->AddTransportInfo(transportInfo); mappedOffer->AddTransportInfo(transportInfo);
mappedOffer->AddContent(std::move(mappedContent)); mappedOffer->AddContent(std::move(mappedContent));
break; break;
} }
} }
if (!found) { if (!found) {
auto mappedContent = createInactiveContentInfo("_" + id); auto mappedContent = createInactiveContentInfo("_" + id);
cricket::MediaDescriptionOptions contentDescription(cricket::MediaType::MEDIA_TYPE_AUDIO, "_" + id, webrtc::RtpTransceiverDirection::kInactive, false); cricket::MediaDescriptionOptions contentDescription(cricket::MediaType::MEDIA_TYPE_AUDIO, "_" + id, webrtc::RtpTransceiverDirection::kInactive, false);
answerOptions.media_description_options.push_back(contentDescription); answerOptions.media_description_options.push_back(contentDescription);
cricket::TransportDescription transportDescription; cricket::TransportDescription transportDescription;
cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription); cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription);
mappedOffer->AddTransportInfo(transportInfo); mappedOffer->AddTransportInfo(transportInfo);
mappedOffer->AddContent(std::move(mappedContent)); mappedOffer->AddContent(std::move(mappedContent));
} }
} }
for (const auto &content : offer->contents) { for (const auto &content : offer->contents) {
if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), contentIdBySsrc(content.ssrc)) == _channelIdOrder.end()) { if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), contentIdBySsrc(content.ssrc)) == _channelIdOrder.end()) {
_channelIdOrder.push_back(contentIdBySsrc(content.ssrc)); _channelIdOrder.push_back(contentIdBySsrc(content.ssrc));
answerOptions.media_description_options.push_back(getIncomingContentDescription(content)); answerOptions.media_description_options.push_back(getIncomingContentDescription(content));
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly); auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly);
cricket::TransportDescription transportDescription; cricket::TransportDescription transportDescription;
cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription); cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription);
mappedOffer->AddTransportInfo(transportInfo); mappedOffer->AddTransportInfo(transportInfo);
mappedOffer->AddContent(std::move(mappedContent)); mappedOffer->AddContent(std::move(mappedContent));
} }
} }
std::unique_ptr<cricket::SessionDescription> answer = _sessionDescriptionFactory->CreateAnswer(mappedOffer.get(), answerOptions, currentSessionDescription.get()); std::unique_ptr<cricket::SessionDescription> answer = _sessionDescriptionFactory->CreateAnswer(mappedOffer.get(), answerOptions, currentSessionDescription.get());
auto mappedAnswer = std::make_unique<NegotiationContents>(); auto mappedAnswer = std::make_unique<NegotiationContents>();
mappedAnswer->exchangeId = offer->exchangeId; mappedAnswer->exchangeId = offer->exchangeId;
std::vector<signaling::MediaContent> incomingChannels; std::vector<signaling::MediaContent> incomingChannels;
for (const auto &content : answer->contents()) { for (const auto &content : answer->contents()) {
auto mappedContent = convertContentInfoToSingalingContent(content); auto mappedContent = convertContentInfoToSingalingContent(content);
if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kRecvOnly) { if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kRecvOnly) {
for (const auto &offerContent : offer->contents) { for (const auto &offerContent : offer->contents) {
if (contentIdBySsrc(offerContent.ssrc) == content.mid()) { if (contentIdBySsrc(offerContent.ssrc) == content.mid()) {
mappedContent.ssrc = offerContent.ssrc; mappedContent.ssrc = offerContent.ssrc;
mappedContent.ssrcGroups = offerContent.ssrcGroups; mappedContent.ssrcGroups = offerContent.ssrcGroups;
break; break;
} }
} }
incomingChannels.push_back(mappedContent); incomingChannels.push_back(mappedContent);
mappedAnswer->contents.push_back(std::move(mappedContent)); mappedAnswer->contents.push_back(std::move(mappedContent));
} }
} }
_incomingChannels = incomingChannels; _incomingChannels = incomingChannels;
return mappedAnswer; return mappedAnswer;
} }
@ -621,17 +619,16 @@ void ContentNegotiationContext::setAnswer(std::unique_ptr<ContentNegotiationCont
if (_pendingOutgoingOffer->exchangeId != answer->exchangeId) { if (_pendingOutgoingOffer->exchangeId != answer->exchangeId) {
return; return;
} }
_pendingOutgoingOffer.reset(); _pendingOutgoingOffer.reset();
_needNegotiation = false;
_outgoingChannels.clear(); _outgoingChannels.clear();
for (const auto &content : answer->contents) { for (const auto &content : answer->contents) {
for (const auto &pendingChannel : _outgoingChannelDescriptions) { for (const auto &pendingChannel : _outgoingChannelDescriptions) {
if (pendingChannel.ssrc != 0 && content.ssrc == pendingChannel.ssrc) { if (pendingChannel.ssrc != 0 && content.ssrc == pendingChannel.ssrc) {
_outgoingChannels.emplace_back(pendingChannel.description.mid, content); _outgoingChannels.emplace_back(pendingChannel.description.mid, content);
break; break;
} }
} }
@ -639,53 +636,52 @@ void ContentNegotiationContext::setAnswer(std::unique_ptr<ContentNegotiationCont
} }
std::string ContentNegotiationContext::takeNextOutgoingChannelId() { std::string ContentNegotiationContext::takeNextOutgoingChannelId() {
std::ostringstream result; const auto result = "m" + std::to_string(_nextOutgoingChannelId);
result << "m" << _nextOutgoingChannelId;
_nextOutgoingChannelId++; _nextOutgoingChannelId++;
return result.str(); return result;
} }
std::unique_ptr<ContentNegotiationContext::CoordinatedState> ContentNegotiationContext::coordinatedState() const { std::unique_ptr<ContentNegotiationContext::CoordinatedState> ContentNegotiationContext::coordinatedState() const {
auto result = std::make_unique<ContentNegotiationContext::CoordinatedState>(); auto result = std::make_unique<ContentNegotiationContext::CoordinatedState>();
result->incomingContents = _incomingChannels; result->incomingContents = _incomingChannels;
for (const auto &channel : _outgoingChannels) { for (const auto &channel : _outgoingChannels) {
bool found = false; bool found = false;
for (const auto &channelDescription : _outgoingChannelDescriptions) { for (const auto &channelDescription : _outgoingChannelDescriptions) {
if (channelDescription.description.mid == channel.id) { if (channelDescription.description.mid == channel.id) {
found = true; found = true;
break; break;
} }
} }
if (found) { if (found) {
result->outgoingContents.push_back(channel.content); result->outgoingContents.push_back(channel.content);
} }
} }
return result; return result;
} }
absl::optional<uint32_t> ContentNegotiationContext::outgoingChannelSsrc(std::string const &id) const { absl::optional<uint32_t> ContentNegotiationContext::outgoingChannelSsrc(std::string const &id) const {
for (const auto &channel : _outgoingChannels) { for (const auto &channel : _outgoingChannels) {
bool found = false; bool found = false;
for (const auto &channelDescription : _outgoingChannelDescriptions) { for (const auto &channelDescription : _outgoingChannelDescriptions) {
if (channelDescription.description.mid == channel.id) { if (channelDescription.description.mid == channel.id) {
found = true; found = true;
break; break;
} }
} }
if (found && channel.id == id) { if (found && channel.id == id) {
if (channel.content.ssrc != 0) { if (channel.content.ssrc != 0) {
return channel.content.ssrc; return channel.content.ssrc;
} }
} }
} }
return absl::nullopt; return absl::nullopt;
} }

View file

@ -0,0 +1,597 @@
#include "v2/DirectNetworkingImpl.h"
#include "p2p/base/basic_packet_socket_factory.h"
#include "p2p/client/basic_port_allocator.h"
#include "p2p/base/p2p_transport_channel.h"
#include "p2p/base/basic_async_resolver_factory.h"
#include "api/packet_socket_factory.h"
#include "rtc_base/rtc_certificate_generator.h"
#include "p2p/base/ice_credentials_iterator.h"
#include "api/jsep_ice_candidate.h"
#include "p2p/base/dtls_transport.h"
#include "p2p/base/dtls_transport_factory.h"
#include "pc/dtls_srtp_transport.h"
#include "pc/dtls_transport.h"
#include "pc/jsep_transport_controller.h"
#include "api/async_dns_resolver.h"
#include "TurnCustomizerImpl.h"
#include "ReflectorRelayPortFactory.h"
#include "SctpDataChannelProviderInterfaceImpl.h"
#include "StaticThreads.h"
#include "platform/PlatformInterface.h"
#include "p2p/base/turn_port.h"
#include "ReflectorPort.h"
#include "FieldTrialsConfig.h"
namespace tgcalls {
namespace {
}
class DirectPacketTransport : public rtc::PacketTransportInternal, public std::enable_shared_from_this<DirectPacketTransport> {
public:
DirectPacketTransport(
rtc::Thread *thread,
EncryptionKey const &encryptionKey,
std::shared_ptr<DirectConnectionChannel> channel, std::function<void(bool)> &&isConnectedUpdated
) :
_isConnectedUpdated(std::move(isConnectedUpdated)),
_thread(thread),
_encryption(
EncryptedConnection::Type::Transport,
encryptionKey,
[=](int delayMs, int cause) {
assert(false);
}),
_channel(channel) {
assert(_thread->IsCurrent());
}
virtual ~DirectPacketTransport() {
}
void start() {
auto weakSelf = std::weak_ptr<DirectPacketTransport>(shared_from_this());
_onIncomingPacketToken = _channel->addOnIncomingPacket([weakSelf, thread = _thread](std::shared_ptr<std::vector<uint8_t>> packet) {
thread->PostTask([weakSelf, packet] {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
strongSelf->processIncomingPacket(packet);
});
});
updateState();
runStateTimer();
}
void stop() {
if (!_onIncomingPacketToken.empty()) {
_channel->removeOnIncomingPacket(_onIncomingPacketToken);
_onIncomingPacketToken.resize(0);
}
}
public:
virtual const std::string &transport_name() const override {
return name;
}
// The transport has been established.
virtual bool writable() const override {
return _isConnected;
}
// The transport has received a packet in the last X milliseconds, here X is
// configured by each implementation.
virtual bool receiving() const override {
return _isConnected;
}
// Attempts to send the given packet.
// The return value is < 0 on failure. The return value in failure case is not
// descriptive. Depending on failure cause and implementation details
// GetError() returns an descriptive errno.h error value.
// This mimics posix socket send() or sendto() behavior.
// TODO(johan): Reliable, meaningful, consistent error codes for all
// implementations would be nice.
// TODO(johan): Remove the default argument once channel code is updated.
virtual int SendPacket(const char *data,
size_t len,
const rtc::PacketOptions& options,
int flags = 0) override {
if (!_isConnected) {
_lastError = ENOTCONN;
return -1;
}
rtc::CopyOnWriteBuffer buffer;
buffer.AppendData(data, len);
Message message = flags == 0 ? Message { AudioDataMessage { buffer } } : Message { VideoDataMessage { buffer } };
if (const auto prepared = _encryption.prepareForSending(message)) {
rtc::PacketOptions packetOptions;
rtc::ByteBufferWriter bufferWriter;
bufferWriter.WriteUInt32((uint32_t)prepared->bytes.size());
bufferWriter.WriteBytes(reinterpret_cast<const char *>(prepared->bytes.data()), prepared->bytes.size());
while (bufferWriter.Length() % 4 != 0) {
bufferWriter.WriteUInt8(0);
}
auto packet = std::make_unique<std::vector<uint8_t>>(bufferWriter.Data(), bufferWriter.Data() + bufferWriter.Length());
_channel->sendPacket(std::move(packet));
}
rtc::SentPacket sentPacket;
sentPacket.packet_id = options.packet_id;
sentPacket.send_time_ms = rtc::TimeMillis();
SignalSentPacket(this, sentPacket);
return 0;
}
// Sets a socket option. Note that not all options are
// supported by all transport types.
virtual int SetOption(rtc::Socket::Option opt, int value) override {
return 0;
}
// TODO(pthatcher): Once Chrome's MockPacketTransportInterface implements
// this, remove the default implementation.
virtual bool GetOption(rtc::Socket::Option opt, int *value) override {
return false;
}
// Returns the most recent error that occurred on this channel.
virtual int GetError() override {
return _lastError;
}
// Returns the current network route with transport overhead.
// TODO(zhihuang): Make it pure virtual once the Chrome/remoting is updated.
virtual absl::optional<rtc::NetworkRoute> network_route() const override {
return absl::nullopt;
}
/*sigslot::signal1<PacketTransportInternal*> SignalWritableState;
// Emitted when the PacketTransportInternal is ready to send packets. "Ready
// to send" is more sensitive than the writable state; a transport may be
// writable, but temporarily not able to send packets. For example, the
// underlying transport's socket buffer may be full, as indicated by
// SendPacket's return code and/or GetError.
sigslot::signal1<PacketTransportInternal*> SignalReadyToSend;
// Emitted when receiving state changes to true.
sigslot::signal1<PacketTransportInternal*> SignalReceivingState;
// Signalled each time a packet is received on this channel.
sigslot::signal5<PacketTransportInternal*,
const char*,
size_t,
// TODO(bugs.webrtc.org/9584): Change to passing the int64_t
// timestamp by value.
const int64_t&,
int>
SignalReadPacket;
// Signalled each time a packet is sent on this channel.
sigslot::signal2<PacketTransportInternal*, const rtc::SentPacket&>
SignalSentPacket;
// Signalled when the current network route has changed.
sigslot::signal1<absl::optional<rtc::NetworkRoute>> SignalNetworkRouteChanged;
// Signalled when the transport is closed.
sigslot::signal1<PacketTransportInternal*> SignalClosed;*/
private:
void runStateTimer() {
const auto weakSelf = std::weak_ptr<DirectPacketTransport>(shared_from_this());
_thread->PostDelayedTask([weakSelf]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
strongSelf->updateState();
strongSelf->runStateTimer();
}, webrtc::TimeDelta::Millis(100));
}
std::unique_ptr<std::vector<uint8_t>> makeServerHelloPacket() {
rtc::ByteBufferWriter bufferWriter;
for (int i = 0; i < 12; i++) {
bufferWriter.WriteUInt8(0xffu);
}
bufferWriter.WriteUInt8(0xfeu);
for (int i = 0; i < 3; i++) {
bufferWriter.WriteUInt8(0xffu);
}
bufferWriter.WriteUInt64(123);
while (bufferWriter.Length() % 4 != 0) {
bufferWriter.WriteUInt8(0);
}
return std::make_unique<std::vector<uint8_t>>(bufferWriter.Data(), bufferWriter.Data() + bufferWriter.Length());
}
std::unique_ptr<std::vector<uint8_t>> makePingPacket() {
rtc::ByteBufferWriter bufferWriter;
bufferWriter.WriteUInt32(_pingMarker);
return std::make_unique<std::vector<uint8_t>>(bufferWriter.Data(), bufferWriter.Data() + bufferWriter.Length());
}
void updateState() {
auto timestamp = rtc::TimeMillis();
if (_isConnected && _lastDataReceivedTimestamp < timestamp - _keepalivePingInterval * 2) {
_isConnected = false;
SignalWritableState(this);
if (_isConnectedUpdated) {
_isConnectedUpdated(_isConnected);
}
}
if (_isConnected) {
if (_lastPingSentTimestamp < timestamp - _initialPingInterval) {
_lastPingSentTimestamp = timestamp;
auto packet = makePingPacket();
_channel->sendPacket(std::move(packet));
}
} else {
if (_lastPingSentTimestamp < timestamp - _keepalivePingInterval) {
_lastPingSentTimestamp = timestamp;
if (_hasCompletedHello) {
auto packet = makePingPacket();
_channel->sendPacket(std::move(packet));
} else {
auto packet = makeServerHelloPacket();
_channel->sendPacket(std::move(packet));
}
}
}
}
void processIncomingPacket(std::shared_ptr<std::vector<uint8_t>> const &packet) {
rtc::ByteBufferReader reader(reinterpret_cast<const char *>(packet->data()), packet->size());
uint32_t header = 0;
if (!reader.ReadUInt32(&header)) {
return;
}
_lastDataReceivedTimestamp = rtc::TimeMillis();
if (!_isConnected) {
_isConnected = true;
SignalWritableState(this);
SignalReadyToSend(this);
SignalReceivingState(this);
if (_isConnectedUpdated) {
_isConnectedUpdated(_isConnected);
}
}
if (header == _pingMarker) {
} else {
bool isSpecialPacket = false;
if (packet->size() >= 12) {
uint8_t specialTag[12];
memcpy(specialTag, packet->data(), 12);
uint8_t expectedSpecialTag[12];
memset(expectedSpecialTag, 0xff, 12);
if (memcmp(specialTag, expectedSpecialTag, 12) == 0) {
isSpecialPacket = true;
}
}
if (!isSpecialPacket) {
rtc::ByteBufferReader dataPacketReader(reinterpret_cast<const char *>(packet->data()), packet->size());
uint32_t dataSize = 0;
if (!dataPacketReader.ReadUInt32(&dataSize)) {
return;
}
if (dataSize > packet->size() - 4) {
return;
}
if (auto decrypted = _encryption.handleIncomingPacket(reinterpret_cast<const char *>(packet->data()) + 4, dataSize)) {
handleIncomingMessage(decrypted->main);
for (auto &message : decrypted->additional) {
handleIncomingMessage(message);
}
/*if (_transportMessageReceived) {
_transportMessageReceived(std::move(decrypted->main));
for (auto &message : decrypted->additional) {
_transportMessageReceived(std::move(message));
}
}*/
} else {
RTC_LOG(LS_ERROR) << "DirectPacketTransport: could not decrypt incoming packet";
}
/*uint32_t dataSize = 0;
memcpy(&dataSize, packet->data(), 4);
dataSize = be32toh(dataSize);
if (dataSize > packet->size() - 4) {
RTC_LOG(LS_WARNING) << "DirectPacketTransport: Received data packet with invalid size tag";
} else {
SignalReadPacket(this, reinterpret_cast<const char *>(packet->data() + 4), dataSize, rtc::TimeMicros(), 0);
}*/
}
}
}
void handleIncomingMessage(DecryptedMessage const &message) {
const auto data = &message.message.data;
if (const auto dataMessage = absl::get_if<AudioDataMessage>(data)) {
SignalReadPacket(this, reinterpret_cast<const char *>(dataMessage->data.data()), dataMessage->data.size(), rtc::TimeMicros(), 0);
} else if (const auto dataMessage = absl::get_if<VideoDataMessage>(data)) {
SignalReadPacket(this, reinterpret_cast<const char *>(dataMessage->data.data()), dataMessage->data.size(), rtc::TimeMicros(), 1);
} else {
RTC_LOG(LS_INFO) << "DirectPacketTransport: unknown incoming message";
}
}
private:
std::string name = "DirectPacketTransport";
std::function<void(bool)> _isConnectedUpdated;
rtc::Thread *_thread = nullptr;
EncryptedConnection _encryption;
std::shared_ptr<DirectConnectionChannel> _channel;
std::vector<uint8_t> _onIncomingPacketToken;
int _lastError = 0;
int64_t _lastPingSentTimestamp = 0;
int64_t _lastDataReceivedTimestamp = 0;
bool _isConnected = false;
bool _hasCompletedHello = false;
uint32_t _pingMarker = 0xabcd0102;
int64_t _initialPingInterval = 100;
int64_t _keepalivePingInterval = 1000;
};
class DirectRtpTransport : public webrtc::RtpTransport {
public:
explicit DirectRtpTransport() :
webrtc::RtpTransport(true) {
}
virtual bool IsSrtpActive() const override {
return true;
}
};
DirectNetworkingImpl::DirectNetworkingImpl(Configuration &&configuration) :
_threads(std::move(configuration.threads)),
_isOutgoing(configuration.isOutgoing),
_rtcServers(configuration.rtcServers),
_stateUpdated(std::move(configuration.stateUpdated)),
_transportMessageReceived(std::move(configuration.transportMessageReceived)),
_rtcpPacketReceived(std::move(configuration.rtcpPacketReceived)),
_dataChannelStateUpdated(configuration.dataChannelStateUpdated),
_dataChannelMessageReceived(configuration.dataChannelMessageReceived) {
assert(_threads->getNetworkThread()->IsCurrent());
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), true);
_localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
_rtpTransport = std::make_unique<DirectRtpTransport>();
_rtpTransport->SignalReadyToSend.connect(this, &DirectNetworkingImpl::DtlsReadyToSend);
_rtpTransport->SignalRtcpPacketReceived.connect(this, &DirectNetworkingImpl::OnRtcpPacketReceived_n);
_directConnectionChannel = configuration.directConnectionChannel;
_packetTransport = std::make_shared<DirectPacketTransport>(
_threads->getNetworkThread(),
configuration.encryptionKey,
_directConnectionChannel,
[this](bool isConnected) {
this->_transportIsConnected = isConnected;
this->UpdateAggregateStates_n();
}
);
resetDtlsSrtpTransport();
}
DirectNetworkingImpl::~DirectNetworkingImpl() {
assert(_threads->getNetworkThread()->IsCurrent());
RTC_LOG(LS_INFO) << "DirectNetworkingImpl::~DirectNetworkingImpl()";
_rtpTransport.reset();
_dataChannelInterface.reset();
_packetTransport.reset();
}
void DirectNetworkingImpl::resetDtlsSrtpTransport() {
_rtpTransport->SetRtpPacketTransport(_packetTransport.get());
}
void DirectNetworkingImpl::start() {
const auto weak = std::weak_ptr<DirectNetworkingImpl>(shared_from_this());
_dataChannelInterface.reset(new SctpDataChannelProviderInterfaceImpl(
_packetTransport.get(),
_isOutgoing,
[weak, threads = _threads](bool state) {
assert(threads->getNetworkThread()->IsCurrent());
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->_dataChannelStateUpdated(state);
},
[weak, threads = _threads]() {
assert(threads->getNetworkThread()->IsCurrent());
const auto strong = weak.lock();
if (!strong) {
return;
}
//strong->restartDataChannel();
},
[weak, threads = _threads](std::string const &message) {
assert(threads->getNetworkThread()->IsCurrent());
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->_dataChannelMessageReceived(message);
},
_threads
));
_lastDisconnectedTimestamp = rtc::TimeMillis();
checkConnectionTimeout();
_packetTransport->start();
}
void DirectNetworkingImpl::stop() {
_rtpTransport->SignalWritableState.disconnect(this);
//_rtpTransport->SignalReceivingState.disconnect(this);
_dataChannelInterface.reset();
_rtpTransport.reset();
_packetTransport->stop();
_localIceParameters = PeerIceParameters(rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), true);
_localCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
}
PeerIceParameters DirectNetworkingImpl::getLocalIceParameters() {
return _localIceParameters;
}
std::unique_ptr<rtc::SSLFingerprint> DirectNetworkingImpl::getLocalFingerprint() {
auto certificate = _localCertificate;
if (!certificate) {
return nullptr;
}
return rtc::SSLFingerprint::CreateFromCertificate(*certificate);
}
void DirectNetworkingImpl::setRemoteParams(PeerIceParameters const &remoteIceParameters, rtc::SSLFingerprint *fingerprint, std::string const &sslSetup) {
}
void DirectNetworkingImpl::addCandidates(std::vector<cricket::Candidate> const &candidates) {
}
void DirectNetworkingImpl::sendDataChannelMessage(std::string const &message) {
if (_dataChannelInterface) {
_dataChannelInterface->sendDataChannelMessage(message);
}
}
webrtc::RtpTransport *DirectNetworkingImpl::getRtpTransport() {
return _rtpTransport.get();
}
void DirectNetworkingImpl::checkConnectionTimeout() {
const auto weak = std::weak_ptr<DirectNetworkingImpl>(shared_from_this());
_threads->getNetworkThread()->PostDelayedTask([weak]() {
auto strong = weak.lock();
if (!strong) {
return;
}
int64_t currentTimestamp = rtc::TimeMillis();
const int64_t maxTimeout = 20000;
if (!strong->_isConnected && strong->_lastDisconnectedTimestamp + maxTimeout < currentTimestamp) {
RTC_LOG(LS_INFO) << "DirectNetworkingImpl timeout " << (currentTimestamp - strong->_lastDisconnectedTimestamp) << " ms";
strong->_isFailed = true;
strong->notifyStateUpdated();
}
strong->checkConnectionTimeout();
}, webrtc::TimeDelta::Millis(1000));
}
void DirectNetworkingImpl::OnTransportWritableState_n(rtc::PacketTransportInternal *transport) {
assert(_threads->getNetworkThread()->IsCurrent());
UpdateAggregateStates_n();
}
void DirectNetworkingImpl::OnTransportReceivingState_n(rtc::PacketTransportInternal *transport) {
assert(_threads->getNetworkThread()->IsCurrent());
UpdateAggregateStates_n();
}
void DirectNetworkingImpl::DtlsReadyToSend(bool isReadyToSend) {
UpdateAggregateStates_n();
if (isReadyToSend) {
const auto weak = std::weak_ptr<DirectNetworkingImpl>(shared_from_this());
_threads->getNetworkThread()->PostTask([weak]() {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->UpdateAggregateStates_n();
});
}
}
void DirectNetworkingImpl::OnRtcpPacketReceived_n(rtc::CopyOnWriteBuffer *packet, int64_t packet_time_us) {
if (_rtcpPacketReceived) {
_rtcpPacketReceived(*packet, packet_time_us);
}
}
void DirectNetworkingImpl::UpdateAggregateStates_n() {
assert(_threads->getNetworkThread()->IsCurrent());
bool isConnected = _transportIsConnected;
if (_isConnected != isConnected) {
_isConnected = isConnected;
if (!isConnected) {
_lastDisconnectedTimestamp = rtc::TimeMillis();
}
notifyStateUpdated();
if (_dataChannelInterface) {
_dataChannelInterface->updateIsConnected(isConnected);
}
}
}
void DirectNetworkingImpl::notifyStateUpdated() {
DirectNetworkingImpl::State emitState;
emitState.isReadyToSendData = _isConnected;
emitState.route = _currentRouteDescription;
emitState.connection = _currentConnectionDescription;
emitState.isFailed = _isFailed;
_stateUpdated(emitState);
}
} // namespace tgcalls

View file

@ -0,0 +1,119 @@
#ifndef TGCALLS_DIRECT_NETWORKING_IMPL_H
#define TGCALLS_DIRECT_NETWORKING_IMPL_H
#ifdef WEBRTC_WIN
// Compiler errors in conflicting Windows headers if not included here.
#include <winsock2.h>
#endif // WEBRTC_WIN
#include "rtc_base/copy_on_write_buffer.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
#include "api/candidate.h"
#include "media/base/media_channel.h"
#include "rtc_base/ssl_fingerprint.h"
#include "pc/sctp_data_channel.h"
#include "p2p/base/port.h"
#include "api/transport/field_trial_based_config.h"
#include <functional>
#include <memory>
#include "InstanceNetworking.h"
#include "Message.h"
#include "ThreadLocalObject.h"
#include "Instance.h"
#include "EncryptedConnection.h"
namespace rtc {
class BasicPacketSocketFactory;
class BasicNetworkManager;
class PacketTransportInternal;
struct NetworkRoute;
} // namespace rtc
namespace cricket {
class BasicPortAllocator;
class P2PTransportChannel;
class IceTransportInternal;
class DtlsTransport;
class RelayPortFactoryInterface;
} // namespace cricket
namespace webrtc {
class TurnCustomizer;
class DtlsSrtpTransport;
class RtpTransport;
class AsyncDnsResolverFactoryInterface;
} // namespace webrtc
namespace tgcalls {
struct Message;
class SctpDataChannelProviderInterfaceImpl;
class Threads;
class DirectPacketTransport;
class DirectRtpTransport;
class DirectNetworkingImpl : public InstanceNetworking, public sigslot::has_slots<>, public std::enable_shared_from_this<DirectNetworkingImpl> {
public:
static webrtc::CryptoOptions getDefaulCryptoOptions();
DirectNetworkingImpl(Configuration &&configuration);
~DirectNetworkingImpl();
void start();
void stop();
PeerIceParameters getLocalIceParameters();
std::unique_ptr<rtc::SSLFingerprint> getLocalFingerprint();
void setRemoteParams(PeerIceParameters const &remoteIceParameters, rtc::SSLFingerprint *fingerprint, std::string const &sslSetup);
void addCandidates(std::vector<cricket::Candidate> const &candidates);
void sendDataChannelMessage(std::string const &message);
webrtc::RtpTransport *getRtpTransport();
private:
void checkConnectionTimeout();
void notifyStateUpdated();
void resetDtlsSrtpTransport();
void DtlsReadyToSend(bool DtlsReadyToSend);
void UpdateAggregateStates_n();
void OnRtcpPacketReceived_n(rtc::CopyOnWriteBuffer *packet, int64_t packet_time_us);
void OnTransportWritableState_n(rtc::PacketTransportInternal *transport);
void OnTransportReceivingState_n(rtc::PacketTransportInternal *transport);
private:
std::shared_ptr<Threads> _threads;
bool _isOutgoing = false;
rtc::scoped_refptr<rtc::RTCCertificate> _localCertificate;
std::vector<RtcServer> _rtcServers;
PeerIceParameters _localIceParameters;
std::unique_ptr<DirectConnectionChannel> _channel;
std::shared_ptr<DirectConnectionChannel> _directConnectionChannel;
std::shared_ptr<DirectPacketTransport> _packetTransport;
std::unique_ptr<DirectRtpTransport> _rtpTransport;
std::unique_ptr<SctpDataChannelProviderInterfaceImpl> _dataChannelInterface;
std::function<void(const DirectNetworkingImpl::State &)> _stateUpdated;
std::function<void(rtc::CopyOnWriteBuffer const &, bool)> _transportMessageReceived;
std::function<void(rtc::CopyOnWriteBuffer const &, int64_t)> _rtcpPacketReceived;
std::function<void(bool)> _dataChannelStateUpdated;
std::function<void(std::string const &)> _dataChannelMessageReceived;
bool _transportIsConnected = false;
bool _isConnected = false;
bool _isFailed = false;
int64_t _lastDisconnectedTimestamp = 0;
absl::optional<RouteDescription> _currentRouteDescription;
absl::optional<ConnectionDescription> _currentConnectionDescription;
};
} // namespace tgcalls
#endif

View file

@ -0,0 +1,169 @@
#ifndef TGCALLS_INSTANCE_NETWORKING_H
#define TGCALLS_INSTANCE_NETWORKING_H
#ifdef WEBRTC_WIN
// Compiler errors in conflicting Windows headers if not included here.
#include <winsock2.h>
#endif // WEBRTC_WIN
#include "rtc_base/copy_on_write_buffer.h"
#include "rtc_base/third_party/sigslot/sigslot.h"
#include "api/candidate.h"
#include "media/base/media_channel.h"
#include "rtc_base/ssl_fingerprint.h"
#include "pc/sctp_data_channel.h"
#include "p2p/base/port.h"
#include "api/transport/field_trial_based_config.h"
#include <functional>
#include <memory>
#include "Message.h"
#include "ThreadLocalObject.h"
#include "Instance.h"
namespace rtc {
class BasicPacketSocketFactory;
class BasicNetworkManager;
class PacketTransportInternal;
struct NetworkRoute;
} // namespace rtc
namespace cricket {
class BasicPortAllocator;
class P2PTransportChannel;
class IceTransportInternal;
class DtlsTransport;
class RelayPortFactoryInterface;
} // namespace cricket
namespace webrtc {
class TurnCustomizer;
class DtlsSrtpTransport;
class RtpTransport;
class AsyncDnsResolverFactoryInterface;
} // namespace webrtc
namespace tgcalls {
struct Message;
class SctpDataChannelProviderInterfaceImpl;
class Threads;
class InstanceNetworking {
public:
struct RouteDescription {
explicit RouteDescription(std::string const &localDescription_, std::string const &remoteDescription_) :
localDescription(localDescription_),
remoteDescription(remoteDescription_) {
}
std::string localDescription;
std::string remoteDescription;
bool operator==(RouteDescription const &rhs) const {
if (localDescription != rhs.localDescription) {
return false;
}
if (remoteDescription != rhs.remoteDescription) {
return false;
}
return true;
}
bool operator!=(const RouteDescription& rhs) const {
return !(*this == rhs);
}
};
struct ConnectionDescription {
struct CandidateDescription {
std::string protocol;
std::string type;
std::string address;
bool operator==(CandidateDescription const &rhs) const {
if (protocol != rhs.protocol) {
return false;
}
if (type != rhs.type) {
return false;
}
if (address != rhs.address) {
return false;
}
return true;
}
bool operator!=(const CandidateDescription& rhs) const {
return !(*this == rhs);
}
};
CandidateDescription local;
CandidateDescription remote;
bool operator==(ConnectionDescription const &rhs) const {
if (local != rhs.local) {
return false;
}
if (remote != rhs.remote) {
return false;
}
return true;
}
bool operator!=(const ConnectionDescription& rhs) const {
return !(*this == rhs);
}
};
struct State {
bool isReadyToSendData = false;
bool isFailed = false;
absl::optional<RouteDescription> route;
absl::optional<ConnectionDescription> connection;
};
struct Configuration {
EncryptionKey encryptionKey;
bool isOutgoing = false;
bool enableStunMarking = false;
bool enableTCP = false;
bool enableP2P = false;
std::vector<RtcServer> rtcServers;
absl::optional<Proxy> proxy;
std::function<void(const InstanceNetworking::State &)> stateUpdated;
std::function<void(const cricket::Candidate &)> candidateGathered;
std::function<void(rtc::CopyOnWriteBuffer const &, bool)> transportMessageReceived;
std::function<void(rtc::CopyOnWriteBuffer const &, int64_t)> rtcpPacketReceived;
std::function<void(bool)> dataChannelStateUpdated;
std::function<void(std::string const &)> dataChannelMessageReceived;
std::shared_ptr<Threads> threads;
std::shared_ptr<DirectConnectionChannel> directConnectionChannel;
};
static webrtc::CryptoOptions getDefaulCryptoOptions();
static ConnectionDescription::CandidateDescription connectionDescriptionFromCandidate(cricket::Candidate const &candidate);
virtual ~InstanceNetworking() = default;
virtual void start() = 0;
virtual void stop() = 0;
virtual PeerIceParameters getLocalIceParameters() = 0;
virtual std::unique_ptr<rtc::SSLFingerprint> getLocalFingerprint() = 0;
virtual void setRemoteParams(PeerIceParameters const &remoteIceParameters, rtc::SSLFingerprint *fingerprint, std::string const &sslSetup) = 0;
virtual void addCandidates(std::vector<cricket::Candidate> const &candidates) = 0;
virtual void sendDataChannelMessage(std::string const &message) = 0;
virtual webrtc::RtpTransport *getRtpTransport() = 0;
};
} // namespace tgcalls
#endif

View file

@ -4,6 +4,7 @@
#include "VideoCaptureInterfaceImpl.h" #include "VideoCaptureInterfaceImpl.h"
#include "VideoCapturerInterface.h" #include "VideoCapturerInterface.h"
#include "v2/NativeNetworkingImpl.h" #include "v2/NativeNetworkingImpl.h"
#include "v2/DirectNetworkingImpl.h"
#include "v2/Signaling.h" #include "v2/Signaling.h"
#include "v2/ContentNegotiation.h" #include "v2/ContentNegotiation.h"
@ -77,7 +78,7 @@ SignalingProtocolVersion signalingProtocolVersion(std::string const &version) {
} else if (version == "8.0.0") { } else if (version == "8.0.0") {
return SignalingProtocolVersion::V2; return SignalingProtocolVersion::V2;
} else if (version == "9.0.0") { } else if (version == "9.0.0") {
return SignalingProtocolVersion::V3; return SignalingProtocolVersion::V2;
} else { } else {
RTC_LOG(LS_ERROR) << "signalingProtocolVersion: unknown version " << version; RTC_LOG(LS_ERROR) << "signalingProtocolVersion: unknown version " << version;
@ -136,13 +137,12 @@ public:
audioOptions.noise_suppression = true; audioOptions.noise_suppression = true;
} }
std::ostringstream contentId; const auto contentId = std::to_string(_ssrc);
contentId << _ssrc;
std::vector<std::string> streamIds; std::vector<std::string> streamIds;
streamIds.push_back(contentId.str()); streamIds.push_back(contentId);
_outgoingAudioChannel = _channelManager->CreateVoiceChannel(call, cricket::MediaConfig(), contentId.str(), false, NativeNetworkingImpl::getDefaulCryptoOptions(), audioOptions); _outgoingAudioChannel = _channelManager->CreateVoiceChannel(call, cricket::MediaConfig(), contentId, false, NativeNetworkingImpl::getDefaulCryptoOptions(), audioOptions);
_threads->getNetworkThread()->BlockingCall([&]() { _threads->getNetworkThread()->BlockingCall([&]() {
_outgoingAudioChannel->SetRtpTransport(rtpTransport); _outgoingAudioChannel->SetRtpTransport(rtpTransport);
}); });
@ -272,12 +272,9 @@ public:
audioOptions.audio_jitter_buffer_fast_accelerate = true; audioOptions.audio_jitter_buffer_fast_accelerate = true;
audioOptions.audio_jitter_buffer_min_delay_ms = 50; audioOptions.audio_jitter_buffer_min_delay_ms = 50;
std::ostringstream contentId; const auto streamId = std::to_string(_ssrc);
contentId << _ssrc;
std::string streamId = contentId.str(); _audioChannel = _channelManager->CreateVoiceChannel(call, cricket::MediaConfig(), streamId, false, NativeNetworkingImpl::getDefaulCryptoOptions(), audioOptions);
_audioChannel = _channelManager->CreateVoiceChannel(call, cricket::MediaConfig(), contentId.str(), false, NativeNetworkingImpl::getDefaulCryptoOptions(), audioOptions);
_threads->getNetworkThread()->BlockingCall([&]() { _threads->getNetworkThread()->BlockingCall([&]() {
_audioChannel->SetRtpTransport(rtpTransport); _audioChannel->SetRtpTransport(rtpTransport);
}); });
@ -401,10 +398,7 @@ public:
cricket::VideoOptions videoOptions; cricket::VideoOptions videoOptions;
videoOptions.is_screencast = isScreencast; videoOptions.is_screencast = isScreencast;
std::ostringstream contentId; _outgoingVideoChannel = _channelManager->CreateVideoChannel(call, cricket::MediaConfig(), std::to_string(mediaContent.ssrc), false, NativeNetworkingImpl::getDefaulCryptoOptions(), videoOptions, videoBitrateAllocatorFactory);
contentId << mediaContent.ssrc;
_outgoingVideoChannel = _channelManager->CreateVideoChannel(call, cricket::MediaConfig(), contentId.str(), false, NativeNetworkingImpl::getDefaulCryptoOptions(), videoOptions, videoBitrateAllocatorFactory);
_threads->getNetworkThread()->BlockingCall([&]() { _threads->getNetworkThread()->BlockingCall([&]() {
_outgoingVideoChannel->SetRtpTransport(rtpTransport); _outgoingVideoChannel->SetRtpTransport(rtpTransport);
}); });
@ -702,10 +696,9 @@ public:
_videoBitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory(); _videoBitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory();
std::ostringstream contentId; const auto contentId = std::to_string(mediaContent.ssrc);
contentId << mediaContent.ssrc;
_videoChannel = _channelManager->CreateVideoChannel(call, cricket::MediaConfig(), contentId.str(), false, NativeNetworkingImpl::getDefaulCryptoOptions(), cricket::VideoOptions(), _videoBitrateAllocatorFactory.get()); _videoChannel = _channelManager->CreateVideoChannel(call, cricket::MediaConfig(), contentId, false, NativeNetworkingImpl::getDefaulCryptoOptions(), cricket::VideoOptions(), _videoBitrateAllocatorFactory.get());
_threads->getNetworkThread()->BlockingCall([&]() { _threads->getNetworkThread()->BlockingCall([&]() {
_videoChannel->SetRtpTransport(rtpTransport); _videoChannel->SetRtpTransport(rtpTransport);
}); });
@ -750,7 +743,7 @@ public:
videoRecvStreamParams.ssrcs = allSsrcs; videoRecvStreamParams.ssrcs = allSsrcs;
videoRecvStreamParams.cname = "cname"; videoRecvStreamParams.cname = "cname";
videoRecvStreamParams.set_stream_ids({ contentId.str() }); videoRecvStreamParams.set_stream_ids({ contentId });
auto incomingVideoDescription = std::make_unique<cricket::VideoContentDescription>(); auto incomingVideoDescription = std::make_unique<cricket::VideoContentDescription>();
for (const auto &rtpExtension : mediaContent.rtpExtensions) { for (const auto &rtpExtension : mediaContent.rtpExtensions) {
@ -824,8 +817,8 @@ struct StateLogRecord {
struct NetworkStateLogRecord { struct NetworkStateLogRecord {
bool isConnected = false; bool isConnected = false;
bool isFailed = false; bool isFailed = false;
absl::optional<NativeNetworkingImpl::RouteDescription> route; absl::optional<InstanceNetworking::RouteDescription> route;
absl::optional<NativeNetworkingImpl::ConnectionDescription> connection; absl::optional<InstanceNetworking::ConnectionDescription> connection;
bool operator==(NetworkStateLogRecord const &rhs) const { bool operator==(NetworkStateLogRecord const &rhs) const {
if (isConnected != rhs.isConnected) { if (isConnected != rhs.isConnected) {
@ -858,23 +851,25 @@ public:
_threads(threads), _threads(threads),
_rtcServers(descriptor.rtcServers), _rtcServers(descriptor.rtcServers),
_proxy(std::move(descriptor.proxy)), _proxy(std::move(descriptor.proxy)),
_directConnectionChannel(descriptor.directConnectionChannel),
_enableP2P(descriptor.config.enableP2P), _enableP2P(descriptor.config.enableP2P),
_encryptionKey(std::move(descriptor.encryptionKey)), _encryptionKey(std::move(descriptor.encryptionKey)),
_stateUpdated(descriptor.stateUpdated), _stateUpdated(descriptor.stateUpdated),
_signalBarsUpdated(descriptor.signalBarsUpdated), _signalBarsUpdated(descriptor.signalBarsUpdated),
_audioLevelsUpdated(descriptor.audioLevelsUpdated), _audioLevelUpdated(descriptor.audioLevelsUpdated),
_remoteBatteryLevelIsLowUpdated(descriptor.remoteBatteryLevelIsLowUpdated), _remoteBatteryLevelIsLowUpdated(descriptor.remoteBatteryLevelIsLowUpdated),
_remoteMediaStateUpdated(descriptor.remoteMediaStateUpdated), _remoteMediaStateUpdated(descriptor.remoteMediaStateUpdated),
_remotePrefferedAspectRatioUpdated(descriptor.remotePrefferedAspectRatioUpdated), _remotePrefferedAspectRatioUpdated(descriptor.remotePrefferedAspectRatioUpdated),
_signalingDataEmitted(descriptor.signalingDataEmitted), _signalingDataEmitted(descriptor.signalingDataEmitted),
_createAudioDeviceModule(descriptor.createAudioDeviceModule), _createAudioDeviceModule(descriptor.createAudioDeviceModule),
_devicesConfig(descriptor.mediaDevicesConfig),
_statsLogPath(descriptor.config.statsLogPath), _statsLogPath(descriptor.config.statsLogPath),
_eventLog(std::make_unique<webrtc::RtcEventLogNull>()), _eventLog(std::make_unique<webrtc::RtcEventLogNull>()),
_taskQueueFactory(webrtc::CreateDefaultTaskQueueFactory()), _taskQueueFactory(webrtc::CreateDefaultTaskQueueFactory()),
_initialInputDeviceId(std::move(descriptor.initialInputDeviceId)), _initialInputDeviceId(std::move(descriptor.initialInputDeviceId)),
_initialOutputDeviceId(std::move(descriptor.initialOutputDeviceId)), _initialOutputDeviceId(std::move(descriptor.initialOutputDeviceId)),
_videoCapture(descriptor.videoCapture), _videoCapture(descriptor.videoCapture),
_platformContext(descriptor.platformContext) { _platformContext(descriptor.platformContext) {
webrtc::field_trial::InitFieldTrialsFromString( webrtc::field_trial::InitFieldTrialsFromString(
"WebRTC-DataChannel-Dcsctp/Enabled/" "WebRTC-DataChannel-Dcsctp/Enabled/"
"WebRTC-Audio-MinimizeResamplingOnMobile/Enabled/" "WebRTC-Audio-MinimizeResamplingOnMobile/Enabled/"
@ -901,7 +896,7 @@ public:
_contentNegotiationContext.reset(); _contentNegotiationContext.reset();
_networking->perform([](NativeNetworkingImpl *networking) { _networking->perform([](InstanceNetworking *networking) {
networking->stop(); networking->stop();
}); });
@ -957,68 +952,136 @@ public:
proxy = *(_proxy.get()); proxy = *(_proxy.get());
} }
_networking.reset(new ThreadLocalObject<NativeNetworkingImpl>(_threads->getNetworkThread(), [weak, threads = _threads, isOutgoing = _encryptionKey.isOutgoing, rtcServers = _rtcServers, proxy, enableP2P = _enableP2P]() { _networking.reset(new ThreadLocalObject<NativeNetworkingImpl>(_threads->getNetworkThread(), [weak, threads = _threads, encryptionKey = _encryptionKey, isOutgoing = _encryptionKey.isOutgoing, rtcServers = _rtcServers, proxy, enableP2P = _enableP2P, directConnectionChannel = _directConnectionChannel]() {
return new NativeNetworkingImpl(NativeNetworkingImpl::Configuration{ if (directConnectionChannel) {
.isOutgoing = isOutgoing, return new NativeNetworkingImpl(InstanceNetworking::Configuration {
.enableStunMarking = false, .encryptionKey = encryptionKey,
.enableTCP = false, .isOutgoing = isOutgoing,
.enableP2P = enableP2P, .enableStunMarking = false,
.rtcServers = rtcServers, .enableTCP = false,
.proxy = proxy, .enableP2P = enableP2P,
.stateUpdated = [threads, weak](const NativeNetworkingImpl::State &state) { .rtcServers = rtcServers,
threads->getMediaThread()->PostTask([=] { .proxy = proxy,
const auto strong = weak.lock(); .stateUpdated = [threads, weak](const InstanceNetworking::State &state) {
if (!strong) { threads->getMediaThread()->PostTask([=] {
return; const auto strong = weak.lock();
} if (!strong) {
strong->onNetworkStateUpdated(state); return;
}); }
}, strong->onNetworkStateUpdated(state);
.candidateGathered = [threads, weak](const cricket::Candidate &candidate) { });
threads->getMediaThread()->PostTask([=] { },
const auto strong = weak.lock(); .candidateGathered = [threads, weak](const cricket::Candidate &candidate) {
if (!strong) { threads->getMediaThread()->PostTask([=] {
return; const auto strong = weak.lock();
} if (!strong) {
return;
}
strong->sendCandidate(candidate); strong->sendCandidate(candidate);
}); });
}, },
.transportMessageReceived = [threads, weak](rtc::CopyOnWriteBuffer const &packet, bool isMissing) { .transportMessageReceived = [threads, weak](rtc::CopyOnWriteBuffer const &packet, bool isMissing) {
threads->getMediaThread()->PostTask([=] { threads->getMediaThread()->PostTask([=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
});
},
.rtcpPacketReceived = [threads, weak](rtc::CopyOnWriteBuffer const &packet, int64_t timestamp) {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->_call->Receiver()->DeliverPacket(webrtc::MediaType::ANY, packet, timestamp);
},
.dataChannelStateUpdated = [threads, weak](bool isDataChannelOpen) {
threads->getMediaThread()->PostTask([=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->onDataChannelStateUpdated(isDataChannelOpen);
});
},
.dataChannelMessageReceived = [threads, weak](std::string const &message) {
threads->getMediaThread()->PostTask([=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->onDataChannelMessage(message);
});
},
.threads = threads,
.directConnectionChannel = directConnectionChannel,
});
} else {
return new NativeNetworkingImpl(InstanceNetworking::Configuration{
.encryptionKey = encryptionKey,
.isOutgoing = isOutgoing,
.enableStunMarking = false,
.enableTCP = false,
.enableP2P = enableP2P,
.rtcServers = rtcServers,
.proxy = proxy,
.stateUpdated = [threads, weak](const InstanceNetworking::State &state) {
threads->getMediaThread()->PostTask([=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->onNetworkStateUpdated(state);
});
},
.candidateGathered = [threads, weak](const cricket::Candidate &candidate) {
threads->getMediaThread()->PostTask([=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
strong->sendCandidate(candidate);
});
},
.transportMessageReceived = [threads, weak](rtc::CopyOnWriteBuffer const &packet, bool isMissing) {
threads->getMediaThread()->PostTask([=] {
const auto strong = weak.lock();
if (!strong) {
return;
}
});
},
.rtcpPacketReceived = [threads, weak](rtc::CopyOnWriteBuffer const &packet, int64_t timestamp) {
const auto strong = weak.lock(); const auto strong = weak.lock();
if (!strong) { if (!strong) {
return; return;
} }
}); strong->_call->Receiver()->DeliverPacket(webrtc::MediaType::ANY, packet, timestamp);
}, },
.rtcpPacketReceived = [threads, weak](rtc::CopyOnWriteBuffer const &packet, int64_t timestamp) { .dataChannelStateUpdated = [threads, weak](bool isDataChannelOpen) {
const auto strong = weak.lock(); threads->getMediaThread()->PostTask([=] {
if (!strong) { const auto strong = weak.lock();
return; if (!strong) {
} return;
strong->_call->Receiver()->DeliverPacket(webrtc::MediaType::ANY, packet, timestamp); }
}, strong->onDataChannelStateUpdated(isDataChannelOpen);
.dataChannelStateUpdated = [threads, weak](bool isDataChannelOpen) { });
threads->getMediaThread()->PostTask([=] { },
const auto strong = weak.lock(); .dataChannelMessageReceived = [threads, weak](std::string const &message) {
if (!strong) { threads->getMediaThread()->PostTask([=] {
return; const auto strong = weak.lock();
} if (!strong) {
strong->onDataChannelStateUpdated(isDataChannelOpen); return;
}); }
}, strong->onDataChannelMessage(message);
.dataChannelMessageReceived = [threads, weak](std::string const &message) { });
threads->getMediaThread()->PostTask([=] { },
const auto strong = weak.lock(); .threads = threads,
if (!strong) { .directConnectionChannel = directConnectionChannel,
return; });
} }
strong->onDataChannelMessage(message);
});
},
.threads = threads
});
})); }));
PlatformInterface::SharedInstance()->configurePlatformAudio(); PlatformInterface::SharedInstance()->configurePlatformAudio();
@ -1033,12 +1096,13 @@ public:
mediaDeps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory<webrtc::AudioEncoderOpus, webrtc::AudioEncoderL16>(); mediaDeps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory<webrtc::AudioEncoderOpus, webrtc::AudioEncoderL16>();
mediaDeps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory<webrtc::AudioDecoderOpus, webrtc::AudioDecoderL16>(); mediaDeps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory<webrtc::AudioDecoderOpus, webrtc::AudioDecoderL16>();
mediaDeps.video_encoder_factory = PlatformInterface::SharedInstance()->makeVideoEncoderFactory(_platformContext, true); mediaDeps.video_encoder_factory = PlatformInterface::SharedInstance()->makeVideoEncoderFactory(_platformContext, true);
mediaDeps.video_decoder_factory = PlatformInterface::SharedInstance()->makeVideoDecoderFactory(_platformContext); mediaDeps.video_decoder_factory = PlatformInterface::SharedInstance()->makeVideoDecoderFactory(_platformContext);
mediaDeps.adm = _audioDeviceModule; mediaDeps.adm = _audioDeviceModule;
webrtc:: AudioProcessingBuilder builder;
mediaDeps.audio_processing = builder.Create();
_availableVideoFormats = mediaDeps.video_encoder_factory->GetSupportedFormats(); _availableVideoFormats = mediaDeps.video_encoder_factory->GetSupportedFormats();
@ -1061,6 +1125,9 @@ public:
_threads->getWorkerThread()->BlockingCall([&]() { _threads->getWorkerThread()->BlockingCall([&]() {
callConfig.audio_state = _channelManager->media_engine()->voice().GetAudioState(); callConfig.audio_state = _channelManager->media_engine()->voice().GetAudioState();
_call.reset(webrtc::Call::Create(callConfig)); _call.reset(webrtc::Call::Create(callConfig));
// SetAudioInputDeviceById(_audioDeviceModule.get(), _devicesConfig.audioInputId);
// SetAudioOutputDeviceById(_audioDeviceModule.get(), _devicesConfig.audioOutputId);
}); });
_uniqueRandomIdGenerator.reset(new rtc::UniqueRandomIdGenerator()); _uniqueRandomIdGenerator.reset(new rtc::UniqueRandomIdGenerator());
@ -1072,7 +1139,7 @@ public:
_videoBitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory(); _videoBitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory();
_networking->perform([](NativeNetworkingImpl *networking) { _networking->perform([](InstanceNetworking *networking) {
networking->start(); networking->start();
}); });
@ -1087,7 +1154,7 @@ public:
beginQualityTimer(0); beginQualityTimer(0);
beginLogTimer(0); beginLogTimer(0);
NativeNetworkingImpl::State initialNetworkState; InstanceNetworking::State initialNetworkState;
initialNetworkState.isReadyToSendData = false; initialNetworkState.isReadyToSendData = false;
onNetworkStateUpdated(initialNetworkState); onNetworkStateUpdated(initialNetworkState);
} }
@ -1287,6 +1354,7 @@ public:
outgoingAudioContent.value(), outgoingAudioContent.value(),
_threads _threads
)); ));
_outgoingAudioChannel->setIsMuted(_isMicrophoneMuted);
} }
} }
} }
@ -1481,10 +1549,16 @@ public:
void sendInitialSetup() { void sendInitialSetup() {
const auto weak = std::weak_ptr<InstanceV2ImplInternal>(shared_from_this()); const auto weak = std::weak_ptr<InstanceV2ImplInternal>(shared_from_this());
_networking->perform([weak, threads = _threads, isOutgoing = _encryptionKey.isOutgoing](NativeNetworkingImpl *networking) { _networking->perform([weak, threads = _threads, isOutgoing = _encryptionKey.isOutgoing](InstanceNetworking *networking) {
auto localFingerprint = networking->getLocalFingerprint(); auto localFingerprint = networking->getLocalFingerprint();
std::string hash = localFingerprint->algorithm; std::string hash;
std::string fingerprint = localFingerprint->GetRfc4572Fingerprint(); std::string fingerprint;
if (localFingerprint) {
hash = localFingerprint->algorithm;
fingerprint = localFingerprint->GetRfc4572Fingerprint();
}
std::string setup; std::string setup;
if (isOutgoing) { if (isOutgoing) {
setup = "actpass"; setup = "actpass";
@ -1497,7 +1571,7 @@ public:
std::string pwd = localIceParams.pwd; std::string pwd = localIceParams.pwd;
bool supportsRenomination = localIceParams.supportsRenomination; bool supportsRenomination = localIceParams.supportsRenomination;
threads->getMediaThread()->PostTask([weak, ufrag, pwd, supportsRenomination, hash, fingerprint, setup, localIceParams]() { threads->getMediaThread()->PostTask([weak, ufrag, pwd, supportsRenomination, hash, fingerprint, setup]() {
const auto strong = weak.lock(); const auto strong = weak.lock();
if (!strong) { if (!strong) {
return; return;
@ -1614,7 +1688,7 @@ public:
sslSetup = initialSetup->fingerprints[0].setup; sslSetup = initialSetup->fingerprints[0].setup;
} }
_networking->perform([threads = _threads, remoteIceParameters = std::move(remoteIceParameters), fingerprint = std::move(fingerprint), sslSetup = std::move(sslSetup)](NativeNetworkingImpl *networking) { _networking->perform([threads = _threads, remoteIceParameters = std::move(remoteIceParameters), fingerprint = std::move(fingerprint), sslSetup = std::move(sslSetup)](InstanceNetworking *networking) {
networking->setRemoteParams(remoteIceParameters, fingerprint.get(), sslSetup); networking->setRemoteParams(remoteIceParameters, fingerprint.get(), sslSetup);
}); });
@ -1726,13 +1800,13 @@ public:
if (_pendingIceCandidates.size() == 0) { if (_pendingIceCandidates.size() == 0) {
return; return;
} }
_networking->perform([threads = _threads, parsedCandidates = _pendingIceCandidates](NativeNetworkingImpl *networking) { _networking->perform([threads = _threads, parsedCandidates = _pendingIceCandidates](InstanceNetworking *networking) {
networking->addCandidates(parsedCandidates); networking->addCandidates(parsedCandidates);
}); });
_pendingIceCandidates.clear(); _pendingIceCandidates.clear();
} }
void onNetworkStateUpdated(NativeNetworkingImpl::State const &state) { void onNetworkStateUpdated(InstanceNetworking::State const &state) {
State mappedState; State mappedState;
if (state.isFailed) { if (state.isFailed) {
mappedState = State::Failed; mappedState = State::Failed;
@ -1775,7 +1849,7 @@ public:
auto data = message.serialize(); auto data = message.serialize();
std::string stringData(data.begin(), data.end()); std::string stringData(data.begin(), data.end());
RTC_LOG(LS_INFO) << "sendDataChannelMessage: " << stringData; RTC_LOG(LS_INFO) << "sendDataChannelMessage: " << stringData;
_networking->perform([stringData = std::move(stringData)](NativeNetworkingImpl *networking) { _networking->perform([stringData = std::move(stringData)](InstanceNetworking *networking) {
networking->sendDataChannelMessage(stringData); networking->sendDataChannelMessage(stringData);
}); });
} }
@ -1932,7 +2006,7 @@ public:
} }
} }
void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) { void setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
_currentSink = sink; _currentSink = sink;
if (_incomingVideoChannel) { if (_incomingVideoChannel) {
_incomingVideoChannel->addSink(sink); _incomingVideoChannel->addSink(sink);
@ -1978,14 +2052,10 @@ public:
for (const auto &record : _networkStateLogRecords) { for (const auto &record : _networkStateLogRecords) {
json11::Json::object jsonRecord; json11::Json::object jsonRecord;
std::ostringstream timestampString;
if (baseTimestamp == 0) { if (baseTimestamp == 0) {
baseTimestamp = record.timestamp; baseTimestamp = record.timestamp;
} }
timestampString << (record.timestamp - baseTimestamp); jsonRecord.insert(std::make_pair("t", json11::Json(std::to_string(record.timestamp - baseTimestamp))));
jsonRecord.insert(std::make_pair("t", json11::Json(timestampString.str())));
jsonRecord.insert(std::make_pair("c", json11::Json(record.record.isConnected ? 1 : 0))); jsonRecord.insert(std::make_pair("c", json11::Json(record.record.isConnected ? 1 : 0)));
if (record.record.route) { if (record.record.route) {
jsonRecord.insert(std::make_pair("local", json11::Json(record.record.route->localDescription))); jsonRecord.insert(std::make_pair("local", json11::Json(record.record.route->localDescription)));
@ -1994,7 +2064,7 @@ public:
if (record.record.connection) { if (record.record.connection) {
json11::Json::object jsonConnection; json11::Json::object jsonConnection;
auto serializeCandidate = [](NativeNetworkingImpl::ConnectionDescription::CandidateDescription const &candidate) -> json11::Json::object { auto serializeCandidate = [](InstanceNetworking::ConnectionDescription::CandidateDescription const &candidate) -> json11::Json::object {
json11::Json::object jsonCandidate; json11::Json::object jsonCandidate;
jsonCandidate.insert(std::make_pair("type", json11::Json(candidate.type))); jsonCandidate.insert(std::make_pair("type", json11::Json(candidate.type)));
@ -2077,16 +2147,18 @@ private:
std::shared_ptr<Threads> _threads; std::shared_ptr<Threads> _threads;
std::vector<RtcServer> _rtcServers; std::vector<RtcServer> _rtcServers;
std::unique_ptr<Proxy> _proxy; std::unique_ptr<Proxy> _proxy;
std::shared_ptr<DirectConnectionChannel> _directConnectionChannel;
bool _enableP2P = false; bool _enableP2P = false;
EncryptionKey _encryptionKey; EncryptionKey _encryptionKey;
std::function<void(State)> _stateUpdated; std::function<void(State)> _stateUpdated;
std::function<void(int)> _signalBarsUpdated; std::function<void(int)> _signalBarsUpdated;
std::function<void(float, float)> _audioLevelsUpdated; std::function<void(float, float )> _audioLevelUpdated;
std::function<void(bool)> _remoteBatteryLevelIsLowUpdated; std::function<void(bool)> _remoteBatteryLevelIsLowUpdated;
std::function<void(AudioState, VideoState)> _remoteMediaStateUpdated; std::function<void(AudioState, VideoState)> _remoteMediaStateUpdated;
std::function<void(float)> _remotePrefferedAspectRatioUpdated; std::function<void(float)> _remotePrefferedAspectRatioUpdated;
std::function<void(const std::vector<uint8_t> &)> _signalingDataEmitted; std::function<void(const std::vector<uint8_t> &)> _signalingDataEmitted;
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule; std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule;
MediaDevicesConfig _devicesConfig;
FilePath _statsLogPath; FilePath _statsLogPath;
std::unique_ptr<SignalingConnection> _signalingConnection; std::unique_ptr<SignalingConnection> _signalingConnection;
@ -2098,7 +2170,7 @@ private:
std::vector<StateLogRecord<NetworkStateLogRecord>> _networkStateLogRecords; std::vector<StateLogRecord<NetworkStateLogRecord>> _networkStateLogRecords;
std::vector<StateLogRecord<NetworkBitrateLogRecord>> _networkBitrateLogRecords; std::vector<StateLogRecord<NetworkBitrateLogRecord>> _networkBitrateLogRecords;
absl::optional<NativeNetworkingImpl::State> _networkState; absl::optional<InstanceNetworking::State> _networkState;
bool _handshakeCompleted = false; bool _handshakeCompleted = false;
std::vector<cricket::Candidate> _pendingIceCandidates; std::vector<cricket::Candidate> _pendingIceCandidates;
@ -2202,7 +2274,7 @@ void InstanceV2Impl::setMuteMicrophone(bool muteMicrophone) {
}); });
} }
void InstanceV2Impl::setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) { void InstanceV2Impl::setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
_internal->perform([sink](InstanceV2ImplInternal *internal) { _internal->perform([sink](InstanceV2ImplInternal *internal) {
internal->setIncomingVideoOutput(sink); internal->setIncomingVideoOutput(sink);
}); });

View file

@ -27,7 +27,7 @@ public:
bool supportsVideo() override { bool supportsVideo() override {
return true; return true;
} }
void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override; void setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override;
void setAudioOutputGainControlEnabled(bool enabled) override; void setAudioOutputGainControlEnabled(bool enabled) override;
void setEchoCancellationStrength(int strength) override; void setEchoCancellationStrength(int strength) override;
void setAudioInputDevice(std::string id) override; void setAudioInputDevice(std::string id) override;

View file

@ -325,8 +325,8 @@ struct StateLogRecord {
struct NetworkStateLogRecord { struct NetworkStateLogRecord {
bool isConnected = false; bool isConnected = false;
bool isFailed = false; bool isFailed = false;
absl::optional<NativeNetworkingImpl::RouteDescription> route; absl::optional<InstanceNetworking::RouteDescription> route;
absl::optional<NativeNetworkingImpl::ConnectionDescription> connection; absl::optional<InstanceNetworking::ConnectionDescription> connection;
bool operator==(NetworkStateLogRecord const &rhs) const { bool operator==(NetworkStateLogRecord const &rhs) const {
if (isConnected != rhs.isConnected) { if (isConnected != rhs.isConnected) {
@ -363,7 +363,7 @@ public:
_encryptionKey(std::move(descriptor.encryptionKey)), _encryptionKey(std::move(descriptor.encryptionKey)),
_stateUpdated(descriptor.stateUpdated), _stateUpdated(descriptor.stateUpdated),
_signalBarsUpdated(descriptor.signalBarsUpdated), _signalBarsUpdated(descriptor.signalBarsUpdated),
_audioLevelsUpdated(descriptor.audioLevelsUpdated), _audioLevelUpdated(descriptor.audioLevelsUpdated),
_remoteBatteryLevelIsLowUpdated(descriptor.remoteBatteryLevelIsLowUpdated), _remoteBatteryLevelIsLowUpdated(descriptor.remoteBatteryLevelIsLowUpdated),
_remoteMediaStateUpdated(descriptor.remoteMediaStateUpdated), _remoteMediaStateUpdated(descriptor.remoteMediaStateUpdated),
_remotePrefferedAspectRatioUpdated(descriptor.remotePrefferedAspectRatioUpdated), _remotePrefferedAspectRatioUpdated(descriptor.remotePrefferedAspectRatioUpdated),
@ -373,7 +373,7 @@ public:
_eventLog(std::make_unique<webrtc::RtcEventLogNull>()), _eventLog(std::make_unique<webrtc::RtcEventLogNull>()),
_taskQueueFactory(webrtc::CreateDefaultTaskQueueFactory()), _taskQueueFactory(webrtc::CreateDefaultTaskQueueFactory()),
_videoCapture(descriptor.videoCapture), _videoCapture(descriptor.videoCapture),
_platformContext(descriptor.platformContext) { _platformContext(descriptor.platformContext) {
webrtc::field_trial::InitFieldTrialsFromString( webrtc::field_trial::InitFieldTrialsFromString(
"WebRTC-DataChannel-Dcsctp/Enabled/" "WebRTC-DataChannel-Dcsctp/Enabled/"
"WebRTC-Audio-iOS-Holding/Enabled/" "WebRTC-Audio-iOS-Holding/Enabled/"
@ -459,6 +459,11 @@ public:
cricket::MediaEngineDependencies mediaDeps; cricket::MediaEngineDependencies mediaDeps;
mediaDeps.adm = _audioDeviceModule; mediaDeps.adm = _audioDeviceModule;
webrtc:: AudioProcessingBuilder builder;
mediaDeps.audio_processing = builder.Create();
mediaDeps.task_queue_factory = peerConnectionFactoryDependencies.task_queue_factory.get(); mediaDeps.task_queue_factory = peerConnectionFactoryDependencies.task_queue_factory.get();
mediaDeps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory<webrtc::AudioEncoderOpus>(); mediaDeps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory<webrtc::AudioEncoderOpus>();
mediaDeps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory<webrtc::AudioDecoderOpus>(); mediaDeps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory<webrtc::AudioDecoderOpus>();
@ -607,10 +612,10 @@ public:
return; return;
} }
NativeNetworkingImpl::ConnectionDescription connectionDescription; InstanceNetworking::ConnectionDescription connectionDescription;
connectionDescription.local = NativeNetworkingImpl::connectionDescriptionFromCandidate(event.selected_candidate_pair.local); connectionDescription.local = InstanceNetworking::connectionDescriptionFromCandidate(event.selected_candidate_pair.local);
connectionDescription.remote = NativeNetworkingImpl::connectionDescriptionFromCandidate(event.selected_candidate_pair.remote); connectionDescription.remote = InstanceNetworking::connectionDescriptionFromCandidate(event.selected_candidate_pair.remote);
if (!strong->_currentConnectionDescription || strong->_currentConnectionDescription.value() != connectionDescription) { if (!strong->_currentConnectionDescription || strong->_currentConnectionDescription.value() != connectionDescription) {
strong->_currentConnectionDescription = std::move(connectionDescription); strong->_currentConnectionDescription = std::move(connectionDescription);
@ -649,10 +654,8 @@ public:
if (server.isTurn) { if (server.isTurn) {
webrtc::PeerConnectionInterface::IceServer mappedServer; webrtc::PeerConnectionInterface::IceServer mappedServer;
std::ostringstream uri; mappedServer.urls.push_back(
uri << "turn:" << address.HostAsURIString() << ":" << server.port; "turn:" + address.HostAsURIString() + ":" + std::to_string(server.port));
mappedServer.urls.push_back(uri.str());
mappedServer.username = server.login; mappedServer.username = server.login;
mappedServer.password = server.password; mappedServer.password = server.password;
@ -660,10 +663,8 @@ public:
} else { } else {
webrtc::PeerConnectionInterface::IceServer mappedServer; webrtc::PeerConnectionInterface::IceServer mappedServer;
std::ostringstream uri; mappedServer.urls.push_back(
uri << "stun:" << address.HostAsURIString() << ":" << server.port; "stun:" + address.HostAsURIString() + ":" + std::to_string(server.port));
mappedServer.urls.push_back(uri.str());
peerConnectionConfiguration.servers.push_back(mappedServer); peerConnectionConfiguration.servers.push_back(mappedServer);
} }
@ -704,7 +705,7 @@ public:
parameters.encodings[0].max_bitrate_bps = 32 * 1024; parameters.encodings[0].max_bitrate_bps = 32 * 1024;
_outgoingAudioTransceiver->sender()->SetParameters(parameters); _outgoingAudioTransceiver->sender()->SetParameters(parameters);
_outgoingAudioTrack->set_enabled(true); _outgoingAudioTrack->set_enabled(!_isMicrophoneMuted);
} }
} }
@ -1451,14 +1452,10 @@ public:
for (const auto &record : _networkStateLogRecords) { for (const auto &record : _networkStateLogRecords) {
json11::Json::object jsonRecord; json11::Json::object jsonRecord;
std::ostringstream timestampString;
if (baseTimestamp == 0) { if (baseTimestamp == 0) {
baseTimestamp = record.timestamp; baseTimestamp = record.timestamp;
} }
timestampString << (record.timestamp - baseTimestamp); jsonRecord.insert(std::make_pair("t", json11::Json(std::to_string(record.timestamp - baseTimestamp))));
jsonRecord.insert(std::make_pair("t", json11::Json(timestampString.str())));
jsonRecord.insert(std::make_pair("c", json11::Json(record.record.isConnected ? 1 : 0))); jsonRecord.insert(std::make_pair("c", json11::Json(record.record.isConnected ? 1 : 0)));
if (record.record.route) { if (record.record.route) {
jsonRecord.insert(std::make_pair("local", json11::Json(record.record.route->localDescription))); jsonRecord.insert(std::make_pair("local", json11::Json(record.record.route->localDescription)));
@ -1467,7 +1464,7 @@ public:
if (record.record.connection) { if (record.record.connection) {
json11::Json::object jsonConnection; json11::Json::object jsonConnection;
auto serializeCandidate = [](NativeNetworkingImpl::ConnectionDescription::CandidateDescription const &candidate) -> json11::Json::object { auto serializeCandidate = [](InstanceNetworking::ConnectionDescription::CandidateDescription const &candidate) -> json11::Json::object {
json11::Json::object jsonCandidate; json11::Json::object jsonCandidate;
jsonCandidate.insert(std::make_pair("type", json11::Json(candidate.type))); jsonCandidate.insert(std::make_pair("type", json11::Json(candidate.type)));
@ -1554,7 +1551,7 @@ private:
EncryptionKey _encryptionKey; EncryptionKey _encryptionKey;
std::function<void(State)> _stateUpdated; std::function<void(State)> _stateUpdated;
std::function<void(int)> _signalBarsUpdated; std::function<void(int)> _signalBarsUpdated;
std::function<void(float, float)> _audioLevelsUpdated; std::function<void(float, float )> _audioLevelUpdated;
std::function<void(bool)> _remoteBatteryLevelIsLowUpdated; std::function<void(bool)> _remoteBatteryLevelIsLowUpdated;
std::function<void(AudioState, VideoState)> _remoteMediaStateUpdated; std::function<void(AudioState, VideoState)> _remoteMediaStateUpdated;
std::function<void(float)> _remotePrefferedAspectRatioUpdated; std::function<void(float)> _remotePrefferedAspectRatioUpdated;
@ -1567,7 +1564,7 @@ private:
bool _isConnected = false; bool _isConnected = false;
bool _isFailed = false; bool _isFailed = false;
absl::optional<NativeNetworkingImpl::ConnectionDescription> _currentConnectionDescription; absl::optional<InstanceNetworking::ConnectionDescription> _currentConnectionDescription;
absl::optional<NetworkStateLogRecord> _currentNetworkStateLogRecord; absl::optional<NetworkStateLogRecord> _currentNetworkStateLogRecord;
std::vector<StateLogRecord<NetworkStateLogRecord>> _networkStateLogRecords; std::vector<StateLogRecord<NetworkStateLogRecord>> _networkStateLogRecords;
@ -1665,7 +1662,7 @@ void InstanceV2ReferenceImpl::setMuteMicrophone(bool muteMicrophone) {
}); });
} }
void InstanceV2ReferenceImpl::setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) { void InstanceV2ReferenceImpl::setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
_internal->perform([sink](InstanceV2ReferenceImplInternal *internal) { _internal->perform([sink](InstanceV2ReferenceImplInternal *internal) {
internal->setIncomingVideoOutput(sink); internal->setIncomingVideoOutput(sink);
}); });

View file

@ -27,7 +27,7 @@ public:
bool supportsVideo() override { bool supportsVideo() override {
return true; return true;
} }
void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override; void setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override;
void setAudioOutputGainControlEnabled(bool enabled) override; void setAudioOutputGainControlEnabled(bool enabled) override;
void setEchoCancellationStrength(int strength) override; void setEchoCancellationStrength(int strength) override;
void setAudioInputDevice(std::string id) override; void setAudioInputDevice(std::string id) override;

View file

@ -186,8 +186,8 @@ private:
} }
NativeNetworkingImpl::ConnectionDescription::CandidateDescription NativeNetworkingImpl::connectionDescriptionFromCandidate(cricket::Candidate const &candidate) { InstanceNetworking::ConnectionDescription::CandidateDescription InstanceNetworking::connectionDescriptionFromCandidate(cricket::Candidate const &candidate) {
NativeNetworkingImpl::ConnectionDescription::CandidateDescription result; InstanceNetworking::ConnectionDescription::CandidateDescription result;
result.type = candidate.type(); result.type = candidate.type();
result.protocol = candidate.protocol(); result.protocol = candidate.protocol();
@ -569,8 +569,8 @@ void NativeNetworkingImpl::transportRouteChanged(absl::optional<rtc::NetworkRout
void NativeNetworkingImpl::candidatePairChanged(cricket::CandidatePairChangeEvent const &event) { void NativeNetworkingImpl::candidatePairChanged(cricket::CandidatePairChangeEvent const &event) {
ConnectionDescription connectionDescription; ConnectionDescription connectionDescription;
connectionDescription.local = connectionDescriptionFromCandidate(event.selected_candidate_pair.local); connectionDescription.local = InstanceNetworking::connectionDescriptionFromCandidate(event.selected_candidate_pair.local);
connectionDescription.remote = connectionDescriptionFromCandidate(event.selected_candidate_pair.remote); connectionDescription.remote = InstanceNetworking::connectionDescriptionFromCandidate(event.selected_candidate_pair.remote);
if (!_currentConnectionDescription || _currentConnectionDescription.value() != connectionDescription) { if (!_currentConnectionDescription || _currentConnectionDescription.value() != connectionDescription) {
_currentConnectionDescription = std::move(connectionDescription); _currentConnectionDescription = std::move(connectionDescription);

View file

@ -18,6 +18,7 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include "InstanceNetworking.h"
#include "Message.h" #include "Message.h"
#include "ThreadLocalObject.h" #include "ThreadLocalObject.h"
#include "Instance.h" #include "Instance.h"
@ -50,117 +51,24 @@ struct Message;
class SctpDataChannelProviderInterfaceImpl; class SctpDataChannelProviderInterfaceImpl;
class Threads; class Threads;
class NativeNetworkingImpl : public sigslot::has_slots<>, public std::enable_shared_from_this<NativeNetworkingImpl> { class NativeNetworkingImpl : public InstanceNetworking, public sigslot::has_slots<>, public std::enable_shared_from_this<NativeNetworkingImpl> {
public: public:
struct RouteDescription {
explicit RouteDescription(std::string const &localDescription_, std::string const &remoteDescription_) :
localDescription(localDescription_),
remoteDescription(remoteDescription_) {
}
std::string localDescription;
std::string remoteDescription;
bool operator==(RouteDescription const &rhs) const {
if (localDescription != rhs.localDescription) {
return false;
}
if (remoteDescription != rhs.remoteDescription) {
return false;
}
return true;
}
bool operator!=(const RouteDescription& rhs) const {
return !(*this == rhs);
}
};
struct ConnectionDescription {
struct CandidateDescription {
std::string protocol;
std::string type;
std::string address;
bool operator==(CandidateDescription const &rhs) const {
if (protocol != rhs.protocol) {
return false;
}
if (type != rhs.type) {
return false;
}
if (address != rhs.address) {
return false;
}
return true;
}
bool operator!=(const CandidateDescription& rhs) const {
return !(*this == rhs);
}
};
CandidateDescription local;
CandidateDescription remote;
bool operator==(ConnectionDescription const &rhs) const {
if (local != rhs.local) {
return false;
}
if (remote != rhs.remote) {
return false;
}
return true;
}
bool operator!=(const ConnectionDescription& rhs) const {
return !(*this == rhs);
}
};
struct State {
bool isReadyToSendData = false;
bool isFailed = false;
absl::optional<RouteDescription> route;
absl::optional<ConnectionDescription> connection;
};
struct Configuration {
bool isOutgoing = false;
bool enableStunMarking = false;
bool enableTCP = false;
bool enableP2P = false;
std::vector<RtcServer> rtcServers;
absl::optional<Proxy> proxy;
std::function<void(const NativeNetworkingImpl::State &)> stateUpdated;
std::function<void(const cricket::Candidate &)> candidateGathered;
std::function<void(rtc::CopyOnWriteBuffer const &, bool)> transportMessageReceived;
std::function<void(rtc::CopyOnWriteBuffer const &, int64_t)> rtcpPacketReceived;
std::function<void(bool)> dataChannelStateUpdated;
std::function<void(std::string const &)> dataChannelMessageReceived;
std::shared_ptr<Threads> threads;
};
static webrtc::CryptoOptions getDefaulCryptoOptions(); static webrtc::CryptoOptions getDefaulCryptoOptions();
static ConnectionDescription::CandidateDescription connectionDescriptionFromCandidate(cricket::Candidate const &candidate);
NativeNetworkingImpl(Configuration &&configuration); NativeNetworkingImpl(Configuration &&configuration);
~NativeNetworkingImpl(); virtual ~NativeNetworkingImpl();
void start(); virtual void start() override;
void stop(); virtual void stop() override;
PeerIceParameters getLocalIceParameters(); virtual PeerIceParameters getLocalIceParameters() override;
std::unique_ptr<rtc::SSLFingerprint> getLocalFingerprint(); virtual std::unique_ptr<rtc::SSLFingerprint> getLocalFingerprint() override;
void setRemoteParams(PeerIceParameters const &remoteIceParameters, rtc::SSLFingerprint *fingerprint, std::string const &sslSetup); virtual void setRemoteParams(PeerIceParameters const &remoteIceParameters, rtc::SSLFingerprint *fingerprint, std::string const &sslSetup) override;
void addCandidates(std::vector<cricket::Candidate> const &candidates); virtual void addCandidates(std::vector<cricket::Candidate> const &candidates) override;
void sendDataChannelMessage(std::string const &message); virtual void sendDataChannelMessage(std::string const &message) override;
webrtc::RtpTransport *getRtpTransport(); virtual webrtc::RtpTransport *getRtpTransport() override;
private: private:
void resetDtlsSrtpTransport(); void resetDtlsSrtpTransport();
@ -191,7 +99,7 @@ private:
std::vector<RtcServer> _rtcServers; std::vector<RtcServer> _rtcServers;
absl::optional<Proxy> _proxy; absl::optional<Proxy> _proxy;
std::function<void(const NativeNetworkingImpl::State &)> _stateUpdated; std::function<void(const InstanceNetworking::State &)> _stateUpdated;
std::function<void(const cricket::Candidate &)> _candidateGathered; std::function<void(const cricket::Candidate &)> _candidateGathered;
std::function<void(rtc::CopyOnWriteBuffer const &, bool)> _transportMessageReceived; std::function<void(rtc::CopyOnWriteBuffer const &, bool)> _transportMessageReceived;
std::function<void(rtc::CopyOnWriteBuffer const &, int64_t)> _rtcpPacketReceived; std::function<void(rtc::CopyOnWriteBuffer const &, int64_t)> _rtcpPacketReceived;

View file

@ -11,9 +11,7 @@ namespace tgcalls {
namespace signaling { namespace signaling {
static std::string uint32ToString(uint32_t value) { static std::string uint32ToString(uint32_t value) {
std::ostringstream stringStream; return std::to_string(value);
stringStream << value;
return stringStream.str();
} }
static uint32_t stringToUInt32(std::string const &string) { static uint32_t stringToUInt32(std::string const &string) {
@ -221,7 +219,7 @@ absl::optional<PayloadType> PayloadType_parse(json11::Json::object const &object
json11::Json::object MediaContent_serialize(MediaContent const &mediaContent) { json11::Json::object MediaContent_serialize(MediaContent const &mediaContent) {
json11::Json::object object; json11::Json::object object;
std::string mappedType; std::string mappedType;
switch (mediaContent.type) { switch (mediaContent.type) {
case MediaContent::Type::Audio: { case MediaContent::Type::Audio: {
@ -268,7 +266,7 @@ json11::Json::object MediaContent_serialize(MediaContent const &mediaContent) {
absl::optional<MediaContent> MediaContent_parse(json11::Json::object const &object) { absl::optional<MediaContent> MediaContent_parse(json11::Json::object const &object) {
MediaContent result; MediaContent result;
const auto type = object.find("type"); const auto type = object.find("type");
if (type == object.end() || !type->second.is_string()) { if (type == object.end() || !type->second.is_string()) {
RTC_LOG(LS_ERROR) << "Signaling: type must be a string"; RTC_LOG(LS_ERROR) << "Signaling: type must be a string";
@ -362,7 +360,7 @@ absl::optional<MediaContent> MediaContent_parse(json11::Json::object const &obje
std::vector<uint8_t> InitialSetupMessage_serialize(const InitialSetupMessage * const message) { std::vector<uint8_t> InitialSetupMessage_serialize(const InitialSetupMessage * const message) {
json11::Json::object object; json11::Json::object object;
object.insert(std::make_pair("@type", json11::Json("InitialSetup"))); object.insert(std::make_pair("@type", json11::Json("InitialSetup")));
object.insert(std::make_pair("ufrag", json11::Json(message->ufrag))); object.insert(std::make_pair("ufrag", json11::Json(message->ufrag)));
object.insert(std::make_pair("pwd", json11::Json(message->pwd))); object.insert(std::make_pair("pwd", json11::Json(message->pwd)));
@ -425,7 +423,7 @@ absl::optional<InitialSetupMessage> InitialSetupMessage_parse(json11::Json::obje
RTC_LOG(LS_ERROR) << "Signaling: fingerprint must be a string"; RTC_LOG(LS_ERROR) << "Signaling: fingerprint must be a string";
return absl::nullopt; return absl::nullopt;
} }
DtlsFingerprint parsedFingerprint; DtlsFingerprint parsedFingerprint;
parsedFingerprint.hash = hash->second.string_value(); parsedFingerprint.hash = hash->second.string_value();
parsedFingerprint.setup = setup->second.string_value(); parsedFingerprint.setup = setup->second.string_value();
@ -445,11 +443,11 @@ absl::optional<InitialSetupMessage> InitialSetupMessage_parse(json11::Json::obje
std::vector<uint8_t> NegotiateChannelsMessage_serialize(const NegotiateChannelsMessage * const message) { std::vector<uint8_t> NegotiateChannelsMessage_serialize(const NegotiateChannelsMessage * const message) {
json11::Json::object object; json11::Json::object object;
object.insert(std::make_pair("@type", json11::Json("NegotiateChannels"))); object.insert(std::make_pair("@type", json11::Json("NegotiateChannels")));
object.insert(std::make_pair("exchangeId", json11::Json(uint32ToString(message->exchangeId)))); object.insert(std::make_pair("exchangeId", json11::Json(uint32ToString(message->exchangeId))));
json11::Json::array contents; json11::Json::array contents;
for (const auto &content : message->contents) { for (const auto &content : message->contents) {
contents.push_back(json11::Json(MediaContent_serialize(content))); contents.push_back(json11::Json(MediaContent_serialize(content)));
@ -463,9 +461,9 @@ std::vector<uint8_t> NegotiateChannelsMessage_serialize(const NegotiateChannelsM
absl::optional<NegotiateChannelsMessage> NegotiateChannelsMessage_parse(json11::Json::object const &object) { absl::optional<NegotiateChannelsMessage> NegotiateChannelsMessage_parse(json11::Json::object const &object) {
NegotiateChannelsMessage message; NegotiateChannelsMessage message;
const auto exchangeId = object.find("exchangeId"); const auto exchangeId = object.find("exchangeId");
if (exchangeId == object.end()) { if (exchangeId == object.end()) {
RTC_LOG(LS_ERROR) << "Signaling: exchangeId must be present"; RTC_LOG(LS_ERROR) << "Signaling: exchangeId must be present";
return absl::nullopt; return absl::nullopt;

View file

@ -57,9 +57,7 @@ namespace tgcalls {
namespace { namespace {
static std::string intToString(int value) { static std::string intToString(int value) {
std::ostringstream stringStream; return std::to_string(value);
stringStream << value;
return stringStream.str();
} }
static VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(VideoCaptureInterface *videoCapture) { static VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(VideoCaptureInterface *videoCapture) {
@ -1214,7 +1212,7 @@ public:
_encryptionKey(std::move(descriptor.encryptionKey)), _encryptionKey(std::move(descriptor.encryptionKey)),
_stateUpdated(descriptor.stateUpdated), _stateUpdated(descriptor.stateUpdated),
_signalBarsUpdated(descriptor.signalBarsUpdated), _signalBarsUpdated(descriptor.signalBarsUpdated),
_audioLevelsUpdated(descriptor.audioLevelsUpdated), _audioLevelUpdated(descriptor.audioLevelsUpdated),
_remoteBatteryLevelIsLowUpdated(descriptor.remoteBatteryLevelIsLowUpdated), _remoteBatteryLevelIsLowUpdated(descriptor.remoteBatteryLevelIsLowUpdated),
_remoteMediaStateUpdated(descriptor.remoteMediaStateUpdated), _remoteMediaStateUpdated(descriptor.remoteMediaStateUpdated),
_remotePrefferedAspectRatioUpdated(descriptor.remotePrefferedAspectRatioUpdated), _remotePrefferedAspectRatioUpdated(descriptor.remotePrefferedAspectRatioUpdated),
@ -1253,14 +1251,15 @@ public:
void start() { void start() {
const auto weak = std::weak_ptr<InstanceV2_4_0_0ImplInternal>(shared_from_this()); const auto weak = std::weak_ptr<InstanceV2_4_0_0ImplInternal>(shared_from_this());
_networking.reset(new ThreadLocalObject<NativeNetworkingImpl>(_threads->getNetworkThread(), [weak, threads = _threads, isOutgoing = _encryptionKey.isOutgoing, rtcServers = _rtcServers, enableP2P = _enableP2P]() { _networking.reset(new ThreadLocalObject<NativeNetworkingImpl>(_threads->getNetworkThread(), [weak, threads = _threads, encryptionKey = _encryptionKey, isOutgoing = _encryptionKey.isOutgoing, rtcServers = _rtcServers, enableP2P = _enableP2P]() {
return new NativeNetworkingImpl(NativeNetworkingImpl::Configuration{ return new NativeNetworkingImpl(InstanceNetworking::Configuration{
.encryptionKey = encryptionKey,
.isOutgoing = isOutgoing, .isOutgoing = isOutgoing,
.enableStunMarking = false, .enableStunMarking = false,
.enableTCP = false, .enableTCP = false,
.enableP2P = enableP2P, .enableP2P = enableP2P,
.rtcServers = rtcServers, .rtcServers = rtcServers,
.stateUpdated = [threads, weak](const NativeNetworkingImpl::State &state) { .stateUpdated = [threads, weak](const InstanceNetworking::State &state) {
threads->getMediaThread()->PostTask([=] { threads->getMediaThread()->PostTask([=] {
const auto strong = weak.lock(); const auto strong = weak.lock();
if (!strong) { if (!strong) {
@ -1335,6 +1334,10 @@ public:
mediaDeps.adm = _audioDeviceModule; mediaDeps.adm = _audioDeviceModule;
webrtc:: AudioProcessingBuilder builder;
mediaDeps.audio_processing = builder.Create();
_availableVideoFormats = mediaDeps.video_encoder_factory->GetSupportedFormats(); _availableVideoFormats = mediaDeps.video_encoder_factory->GetSupportedFormats();
std::unique_ptr<cricket::MediaEngineInterface> mediaEngine = cricket::CreateMediaEngine(std::move(mediaDeps)); std::unique_ptr<cricket::MediaEngineInterface> mediaEngine = cricket::CreateMediaEngine(std::move(mediaDeps));
@ -1474,6 +1477,7 @@ public:
_negotiatedOutgoingAudioContent.value(), _negotiatedOutgoingAudioContent.value(),
_threads _threads
)); ));
_outgoingAudioChannel->setIsMuted(_isMicrophoneMuted);
} }
adjustBitratePreferences(true); adjustBitratePreferences(true);
@ -1838,7 +1842,7 @@ public:
_pendingIceCandidates.clear(); _pendingIceCandidates.clear();
} }
void onNetworkStateUpdated(NativeNetworkingImpl::State const &state) { void onNetworkStateUpdated(InstanceNetworking::State const &state) {
State mappedState; State mappedState;
if (state.isReadyToSendData) { if (state.isReadyToSendData) {
mappedState = State::Established; mappedState = State::Established;
@ -2078,7 +2082,7 @@ private:
EncryptionKey _encryptionKey; EncryptionKey _encryptionKey;
std::function<void(State)> _stateUpdated; std::function<void(State)> _stateUpdated;
std::function<void(int)> _signalBarsUpdated; std::function<void(int)> _signalBarsUpdated;
std::function<void(float, float)> _audioLevelsUpdated; std::function<void(float, float)> _audioLevelUpdated;
std::function<void(bool)> _remoteBatteryLevelIsLowUpdated; std::function<void(bool)> _remoteBatteryLevelIsLowUpdated;
std::function<void(AudioState, VideoState)> _remoteMediaStateUpdated; std::function<void(AudioState, VideoState)> _remoteMediaStateUpdated;
std::function<void(float)> _remotePrefferedAspectRatioUpdated; std::function<void(float)> _remotePrefferedAspectRatioUpdated;
@ -2187,7 +2191,7 @@ void InstanceV2_4_0_0Impl::setMuteMicrophone(bool muteMicrophone) {
}); });
} }
void InstanceV2_4_0_0Impl::setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) { void InstanceV2_4_0_0Impl::setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
_internal->perform([sink](InstanceV2_4_0_0ImplInternal *internal) { _internal->perform([sink](InstanceV2_4_0_0ImplInternal *internal) {
internal->setIncomingVideoOutput(sink); internal->setIncomingVideoOutput(sink);
}); });

View file

@ -27,7 +27,7 @@ public:
bool supportsVideo() override { bool supportsVideo() override {
return true; return true;
} }
void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override; void setIncomingVideoOutput(std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) override;
void setAudioOutputGainControlEnabled(bool enabled) override; void setAudioOutputGainControlEnabled(bool enabled) override;
void setEchoCancellationStrength(int strength) override; void setEchoCancellationStrength(int strength) override;
void setAudioInputDevice(std::string id) override; void setAudioInputDevice(std::string id) override;

View file

@ -10,9 +10,7 @@ namespace tgcalls {
namespace signaling_4_0_0 { namespace signaling_4_0_0 {
static std::string uint32ToString(uint32_t value) { static std::string uint32ToString(uint32_t value) {
std::ostringstream stringStream; return std::to_string(value);
stringStream << value;
return stringStream.str();
} }
static uint32_t stringToUInt32(std::string const &string) { static uint32_t stringToUInt32(std::string const &string) {

View file

@ -1,3 +1,4 @@
chat_BlurAlpha=-1056964608
chat_unreadMessagesStartText=-14512671 chat_unreadMessagesStartText=-14512671
chat_inFileBackgroundSelected=-2823172 chat_inFileBackgroundSelected=-2823172
radioBackgroundChecked=-14638337 radioBackgroundChecked=-14638337
@ -74,6 +75,7 @@ chat_inBubbleShadow=1981626195
chat_outAudioProgress=-10907938 chat_outAudioProgress=-10907938
player_progress=-14441474 player_progress=-14441474
chat_inReplyLine=-12478487 chat_inReplyLine=-12478487
chat_inQuote=-12478487
dialogLineProgressBackground=-3152133 dialogLineProgressBackground=-3152133
chat_inReplyNameText=-13464859 chat_inReplyNameText=-13464859
chat_outAudioPerfomerSelectedText=-1 chat_outAudioPerfomerSelectedText=-1

View file

@ -43,6 +43,7 @@ chat_inLoaderSelected=-12277262
chat_outLocationIcon=-2105761599 chat_outLocationIcon=-2105761599
chat_outAudioProgress=-6239505 chat_outAudioProgress=-6239505
chat_inReplyLine=-348807706 chat_inReplyLine=-348807706
chat_inQuote=-348807706
dialogLineProgressBackground=-2035723 dialogLineProgressBackground=-2035723
chat_inReplyNameText=-14643754 chat_inReplyNameText=-14643754
chats_onlineCircle=-13192972 chats_onlineCircle=-13192972

Binary file not shown.

View file

@ -22,7 +22,7 @@ chat_attachAudioBackground=-626837
location_sendLocationBackground=-9919529 location_sendLocationBackground=-9919529
actionBarDefaultSubmenuBackground=-14075831 actionBarDefaultSubmenuBackground=-14075831
switchTrackBlueThumb=-14866637 switchTrackBlueThumb=-14866637
avatar_nameInMessageViolet=-6643205 avatar_nameInMessageViolet=-7565846
emptyListPlaceholder=-8549479 emptyListPlaceholder=-8549479
chat_inAudioSelectedProgress=-1 chat_inAudioSelectedProgress=-1
chats_nameMessage=-1446156 chats_nameMessage=-1446156
@ -118,6 +118,7 @@ chat_outAudioProgress=-1
stickers_menu=-10719105 stickers_menu=-10719105
player_progress=-10177041 player_progress=-10177041
chat_inReplyLine=-8796932 chat_inReplyLine=-8796932
chat_inQuote=-8796932
chat_inAudioPerfomerSelectedText=-7490861 chat_inAudioPerfomerSelectedText=-7490861
dialogBackground=-14602949 dialogBackground=-14602949
dialogLineProgressBackground=-13548718 dialogLineProgressBackground=-13548718
@ -138,10 +139,10 @@ chat_messagePanelSend=-10177041
passport_authorizeBackground=-12352582 passport_authorizeBackground=-12352582
chat_inSentClock=-10653824 chat_inSentClock=-10653824
chats_menuTopShadow=789516 chats_menuTopShadow=789516
avatar_nameInMessageRed=-21124 avatar_nameInMessageRed=-1537928
chat_botSwitchToInlineText=-8796932 chat_botSwitchToInlineText=-8796932
chats_nameMessageArchived=-8549479 chats_nameMessageArchived=-8549479
avatar_nameInMessageOrange=-13984 avatar_nameInMessageOrange=-1528998
chats_pinnedIcon=-10982016 chats_pinnedIcon=-10982016
chat_attachActiveTab=-9781249 chat_attachActiveTab=-9781249
chat_replyPanelLine=1578572317 chat_replyPanelLine=1578572317
@ -166,12 +167,12 @@ avatar_background2Red=-2013369
chat_emojiPanelBadgeBackground=-11291403 chat_emojiPanelBadgeBackground=-11291403
chat_inForwardedNameText=-8796932 chat_inForwardedNameText=-8796932
chats_actionBackground=-10509346 chats_actionBackground=-10509346
avatar_nameInMessageGreen=-7018619 avatar_nameInMessageGreen=-8531855
chat_outContactNameText=-1 chat_outContactNameText=-1
chat_inSiteNameText=-8796932 chat_inSiteNameText=-8796932
chat_linkSelectBackground=1516415459 chat_linkSelectBackground=1516415459
windowBackgroundWhiteBlueText=-10177041 windowBackgroundWhiteBlueText=-10177041
avatar_nameInMessageCyan=-10623523 avatar_nameInMessageCyan=-11285306
chat_inLocationBackground=-13417903 chat_inLocationBackground=-13417903
radioBackground=-1635939431 radioBackground=-1635939431
profile_tabText=2027746559 profile_tabText=2027746559
@ -278,12 +279,12 @@ key_chat_messagePanelVoiceLockBackground=-13548712
chat_outFileNameText=-1 chat_outFileNameText=-1
picker_enabledButton=-9781249 picker_enabledButton=-9781249
inappPlayerBackground=-14602949 inappPlayerBackground=-14602949
avatar_nameInMessagePink=-624741 avatar_nameInMessagePink=-1543014
windowBackgroundWhiteGrayText=-8549479 windowBackgroundWhiteGrayText=-8549479
statisticChartSignature=-1214008894 statisticChartSignature=-1214008894
actionBarDefaultSubmenuItemIcon=1859974399 actionBarDefaultSubmenuItemIcon=1859974399
chat_attachPollBackground=-2183099 chat_attachPollBackground=-2183099
avatar_nameInMessageBlue=-8796932 avatar_nameInMessageBlue=-10703382
dialogTextBlack=-592138 dialogTextBlack=-592138
actionBarDefault=-14406343 actionBarDefault=-14406343
location_placeLocationBackground=-9919529 location_placeLocationBackground=-9919529
@ -425,4 +426,7 @@ chat_topPanelBackground=-14602949
chat_outSentClock=-8213557 chat_outSentClock=-8213557
dialogBackgroundGray=-14932431 dialogBackgroundGray=-14932431
chat_searchPanelText=-8796932 chat_searchPanelText=-8796932
chat_inContactIcon=-1 chat_inContactIcon=-1
code_comment=-2130706433
chat_outCodeBackground=857487708
chat_inCodeBackground=856033549

View file

@ -79,6 +79,7 @@ chat_outAudioProgress=-12155183
player_progress=-14574092 player_progress=-14574092
chat_stickerReplyMessageText=-7565679 chat_stickerReplyMessageText=-7565679
chat_inReplyLine=-12676640 chat_inReplyLine=-12676640
chat_inQuote=-12676640
dialogLineProgressBackground=-3348999 dialogLineProgressBackground=-3348999
chat_inReplyNameText=-13531425 chat_inReplyNameText=-13531425
chat_outAudioPerfomerSelectedText=-1 chat_outAudioPerfomerSelectedText=-1

View file

@ -22,7 +22,7 @@ chat_attachAudioBackground=-626837
location_sendLocationBackground=-9919529 location_sendLocationBackground=-9919529
actionBarDefaultSubmenuBackground=-14145495 actionBarDefaultSubmenuBackground=-14145495
switchTrackBlueThumb=-14803424 switchTrackBlueThumb=-14803424
avatar_nameInMessageViolet=-6643205 avatar_nameInMessageViolet=-7565846
emptyListPlaceholder=-8553091 emptyListPlaceholder=-8553091
chat_inAudioSelectedProgress=-1 chat_inAudioSelectedProgress=-1
chats_nameMessage=-1315861 chats_nameMessage=-1315861
@ -121,6 +121,7 @@ chat_outAudioProgress=-1
stickers_menu=-10724260 stickers_menu=-10724260
player_progress=-11292689 player_progress=-11292689
chat_inReplyLine=-8796932 chat_inReplyLine=-8796932
chat_inQuote=-8796932
chat_inAudioPerfomerSelectedText=-7490861 chat_inAudioPerfomerSelectedText=-7490861
dialogBackground=-14803426 dialogBackground=-14803426
dialogLineProgressBackground=-12303292 dialogLineProgressBackground=-12303292
@ -146,10 +147,10 @@ chats_sentReadCheck=-12145165
passport_authorizeBackground=-12352582 passport_authorizeBackground=-12352582
chat_inSentClock=-11050909 chat_inSentClock=-11050909
chats_menuTopShadow=789516 chats_menuTopShadow=789516
avatar_nameInMessageRed=-21124 avatar_nameInMessageRed=-1537928
chat_botSwitchToInlineText=-8796932 chat_botSwitchToInlineText=-8796932
chats_nameMessageArchived=-8553091 chats_nameMessageArchived=-8553091
avatar_nameInMessageOrange=-13984 avatar_nameInMessageOrange=-1528998
chats_pinnedIcon=-10197916 chats_pinnedIcon=-10197916
chat_attachActiveTab=-9781249 chat_attachActiveTab=-9781249
chat_replyPanelLine=838860800 chat_replyPanelLine=838860800
@ -176,14 +177,14 @@ avatar_background2Red=-2530993
chat_emojiPanelBadgeBackground=-11291403 chat_emojiPanelBadgeBackground=-11291403
chat_inForwardedNameText=-8930052 chat_inForwardedNameText=-8930052
chats_actionBackground=-10575653 chats_actionBackground=-10575653
avatar_nameInMessageGreen=-7018619 avatar_nameInMessageGreen=-8531855
chat_outContactNameText=-1 chat_outContactNameText=-1
chat_TextSelectionCursor=-11028499 chat_TextSelectionCursor=-11028499
chat_inSiteNameText=-8796932 chat_inSiteNameText=-8796932
chat_linkSelectBackground=1516415459 chat_linkSelectBackground=1516415459
chats_archivePullDownBackground=-14145496 chats_archivePullDownBackground=-14145496
windowBackgroundWhiteBlueText=-10177041 windowBackgroundWhiteBlueText=-10177041
avatar_nameInMessageCyan=-10623523 avatar_nameInMessageCyan=-11285306
chat_inLocationBackground=-12829636 chat_inLocationBackground=-12829636
radioBackground=-10461088 radioBackground=-10461088
profile_tabText=2030043135 profile_tabText=2030043135
@ -296,12 +297,12 @@ key_chat_messagePanelVoiceLockBackground=-14606046
chat_outFileNameText=-1 chat_outFileNameText=-1
picker_enabledButton=-9781249 picker_enabledButton=-9781249
inappPlayerBackground=-15066597 inappPlayerBackground=-15066597
avatar_nameInMessagePink=-624741 avatar_nameInMessagePink=-1543014
windowBackgroundWhiteGrayText=1862270975 windowBackgroundWhiteGrayText=1862270975
statisticChartSignature=-1214008894 statisticChartSignature=-1214008894
actionBarDefaultSubmenuItemIcon=2029911551 actionBarDefaultSubmenuItemIcon=2029911551
chat_attachPollBackground=-2183099 chat_attachPollBackground=-2183099
avatar_nameInMessageBlue=-8796932 avatar_nameInMessageBlue=-10703382
dialogTextBlack=-592138 dialogTextBlack=-592138
actionBarDefault=-14474458 actionBarDefault=-14474458
location_placeLocationBackground=-9919529 location_placeLocationBackground=-9919529
@ -451,3 +452,6 @@ chat_outSentClock=-6698513
dialogBackgroundGray=-14013910 dialogBackgroundGray=-14013910
chat_searchPanelText=-10767620 chat_searchPanelText=-10767620
chat_inContactIcon=-1 chat_inContactIcon=-1
code_comment=-2130706433
chat_outCodeBackground=859062986
chat_inCodeBackground=855638016

View file

@ -24,6 +24,8 @@
package org.telegram.PhoneFormat; package org.telegram.PhoneFormat;
import androidx.annotation.NonNull;
import org.telegram.messenger.ApplicationLoader; import org.telegram.messenger.ApplicationLoader;
import org.telegram.messenger.FileLog; import org.telegram.messenger.FileLog;
@ -181,6 +183,7 @@ public class PhoneFormat {
return res; return res;
} }
@NonNull
public String format(String orig) { public String format(String orig) {
if (!initialzed) { if (!initialzed) {
return orig; return orig;

View file

@ -209,6 +209,7 @@ public class AndroidUtilities {
public final static int REPLACING_TAG_TYPE_LINK = 0; public final static int REPLACING_TAG_TYPE_LINK = 0;
public final static int REPLACING_TAG_TYPE_BOLD = 1; public final static int REPLACING_TAG_TYPE_BOLD = 1;
public final static int REPLACING_TAG_TYPE_LINKBOLD = 2;
public final static String TYPEFACE_ROBOTO_MEDIUM = "fonts/rmedium.ttf"; public final static String TYPEFACE_ROBOTO_MEDIUM = "fonts/rmedium.ttf";
public final static String TYPEFACE_ROBOTO_MEDIUM_ITALIC = "fonts/rmediumitalic.ttf"; public final static String TYPEFACE_ROBOTO_MEDIUM_ITALIC = "fonts/rmediumitalic.ttf";
@ -453,6 +454,10 @@ public class AndroidUtilities {
return null; return null;
} }
public static CharSequence premiumText(String str, Runnable runnable) {
return replaceSingleTag(str, -1, REPLACING_TAG_TYPE_LINKBOLD, runnable);
}
public static CharSequence replaceSingleTag(String str, Runnable runnable) { public static CharSequence replaceSingleTag(String str, Runnable runnable) {
return replaceSingleTag(str, -1, 0, runnable); return replaceSingleTag(str, -1, 0, runnable);
} }
@ -473,7 +478,7 @@ public class AndroidUtilities {
} }
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(str); SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(str);
if (index >= 0) { if (index >= 0) {
if (type == REPLACING_TAG_TYPE_LINK) { if (type == REPLACING_TAG_TYPE_LINK || type == REPLACING_TAG_TYPE_LINKBOLD) {
spannableStringBuilder.setSpan(new ClickableSpan() { spannableStringBuilder.setSpan(new ClickableSpan() {
@Override @Override
@ -483,6 +488,9 @@ public class AndroidUtilities {
if (colorKey >= 0) { if (colorKey >= 0) {
ds.setColor(Theme.getColor(colorKey, resourcesProvider)); ds.setColor(Theme.getColor(colorKey, resourcesProvider));
} }
if (type == REPLACING_TAG_TYPE_LINKBOLD) {
ds.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM));
}
} }
@Override @Override
@ -593,8 +601,11 @@ public class AndroidUtilities {
if (child.getVisibility() != View.VISIBLE || child instanceof PeerStoriesView && child != onlyThisView) { if (child.getVisibility() != View.VISIBLE || child instanceof PeerStoriesView && child != onlyThisView) {
continue; continue;
} }
if (child instanceof StoryMediaAreasView.AreaView && !((StoryMediaAreasView) container).hasSelected() && (x < dp(60) || x > container.getWidth() - dp(60))) { if (child instanceof StoryMediaAreasView.AreaView) {
continue; StoryMediaAreasView areasView = (StoryMediaAreasView) container;
if (!(areasView.hasSelected() && (x < dp(60) || x > container.getWidth() - dp(60))) && !areasView.hasAreaAboveAt(x, y)) {
continue;
}
} }
child.getHitRect(AndroidUtilities.rectTmp2); child.getHitRect(AndroidUtilities.rectTmp2);
if (AndroidUtilities.rectTmp2.contains((int) x, (int) y) && child.isClickable()) { if (AndroidUtilities.rectTmp2.contains((int) x, (int) y) && child.isClickable()) {
@ -3454,7 +3465,7 @@ public class AndroidUtilities {
return stringBuilder.toString(); return stringBuilder.toString();
} }
public static final String[] numbersSignatureArray = {"", "K", "M", "G", "T", "P"}; public static final String[] numbersSignatureArray = {"", "K", "M", "B", "T", "P"};
public static String formatWholeNumber(int v, int dif) { public static String formatWholeNumber(int v, int dif) {
if (v == 0) { if (v == 0) {
@ -4632,6 +4643,20 @@ public class AndroidUtilities {
} }
} }
public static void setLightStatusBar(View view, boolean enable) {
if (view != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
int flags = view.getSystemUiVisibility();
if (((flags & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) > 0) != enable) {
if (enable) {
flags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
} else {
flags &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
}
view.setSystemUiVisibility(flags);
}
}
}
public static void setLightNavigationBar(Window window, boolean enable) { public static void setLightNavigationBar(Window window, boolean enable) {
if (window != null) { if (window != null) {
setLightNavigationBar(window.getDecorView(), enable); setLightNavigationBar(window.getDecorView(), enable);
@ -5282,6 +5307,10 @@ public class AndroidUtilities {
return Math.max(x1, x2) > Math.min(y1, y2) && Math.max(y1, y2) > Math.min(x1, x2); return Math.max(x1, x2) > Math.min(y1, y2) && Math.max(y1, y2) > Math.min(x1, x2);
} }
public static boolean intersect1dInclusive(int x1, int x2, int y1, int y2) {
return Math.max(x1, x2) >= Math.min(y1, y2) && Math.max(y1, y2) >= Math.min(x1, x2);
}
public static String getSysInfoString(String path) { public static String getSysInfoString(String path) {
RandomAccessFile reader = null; RandomAccessFile reader = null;
try { try {

View file

@ -27,6 +27,7 @@ import android.os.Handler;
import android.os.PowerManager; import android.os.PowerManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.multidex.MultiDex; import androidx.multidex.MultiDex;
@ -38,6 +39,8 @@ import org.telegram.messenger.voip.VideoCapturerDevice;
import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.ui.Components.ForegroundDetector; import org.telegram.ui.Components.ForegroundDetector;
import org.telegram.ui.Components.Premium.boosts.BoostRepository;
import org.telegram.ui.IUpdateLayout;
import org.telegram.ui.LauncherIconController; import org.telegram.ui.LauncherIconController;
import java.io.File; import java.io.File;
@ -123,10 +126,18 @@ public class ApplicationLoader extends Application {
return applicationLoaderInstance.isHuaweiBuild(); return applicationLoaderInstance.isHuaweiBuild();
} }
public static boolean isStandaloneBuild() {
return applicationLoaderInstance.isStandalone();
}
protected boolean isHuaweiBuild() { protected boolean isHuaweiBuild() {
return false; return false;
} }
protected boolean isStandalone() {
return false;
}
public static File getFilesDirFixed() { public static File getFilesDirFixed() {
for (int a = 0; a < 10; a++) { for (int a = 0; a < 10; a++) {
File path = ApplicationLoader.applicationContext.getFilesDir(); File path = ApplicationLoader.applicationContext.getFilesDir();
@ -562,4 +573,13 @@ public class ApplicationLoader extends Application {
public boolean openApkInstall(Activity activity, TLRPC.Document document) { public boolean openApkInstall(Activity activity, TLRPC.Document document) {
return false; return false;
} }
public boolean showUpdateAppPopup(Context context, TLRPC.TL_help_appUpdate update, int account) {
return false;
}
public IUpdateLayout takeUpdateLayout(Activity activity, ViewGroup sideMenu, ViewGroup sideMenuContainer) {
return null;
}
} }

View file

@ -59,6 +59,7 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
private String lastPremiumTransaction; private String lastPremiumTransaction;
private String lastPremiumToken; private String lastPremiumToken;
private boolean isDisconnected; private boolean isDisconnected;
private Runnable onCanceled;
public static BillingController getInstance() { public static BillingController getInstance() {
if (instance == null) { if (instance == null) {
@ -74,6 +75,10 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
.build(); .build();
} }
public void setOnCanceled(Runnable onCanceled) {
this.onCanceled = onCanceled;
}
public String getLastPremiumTransaction() { public String getLastPremiumTransaction() {
return lastPremiumTransaction; return lastPremiumTransaction;
} }
@ -177,7 +182,7 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
if (purchase.getProducts().contains(productId)) { if (purchase.getProducts().contains(productId)) {
productsToBeConsumed.incrementAndGet(); productsToBeConsumed.incrementAndGet();
billingClient.consumeAsync(ConsumeParams.newBuilder() billingClient.consumeAsync(ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken()) .setPurchaseToken(purchase.getPurchaseToken())
.build(), (billingResult1, s) -> { .build(), (billingResult1, s) -> {
if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK) { if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK) {
productsConsumed.add(productId); productsConsumed.add(productId);
@ -228,6 +233,10 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
if (billing.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) { if (billing.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
PremiumPreviewFragment.sentPremiumBuyCanceled(); PremiumPreviewFragment.sentPremiumBuyCanceled();
} }
if (onCanceled != null) {
onCanceled.run();
onCanceled = null;
}
return; return;
} }
if (list == null || list.isEmpty()) { if (list == null || list.isEmpty()) {
@ -269,7 +278,10 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
consumeGiftPurchase(purchase, req.purpose); consumeGiftPurchase(purchase, req.purpose);
} else if (error != null) { } else if (error != null) {
FileLog.d("Billing: Confirmation Error: " + error.code + " " + error.text); if (onCanceled != null) {
onCanceled.run();
onCanceled = null;
}
NotificationCenter.getGlobalInstance().postNotificationNameOnUIThread(NotificationCenter.billingConfirmPurchaseError, req, error); NotificationCenter.getGlobalInstance().postNotificationNameOnUIThread(NotificationCenter.billingConfirmPurchaseError, req, error);
} }
}, ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagInvokeAfter); }, ConnectionsManager.RequestFlagFailOnServerErrors | ConnectionsManager.RequestFlagInvokeAfter);
@ -285,7 +297,9 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
* Without confirmation the user will not be able to buy the product again. * Without confirmation the user will not be able to buy the product again.
*/ */
private void consumeGiftPurchase(Purchase purchase, TLRPC.InputStorePaymentPurpose purpose) { private void consumeGiftPurchase(Purchase purchase, TLRPC.InputStorePaymentPurpose purpose) {
if (purpose instanceof TLRPC.TL_inputStorePaymentGiftPremium) { if (purpose instanceof TLRPC.TL_inputStorePaymentGiftPremium
|| purpose instanceof TLRPC.TL_inputStorePaymentPremiumGiftCode
|| purpose instanceof TLRPC.TL_inputStorePaymentPremiumGiveaway) {
billingClient.consumeAsync( billingClient.consumeAsync(
ConsumeParams.newBuilder() ConsumeParams.newBuilder()
.setPurchaseToken(purchase.getPurchaseToken()) .setPurchaseToken(purchase.getPurchaseToken())

View file

@ -24,14 +24,13 @@ public class BuildVars {
public static boolean USE_CLOUD_STRINGS = true; public static boolean USE_CLOUD_STRINGS = true;
public static boolean CHECK_UPDATES = true; public static boolean CHECK_UPDATES = true;
public static boolean NO_SCOPED_STORAGE = Build.VERSION.SDK_INT <= 29; public static boolean NO_SCOPED_STORAGE = Build.VERSION.SDK_INT <= 29;
public static int BUILD_VERSION = 3926; public static int BUILD_VERSION = 4056;
public static String BUILD_VERSION_STRING = "10.1.1"; public static String BUILD_VERSION_STRING = "10.2.0";
public static int APP_ID = 4; public static int APP_ID = 4;
public static String APP_HASH = "014b35b6184100b085b0d0572f9b5103"; public static String APP_HASH = "014b35b6184100b085b0d0572f9b5103";
// SafetyNet key for Google Identity SDK, set it to empty to disable // SafetyNet key for Google Identity SDK, set it to empty to disable
public static String SAFETYNET_KEY = "AIzaSyDqt8P-7F7CPCseMkOiVRgb1LY8RN1bvH8"; public static String SAFETYNET_KEY = "AIzaSyDqt8P-7F7CPCseMkOiVRgb1LY8RN1bvH8";
public static String SMS_HASH = isStandaloneApp() ? "w0lkcmTZkKh" : (DEBUG_VERSION ? "O2P2z+/jBpJ" : "oLeq9AcOZkT");
public static String PLAYSTORE_APP_URL = "https://play.google.com/store/apps/details?id=org.telegram.messenger"; public static String PLAYSTORE_APP_URL = "https://play.google.com/store/apps/details?id=org.telegram.messenger";
public static String HUAWEI_STORE_URL = "https://appgallery.huawei.com/app/C101184875"; public static String HUAWEI_STORE_URL = "https://appgallery.huawei.com/app/C101184875";
public static String GOOGLE_AUTH_CLIENT_ID = "760348033671-81kmi3pi84p11ub8hp9a1funsv0rn2p9.apps.googleusercontent.com"; public static String GOOGLE_AUTH_CLIENT_ID = "760348033671-81kmi3pi84p11ub8hp9a1funsv0rn2p9.apps.googleusercontent.com";
@ -49,7 +48,7 @@ public class BuildVars {
} }
public static boolean useInvoiceBilling() { public static boolean useInvoiceBilling() {
return BillingController.billingClientEmpty || DEBUG_VERSION || isStandaloneApp() || isBetaApp() || isHuaweiStoreApp() || hasDirectCurrency(); return BillingController.billingClientEmpty || DEBUG_VERSION || ApplicationLoader.isStandaloneBuild() || isBetaApp() || isHuaweiStoreApp() || hasDirectCurrency();
} }
private static boolean hasDirectCurrency() { private static boolean hasDirectCurrency() {
@ -68,14 +67,6 @@ public class BuildVars {
return false; return false;
} }
private static Boolean standaloneApp;
public static boolean isStandaloneApp() {
if (standaloneApp == null) {
standaloneApp = ApplicationLoader.applicationContext != null && "org.telegram.messenger.web".equals(ApplicationLoader.applicationContext.getPackageName());
}
return standaloneApp;
}
private static Boolean betaApp; private static Boolean betaApp;
public static boolean isBetaApp() { public static boolean isBetaApp() {
if (betaApp == null) { if (betaApp == null) {
@ -88,4 +79,8 @@ public class BuildVars {
public static boolean isHuaweiStoreApp() { public static boolean isHuaweiStoreApp() {
return ApplicationLoader.isHuaweiStoreBuild(); return ApplicationLoader.isHuaweiStoreBuild();
} }
public static String getSmsHash() {
return ApplicationLoader.isStandaloneBuild() ? "w0lkcmTZkKh" : (DEBUG_VERSION ? "O2P2z+/jBpJ" : "oLeq9AcOZkT");
}
} }

View file

@ -4,8 +4,13 @@ import com.google.android.exoplayer2.util.Consumer;
import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.ui.Components.Bulletin; import org.telegram.tgnet.tl.TL_stories;
import org.telegram.ui.Components.BulletinFactory; import org.telegram.ui.Components.BulletinFactory;
import org.telegram.ui.Components.Premium.boosts.BoostRepository;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ChannelBoostsController { public class ChannelBoostsController {
@ -23,86 +28,123 @@ public class ChannelBoostsController {
} }
public void getBoostsStats(long dialogId, Consumer<TLRPC.TL_stories_boostsStatus> consumer) { public void getBoostsStats(long dialogId, Consumer<TL_stories.TL_premium_boostsStatus> consumer) {
TLRPC.TL_stories_getBoostsStatus req = new TLRPC.TL_stories_getBoostsStatus(); TL_stories.TL_premium_getBoostsStatus req = new TL_stories.TL_premium_getBoostsStatus();
req.peer = messagesController.getInputPeer(dialogId); req.peer = messagesController.getInputPeer(dialogId);
connectionsManager.sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { connectionsManager.sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
if (response != null) { if (response != null) {
consumer.accept((TLRPC.TL_stories_boostsStatus) response); consumer.accept((TL_stories.TL_premium_boostsStatus) response);
} else { } else {
BulletinFactory.showForError(error); BulletinFactory.showForError(error);
consumer.accept(null); consumer.accept(null);
} }
})); }));
} }
public void userCanBoostChannel(long dialogId, Consumer<CanApplyBoost> consumer) { public void userCanBoostChannel(long dialogId, TL_stories.TL_premium_boostsStatus boostsStatus, Consumer<CanApplyBoost> consumer) {
TLRPC.TL_stories_canApplyBoost req = new TLRPC.TL_stories_canApplyBoost(); CanApplyBoost canApplyBoost = new CanApplyBoost();
req.peer = messagesController.getInputPeer(dialogId); canApplyBoost.currentPeer = messagesController.getPeer(dialogId);
connectionsManager.sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> { canApplyBoost.currentDialogId = dialogId;
CanApplyBoost canApplyBoost = new CanApplyBoost(); canApplyBoost.currentChat = messagesController.getChat(-dialogId);
if (response != null) { BoostRepository.getMyBoosts(myBoosts -> {
canApplyBoost.canApply = true; canApplyBoost.isMaxLvl = boostsStatus.next_level_boosts <= 0;
if (response instanceof TLRPC.TL_stories_canApplyBoostReplace) { canApplyBoost.setMyBoosts(myBoosts);
TLRPC.TL_stories_canApplyBoostReplace canApplyBoostReplace = (TLRPC.TL_stories_canApplyBoostReplace) response; consumer.accept(canApplyBoost);
messagesController.putChats(canApplyBoostReplace.chats, false); }, error -> {
canApplyBoost.replaceDialogId = DialogObject.getPeerDialogId(canApplyBoostReplace.current_boost); if (error.text.startsWith("FLOOD_WAIT")) {
if (canApplyBoost.replaceDialogId == 0 && canApplyBoostReplace.chats.size() > 0) { canApplyBoost.floodWait = Utilities.parseInt(error.text);
canApplyBoost.replaceDialogId = -canApplyBoostReplace.chats.get(0).id; } else if (error.text.startsWith("BOOSTS_EMPTY")) {
} canApplyBoost.empty = true;
} }
} else { canApplyBoost.canApply = false;
if (error != null) {
if (error.text.equals("SAME_BOOST_ALREADY_ACTIVE") || error.text.equals("BOOST_NOT_MODIFIED")) {
canApplyBoost.alreadyActive = true;
} else if (error.text.equals("PREMIUM_GIFTED_NOT_ALLOWED")) {
canApplyBoost.giftedPremium = true;
} else if (error.text.startsWith("FLOOD_WAIT")) {
canApplyBoost.floodWait = Utilities.parseInt(error.text);
canApplyBoost.lastCheckTime = System.currentTimeMillis();
}
}
}
consumer.accept(canApplyBoost); consumer.accept(canApplyBoost);
}), ConnectionsManager.RequestFlagDoNotWaitFloodWait);
}
public void applyBoost(long dialogId) {
TLRPC.TL_stories_applyBoost req = new TLRPC.TL_stories_applyBoost();
req.peer = messagesController.getInputPeer(dialogId);
connectionsManager.sendRequest(req, (response, error) -> {
}); });
} }
public int getTotalBooststToLevel(int level) { public void applyBoost(long dialogId, int slot, Utilities.Callback<TL_stories.TL_premium_myBoosts> onDone, Utilities.Callback<TLRPC.TL_error> onError) {
int count = 0; BoostRepository.applyBoost(-dialogId, Arrays.asList(slot), onDone, onError);
if (level >= 1) {
count += BOOSTS_FOR_LEVEL_1;
}
if (level >= 2) {
count += BOOSTS_FOR_LEVEL_2;
}
return count;
} }
public static class CanApplyBoost { public static class CanApplyBoost {
public boolean canApply; public boolean canApply;
public boolean empty;
public long replaceDialogId; public long replaceDialogId;
public boolean alreadyActive; public boolean alreadyActive;
public boolean needSelector;
public int floodWait; public int floodWait;
public boolean giftedPremium; public int slot;
private long lastCheckTime; public TL_stories.TL_premium_myBoosts myBoosts;
public int boostCount = 0;
public TLRPC.Peer currentPeer;
public long currentDialogId;
public TLRPC.Chat currentChat;
public boolean boostedNow;
public boolean isMaxLvl;
public void checkTime() { public void setMyBoosts(TL_stories.TL_premium_myBoosts myBoosts) {
floodWait -= (System.currentTimeMillis() - lastCheckTime) / 1000; this.myBoosts = myBoosts;
lastCheckTime = System.currentTimeMillis(); boostCount = 0;
if (floodWait < 0) { slot = 0;
floodWait = 0; alreadyActive = false;
canApply = false;
needSelector = false;
replaceDialogId = 0;
if (myBoosts.my_boosts.isEmpty()) {
empty = true;
}
//search boosted count
for (TL_stories.TL_myBoost myBoost : myBoosts.my_boosts) {
if (currentDialogId == DialogObject.getPeerDialogId(myBoost.peer)) {
boostCount++;
}
}
if (boostCount > 0) {
alreadyActive = true;
}
//search free slot
for (TL_stories.TL_myBoost myBoost : myBoosts.my_boosts) {
if (myBoost.peer == null) {
slot = myBoost.slot;
break;
}
}
boolean noFreeSlot = slot == 0;
if (noFreeSlot) {
//only replacement
List<TL_stories.TL_myBoost> replaceBoost = new ArrayList<>();
for (TL_stories.TL_myBoost myBoost : myBoosts.my_boosts) {
if (myBoost.peer != null && DialogObject.getPeerDialogId(myBoost.peer) != -currentChat.id) {
replaceBoost.add(myBoost);
}
}
if (replaceBoost.size() == 1) {
TL_stories.TL_myBoost myBoost = replaceBoost.get(0);
replaceDialogId = DialogObject.getPeerDialogId(myBoost.peer);
slot = myBoost.slot;
canApply = true;
} else if (replaceBoost.size() > 1) {
needSelector = true;
if (!BoostRepository.isMultiBoostsAvailable()) {
TL_stories.TL_myBoost myBoost = replaceBoost.get(0);
replaceDialogId = DialogObject.getPeerDialogId(myBoost.peer);
slot = myBoost.slot;
}
canApply = true;
} else {
canApply = false;
}
} else {
canApply = true; canApply = true;
} }
if (isMaxLvl) {
canApply = false;
}
} }
} }
} }

View file

@ -1,17 +1,11 @@
package org.telegram.messenger; package org.telegram.messenger;
import com.google.android.exoplayer2.util.Log;
import org.checkerframework.checker.units.qual.A;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.RequestDelegate;
import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.tgnet.tl.TL_stories;
import org.telegram.ui.ChatActivity; import org.telegram.ui.ChatActivity;
import org.telegram.ui.Stories.StoriesStorage; import org.telegram.ui.Stories.StoriesStorage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
public class ChatMessagesMetadataController { public class ChatMessagesMetadataController {
@ -53,8 +47,8 @@ public class ChatMessagesMetadataController {
extendedMediaToCheck.add(messageObject); extendedMediaToCheck.add(messageObject);
} }
if (messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION || messageObject.messageOwner.replyStory != null) { if (messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION || messageObject.messageOwner.replyStory != null) {
TLRPC.StoryItem storyItem = messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION ? messageObject.messageOwner.media.storyItem : messageObject.messageOwner.replyStory; TL_stories.StoryItem storyItem = messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION ? messageObject.messageOwner.media.storyItem : messageObject.messageOwner.replyStory;
if (storyItem == null || storyItem instanceof TLRPC.TL_storyItemDeleted) { if (storyItem == null || storyItem instanceof TL_stories.TL_storyItemDeleted) {
continue; continue;
} }
if (currentTime - storyItem.lastUpdateTime > 1000 * 5 * 60) { if (currentTime - storyItem.lastUpdateTime > 1000 * 5 * 60) {
@ -74,9 +68,9 @@ public class ChatMessagesMetadataController {
return; return;
} }
for (int i = 0; i < visibleObjects.size(); i++) { for (int i = 0; i < visibleObjects.size(); i++) {
TLRPC.TL_stories_getStoriesByID req = new TLRPC.TL_stories_getStoriesByID(); TL_stories.TL_stories_getStoriesByID req = new TL_stories.TL_stories_getStoriesByID();
MessageObject messageObject = visibleObjects.get(i); MessageObject messageObject = visibleObjects.get(i);
TLRPC.StoryItem storyItem = new TLRPC.TL_storyItem(); TL_stories.StoryItem storyItem = new TL_stories.TL_storyItem();
if (messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION) { if (messageObject.type == MessageObject.TYPE_STORY || messageObject.type == MessageObject.TYPE_STORY_MENTION) {
storyItem = messageObject.messageOwner.media.storyItem; storyItem = messageObject.messageOwner.media.storyItem;
storyItem.dialogId = messageObject.messageOwner.media.user_id; storyItem.dialogId = messageObject.messageOwner.media.user_id;
@ -91,18 +85,18 @@ public class ChatMessagesMetadataController {
req.id.add(storyItem.id); req.id.add(storyItem.id);
int storyId = storyItem.id; int storyId = storyItem.id;
int reqId = chatActivity.getConnectionsManager().sendRequest(req, (response, error) -> { int reqId = chatActivity.getConnectionsManager().sendRequest(req, (response, error) -> {
TLRPC.StoryItem newStoryItem = null; TL_stories.StoryItem newStoryItem = null;
if (response != null) { if (response != null) {
TLRPC.TL_stories_stories stories = (TLRPC.TL_stories_stories) response; TL_stories.TL_stories_stories stories = (TL_stories.TL_stories_stories) response;
if (stories.stories.size() > 0) { if (stories.stories.size() > 0) {
newStoryItem = stories.stories.get(0); newStoryItem = stories.stories.get(0);
} }
if (newStoryItem == null) { if (newStoryItem == null) {
newStoryItem = new TLRPC.TL_storyItemDeleted(); newStoryItem = new TL_stories.TL_storyItemDeleted();
} }
newStoryItem.lastUpdateTime = System.currentTimeMillis(); newStoryItem.lastUpdateTime = System.currentTimeMillis();
newStoryItem.id = storyId; newStoryItem.id = storyId;
TLRPC.StoryItem finalNewStoryItem = newStoryItem; TL_stories.StoryItem finalNewStoryItem = newStoryItem;
AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.runOnUIThread(() -> {
boolean wasExpired = messageObject.isExpiredStory(); boolean wasExpired = messageObject.isExpiredStory();
StoriesStorage.applyStory(chatActivity.getCurrentAccount(), storyDialogId, messageObject, finalNewStoryItem); StoriesStorage.applyStory(chatActivity.getCurrentAccount(), storyDialogId, messageObject, finalNewStoryItem);
@ -137,7 +131,8 @@ public class ChatMessagesMetadataController {
MessageObject messageObject = visibleObjects.get(i); MessageObject messageObject = visibleObjects.get(i);
req.id.add(messageObject.getId()); req.id.add(messageObject.getId());
} }
int reqId = chatActivity.getConnectionsManager().sendRequest(req, (response, error) -> { final int[] reqId = new int[1];
reqId[0] = chatActivity.getConnectionsManager().sendRequest(req, (response, error) -> {
if (error == null) { if (error == null) {
TLRPC.Updates updates = (TLRPC.Updates) response; TLRPC.Updates updates = (TLRPC.Updates) response;
for (int i = 0; i < updates.updates.size(); i++) { for (int i = 0; i < updates.updates.size(); i++) {
@ -147,9 +142,12 @@ public class ChatMessagesMetadataController {
} }
chatActivity.getMessagesController().processUpdates(updates, false); chatActivity.getMessagesController().processUpdates(updates, false);
} }
AndroidUtilities.runOnUIThread(() -> {
reactionsRequests.remove((Object) reqId[0]);
});
}); });
reactionsRequests.add(reqId); reactionsRequests.add(reqId[0]);
if (reactionsRequests.size() > 5) { while (reactionsRequests.size() > 4) {
chatActivity.getConnectionsManager().cancelRequest(reactionsRequests.remove(0), false); chatActivity.getConnectionsManager().cancelRequest(reactionsRequests.remove(0), false);
} }
} }
@ -164,13 +162,17 @@ public class ChatMessagesMetadataController {
MessageObject messageObject = visibleObjects.get(i); MessageObject messageObject = visibleObjects.get(i);
req.id.add(messageObject.getId()); req.id.add(messageObject.getId());
} }
int reqId = chatActivity.getConnectionsManager().sendRequest(req, (response, error) -> { final int[] reqId = new int[1];
reqId[0] = chatActivity.getConnectionsManager().sendRequest(req, (response, error) -> {
if (error == null) { if (error == null) {
chatActivity.getMessagesController().processUpdates((TLRPC.Updates) response, false); chatActivity.getMessagesController().processUpdates((TLRPC.Updates) response, false);
} }
AndroidUtilities.runOnUIThread(() -> {
extendedMediaRequests.remove((Object) reqId[0]);
});
}); });
extendedMediaRequests.add(reqId); extendedMediaRequests.add(reqId[0]);
if (extendedMediaRequests.size() > 10) { while (extendedMediaRequests.size() > 10) {
chatActivity.getConnectionsManager().cancelRequest(extendedMediaRequests.remove(0), false); chatActivity.getConnectionsManager().cancelRequest(extendedMediaRequests.remove(0), false);
} }
} }

View file

@ -0,0 +1,754 @@
package org.telegram.messenger;
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.text.Editable;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.CharacterStyle;
import android.text.style.LineHeightSpan;
import android.text.style.MetricAffectingSpan;
import android.util.Log;
import androidx.annotation.NonNull;
import org.telegram.tgnet.AbstractSerializedData;
import org.telegram.tgnet.SerializedData;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.TextStyleSpan;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class CodeHighlighting {
public static final int MATCH_NONE = 0;
public static final int MATCH_KEYWORD = 1;
public static final int MATCH_OPERATOR = 2;
public static final int MATCH_CONSTANT = 3;
public static final int MATCH_STRING = 4;
public static final int MATCH_NUMBER = 5;
public static final int MATCH_COMMENT = 6;
public static final int MATCH_FUNCTION = 7;
public static class Span extends MetricAffectingSpan {
public final String lng;
public final String code;
public final int currentType;
public final TextStyleSpan.TextStyleRun style;
public final float decrementSize;
public final boolean smallerSize;
public Span(boolean smallerSize, int type, TextStyleSpan.TextStyleRun style, String lng, String code) {
this.smallerSize = smallerSize;
this.lng = lng;
this.code = code;
if (code == null) {
this.decrementSize = 2;
} else if (code.length() > 120) {
this.decrementSize = 5;
} else if (code.length() > 50) {
this.decrementSize = 3;
} else {
this.decrementSize = 2;
}
this.currentType = type;
this.style = style;
}
@Override
public void updateMeasureState(TextPaint p) {
if (smallerSize) {
p.setTextSize(AndroidUtilities.dp(SharedConfig.fontSize - decrementSize));
}
p.setFlags(p.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
if (style != null) {
style.applyStyle(p);
} else {
p.setTypeface(Typeface.MONOSPACE);
}
}
@Override
public void updateDrawState(TextPaint p) {
if (smallerSize) {
p.setTextSize(AndroidUtilities.dp(SharedConfig.fontSize - decrementSize));
}
if (currentType == 2) {
p.setColor(0xffffffff);
} else if (currentType == 1) {
p.setColor(Theme.getColor(Theme.key_chat_messageTextOut));
} else {
p.setColor(Theme.getColor(Theme.key_chat_messageTextIn));
}
if (style != null) {
style.applyStyle(p);
} else {
p.setTypeface(Typeface.MONOSPACE);
p.setUnderlineText(false);
}
}
}
public static class ColorSpan extends CharacterStyle {
public int group;
public ColorSpan(int match) {
this.group = match;
}
public int getColorKey() {
switch (this.group) {
case MATCH_KEYWORD: return Theme.key_code_keyword;
case MATCH_OPERATOR: return Theme.key_code_operator;
case MATCH_CONSTANT: return Theme.key_code_constant;
case MATCH_STRING: return Theme.key_code_string;
case MATCH_NUMBER: return Theme.key_code_number;
case MATCH_COMMENT: return Theme.key_code_comment;
case MATCH_FUNCTION: return Theme.key_code_function;
default: return -1;
}
}
@Override
public void updateDrawState(TextPaint tp) {
tp.setColor(Theme.getColor(getColorKey()));
}
}
// setting 100-300 spans is hard
// fast way to fix this is to do it on another thread, and not give access to data until it's finished processing
public static class LockedSpannableString extends SpannableString {
public LockedSpannableString(CharSequence text) {
super(text);
}
private boolean ready;
public void unlock() {
this.ready = true;
}
@Override
public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
if (!ready) return (T[]) Array.newInstance(kind, 0);
return super.getSpans(queryStart, queryEnd, kind);
}
@Override
public int nextSpanTransition(int start, int limit, Class kind) {
if (!ready) return limit;
return super.nextSpanTransition(start, limit, kind);
}
@Override
public int getSpanStart(Object what) {
if (!ready) return -1;
return super.getSpanStart(what);
}
@Override
public int getSpanEnd(Object what) {
if (!ready) return -1;
return super.getSpanEnd(what);
}
@Override
public int getSpanFlags(Object what) {
if (!ready) return 0;
return super.getSpanFlags(what);
}
}
private static final HashMap<String, Highlighting> processedHighlighting = new HashMap<>();
private static class Highlighting {
String text, language;
SpannableString result;
}
public static SpannableString getHighlighted(String text, String language) {
if (TextUtils.isEmpty(language)) {
return new SpannableString(text);
}
final String key = language + "`" + text;
Highlighting process = processedHighlighting.get(key);
if (process == null) {
process = new Highlighting();
process.text = text;
process.language = language;
process.result = new LockedSpannableString(text);
highlight(process.result, 0, process.result.length(), language, 0, null, true);
Iterator<String> keys = processedHighlighting.keySet().iterator();
while (keys.hasNext() && processedHighlighting.size() > 8) {
keys.next();
keys.remove();
}
processedHighlighting.put(key, process);
}
return process.result;
}
public static void highlight(Spannable text, int start, int end, String lng, int type, TextStyleSpan.TextStyleRun style, boolean smallerSize) {
if (text == null) {
return;
}
// text.setSpan(new Span(smallerSize, type, style, lng, text.subSequence(start, end).toString(), start <= 0, end >= text.length() - 1, start, end), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Utilities.searchQueue.postRunnable(() -> {
if (compiledPatterns == null)
parse();
long S = System.currentTimeMillis();
final StringToken[][] tokens = new StringToken[1][];
try {
tokens[0] = tokenize(text.subSequence(start, end).toString(), compiledPatterns == null ? null : compiledPatterns.get(lng)).toArray();
} catch (Exception e) {
FileLog.e(e);
}
FileLog.d("[CodeHighlighter] tokenize took " + (System.currentTimeMillis() - S) + "ms");
long S2 = System.currentTimeMillis();
ArrayList<CachedToSpan> spans = new ArrayList<>();
colorize(text, start, end, tokens[0], -1, spans);
FileLog.d("[CodeHighlighter] colorize took " + (System.currentTimeMillis() - S2) + "ms");
if (!spans.isEmpty()) {
if (text instanceof LockedSpannableString) {
long S3 = System.currentTimeMillis();
for (int i = 0; i < spans.size(); ++i) {
CachedToSpan span = spans.get(i);
text.setSpan(new ColorSpan(span.group), span.start, span.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
FileLog.d("[CodeHighlighter] applying " + spans.size() + " colorize spans took " + (System.currentTimeMillis() - S3) + "ms in another thread");
AndroidUtilities.runOnUIThread(() -> {
((LockedSpannableString) text).unlock();
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.emojiLoaded);
});
} else {
AndroidUtilities.runOnUIThread(() -> {
long S3 = System.currentTimeMillis();
for (int i = 0; i < spans.size(); ++i) {
CachedToSpan span = spans.get(i);
text.setSpan(new ColorSpan(span.group), span.start, span.end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
FileLog.d("[CodeHighlighter] applying " + spans.size() + " colorize spans took " + (System.currentTimeMillis() - S3) + "ms");
NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.emojiLoaded);
});
}
}
});
}
private static void colorize(Spannable text, int start, int end, StringToken[] tokens, int defGroup, ArrayList<CachedToSpan> result) {
if (tokens == null) {
return;
}
int p = start;
for (int i = 0; i < tokens.length && p < end; ++i) {
StringToken t = tokens[i];
if (t == null) continue;
if (t.string != null) {
int group = t.group;
if (defGroup != -1) {
group = defGroup;
}
if (group == -1) {
p += t.length();
continue;
}
result.add(new CachedToSpan(group, p, p + t.length()));
// text.setSpan(new ColorSpan(group), p, p + t.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else if (t.inside != null) {
colorize(text, p, p + t.length(), t.inside.toArray(), t.group, result);
}
p += t.length();
}
}
private static class CachedToSpan {
public int group;
public int start, end;
public CachedToSpan(int group, int start, int end) {
this.group = group;
this.start = start;
this.end = end;
}
}
private static LinkedList tokenize(String text, TokenPattern[] grammar) {
return tokenize(text, grammar, null);
}
private static LinkedList tokenize(String text, TokenPattern[] grammar, TokenPattern ignorePattern) {
LinkedList list = new LinkedList();
list.addAfter(list.head, new StringToken(text));
grammar = flatRest(grammar);
matchGrammar(text, list, grammar, list.head, 0, null, ignorePattern);
return list;
}
private static TokenPattern[] flatRest(TokenPattern[] patterns) {
if (patterns == null) {
return null;
}
ArrayList<TokenPattern> result = null;
for (int i = 0; i < patterns.length; ++i) {
if (patterns[i].pattern != null && "REST".equals(patterns[i].pattern.patternSource)) {
if (result == null) {
result = new ArrayList<>();
Collections.addAll(result, patterns);
}
result.remove(patterns[i]);
if (!TextUtils.isEmpty(patterns[i].insideLanguage) && compiledPatterns != null) {
TokenPattern[] grammar = compiledPatterns.get(patterns[i].insideLanguage);
if (grammar != null) {
Collections.addAll(result, grammar);
}
}
}
}
if (result != null) {
return result.toArray(new TokenPattern[0]);
}
return patterns;
}
private static void matchGrammar(String text, LinkedList tokenList, TokenPattern[] grammar, Node startNode, int startPos, RematchOptions rematch, TokenPattern ignorePattern) {
if (grammar == null) {
return;
}
for (TokenPattern pattern : grammar) {
if (pattern == ignorePattern || rematch != null && rematch.cause == pattern) {
return;
}
Node currentNode = startNode.next;
int pos = startPos;
for (;
currentNode != tokenList.tail;
pos += currentNode.value.length(), currentNode = currentNode.next
) {
if (rematch != null && pos >= rematch.reach) {
return;
}
if (tokenList.length > text.length()) {
FileLog.e("[CodeHighlighter] Something went terribly wrong, ABORT, ABORT!");
return;
}
if (currentNode.value.string == null || currentNode.value.token) {
continue;
}
String str = currentNode.value.string;
int removeCount = 1;
Match match;
if (pattern.greedy) {
match = matchPattern(pattern, pos, text);
if (match == null || match.index >= text.length()) {
break;
}
int from = match.index;
int to = match.index + match.length;
int p = pos;
p += currentNode.value.length();
while (from >= p) {
currentNode = currentNode.next;
p += currentNode.value.length();
}
p -= currentNode.value.length();
pos = p;
if (currentNode.value.string == null || currentNode.value.token) {
continue;
}
for (
Node k = currentNode;
k != tokenList.tail && (p < to || !k.value.token);
k = k.next
) {
removeCount++;
p += k.value.length();
}
removeCount--;
str = text.substring(pos, p);
match.index -= pos;
} else {
match = matchPattern(pattern, 0, str);
if (match == null) {
continue;
}
}
int from = match.index;
String before = str.substring(0, from);
String after = str.substring(from + match.length);
int reach = pos + str.length();
if (rematch != null && reach > rematch.reach) {
rematch.reach = reach;
}
Node removeFrom = currentNode.prev;
if (before.length() > 0) {
removeFrom = tokenList.addAfter(removeFrom, new StringToken(before));
pos += before.length();
}
tokenList.removeRange(removeFrom, removeCount);
StringToken wrapped;
if (pattern.insideTokenPatterns != null) {
wrapped = new StringToken(pattern.group, tokenize(match.string, pattern.insideTokenPatterns), match.length);
} else if (pattern.insideLanguage != null) {
wrapped = new StringToken(pattern.group, tokenize(match.string, compiledPatterns.get(pattern.insideLanguage), pattern), match.length);
} else {
wrapped = new StringToken(pattern.group, match.string);
}
currentNode = tokenList.addAfter(removeFrom, wrapped);
if (after.length() > 0) {
tokenList.addAfter(currentNode, new StringToken(after));
}
if (removeCount > 1) {
RematchOptions nestedRematch = new RematchOptions();
nestedRematch.cause = pattern;
nestedRematch.reach = reach;
matchGrammar(text, tokenList, grammar, currentNode.prev, pos, nestedRematch, ignorePattern);
if (rematch != null && nestedRematch.reach > rematch.reach) {
rematch.reach = nestedRematch.reach;
}
}
}
}
}
private static Match matchPattern(TokenPattern pattern, int pos, String text) {
Matcher matcher = pattern.pattern.getPattern().matcher(text);
matcher.region(pos, text.length());
if (!matcher.find()) {
return null;
}
Match match = new Match();
match.index = matcher.start();
if (pattern.lookbehind && matcher.groupCount() >= 1) {
match.index += matcher.end(1) - matcher.start(1);
}
match.length = matcher.end() - match.index;
match.string = text.substring(match.index, match.index + match.length);
return match;
}
private static class RematchOptions {
TokenPattern cause;
int reach;
}
private static class Match {
int index;
int length;
String string;
}
private static class LinkedList {
public Node head;
public Node tail;
public int length = 0;
public LinkedList() {
head = new Node();
tail = new Node();
head.next = tail;
tail.prev = head;
}
public Node addAfter(Node node, StringToken value) {
Node next = node.next;
Node newNode = new Node();
newNode.value = value;
newNode.prev = node;
newNode.next = next;
node.next = newNode;
next.prev = newNode;
length++;
return newNode;
}
public void removeRange(Node node, int count) {
Node next = node.next;
int i = 0;
for (; i < count && next != tail; ++i) {
next = next.next;
}
node.next = next;
next.prev = node;
length -= i;
}
public StringToken[] toArray() {
StringToken[] array = new StringToken[length];
Node node = head.next;
for (int i = 0; i < length && node != tail; node = node.next, ++i) {
array[i] = node.value;
}
return array;
}
}
private static class Node {
public Node prev;
public Node next;
public StringToken value;
}
private static class StringToken {
final boolean token;
final int group;
final String string;
final CodeHighlighting.LinkedList inside;
final int insideLength;
public StringToken(int group, String string) {
this.token = true;
this.group = group;
this.string = string;
this.inside = null;
this.insideLength = 0;
}
public StringToken(int group, CodeHighlighting.LinkedList inside, int len) {
this.token = true;
this.group = group;
this.string = null;
this.inside = inside;
this.insideLength = len;
}
public StringToken(String string) {
this.token = false;
this.group = -1;
this.string = string;
this.inside = null;
this.insideLength = 0;
}
public int length() {
if (string != null) {
return string.length();
}
return insideLength;
}
}
private static HashMap<String, TokenPattern[]> compiledPatterns;
private static void parse() {
GZIPInputStream zipStream = null;
BufferedInputStream bufStream = null;
InputStream stream = null;
try {
long start = System.currentTimeMillis();
stream = ApplicationLoader.applicationContext.getAssets().open("codelng.gzip");
zipStream = new GZIPInputStream(stream, 65536);
bufStream = new BufferedInputStream(zipStream, 65536);
StreamReader reader = new StreamReader(bufStream);
HashMap<Integer, String[]> languages = new HashMap<>();
int languagesCount = reader.readUint8();
for (int i = 0; i < languagesCount; ++i) {
int lngid = reader.readUint8();
int aliasesCount = reader.readUint8();
String[] aliases = new String[aliasesCount];
for (int j = 0; j < aliasesCount; ++j) {
aliases[j] = reader.readString();
}
languages.put(lngid, aliases);
}
int patternsCount = reader.readUint16();
ParsedPattern[] patterns = new ParsedPattern[patternsCount];
for (int i = 0; i < patternsCount; ++i) {
patterns[i] = new ParsedPattern();
int b = reader.readUint8();
patterns[i].multiline = (b & 1) != 0;
patterns[i].caseInsensitive = (b & 2) != 0;
patterns[i].pattern = reader.readString();
}
if (compiledPatterns == null) {
compiledPatterns = new HashMap<>();
}
for (int i = 0; i < languagesCount; ++i) {
int lngid = reader.readUint8();
TokenPattern[] tokens = readTokens(reader, patterns, languages);
String[] aliases = languages.get(lngid);
for (String alias : aliases) {
compiledPatterns.put(alias, tokens);
}
}
FileLog.d("[CodeHighlighter] Successfully read " + languagesCount + " languages, " + patternsCount + " patterns in " + (System.currentTimeMillis() - start) + "ms from codelng.gzip");
} catch (Exception e) {
FileLog.e(e);
} finally {
try {
if (zipStream != null) {
zipStream.close();
}
if (bufStream != null) {
bufStream.close();
}
if (stream != null) {
stream.close();
}
} catch (Exception e) {
FileLog.e(e);
}
}
}
private static class ParsedPattern {
String pattern;
boolean multiline;
boolean caseInsensitive;
public int flags() {
return (multiline ? Pattern.MULTILINE : 0) | (caseInsensitive ? Pattern.CASE_INSENSITIVE : 0);
}
private CachedPattern cachedPattern;
public CachedPattern getCachedPattern() {
if (cachedPattern == null) {
cachedPattern = new CachedPattern(pattern, flags());
}
return cachedPattern;
}
}
private static TokenPattern[] readTokens(StreamReader reader, ParsedPattern[] patterns, HashMap<Integer, String[]> languages) throws IOException {
int count = reader.readUint8();
TokenPattern[] tokens = new TokenPattern[count];
for (int i = 0; i < count; ++i) {
int b = reader.readUint8();
int type = b & 0x03;
int match = (b >> 2) & 0x07;
boolean greedy = (b & (1 << 5)) != 0;
boolean lookbehind = (b & (1 << 6)) != 0;
int pid = reader.readUint16();
if (type == 0) {
tokens[i] = new TokenPattern(match, patterns[pid].getCachedPattern());
} else if (type == 1) {
if (match == 0) {
tokens[i] = new TokenPattern(patterns[pid].getCachedPattern(), readTokens(reader, patterns, languages));
} else {
tokens[i] = new TokenPattern(match, patterns[pid].getCachedPattern(), readTokens(reader, patterns, languages));
}
} else if (type == 2) {
int lid = reader.readUint8();
tokens[i] = new TokenPattern(patterns[pid].getCachedPattern(), languages.get(lid)[0]);
}
if (greedy) tokens[i].greedy = true;
if (lookbehind) tokens[i].lookbehind = true;
}
return tokens;
}
private static class StreamReader {
private final InputStream is;
public StreamReader(InputStream is) {
this.is = is;
}
public int readUint8() throws IOException {
return is.read() & 0xFF;
}
public int readUint16() throws IOException {
return (is.read() & 0xFF) | ((is.read() & 0xFF) << 8);
}
public String readString() throws IOException {
int l = is.read();
if (l >= 254) {
l = is.read() | (is.read() << 8) | (is.read() << 16);
}
byte[] b = new byte[l];
for (int i = 0; i < l; ++i)
b[i] = (byte) is.read();
return new String(b, StandardCharsets.US_ASCII);
}
}
private static class TokenPattern {
public final CachedPattern pattern;
public int group = -1;
public boolean lookbehind;
public boolean greedy;
public TokenPattern[] insideTokenPatterns;
public String insideLanguage;
public TokenPattern(int group, CachedPattern pattern) {
this.pattern = pattern;
this.group = group;
}
public TokenPattern(CachedPattern pattern, TokenPattern...inside) {
this.pattern = pattern;
this.insideTokenPatterns = inside;
}
public TokenPattern(CachedPattern pattern, String lang) {
this.pattern = pattern;
this.insideLanguage = lang;
}
public TokenPattern(int group, CachedPattern pattern, TokenPattern...inside) {
this.group = group;
this.pattern = pattern;
this.insideTokenPatterns = inside;
}
}
private static class CachedPattern {
private Pattern pattern;
private String patternSource;
private int patternSourceFlags;
public CachedPattern(String pattern, int flags) {
patternSource = pattern;
patternSourceFlags = flags;
}
public Pattern getPattern() {
if (pattern == null) {
pattern = Pattern.compile(patternSource, patternSourceFlags);
}
return pattern;
}
}
}

View file

@ -27,6 +27,7 @@ import android.provider.ContactsContract;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.SparseArray; import android.util.SparseArray;
import androidx.annotation.NonNull;
import androidx.collection.LongSparseArray; import androidx.collection.LongSparseArray;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
@ -2214,7 +2215,7 @@ public class ContactsController extends BaseController {
if (systemAccount == null || user == null) { if (systemAccount == null || user == null) {
return -1; return -1;
} }
if (!hasContactsPermission()) { if (!hasContactsWritePermission()) {
return -1; return -1;
} }
long res = -1; long res = -1;
@ -2239,8 +2240,8 @@ public class ContactsController extends BaseController {
if (result != null && result.length > 0 && result[0].uri != null) { if (result != null && result.length > 0 && result[0].uri != null) {
res = Long.parseLong(result[0].uri.getLastPathSegment()); res = Long.parseLong(result[0].uri.getLastPathSegment());
} }
} catch (Exception ignore) { } catch (Exception e) {
FileLog.e(e);
} }
synchronized (observerLock) { synchronized (observerLock) {
ignoreChanges = false; ignoreChanges = false;
@ -2863,6 +2864,7 @@ public class ContactsController extends BaseController {
} }
} }
@NonNull
public static String formatName(TLRPC.User user) { public static String formatName(TLRPC.User user) {
if (user == null) { if (user == null) {
return ""; return "";
@ -2870,10 +2872,12 @@ public class ContactsController extends BaseController {
return formatName(user.first_name, user.last_name, 0); return formatName(user.first_name, user.last_name, 0);
} }
@NonNull
public static String formatName(String firstName, String lastName) { public static String formatName(String firstName, String lastName) {
return formatName(firstName, lastName, 0); return formatName(firstName, lastName, 0);
} }
@NonNull
public static String formatName(String firstName, String lastName, int maxLength) { public static String formatName(String firstName, String lastName, int maxLength) {
/*if ((firstName == null || firstName.length() == 0) && (lastName == null || lastName.length() == 0)) { /*if ((firstName == null || firstName.length() == 0) && (lastName == null || lastName.length() == 0)) {
return LocaleController.getString("HiddenName", R.string.HiddenName); return LocaleController.getString("HiddenName", R.string.HiddenName);

View file

@ -1376,6 +1376,14 @@ public class DatabaseMigrationHelper {
version = 134; version = 134;
} }
if (version == 134) {
database.executeFast("DROP TABLE user_photos").stepThis().dispose();
database.executeFast("CREATE TABLE dialog_photos(uid INTEGER, id INTEGER, num INTEGER, data BLOB, PRIMARY KEY (uid, id))").stepThis().dispose();
database.executeFast("CREATE TABLE dialog_photos_count(uid INTEGER PRIMARY KEY, count INTEGER)").stepThis().dispose();
database.executeFast("PRAGMA user_version = 135").stepThis().dispose();
version = 135;
}
return version; return version;
} }

View file

@ -535,14 +535,14 @@ public class Emoji {
} }
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, boolean createNew) { public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, boolean createNew) {
return replaceEmoji(cs, fontMetrics, AndroidUtilities.dp(16), createNew, null); return replaceEmoji(cs, fontMetrics, createNew, null);
} }
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size, boolean createNew) { public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size, boolean createNew) {
return replaceEmoji(cs, fontMetrics, size, createNew, null); return replaceEmoji(cs, fontMetrics, createNew, null);
} }
public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, int size, boolean createNew, int[] emojiOnly) { public static CharSequence replaceEmoji(CharSequence cs, Paint.FontMetricsInt fontMetrics, boolean createNew, int[] emojiOnly) {
return replaceEmoji(cs, fontMetrics, createNew, emojiOnly, DynamicDrawableSpan.ALIGN_BOTTOM); return replaceEmoji(cs, fontMetrics, createNew, emojiOnly, DynamicDrawableSpan.ALIGN_BOTTOM);
} }

View file

@ -8,15 +8,12 @@
package org.telegram.messenger; package org.telegram.messenger;
import android.os.Build;
import com.google.android.exoplayer2.util.Log;
import org.telegram.messenger.utils.ImmutableByteArrayOutputStream; import org.telegram.messenger.utils.ImmutableByteArrayOutputStream;
import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.NativeByteBuffer; import org.telegram.tgnet.NativeByteBuffer;
import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.tgnet.tl.TL_stories;
import org.telegram.ui.LaunchActivity; import org.telegram.ui.LaunchActivity;
import org.telegram.ui.Storage.CacheModel; import org.telegram.ui.Storage.CacheModel;
@ -783,7 +780,7 @@ public class FileLoadOperation {
public boolean start(final FileLoadOperationStream stream, final long streamOffset, final boolean streamPriority) { public boolean start(final FileLoadOperationStream stream, final long streamOffset, final boolean streamPriority) {
startTime = System.currentTimeMillis(); startTime = System.currentTimeMillis();
updateParams(); updateParams();
isStory = parentObject instanceof TLRPC.TL_storyItem; isStory = parentObject instanceof TL_stories.TL_storyItem;
if (currentDownloadChunkSize == 0) { if (currentDownloadChunkSize == 0) {
if (forceSmallChunk) { if (forceSmallChunk) {
if (BuildVars.LOGS_ENABLED) { if (BuildVars.LOGS_ENABLED) {

View file

@ -13,6 +13,7 @@ import android.util.SparseArray;
import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.tgnet.tl.TL_stories;
import org.telegram.ui.LaunchActivity; import org.telegram.ui.LaunchActivity;
import java.io.File; import java.io.File;
@ -69,8 +70,8 @@ public class FileLoader extends BaseController {
fileMeta.messageType = messageObject.type; fileMeta.messageType = messageObject.type;
fileMeta.messageSize = messageObject.getSize(); fileMeta.messageSize = messageObject.getSize();
return fileMeta; return fileMeta;
} else if (parentObject instanceof TLRPC.StoryItem) { } else if (parentObject instanceof TL_stories.StoryItem) {
TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject; TL_stories.StoryItem storyItem = (TL_stories.StoryItem) parentObject;
FilePathDatabase.FileMeta fileMeta = new FilePathDatabase.FileMeta(); FilePathDatabase.FileMeta fileMeta = new FilePathDatabase.FileMeta();
fileMeta.dialogId = storyItem.dialogId; fileMeta.dialogId = storyItem.dialogId;
fileMeta.messageType = MessageObject.TYPE_STORY; fileMeta.messageType = MessageObject.TYPE_STORY;
@ -824,9 +825,10 @@ public class FileLoader extends BaseController {
type = MEDIA_DIR_DOCUMENT; type = MEDIA_DIR_DOCUMENT;
} }
} }
FileLoaderPriorityQueue loaderQueue; FileLoaderPriorityQueue loaderQueue;
int index = Utilities.clamp(operation.getDatacenterId() - 1, 4, 0); int index = Utilities.clamp(operation.getDatacenterId() - 1, 4, 0);
boolean isStory = parentObject instanceof TLRPC.StoryItem; boolean isStory = parentObject instanceof TL_stories.StoryItem;
if (operation.totalBytesCount > 20 * 1024 * 1024 || isStory) { if (operation.totalBytesCount > 20 * 1024 * 1024 || isStory) {
loaderQueue = largeFilesQueue[index]; loaderQueue = largeFilesQueue[index];
} else { } else {
@ -982,15 +984,15 @@ public class FileLoader extends BaseController {
loaderQueue.checkLoadingOperations(operation.isStory && priority >= PRIORITY_HIGH); loaderQueue.checkLoadingOperations(operation.isStory && priority >= PRIORITY_HIGH);
if (BuildVars.LOGS_ENABLED) { if (BuildVars.LOGS_ENABLED) {
FileLog.d("create load operation fileName=" + finalFileName + " documentName=" + getDocumentFileName(document) + "size=" + AndroidUtilities.formatFileSize(operation.totalBytesCount) + " position in queue " + operation.getPositionInQueue() + " account=" + currentAccount); FileLog.d("create load operation fileName=" + finalFileName + " documentName=" + getDocumentFileName(document) + " size=" + AndroidUtilities.formatFileSize(operation.totalBytesCount) + " position in queue " + operation.getPositionInQueue() + " account=" + currentAccount);
} }
return operation; return operation;
} }
private boolean canSaveAsFile(Object parentObject) { public static boolean canSaveAsFile(Object parentObject) {
if (parentObject instanceof MessageObject) { if (parentObject instanceof MessageObject) {
MessageObject messageObject = (MessageObject) parentObject; MessageObject messageObject = (MessageObject) parentObject;
if (!messageObject.isDocument()) { if (!messageObject.isDocument() || messageObject.isRoundVideo() || messageObject.isVoice()) {
return false; return false;
} }
return true; return true;
@ -1169,10 +1171,6 @@ public class FileLoader extends BaseController {
} }
public File getPathToMessage(TLRPC.Message message, boolean useFileDatabaseQueue) { public File getPathToMessage(TLRPC.Message message, boolean useFileDatabaseQueue) {
return getPathToMessage(message, useFileDatabaseQueue, false);
}
public File getPathToMessage(TLRPC.Message message, boolean useFileDatabaseQueue, boolean saveAsFile) {
if (message == null) { if (message == null) {
return new File(""); return new File("");
} }
@ -1188,7 +1186,7 @@ public class FileLoader extends BaseController {
} }
} else { } else {
if (MessageObject.getMedia(message) instanceof TLRPC.TL_messageMediaDocument) { if (MessageObject.getMedia(message) instanceof TLRPC.TL_messageMediaDocument) {
return getPathToAttach(MessageObject.getMedia(message).document, null,null, MessageObject.getMedia(message).ttl_seconds != 0, useFileDatabaseQueue, saveAsFile); return getPathToAttach(MessageObject.getMedia(message).document, null, MessageObject.getMedia(message).ttl_seconds != 0, useFileDatabaseQueue);
} else if (MessageObject.getMedia(message) instanceof TLRPC.TL_messageMediaPhoto) { } else if (MessageObject.getMedia(message) instanceof TLRPC.TL_messageMediaPhoto) {
ArrayList<TLRPC.PhotoSize> sizes = MessageObject.getMedia(message).photo.sizes; ArrayList<TLRPC.PhotoSize> sizes = MessageObject.getMedia(message).photo.sizes;
if (sizes.size() > 0) { if (sizes.size() > 0) {
@ -1225,22 +1223,21 @@ public class FileLoader extends BaseController {
} }
public File getPathToAttach(TLObject attach, String ext, boolean forceCache) { public File getPathToAttach(TLObject attach, String ext, boolean forceCache) {
return getPathToAttach(attach, null, ext, forceCache, true, false); return getPathToAttach(attach, null, ext, forceCache, true);
} }
public File getPathToAttach(TLObject attach, String ext, boolean forceCache, boolean useFileDatabaseQueue) { public File getPathToAttach(TLObject attach, String ext, boolean forceCache, boolean useFileDatabaseQueue) {
return getPathToAttach(attach, null, ext, forceCache, useFileDatabaseQueue, false); return getPathToAttach(attach, null, ext, forceCache, useFileDatabaseQueue);
} }
/** /**
* Return real file name. Used before file.exist() * Return real file name. Used before file.exist()
*/ */
public File getPathToAttach(TLObject attach, String size, String ext, boolean forceCache, boolean useFileDatabaseQueue, boolean saveAsFile) { public File getPathToAttach(TLObject attach, String size, String ext, boolean forceCache, boolean useFileDatabaseQueue) {
File dir = null; File dir = null;
long documentId = 0; long documentId = 0;
int dcId = 0; int dcId = 0;
int type = 0; int type = 0;
String fileName = null;
if (forceCache) { if (forceCache) {
dir = getDirectory(MEDIA_DIR_CACHE); dir = getDirectory(MEDIA_DIR_CACHE);
} else { } else {
@ -1257,13 +1254,7 @@ public class FileLoader extends BaseController {
} else if (MessageObject.isVideoDocument(document)) { } else if (MessageObject.isVideoDocument(document)) {
type = MEDIA_DIR_VIDEO; type = MEDIA_DIR_VIDEO;
} else { } else {
String documentFileName = getDocumentFileName(document); type = MEDIA_DIR_DOCUMENT;
if (saveAsFile && !TextUtils.isEmpty(documentFileName)) {
fileName = documentFileName;
type = MEDIA_DIR_FILES;
} else {
type = MEDIA_DIR_DOCUMENT;
}
} }
} }
documentId = document.id; documentId = document.id;
@ -1334,10 +1325,7 @@ public class FileLoader extends BaseController {
return new File(path); return new File(path);
} }
} }
if (fileName == null) { return new File(dir, getAttachFileName(attach, ext));
fileName = getAttachFileName(attach, ext);
}
return new File(dir, fileName);
} }
public FilePathDatabase getFileDatabase() { public FilePathDatabase getFileDatabase() {

View file

@ -2,15 +2,14 @@ package org.telegram.messenger;
import android.os.SystemClock; import android.os.SystemClock;
import org.checkerframework.checker.units.qual.A;
import org.telegram.tgnet.RequestDelegate; import org.telegram.tgnet.RequestDelegate;
import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.tgnet.tl.TL_stories;
import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.Theme;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
public class FileRefController extends BaseController { public class FileRefController extends BaseController {
@ -70,8 +69,8 @@ public class FileRefController extends BaseController {
} }
public static String getKeyForParentObject(Object parentObject) { public static String getKeyForParentObject(Object parentObject) {
if (parentObject instanceof TLRPC.StoryItem) { if (parentObject instanceof TL_stories.StoryItem) {
TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject; TL_stories.StoryItem storyItem = (TL_stories.StoryItem) parentObject;
if (storyItem.dialogId == 0) { if (storyItem.dialogId == 0) {
FileLog.d("failed request reference can't find dialogId"); FileLog.d("failed request reference can't find dialogId");
return null; return null;
@ -134,8 +133,8 @@ public class FileRefController extends BaseController {
if (BuildVars.LOGS_ENABLED) { if (BuildVars.LOGS_ENABLED) {
FileLog.d("start loading request reference parent " + getObjectString(parentObject) + " args = " + args[0]); FileLog.d("start loading request reference parent " + getObjectString(parentObject) + " args = " + args[0]);
} }
if (args[0] instanceof TLRPC.TL_storyItem) { if (args[0] instanceof TL_stories.TL_storyItem) {
TLRPC.TL_storyItem storyItem = (TLRPC.TL_storyItem) args[0]; TL_stories.TL_storyItem storyItem = (TL_stories.TL_storyItem) args[0];
locationKey = "story_" + storyItem.id; locationKey = "story_" + storyItem.id;
location = new TLRPC.TL_inputDocumentFileLocation(); location = new TLRPC.TL_inputDocumentFileLocation();
location.id = storyItem.media.document.id; location.id = storyItem.media.document.id;
@ -326,8 +325,8 @@ public class FileRefController extends BaseController {
if (parentObject instanceof String) { if (parentObject instanceof String) {
return (String) parentObject; return (String) parentObject;
} }
if (parentObject instanceof TLRPC.StoryItem) { if (parentObject instanceof TL_stories.StoryItem) {
TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject; TL_stories.StoryItem storyItem = (TL_stories.StoryItem) parentObject;
return "story(dialogId=" + storyItem.dialogId + " id=" + storyItem.id + ")"; return "story(dialogId=" + storyItem.dialogId + " id=" + storyItem.id + ")";
} }
if (parentObject instanceof MessageObject) { if (parentObject instanceof MessageObject) {
@ -349,9 +348,9 @@ public class FileRefController extends BaseController {
} }
private void requestReferenceFromServer(Object parentObject, String locationKey, String parentKey, Object[] args) { private void requestReferenceFromServer(Object parentObject, String locationKey, String parentKey, Object[] args) {
if (parentObject instanceof TLRPC.StoryItem) { if (parentObject instanceof TL_stories.StoryItem) {
TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject; TL_stories.StoryItem storyItem = (TL_stories.StoryItem) parentObject;
TLRPC.TL_stories_getStoriesByID req = new TLRPC.TL_stories_getStoriesByID(); TL_stories.TL_stories_getStoriesByID req = new TL_stories.TL_stories_getStoriesByID();
req.peer = getMessagesController().getInputPeer(storyItem.dialogId); req.peer = getMessagesController().getInputPeer(storyItem.dialogId);
req.id.add(storyItem.id); req.id.add(storyItem.id);
getConnectionsManager().sendRequest(req, (response, error) -> { getConnectionsManager().sendRequest(req, (response, error) -> {
@ -546,8 +545,8 @@ public class FileRefController extends BaseController {
if (BuildVars.DEBUG_VERSION) { if (BuildVars.DEBUG_VERSION) {
FileLog.d("fileref updated for " + requester.args[0] + " " + requester.locationKey); FileLog.d("fileref updated for " + requester.args[0] + " " + requester.locationKey);
} }
if (requester.args[0] instanceof TLRPC.TL_storyItem) { if (requester.args[0] instanceof TL_stories.TL_storyItem) {
TLRPC.TL_storyItem storyItem = (TLRPC.TL_storyItem) requester.args[0]; TL_stories.TL_storyItem storyItem = (TL_stories.TL_storyItem) requester.args[0];
storyItem.media.document.file_reference = file_reference; storyItem.media.document.file_reference = file_reference;
return true; return true;
} else if (requester.args[0] instanceof TLRPC.TL_inputSingleMedia) { } else if (requester.args[0] instanceof TLRPC.TL_inputSingleMedia) {
@ -915,6 +914,11 @@ public class FileRefController extends BaseController {
if (result == null) { if (result == null) {
result = getFileReference(appUpdate.sticker, requester.location, needReplacement, locationReplacement); result = getFileReference(appUpdate.sticker, requester.location, needReplacement, locationReplacement);
} }
} else if (response instanceof TLRPC.TL_messages_webPage) {
TLRPC.TL_messages_webPage res = (TLRPC.TL_messages_webPage) response;
getMessagesController().putChats(res.chats, false);
getMessagesController().putUsers(res.users, false);
result = getFileReference(res.webpage, requester.location, needReplacement, locationReplacement);
} else if (response instanceof TLRPC.WebPage) { } else if (response instanceof TLRPC.WebPage) {
result = getFileReference((TLRPC.WebPage) response, requester.location, needReplacement, locationReplacement); result = getFileReference((TLRPC.WebPage) response, requester.location, needReplacement, locationReplacement);
} else if (response instanceof TLRPC.TL_account_wallPapers) { } else if (response instanceof TLRPC.TL_account_wallPapers) {
@ -1042,11 +1046,11 @@ public class FileRefController extends BaseController {
break; break;
} }
} }
} else if (response instanceof TLRPC.TL_stories_stories) { } else if (response instanceof TL_stories.TL_stories_stories) {
TLRPC.TL_stories_stories stories = (TLRPC.TL_stories_stories) response; TL_stories.TL_stories_stories stories = (TL_stories.TL_stories_stories) response;
TLRPC.StoryItem newStoryItem = null; TL_stories.StoryItem newStoryItem = null;
if (!stories.stories.isEmpty()) { if (!stories.stories.isEmpty()) {
TLRPC.StoryItem storyItem = stories.stories.get(0); TL_stories.StoryItem storyItem = stories.stories.get(0);
if (storyItem.media != null) { if (storyItem.media != null) {
newStoryItem = storyItem; newStoryItem = storyItem;
if (storyItem.media.photo != null) { if (storyItem.media.photo != null) {
@ -1060,12 +1064,12 @@ public class FileRefController extends BaseController {
Object arg = requester.args[1]; Object arg = requester.args[1];
if (arg instanceof FileLoadOperation) { if (arg instanceof FileLoadOperation) {
FileLoadOperation operation = (FileLoadOperation) requester.args[1]; FileLoadOperation operation = (FileLoadOperation) requester.args[1];
if (operation.parentObject instanceof TLRPC.StoryItem) { if (operation.parentObject instanceof TL_stories.StoryItem) {
TLRPC.StoryItem storyItem = (TLRPC.StoryItem) operation.parentObject; TL_stories.StoryItem storyItem = (TL_stories.StoryItem) operation.parentObject;
if (newStoryItem == null) { if (newStoryItem == null) {
TLRPC.TL_updateStory story = new TLRPC.TL_updateStory(); TL_stories.TL_updateStory story = new TL_stories.TL_updateStory();
story.peer = getMessagesController().getPeer(storyItem.dialogId); story.peer = getMessagesController().getPeer(storyItem.dialogId);
story.story = new TLRPC.TL_storyItemDeleted(); story.story = new TL_stories.TL_storyItemDeleted();
story.story.id = storyItem.id; story.story.id = storyItem.id;
ArrayList<TLRPC.Update> updates = new ArrayList<>(); ArrayList<TLRPC.Update> updates = new ArrayList<>();
updates.add(story); updates.add(story);
@ -1077,7 +1081,7 @@ public class FileRefController extends BaseController {
} }
} }
if (newStoryItem != null && result == null) { if (newStoryItem != null && result == null) {
TLRPC.TL_updateStory updateStory = new TLRPC.TL_updateStory(); TL_stories.TL_updateStory updateStory = new TL_stories.TL_updateStory();
updateStory.peer = MessagesController.getInstance(currentAccount).getPeer(storyItem.dialogId); updateStory.peer = MessagesController.getInstance(currentAccount).getPeer(storyItem.dialogId);
updateStory.story = newStoryItem; updateStory.story = newStoryItem;
ArrayList<TLRPC.Update> updates = new ArrayList<>(); ArrayList<TLRPC.Update> updates = new ArrayList<>();

View file

@ -1,193 +0,0 @@
package org.telegram.messenger;
import android.text.TextUtils;
import android.util.LongSparseArray;
import android.util.SparseBooleanArray;
import org.telegram.tgnet.TLRPC;
import java.util.ArrayList;
public class ForwardingMessagesParams {
public LongSparseArray<MessageObject.GroupedMessages> groupedMessagesMap = new LongSparseArray<>();
public ArrayList<MessageObject> messages;
public ArrayList<MessageObject> previewMessages = new ArrayList<>();
public SparseBooleanArray selectedIds = new SparseBooleanArray();
public boolean hideForwardSendersName;
public boolean hideCaption;
public boolean hasCaption;
public boolean hasSenders;
public boolean isSecret;
public boolean willSeeSenders;
public boolean multiplyUsers;
public boolean hasSpoilers;
public ArrayList<TLRPC.TL_pollAnswerVoters> pollChoosenAnswers = new ArrayList<>();
public ForwardingMessagesParams(ArrayList<MessageObject> messages, long newDialogId) {
this.messages = messages;
hasCaption = false;
hasSenders = false;
isSecret = DialogObject.isEncryptedDialog(newDialogId);
hasSpoilers = false;
ArrayList<String> hiddenSendersName = new ArrayList<>();
for (int i = 0; i < messages.size(); i++) {
MessageObject messageObject = messages.get(i);
if (!TextUtils.isEmpty(messageObject.caption)) {
hasCaption = true;
}
selectedIds.put(messageObject.getId(), true);
TLRPC.Message message = new TLRPC.TL_message();
message.id = messageObject.messageOwner.id;
message.grouped_id = messageObject.messageOwner.grouped_id;
message.peer_id = messageObject.messageOwner.peer_id;
message.from_id = messageObject.messageOwner.from_id;
message.message = messageObject.messageOwner.message;
message.media = messageObject.messageOwner.media;
message.action = messageObject.messageOwner.action;
message.edit_date = 0;
if (messageObject.messageOwner.entities != null) {
message.entities.addAll(messageObject.messageOwner.entities);
if (!hasSpoilers) {
for (TLRPC.MessageEntity e : message.entities) {
if (e instanceof TLRPC.TL_messageEntitySpoiler) {
hasSpoilers = true;
break;
}
}
}
}
message.out = true;
message.unread = false;
message.via_bot_id = messageObject.messageOwner.via_bot_id;
message.reply_markup = messageObject.messageOwner.reply_markup;
message.post = messageObject.messageOwner.post;
message.legacy = messageObject.messageOwner.legacy;
message.restriction_reason = messageObject.messageOwner.restriction_reason;
message.replyMessage = messageObject.messageOwner.replyMessage;
TLRPC.MessageFwdHeader header = null;
long clientUserId = UserConfig.getInstance(messageObject.currentAccount).clientUserId;
if (!isSecret) {
if (messageObject.messageOwner.fwd_from != null) {
header = messageObject.messageOwner.fwd_from;
if (!messageObject.isDice()) {
hasSenders = true;
} else {
willSeeSenders = true;
}
if (header.from_id == null && !hiddenSendersName.contains(header.from_name)) {
hiddenSendersName.add(header.from_name);
}
} else if (messageObject.messageOwner.from_id.user_id == 0 || messageObject.messageOwner.dialog_id != clientUserId || messageObject.messageOwner.from_id.user_id != clientUserId) {
header = new TLRPC.TL_messageFwdHeader();
header.from_id = messageObject.messageOwner.from_id;
if (!messageObject.isDice()) {
hasSenders = true;
} else {
willSeeSenders = true;
}
}
}
if (header != null) {
message.fwd_from = header;
message.flags |= TLRPC.MESSAGE_FLAG_FWD;
}
message.dialog_id = newDialogId;
MessageObject previewMessage = new MessageObject(messageObject.currentAccount, message, true, false) {
@Override
public boolean needDrawForwarded() {
if (hideForwardSendersName) {
return false;
}
return super.needDrawForwarded();
}
};
previewMessage.preview = true;
if (previewMessage.getGroupId() != 0) {
MessageObject.GroupedMessages groupedMessages = groupedMessagesMap.get(previewMessage.getGroupId(), null);
if (groupedMessages == null) {
groupedMessages = new MessageObject.GroupedMessages();
groupedMessagesMap.put(previewMessage.getGroupId(), groupedMessages);
}
groupedMessages.messages.add(previewMessage);
}
previewMessages.add(0, previewMessage);
if (messageObject.isPoll()) {
TLRPC.TL_messageMediaPoll mediaPoll = (TLRPC.TL_messageMediaPoll) messageObject.messageOwner.media;
PreviewMediaPoll newMediaPoll = new PreviewMediaPoll();
newMediaPoll.poll = mediaPoll.poll;
newMediaPoll.provider = mediaPoll.provider;
newMediaPoll.results = new TLRPC.TL_pollResults();
newMediaPoll.totalVotersCached = newMediaPoll.results.total_voters = mediaPoll.results.total_voters;
previewMessage.messageOwner.media = newMediaPoll;
if (messageObject.canUnvote()) {
for (int a = 0, N = mediaPoll.results.results.size(); a < N; a++) {
TLRPC.TL_pollAnswerVoters answer = mediaPoll.results.results.get(a);
if (answer.chosen) {
TLRPC.TL_pollAnswerVoters newAnswer = new TLRPC.TL_pollAnswerVoters();
newAnswer.chosen = answer.chosen;
newAnswer.correct = answer.correct;
newAnswer.flags = answer.flags;
newAnswer.option = answer.option;
newAnswer.voters = answer.voters;
pollChoosenAnswers.add(newAnswer);
newMediaPoll.results.results.add(newAnswer);
} else {
newMediaPoll.results.results.add(answer);
}
}
}
}
}
ArrayList<Long> uids = new ArrayList<>();
for (int a = 0; a < messages.size(); a++) {
MessageObject object = messages.get(a);
long uid;
if (object.isFromUser()) {
uid = object.messageOwner.from_id.user_id;
} else {
TLRPC.Chat chat = MessagesController.getInstance(object.currentAccount).getChat(object.messageOwner.peer_id.channel_id);
if (ChatObject.isChannel(chat) && chat.megagroup && object.isForwardedChannelPost()) {
uid = -object.messageOwner.fwd_from.from_id.channel_id;
} else {
uid = -object.messageOwner.peer_id.channel_id;
}
}
if (!uids.contains(uid)) {
uids.add(uid);
}
}
if (uids.size() + hiddenSendersName.size() > 1) {
multiplyUsers = true;
}
for (int i = 0; i < groupedMessagesMap.size(); i++) {
groupedMessagesMap.valueAt(i).calculate();
}
}
public void getSelectedMessages(ArrayList<MessageObject> messagesToForward) {
messagesToForward.clear();
for (int i = 0; i < messages.size(); i++) {
MessageObject messageObject = messages.get(i);
int id = messageObject.getId();
if (selectedIds.get(id, false)) {
messagesToForward.add(messageObject);
}
}
}
public class PreviewMediaPoll extends TLRPC.TL_messageMediaPoll {
public int totalVotersCached;
}
}

View file

@ -32,6 +32,7 @@ import android.os.Environment;
import android.os.SystemClock; import android.os.SystemClock;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.util.SparseArray; import android.util.SparseArray;

View file

@ -2302,7 +2302,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
} }
} }
public void setImageX(int x) { public void setImageX(float x) {
imageX = x; imageX = x;
} }

View file

@ -108,11 +108,12 @@ public class LiteMode {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public static int getBatteryLevel() { public static int getBatteryLevel() {
if (lastBatteryLevelCached < 0 || System.currentTimeMillis() - lastBatteryLevelChecked > 1000 * 12) { long time = 0;
if (lastBatteryLevelCached < 0 || (time = System.currentTimeMillis()) - lastBatteryLevelChecked > 1000 * 12) {
BatteryManager batteryManager = (BatteryManager) ApplicationLoader.applicationContext.getSystemService(Context.BATTERY_SERVICE); BatteryManager batteryManager = (BatteryManager) ApplicationLoader.applicationContext.getSystemService(Context.BATTERY_SERVICE);
if (batteryManager != null) { if (batteryManager != null) {
lastBatteryLevelCached = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY); lastBatteryLevelCached = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
lastBatteryLevelChecked = System.currentTimeMillis(); lastBatteryLevelChecked = time;
} }
} }
return lastBatteryLevelCached; return lastBatteryLevelCached;

View file

@ -70,6 +70,10 @@ public class LocaleController {
public FastDateFormat formatterScheduleDay; public FastDateFormat formatterScheduleDay;
public FastDateFormat formatterScheduleYear; public FastDateFormat formatterScheduleYear;
public FastDateFormat formatterMonthYear; public FastDateFormat formatterMonthYear;
public FastDateFormat formatterGiveawayCard;
public FastDateFormat formatterBoostExpired;
public FastDateFormat formatterGiveawayMonthDay;
public FastDateFormat formatterGiveawayMonthDayYear;
public FastDateFormat[] formatterScheduleSend = new FastDateFormat[15]; public FastDateFormat[] formatterScheduleSend = new FastDateFormat[15];
private static HashMap<Integer, String> resourcesCacheMap = new HashMap<>(); private static HashMap<Integer, String> resourcesCacheMap = new HashMap<>();
@ -1908,6 +1912,10 @@ public class LocaleController {
|| currentLocaleInfo != null && currentLocaleInfo.isRtl; || currentLocaleInfo != null && currentLocaleInfo.isRtl;
nameDisplayOrder = lang.equals("ko") ? 2 : 1; nameDisplayOrder = lang.equals("ko") ? 2 : 1;
formatterBoostExpired = createFormatter(locale, getStringInternal("formatterBoostExpired", R.string.formatterBoostExpired), "MMM dd, yyyy");
formatterGiveawayCard = createFormatter(locale, getStringInternal("formatterGiveawayCard", R.string.formatterGiveawayCard), "dd MMM yyyy");
formatterGiveawayMonthDay = createFormatter(locale, getStringInternal("formatterGiveawayMonthDay", R.string.formatterGiveawayMonthDay), "MMMM dd");
formatterGiveawayMonthDayYear = createFormatter(locale, getStringInternal("formatterGiveawayMonthDayYear", R.string.formatterGiveawayMonthDayYear), "MMMM dd, yyyy");
formatterMonthYear = createFormatter(locale, getStringInternal("formatterMonthYear", R.string.formatterMonthYear), "MMM yyyy"); formatterMonthYear = createFormatter(locale, getStringInternal("formatterMonthYear", R.string.formatterMonthYear), "MMM yyyy");
formatterDayMonth = createFormatter(locale, getStringInternal("formatterMonth", R.string.formatterMonth), "dd MMM"); formatterDayMonth = createFormatter(locale, getStringInternal("formatterMonth", R.string.formatterMonth), "dd MMM");
formatterYear = createFormatter(locale, getStringInternal("formatterYear", R.string.formatterYear), "dd.MM.yy"); formatterYear = createFormatter(locale, getStringInternal("formatterYear", R.string.formatterYear), "dd.MM.yy");

View file

@ -74,6 +74,7 @@ import org.telegram.tgnet.AbstractSerializedData;
import org.telegram.tgnet.ConnectionsManager; import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.tgnet.tl.TL_stories;
import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.Theme;
@ -129,6 +130,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return false; return false;
} }
public VideoConvertMessage getCurrentForegroundConverMessage() {
return currentForegroundConvertingVideo;
}
private static class AudioBuffer { private static class AudioBuffer {
public AudioBuffer(int capacity) { public AudioBuffer(int capacity) {
buffer = ByteBuffer.allocateDirect(capacity); buffer = ByteBuffer.allocateDirect(capacity);
@ -490,6 +495,21 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
this.hasSpoiler = state instanceof PhotoEntry && ((PhotoEntry) state).hasSpoiler; this.hasSpoiler = state instanceof PhotoEntry && ((PhotoEntry) state).hasSpoiler;
} }
public PhotoEntry clone() {
PhotoEntry photoEntry = new PhotoEntry(bucketId, imageId, dateTaken, path, isVideo ? duration : orientation, isVideo, width, height, size);
photoEntry.invert = invert;
photoEntry.isMuted = isMuted;
photoEntry.canDeleteAfter = canDeleteAfter;
photoEntry.hasSpoiler = hasSpoiler;
photoEntry.isChatPreviewSpoilerRevealed = isChatPreviewSpoilerRevealed;
photoEntry.isAttachSpoilerRevealed = isAttachSpoilerRevealed;
photoEntry.emojiMarkup = emojiMarkup;
photoEntry.gradientTopColor = gradientTopColor;
photoEntry.gradientBottomColor = gradientBottomColor;
photoEntry.copyFrom(this);
return photoEntry;
}
@Override @Override
public String getPath() { public String getPath() {
return path; return path;
@ -559,6 +579,26 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return imageUrl; return imageUrl;
} }
} }
public SearchImage clone() {
SearchImage searchImage = new SearchImage();
searchImage.id = id;
searchImage.imageUrl = imageUrl;
searchImage.thumbUrl = thumbUrl;
searchImage.width = width;
searchImage.height = height;
searchImage.size = size;
searchImage.type = type;
searchImage.date = date;
searchImage.caption = caption;
searchImage.document = document;
searchImage.photo = photo;
searchImage.photoSize = photoSize;
searchImage.thumbPhotoSize = thumbPhotoSize;
searchImage.inlineResult = inlineResult;
searchImage.params = params;
return searchImage;
}
} }
AudioManager.OnAudioFocusChangeListener audioRecordFocusChangedListener = focusChange -> { AudioManager.OnAudioFocusChangeListener audioRecordFocusChangedListener = focusChange -> {
@ -620,7 +660,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private static final int AUDIO_FOCUSED = 2; private static final int AUDIO_FOCUSED = 2;
private static final ConcurrentHashMap<String, Integer> cachedEncoderBitrates = new ConcurrentHashMap<>(); private static final ConcurrentHashMap<String, Integer> cachedEncoderBitrates = new ConcurrentHashMap<>();
private static class VideoConvertMessage { private ArrayList<VideoConvertMessage> foregroundConvertingMessages = new ArrayList<>();
private VideoConvertMessage currentForegroundConvertingVideo;
public static class VideoConvertMessage {
public MessageObject messageObject; public MessageObject messageObject;
public VideoEditedInfo videoEditedInfo; public VideoEditedInfo videoEditedInfo;
public int currentAccount; public int currentAccount;
@ -723,7 +766,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
private long recordDialogId; private long recordDialogId;
private MessageObject recordReplyingMsg; private MessageObject recordReplyingMsg;
private MessageObject recordReplyingTopMsg; private MessageObject recordReplyingTopMsg;
private TLRPC.StoryItem recordReplyingStory; private TL_stories.StoryItem recordReplyingStory;
private short[] recordSamples = new short[1024]; private short[] recordSamples = new short[1024];
private long samplesCount; private long samplesCount;
@ -3743,7 +3786,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
return downloadingCurrentMessage; return downloadingCurrentMessage;
} }
public void setReplyingMessage(MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem) { public void setReplyingMessage(MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem storyItem) {
recordReplyingMsg = replyToMsg; recordReplyingMsg = replyToMsg;
recordReplyingTopMsg = replyToTopMsg; recordReplyingTopMsg = replyToTopMsg;
recordReplyingStory = storyItem; recordReplyingStory = storyItem;
@ -3765,7 +3808,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
} }
public void startRecording(int currentAccount, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem replyStory, int guid, boolean manual) { public void startRecording(int currentAccount, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem replyStory, int guid, boolean manual) {
boolean paused = false; boolean paused = false;
if (playingMessageObject != null && isPlayingMessage(playingMessageObject) && !isMessagePaused()) { if (playingMessageObject != null && isPlayingMessage(playingMessageObject) && !isMessagePaused()) {
paused = true; paused = true;
@ -4068,7 +4111,18 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
} }
if (path == null || path.length() == 0) { if (path == null || path.length() == 0) {
path = FileLoader.getInstance(currentAccount.getCurrentAccount()).getPathToMessage(message.messageOwner, true, !isMusic).toString(); path = null;
TLRPC.Document document = message.getDocument();
if (!TextUtils.isEmpty(FileLoader.getDocumentFileName(document)) && FileLoader.canSaveAsFile(message)) {
String filename = FileLoader.getDocumentFileName(document);
File newDir = FileLoader.getDirectory(FileLoader.MEDIA_DIR_FILES);
if (newDir != null) {
path = new File(newDir, filename).getAbsolutePath();
}
}
if (path == null) {
path = FileLoader.getInstance(currentAccount.getCurrentAccount()).getPathToMessage(message.messageOwner).toString();
}
} }
File sourceFile = new File(path); File sourceFile = new File(path);
if (!sourceFile.exists()) { if (!sourceFile.exists()) {
@ -4122,7 +4176,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
} }
if (path == null || path.length() == 0) { if (path == null || path.length() == 0) {
path = FileLoader.getInstance(currentAccount.getCurrentAccount()).getPathToMessage(message.messageOwner, true, !isMusic).toString(); path = FileLoader.getInstance(currentAccount.getCurrentAccount()).getPathToMessage(message.messageOwner).toString();
} }
File sourceFile = new File(path); File sourceFile = new File(path);
if (!sourceFile.exists()) { if (!sourceFile.exists()) {
@ -4996,7 +5050,12 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} else if (isEmpty) { } else if (isEmpty) {
new File(messageObject.messageOwner.attachPath).delete(); new File(messageObject.messageOwner.attachPath).delete();
} }
videoConvertQueue.add(new VideoConvertMessage(messageObject, messageObject.videoEditedInfo, withForeground)); VideoConvertMessage videoConvertMessage = new VideoConvertMessage(messageObject, messageObject.videoEditedInfo, withForeground);
videoConvertQueue.add(videoConvertMessage);
if (videoConvertMessage.foreground) {
foregroundConvertingMessages.add(videoConvertMessage);
checkForegroundConvertMessage(false);
}
if (videoConvertQueue.size() == 1) { if (videoConvertQueue.size() == 1) {
startVideoConvertFromQueue(); startVideoConvertFromQueue();
} }
@ -5015,7 +5074,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
videoConvertMessage.videoEditedInfo.canceled = true; videoConvertMessage.videoEditedInfo.canceled = true;
} }
} else { } else {
videoConvertQueue.remove(a); VideoConvertMessage convertMessage = videoConvertQueue.remove(a);
foregroundConvertingMessages.remove(convertMessage);
checkForegroundConvertMessage(true);
} }
break; break;
} }
@ -5024,6 +5085,17 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
} }
private void checkForegroundConvertMessage(boolean cancelled) {
if (!foregroundConvertingMessages.isEmpty()) {
currentForegroundConvertingVideo = foregroundConvertingMessages.get(0);
} else {
currentForegroundConvertingVideo = null;
}
if (currentForegroundConvertingVideo != null || cancelled) {
VideoEncodingService.start(cancelled);
}
}
private boolean startVideoConvertFromQueue() { private boolean startVideoConvertFromQueue() {
if (!videoConvertQueue.isEmpty()) { if (!videoConvertQueue.isEmpty()) {
VideoConvertMessage videoConvertMessage = videoConvertQueue.get(0); VideoConvertMessage videoConvertMessage = videoConvertQueue.get(0);
@ -5033,28 +5105,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
videoEditedInfo.canceled = false; videoEditedInfo.canceled = false;
} }
} }
if (videoConvertMessage.foreground) {
MessageObject messageObject = videoConvertMessage.messageObject;
Intent intent = new Intent(ApplicationLoader.applicationContext, VideoEncodingService.class);
intent.putExtra("path", messageObject.messageOwner.attachPath);
intent.putExtra("currentAccount", messageObject.currentAccount);
if (messageObject.messageOwner.media != null && messageObject.messageOwner.media.document != null) {
for (int a = 0; a < messageObject.messageOwner.media.document.attributes.size(); a++) {
TLRPC.DocumentAttribute documentAttribute = messageObject.messageOwner.media.document.attributes.get(a);
if (documentAttribute instanceof TLRPC.TL_documentAttributeAnimated) {
intent.putExtra("gif", true);
break;
}
}
}
if (messageObject.getId() != 0) {
try {
ApplicationLoader.applicationContext.startService(intent);
} catch (Throwable e) {
FileLog.e(e);
}
}
}
VideoConvertRunnable.runConversion(videoConvertMessage); VideoConvertRunnable.runConversion(videoConvertMessage);
return true; return true;
} }
@ -5156,10 +5206,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
} }
AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.runOnUIThread(() -> {
if (error || last) { if (error || last) {
boolean cancelled = message.videoEditedInfo.canceled;
synchronized (videoConvertSync) { synchronized (videoConvertSync) {
message.videoEditedInfo.canceled = false; message.videoEditedInfo.canceled = false;
} }
videoConvertQueue.remove(message); videoConvertQueue.remove(message);
foregroundConvertingMessages.remove(message);
checkForegroundConvertMessage(cancelled || error);
startVideoConvertFromQueue(); startVideoConvertFromQueue();
} }
if (error) { if (error) {

View file

@ -60,11 +60,13 @@ import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.EmojiThemes; import org.telegram.ui.ActionBar.EmojiThemes;
import org.telegram.ui.ChatActivity;
import org.telegram.ui.Components.AnimatedEmojiDrawable; import org.telegram.ui.Components.AnimatedEmojiDrawable;
import org.telegram.ui.Components.AnimatedEmojiSpan; import org.telegram.ui.Components.AnimatedEmojiSpan;
import org.telegram.ui.Components.AvatarDrawable; import org.telegram.ui.Components.AvatarDrawable;
import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.Bulletin;
import org.telegram.ui.Components.ChatThemeBottomSheet; import org.telegram.ui.Components.ChatThemeBottomSheet;
import org.telegram.ui.Components.QuoteSpan;
import org.telegram.ui.Components.StickerSetBulletinLayout; import org.telegram.ui.Components.StickerSetBulletinLayout;
import org.telegram.ui.Components.StickersArchiveAlert; import org.telegram.ui.Components.StickersArchiveAlert;
import org.telegram.ui.Components.TextStyleSpan; import org.telegram.ui.Components.TextStyleSpan;
@ -5444,6 +5446,7 @@ public class MediaDataController extends BaseController {
object.replyMessageObject = messageObject; object.replyMessageObject = messageObject;
object.applyTimestampsHighlightForReplyMsg(); object.applyTimestampsHighlightForReplyMsg();
object.messageOwner.reply_to = new TLRPC.TL_messageReplyHeader(); object.messageOwner.reply_to = new TLRPC.TL_messageReplyHeader();
object.messageOwner.reply_to.flags |= 16;
object.messageOwner.reply_to.reply_to_msg_id = messageObject.getId(); object.messageOwner.reply_to.reply_to_msg_id = messageObject.getId();
} }
} }
@ -6137,8 +6140,6 @@ public class MediaDataController extends BaseController {
newRun.flags = TextStyleSpan.FLAG_STYLE_STRIKE; newRun.flags = TextStyleSpan.FLAG_STYLE_STRIKE;
} else if (entity instanceof TLRPC.TL_messageEntityUnderline) { } else if (entity instanceof TLRPC.TL_messageEntityUnderline) {
newRun.flags = TextStyleSpan.FLAG_STYLE_UNDERLINE; newRun.flags = TextStyleSpan.FLAG_STYLE_UNDERLINE;
} else if (entity instanceof TLRPC.TL_messageEntityBlockquote) {
newRun.flags = TextStyleSpan.FLAG_STYLE_QUOTE;
} else if (entity instanceof TLRPC.TL_messageEntityBold) { } else if (entity instanceof TLRPC.TL_messageEntityBold) {
newRun.flags = TextStyleSpan.FLAG_STYLE_BOLD; newRun.flags = TextStyleSpan.FLAG_STYLE_BOLD;
} else if (entity instanceof TLRPC.TL_messageEntityItalic) { } else if (entity instanceof TLRPC.TL_messageEntityItalic) {
@ -6241,8 +6242,6 @@ public class MediaDataController extends BaseController {
entities.add(setEntityStartEnd(new TLRPC.TL_messageEntityStrike(), spanStart, spanEnd)); entities.add(setEntityStartEnd(new TLRPC.TL_messageEntityStrike(), spanStart, spanEnd));
if ((flags & TextStyleSpan.FLAG_STYLE_UNDERLINE) != 0) if ((flags & TextStyleSpan.FLAG_STYLE_UNDERLINE) != 0)
entities.add(setEntityStartEnd(new TLRPC.TL_messageEntityUnderline(), spanStart, spanEnd)); entities.add(setEntityStartEnd(new TLRPC.TL_messageEntityUnderline(), spanStart, spanEnd));
if ((flags & TextStyleSpan.FLAG_STYLE_QUOTE) != 0)
entities.add(setEntityStartEnd(new TLRPC.TL_messageEntityBlockquote(), spanStart, spanEnd));
} }
private TLRPC.MessageEntity setEntityStartEnd(TLRPC.MessageEntity entity, int spanStart, int spanEnd) { private TLRPC.MessageEntity setEntityStartEnd(TLRPC.MessageEntity entity, int spanStart, int spanEnd) {
@ -6282,8 +6281,16 @@ public class MediaDataController extends BaseController {
if (isPre) { if (isPre) {
int firstChar = start > 0 ? message[0].charAt(start - 1) : 0; int firstChar = start > 0 ? message[0].charAt(start - 1) : 0;
boolean replacedFirst = firstChar == ' ' || firstChar == '\n'; boolean replacedFirst = firstChar == ' ' || firstChar == '\n';
String language = "";
int firstNewLine = TextUtils.indexOf(message[0], '\n', start + 3);
if (firstNewLine >= 0 && firstNewLine - (start + 3) > 0) {
language = message[0].toString().substring(start + 3, firstNewLine);
}
CharSequence startMessage = substring(message[0], 0, start - (replacedFirst ? 1 : 0)); CharSequence startMessage = substring(message[0], 0, start - (replacedFirst ? 1 : 0));
CharSequence content = substring(message[0], start + 3, index); int contentStart = start + 3 + language.length() + (!language.isEmpty() ? 1 : 0);
if (contentStart < 0 || contentStart >= message[0].length() || contentStart > index)
continue;
CharSequence content = substring(message[0], contentStart, index);
firstChar = index + 3 < message[0].length() ? message[0].charAt(index + 3) : 0; firstChar = index + 3 < message[0].length() ? message[0].charAt(index + 3) : 0;
CharSequence endMessage = substring(message[0], index + 3 + (firstChar == ' ' || firstChar == '\n' ? 1 : 0), message[0].length()); CharSequence endMessage = substring(message[0], index + 3 + (firstChar == ' ' || firstChar == '\n' ? 1 : 0), message[0].length());
if (startMessage.length() != 0) { if (startMessage.length() != 0) {
@ -6291,15 +6298,18 @@ public class MediaDataController extends BaseController {
} else { } else {
replacedFirst = true; replacedFirst = true;
} }
if (endMessage.length() != 0) { if (endMessage.length() > 0 && endMessage.charAt(0) != '\n') {
endMessage = AndroidUtilities.concat("\n", endMessage); endMessage = AndroidUtilities.concat("\n", endMessage);
} }
if (content.length() > 0 && content.charAt(content.length() - 1) == '\n') {
content = substring(content, 0, content.length() - 1);
}
if (!TextUtils.isEmpty(content)) { if (!TextUtils.isEmpty(content)) {
message[0] = AndroidUtilities.concat(startMessage, content, endMessage); message[0] = AndroidUtilities.concat(startMessage, content, endMessage);
TLRPC.TL_messageEntityPre entity = new TLRPC.TL_messageEntityPre(); TLRPC.TL_messageEntityPre entity = new TLRPC.TL_messageEntityPre();
entity.offset = start + (replacedFirst ? 0 : 1); entity.offset = start + (replacedFirst ? 0 : 1);
entity.length = index - start - 3 + (replacedFirst ? 0 : 1); entity.length = index - start - 3 - (language.length() + (!language.isEmpty() ? 1 : 0)) + (replacedFirst ? 0 : 1);
entity.language = ""; entity.language = language;
entities.add(entity); entities.add(entity);
lastIndex -= 6; lastIndex -= 6;
} }
@ -6405,6 +6415,47 @@ public class MediaDataController extends BaseController {
} }
} }
CodeHighlighting.Span[] codeSpans = spannable.getSpans(0, message[0].length(), CodeHighlighting.Span.class);
if (codeSpans != null && codeSpans.length > 0) {
if (entities == null) {
entities = new ArrayList<>();
}
for (int b = 0; b < codeSpans.length; ++b) {
CodeHighlighting.Span span = codeSpans[b];
if (span != null) {
try {
TLRPC.TL_messageEntityPre entity = new TLRPC.TL_messageEntityPre();
entity.offset = spannable.getSpanStart(span);
entity.length = Math.min(spannable.getSpanEnd(span), message[0].length()) - entity.offset;
entity.language = span.lng;
entities.add(entity);
} catch (Exception e) {
FileLog.e(e);
}
}
}
}
QuoteSpan[] quoteSpans = spannable.getSpans(0, message[0].length(), QuoteSpan.class);
if (quoteSpans != null && quoteSpans.length > 0) {
if (entities == null) {
entities = new ArrayList<>();
}
for (int b = 0; b < quoteSpans.length; ++b) {
QuoteSpan span = quoteSpans[b];
if (span != null) {
try {
TLRPC.TL_messageEntityBlockquote entity = new TLRPC.TL_messageEntityBlockquote();
entity.offset = spannable.getSpanStart(span);
entity.length = Math.min(spannable.getSpanEnd(span), message[0].length()) - entity.offset;
entities.add(entity);
} catch (Exception e) {
FileLog.e(e);
}
}
}
}
if (spannable instanceof Spannable) { if (spannable instanceof Spannable) {
AndroidUtilities.addLinks((Spannable) spannable, Linkify.WEB_URLS, false, false); AndroidUtilities.addLinks((Spannable) spannable, Linkify.WEB_URLS, false, false);
URLSpan[] spansUrl = spannable.getSpans(0, message[0].length(), URLSpan.class); URLSpan[] spansUrl = spannable.getSpans(0, message[0].length(), URLSpan.class);
@ -6452,6 +6503,17 @@ public class MediaDataController extends BaseController {
allowEntity = false; allowEntity = false;
} }
} }
if (allowEntity) {
// check if it is inside a code block: do not convert __ ** || to styles inside code
for (int i = 0; i < entities.size(); ++i) {
if (entities.get(i) instanceof TLRPC.TL_messageEntityPre) {
if (AndroidUtilities.intersect1d(m.start() - offset, m.end() - offset, entities.get(i).offset, entities.get(i).offset + entities.get(i).length)) {
allowEntity = false;
break;
}
}
}
}
if (allowEntity) { if (allowEntity) {
cs = cs.subSequence(0, m.start() - offset) + gr + cs.subSequence(m.end() - offset, cs.length()); cs = cs.subSequence(0, m.start() - offset) + gr + cs.subSequence(m.end() - offset, cs.length());
@ -6556,10 +6618,10 @@ public class MediaDataController extends BaseController {
} }
public void saveDraft(long dialogId, int threadId, CharSequence message, ArrayList<TLRPC.MessageEntity> entities, TLRPC.Message replyToMessage, boolean noWebpage) { public void saveDraft(long dialogId, int threadId, CharSequence message, ArrayList<TLRPC.MessageEntity> entities, TLRPC.Message replyToMessage, boolean noWebpage) {
saveDraft(dialogId, threadId, message, entities, replyToMessage, noWebpage, false); saveDraft(dialogId, threadId, message, entities, replyToMessage, null, noWebpage, false);
} }
public void saveDraft(long dialogId, int threadId, CharSequence message, ArrayList<TLRPC.MessageEntity> entities, TLRPC.Message replyToMessage, boolean noWebpage, boolean clean) { public void saveDraft(long dialogId, int threadId, CharSequence message, ArrayList<TLRPC.MessageEntity> entities, TLRPC.Message replyToMessage, ChatActivity.ReplyQuote quote, boolean noWebpage, boolean clean) {
TLRPC.DraftMessage draftMessage; TLRPC.DraftMessage draftMessage;
if (getMessagesController().isForum(dialogId) && threadId == 0) { if (getMessagesController().isForum(dialogId) && threadId == 0) {
replyToMessage = null; replyToMessage = null;
@ -6573,8 +6635,28 @@ public class MediaDataController extends BaseController {
draftMessage.message = message == null ? "" : message.toString(); draftMessage.message = message == null ? "" : message.toString();
draftMessage.no_webpage = noWebpage; draftMessage.no_webpage = noWebpage;
if (replyToMessage != null) { if (replyToMessage != null) {
draftMessage.reply_to_msg_id = replyToMessage.id; draftMessage.reply_to = new TLRPC.TL_inputReplyToMessage();
draftMessage.flags |= 1; draftMessage.flags |= 16;
draftMessage.reply_to.reply_to_msg_id = replyToMessage.id;
if (quote != null) {
draftMessage.reply_to.quote_text = quote.getText();
if (draftMessage.reply_to.quote_text != null) {
draftMessage.reply_to.flags |= 64;
}
draftMessage.reply_to.quote_entities = quote.getEntities();
if (draftMessage.reply_to.quote_entities != null && !draftMessage.reply_to.quote_entities.isEmpty()) {
draftMessage.reply_to.quote_entities = new ArrayList<>(draftMessage.reply_to.quote_entities);
draftMessage.reply_to.flags |= 128;
}
if (quote.message != null && quote.message.messageOwner != null) {
TLRPC.Peer peer2 = getMessagesController().getPeer(dialogId);
TLRPC.Peer thisPeer = quote.message.messageOwner.peer_id;
if (peer2 != null && !MessageObject.peersEqual(peer2, thisPeer)) {
draftMessage.reply_to.flags |= 1;
draftMessage.reply_to.reply_to_peer_id = getMessagesController().getInputPeer(peer2);
}
}
}
} }
if (entities != null && !entities.isEmpty()) { if (entities != null && !entities.isEmpty()) {
draftMessage.entities = entities; draftMessage.entities = entities;
@ -6584,8 +6666,10 @@ public class MediaDataController extends BaseController {
SparseArray<TLRPC.DraftMessage> threads = drafts.get(dialogId); SparseArray<TLRPC.DraftMessage> threads = drafts.get(dialogId);
TLRPC.DraftMessage currentDraft = threads == null ? null : threads.get(threadId); TLRPC.DraftMessage currentDraft = threads == null ? null : threads.get(threadId);
if (!clean) { if (!clean) {
if (currentDraft != null && currentDraft.message.equals(draftMessage.message) && currentDraft.reply_to_msg_id == draftMessage.reply_to_msg_id && currentDraft.no_webpage == draftMessage.no_webpage || if (
currentDraft == null && TextUtils.isEmpty(draftMessage.message) && draftMessage.reply_to_msg_id == 0) { currentDraft != null && currentDraft.message.equals(draftMessage.message) && replyToEquals(currentDraft.reply_to, draftMessage.reply_to) && currentDraft.no_webpage == draftMessage.no_webpage ||
currentDraft == null && TextUtils.isEmpty(draftMessage.message) && (draftMessage.reply_to == null || draftMessage.reply_to.reply_to_msg_id == 0)
) {
return; return;
} }
} }
@ -6599,14 +6683,16 @@ public class MediaDataController extends BaseController {
if (req.peer == null) { if (req.peer == null) {
return; return;
} }
if (threadId != 0) {
req.top_msg_id = threadId;
}
req.message = draftMessage.message; req.message = draftMessage.message;
req.no_webpage = draftMessage.no_webpage; req.no_webpage = draftMessage.no_webpage;
req.reply_to_msg_id = draftMessage.reply_to_msg_id; req.reply_to = draftMessage.reply_to;
req.entities = draftMessage.entities; if (req.reply_to != null) {
req.flags = draftMessage.flags; req.flags |= 16;
}
if ((draftMessage.flags & 8) != 0) {
req.entities = draftMessage.entities;
req.flags |= 8;
}
getConnectionsManager().sendRequest(req, (response, error) -> { getConnectionsManager().sendRequest(req, (response, error) -> {
}); });
@ -6616,9 +6702,65 @@ public class MediaDataController extends BaseController {
} }
} }
private static boolean replyToEquals(TLRPC.InputReplyTo a, TLRPC.InputReplyTo b) {
if (a == b) {
return true;
}
if (a instanceof TLRPC.TL_inputReplyToMessage != b instanceof TLRPC.TL_inputReplyToMessage) {
return false;
}
if (a instanceof TLRPC.TL_inputReplyToMessage) {
if (!MessageObject.peersEqual(a.reply_to_peer_id, b.reply_to_peer_id)) {
return false;
}
if (!TextUtils.equals(a.quote_text, b.quote_text)) {
return false;
}
return a.reply_to_msg_id == b.reply_to_msg_id;
}
if (a instanceof TLRPC.TL_inputReplyToStory) {
return a.user_id == b.user_id && a.story_id == b.story_id;
}
return true;
}
private static TLRPC.InputReplyTo toInputReplyTo(int currentAccount, TLRPC.MessageReplyHeader reply_to) {
if (reply_to instanceof TLRPC.TL_messageReplyStoryHeader) {
TLRPC.TL_inputReplyToStory inputReplyTo = new TLRPC.TL_inputReplyToStory();
inputReplyTo.user_id = MessagesController.getInstance(currentAccount).getInputUser(reply_to.user_id);
inputReplyTo.story_id = reply_to.story_id;
return inputReplyTo;
} else if (reply_to instanceof TLRPC.TL_messageReplyHeader) {
TLRPC.TL_inputReplyToMessage inputReplyTo = new TLRPC.TL_inputReplyToMessage();
inputReplyTo.reply_to_msg_id = reply_to.reply_to_msg_id;
if ((reply_to.flags & 1) != 0) {
inputReplyTo.reply_to_peer_id = MessagesController.getInstance(currentAccount).getInputPeer(reply_to.reply_to_peer_id);
if (inputReplyTo.reply_to_peer_id != null) {
inputReplyTo.flags |= 2;
}
}
if ((reply_to.flags & 2) != 0) {
inputReplyTo.flags |= 1;
inputReplyTo.top_msg_id = reply_to.reply_to_top_id;
}
if ((reply_to.flags & 64) != 0) {
inputReplyTo.flags |= 4;
inputReplyTo.quote_text = reply_to.quote_text;
}
if ((reply_to.flags & 128) != 0) {
inputReplyTo.flags |= 8;
inputReplyTo.quote_entities = reply_to.quote_entities;
}
return inputReplyTo;
}
return null;
}
public void saveDraft(long dialogId, int threadId, TLRPC.DraftMessage draft, TLRPC.Message replyToMessage, boolean fromServer) { public void saveDraft(long dialogId, int threadId, TLRPC.DraftMessage draft, TLRPC.Message replyToMessage, boolean fromServer) {
if (getMessagesController().isForum(dialogId) && threadId == 0 && TextUtils.isEmpty(draft.message)) { if (getMessagesController().isForum(dialogId) && threadId == 0 && TextUtils.isEmpty(draft.message)) {
draft.reply_to_msg_id = 0; if (draft.reply_to instanceof TLRPC.TL_inputReplyToMessage) {
((TLRPC.TL_inputReplyToMessage) draft.reply_to).reply_to_msg_id = 0;
}
} }
SharedPreferences.Editor editor = draftPreferences.edit(); SharedPreferences.Editor editor = draftPreferences.edit();
MessagesController messagesController = getMessagesController(); MessagesController messagesController = getMessagesController();
@ -6667,6 +6809,16 @@ public class MediaDataController extends BaseController {
} }
} }
SparseArray<TLRPC.Message> threads = draftMessages.get(dialogId); SparseArray<TLRPC.Message> threads = draftMessages.get(dialogId);
if (replyToMessage == null && draft != null && draft.reply_to != null) {
if (threads != null) {
replyToMessage = threads.get(threadId);
}
if (replyToMessage == null || replyToMessage.id != draft.reply_to.reply_to_msg_id || !MessageObject.peersEqual(replyToMessage.peer_id, getMessagesController().getPeer(draft.reply_to.reply_to_msg_id))) {
replyToMessage = null;
}
} else if (draft != null && draft.reply_to == null) {
replyToMessage = null;
}
if (replyToMessage == null) { if (replyToMessage == null) {
if (threads != null) { if (threads != null) {
threads.remove(threadId); threads.remove(threadId);
@ -6693,7 +6845,7 @@ public class MediaDataController extends BaseController {
} }
editor.commit(); editor.commit();
if (fromServer && (threadId == 0 || getMessagesController().isForum(dialogId))) { if (fromServer && (threadId == 0 || getMessagesController().isForum(dialogId))) {
if (draft != null && draft.reply_to_msg_id != 0 && replyToMessage == null) { if (draft != null && draft.reply_to != null && draft.reply_to.reply_to_msg_id != 0 && replyToMessage == null) {
TLRPC.User user = null; TLRPC.User user = null;
TLRPC.Chat chat = null; TLRPC.Chat chat = null;
if (DialogObject.isUserDialog(dialogId)) { if (DialogObject.isUserDialog(dialogId)) {
@ -6703,7 +6855,7 @@ public class MediaDataController extends BaseController {
} }
if (user != null || chat != null) { if (user != null || chat != null) {
long channelId = ChatObject.isChannel(chat) ? chat.id : 0; long channelId = ChatObject.isChannel(chat) ? chat.id : 0;
int messageId = draft.reply_to_msg_id; int messageId = draft.reply_to.reply_to_msg_id;
getMessagesStorage().getStorageQueue().postRunnable(() -> { getMessagesStorage().getStorageQueue().postRunnable(() -> {
try { try {
@ -6763,7 +6915,7 @@ public class MediaDataController extends BaseController {
AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.runOnUIThread(() -> {
SparseArray<TLRPC.DraftMessage> threads = drafts.get(dialogId); SparseArray<TLRPC.DraftMessage> threads = drafts.get(dialogId);
TLRPC.DraftMessage draftMessage = threads != null ? threads.get(threadId) : null; TLRPC.DraftMessage draftMessage = threads != null ? threads.get(threadId) : null;
if (draftMessage != null && draftMessage.reply_to_msg_id == message.id) { if (draftMessage != null && draftMessage.reply_to != null && draftMessage.reply_to.reply_to_msg_id == message.id) {
SparseArray<TLRPC.Message> threads2 = draftMessages.get(dialogId); SparseArray<TLRPC.Message> threads2 = draftMessages.get(dialogId);
if (threads2 == null) { if (threads2 == null) {
threads2 = new SparseArray<>(); threads2 = new SparseArray<>();
@ -6822,10 +6974,12 @@ public class MediaDataController extends BaseController {
} else { } else {
draftPreferences.edit().remove("t_" + dialogId + "_" + threadId).remove("rt_" + dialogId + "_" + threadId).commit(); draftPreferences.edit().remove("t_" + dialogId + "_" + threadId).remove("rt_" + dialogId + "_" + threadId).commit();
} }
} else if (draftMessage.reply_to_msg_id != 0) { } else if (draftMessage.reply_to == null || draftMessage.reply_to.reply_to_msg_id != 0) {
draftMessage.reply_to_msg_id = 0; if (draftMessage.reply_to != null) {
draftMessage.reply_to.reply_to_msg_id = 0;
}
draftMessage.flags &= ~1; draftMessage.flags &= ~1;
saveDraft(dialogId, threadId, draftMessage.message, draftMessage.entities, null, draftMessage.no_webpage, true); saveDraft(dialogId, threadId, draftMessage.message, draftMessage.entities, null, null, draftMessage.no_webpage, true);
} }
} }
@ -7476,10 +7630,10 @@ public class MediaDataController extends BaseController {
} }
public void getEmojiSuggestions(String[] langCodes, String keyword, boolean fullMatch, KeywordResultCallback callback, final CountDownLatch sync, boolean allowAnimated, boolean allowTopicIcons, boolean includeSingleEmoji, Integer maxAnimatedPerEmoji) { public void getEmojiSuggestions(String[] langCodes, String keyword, boolean fullMatch, KeywordResultCallback callback, final CountDownLatch sync, boolean allowAnimated, boolean allowTopicIcons, boolean includeSingleEmoji, Integer maxAnimatedPerEmoji) {
getEmojiSuggestions(langCodes, keyword, fullMatch, callback, sync, allowAnimated, allowTopicIcons, includeSingleEmoji, false, maxAnimatedPerEmoji); getEmojiSuggestions(langCodes, keyword, fullMatch, callback, sync, allowAnimated, allowTopicIcons, includeSingleEmoji, false, maxAnimatedPerEmoji, false);
} }
public void getEmojiSuggestions(String[] langCodes, String keyword, boolean fullMatch, KeywordResultCallback callback, final CountDownLatch sync, boolean allowAnimated, boolean allowTopicIcons, boolean includeSingleEmoji, boolean forcePremium, Integer maxAnimatedPerEmoji) { public void getEmojiSuggestions(String[] langCodes, String keyword, boolean fullMatch, KeywordResultCallback callback, final CountDownLatch sync, boolean allowAnimated, boolean allowTopicIcons, boolean includeSingleEmoji, boolean forcePremium, Integer maxAnimatedPerEmoji, boolean includeOnlyTextColor) {
if (callback == null) { if (callback == null) {
return; return;
} }
@ -7613,8 +7767,8 @@ public class MediaDataController extends BaseController {
} }
}); });
String aliasFinal = alias; String aliasFinal = alias;
if (allowAnimated && SharedConfig.suggestAnimatedEmoji) { if (allowAnimated) {
fillWithAnimatedEmoji(result, maxAnimatedPerEmoji, allowTopicIcons, forcePremium, () -> { fillWithAnimatedEmoji(result, maxAnimatedPerEmoji, allowTopicIcons, forcePremium, includeOnlyTextColor, () -> {
if (sync != null) { if (sync != null) {
callback.run(result, aliasFinal); callback.run(result, aliasFinal);
sync.countDown(); sync.countDown();
@ -7642,7 +7796,7 @@ public class MediaDataController extends BaseController {
private boolean triedLoadingEmojipacks = false; private boolean triedLoadingEmojipacks = false;
public void fillWithAnimatedEmoji(ArrayList<KeywordResult> result, Integer maxAnimatedPerEmojiInput, boolean allowTopicIcons, boolean forcePremium, Runnable onDone) { public void fillWithAnimatedEmoji(ArrayList<KeywordResult> result, Integer maxAnimatedPerEmojiInput, boolean allowTopicIcons, boolean forcePremium, boolean includeOnlyTextColor, Runnable onDone) {
if (result == null || result.isEmpty()) { if (result == null || result.isEmpty()) {
if (onDone != null) { if (onDone != null) {
onDone.run(); onDone.run();
@ -8247,4 +8401,42 @@ public class MediaDataController extends BaseController {
})); }));
} }
} }
public TLRPC.TL_emojiList replyIconsDefault;
public void loadReplyIcons() {
SharedPreferences preferences = ApplicationLoader.applicationContext.getSharedPreferences("replyicons_" + currentAccount, Context.MODE_PRIVATE);
String value = preferences.getString("replyicons", null);
long lastCheckTime = preferences.getLong("replyicons_last_check", 0);
TLRPC.TL_emojiList emojiList = null;
if (value != null) {
SerializedData serializedData = new SerializedData(Utilities.hexToBytes(value));
try {
emojiList = (TLRPC.TL_emojiList) TLRPC.TL_emojiList.TLdeserialize(serializedData, serializedData.readInt32(true), true);
replyIconsDefault = emojiList;
} catch (Throwable e) {
FileLog.e(e);
}
}
if (emojiList == null || (System.currentTimeMillis() - lastCheckTime) > 24 * 60 * 60 * 1000 || BuildVars.DEBUG_PRIVATE_VERSION) {
TLRPC.TL_account_getDefaultBackgroundEmojis req = new TLRPC.TL_account_getDefaultBackgroundEmojis();
if (emojiList != null) {
req.hash = emojiList.hash;
}
getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
if (response instanceof TLRPC.TL_emojiList) {
SerializedData data = new SerializedData(response.getObjectSize());
response.serializeToStream(data);
SharedPreferences.Editor editor = preferences.edit();
replyIconsDefault = (TLRPC.TL_emojiList) response;
editor.putString("replyicons", Utilities.bytesToHex(data.toByteArray()));
editor.putLong("replyicons_last_check", System.currentTimeMillis());
editor.apply();
}
}));
}
}
} }

View file

@ -1,6 +1,7 @@
package org.telegram.messenger; package org.telegram.messenger;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import android.util.LongSparseArray; import android.util.LongSparseArray;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -57,7 +58,8 @@ public class MemberRequestsController extends BaseController {
AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.runOnUIThread(() -> {
if (error == null) { if (error == null) {
TLRPC.TL_messages_chatInviteImporters importers = (TLRPC.TL_messages_chatInviteImporters) response; TLRPC.TL_messages_chatInviteImporters importers = (TLRPC.TL_messages_chatInviteImporters) response;
firstImportersCache.put(chatId, importers); if (lastImporter == null && isEmptyQuery)
firstImportersCache.put(chatId, importers);
} }
onComplete.run(response, error); onComplete.run(response, error);
}); });

View file

@ -0,0 +1,557 @@
package org.telegram.messenger;
import android.net.Uri;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.CharacterStyle;
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseBooleanArray;
import org.telegram.tgnet.ConnectionsManager;
import org.telegram.tgnet.TLRPC;
import org.telegram.ui.ChatActivity;
import org.telegram.ui.Components.AnimatedEmojiSpan;
import org.telegram.ui.Components.MessagePreviewView;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MessagePreviewParams {
private static ArrayList<MessageObject> singletonArrayList(MessageObject obj) {
ArrayList<MessageObject> list = new ArrayList<MessageObject>();
list.add(obj);
return list;
}
public class Messages {
private Boolean out;
private int type;
private long dialogId;
public LongSparseArray<MessageObject.GroupedMessages> groupedMessagesMap = new LongSparseArray<>();
public ArrayList<MessageObject> messages;
public ArrayList<MessageObject> previewMessages = new ArrayList<>();
public SparseBooleanArray selectedIds = new SparseBooleanArray();
public ArrayList<TLRPC.TL_pollAnswerVoters> pollChosenAnswers = new ArrayList<>();
public boolean hasSpoilers;
public boolean hasText;
public Messages(Boolean out, int type, MessageObject message) {
this(out, type, singletonArrayList(message), message.getDialogId(), null);
}
public Messages(Boolean out, int type, MessageObject message, long newDialogId) {
this(out, type, singletonArrayList(message), newDialogId, null);
}
public Messages(Boolean out, int type, ArrayList<MessageObject> messages, long newDialogId, SparseBooleanArray pastSelectedIds) {
this.out = out;
this.type = type;
this.dialogId = newDialogId;
this.messages = messages;
if (pastSelectedIds != null) {
selectedIds = pastSelectedIds;
}
for (int i = 0; i < messages.size(); i++) {
MessageObject messageObject = messages.get(i);
if (type == 0 && pastSelectedIds == null) {
selectedIds.put(messageObject.getId(), true);
}
MessageObject previewMessage = toPreviewMessage(messageObject, out, type);
if (!hasSpoilers) {
for (TLRPC.MessageEntity e : previewMessage.messageOwner.entities) {
if (e instanceof TLRPC.TL_messageEntitySpoiler) {
hasSpoilers = true;
break;
}
}
}
previewMessage.messageOwner.dialog_id = newDialogId;
if (previewMessage.getGroupId() != 0) {
MessageObject.GroupedMessages groupedMessages = groupedMessagesMap.get(previewMessage.getGroupId(), null);
if (groupedMessages == null) {
groupedMessages = new MessageObject.GroupedMessages();
groupedMessagesMap.put(previewMessage.getGroupId(), groupedMessages);
}
groupedMessages.messages.add(previewMessage);
}
previewMessages.add(0, previewMessage);
if (messageObject.isPoll()) {
TLRPC.TL_messageMediaPoll mediaPoll = (TLRPC.TL_messageMediaPoll) messageObject.messageOwner.media;
PreviewMediaPoll newMediaPoll = new PreviewMediaPoll();
newMediaPoll.poll = mediaPoll.poll;
newMediaPoll.provider = mediaPoll.provider;
newMediaPoll.results = new TLRPC.TL_pollResults();
newMediaPoll.totalVotersCached = newMediaPoll.results.total_voters = mediaPoll.results.total_voters;
previewMessage.messageOwner.media = newMediaPoll;
if (messageObject.canUnvote()) {
for (int a = 0, N = mediaPoll.results.results.size(); a < N; a++) {
TLRPC.TL_pollAnswerVoters answer = mediaPoll.results.results.get(a);
if (answer.chosen) {
TLRPC.TL_pollAnswerVoters newAnswer = new TLRPC.TL_pollAnswerVoters();
newAnswer.chosen = answer.chosen;
newAnswer.correct = answer.correct;
newAnswer.flags = answer.flags;
newAnswer.option = answer.option;
newAnswer.voters = answer.voters;
pollChosenAnswers.add(newAnswer);
newMediaPoll.results.results.add(newAnswer);
} else {
newMediaPoll.results.results.add(answer);
}
}
}
}
}
for (int i = 0; i < groupedMessagesMap.size(); i++) {
groupedMessagesMap.valueAt(i).calculate();
}
if (groupedMessagesMap != null && groupedMessagesMap.size() > 0) {
MessageObject.GroupedMessages group = groupedMessagesMap.valueAt(0);
hasText = group.findCaptionMessageObject() != null;
} else if (messages.size() == 1) {
MessageObject msg = messages.get(0);
if (msg.type == MessageObject.TYPE_TEXT || msg.type == MessageObject.TYPE_EMOJIS) {
hasText = !TextUtils.isEmpty(msg.messageText);
} else {
hasText = !TextUtils.isEmpty(msg.caption);
}
}
}
public void getSelectedMessages(ArrayList<MessageObject> messagesToForward) {
messagesToForward.clear();
for (int i = 0; i < messages.size(); i++) {
MessageObject messageObject = messages.get(i);
int id = messageObject.getId();
if (selectedIds.get(id, false)) {
messagesToForward.add(messageObject);
}
}
}
public Messages checkEdits(ArrayList<MessageObject> replaceMessageObjects) {
if (messages == null || messages.size() > 1 || replaceMessageObjects == null) {
return null;
}
boolean replaced = false;
for (int i = 0; i < messages.size(); ++i) {
MessageObject msg = messages.get(i);
if (msg == null) continue;
for (int j = 0; j < replaceMessageObjects.size(); ++j) {
MessageObject msg2 = replaceMessageObjects.get(j);
if (msg2 == null) continue;
if (msg.getId() == msg2.getId() && msg.getDialogId() == msg2.getDialogId()) {
messages.set(i, msg2);
replaced = true;
break;
}
}
}
if (replaced) {
return new Messages(out, type, messages, dialogId, null);
}
return null;
}
}
public Messages replyMessage;
public Messages forwardMessages;
public Messages linkMessage;
public TLRPC.WebPage linkMedia;
public ChatActivity.ReplyQuote quote;
public int quoteStart, quoteEnd;
public boolean hasCaption;
public boolean hasSenders;
public boolean isSecret;
public boolean multipleUsers;
public boolean hideForwardSendersName;
public boolean hideCaption;
public boolean willSeeSenders;
public boolean singleLink;
public boolean hasMedia;
public boolean webpageSmall;
public boolean webpageTop;
public boolean webpagePhoto;
public boolean secret;
public boolean noforwards;
public TLRPC.WebPage webpage;
public CharacterStyle currentLink;
public MessagePreviewParams(boolean secret, boolean noforwards) {
this.secret = secret;
this.noforwards = secret || noforwards;
}
public void updateReply(MessageObject replyMessageObject, MessageObject.GroupedMessages group, long dialogId, ChatActivity.ReplyQuote replyQuote) {
if (replyMessageObject == null || replyMessageObject.type == MessageObject.TYPE_DATE || replyMessageObject.type == MessageObject.TYPE_ACTION_PHOTO || replyMessageObject.type == MessageObject.TYPE_ACTION_WALLPAPER || replyMessageObject.type == MessageObject.TYPE_SUGGEST_PHOTO) {
replyMessageObject = null;
replyQuote = null;
}
if (replyMessageObject != null || replyQuote != null) {
if (group != null) {
replyMessage = new Messages(null, 1, group.messages, dialogId, null);
} else {
replyMessage = new Messages(null, 1, replyMessageObject != null ? replyMessageObject : replyQuote.message, dialogId);
}
if (!replyMessage.messages.isEmpty()) {
this.quote = replyQuote;
if (replyQuote != null) {
quoteStart = replyQuote.start;
quoteEnd = replyQuote.end;
}
} else {
replyMessage = null;
}
} else {
replyMessage = null;
quote = null;
}
}
public void updateLinkInvertMedia(boolean invertMedia) {
webpageTop = invertMedia;
}
public void updateLink(int currentAccount, TLRPC.WebPage foundWebpage, CharSequence messageText, MessageObject replyMessageObject, ChatActivity.ReplyQuote replyQuote, MessageObject inherit) {
hasMedia = false;
singleLink = true;
boolean wasDifferent = webpage != foundWebpage;
webpage = foundWebpage;
if (TextUtils.isEmpty(messageText) && webpage == null) {
this.linkMessage = null;
} else {
if (messageText == null) {
messageText = "";
}
boolean wasEmpty = linkMessage == null || wasDifferent;
if (linkMessage == null && inherit != null && inherit.messageOwner != null) {
webpageTop = inherit.messageOwner.invert_media;
if (inherit.messageOwner.media != null && inherit.messageOwner.media.force_small_media) {
webpageSmall = true;
}
}
TLRPC.Message message = new TLRPC.TL_message();
messageText = new SpannableStringBuilder(AndroidUtilities.getTrimmedString(messageText));
CharSequence[] cs = new CharSequence[] { messageText };
message.peer_id = new TLRPC.TL_peerUser();
message.peer_id.user_id = UserConfig.getInstance(currentAccount).getClientUserId();
message.from_id = new TLRPC.TL_peerUser();
message.from_id.user_id = UserConfig.getInstance(currentAccount).getClientUserId();
message.entities = MediaDataController.getInstance(currentAccount).getEntities(cs, true);
message.message = cs[0].toString();
message.invert_media = webpageTop;
if (foundWebpage != null) {
message.flags |= 512;
message.media = new TLRPC.TL_messageMediaWebPage();
message.media.webpage = foundWebpage;
message.media.force_large_media = !webpageSmall;
message.media.force_small_media = webpageSmall;
hasMedia = message.media.webpage.photo != null;
} else {
hasMedia = false;
}
message.out = true;
message.unread = false;
if (replyMessageObject != null) {
message.replyMessage = replyMessageObject.messageOwner;
message.reply_to = new TLRPC.TL_messageReplyHeader();
if (replyQuote != null) {
message.reply_to.quote_text = replyQuote.getText();
message.reply_to.flags |= 64;
message.reply_to.quote_entities = replyQuote.getEntities();
if (message.reply_to.quote_entities != null) {
message.reply_to.flags |= 128;
}
}
}
this.linkMessage = new Messages(true, 2, new MessageObject(currentAccount, message, true, false));
if (this.linkMessage.messages.isEmpty()) {
this.linkMessage = null;
} else {
final MessageObject msg = this.linkMessage.messages.get(0);
if (msg.messageText instanceof Spanned && !TextUtils.isEmpty(msg.messageText)) {
URLSpan[] links = ((Spanned) msg.messageText).getSpans(0, msg.messageText.length(), URLSpan.class);
singleLink = links == null || links.length <= 1;
} else if (msg.caption instanceof Spanned && !TextUtils.isEmpty(msg.caption)) {
URLSpan[] links = ((Spanned) msg.messageText).getSpans(0, msg.caption.length(), URLSpan.class);
singleLink = links == null || links.length <= 1;
}
hasMedia = msg.hasLinkMediaToMakeSmall();
if (wasEmpty && inherit != null && inherit.messageOwner != null && inherit.messageOwner.media != null) {
webpageSmall = inherit.messageOwner.media.force_small_media || msg.isLinkMediaSmall() && !inherit.messageOwner.media.force_large_media;
} else if (wasEmpty) {
webpageSmall = msg.isLinkMediaSmall();
}
if (msg != null && msg.messageOwner != null && msg.messageOwner.media != null) {
msg.messageOwner.media.force_large_media = !webpageSmall;
msg.messageOwner.media.force_small_media = webpageSmall;
}
}
}
if (previewView != null) {
previewView.updateLink();
}
}
public void checkCurrentLink(MessageObject msg) {
currentLink = null;
if (msg != null && msg.messageText instanceof Spanned && webpage != null && webpage.url != null) {
Spanned spanned = (Spanned) msg.messageText;
URLSpan[] urlSpans = spanned.getSpans(0, spanned.length(), URLSpan.class);
for (int i = 0; i < urlSpans.length; ++i) {
if (areUrlsEqual(urlSpans[i].getURL(), webpage.url)) {
currentLink = urlSpans[i];
break;
}
}
}
}
public boolean hasLink(CharSequence text, String url) {
if (url != null) {
Spannable spanned = SpannableString.valueOf(text);
try {
AndroidUtilities.addLinks(spanned, Linkify.WEB_URLS);
} catch (Exception e) {
FileLog.e(e);
}
URLSpan[] urlSpans = spanned.getSpans(0, spanned.length(), URLSpan.class);
for (int i = 0; i < urlSpans.length; ++i) {
if (areUrlsEqual(urlSpans[i].getURL(), url)) {
return true;
}
}
}
return false;
}
public static boolean areUrlsEqual(String url1, String url2) {
if (url1 == null || url2 == null) {
return url1 == null;
}
Uri uri1 = Uri.parse(url1);
Uri uri2 = Uri.parse(url2);
return uri1 == uri2 || uri1 != null && uri2 != null &&
(uri1.getHost() != null && uri1.getHost().equalsIgnoreCase(uri2.getHost())) &&
uri1.getPort() == uri2.getPort() &&
normalizePath(uri1.getPath()).equals(normalizePath(uri2.getPath())) &&
(uri1.getQuery() == null ? uri2.getQuery() == null : uri1.getQuery().equals(uri2.getQuery()));
}
private static String normalizePath(String path) {
if (path == null) return "";
return (path.endsWith("/") ? path : path + "/");
}
public void updateForward(ArrayList<MessageObject> forwardMessages, long dialogId) {
hasCaption = false;
hasSenders = false;
isSecret = DialogObject.isEncryptedDialog(dialogId);
multipleUsers = false;
if (forwardMessages != null) {
ArrayList<String> hiddenSendersName = new ArrayList<>();
for (int i = 0; i < forwardMessages.size(); ++i) {
MessageObject messageObject = forwardMessages.get(i);
if (!TextUtils.isEmpty(messageObject.caption)) {
hasCaption = true;
}
if (!isSecret) {
if (messageObject.messageOwner.fwd_from != null) {
TLRPC.MessageFwdHeader header = messageObject.messageOwner.fwd_from;
if (header.from_id == null && !hiddenSendersName.contains(header.from_name)) {
hiddenSendersName.add(header.from_name);
}
}
}
}
this.forwardMessages = new Messages(true, 0, forwardMessages, dialogId, this.forwardMessages != null ? this.forwardMessages.selectedIds : null);
if (this.forwardMessages.messages.isEmpty()) {
this.forwardMessages = null;
}
ArrayList<Long> uids = new ArrayList<>();
for (int a = 0; a < forwardMessages.size(); a++) {
MessageObject object = forwardMessages.get(a);
long uid;
if (object.isFromUser()) {
uid = object.messageOwner.from_id.user_id;
} else {
TLRPC.Chat chat = MessagesController.getInstance(object.currentAccount).getChat(object.messageOwner.peer_id.channel_id);
if (ChatObject.isChannel(chat) && chat.megagroup && object.isForwardedChannelPost()) {
uid = -object.messageOwner.fwd_from.from_id.channel_id;
} else {
uid = -object.messageOwner.peer_id.channel_id;
}
}
if (!uids.contains(uid)) {
uids.add(uid);
}
}
if (uids.size() + hiddenSendersName.size() > 1) {
multipleUsers = true;
}
} else {
this.forwardMessages = null;
}
}
private MessageObject toPreviewMessage(MessageObject messageObject, Boolean out, final int msgtype) {
TLRPC.Message message = new TLRPC.TL_message();
if (msgtype != 1) {
message.date = ConnectionsManager.getInstance(messageObject.currentAccount).getCurrentTime();
} else {
message.date = messageObject.messageOwner.date;
}
message.id = messageObject.messageOwner.id;
message.grouped_id = messageObject.messageOwner.grouped_id;
message.peer_id = messageObject.messageOwner.peer_id;
message.from_id = messageObject.messageOwner.from_id;
message.message = messageObject.messageOwner.message;
message.media = messageObject.messageOwner.media;
message.action = messageObject.messageOwner.action;
message.edit_date = 0;
if (messageObject.messageOwner.entities != null) {
message.entities.addAll(messageObject.messageOwner.entities);
}
message.out = out == null ? messageObject.messageOwner.out : out;
message.unread = false;
message.via_bot_id = messageObject.messageOwner.via_bot_id;
message.reply_markup = messageObject.messageOwner.reply_markup;
message.post = messageObject.messageOwner.post;
message.legacy = messageObject.messageOwner.legacy;
message.restriction_reason = messageObject.messageOwner.restriction_reason;
message.replyMessage = messageObject.messageOwner.replyMessage;
if (message.replyMessage == null && messageObject.replyMessageObject != null) {
message.replyMessage = messageObject.replyMessageObject.messageOwner;
}
message.reply_to = messageObject.messageOwner.reply_to;
message.invert_media = messageObject.messageOwner.invert_media;
if (msgtype == 0) {
TLRPC.MessageFwdHeader header = null;
long clientUserId = UserConfig.getInstance(messageObject.currentAccount).clientUserId;
if (!isSecret) {
if (messageObject.messageOwner.fwd_from != null) {
header = messageObject.messageOwner.fwd_from;
if (!messageObject.isDice()) {
hasSenders = true;
} else {
willSeeSenders = true;
}
} else if (messageObject.messageOwner.from_id.user_id == 0 || messageObject.messageOwner.dialog_id != clientUserId || messageObject.messageOwner.from_id.user_id != clientUserId) {
header = new TLRPC.TL_messageFwdHeader();
header.from_id = messageObject.messageOwner.from_id;
if (!messageObject.isDice()) {
hasSenders = true;
} else {
willSeeSenders = true;
}
}
}
if (header != null) {
message.fwd_from = header;
message.flags |= TLRPC.MESSAGE_FLAG_FWD;
}
}
MessageObject previewMessage = new MessageObject(messageObject.currentAccount, message, true, false) {
@Override
public void generateLayout(TLRPC.User fromUser) {
super.generateLayout(fromUser);
if (msgtype == 2) {
checkCurrentLink(this);
}
}
@Override
public boolean needDrawForwarded() {
if (hideForwardSendersName) {
return false;
}
return super.needDrawForwarded();
}
};
previewMessage.previewForward = msgtype == 0;
// previewMessage.forceAvatar = msgtype == 1 && !message.out;
previewMessage.preview = true;
return previewMessage;
}
public static class PreviewMediaPoll extends TLRPC.TL_messageMediaPoll {
public int totalVotersCached;
}
public boolean isEmpty() {
return (
(forwardMessages == null || forwardMessages.messages == null || forwardMessages.messages.isEmpty()) &&
(replyMessage == null || replyMessage.messages == null || replyMessage.messages.isEmpty()) &&
(linkMessage == null || linkMessage.messages == null || linkMessage.messages.isEmpty())
);
}
private MessagePreviewView previewView;
public void attach(MessagePreviewView previewView) {
this.previewView = previewView;
}
public void checkEdits(ArrayList<MessageObject> replaceMessageObjects) {
boolean replaced = false;
if (forwardMessages != null) {
Messages newForwardMessages = forwardMessages.checkEdits(replaceMessageObjects);
if (newForwardMessages != null) {
forwardMessages = newForwardMessages;
replaced = true;
}
}
if (replyMessage != null) {
Messages newReplyMessages = replyMessage.checkEdits(replaceMessageObjects);
if (newReplyMessages != null) {
replyMessage = newReplyMessages;
replaced = true;
}
}
if (linkMessage != null) {
Messages newLinkMessages = linkMessage.checkEdits(replaceMessageObjects);
if (newLinkMessages != null) {
linkMessage = newLinkMessages;
replaced = true;
}
}
if (replaced && previewView != null) {
previewView.updateAll();
}
}
}

View file

@ -27,12 +27,13 @@ import android.os.SystemClock;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
import android.util.Log;
import android.util.Pair; import android.util.Pair;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseBooleanArray; import android.util.SparseBooleanArray;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.LongSparseArray; import androidx.collection.LongSparseArray;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
@ -52,6 +53,8 @@ import org.telegram.tgnet.RequestDelegate;
import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.SerializedData;
import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.tgnet.tl.TL_stories;
import org.telegram.tgnet.tl.TL_chatlists;
import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.Theme;
@ -147,6 +150,11 @@ public class MessagesController extends BaseController implements NotificationCe
private boolean hasStories; private boolean hasStories;
public long storiesChangelogUserId = 777000; public long storiesChangelogUserId = 777000;
private ChannelBoostsController channelBoostsControler; private ChannelBoostsController channelBoostsControler;
public long giveawayAddPeersMax = 10;
public long giveawayPeriodMax = 7;
public long giveawayCountriesMax = 10;
public long giveawayBoostsPerPremium = 4;
public long boostsPerSentGift = 3;
public static TLRPC.Peer getPeerFromInputPeer(TLRPC.InputPeer peer) { public static TLRPC.Peer getPeerFromInputPeer(TLRPC.InputPeer peer) {
if (peer.chat_id != 0) { if (peer.chat_id != 0) {
@ -181,13 +189,13 @@ public class MessagesController extends BaseController implements NotificationCe
public ChatlistUpdatesStat() { public ChatlistUpdatesStat() {
this.loading = true; this.loading = true;
} }
public ChatlistUpdatesStat(TLRPC.TL_chatlists_chatlistUpdates value) { public ChatlistUpdatesStat(TL_chatlists.TL_chatlists_chatlistUpdates value) {
this.lastRequestTime = System.currentTimeMillis(); this.lastRequestTime = System.currentTimeMillis();
this.lastValue = value; this.lastValue = value;
} }
boolean loading = false; boolean loading = false;
long lastRequestTime; long lastRequestTime;
TLRPC.TL_chatlists_chatlistUpdates lastValue; TL_chatlists.TL_chatlists_chatlistUpdates lastValue;
} }
private boolean dialogsInTransaction; private boolean dialogsInTransaction;
@ -506,6 +514,10 @@ public class MessagesController extends BaseController implements NotificationCe
public int ringtoneSizeMax; public int ringtoneSizeMax;
public boolean storiesExportNopublicLink; public boolean storiesExportNopublicLink;
public int authorizationAutoconfirmPeriod; public int authorizationAutoconfirmPeriod;
public int channelColorLevelMin;
public int quoteLengthMax;
public boolean giveawayGiftsPurchaseAvailable;
public PeerColors peerColors;
public int channelsLimitDefault; public int channelsLimitDefault;
public int channelsLimitPremium; public int channelsLimitPremium;
@ -1012,7 +1024,7 @@ public class MessagesController extends BaseController implements NotificationCe
public ArrayList<TLRPC.Dialog> dialogs = new ArrayList<>(); public ArrayList<TLRPC.Dialog> dialogs = new ArrayList<>();
public ArrayList<TLRPC.Dialog> dialogsForward = new ArrayList<>(); public ArrayList<TLRPC.Dialog> dialogsForward = new ArrayList<>();
public ArrayList<TLRPC.TL_exportedChatlistInvite> invites = null; public ArrayList<TL_chatlists.TL_exportedChatlistInvite> invites = null;
private static int dialogFilterPointer = 10; private static int dialogFilterPointer = 10;
public int localId = dialogFilterPointer++; public int localId = dialogFilterPointer++;
@ -1383,6 +1395,11 @@ public class MessagesController extends BaseController implements NotificationCe
largeQueueMaxActiveOperations = mainPreferences.getInt("largeQueueMaxActiveOperations", 2); largeQueueMaxActiveOperations = mainPreferences.getInt("largeQueueMaxActiveOperations", 2);
stealthModeFuture = mainPreferences.getInt("stories_stealth_future_period", 25 * 60); stealthModeFuture = mainPreferences.getInt("stories_stealth_future_period", 25 * 60);
storiesChangelogUserId = mainPreferences.getLong("stories_changelog_user_id", 777000); storiesChangelogUserId = mainPreferences.getLong("stories_changelog_user_id", 777000);
giveawayAddPeersMax = mainPreferences.getLong("giveaway_add_peers_max", 10);
giveawayCountriesMax = mainPreferences.getLong("giveaway_countries_max", 10);
giveawayBoostsPerPremium = mainPreferences.getLong("giveaway_boosts_per_premium", 4);
boostsPerSentGift = mainPreferences.getLong("boosts_per_sent_gift", 3);
giveawayPeriodMax = mainPreferences.getLong("giveaway_period_max", 7);
stealthModePast = mainPreferences.getInt("stories_stealth_past_period", 5 * 60); stealthModePast = mainPreferences.getInt("stories_stealth_past_period", 5 * 60);
stealthModeCooldown = mainPreferences.getInt("stories_stealth_cooldown_period", 60 * 60); stealthModeCooldown = mainPreferences.getInt("stories_stealth_cooldown_period", 60 * 60);
boolean isTest = ConnectionsManager.native_isTestBackend(currentAccount) != 0; boolean isTest = ConnectionsManager.native_isTestBackend(currentAccount) != 0;
@ -1400,6 +1417,10 @@ public class MessagesController extends BaseController implements NotificationCe
storiesEntities = mainPreferences.getString("storiesEntities", "premium"); storiesEntities = mainPreferences.getString("storiesEntities", "premium");
storiesExportNopublicLink = mainPreferences.getBoolean("storiesExportNopublicLink", false); storiesExportNopublicLink = mainPreferences.getBoolean("storiesExportNopublicLink", false);
authorizationAutoconfirmPeriod = mainPreferences.getInt("authorization_autoconfirm_period", 604800); authorizationAutoconfirmPeriod = mainPreferences.getInt("authorization_autoconfirm_period", 604800);
channelColorLevelMin = mainPreferences.getInt("channelColorLevelMin", 1);
quoteLengthMax = mainPreferences.getInt("quoteLengthMax", 1024);
giveawayGiftsPurchaseAvailable = mainPreferences.getBoolean("giveawayGiftsPurchaseAvailable", false);
peerColors = PeerColors.fromString(mainPreferences.getString("peerColors", ""));
BuildVars.GOOGLE_AUTH_CLIENT_ID = mainPreferences.getString("googleAuthClientId", BuildVars.GOOGLE_AUTH_CLIENT_ID); BuildVars.GOOGLE_AUTH_CLIENT_ID = mainPreferences.getString("googleAuthClientId", BuildVars.GOOGLE_AUTH_CLIENT_ID);
if (mainPreferences.contains("dcDomainName2")) { if (mainPreferences.contains("dcDomainName2")) {
dcDomainName = mainPreferences.getString("dcDomainName2", "apv3.stel.com"); dcDomainName = mainPreferences.getString("dcDomainName2", "apv3.stel.com");
@ -2166,9 +2187,66 @@ public class MessagesController extends BaseController implements NotificationCe
boolean keelAliveChanged = false; boolean keelAliveChanged = false;
resetAppConfig(); resetAppConfig();
TLRPC.TL_jsonObject liteAppOptions = null; TLRPC.TL_jsonObject liteAppOptions = null;
TLRPC.TL_jsonObject peer_colors = null, dark_peer_colors = null;
TLRPC.TL_jsonArray peer_colors_available = null;
for (int a = 0, N = object.value.size(); a < N; a++) { for (int a = 0, N = object.value.size(); a < N; a++) {
TLRPC.TL_jsonObjectValue value = object.value.get(a); TLRPC.TL_jsonObjectValue value = object.value.get(a);
switch (value.key) { switch (value.key) {
case "boosts_per_sent_gift": {
if (value.value instanceof TLRPC.TL_jsonNumber) {
long val = (long) ((TLRPC.TL_jsonNumber) value.value).value;
if (val != boostsPerSentGift) {
boostsPerSentGift = val;
editor.putLong("boosts_per_sent_gift", boostsPerSentGift);
changed = true;
}
}
break;
}
case "giveaway_boosts_per_premium": {
if (value.value instanceof TLRPC.TL_jsonNumber) {
long val = (long) ((TLRPC.TL_jsonNumber) value.value).value;
if (val != giveawayBoostsPerPremium) {
giveawayBoostsPerPremium = val;
editor.putLong("giveaway_boosts_per_premium", giveawayBoostsPerPremium);
changed = true;
}
}
break;
}
case "giveaway_period_max": {
if (value.value instanceof TLRPC.TL_jsonNumber) {
long val = (long) ((TLRPC.TL_jsonNumber) value.value).value;
if (val != giveawayPeriodMax) {
giveawayPeriodMax = val;
editor.putLong("giveaway_period_max", giveawayPeriodMax);
changed = true;
}
}
break;
}
case "giveaway_add_peers_max": {
if (value.value instanceof TLRPC.TL_jsonNumber) {
long val = (long) ((TLRPC.TL_jsonNumber) value.value).value;
if (val != giveawayAddPeersMax) {
giveawayAddPeersMax = val;
editor.putLong("giveaway_add_peers_max", giveawayAddPeersMax);
changed = true;
}
}
break;
}
case "giveaway_countries_max": {
if (value.value instanceof TLRPC.TL_jsonNumber) {
long val = (long) ((TLRPC.TL_jsonNumber) value.value).value;
if (val != giveawayCountriesMax) {
giveawayCountriesMax = val;
editor.putLong("giveaway_countries_max", giveawayCountriesMax);
changed = true;
}
}
break;
}
case "stories_changelog_user_id": { case "stories_changelog_user_id": {
if (value.value instanceof TLRPC.TL_jsonNumber) { if (value.value instanceof TLRPC.TL_jsonNumber) {
storiesChangelogUserId = (long) ((TLRPC.TL_jsonNumber) value.value).value; storiesChangelogUserId = (long) ((TLRPC.TL_jsonNumber) value.value).value;
@ -3383,8 +3461,66 @@ public class MessagesController extends BaseController implements NotificationCe
} }
break; break;
} }
case "channel_color_level_min": {
if (value.value instanceof TLRPC.TL_jsonNumber) {
TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value;
if (channelColorLevelMin != num.value) {
channelColorLevelMin = (int) num.value;
editor.putInt("channelColorLevelMin", channelColorLevelMin);
changed = true;
}
}
break;
}
case "quote_length_max": {
if (value.value instanceof TLRPC.TL_jsonNumber) {
TLRPC.TL_jsonNumber num = (TLRPC.TL_jsonNumber) value.value;
if (quoteLengthMax != num.value) {
quoteLengthMax = (int) num.value;
editor.putInt("quoteLengthMax", quoteLengthMax);
changed = true;
}
}
break;
}
case "peer_colors": {
if (value.value instanceof TLRPC.TL_jsonObject) {
peer_colors = (TLRPC.TL_jsonObject) value.value;
}
break;
}
case "dark_peer_colors": {
if (value.value instanceof TLRPC.TL_jsonObject) {
dark_peer_colors = (TLRPC.TL_jsonObject) value.value;
}
break;
}
case "peer_colors_available": {
if (value.value instanceof TLRPC.TL_jsonArray) {
peer_colors_available = (TLRPC.TL_jsonArray) value.value;
}
break;
}
case "giveaway_gifts_purchase_available": {
if (value.value instanceof TLRPC.TL_jsonBool) {
if (giveawayGiftsPurchaseAvailable != ((TLRPC.TL_jsonBool) value.value).value) {
giveawayGiftsPurchaseAvailable = ((TLRPC.TL_jsonBool) value.value).value;
editor.putBoolean("giveawayGiftsPurchaseAvailable", giveawayGiftsPurchaseAvailable);
changed = true;
}
}
break;
}
} }
} }
PeerColors newPeerColors = PeerColors.fromJSON(peer_colors, dark_peer_colors, peer_colors_available);
if (peerColors == null || !TextUtils.equals(peerColors.toString(), newPeerColors.toString())) {
peerColors = newPeerColors;
editor.putString("peerColors", peerColors.toString());
changed = true;
}
if (changed) { if (changed) {
editor.apply(); editor.apply();
} }
@ -3404,6 +3540,196 @@ public class MessagesController extends BaseController implements NotificationCe
logDeviceStats(); logDeviceStats();
} }
public static class PeerColors {
public final ArrayList<PeerColor> colors = new ArrayList<>();
private final LongSparseArray<PeerColor> colorsById = new LongSparseArray<>();
private PeerColors() {}
@Nullable
public PeerColor getColor(int colorId) {
return colorsById.get(colorId);
}
@NonNull
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < colors.size(); ++i) {
PeerColor color = colors.get(i);
if (i > 0) sb.append(";");
color.appendString(sb);
}
return sb.toString();
}
public static PeerColors fromString(String str) {
if (str == null) return null;
final PeerColors peerColors = new PeerColors();
final String[] colorParts = str.split(";");
for (int i = 0; i < colorParts.length; ++i) {
PeerColor peerColor = PeerColor.fromString(colorParts[i]);
if (peerColor == null)
continue;
peerColors.colors.add(peerColor);
peerColors.colorsById.put(peerColor.id, peerColor);
}
return peerColors;
}
private static int color(String str) {
return Integer.parseUnsignedInt("ff" + str, 16);
}
public static PeerColors fromJSON(
TLRPC.TL_jsonObject peer_colors,
TLRPC.TL_jsonObject dark_peer_colors,
TLRPC.TL_jsonArray peer_colors_available
) {
try {
PeerColors peerColors = new PeerColors();
if (peer_colors != null) {
for (TLRPC.TL_jsonObjectValue pair : peer_colors.value) {
final int id = Utilities.parseInt(pair.key);
if (!(pair.value instanceof TLRPC.TL_jsonArray))
continue;
ArrayList<TLRPC.JSONValue> val = ((TLRPC.TL_jsonArray) pair.value).value;
if (val.isEmpty())
continue;
PeerColor peerColor = new PeerColor();
try {
peerColor.id = id;
peerColor.color1 = peerColor.darkColor1 = color(((TLRPC.TL_jsonString) val.get(0)).value);
peerColor.color2 = peerColor.darkColor2 = val.size() > 1 ? color(((TLRPC.TL_jsonString) val.get(1)).value) : peerColor.color1;
peerColor.color3 = peerColor.darkColor3 = val.size() > 2 ? color(((TLRPC.TL_jsonString) val.get(2)).value) : peerColor.color1;
} catch (Exception e2) {
FileLog.e(e2);
continue;
}
if (peerColor.id < 7) continue;
peerColors.colorsById.put(id, peerColor);
}
}
if (dark_peer_colors != null) {
for (TLRPC.TL_jsonObjectValue pair : dark_peer_colors.value) {
final int id = Utilities.parseInt(pair.key);
if (!(pair.value instanceof TLRPC.TL_jsonArray))
continue;
ArrayList<TLRPC.JSONValue> val = ((TLRPC.TL_jsonArray) pair.value).value;
if (val.isEmpty())
continue;
PeerColor peerColor = peerColors.colorsById.get(id);
if (peerColor == null) continue;
try {
peerColor.id = id;
peerColor.darkColor1 = color(((TLRPC.TL_jsonString) val.get(0)).value);
peerColor.darkColor2 = val.size() > 1 ? color(((TLRPC.TL_jsonString) val.get(1)).value) : peerColor.darkColor1;
peerColor.darkColor3 = val.size() > 2 ? color(((TLRPC.TL_jsonString) val.get(2)).value) : peerColor.darkColor1;
} catch (Exception e2) {
FileLog.e(e2);
continue;
}
peerColors.colorsById.put(id, peerColor);
}
}
peerColors.colors.clear();
if (peer_colors_available != null) {
for (TLRPC.JSONValue idvalue : peer_colors_available.value) {
if (!(idvalue instanceof TLRPC.TL_jsonNumber))
continue;
final int id = (int) ((TLRPC.TL_jsonNumber) idvalue).value;
PeerColor color = peerColors.colorsById.get(id);
if (color == null) continue;
peerColors.colors.add(color);
}
}
return peerColors;
} catch (Exception e) {
FileLog.e(e);
}
return null;
}
}
public static class PeerColor {
public int id;
private int color1, color2, color3;
private int darkColor1, darkColor2, darkColor3;
public int getColor1() {
return Theme.isCurrentThemeDark() ? darkColor1 : color1;
}
public int getColor2() {
return Theme.isCurrentThemeDark() ? darkColor2 : color2;
}
public int getColor3() {
return Theme.isCurrentThemeDark() ? darkColor3 : color3;
}
public boolean hasColor2() {
return getColor2() != getColor1();
}
public boolean hasColor3() {
return getColor3() != getColor1();
}
public void appendString(StringBuilder sb) {
sb.append("#");
sb.append(id);
sb.append("{");
sb.append(color1);
if (color2 != color1) {
sb.append(",");
sb.append(color2);
if (color3 != color1) {
sb.append(",");
sb.append(color3);
}
}
if (darkColor1 != color1 || darkColor2 != color2 || darkColor3 != color3) {
sb.append("@");
sb.append(darkColor1);
if (darkColor2 != darkColor1) {
sb.append(",");
sb.append(darkColor2);
if (darkColor3 != darkColor1) {
sb.append(",");
sb.append(darkColor3);
}
}
}
sb.append("}");
}
public static PeerColor fromString(String string) {
if (string == null || string.isEmpty() || string.charAt(0) != '#')
return null;
int index = string.indexOf('{');
if (index < 0) return null;
try {
final PeerColor peerColor = new PeerColor();
peerColor.id = Utilities.parseInt(string.substring(1, index));
final String[] parts = string.substring(index + 1, string.length() - 1).split("@");
String[] colorsString = parts[0].split(",");
peerColor.color1 = Utilities.parseInt(colorsString[0]);
peerColor.color2 = colorsString.length >= 2 ? Utilities.parseInt(colorsString[1]) : peerColor.color1;
peerColor.color3 = colorsString.length >= 3 ? Utilities.parseInt(colorsString[2]) : peerColor.color1;
if (parts.length >= 2) {
colorsString = parts[1].split(",");
peerColor.darkColor1 = Utilities.parseInt(colorsString[0]);
peerColor.darkColor2 = colorsString.length >= 2 ? Utilities.parseInt(colorsString[1]) : peerColor.color1;
peerColor.darkColor3 = colorsString.length >= 3 ? Utilities.parseInt(colorsString[2]) : peerColor.color1;
} else {
peerColor.darkColor1 = peerColor.color1;
peerColor.darkColor2 = peerColor.color2;
peerColor.darkColor3 = peerColor.color3;
}
return peerColor;
} catch (Exception e) {
FileLog.e(e);
}
return null;
}
}
private void resetAppConfig() { private void resetAppConfig() {
getfileExperimentalParams = false; getfileExperimentalParams = false;
collectDeviceStats = false; collectDeviceStats = false;
@ -3819,7 +4145,8 @@ public class MessagesController extends BaseController implements NotificationCe
if (bigSize != null) { if (bigSize != null) {
user.photo.photo_big = bigSize.location; user.photo.photo_big = bigSize.location;
} }
getMessagesStorage().clearUserPhotos(user.id); getDialogPhotos(user.id).reset();
getDialogPhotos(user.id).load(0, DialogPhotos.STEP);
ArrayList<TLRPC.User> users = new ArrayList<>(); ArrayList<TLRPC.User> users = new ArrayList<>();
users.add(user); users.add(user);
getMessagesStorage().putUsersAndChats(users, null, false, true); getMessagesStorage().putUsersAndChats(users, null, false, true);
@ -5096,7 +5423,7 @@ public class MessagesController extends BaseController implements NotificationCe
TLRPC.TL_messages_chatFull res = (TLRPC.TL_messages_chatFull) response; TLRPC.TL_messages_chatFull res = (TLRPC.TL_messages_chatFull) response;
getMessagesStorage().putUsersAndChats(res.users, res.chats, true, true); getMessagesStorage().putUsersAndChats(res.users, res.chats, true, true);
getMessagesStorage().updateChatInfo(res.full_chat, false); getMessagesStorage().updateChatInfo(res.full_chat, false);
getStoriesController().updateStoriesFromFullPeer(dialogId, res.full_chat.stories);
if (ChatObject.isChannel(chat)) { if (ChatObject.isChannel(chat)) {
Integer value = dialogs_read_inbox_max.get(dialogId); Integer value = dialogs_read_inbox_max.get(dialogId);
if (value == null) { if (value == null) {
@ -5211,6 +5538,7 @@ public class MessagesController extends BaseController implements NotificationCe
putChats(res.chats, false); putChats(res.chats, false);
res.full_user.user = getUser(res.full_user.id); res.full_user.user = getUser(res.full_user.id);
getMessagesStorage().updateUserInfo(userFull, false); getMessagesStorage().updateUserInfo(userFull, false);
getStoriesController().updateStoriesFromFullPeer(dialogId, userFull.stories);
ChatThemeController.getInstance(currentAccount).saveChatWallpaper(res.full_user.id, res.full_user.wallpaper); ChatThemeController.getInstance(currentAccount).saveChatWallpaper(res.full_user.id, res.full_user.wallpaper);
AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.runOnUIThread(() -> {
@ -5721,55 +6049,340 @@ public class MessagesController extends BaseController implements NotificationCe
}); });
} }
public void loadDialogPhotos(long did, int count, int maxId, boolean fromCache, int classGuid) { private LongSparseArray<DialogPhotos> dialogPhotos = new LongSparseArray<>();
if (fromCache) {
getMessagesStorage().getDialogPhotos(did, count, maxId, classGuid); public DialogPhotos getDialogPhotos(long dialogId) {
} else { DialogPhotos photos = dialogPhotos.get(dialogId);
if (did > 0) { if (photos == null) {
TLRPC.User user = getUser(did); dialogPhotos.put(dialogId, photos = new DialogPhotos(dialogId));
}
return photos;
}
public class DialogPhotos {
public final long dialogId;
public final ArrayList<TLRPC.Photo> photos = new ArrayList<>();
public boolean fromCache = true;
public boolean loaded = false;
public final static int STEP = 80;
public DialogPhotos(long dialogId) {
this.dialogId = dialogId;
}
public void loadAfter(int position, boolean after) {
if (photos.isEmpty()) {
load(0, STEP);
return;
}
if (position < 0) {
position += photos.size();
}
if (position >= photos.size()) {
position -= photos.size();
}
if (position < 0 || position >= photos.size()) {
return;
}
boolean hasEmpty = false;
for (int i = 0; i < photos.size(); ++i) {
if (photos.get(i) == null) {
hasEmpty = true;
break;
}
}
if (!hasEmpty) {
return;
}
if (after) {
int p = position;
while (photos.get(p) != null) {
p++;
if (p >= photos.size()) {
p = 0;
}
}
int count;
for (count = 0; count <= STEP && p + count < photos.size() && photos.get(p + count) == null; ++count);
if (count > 0) {
load(p, count);
}
} else {
int p = position;
while (photos.get(p) != null) {
p--;
if (p < 0) {
p = photos.size() - 1;
}
}
int count;
for (count = 0; count <= STEP && p - count >= 0 && photos.get(p - count) == null; ++count);
if (count > 0) {
load(p - count, count);
}
}
}
private boolean loading;
public void load(int offset, int count) {
if (loading || count <= 0 || offset < 0) {
return;
}
loading = true;
int reqId;
if (dialogId >= 0) {
TLRPC.User user = getUser(dialogId);
if (user == null) { if (user == null) {
loading = false;
return; return;
} }
TLRPC.TL_photos_getUserPhotos req = new TLRPC.TL_photos_getUserPhotos(); TLRPC.TL_photos_getUserPhotos req = new TLRPC.TL_photos_getUserPhotos();
req.offset = offset;
req.limit = count; req.limit = count;
req.offset = 0; req.max_id = 0;
req.max_id = maxId;
req.user_id = getInputUser(user); req.user_id = getInputUser(user);
int reqId = getConnectionsManager().sendRequest(req, (response, error) -> { reqId = getConnectionsManager().sendRequest(req, (response, error) -> {
if (error == null) { if (error == null) {
TLRPC.photos_Photos res = (TLRPC.photos_Photos) response; final TLRPC.photos_Photos res = (TLRPC.photos_Photos) response;
processLoadedUserPhotos(res, null, did, count, maxId, false, classGuid); getMessagesStorage().putUsersAndChats(res.users, null, true, true);
AndroidUtilities.runOnUIThread(() -> {
putUsers(res.users, false);
onLoaded(offset, count, res);
});
} }
}); });
getConnectionsManager().bindRequestToGuid(reqId, classGuid); } else {
} else if (did < 0) {
TLRPC.TL_messages_search req = new TLRPC.TL_messages_search(); TLRPC.TL_messages_search req = new TLRPC.TL_messages_search();
req.filter = new TLRPC.TL_inputMessagesFilterChatPhotos(); req.filter = new TLRPC.TL_inputMessagesFilterChatPhotos();
req.add_offset = offset;
req.limit = count; req.limit = count;
req.offset_id = maxId; req.offset_id = 0;
req.q = ""; req.q = "";
req.peer = getInputPeer(did); req.peer = getInputPeer(dialogId);
int reqId = getConnectionsManager().sendRequest(req, (response, error) -> { reqId = getConnectionsManager().sendRequest(req, (response, error) -> {
if (error == null) { if (error == null) {
TLRPC.messages_Messages messages = (TLRPC.messages_Messages) response; TLRPC.messages_Messages messages = (TLRPC.messages_Messages) response;
TLRPC.TL_photos_photos res = new TLRPC.TL_photos_photos(); getMessagesStorage().putUsersAndChats(messages.users, messages.chats, true, true);
ArrayList<TLRPC.Message> arrayList = new ArrayList<>(); AndroidUtilities.runOnUIThread(() -> {
res.count = messages.count; putUsers(messages.users, false);
res.users.addAll(messages.users); putChats(messages.chats, false);
for (int a = 0; a < messages.messages.size(); a++) { TLRPC.photos_Photos res = new TLRPC.TL_photos_photos();
TLRPC.Message message = messages.messages.get(a); res.count = messages.count;
if (message.action == null || message.action.photo == null) { for (int a = 0; a < messages.messages.size(); a++) {
continue; TLRPC.Message message = messages.messages.get(a);
if (message.action == null || message.action.photo == null) {
continue;
}
res.photos.add(message.action.photo);
} }
res.photos.add(message.action.photo); onLoaded(offset, count, res);
arrayList.add(message); });
}
processLoadedUserPhotos(res, arrayList, did, count, maxId, false, classGuid);
} }
}); });
getConnectionsManager().bindRequestToGuid(reqId, classGuid);
} }
} }
private void onLoaded(int offset, int count, TLRPC.photos_Photos res) {
boolean wasLoaded = loaded;
loading = false;
loaded = true;
fromCache = false;
res.count = Math.max(res.count, res.photos.size());
boolean reset = res.count != photos.size() || offset + count > photos.size();
if (!reset) {
for (int i = 0; i < res.photos.size(); ++i) {
if (photos.get(offset + i) != null && photos.get(offset + i).id != res.photos.get(i).id) {
reset = true;
break;
}
}
}
if (reset) {
photos.clear();
for (int i = 0; i < res.count; ++i) {
int lindex = i - offset;
photos.add(lindex >= 0 && lindex < res.photos.size() ? res.photos.get(lindex) : null);
}
} else {
for (int i = 0; i < res.photos.size(); ++i) {
photos.set(offset + i, res.photos.get(i));
}
}
saveCache();
getNotificationCenter().postNotificationName(NotificationCenter.dialogPhotosUpdate, this);
if (!wasLoaded && offset == 0 && count < photos.size() && photos.size() - count > STEP) {
load(photos.size() - STEP, STEP);
}
}
public void addPhotoAtStart(TLRPC.Photo photo) {
if (true) {
return;
}
if (photo == null || !loaded && !fromCache) {
return;
}
removePhotoInternal(photo.id);
photos.add(0, photo);
saveCache();
getNotificationCenter().postNotificationName(NotificationCenter.dialogPhotosUpdate, this);
}
public void removePhoto(long photoId) {
if (removePhotoInternal(photoId)) {
saveCache();
getNotificationCenter().postNotificationName(NotificationCenter.dialogPhotosUpdate, this);
}
}
public void moveToStart(int index) {
if (index < 0 || index >= photos.size()) {
return;
}
photos.add(0, photos.remove(index));
saveCache();
getNotificationCenter().postNotificationName(NotificationCenter.dialogPhotosUpdate, this);
}
private boolean removePhotoInternal(long photoId) {
boolean changed = false;
for (int i = 0; i < photos.size(); ++i) {
TLRPC.Photo p = photos.get(i);
if (p != null && p.id == photoId) {
photos.remove(i);
i--;
changed = true;
}
}
return changed;
}
public int getCount() {
return photos.size();
}
public void loadCache() {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
SQLiteDatabase database = getMessagesStorage().getDatabase();
SQLiteCursor cursor = null;
int count = 0;
final HashMap<Integer, TLRPC.Photo> photoEntries = new HashMap<>();
try {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT count FROM dialog_photos_count WHERE uid = %d", dialogId));
if (cursor.next()) {
count = cursor.intValue(0);
}
cursor.dispose();
cursor = null;
cursor = database.queryFinalized(String.format(Locale.US, "SELECT num, data FROM dialog_photos WHERE uid = %d", dialogId));
while (cursor.next()) {
int position = cursor.intValue(0);
TLRPC.Photo photo = null;
NativeByteBuffer data = cursor.byteBufferValue(1);
if (data != null) {
int magic = data.readInt32(false);
if (magic == TLRPC.TL_null.constructor) {
photo = null;
} else {
photo = TLRPC.Photo.TLdeserialize(data, magic, false);
}
}
if (photo != null) {
photoEntries.put(position, photo);
}
}
cursor.dispose();
cursor = null;
} catch (Exception e) {
} finally {
if (cursor != null) {
cursor.dispose();
cursor = null;
}
}
count = Math.max(count, photoEntries.size());
final int finalCount = count;
AndroidUtilities.runOnUIThread(() -> {
photos.clear();
for (int i = 0; i < finalCount; ++i) {
photos.add(null);
}
for (Map.Entry<Integer, TLRPC.Photo> entry : photoEntries.entrySet()) {
photos.set(entry.getKey(), entry.getValue());
}
getNotificationCenter().postNotificationName(NotificationCenter.dialogPhotosUpdate, this);
load(0, STEP);
});
});
}
private void saveCache() {
getMessagesStorage().getStorageQueue().postRunnable(() -> {
SQLiteDatabase database = getMessagesStorage().getDatabase();
SQLitePreparedStatement state = null;
try {
database.executeFast("DELETE FROM dialog_photos WHERE uid = " + dialogId).stepThis().dispose();
database.executeFast("DELETE FROM dialog_photos_count WHERE uid = " + dialogId).stepThis().dispose();
database.executeFast("REPLACE INTO dialog_photos_count VALUES(" + dialogId + ", " + photos.size() + ")").stepThis().dispose();
state = database.executeFast("REPLACE INTO dialog_photos VALUES(?, ?, ?, ?)");
for (int i = 0; i < photos.size(); ++i) {
TLRPC.Photo photo = photos.get(i);
if (photo == null) {
continue;
}
if (photo.file_reference == null) {
photo.file_reference = new byte[0];
}
state.requery();
NativeByteBuffer data = new NativeByteBuffer(photo.getObjectSize());
photo.serializeToStream(data);
state.bindLong(1, dialogId);
state.bindLong(2, photo.id);
state.bindInteger(3, i);
state.bindByteBuffer(4, data);
state.step();
data.reuse();
}
state.dispose();
state = null;
} catch (Exception e) {
} finally {
if (state != null) {
state.dispose();
state = null;
}
}
});
}
public void reset() {
photos.clear();
fromCache = true;
saveCache();
}
} }
public void blockPeer(long id) { public void blockPeer(long id) {
@ -6005,7 +6618,17 @@ public class MessagesController extends BaseController implements NotificationCe
} }
public void deleteUserPhoto(TLRPC.InputPhoto photo) { public void deleteUserPhoto(TLRPC.InputPhoto photo) {
long dialogId = getUserConfig().getClientUserId();
if (photo == null) { if (photo == null) {
DialogPhotos photos = getDialogPhotos(dialogId);
if (photos != null && photos.photos.size() > 0) {
TLRPC.Photo removingPhoto = photos.photos.get(0);
if (removingPhoto != null) {
photos.removePhoto(removingPhoto.id);
}
}
TLRPC.TL_photos_updateProfilePhoto req = new TLRPC.TL_photos_updateProfilePhoto(); TLRPC.TL_photos_updateProfilePhoto req = new TLRPC.TL_photos_updateProfilePhoto();
req.id = new TLRPC.TL_inputPhotoEmpty(); req.id = new TLRPC.TL_inputPhotoEmpty();
// getUserConfig().getCurrentUser().photo = new TLRPC.TL_userProfilePhotoEmpty(); // getUserConfig().getCurrentUser().photo = new TLRPC.TL_userProfilePhotoEmpty();
@ -6051,7 +6674,7 @@ public class MessagesController extends BaseController implements NotificationCe
user1.photo = new TLRPC.TL_userProfilePhotoEmpty(); user1.photo = new TLRPC.TL_userProfilePhotoEmpty();
} }
TLRPC.UserFull userFull = getUserFull(getUserConfig().getClientUserId()); TLRPC.UserFull userFull = getUserFull(dialogId);
userFull.profile_photo = photos_photo.photo; userFull.profile_photo = photos_photo.photo;
getMessagesStorage().updateUserInfo(userFull, false); getMessagesStorage().updateUserInfo(userFull, false);
@ -6068,25 +6691,13 @@ public class MessagesController extends BaseController implements NotificationCe
} else { } else {
TLRPC.TL_photos_deletePhotos req = new TLRPC.TL_photos_deletePhotos(); TLRPC.TL_photos_deletePhotos req = new TLRPC.TL_photos_deletePhotos();
req.id.add(photo); req.id.add(photo);
getDialogPhotos(dialogId).removePhoto(photo.id);
getConnectionsManager().sendRequest(req, (response, error) -> { getConnectionsManager().sendRequest(req, (response, error) -> {
}); });
} }
} }
public void processLoadedUserPhotos(final TLRPC.photos_Photos res, ArrayList<TLRPC.Message> messages, long did, int count, int maxId, boolean fromCache, int classGuid) {
if (!fromCache) {
getMessagesStorage().putUsersAndChats(res.users, null, true, true);
getMessagesStorage().putDialogPhotos(did, res, messages);
} else if (res == null || res.photos.isEmpty()) {
loadDialogPhotos(did, count, maxId, false, classGuid);
}
AndroidUtilities.runOnUIThread(() -> {
putUsers(res.users, fromCache);
getNotificationCenter().postNotificationName(NotificationCenter.dialogPhotosLoaded, did, count, fromCache, classGuid, res.photos, messages);
});
}
public void uploadAndApplyUserAvatar(TLRPC.FileLocation location) { public void uploadAndApplyUserAvatar(TLRPC.FileLocation location) {
if (location == null) { if (location == null) {
return; return;
@ -8425,16 +9036,6 @@ public class MessagesController extends BaseController implements NotificationCe
reload = ((SystemClock.elapsedRealtime() - lastScheduledServerQueryTime.get(dialogId, 0L)) > 60 * 1000); reload = ((SystemClock.elapsedRealtime() - lastScheduledServerQueryTime.get(dialogId, 0L)) > 60 * 1000);
} else { } else {
reload = resCount == 0 && (!isInitialLoading || (SystemClock.elapsedRealtime() - lastServerQueryTime.get(dialogId, 0L)) > 60 * 1000 || (isCache && isTopic)); reload = resCount == 0 && (!isInitialLoading || (SystemClock.elapsedRealtime() - lastServerQueryTime.get(dialogId, 0L)) > 60 * 1000 || (isCache && isTopic));
// if (isCache && dialogId < 0) {
// TLRPC.Chat chat = getChat(-dialogId);
// if (chat == null) {
// chat = chatsDict.get(-dialogId);
// }
// if (chat != null && mode == 0 && ChatObject.isNotInChat(chat) && (SystemClock.elapsedRealtime() - lastServerQueryTime.get(dialogId, 0L)) > 24 * 60 * 60 * 1000) {
// messagesRes.messages.clear();
// reload = true;
// }
// }
} }
if (!DialogObject.isEncryptedDialog(dialogId) && isCache && reload) { if (!DialogObject.isEncryptedDialog(dialogId) && isCache && reload) {
int hash; int hash;
@ -8556,6 +9157,14 @@ public class MessagesController extends BaseController implements NotificationCe
if (loaderLogger != null) { if (loaderLogger != null) {
loaderLogger.logStageQueueProcessing(); loaderLogger.logStageQueueProcessing();
} }
if (mode == ChatActivity.MODE_SCHEDULED) {
Collections.sort(objects, (o1, o2) -> {
if (o1.messageOwner.date == o2.messageOwner.date && o1.getId() >= 0 && o2.getId() >= 0) {
return o2.getId() - o1.getId();
}
return o2.messageOwner.date - o1.messageOwner.date;
});
}
AndroidUtilities.runOnUIThread(() -> { AndroidUtilities.runOnUIThread(() -> {
putUsers(messagesRes.users, isCache); putUsers(messagesRes.users, isCache);
putChats(messagesRes.chats, isCache); putChats(messagesRes.chats, isCache);
@ -8584,7 +9193,7 @@ public class MessagesController extends BaseController implements NotificationCe
} }
} }
if (mode == 1 && count == 1) { if (mode == 1 && count == 1) {
getNotificationCenter().postNotificationName(NotificationCenter.scheduledMessagesUpdated, dialogId, objects.size()); getNotificationCenter().postNotificationName(NotificationCenter.scheduledMessagesUpdated, dialogId, objects.size(), false);
} }
if (!DialogObject.isEncryptedDialog(dialogId)) { if (!DialogObject.isEncryptedDialog(dialogId)) {
@ -11992,7 +12601,7 @@ public class MessagesController extends BaseController implements NotificationCe
File src = new File(videoPath); File src = new File(videoPath);
src.renameTo(destFile); src.renameTo(destFile);
} }
getMessagesStorage().addDialogPhoto(-chatId, photo); getDialogPhotos(-chatId).addPhotoAtStart(photo);
} }
} }
processUpdates(updates, false); processUpdates(updates, false);
@ -14761,8 +15370,8 @@ public class MessagesController extends BaseController implements NotificationCe
chatInfoToUpdate = new ArrayList<>(); chatInfoToUpdate = new ArrayList<>();
} }
chatInfoToUpdate.add(update.participants); chatInfoToUpdate.add(update.participants);
} if (baseUpdate instanceof TLRPC.TL_updateStory) { } if (baseUpdate instanceof TL_stories.TL_updateStory) {
getStoriesController().processUpdate((TLRPC.TL_updateStory) baseUpdate); getStoriesController().processUpdate((TL_stories.TL_updateStory) baseUpdate);
} else if (baseUpdate instanceof TLRPC.TL_updateUserStatus) { } else if (baseUpdate instanceof TLRPC.TL_updateUserStatus) {
interfaceUpdateMask |= UPDATE_MASK_STATUS; interfaceUpdateMask |= UPDATE_MASK_STATUS;
if (updatesOnMainThread == null) { if (updatesOnMainThread == null) {
@ -14785,6 +15394,9 @@ public class MessagesController extends BaseController implements NotificationCe
TLRPC.TL_updateUserPhoto update = (TLRPC.TL_updateUserPhoto) baseUpdate; TLRPC.TL_updateUserPhoto update = (TLRPC.TL_updateUserPhoto) baseUpdate;
interfaceUpdateMask |= UPDATE_MASK_AVATAR; interfaceUpdateMask |= UPDATE_MASK_AVATAR;
getMessagesStorage().clearUserPhotos(update.user_id); getMessagesStorage().clearUserPhotos(update.user_id);
if (dialogPhotos.get(update.user_id) != null) {
dialogPhotos.get(update.user_id).reset();
}
if (updatesOnMainThread == null) { if (updatesOnMainThread == null) {
updatesOnMainThread = new ArrayList<>(); updatesOnMainThread = new ArrayList<>();
} }
@ -14803,8 +15415,8 @@ public class MessagesController extends BaseController implements NotificationCe
updatesOnMainThread = new ArrayList<>(); updatesOnMainThread = new ArrayList<>();
} }
updatesOnMainThread.add(baseUpdate); updatesOnMainThread.add(baseUpdate);
} else if (baseUpdate instanceof TLRPC.TL_updateReadStories) { } else if (baseUpdate instanceof TL_stories.TL_updateReadStories) {
TLRPC.TL_updateReadStories updateReadStories = (TLRPC.TL_updateReadStories) baseUpdate; TL_stories.TL_updateReadStories updateReadStories = (TL_stories.TL_updateReadStories) baseUpdate;
long dialogId = DialogObject.getPeerDialogId(updateReadStories.peer); long dialogId = DialogObject.getPeerDialogId(updateReadStories.peer);
getStoriesController().markStoriesAsReadFromServer(dialogId, updateReadStories.max_id); getStoriesController().markStoriesAsReadFromServer(dialogId, updateReadStories.max_id);
} else if (baseUpdate instanceof TLRPC.TL_updatePeerSettings) { } else if (baseUpdate instanceof TLRPC.TL_updatePeerSettings) {
@ -14955,6 +15567,7 @@ public class MessagesController extends BaseController implements NotificationCe
} }
if ((update.flags & 2) != 0) { if ((update.flags & 2) != 0) {
TLRPC.TL_message newMessage = new TLRPC.TL_message(); TLRPC.TL_message newMessage = new TLRPC.TL_message();
newMessage.invert_media = update.invert_media;
newMessage.local_id = newMessage.id = getUserConfig().getNewMessageId(); newMessage.local_id = newMessage.id = getUserConfig().getNewMessageId();
getUserConfig().saveConfig(false); getUserConfig().saveConfig(false);
newMessage.unread = true; newMessage.unread = true;
@ -14999,7 +15612,7 @@ public class MessagesController extends BaseController implements NotificationCe
} }
pushMessages.add(obj); pushMessages.add(obj);
} }
} else if (baseUpdate instanceof TLRPC.TL_updateStoriesStealthMode) { } else if (baseUpdate instanceof TL_stories.TL_updateStoriesStealthMode) {
if (updatesOnMainThread == null) { if (updatesOnMainThread == null) {
updatesOnMainThread = new ArrayList<>(); updatesOnMainThread = new ArrayList<>();
} }
@ -16025,8 +16638,8 @@ public class MessagesController extends BaseController implements NotificationCe
} }
int threadId = update.top_msg_id; int threadId = update.top_msg_id;
getMediaDataController().saveDraft(did, threadId, update.draft, null, true); getMediaDataController().saveDraft(did, threadId, update.draft, null, true);
} else if (baseUpdate instanceof TLRPC.TL_updateStoriesStealthMode) { } else if (baseUpdate instanceof TL_stories.TL_updateStoriesStealthMode) {
TLRPC.TL_updateStoriesStealthMode storiesStealthModeUpdate = (TLRPC.TL_updateStoriesStealthMode) baseUpdate; TL_stories.TL_updateStoriesStealthMode storiesStealthModeUpdate = (TL_stories.TL_updateStoriesStealthMode) baseUpdate;
getStoriesController().setStealthMode(storiesStealthModeUpdate.stealth_mode); getStoriesController().setStealthMode(storiesStealthModeUpdate.stealth_mode);
} else if (baseUpdate instanceof TLRPC.TL_updateReadFeaturedStickers) { } else if (baseUpdate instanceof TLRPC.TL_updateReadFeaturedStickers) {
getMediaDataController().markFeaturedStickersAsRead(false, false); getMediaDataController().markFeaturedStickersAsRead(false, false);
@ -17676,7 +18289,7 @@ public class MessagesController extends BaseController implements NotificationCe
} }
for (int a = 0, N = reasons.size(); a < N; a++) { for (int a = 0, N = reasons.size(); a < N; a++) {
TLRPC.TL_restrictionReason reason = reasons.get(a); TLRPC.TL_restrictionReason reason = reasons.get(a);
if ("all".equals(reason.platform) || !BuildVars.isStandaloneApp() && !BuildVars.isBetaApp() && "android".equals(reason.platform)) { if ("all".equals(reason.platform) || !ApplicationLoader.isStandaloneBuild() && !BuildVars.isBetaApp() && "android".equals(reason.platform)) {
return reason.text; return reason.text;
} }
} }
@ -18352,12 +18965,12 @@ public class MessagesController extends BaseController implements NotificationCe
} }
final ChatlistUpdatesStat finalStat = stat; final ChatlistUpdatesStat finalStat = stat;
finalStat.loading = false; finalStat.loading = false;
TLRPC.TL_chatlists_getChatlistUpdates req = new TLRPC.TL_chatlists_getChatlistUpdates(); TL_chatlists.TL_chatlists_getChatlistUpdates req = new TL_chatlists.TL_chatlists_getChatlistUpdates();
req.chatlist = new TLRPC.TL_inputChatlistDialogFilter(); req.chatlist = new TL_chatlists.TL_inputChatlistDialogFilter();
req.chatlist.filter_id = filterId; req.chatlist.filter_id = filterId;
getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> { getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> {
if (res instanceof TLRPC.TL_chatlists_chatlistUpdates) { if (res instanceof TL_chatlists.TL_chatlists_chatlistUpdates) {
TLRPC.TL_chatlists_chatlistUpdates updates = (TLRPC.TL_chatlists_chatlistUpdates) res; TL_chatlists.TL_chatlists_chatlistUpdates updates = (TL_chatlists.TL_chatlists_chatlistUpdates) res;
putChats(updates.chats, false); putChats(updates.chats, false);
putUsers(updates.users, false); putUsers(updates.users, false);
chatlistFoldersUpdates.put(filterId, new ChatlistUpdatesStat(updates)); chatlistFoldersUpdates.put(filterId, new ChatlistUpdatesStat(updates));
@ -18369,7 +18982,7 @@ public class MessagesController extends BaseController implements NotificationCe
} }
} }
public TLRPC.TL_chatlists_chatlistUpdates getChatlistFolderUpdates(int filterId) { public TL_chatlists.TL_chatlists_chatlistUpdates getChatlistFolderUpdates(int filterId) {
ChatlistUpdatesStat stat = chatlistFoldersUpdates.get(filterId); ChatlistUpdatesStat stat = chatlistFoldersUpdates.get(filterId);
if (stat == null) { if (stat == null) {
return null; return null;

View file

@ -33,6 +33,7 @@ import org.telegram.tgnet.NativeByteBuffer;
import org.telegram.tgnet.RequestDelegate; import org.telegram.tgnet.RequestDelegate;
import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.tgnet.tl.TL_stories;
import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Adapters.DialogsSearchAdapter; import org.telegram.ui.Adapters.DialogsSearchAdapter;
import org.telegram.ui.DialogsActivity; import org.telegram.ui.DialogsActivity;
@ -96,7 +97,7 @@ public class MessagesStorage extends BaseController {
} }
} }
public final static int LAST_DB_VERSION = 134; public final static int LAST_DB_VERSION = 135;
private boolean databaseMigrationInProgress; private boolean databaseMigrationInProgress;
public boolean showClearDatabaseAlert; public boolean showClearDatabaseAlert;
private LongSparseIntArray dialogIsForum = new LongSparseIntArray(); private LongSparseIntArray dialogIsForum = new LongSparseIntArray();
@ -461,7 +462,7 @@ public class MessagesStorage extends BaseController {
"channel_users_v2", "channel_users_v2",
"channel_admins_v3", "channel_admins_v3",
"contacts", "contacts",
"user_photos", "dialog_photos",
"dialog_settings", "dialog_settings",
"web_recent_v3", "web_recent_v3",
"stickers_v2", "stickers_v2",
@ -589,7 +590,8 @@ public class MessagesStorage extends BaseController {
database.executeFast("CREATE TABLE channel_users_v2(did INTEGER, uid INTEGER, date INTEGER, data BLOB, PRIMARY KEY(did, uid))").stepThis().dispose(); database.executeFast("CREATE TABLE channel_users_v2(did INTEGER, uid INTEGER, date INTEGER, data BLOB, PRIMARY KEY(did, uid))").stepThis().dispose();
database.executeFast("CREATE TABLE channel_admins_v3(did INTEGER, uid INTEGER, data BLOB, PRIMARY KEY(did, uid))").stepThis().dispose(); database.executeFast("CREATE TABLE channel_admins_v3(did INTEGER, uid INTEGER, data BLOB, PRIMARY KEY(did, uid))").stepThis().dispose();
database.executeFast("CREATE TABLE contacts(uid INTEGER PRIMARY KEY, mutual INTEGER)").stepThis().dispose(); database.executeFast("CREATE TABLE contacts(uid INTEGER PRIMARY KEY, mutual INTEGER)").stepThis().dispose();
database.executeFast("CREATE TABLE user_photos(uid INTEGER, id INTEGER, data BLOB, PRIMARY KEY (uid, id))").stepThis().dispose(); database.executeFast("CREATE TABLE dialog_photos(uid INTEGER, id INTEGER, num INTEGER, data BLOB, PRIMARY KEY (uid, id))").stepThis().dispose();
database.executeFast("CREATE TABLE dialog_photos_count(uid INTEGER PRIMARY KEY, count INTEGER)").stepThis().dispose();
database.executeFast("CREATE TABLE dialog_settings(did INTEGER PRIMARY KEY, flags INTEGER);").stepThis().dispose(); database.executeFast("CREATE TABLE dialog_settings(did INTEGER PRIMARY KEY, flags INTEGER);").stepThis().dispose();
database.executeFast("CREATE TABLE web_recent_v3(id TEXT, type INTEGER, image_url TEXT, thumb_url TEXT, local_url TEXT, width INTEGER, height INTEGER, size INTEGER, date INTEGER, document BLOB, PRIMARY KEY (id, type));").stepThis().dispose(); database.executeFast("CREATE TABLE web_recent_v3(id TEXT, type INTEGER, image_url TEXT, thumb_url TEXT, local_url TEXT, width INTEGER, height INTEGER, size INTEGER, date INTEGER, document BLOB, PRIMARY KEY (id, type));").stepThis().dispose();
database.executeFast("CREATE TABLE stickers_v2(id INTEGER PRIMARY KEY, data BLOB, date INTEGER, hash INTEGER);").stepThis().dispose(); database.executeFast("CREATE TABLE stickers_v2(id INTEGER PRIMARY KEY, data BLOB, date INTEGER, hash INTEGER);").stepThis().dispose();
@ -1332,6 +1334,8 @@ public class MessagesStorage extends BaseController {
database.executeFast("DELETE FROM chat_pinned_count").stepThis().dispose(); database.executeFast("DELETE FROM chat_pinned_count").stepThis().dispose();
database.executeFast("DELETE FROM profile_stories").stepThis().dispose(); database.executeFast("DELETE FROM profile_stories").stepThis().dispose();
database.executeFast("DELETE FROM story_pushes").stepThis().dispose(); database.executeFast("DELETE FROM story_pushes").stepThis().dispose();
database.executeFast("DELETE FROM dialog_photos").stepThis().dispose();
database.executeFast("DELETE FROM dialog_photos_count").stepThis().dispose();
cursor = database.queryFinalized("SELECT did FROM dialogs WHERE 1"); cursor = database.queryFinalized("SELECT did FROM dialogs WHERE 1");
while (cursor.next()) { while (cursor.next()) {
@ -4045,51 +4049,11 @@ public class MessagesStorage extends BaseController {
}); });
} }
public void getDialogPhotos(long did, int count, int maxId, int classGuid) {
storageQueue.postRunnable(() -> {
SQLiteCursor cursor = null;
try {
if (maxId != 0) {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM user_photos WHERE uid = %d AND id < %d ORDER BY rowid ASC LIMIT %d", did, maxId, count));
} else {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM user_photos WHERE uid = %d ORDER BY rowid ASC LIMIT %d", did, count));
}
TLRPC.photos_Photos res = new TLRPC.TL_photos_photos();
ArrayList<TLRPC.Message> messages = new ArrayList<>();
while (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
TLRPC.Photo photo = TLRPC.Photo.TLdeserialize(data, data.readInt32(false), false);
if (data.remaining() > 0) {
messages.add(TLRPC.Message.TLdeserialize(data, data.readInt32(false), false));
} else {
messages.add(null);
}
data.reuse();
res.photos.add(photo);
}
}
cursor.dispose();
cursor = null;
Utilities.stageQueue.postRunnable(() -> getMessagesController().processLoadedUserPhotos(res, messages, did, count, maxId, true, classGuid));
} catch (Exception e) {
checkSQLException(e);
} finally {
if (cursor != null) {
cursor.dispose();
}
}
});
}
public void clearUserPhotos(long dialogId) { public void clearUserPhotos(long dialogId) {
storageQueue.postRunnable(() -> { storageQueue.postRunnable(() -> {
try { try {
database.executeFast("DELETE FROM user_photos WHERE uid = " + dialogId).stepThis().dispose(); database.executeFast("DELETE FROM dialog_photos WHERE uid = " + dialogId).stepThis().dispose();
database.executeFast("DELETE FROM dialog_photos_count WHERE uid = " + dialogId).stepThis().dispose();
} catch (Exception e) { } catch (Exception e) {
checkSQLException(e); checkSQLException(e);
} }
@ -4099,7 +4063,8 @@ public class MessagesStorage extends BaseController {
public void clearUserPhoto(long dialogId, long pid) { public void clearUserPhoto(long dialogId, long pid) {
storageQueue.postRunnable(() -> { storageQueue.postRunnable(() -> {
try { try {
database.executeFast("DELETE FROM user_photos WHERE uid = " + dialogId + " AND id = " + pid).stepThis().dispose(); database.executeFast("DELETE FROM dialog_photos WHERE uid = " + dialogId + " AND id = " + pid).stepThis().dispose();
database.executeFast("UPDATE dialog_photos_count SET count = count - 1 WHERE uid = " + dialogId + " AND count > 0").stepThis().dispose();
} catch (Exception e) { } catch (Exception e) {
checkSQLException(e); checkSQLException(e);
} }
@ -4267,92 +4232,6 @@ public class MessagesStorage extends BaseController {
}); });
} }
public void putDialogPhotos(long did, TLRPC.photos_Photos photos, ArrayList<TLRPC.Message> messages) {
if (photos == null) {
return;
}
storageQueue.postRunnable(() -> {
putDialogPhotosInternal(did, photos, messages);
});
}
private void putDialogPhotosInternal(long did, TLRPC.photos_Photos photos, ArrayList<TLRPC.Message> messages) {
SQLitePreparedStatement state = null;
try {
database.executeFast("DELETE FROM user_photos WHERE uid = " + did).stepThis().dispose();
state = database.executeFast("REPLACE INTO user_photos VALUES(?, ?, ?)");
for (int a = 0, N = photos.photos.size(); a < N; a++) {
TLRPC.Photo photo = photos.photos.get(a);
if (photo instanceof TLRPC.TL_photoEmpty || photo == null) {
continue;
}
if (photo.file_reference == null) {
photo.file_reference = new byte[0];
}
state.requery();
int size = photo.getObjectSize();
if (messages != null && a < messages.size() && messages.get(a) != null) {
size += messages.get(a).getObjectSize();
}
NativeByteBuffer data = new NativeByteBuffer(size);
photo.serializeToStream(data);
if (messages != null && a < messages.size() && messages.get(a) != null) {
messages.get(a).serializeToStream(data);
}
state.bindLong(1, did);
state.bindLong(2, photo.id);
state.bindByteBuffer(3, data);
state.step();
data.reuse();
}
state.dispose();
state = null;
} catch (Exception e) {
checkSQLException(e);
} finally {
if (state != null) {
state.dispose();
}
}
}
public void addDialogPhoto(long did, TLRPC.Photo photoToAdd) {
storageQueue.postRunnable(() -> {
SQLiteCursor cursor = null;
try {
cursor = database.queryFinalized(String.format(Locale.US, "SELECT data FROM user_photos WHERE uid = %d ORDER BY rowid ASC", did));
TLRPC.photos_Photos res = new TLRPC.TL_photos_photos();
ArrayList<TLRPC.Message> messages = new ArrayList<>();
while (cursor.next()) {
NativeByteBuffer data = cursor.byteBufferValue(0);
if (data != null) {
TLRPC.Photo photo = TLRPC.Photo.TLdeserialize(data, data.readInt32(false), false);
if (data.remaining() > 0) {
messages.add(TLRPC.Message.TLdeserialize(data, data.readInt32(false), false));
} else {
messages.add(null);
}
data.reuse();
res.photos.add(photo);
messages.add(null);
}
}
cursor.dispose();
cursor = null;
res.photos.add(0, photoToAdd);
putDialogPhotosInternal(did, res, messages);
} catch (Exception e) {
checkSQLException(e);
} finally {
if (cursor != null) {
cursor.dispose();
}
}
});
}
public void emptyMessagesMedia(long dialogId, ArrayList<Integer> mids) { public void emptyMessagesMedia(long dialogId, ArrayList<Integer> mids) {
storageQueue.postRunnable(() -> { storageQueue.postRunnable(() -> {
SQLiteCursor cursor = null; SQLiteCursor cursor = null;
@ -7056,6 +6935,19 @@ public class MessagesStorage extends BaseController {
if (info != null && info.inviterId != 0) { if (info != null && info.inviterId != 0) {
getUsersInternal("" + info.inviterId, loadedUsers); getUsersInternal("" + info.inviterId, loadedUsers);
} }
if (info != null && info.recent_requesters != null && !info.recent_requesters.isEmpty()) {
StringBuilder usersToLoad = new StringBuilder();
for (int i = 0; i < Math.min(3, info.recent_requesters.size()); ++i) {
long uid = info.recent_requesters.get(info.recent_requesters.size() - 1 - i);
if (i > 0) {
usersToLoad.append(',');
}
usersToLoad.append(uid);
}
if (usersToLoad.length() > 0) {
getUsersInternal(usersToLoad.toString(), loadedUsers);
}
}
cursor = getMessagesStorage().getDatabase().queryFinalized(String.format(Locale.US, "SELECT mid FROM chat_pinned_v2 WHERE uid = %d ORDER BY mid DESC", -chatId)); cursor = getMessagesStorage().getDatabase().queryFinalized(String.format(Locale.US, "SELECT mid FROM chat_pinned_v2 WHERE uid = %d ORDER BY mid DESC", -chatId));
while (cursor.next()) { while (cursor.next()) {
@ -8505,7 +8397,7 @@ public class MessagesStorage extends BaseController {
if (!cursor.isNull(6)) { if (!cursor.isNull(6)) {
data = cursor.byteBufferValue(6); data = cursor.byteBufferValue(6);
if (data != null) { if (data != null) {
message.replyStory = TLRPC.StoryItem.TLdeserialize(data, data.readInt32(false), false); message.replyStory = TL_stories.StoryItem.TLdeserialize(data, data.readInt32(false), false);
data.reuse(); data.reuse();
} }
} }
@ -8629,6 +8521,7 @@ public class MessagesStorage extends BaseController {
TLRPC.Message object = arrayList.get(a); TLRPC.Message object = arrayList.get(a);
object.replyMessage = message; object.replyMessage = message;
if (object.reply_to != null) { if (object.reply_to != null) {
object.reply_to.flags |= 16;
object.reply_to.reply_to_msg_id = message.id; object.reply_to.reply_to_msg_id = message.id;
} }
} }
@ -9433,6 +9326,31 @@ public class MessagesStorage extends BaseController {
return str.toString().toLowerCase(); return str.toString().toLowerCase();
} }
public boolean containsLocalDialog(long did) {
CountDownLatch countDownLatch = new CountDownLatch(1);
Boolean[] result = new Boolean[] { false };
storageQueue.postRunnable(() -> {
SQLiteCursor cursor = null;
try {
cursor = database.queryFinalized("SELECT date FROM dialogs WHERE did = " + did);
result[0] = cursor.next();
} catch (Exception e) {
checkSQLException(e);
} finally {
if (cursor != null) {
cursor.dispose();
}
}
countDownLatch.countDown();
});
try {
countDownLatch.await();
} catch (Exception e) {
checkSQLException(e);
}
return result[0];
}
private void putUsersInternal(List<TLRPC.User> users) throws Exception { private void putUsersInternal(List<TLRPC.User> users) throws Exception {
if (users == null || users.isEmpty()) { if (users == null || users.isEmpty()) {
return; return;
@ -12256,7 +12174,7 @@ public class MessagesStorage extends BaseController {
} }
cursor.dispose(); cursor.dispose();
cursor = null; cursor = null;
AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.scheduledMessagesUpdated, did, count)); AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.scheduledMessagesUpdated, did, count, true));
} catch (Exception e) { } catch (Exception e) {
checkSQLException(e); checkSQLException(e);
} finally { } finally {
@ -14180,22 +14098,8 @@ public class MessagesStorage extends BaseController {
} }
if (message.action instanceof TLRPC.TL_messageActionGeoProximityReached) { if (message.action instanceof TLRPC.TL_messageActionGeoProximityReached) {
TLRPC.TL_messageActionGeoProximityReached action = (TLRPC.TL_messageActionGeoProximityReached) message.action; TLRPC.TL_messageActionGeoProximityReached action = (TLRPC.TL_messageActionGeoProximityReached) message.action;
long id = MessageObject.getPeerId(action.from_id); addLoadPeerInfo(action.from_id, usersToLoad, chatsToLoad);
if (DialogObject.isUserDialog(id)) { addLoadPeerInfo(action.to_id, usersToLoad, chatsToLoad);
if (!usersToLoad.contains(id)) {
usersToLoad.add(id);
}
} else if (!chatsToLoad.contains(-id)) {
chatsToLoad.add(-id);
}
id = MessageObject.getPeerId(action.to_id);
if (id > 0) {
if (!usersToLoad.contains(id)) {
usersToLoad.add(id);
}
} else if (!chatsToLoad.contains(-id)) {
chatsToLoad.add(-id);
}
} }
if (!message.action.users.isEmpty()) { if (!message.action.users.isEmpty()) {
for (int a = 0; a < message.action.users.size(); a++) { for (int a = 0; a < message.action.users.size(); a++) {
@ -14226,82 +14130,28 @@ public class MessagesStorage extends BaseController {
TLRPC.TL_messageMediaPoll messageMediaPoll = (TLRPC.TL_messageMediaPoll) message.media; TLRPC.TL_messageMediaPoll messageMediaPoll = (TLRPC.TL_messageMediaPoll) message.media;
if (!messageMediaPoll.results.recent_voters.isEmpty()) { if (!messageMediaPoll.results.recent_voters.isEmpty()) {
for (int i = 0; i < messageMediaPoll.results.recent_voters.size(); i++) { for (int i = 0; i < messageMediaPoll.results.recent_voters.size(); i++) {
TLRPC.Peer peer = messageMediaPoll.results.recent_voters.get(i); addLoadPeerInfo(messageMediaPoll.results.recent_voters.get(i), usersToLoad, chatsToLoad);
if (peer.user_id != 0) {
usersToLoad.add(peer.user_id);
} else if (peer.chat_id != 0) {
chatsToLoad.add(-peer.chat_id);
} else if (peer.channel_id != 0) {
chatsToLoad.add(-peer.channel_id);
}
} }
} }
} }
if (message.media.peer != null) { if (message.media.peer != null) {
long dialogId = DialogObject.getPeerDialogId(message.media.peer); addLoadPeerInfo(message.media.peer, usersToLoad, chatsToLoad);
if (dialogId > 0) {
usersToLoad.add(dialogId);
}
if (dialogId < 0) {
chatsToLoad.add(-dialogId);
}
} }
} }
if (message.replies != null) { if (message.replies != null) {
for (int a = 0, N = message.replies.recent_repliers.size(); a < N; a++) { for (int a = 0, N = message.replies.recent_repliers.size(); a < N; a++) {
long id = MessageObject.getPeerId(message.replies.recent_repliers.get(a)); addLoadPeerInfo(message.replies.recent_repliers.get(a), usersToLoad, chatsToLoad);
if (DialogObject.isUserDialog(id)) {
if (!usersToLoad.contains(id)) {
usersToLoad.add(id);
}
} else if (DialogObject.isChatDialog(id)) {
if (!chatsToLoad.contains(-id)) {
chatsToLoad.add(-id);
}
}
} }
} }
if (message.reply_to != null && message.reply_to.reply_to_peer_id != null) { if (message.reply_to != null && message.reply_to.reply_to_peer_id != null) {
long id = MessageObject.getPeerId(message.reply_to.reply_to_peer_id); addLoadPeerInfo(message.reply_to.reply_to_peer_id, usersToLoad, chatsToLoad);
if (DialogObject.isUserDialog(id)) {
if (!usersToLoad.contains(id)) {
usersToLoad.add(id);
}
} else if (DialogObject.isChatDialog(id)) {
if (!chatsToLoad.contains(-id)) {
chatsToLoad.add(-id);
}
}
} }
if (message.fwd_from != null) { if (message.fwd_from != null) {
if (message.fwd_from.from_id instanceof TLRPC.TL_peerUser) { addLoadPeerInfo(message.fwd_from.from_id, usersToLoad, chatsToLoad);
if (!usersToLoad.contains(message.fwd_from.from_id.user_id)) { addLoadPeerInfo(message.fwd_from.saved_from_peer, usersToLoad, chatsToLoad);
usersToLoad.add(message.fwd_from.from_id.user_id); }
} if (message.reply_to != null && message.reply_to.reply_from != null && message.reply_to.reply_from.from_id != null) {
} else if (message.fwd_from.from_id instanceof TLRPC.TL_peerChannel) { addLoadPeerInfo(message.reply_to.reply_from.from_id, usersToLoad, chatsToLoad);
if (!chatsToLoad.contains(message.fwd_from.from_id.channel_id)) {
chatsToLoad.add(message.fwd_from.from_id.channel_id);
}
} else if (message.fwd_from.from_id instanceof TLRPC.TL_peerChat) {
if (!chatsToLoad.contains(message.fwd_from.from_id.chat_id)) {
chatsToLoad.add(message.fwd_from.from_id.chat_id);
}
}
if (message.fwd_from.saved_from_peer != null) {
if (message.fwd_from.saved_from_peer.user_id != 0) {
if (!chatsToLoad.contains(message.fwd_from.saved_from_peer.user_id)) {
usersToLoad.add(message.fwd_from.saved_from_peer.user_id);
}
} else if (message.fwd_from.saved_from_peer.channel_id != 0) {
if (!chatsToLoad.contains(message.fwd_from.saved_from_peer.channel_id)) {
chatsToLoad.add(message.fwd_from.saved_from_peer.channel_id);
}
} else if (message.fwd_from.saved_from_peer.chat_id != 0) {
if (!chatsToLoad.contains(message.fwd_from.saved_from_peer.chat_id)) {
chatsToLoad.add(message.fwd_from.saved_from_peer.chat_id);
}
}
}
} }
if (message.params != null) { if (message.params != null) {
String peerIdStr = message.params.get("fwd_peer"); String peerIdStr = message.params.get("fwd_peer");
@ -14316,6 +14166,22 @@ public class MessagesStorage extends BaseController {
} }
} }
private static void addLoadPeerInfo(TLRPC.Peer peer, ArrayList<Long> usersToLoad, ArrayList<Long> chatsToLoad) {
if (peer instanceof TLRPC.TL_peerUser) {
if (!usersToLoad.contains(peer.user_id)) {
usersToLoad.add(peer.user_id);
}
} else if (peer instanceof TLRPC.TL_peerChannel) {
if (!chatsToLoad.contains(peer.channel_id)) {
chatsToLoad.add(peer.channel_id);
}
} else if (peer instanceof TLRPC.TL_peerChat) {
if (!chatsToLoad.contains(peer.chat_id)) {
chatsToLoad.add(peer.chat_id);
}
}
}
public void getDialogs(int folderId, int offset, int count, boolean loadDraftsPeersAndFolders) { public void getDialogs(int folderId, int offset, int count, boolean loadDraftsPeersAndFolders) {
long[] draftsDialogIds; long[] draftsDialogIds;
if (loadDraftsPeersAndFolders) { if (loadDraftsPeersAndFolders) {
@ -15569,7 +15435,7 @@ public class MessagesStorage extends BaseController {
if (dialogsType != 4 && (replies).startsWith(search1) || replies2.startsWith(search1)) { if (dialogsType != 4 && (replies).startsWith(search1) || replies2.startsWith(search1)) {
TLRPC.User user = getMessagesController().getUser(708513L); TLRPC.User user = getMessagesController().getUser(708513L);
if (user == null) { if (user == null) {
user = getMessagesController().getUser(1271266957L); user = getMessagesController().getUser(UserObject.REPLY_BOT);
} }
if (user != null) { if (user != null) {
DialogsSearchAdapter.DialogSearchResult dialogSearchResult = new DialogsSearchAdapter.DialogSearchResult(); DialogsSearchAdapter.DialogSearchResult dialogSearchResult = new DialogsSearchAdapter.DialogSearchResult();

View file

@ -37,6 +37,7 @@ import android.widget.RemoteViews;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.util.Log;
import org.telegram.messenger.audioinfo.AudioInfo; import org.telegram.messenger.audioinfo.AudioInfo;
import org.telegram.ui.LaunchActivity; import org.telegram.ui.LaunchActivity;
@ -65,6 +66,7 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
private Bitmap albumArtPlaceholder; private Bitmap albumArtPlaceholder;
private int notificationMessageID; private int notificationMessageID;
private ImageReceiver imageReceiver; private ImageReceiver imageReceiver;
private boolean foregroundServiceIsStarted;
private String loadingFilePath; private String loadingFilePath;
@ -311,12 +313,22 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
notification = bldr.build(); notification = bldr.build();
if (isPlaying) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
startForeground(ID_NOTIFICATION, notification); if (!foregroundServiceIsStarted) {
foregroundServiceIsStarted = true;
startForeground(ID_NOTIFICATION, notification);
} else {
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.notify(ID_NOTIFICATION, notification);
}
} else { } else {
stopForeground(false); if (isPlaying) {
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); startForeground(ID_NOTIFICATION, notification);
nm.notify(ID_NOTIFICATION, notification); } else {
stopForeground(false);
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
nm.notify(ID_NOTIFICATION, notification);
}
} }
} else { } else {
@ -504,6 +516,7 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
public void onDestroy() { public void onDestroy() {
unregisterReceiver(headsetPlugReceiver); unregisterReceiver(headsetPlugReceiver);
super.onDestroy(); super.onDestroy();
stopForeground(true);
if (remoteControlClient != null) { if (remoteControlClient != null) {
RemoteControlClient.MetadataEditor metadataEditor = remoteControlClient.editMetadata(true); RemoteControlClient.MetadataEditor metadataEditor = remoteControlClient.editMetadata(true);
metadataEditor.clear(); metadataEditor.clear();

View file

@ -22,7 +22,7 @@ import java.util.zip.ZipFile;
public class NativeLoader { public class NativeLoader {
private final static int LIB_VERSION = 46; private final static int LIB_VERSION = 47;
private final static String LIB_NAME = "tmessages." + LIB_VERSION; private final static String LIB_NAME = "tmessages." + LIB_VERSION;
private final static String LIB_SO_NAME = "lib" + LIB_NAME + ".so"; private final static String LIB_SO_NAME = "lib" + LIB_NAME + ".so";
private final static String LOCALE_LIB_SO_NAME = "lib" + LIB_NAME + "loc.so"; private final static String LOCALE_LIB_SO_NAME = "lib" + LIB_NAME + "loc.so";

View file

@ -209,6 +209,8 @@ public class NotificationCenter {
public static final int updateBotMenuButton = totalEvents++; public static final int updateBotMenuButton = totalEvents++;
public static final int boostedChannelByUser = totalEvents++;
public static final int boostByChannelCreated = totalEvents++;
public static final int didUpdatePremiumGiftStickers = totalEvents++; public static final int didUpdatePremiumGiftStickers = totalEvents++;
public static final int didUpdatePremiumGiftFieldIcon = totalEvents++; public static final int didUpdatePremiumGiftFieldIcon = totalEvents++;
public static final int storiesEnabledUpdate = totalEvents++; public static final int storiesEnabledUpdate = totalEvents++;
@ -216,10 +218,10 @@ public class NotificationCenter {
public static final int storiesLimitUpdate = totalEvents++; public static final int storiesLimitUpdate = totalEvents++;
public static final int storiesSendAsUpdate = totalEvents++; public static final int storiesSendAsUpdate = totalEvents++;
public static final int unconfirmedAuthUpdate = totalEvents++; public static final int unconfirmedAuthUpdate = totalEvents++;
public static final int dialogPhotosUpdate = totalEvents++;
//global //global
public static final int pushMessagesUpdated = totalEvents++; public static final int pushMessagesUpdated = totalEvents++;
public static final int stopEncodingService = totalEvents++;
public static final int wallpapersDidLoad = totalEvents++; public static final int wallpapersDidLoad = totalEvents++;
public static final int wallpapersNeedReload = totalEvents++; public static final int wallpapersNeedReload = totalEvents++;
public static final int didReceiveSmsCode = totalEvents++; public static final int didReceiveSmsCode = totalEvents++;
@ -294,7 +296,8 @@ public class NotificationCenter {
public static final int uploadStoryEnd = totalEvents++; public static final int uploadStoryEnd = totalEvents++;
public static final int customTypefacesLoaded = totalEvents++; public static final int customTypefacesLoaded = totalEvents++;
public static final int stealthModeChanged = totalEvents++; public static final int stealthModeChanged = totalEvents++;
public static final int onReceivedChannelDifference = totalEvents++;; public static final int onReceivedChannelDifference = totalEvents++;
public static final int storiesReadUpdated = totalEvents++;
public static boolean alreadyLogged; public static boolean alreadyLogged;

View file

@ -1804,6 +1804,8 @@ public class NotificationsController extends BaseController {
} }
return LocaleController.formatString("NotificationGroupInvitedToCall", R.string.NotificationGroupInvitedToCall, name, chat.title, names.toString()); return LocaleController.formatString("NotificationGroupInvitedToCall", R.string.NotificationGroupInvitedToCall, name, chat.title, names.toString());
} }
} else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionGiftCode) {
return LocaleController.getString("BoostingReceivedGiftNoName", R.string.BoostingReceivedGiftNoName);
} else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByLink) { } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByLink) {
return LocaleController.formatString("NotificationInvitedToGroupByLink", R.string.NotificationInvitedToGroupByLink, name, chat.title); return LocaleController.formatString("NotificationInvitedToGroupByLink", R.string.NotificationInvitedToGroupByLink, name, chat.title);
} else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatEditTitle) { } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatEditTitle) {
@ -2116,6 +2118,8 @@ public class NotificationsController extends BaseController {
} else { } else {
return LocaleController.getString("Poll", R.string.Poll); return LocaleController.getString("Poll", R.string.Poll);
} }
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGiveaway) {
return LocaleController.getString("BoostingGiveaway", R.string.BoostingGiveaway);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) { } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) {
return LocaleController.getString("AttachLocation", R.string.AttachLocation); return LocaleController.getString("AttachLocation", R.string.AttachLocation);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeoLive) { } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeoLive) {
@ -2362,6 +2366,9 @@ public class NotificationsController extends BaseController {
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaContact) { } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaContact) {
TLRPC.TL_messageMediaContact mediaContact = (TLRPC.TL_messageMediaContact) messageObject.messageOwner.media; TLRPC.TL_messageMediaContact mediaContact = (TLRPC.TL_messageMediaContact) messageObject.messageOwner.media;
msg = LocaleController.formatString("NotificationMessageContact2", R.string.NotificationMessageContact2, name, ContactsController.formatName(mediaContact.first_name, mediaContact.last_name)); msg = LocaleController.formatString("NotificationMessageContact2", R.string.NotificationMessageContact2, name, ContactsController.formatName(mediaContact.first_name, mediaContact.last_name));
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGiveaway) {
TLRPC.TL_messageMediaGiveaway giveaway = (TLRPC.TL_messageMediaGiveaway) messageObject.messageOwner.media;
msg = LocaleController.formatString("NotificationMessageChannelGiveaway", R.string.NotificationMessageChannelGiveaway, name, giveaway.quantity, giveaway.months);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPoll) { } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaPoll) {
TLRPC.TL_messageMediaPoll mediaPoll = (TLRPC.TL_messageMediaPoll) messageObject.messageOwner.media; TLRPC.TL_messageMediaPoll mediaPoll = (TLRPC.TL_messageMediaPoll) messageObject.messageOwner.media;
if (mediaPoll.poll.quiz) { if (mediaPoll.poll.quiz) {
@ -2489,6 +2496,15 @@ public class NotificationsController extends BaseController {
} }
msg = LocaleController.formatString("NotificationGroupInvitedToCall", R.string.NotificationGroupInvitedToCall, name, chat.title, names.toString()); msg = LocaleController.formatString("NotificationGroupInvitedToCall", R.string.NotificationGroupInvitedToCall, name, chat.title, names.toString());
} }
} else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionGiftCode) {
TLRPC.TL_messageActionGiftCode giftCode = (TLRPC.TL_messageActionGiftCode) messageObject.messageOwner.action;
TLRPC.Chat fromChat = MessagesController.getInstance(currentAccount).getChat(-DialogObject.getPeerDialogId(giftCode.boost_peer));
String from = fromChat == null ? null : fromChat.title;
if (from == null) {
msg = LocaleController.getString("BoostingReceivedGiftNoName", R.string.BoostingReceivedGiftNoName);
} else {
msg = LocaleController.formatString("NotificationMessageGiftCode", R.string.NotificationMessageGiftCode, from, LocaleController.formatPluralString("Months", giftCode.months));
}
} else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByLink) { } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatJoinedByLink) {
msg = LocaleController.formatString("NotificationInvitedToGroupByLink", R.string.NotificationInvitedToGroupByLink, name, chat.title); msg = LocaleController.formatString("NotificationInvitedToGroupByLink", R.string.NotificationInvitedToGroupByLink, name, chat.title);
} else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatEditTitle) { } else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatEditTitle) {
@ -2729,6 +2745,9 @@ public class NotificationsController extends BaseController {
} else { } else {
msg = LocaleController.formatString("ChannelMessagePoll2", R.string.ChannelMessagePoll2, name, mediaPoll.poll.question); msg = LocaleController.formatString("ChannelMessagePoll2", R.string.ChannelMessagePoll2, name, mediaPoll.poll.question);
} }
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGiveaway) {
TLRPC.TL_messageMediaGiveaway giveaway = (TLRPC.TL_messageMediaGiveaway) messageObject.messageOwner.media;
msg = LocaleController.formatString("NotificationMessageChannelGiveaway", R.string.NotificationMessageChannelGiveaway, chat.title, giveaway.quantity, giveaway.months);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) { } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) {
msg = LocaleController.formatString("ChannelMessageMap", R.string.ChannelMessageMap, name); msg = LocaleController.formatString("ChannelMessageMap", R.string.ChannelMessageMap, name);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeoLive) { } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeoLive) {
@ -2801,6 +2820,9 @@ public class NotificationsController extends BaseController {
} }
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGame) { } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGame) {
msg = LocaleController.formatString("NotificationMessageGroupGame", R.string.NotificationMessageGroupGame, name, chat.title, messageObject.messageOwner.media.game.title); msg = LocaleController.formatString("NotificationMessageGroupGame", R.string.NotificationMessageGroupGame, name, chat.title, messageObject.messageOwner.media.game.title);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGiveaway) {
TLRPC.TL_messageMediaGiveaway giveaway = (TLRPC.TL_messageMediaGiveaway) messageObject.messageOwner.media;
msg = LocaleController.formatString("NotificationMessageChannelGiveaway", R.string.NotificationMessageChannelGiveaway, chat.title, giveaway.quantity, giveaway.months);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) { } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeo || messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaVenue) {
msg = LocaleController.formatString("NotificationMessageGroupMap", R.string.NotificationMessageGroupMap, name, chat.title); msg = LocaleController.formatString("NotificationMessageGroupMap", R.string.NotificationMessageGroupMap, name, chat.title);
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeoLive) { } else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeoLive) {

View file

@ -622,11 +622,26 @@ public class PushListenerController {
localMessage = true; localMessage = true;
break; break;
} }
case "MESSAGE_GIFTCODE": {
messageText = LocaleController.formatString("NotificationMessageGiftCode", R.string.NotificationMessageGiftCode, args[0], LocaleController.formatPluralString("Months", Utilities.parseInt(args[1])));
localMessage = true;
break;
}
case "MESSAGE_GIVEAWAY": {
messageText = LocaleController.formatString("NotificationMessageGiveaway", R.string.NotificationMessageGiveaway, args[0], args[1], args[2]);
localMessage = true;
break;
}
case "MESSAGES": { case "MESSAGES": {
messageText = LocaleController.formatString("NotificationMessageAlbum", R.string.NotificationMessageAlbum, args[0]); messageText = LocaleController.formatString("NotificationMessageAlbum", R.string.NotificationMessageAlbum, args[0]);
localMessage = true; localMessage = true;
break; break;
} }
case "CHANNEL_MESSAGE_GIVEAWAY": {
messageText = LocaleController.formatString("NotificationMessageChannelGiveaway", R.string.NotificationMessageChannelGiveaway, args[0], args[1], args[2]);
message1 = LocaleController.getString("BoostingGiveaway", R.string.BoostingGiveaway);
break;
}
case "CHANNEL_MESSAGE_NOTEXT": { case "CHANNEL_MESSAGE_NOTEXT": {
messageText = LocaleController.formatString("ChannelMessageNoText", R.string.ChannelMessageNoText, args[0]); messageText = LocaleController.formatString("ChannelMessageNoText", R.string.ChannelMessageNoText, args[0]);
message1 = LocaleController.getString("Message", R.string.Message); message1 = LocaleController.getString("Message", R.string.Message);
@ -737,6 +752,11 @@ public class PushListenerController {
localMessage = true; localMessage = true;
break; break;
} }
case "CHAT_MESSAGE_GIVEAWAY": {
messageText = LocaleController.formatString("NotificationMessageChatGiveaway", R.string.NotificationMessageChatGiveaway, args[0], args[1], args[2], args[3]);
message1 = LocaleController.getString("BoostingGiveaway", R.string.BoostingGiveaway);
break;
}
case "CHAT_MESSAGE_TEXT": { case "CHAT_MESSAGE_TEXT": {
messageText = LocaleController.formatString("NotificationMessageGroupText", R.string.NotificationMessageGroupText, args[0], args[1], args[2]); messageText = LocaleController.formatString("NotificationMessageGroupText", R.string.NotificationMessageGroupText, args[0], args[1], args[2]);
message1 = args[2]; message1 = args[2];
@ -1038,6 +1058,10 @@ public class PushListenerController {
} }
break; break;
} }
case "PINNED_GIVEAWAY": {
messageText = LocaleController.formatString("NotificationPinnedGiveaway", R.string.NotificationPinnedGiveaway, args[0]);
break;
}
case "PINNED_QUIZ": { case "PINNED_QUIZ": {
if (dialogId > 0) { if (dialogId > 0) {
messageText = LocaleController.formatString("NotificationActionPinnedQuizUser", R.string.NotificationActionPinnedQuizUser, args[0], args[1]); messageText = LocaleController.formatString("NotificationActionPinnedQuizUser", R.string.NotificationActionPinnedQuizUser, args[0], args[1]);
@ -1307,6 +1331,12 @@ public class PushListenerController {
case "REACT_GIF": { case "REACT_GIF": {
return LocaleController.formatString("PushReactGif", R.string.PushReactGif, args); return LocaleController.formatString("PushReactGif", R.string.PushReactGif, args);
} }
case "REACT_GIVEAWAY": {
return LocaleController.formatString("NotificationReactGiveaway", R.string.NotificationReactGiveaway, args);
}
case "CHAT_REACT_GIVEAWAY": {
return LocaleController.formatString("NotificationChatReactGiveaway", R.string.NotificationChatReactGiveaway, args);
}
case "CHAT_REACT_TEXT": { case "CHAT_REACT_TEXT": {
return LocaleController.formatString("PushChatReactText", R.string.PushChatReactText, args); return LocaleController.formatString("PushChatReactText", R.string.PushChatReactText, args);
} }

View file

@ -761,9 +761,6 @@ public class SecretChatHelper extends BaseController {
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT; newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT;
getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, newMsgObj.id, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, 0L, existFlags, false); getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, newMsgObj.id, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, 0L, existFlags, false);
getSendMessagesHelper().processSentMessage(newMsgObj.id); getSendMessagesHelper().processSentMessage(newMsgObj.id);
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj)) {
getSendMessagesHelper().stopVideoService(attachPath);
}
getSendMessagesHelper().removeFromSendingMessages(newMsgObj.id, false); getSendMessagesHelper().removeFromSendingMessages(newMsgObj.id, false);
}); });
}); });
@ -773,9 +770,6 @@ public class SecretChatHelper extends BaseController {
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR; newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR;
getNotificationCenter().postNotificationName(NotificationCenter.messageSendError, newMsgObj.id); getNotificationCenter().postNotificationName(NotificationCenter.messageSendError, newMsgObj.id);
getSendMessagesHelper().processSentMessage(newMsgObj.id); getSendMessagesHelper().processSentMessage(newMsgObj.id);
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj)) {
getSendMessagesHelper().stopVideoService(newMsgObj.attachPath);
}
getSendMessagesHelper().removeFromSendingMessages(newMsgObj.id, false); getSendMessagesHelper().removeFromSendingMessages(newMsgObj.id, false);
}); });
} }

View file

@ -34,6 +34,7 @@ import android.provider.OpenableColumns;
import android.text.Spannable; import android.text.Spannable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.Gravity; import android.view.Gravity;
@ -56,6 +57,7 @@ import org.telegram.tgnet.RequestDelegate;
import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.SerializedData;
import org.telegram.tgnet.TLObject; import org.telegram.tgnet.TLObject;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.tgnet.tl.TL_stories;
import org.telegram.ui.ActionBar.AlertDialog; import org.telegram.ui.ActionBar.AlertDialog;
import org.telegram.ui.ActionBar.BaseFragment; import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.Theme; import org.telegram.ui.ActionBar.Theme;
@ -64,6 +66,7 @@ import org.telegram.ui.ChatActivity;
import org.telegram.ui.Components.AlertsCreator; import org.telegram.ui.Components.AlertsCreator;
import org.telegram.ui.Components.AnimatedEmojiSpan; import org.telegram.ui.Components.AnimatedEmojiSpan;
import org.telegram.ui.Components.AnimatedFileDrawable; import org.telegram.ui.Components.AnimatedFileDrawable;
import org.telegram.ui.Components.BotWebViewSheet;
import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.Bulletin;
import org.telegram.ui.Components.LayoutHelper; import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.Point; import org.telegram.ui.Components.Point;
@ -126,27 +129,69 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
return false; return false;
} }
public TLRPC.InputReplyTo creteReplyInput(TLRPC.StoryItem storyItem) { public TLRPC.InputReplyTo createReplyInput(TL_stories.StoryItem storyItem) {
TLRPC.TL_inputReplyToStory replyTo = new TLRPC.TL_inputReplyToStory(); TLRPC.TL_inputReplyToStory replyTo = new TLRPC.TL_inputReplyToStory();
replyTo.story_id = storyItem.id; replyTo.story_id = storyItem.id;
replyTo.user_id = getMessagesController().getInputUser(storyItem.dialogId); replyTo.user_id = getMessagesController().getInputUser(storyItem.dialogId);
return replyTo; return replyTo;
} }
public static TLRPC.InputReplyTo creteReplyInput(int replyToMsgId) { public TLRPC.InputReplyTo createReplyInput(int replyToMsgId) {
return creteReplyInput(replyToMsgId, 0); return createReplyInput(null, replyToMsgId, 0, null);
} }
public static TLRPC.InputReplyTo creteReplyInput(int replyToMsgId, int topMessageId) { public TLRPC.InputReplyTo createReplyInput(TLRPC.InputPeer sendToPeer, int replyToMsgId, int topMessageId, ChatActivity.ReplyQuote replyQuote) {
TLRPC.TL_inputReplyToMessage replyTo = new TLRPC.TL_inputReplyToMessage(); TLRPC.TL_inputReplyToMessage replyTo = new TLRPC.TL_inputReplyToMessage();
replyTo.reply_to_msg_id = replyToMsgId; replyTo.reply_to_msg_id = replyToMsgId;
if (topMessageId != 0) { if (topMessageId != 0) {
replyTo.flags |= 1; replyTo.flags |= 1;
replyTo.top_msg_id = topMessageId; replyTo.top_msg_id = topMessageId;
} }
if (replyQuote != null) {
replyTo.quote_text = replyQuote.getText();
if (!TextUtils.isEmpty(replyTo.quote_text)) {
replyTo.flags |= 4;
replyTo.quote_entities = replyQuote.getEntities();
if (replyTo.quote_entities != null && !replyTo.quote_entities.isEmpty()) {
replyTo.quote_entities = new ArrayList<>(replyTo.quote_entities);
replyTo.flags |= 8;
}
}
}
if (replyQuote != null && replyQuote.message != null) {
long did = replyQuote.message.getDialogId();
TLRPC.InputPeer peer = getMessagesController().getInputPeer(did);
if (peer != null && !MessageObject.peersEqual(peer, sendToPeer)) {
replyTo.flags |= 2;
replyTo.reply_to_peer_id = peer;
}
}
return replyTo; return replyTo;
} }
public TLRPC.InputReplyTo createReplyInput(TLRPC.TL_messageReplyHeader replyHeader) {
TLRPC.TL_inputReplyToMessage replyTo = new TLRPC.TL_inputReplyToMessage();
replyTo.reply_to_msg_id = replyHeader.reply_to_msg_id;
if ((replyHeader.flags & 2) != 0) {
replyTo.flags |= 1;
replyTo.top_msg_id = replyHeader.reply_to_top_id;
}
if ((replyHeader.flags & 1) != 0) {
replyTo.flags |= 2;
replyTo.reply_to_peer_id = MessagesController.getInstance(currentAccount).getInputPeer(replyHeader.reply_to_peer_id);
}
if (replyHeader.quote) {
if ((replyHeader.flags & 64) != 0) {
replyTo.flags |= 4;
replyTo.quote_text = replyHeader.quote_text;
}
if ((replyHeader.flags & 128) != 0) {
replyTo.flags |= 8;
replyTo.quote_entities = replyHeader.quote_entities;
}
}
return replyTo;
}
public class ImportingHistory { public class ImportingHistory {
public String historyPath; public String historyPath;
@ -942,9 +987,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
media.file = file; media.file = file;
MessageObject messageObject = (MessageObject) message.extraHashMap.get(location + "_i"); MessageObject messageObject = (MessageObject) message.extraHashMap.get(location + "_i");
int index = message.messageObjects.indexOf(messageObject); int index = message.messageObjects.indexOf(messageObject);
if (index >= 0) {
stopVideoService(message.messageObjects.get(index).messageOwner.attachPath);
}
message.photoSize = (TLRPC.PhotoSize) message.extraHashMap.get(location + "_t"); message.photoSize = (TLRPC.PhotoSize) message.extraHashMap.get(location + "_t");
if (media.thumb == null && message.photoSize != null && message.photoSize.location != null) { if (media.thumb == null && message.photoSize != null && message.photoSize.location != null) {
message.performMediaUpload = true; message.performMediaUpload = true;
@ -973,9 +1015,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
if (index >= 0) { if (index >= 0) {
req.files.set(index, encryptedFile); req.files.set(index, encryptedFile);
if (inputEncryptedFile.id == 1) { if (inputEncryptedFile.id == 1) {
MessageObject messageObject = (MessageObject) message.extraHashMap.get(location + "_i");
message.photoSize = (TLRPC.PhotoSize) message.extraHashMap.get(location + "_t"); message.photoSize = (TLRPC.PhotoSize) message.extraHashMap.get(location + "_t");
stopVideoService(message.messageObjects.get(index).messageOwner.attachPath);
} }
decryptedMessage = req.messages.get(index); decryptedMessage = req.messages.get(index);
} }
@ -1074,7 +1114,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
boolean isEncrypted = DialogObject.isEncryptedDialog(messageObject.getDialogId()); boolean isEncrypted = DialogObject.isEncryptedDialog(messageObject.getDialogId());
getFileLoader().checkUploadNewDataAvailable(finalPath, isEncrypted, availableSize, finalSize, progress); getFileLoader().checkUploadNewDataAvailable(finalPath, isEncrypted, availableSize, finalSize, progress);
if (finalSize != 0) { if (finalSize != 0) {
stopVideoService(messageObject.messageOwner.attachPath);
ArrayList<DelayedMessage> arr = delayedMessages.get(messageObject.messageOwner.attachPath); ArrayList<DelayedMessage> arr = delayedMessages.get(messageObject.messageOwner.attachPath);
if (arr != null) { if (arr != null) {
for (int a = 0; a < arr.size(); a++) { for (int a = 0; a < arr.size(); a++) {
@ -1112,7 +1151,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
return; return;
} }
String finalPath = (String) args[1]; String finalPath = (String) args[1];
stopVideoService(messageObject.messageOwner.attachPath);
ArrayList<DelayedMessage> arr = delayedMessages.get(finalPath); ArrayList<DelayedMessage> arr = delayedMessages.get(finalPath);
if (arr != null) { if (arr != null) {
@ -1393,7 +1431,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} else { } else {
getFileLoader().cancelFileUpload(key, enc); getFileLoader().cancelFileUpload(key, enc);
} }
stopVideoService(key);
delayedMessages.remove(key); delayedMessages.remove(key);
} }
for (int a = 0, N = checkReadyToSendGroups.size(); a < N; a++) { for (int a = 0, N = checkReadyToSendGroups.size(); a < N; a++) {
@ -1544,7 +1581,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
TLRPC.Message message; TLRPC.Message message;
if (resendMessage != null) { if (resendMessage != null) {
message = resendMessage; message = resendMessage;
req.reply_to = SendMessagesHelper.creteReplyInput(messageId); req.reply_to = createReplyInput(messageId);
req.random_id = resendMessage.random_id; req.random_id = resendMessage.random_id;
} else { } else {
message = new TLRPC.TL_messageService(); message = new TLRPC.TL_messageService();
@ -1558,6 +1595,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
message.flags |= 256; message.flags |= 256;
message.flags |= 8; message.flags |= 8;
message.reply_to = new TLRPC.TL_messageReplyHeader(); message.reply_to = new TLRPC.TL_messageReplyHeader();
message.reply_to.flags |= 16;
message.reply_to.reply_to_msg_id = messageId; message.reply_to.reply_to_msg_id = messageId;
message.peer_id = new TLRPC.TL_peerUser(); message.peer_id = new TLRPC.TL_peerUser();
message.peer_id.user_id = user.id; message.peer_id.user_id = user.id;
@ -1581,7 +1619,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
performSendMessageRequest(req, newMsgObj, null, null, null, null, false); performSendMessageRequest(req, newMsgObj, null, null, null, null, false);
} }
public void sendSticker(TLRPC.Document document, String query, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, MessageObject.SendAnimationData sendAnimationData, boolean notify, int scheduleDate, boolean updateStickersOrder, Object parentObject) { public void sendSticker(TLRPC.Document document, String query, long peer, MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem storyItem, ChatActivity.ReplyQuote quote, MessageObject.SendAnimationData sendAnimationData, boolean notify, int scheduleDate, boolean updateStickersOrder, Object parentObject) {
if (document == null) { if (document == null) {
return; return;
} }
@ -1681,6 +1719,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
SendMessageParams sendMessageParams = SendMessageParams.of((TLRPC.TL_document) finalDocument, null, null, peer, replyToMsg, replyToTopMsg, null, null, null, null, notify, scheduleDate, 0, parentObject, sendAnimationData, false); SendMessageParams sendMessageParams = SendMessageParams.of((TLRPC.TL_document) finalDocument, null, null, peer, replyToMsg, replyToTopMsg, null, null, null, null, notify, scheduleDate, 0, parentObject, sendAnimationData, false);
sendMessageParams.replyToStoryItem = storyItem; sendMessageParams.replyToStoryItem = storyItem;
sendMessageParams.replyQuote = quote;
sendMessage(sendMessageParams); sendMessage(sendMessageParams);
}); });
}); });
@ -1694,6 +1733,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
SendMessageParams sendMessageParams = SendMessageParams.of((TLRPC.TL_document) finalDocument, null, null, peer, replyToMsg, replyToTopMsg, null, null, null, params, notify, scheduleDate, 0, parentObject, sendAnimationData, updateStickersOrder); SendMessageParams sendMessageParams = SendMessageParams.of((TLRPC.TL_document) finalDocument, null, null, peer, replyToMsg, replyToTopMsg, null, null, null, params, notify, scheduleDate, 0, parentObject, sendAnimationData, updateStickersOrder);
sendMessageParams.replyToStoryItem = storyItem; sendMessageParams.replyToStoryItem = storyItem;
sendMessageParams.replyQuote = quote;
sendMessage(sendMessageParams); sendMessage(sendMessageParams);
} }
} }
@ -2050,6 +2090,15 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
newMsg.media_unread = true; newMsg.media_unread = true;
} }
} }
if (replyToTopMsg == null && msgObj.messageOwner.reply_to != null) {
if (
!(msgObj.messageOwner.reply_to.reply_to_peer_id == null || MessageObject.peersEqual(msgObj.messageOwner.reply_to.reply_to_peer_id, msgObj.messageOwner.peer_id)) ||
ids != null && (msgObj.messageOwner.reply_to.flags & 16) != 0 && ids.contains(msgObj.messageOwner.reply_to.reply_to_msg_id)
) {
newMsg.flags |= 8;
newMsg.reply_to = msgObj.messageOwner.reply_to;
}
}
MessageObject newMsgObj = new MessageObject(currentAccount, newMsg, true, true); MessageObject newMsgObj = new MessageObject(currentAccount, newMsg, true, true);
newMsgObj.scheduled = scheduleDate != 0; newMsgObj.scheduled = scheduleDate != 0;
newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING; newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING;
@ -2076,6 +2125,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
if (replyToTopMsg != null) { if (replyToTopMsg != null) {
newMsg.reply_to = new TLRPC.TL_messageReplyHeader(); newMsg.reply_to = new TLRPC.TL_messageReplyHeader();
newMsg.reply_to.flags |= 16;
newMsg.reply_to.reply_to_msg_id = replyToTopMsg.getId(); newMsg.reply_to.reply_to_msg_id = replyToTopMsg.getId();
if (replyToTopMsg.isTopicMainMessage) { if (replyToTopMsg.isTopicMainMessage) {
newMsg.reply_to.forum_topic = true; newMsg.reply_to.forum_topic = true;
@ -2515,7 +2565,15 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
if (type >= 1 && type <= 3 || type >= 5 && type <= 8) { if (type >= 1 && type <= 3 || type >= 5 && type <= 8) {
TLRPC.InputMedia inputMedia = null; TLRPC.InputMedia inputMedia = null;
if (type == 1) { if (type == 1) {
if ((newMsg.media == null || newMsg.media instanceof TLRPC.TL_messageMediaEmpty || newMsg.media != null && newMsg.media.webpage instanceof TLRPC.TL_webPageEmpty) && !messageObject.editingMessageSearchWebPage) {
inputMedia = new TLRPC.TL_inputMediaEmpty();
} else if (newMsg != null && newMsg.media != null && newMsg.media.webpage != null) {
TLRPC.TL_inputMediaWebPage inputWebpage = new TLRPC.TL_inputMediaWebPage();
inputWebpage.url = newMsg.media.webpage.url;
inputWebpage.force_small_media = newMsg.media.force_small_media;
inputWebpage.force_large_media = newMsg.media.force_large_media;
inputMedia = inputWebpage;
}
} else if (type == 2) { } else if (type == 2) {
TLRPC.TL_inputMediaUploadedPhoto uploadedPhoto = new TLRPC.TL_inputMediaUploadedPhoto(); TLRPC.TL_inputMediaUploadedPhoto uploadedPhoto = new TLRPC.TL_inputMediaUploadedPhoto();
uploadedPhoto.spoiler = hasMediaSpoilers; uploadedPhoto.spoiler = hasMediaSpoilers;
@ -2660,6 +2718,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
TLRPC.TL_messages_editMessage request = new TLRPC.TL_messages_editMessage(); TLRPC.TL_messages_editMessage request = new TLRPC.TL_messages_editMessage();
request.id = messageObject.getId(); request.id = messageObject.getId();
request.peer = getMessagesController().getInputPeer(peer); request.peer = getMessagesController().getInputPeer(peer);
request.invert_media = messageObject.messageOwner.invert_media;
if (inputMedia != null) { if (inputMedia != null) {
request.flags |= 16384; request.flags |= 16384;
request.media = inputMedia; request.media = inputMedia;
@ -3211,19 +3270,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
inputInvoice.msg_id = messageObject.getId(); inputInvoice.msg_id = messageObject.getId();
inputInvoice.peer = getMessagesController().getInputPeer(messageObject.messageOwner.peer_id); inputInvoice.peer = getMessagesController().getInputPeer(messageObject.messageOwner.peer_id);
req.invoice = inputInvoice; req.invoice = inputInvoice;
try { final JSONObject themeParams = BotWebViewSheet.makeThemeParams(null);
JSONObject jsonObject = new JSONObject(); if (themeParams != null) {
jsonObject.put("bg_color", Theme.getColor(Theme.key_windowBackgroundWhite));
jsonObject.put("text_color", Theme.getColor(Theme.key_windowBackgroundWhiteBlackText));
jsonObject.put("hint_color", Theme.getColor(Theme.key_windowBackgroundWhiteHintText));
jsonObject.put("link_color", Theme.getColor(Theme.key_windowBackgroundWhiteLinkText));
jsonObject.put("button_color", Theme.getColor(Theme.key_featuredStickers_addButton));
jsonObject.put("button_text_color", Theme.getColor(Theme.key_featuredStickers_buttonText));
req.theme_params = new TLRPC.TL_dataJSON(); req.theme_params = new TLRPC.TL_dataJSON();
req.theme_params.data = jsonObject.toString(); req.theme_params.data = themeParams.toString();
req.flags |= 1; req.flags |= 1;
} catch (Exception e) {
FileLog.e(e);
} }
getConnectionsManager().sendRequest(req, requestDelegate, ConnectionsManager.RequestFlagFailOnServerErrors); getConnectionsManager().sendRequest(req, requestDelegate, ConnectionsManager.RequestFlagFailOnServerErrors);
} else { } else {
@ -3330,6 +3381,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
MessageObject replyToMsg = sendMessageParams.replyToMsg; MessageObject replyToMsg = sendMessageParams.replyToMsg;
MessageObject replyToTopMsg = sendMessageParams.replyToTopMsg; MessageObject replyToTopMsg = sendMessageParams.replyToTopMsg;
TLRPC.WebPage webPage = sendMessageParams.webPage; TLRPC.WebPage webPage = sendMessageParams.webPage;
TLRPC.TL_messageMediaWebPage mediaWebPage = sendMessageParams.mediaWebPage;
boolean searchLinks = sendMessageParams.searchLinks; boolean searchLinks = sendMessageParams.searchLinks;
MessageObject retryMessageObject = sendMessageParams.retryMessageObject; MessageObject retryMessageObject = sendMessageParams.retryMessageObject;
ArrayList<TLRPC.MessageEntity> entities = sendMessageParams.entities; ArrayList<TLRPC.MessageEntity> entities = sendMessageParams.entities;
@ -3342,8 +3394,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
MessageObject.SendAnimationData sendAnimationData = sendMessageParams.sendAnimationData; MessageObject.SendAnimationData sendAnimationData = sendMessageParams.sendAnimationData;
boolean updateStickersOrder = sendMessageParams.updateStickersOrder; boolean updateStickersOrder = sendMessageParams.updateStickersOrder;
boolean hasMediaSpoilers = sendMessageParams.hasMediaSpoilers; boolean hasMediaSpoilers = sendMessageParams.hasMediaSpoilers;
TLRPC.StoryItem replyToStoryItem = sendMessageParams.replyToStoryItem; TL_stories.StoryItem replyToStoryItem = sendMessageParams.replyToStoryItem;
TLRPC.StoryItem sendingStory = sendMessageParams.sendingStory; TL_stories.StoryItem sendingStory = sendMessageParams.sendingStory;
ChatActivity.ReplyQuote replyQuote = sendMessageParams.replyQuote;
boolean invert_media = sendMessageParams.invert_media;
if (user != null && user.phone == null) { if (user != null && user.phone == null) {
return; return;
} }
@ -3354,6 +3409,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
caption = ""; caption = "";
} }
if (replyQuote != null && replyQuote.message != null && replyToMsg != null) {
replyToMsg = replyQuote.message;
}
String originalPath = null; String originalPath = null;
if (params != null && params.containsKey("originalPath")) { if (params != null && params.containsKey("originalPath")) {
originalPath = params.get("originalPath"); originalPath = params.get("originalPath");
@ -3501,7 +3560,9 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
type = MEDIA_TYPE_DICE; type = MEDIA_TYPE_DICE;
caption = ""; caption = "";
} else { } else {
if (webPage == null) { if (mediaWebPage != null) {
newMsg.media = mediaWebPage;
} else if (webPage == null) {
newMsg.media = new TLRPC.TL_messageMediaEmpty(); newMsg.media = new TLRPC.TL_messageMediaEmpty();
} else { } else {
newMsg.media = new TLRPC.TL_messageMediaWebPage(); newMsg.media = new TLRPC.TL_messageMediaWebPage();
@ -3768,6 +3829,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
newMsg.flags |= TLRPC.MESSAGE_FLAG_HAS_MEDIA; newMsg.flags |= TLRPC.MESSAGE_FLAG_HAS_MEDIA;
newMsg.dialog_id = peer; newMsg.dialog_id = peer;
newMsg.invert_media = invert_media;
if (replyToStoryItem != null) { if (replyToStoryItem != null) {
newMsg.reply_to = new TLRPC.TL_messageReplyStoryHeader(); newMsg.reply_to = new TLRPC.TL_messageReplyStoryHeader();
newMsg.reply_to.story_id = replyToStoryItem.id; newMsg.reply_to.story_id = replyToStoryItem.id;
@ -3782,6 +3844,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} else { } else {
newMsg.flags |= TLRPC.MESSAGE_FLAG_REPLY; newMsg.flags |= TLRPC.MESSAGE_FLAG_REPLY;
} }
newMsg.reply_to.flags |= 16;
newMsg.reply_to.reply_to_msg_id = replyToMsg.getId(); newMsg.reply_to.reply_to_msg_id = replyToMsg.getId();
if (replyToTopMsg != null && replyToTopMsg != replyToMsg && replyToTopMsg.getId() != 1) { if (replyToTopMsg != null && replyToTopMsg != replyToMsg && replyToTopMsg.getId() != 1) {
newMsg.reply_to.reply_to_top_id = replyToTopMsg.getId(); newMsg.reply_to.reply_to_top_id = replyToTopMsg.getId();
@ -3796,6 +3859,18 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
newMsg.reply_to.flags |= 8; newMsg.reply_to.flags |= 8;
} }
} }
if (replyQuote != null) {
newMsg.reply_to.quote_text = replyQuote.getText();
if (!TextUtils.isEmpty(newMsg.reply_to.quote_text)) {
newMsg.reply_to.quote = true;
newMsg.reply_to.flags |= 64;
newMsg.reply_to.quote_entities = replyQuote.getEntities();
if (newMsg.reply_to.quote_entities != null && !newMsg.reply_to.quote_entities.isEmpty()) {
newMsg.reply_to.quote_entities = new ArrayList<>(newMsg.reply_to.quote_entities);
newMsg.reply_to.flags |= 128;
}
}
}
} }
if (linkedToGroup != 0) { if (linkedToGroup != 0) {
newMsg.replies = new TLRPC.TL_messageReplies(); newMsg.replies = new TLRPC.TL_messageReplies();
@ -3865,6 +3940,46 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
} }
} }
boolean destroyReply = false;
if (replyToMsg != null && replyToStoryItem == null && newMsg.reply_to != null) {
boolean convertToQuote = false;
TLRPC.Peer peer2 = getMessagesController().getPeer(replyToMsg.getDialogId() > 0 ? replyToMsg.getSenderId() : replyToMsg.getDialogId());
boolean anotherChat = peer2 != null && !MessageObject.peersEqual(getMessagesController().getPeer(replyToMsg.getDialogId()), newMsg.peer_id);
if (replyToMsg.isForwarded() && !replyToMsg.isImportedForward() && replyToMsg.messageOwner.fwd_from.saved_from_peer == null) {
if (replyToMsg.messageOwner.fwd_from.from_id != null && (replyToMsg.messageOwner.fwd_from.flags & 4) != 0) {
peer2 = replyToMsg.messageOwner.fwd_from.from_id;
newMsg.reply_to.reply_to_msg_id = replyToMsg.messageOwner.fwd_from.channel_post;
destroyReply = true;
anotherChat = true;
}
}
final boolean anotherTopic = replyToTopMsg != null && replyToTopMsg.getId() != replyToMsg.getId() && MessageObject.getTopicId(replyToMsg.messageOwner, true) != replyToTopMsg.getId();
if (anotherChat || anotherTopic) {
newMsg.reply_to.flags |= 1;
newMsg.reply_to.reply_to_peer_id = peer2;
convertToQuote = true;
}
if (convertToQuote) {
if (replyQuote == null) {
newMsg.reply_to.quote = false;
replyQuote = ChatActivity.ReplyQuote.from(replyToMsg);
}
if (replyQuote != null) {
if (replyToMsg.messageOwner != null && replyToMsg.messageOwner.media != null) {
newMsg.reply_to.flags |= 256;
newMsg.reply_to.reply_media = replyToMsg.messageOwner.media;
}
if (replyQuote.getText() != null) {
newMsg.reply_to.flags |= 64;
newMsg.reply_to.quote_text = replyQuote.getText();
}
if (replyQuote.getEntities() != null) {
newMsg.reply_to.flags |= 128;
newMsg.reply_to.quote_entities = replyQuote.getEntities();
}
}
}
}
if (MessageObject.isVoiceMessage(newMsg) || MessageObject.isRoundVideoMessage(newMsg)) { if (MessageObject.isVoiceMessage(newMsg) || MessageObject.isRoundVideoMessage(newMsg)) {
newMsg.media_unread = true; newMsg.media_unread = true;
} }
@ -3887,7 +4002,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
MessageObject reply = replyToMsg; MessageObject reply = replyToMsg;
if (replyToTopMsg != null && replyToTopMsg == reply && replyToTopMsg.getId() == 1) { if (replyToTopMsg != null && replyToTopMsg == reply && replyToTopMsg.getId() == 1 || destroyReply) {
reply = null; reply = null;
} }
newMsgObj = new MessageObject(currentAccount, newMsg, reply, true, true); newMsgObj = new MessageObject(currentAccount, newMsg, reply, true, true);
@ -3942,40 +4057,80 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
if (type == 0 || type == 9 && message != null && encryptedChat != null) { if (type == 0 || type == 9 && message != null && encryptedChat != null) {
if (encryptedChat == null) { if (encryptedChat == null) {
TLRPC.TL_messages_sendMessage reqSend = new TLRPC.TL_messages_sendMessage(); if (mediaWebPage != null) {
reqSend.message = message; TLRPC.TL_messages_sendMedia reqSend = new TLRPC.TL_messages_sendMedia();
reqSend.clear_draft = retryMessageObject == null; reqSend.message = message;
reqSend.silent = newMsg.silent; reqSend.clear_draft = retryMessageObject == null;
reqSend.peer = sendToPeer; reqSend.silent = newMsg.silent;
reqSend.random_id = newMsg.random_id; reqSend.peer = sendToPeer;
if (replyToStoryItem != null) { reqSend.random_id = newMsg.random_id;
reqSend.reply_to = creteReplyInput(replyToStoryItem); TLRPC.TL_inputMediaWebPage inputWebPage = new TLRPC.TL_inputMediaWebPage();
reqSend.flags |= 1; inputWebPage.url = mediaWebPage.webpage.url;
} else if (newMsg.reply_to != null) { inputWebPage.force_large_media = mediaWebPage.force_large_media;
int topMsgId = replyToTopMsg == null ? 0 : replyToTopMsg.getId(); inputWebPage.force_small_media = mediaWebPage.force_small_media;
reqSend.reply_to = SendMessagesHelper.creteReplyInput(newMsg.reply_to.reply_to_msg_id, topMsgId); reqSend.media = inputWebPage;
reqSend.flags |= 1; if (replyToStoryItem != null) {
} reqSend.reply_to = createReplyInput(replyToStoryItem);
if (updateStickersOrder && SharedConfig.updateStickersOrderOnSend) { reqSend.flags |= 1;
reqSend.update_stickersets_order = true; } else if (newMsg.reply_to instanceof TLRPC.TL_messageReplyHeader) {
} reqSend.reply_to = createReplyInput((TLRPC.TL_messageReplyHeader) newMsg.reply_to);
if (newMsg.from_id != null) { reqSend.flags |= 1;
reqSend.send_as = getMessagesController().getInputPeer(newMsg.from_id); }
} if (updateStickersOrder && SharedConfig.updateStickersOrderOnSend) {
if (!searchLinks) { reqSend.update_stickersets_order = true;
reqSend.no_webpage = true; }
} if (newMsg.from_id != null) {
if (entities != null && !entities.isEmpty()) { reqSend.send_as = getMessagesController().getInputPeer(newMsg.from_id);
reqSend.entities = entities; }
reqSend.flags |= 8; if (entities != null && !entities.isEmpty()) {
} reqSend.entities = entities;
if (scheduleDate != 0) { reqSend.flags |= 8;
reqSend.schedule_date = scheduleDate; }
reqSend.flags |= 1024; if (scheduleDate != 0) {
} reqSend.schedule_date = scheduleDate;
performSendMessageRequest(reqSend, newMsgObj, null, null, parentObject, params, scheduleDate != 0); reqSend.flags |= 1024;
if (retryMessageObject == null) { }
getMediaDataController().cleanDraft(peer, replyToTopMsg != null ? replyToTopMsg.getId() : 0, false); reqSend.invert_media = newMsg.invert_media;
performSendMessageRequest(reqSend, newMsgObj, null, null, parentObject, params, scheduleDate != 0);
if (retryMessageObject == null) {
getMediaDataController().cleanDraft(peer, replyToTopMsg != null ? replyToTopMsg.getId() : 0, false);
}
} else {
TLRPC.TL_messages_sendMessage reqSend = new TLRPC.TL_messages_sendMessage();
reqSend.message = message;
reqSend.clear_draft = retryMessageObject == null;
reqSend.silent = newMsg.silent;
reqSend.peer = sendToPeer;
reqSend.random_id = newMsg.random_id;
if (replyToStoryItem != null) {
reqSend.reply_to = createReplyInput(replyToStoryItem);
reqSend.flags |= 1;
} else if (newMsg.reply_to instanceof TLRPC.TL_messageReplyHeader) {
reqSend.reply_to = createReplyInput((TLRPC.TL_messageReplyHeader) newMsg.reply_to);
reqSend.flags |= 1;
}
if (updateStickersOrder && SharedConfig.updateStickersOrderOnSend) {
reqSend.update_stickersets_order = true;
}
if (newMsg.from_id != null) {
reqSend.send_as = getMessagesController().getInputPeer(newMsg.from_id);
}
if (!searchLinks) {
reqSend.no_webpage = true;
}
if (entities != null && !entities.isEmpty()) {
reqSend.entities = entities;
reqSend.flags |= 8;
}
if (scheduleDate != 0) {
reqSend.schedule_date = scheduleDate;
reqSend.flags |= 1024;
}
reqSend.invert_media = newMsg.invert_media;
performSendMessageRequest(reqSend, newMsgObj, null, null, parentObject, params, scheduleDate != 0);
if (retryMessageObject == null) {
getMediaDataController().cleanDraft(peer, replyToTopMsg != null ? replyToTopMsg.getId() : 0, false);
}
} }
} else { } else {
TLRPC.TL_decryptedMessage reqSend; TLRPC.TL_decryptedMessage reqSend;
@ -4296,11 +4451,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
if (newMsg.replyStory != null) { if (newMsg.replyStory != null) {
request.flags |= 1; request.flags |= 1;
request.reply_to = creteReplyInput(replyToStoryItem); request.reply_to = createReplyInput(replyToStoryItem);
} else if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) { } else if (newMsg.reply_to instanceof TLRPC.TL_messageReplyHeader) {
int replyToTopMsgId = replyToTopMsg != null ? replyToTopMsg.getId() : 0;
request.flags |= 1; request.flags |= 1;
request.reply_to = SendMessagesHelper.creteReplyInput(newMsg.reply_to.reply_to_msg_id, replyToTopMsgId); request.reply_to = createReplyInput((TLRPC.TL_messageReplyHeader) newMsg.reply_to);
} }
if (scheduleDate != 0) { if (scheduleDate != 0) {
request.schedule_date = scheduleDate; request.schedule_date = scheduleDate;
@ -4335,11 +4489,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
replyToTopMsgInt = replyToTopMsg.getId(); replyToTopMsgInt = replyToTopMsg.getId();
} }
if (replyToStoryItem != null) { if (replyToStoryItem != null) {
request.reply_to = creteReplyInput(replyToStoryItem); request.reply_to = createReplyInput(replyToStoryItem);
request.flags |= 1; request.flags |= 1;
} else if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) { } else if (newMsg.reply_to instanceof TLRPC.TL_messageReplyHeader) {
request.flags |= 1; request.flags |= 1;
request.reply_to = SendMessagesHelper.creteReplyInput(newMsg.reply_to.reply_to_msg_id, replyToTopMsgInt); request.reply_to = createReplyInput((TLRPC.TL_messageReplyHeader) newMsg.reply_to);
} }
request.random_id = newMsg.random_id; request.random_id = newMsg.random_id;
if (newMsg.from_id != null) { if (newMsg.from_id != null) {
@ -4733,21 +4887,17 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
TLRPC.TL_messages_sendInlineBotResult reqSend = new TLRPC.TL_messages_sendInlineBotResult(); TLRPC.TL_messages_sendInlineBotResult reqSend = new TLRPC.TL_messages_sendInlineBotResult();
reqSend.peer = sendToPeer; reqSend.peer = sendToPeer;
reqSend.random_id = newMsg.random_id; reqSend.random_id = newMsg.random_id;
if (replyToTopMsg != null) {
reqSend.reply_to = SendMessagesHelper.creteReplyInput(replyToTopMsg.getId());
reqSend.flags |= 512;
}
if (newMsg.from_id != null) { if (newMsg.from_id != null) {
reqSend.send_as = getMessagesController().getInputPeer(newMsg.from_id); reqSend.send_as = getMessagesController().getInputPeer(newMsg.from_id);
} }
reqSend.hide_via = !params.containsKey("bot"); reqSend.hide_via = !params.containsKey("bot");
if (replyToStoryItem != null) { if (replyToStoryItem != null) {
reqSend.reply_to = creteReplyInput(replyToStoryItem); reqSend.reply_to = createReplyInput(replyToStoryItem);
reqSend.flags |= 1; reqSend.flags |= 1;
} else if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) { } else if (newMsg.reply_to instanceof TLRPC.TL_messageReplyHeader) {
reqSend.flags |= 1; reqSend.flags |= 1;
reqSend.reply_to = SendMessagesHelper.creteReplyInput(newMsg.reply_to.reply_to_msg_id); reqSend.reply_to = createReplyInput((TLRPC.TL_messageReplyHeader) newMsg.reply_to);
} }
reqSend.silent = newMsg.silent; reqSend.silent = newMsg.silent;
if (scheduleDate != 0) { if (scheduleDate != 0) {
@ -5273,10 +5423,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
message.sendDelayedRequests(); message.sendDelayedRequests();
} }
public void stopVideoService(final String path) {
getMessagesStorage().getStorageQueue().postRunnable(() -> AndroidUtilities.runOnUIThread(() -> NotificationCenter.getGlobalInstance().postNotificationName(NotificationCenter.stopEncodingService, path, currentAccount)));
}
protected void putToSendingMessages(TLRPC.Message message, boolean scheduled) { protected void putToSendingMessages(TLRPC.Message message, boolean scheduled) {
if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) { if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) {
AndroidUtilities.runOnUIThread(() -> putToSendingMessages(message, scheduled, true)); AndroidUtilities.runOnUIThread(() -> putToSendingMessages(message, scheduled, true));
@ -5496,6 +5642,13 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
Utilities.stageQueue.postRunnable(() -> getMessagesController().processNewChannelDifferenceParams(newMessage.pts, newMessage.pts_count, newMessage.message.peer_id.channel_id)); Utilities.stageQueue.postRunnable(() -> getMessagesController().processNewChannelDifferenceParams(newMessage.pts, newMessage.pts_count, newMessage.message.peer_id.channel_id));
updatesArr.remove(a); updatesArr.remove(a);
a--; a--;
if (newMessage.message.pinned) {
Utilities.stageQueue.postRunnable(() -> {
ArrayList<Integer> mids = new ArrayList<>();
mids.add(newMessage.message.id);
getMessagesStorage().updatePinnedMessages(-channelId, mids, true, -1, 0, false, null);
});
}
} else if (update instanceof TLRPC.TL_updateNewScheduledMessage) { } else if (update instanceof TLRPC.TL_updateNewScheduledMessage) {
final TLRPC.TL_updateNewScheduledMessage newMessage = (TLRPC.TL_updateNewScheduledMessage) update; final TLRPC.TL_updateNewScheduledMessage newMessage = (TLRPC.TL_updateNewScheduledMessage) update;
newMessages.put(newMessage.message.id, newMessage.message); newMessages.put(newMessage.message.id, newMessage.message);
@ -5695,14 +5848,9 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
removeFromSendingMessages(newMsgObj.id, scheduled); removeFromSendingMessages(newMsgObj.id, scheduled);
}); });
}); });
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj)) {
stopVideoService(attachPath);
}
} else { } else {
AlertsCreator.processError(currentAccount, error, null, req); AlertsCreator.processError(currentAccount, error, null, req);
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj)) {
stopVideoService(newMsgObj.attachPath);
}
removeFromSendingMessages(newMsgObj.id, scheduled); removeFromSendingMessages(newMsgObj.id, scheduled);
revertEditingMessageObject(msgObj); revertEditingMessageObject(msgObj);
} }
@ -5794,6 +5942,14 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
sentMessages.add(message = newMessage.message); sentMessages.add(message = newMessage.message);
Utilities.stageQueue.postRunnable(() -> getMessagesController().processNewChannelDifferenceParams(newMessage.pts, newMessage.pts_count, newMessage.message.peer_id.channel_id)); Utilities.stageQueue.postRunnable(() -> getMessagesController().processNewChannelDifferenceParams(newMessage.pts, newMessage.pts_count, newMessage.message.peer_id.channel_id));
updatesArr.remove(a); updatesArr.remove(a);
a--;
if (newMessage.message.pinned) {
Utilities.stageQueue.postRunnable(() -> {
ArrayList<Integer> mids = new ArrayList<>();
mids.add(newMessage.message.id);
getMessagesStorage().updatePinnedMessages(-channelId, mids, true, -1, 0, false, null);
});
}
break; break;
} else if (update instanceof TLRPC.TL_updateNewScheduledMessage) { } else if (update instanceof TLRPC.TL_updateNewScheduledMessage) {
final TLRPC.TL_updateNewScheduledMessage newMessage = (TLRPC.TL_updateNewScheduledMessage) update; final TLRPC.TL_updateNewScheduledMessage newMessage = (TLRPC.TL_updateNewScheduledMessage) update;
@ -5866,9 +6022,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
processSentMessage(oldId); processSentMessage(oldId);
removeFromSendingMessages(oldId, scheduled); removeFromSendingMessages(oldId, scheduled);
}); });
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj)) {
stopVideoService(attachPath);
}
}); });
} else { } else {
getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, 0L, existFlags, scheduled); getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, oldId, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, 0L, existFlags, scheduled);
@ -5881,9 +6034,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
processSentMessage(oldId); processSentMessage(oldId);
removeFromSendingMessages(oldId, scheduled); removeFromSendingMessages(oldId, scheduled);
}); });
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj)) {
stopVideoService(attachPath);
}
}); });
} }
} }
@ -5896,9 +6046,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR; newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR;
getNotificationCenter().postNotificationName(NotificationCenter.messageSendError, newMsgObj.id); getNotificationCenter().postNotificationName(NotificationCenter.messageSendError, newMsgObj.id);
processSentMessage(newMsgObj.id); processSentMessage(newMsgObj.id);
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj)) {
stopVideoService(newMsgObj.attachPath);
}
removeFromSendingMessages(newMsgObj.id, scheduled); removeFromSendingMessages(newMsgObj.id, scheduled);
} }
}); });
@ -6406,7 +6553,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
private final static int ERROR_TYPE_UNSUPPORTED = 1; private final static int ERROR_TYPE_UNSUPPORTED = 1;
private final static int ERROR_TYPE_FILE_TOO_LARGE = 2; private final static int ERROR_TYPE_FILE_TOO_LARGE = 2;
private static int prepareSendingDocumentInternal(AccountInstance accountInstance, String path, String originalPath, Uri uri, String mime, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, final ArrayList<TLRPC.MessageEntity> entities, final MessageObject editingMessageObject, long[] groupId, boolean isGroupFinal, CharSequence caption, boolean notify, int scheduleDate, Integer[] docType, boolean forceDocument) { private static int prepareSendingDocumentInternal(AccountInstance accountInstance, String path, String originalPath, Uri uri, String mime, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem storyItem, ChatActivity.ReplyQuote quote, final ArrayList<TLRPC.MessageEntity> entities, final MessageObject editingMessageObject, long[] groupId, boolean isGroupFinal, CharSequence caption, boolean notify, int scheduleDate, Integer[] docType, boolean forceDocument) {
if ((path == null || path.length() == 0) && uri == null) { if ((path == null || path.length() == 0) && uri == null) {
return ERROR_TYPE_UNSUPPORTED; return ERROR_TYPE_UNSUPPORTED;
} }
@ -6681,6 +6828,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} else { } else {
SendMessageParams sendMessageParams = SendMessagesHelper.SendMessageParams.of(documentFinal, null, pathFinal, dialogId, replyToMsg, replyToTopMsg, captionFinal, entities, null, params, notify, scheduleDate, 0, parentFinal, null, false); SendMessageParams sendMessageParams = SendMessagesHelper.SendMessageParams.of(documentFinal, null, pathFinal, dialogId, replyToMsg, replyToTopMsg, captionFinal, entities, null, params, notify, scheduleDate, 0, parentFinal, null, false);
sendMessageParams.replyToStoryItem = storyItem; sendMessageParams.replyToStoryItem = storyItem;
sendMessageParams.replyQuote = quote;
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
} }
}); });
@ -6712,7 +6860,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
@UiThread @UiThread
public static void prepareSendingDocument(AccountInstance accountInstance, String path, String originalPath, Uri uri, String caption, String mine, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, MessageObject editingMessageObject, boolean notify, int scheduleDate, InputContentInfoCompat inputContent) { public static void prepareSendingDocument(AccountInstance accountInstance, String path, String originalPath, Uri uri, String caption, String mine, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem storyItem, ChatActivity.ReplyQuote quote, MessageObject editingMessageObject, boolean notify, int scheduleDate, InputContentInfoCompat inputContent) {
if ((path == null || originalPath == null) && uri == null) { if ((path == null || originalPath == null) && uri == null) {
return; return;
} }
@ -6727,11 +6875,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
paths.add(path); paths.add(path);
originalPaths.add(originalPath); originalPaths.add(originalPath);
} }
prepareSendingDocuments(accountInstance, paths, originalPaths, uris, caption, mine, dialogId, replyToMsg, replyToTopMsg, storyItem, editingMessageObject, notify, scheduleDate, inputContent); prepareSendingDocuments(accountInstance, paths, originalPaths, uris, caption, mine, dialogId, replyToMsg, replyToTopMsg, storyItem, quote, editingMessageObject, notify, scheduleDate, inputContent);
} }
@UiThread @UiThread
public static void prepareSendingAudioDocuments(AccountInstance accountInstance, ArrayList<MessageObject> messageObjects, CharSequence caption, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, boolean notify, int scheduleDate, MessageObject editingMessageObject) { public static void prepareSendingAudioDocuments(AccountInstance accountInstance, ArrayList<MessageObject> messageObjects, CharSequence caption, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem storyItem, boolean notify, int scheduleDate, MessageObject editingMessageObject) {
new Thread(() -> { new Thread(() -> {
int count = messageObjects.size(); int count = messageObjects.size();
long groupId = 0; long groupId = 0;
@ -6824,7 +6972,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
@UiThread @UiThread
public static void prepareSendingDocuments(AccountInstance accountInstance, ArrayList<String> paths, ArrayList<String> originalPaths, ArrayList<Uri> uris, String caption, String mime, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, MessageObject editingMessageObject, boolean notify, int scheduleDate, InputContentInfoCompat inputContent) { public static void prepareSendingDocuments(AccountInstance accountInstance, ArrayList<String> paths, ArrayList<String> originalPaths, ArrayList<Uri> uris, String caption, String mime, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem storyItem, ChatActivity.ReplyQuote quote, MessageObject editingMessageObject, boolean notify, int scheduleDate, InputContentInfoCompat inputContent) {
if (paths == null && originalPaths == null && uris == null || paths != null && originalPaths != null && paths.size() != originalPaths.size()) { if (paths == null && originalPaths == null && uris == null || paths != null && originalPaths != null && paths.size() != originalPaths.size()) {
return; return;
} }
@ -6849,7 +6997,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
mediaCount++; mediaCount++;
long prevGroupId = groupId[0]; long prevGroupId = groupId[0];
error = prepareSendingDocumentInternal(accountInstance, paths.get(a), originalPaths.get(a), null, mime, dialogId, replyToMsg, replyToTopMsg, storyItem, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, captionFinal, notify, scheduleDate, docType, inputContent == null); error = prepareSendingDocumentInternal(accountInstance, paths.get(a), originalPaths.get(a), null, mime, dialogId, replyToMsg, replyToTopMsg, storyItem, quote, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, captionFinal, notify, scheduleDate, docType, inputContent == null);
if (prevGroupId != groupId[0] || groupId[0] == -1) { if (prevGroupId != groupId[0] || groupId[0] == -1) {
mediaCount = 1; mediaCount = 1;
} }
@ -6870,7 +7018,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
mediaCount++; mediaCount++;
long prevGroupId = groupId[0]; long prevGroupId = groupId[0];
error = prepareSendingDocumentInternal(accountInstance, null, null, uris.get(a), mime, dialogId, replyToMsg, replyToTopMsg, storyItem, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, captionFinal, notify, scheduleDate, docType, inputContent == null); error = prepareSendingDocumentInternal(accountInstance, null, null, uris.get(a), mime, dialogId, replyToMsg, replyToTopMsg, storyItem, quote, null, editingMessageObject, groupId, mediaCount == 10 || a == count - 1, captionFinal, notify, scheduleDate, docType, inputContent == null);
if (prevGroupId != groupId[0] || groupId[0] == -1) { if (prevGroupId != groupId[0] || groupId[0] == -1) {
mediaCount = 1; mediaCount = 1;
} }
@ -6901,12 +7049,12 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
@UiThread @UiThread
public static void prepareSendingPhoto(AccountInstance accountInstance, String imageFilePath, Uri imageUri, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, CharSequence caption, ArrayList<TLRPC.MessageEntity> entities, ArrayList<TLRPC.InputDocument> stickers, InputContentInfoCompat inputContent, int ttl, MessageObject editingMessageObject, boolean notify, int scheduleDate) { public static void prepareSendingPhoto(AccountInstance accountInstance, String imageFilePath, Uri imageUri, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, ChatActivity.ReplyQuote quote, CharSequence caption, ArrayList<TLRPC.MessageEntity> entities, ArrayList<TLRPC.InputDocument> stickers, InputContentInfoCompat inputContent, int ttl, MessageObject editingMessageObject, boolean notify, int scheduleDate) {
prepareSendingPhoto(accountInstance, imageFilePath, null, imageUri, dialogId, replyToMsg, replyToTopMsg, null, entities, stickers, inputContent, ttl, editingMessageObject, null, notify, scheduleDate, false, caption); prepareSendingPhoto(accountInstance, imageFilePath, null, imageUri, dialogId, replyToMsg, replyToTopMsg, null, null, entities, stickers, inputContent, ttl, editingMessageObject, null, notify, scheduleDate, false, caption);
} }
@UiThread @UiThread
public static void prepareSendingPhoto(AccountInstance accountInstance, String imageFilePath, String thumbFilePath, Uri imageUri, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, ArrayList<TLRPC.MessageEntity> entities, ArrayList<TLRPC.InputDocument> stickers, InputContentInfoCompat inputContent, int ttl, MessageObject editingMessageObject, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, boolean forceDocument, CharSequence caption) { public static void prepareSendingPhoto(AccountInstance accountInstance, String imageFilePath, String thumbFilePath, Uri imageUri, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem storyItem, ChatActivity.ReplyQuote quote, ArrayList<TLRPC.MessageEntity> entities, ArrayList<TLRPC.InputDocument> stickers, InputContentInfoCompat inputContent, int ttl, MessageObject editingMessageObject, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, boolean forceDocument, CharSequence caption) {
SendingMediaInfo info = new SendingMediaInfo(); SendingMediaInfo info = new SendingMediaInfo();
info.path = imageFilePath; info.path = imageFilePath;
info.thumbPath = thumbFilePath; info.thumbPath = thumbFilePath;
@ -6922,11 +7070,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
info.videoEditedInfo = videoEditedInfo; info.videoEditedInfo = videoEditedInfo;
ArrayList<SendingMediaInfo> infos = new ArrayList<>(); ArrayList<SendingMediaInfo> infos = new ArrayList<>();
infos.add(info); infos.add(info);
prepareSendingMedia(accountInstance, infos, dialogId, replyToMsg, replyToTopMsg, null, forceDocument, false, editingMessageObject, notify, scheduleDate, false, inputContent); prepareSendingMedia(accountInstance, infos, dialogId, replyToMsg, replyToTopMsg, null, quote, forceDocument, false, editingMessageObject, notify, scheduleDate, false, inputContent);
} }
@UiThread @UiThread
public static void prepareSendingBotContextResult(BaseFragment fragment, AccountInstance accountInstance, TLRPC.BotInlineResult result, HashMap<String, String> params, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, boolean notify, int scheduleDate) { public static void prepareSendingBotContextResult(BaseFragment fragment, AccountInstance accountInstance, TLRPC.BotInlineResult result, HashMap<String, String> params, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem storyItem, ChatActivity.ReplyQuote quote, boolean notify, int scheduleDate) {
if (result == null) { if (result == null) {
return; return;
} }
@ -7207,6 +7355,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
if (params2 != null) { if (params2 != null) {
params2.replyToStoryItem = storyItem; params2.replyToStoryItem = storyItem;
params2.replyQuote = quote;
accountInstance.getSendMessagesHelper().sendMessage(params2); accountInstance.getSendMessagesHelper().sendMessage(params2);
} }
}); });
@ -7279,6 +7428,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
messageMediaInvoice.total_amount = invoice.total_amount; messageMediaInvoice.total_amount = invoice.total_amount;
messageMediaInvoice.start_param = ""; messageMediaInvoice.start_param = "";
accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(messageMediaInvoice, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate)); accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(messageMediaInvoice, dialogId, replyToMsg, replyToTopMsg, result.send_message.reply_markup, params, notify, scheduleDate));
} else if (result.send_message instanceof TLRPC.TL_botInlineMessageMediaWebPage) {
TLRPC.TL_botInlineMessageMediaWebPage request = (TLRPC.TL_botInlineMessageMediaWebPage) result.send_message;
TLRPC.WebPage webPage = new TLRPC.TL_webPagePending();
webPage.url = request.url;
accountInstance.getSendMessagesHelper().sendMessage(SendMessagesHelper.SendMessageParams.of(result.send_message.message, dialogId, replyToMsg, replyToTopMsg, webPage, !result.send_message.no_webpage, result.send_message.entities, result.send_message.reply_markup, params, notify, scheduleDate, null, false));
} }
} }
@ -7450,7 +7604,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
@UiThread @UiThread
public static void prepareSendingMedia(AccountInstance accountInstance, ArrayList<SendingMediaInfo> media, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, boolean forceDocument, boolean groupMedia, MessageObject editingMessageObject, boolean notify, int scheduleDate, boolean updateStikcersOrder, InputContentInfoCompat inputContent) { public static void prepareSendingMedia(AccountInstance accountInstance, ArrayList<SendingMediaInfo> media, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem storyItem, ChatActivity.ReplyQuote quote, boolean forceDocument, boolean groupMedia, MessageObject editingMessageObject, boolean notify, int scheduleDate, boolean updateStikcersOrder, InputContentInfoCompat inputContent) {
if (media.isEmpty()) { if (media.isEmpty()) {
return; return;
} }
@ -7481,7 +7635,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
boolean isWebP = false; boolean isWebP = false;
if (tempPath != null && info.ttl <= 0 && (tempPath.endsWith(".gif") || (isWebP = tempPath.endsWith(".webp")))) { if (tempPath != null && info.ttl <= 0 && (tempPath.endsWith(".gif") || (isWebP = tempPath.endsWith(".webp")))) {
if (media.size() <= 1 && (!isWebP || shouldSendWebPAsSticker(tempPath, null))) { if (media.size() <= 1 && (!isWebP || shouldSendWebPAsSticker(tempPath, null)) && TextUtils.isEmpty(info.caption)) {
continue; continue;
} else { } else {
info.forceImage = true; info.forceImage = true;
@ -7490,7 +7644,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
continue; continue;
} else if (tempPath == null && info.uri != null) { } else if (tempPath == null && info.uri != null) {
if (MediaController.isGif(info.uri) || (isWebP = MediaController.isWebp(info.uri))) { if (MediaController.isGif(info.uri) || (isWebP = MediaController.isWebp(info.uri))) {
if (media.size() <= 1 && (!isWebP || shouldSendWebPAsSticker(null, info.uri))) { if (media.size() <= 1 && (!isWebP || shouldSendWebPAsSticker(null, info.uri)) && TextUtils.isEmpty(info.caption)) {
continue; continue;
} else { } else {
info.forceImage = true; info.forceImage = true;
@ -7656,6 +7810,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} else { } else {
SendMessageParams sendMessageParams = SendMessageParams.of(documentFinal, null, pathFinal, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, 0, parentFinal, null, false, info.hasMediaSpoilers); SendMessageParams sendMessageParams = SendMessageParams.of(documentFinal, null, pathFinal, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, 0, parentFinal, null, false, info.hasMediaSpoilers);
sendMessageParams.replyToStoryItem = storyItem; sendMessageParams.replyToStoryItem = storyItem;
sendMessageParams.replyQuote = quote;
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
} }
}); });
@ -7729,6 +7884,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} else { } else {
SendMessageParams sendMessageParams = SendMessageParams.of(photoFinal, needDownloadHttpFinal ? info.searchImage.imageUrl : null, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, false, info.hasMediaSpoilers); SendMessageParams sendMessageParams = SendMessageParams.of(photoFinal, needDownloadHttpFinal ? info.searchImage.imageUrl : null, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, false, info.hasMediaSpoilers);
sendMessageParams.replyToStoryItem = storyItem; sendMessageParams.replyToStoryItem = storyItem;
sendMessageParams.replyQuote = quote;
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
} }
}); });
@ -7905,6 +8061,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} else { } else {
SendMessageParams sendMessageParams = SendMessageParams.of(videoFinal, videoEditedInfo, finalPath, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, null, false, info.hasMediaSpoilers); SendMessageParams sendMessageParams = SendMessageParams.of(videoFinal, videoEditedInfo, finalPath, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, null, false, info.hasMediaSpoilers);
sendMessageParams.replyToStoryItem = storyItem; sendMessageParams.replyToStoryItem = storyItem;
sendMessageParams.replyQuote = quote;
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
} }
}); });
@ -8104,6 +8261,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} else { } else {
SendMessageParams sendMessageParams = SendMessageParams.of(photoFinal, null, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, updateStikcersOrder, info.hasMediaSpoilers); SendMessageParams sendMessageParams = SendMessageParams.of(photoFinal, null, dialogId, replyToMsg, replyToTopMsg, info.caption, info.entities, null, params, notify, scheduleDate, info.ttl, parentFinal, updateStikcersOrder, info.hasMediaSpoilers);
sendMessageParams.replyToStoryItem = storyItem; sendMessageParams.replyToStoryItem = storyItem;
sendMessageParams.replyQuote = quote;
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
} }
}); });
@ -8140,7 +8298,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
mediaCount = 0; mediaCount = 0;
} }
mediaCount++; mediaCount++;
int error = prepareSendingDocumentInternal(accountInstance, sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), sendAsDocumentsUri.get(a), extension, dialogId, replyToMsg, replyToTopMsg, storyItem, sendAsDocumentsEntities.get(a), editingMessageObject, groupId2, mediaCount == 10 || a == documentsCount - 1, sendAsDocumentsCaptions.get(a), notify, scheduleDate, null, forceDocument); int error = prepareSendingDocumentInternal(accountInstance, sendAsDocuments.get(a), sendAsDocumentsOriginal.get(a), sendAsDocumentsUri.get(a), extension, dialogId, replyToMsg, replyToTopMsg, storyItem, quote, sendAsDocumentsEntities.get(a), editingMessageObject, groupId2, mediaCount == 10 || a == documentsCount - 1, sendAsDocumentsCaptions.get(a), notify, scheduleDate, null, forceDocument);
handleError(error, accountInstance); handleError(error, accountInstance);
} }
} }
@ -8405,7 +8563,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} }
@UiThread @UiThread
public static void prepareSendingVideo(AccountInstance accountInstance, String videoPath, VideoEditedInfo info, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TLRPC.StoryItem storyItem, ArrayList<TLRPC.MessageEntity> entities, int ttl, MessageObject editingMessageObject, boolean notify, int scheduleDate, boolean forceDocument, boolean hasMediaSpoilers, CharSequence caption) { public static void prepareSendingVideo(AccountInstance accountInstance, String videoPath, VideoEditedInfo info, long dialogId, MessageObject replyToMsg, MessageObject replyToTopMsg, TL_stories.StoryItem storyItem, ChatActivity.ReplyQuote quote, ArrayList<TLRPC.MessageEntity> entities, int ttl, MessageObject editingMessageObject, boolean notify, int scheduleDate, boolean forceDocument, boolean hasMediaSpoilers, CharSequence caption) {
if (videoPath == null || videoPath.length() == 0) { if (videoPath == null || videoPath.length() == 0) {
return; return;
} }
@ -8554,11 +8712,12 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
} else { } else {
SendMessageParams sendMessageParams = SendMessageParams.of(videoFinal, videoEditedInfo, finalPath, dialogId, replyToMsg, replyToTopMsg, captionFinal, entities, null, params, notify, scheduleDate, ttl, parentFinal, null, false, hasMediaSpoilers); SendMessageParams sendMessageParams = SendMessageParams.of(videoFinal, videoEditedInfo, finalPath, dialogId, replyToMsg, replyToTopMsg, captionFinal, entities, null, params, notify, scheduleDate, ttl, parentFinal, null, false, hasMediaSpoilers);
sendMessageParams.replyToStoryItem = storyItem; sendMessageParams.replyToStoryItem = storyItem;
sendMessageParams.replyQuote = quote;
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams); accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
} }
}); });
} else { } else {
prepareSendingDocumentInternal(accountInstance, videoPath, videoPath, null, null, dialogId, replyToMsg, replyToTopMsg, storyItem, entities, editingMessageObject, null, false, caption, notify, scheduleDate, null, forceDocument); prepareSendingDocumentInternal(accountInstance, videoPath, videoPath, null, null, dialogId, replyToMsg, replyToTopMsg, storyItem, quote, entities, editingMessageObject, null, false, caption, notify, scheduleDate, null, forceDocument);
} }
}).start(); }).start();
} }
@ -8574,6 +8733,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
public TLRPC.TL_game game; public TLRPC.TL_game game;
public TLRPC.TL_messageMediaPoll poll; public TLRPC.TL_messageMediaPoll poll;
public TLRPC.TL_messageMediaInvoice invoice; public TLRPC.TL_messageMediaInvoice invoice;
public TLRPC.TL_messageMediaWebPage mediaWebPage;
public long peer; public long peer;
public String path; public String path;
public MessageObject replyToMsg; public MessageObject replyToMsg;
@ -8591,9 +8751,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
public MessageObject.SendAnimationData sendAnimationData; public MessageObject.SendAnimationData sendAnimationData;
public boolean updateStickersOrder; public boolean updateStickersOrder;
public boolean hasMediaSpoilers; public boolean hasMediaSpoilers;
public TLRPC.StoryItem replyToStoryItem; public TL_stories.StoryItem replyToStoryItem;
public TLRPC.StoryItem sendingStory; public TL_stories.StoryItem sendingStory;
public ChatActivity.ReplyQuote replyQuote;
public boolean invert_media;
public static SendMessageParams of(String string, long dialogId) { public static SendMessageParams of(String string, long dialogId) {
return of(string, null, null, null, null, null, null, null, null, null, dialogId, null, null, null, null, true, null, null, null, null, false, 0, 0, null, null, false); return of(string, null, null, null, null, null, null, null, null, null, dialogId, null, null, null, null, true, null, null, null, null, false, 0, 0, null, null, false);

View file

@ -21,6 +21,7 @@ import android.os.Environment;
import android.os.SystemClock; import android.os.SystemClock;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
import android.util.Log;
import android.webkit.WebView; import android.webkit.WebView;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
@ -136,6 +137,14 @@ public class SharedConfig {
return allowPreparingHevcPlayers; return allowPreparingHevcPlayers;
} }
public static void togglePaymentByInvoice() {
payByInvoice = !payByInvoice;
ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE)
.edit()
.putBoolean("payByInvoice", payByInvoice)
.apply();
}
public static void toggleSurfaceInStories() { public static void toggleSurfaceInStories() {
useSurfaceInStories = !useSurfaceInStories; useSurfaceInStories = !useSurfaceInStories;
ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE) ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE)
@ -238,17 +247,19 @@ public class SharedConfig {
public static int scheduledHintShows; public static int scheduledHintShows;
public static long scheduledHintSeenAt; public static long scheduledHintSeenAt;
public static int lockRecordAudioVideoHint; public static int lockRecordAudioVideoHint;
public static boolean forwardingOptionsHintShown; public static boolean forwardingOptionsHintShown, replyingOptionsHintShown;
public static boolean searchMessagesAsListUsed; public static boolean searchMessagesAsListUsed;
public static boolean stickersReorderingHintUsed; public static boolean stickersReorderingHintUsed;
public static int dayNightWallpaperSwitchHint; public static int dayNightWallpaperSwitchHint;
public static boolean storyReactionsLongPressHint; public static boolean storyReactionsLongPressHint;
public static boolean storiesIntroShown;
public static boolean disableVoiceAudioEffects; public static boolean disableVoiceAudioEffects;
public static boolean forceDisableTabletMode; public static boolean forceDisableTabletMode;
public static boolean updateStickersOrderOnSend = true; public static boolean updateStickersOrderOnSend = true;
public static boolean bigCameraForRound; public static boolean bigCameraForRound;
public static boolean useSurfaceInStories; public static boolean useSurfaceInStories;
public static boolean photoViewerBlur = true; public static boolean photoViewerBlur = true;
public static boolean payByInvoice;
public static int stealthModeSendMessageConfirm = 2; public static int stealthModeSendMessageConfirm = 2;
private static int lastLocalId = -210000; private static int lastLocalId = -210000;
@ -437,6 +448,7 @@ public class SharedConfig {
editor.putInt("scheduledHintShows", scheduledHintShows); editor.putInt("scheduledHintShows", scheduledHintShows);
editor.putLong("scheduledHintSeenAt", scheduledHintSeenAt); editor.putLong("scheduledHintSeenAt", scheduledHintSeenAt);
editor.putBoolean("forwardingOptionsHintShown", forwardingOptionsHintShown); editor.putBoolean("forwardingOptionsHintShown", forwardingOptionsHintShown);
editor.putBoolean("replyingOptionsHintShown", replyingOptionsHintShown);
editor.putInt("lockRecordAudioVideoHint", lockRecordAudioVideoHint); editor.putInt("lockRecordAudioVideoHint", lockRecordAudioVideoHint);
editor.putString("storageCacheDir", !TextUtils.isEmpty(storageCacheDir) ? storageCacheDir : ""); editor.putString("storageCacheDir", !TextUtils.isEmpty(storageCacheDir) ? storageCacheDir : "");
editor.putBoolean("proxyRotationEnabled", proxyRotationEnabled); editor.putBoolean("proxyRotationEnabled", proxyRotationEnabled);
@ -609,12 +621,14 @@ public class SharedConfig {
searchMessagesAsListUsed = preferences.getBoolean("searchMessagesAsListUsed", false); searchMessagesAsListUsed = preferences.getBoolean("searchMessagesAsListUsed", false);
stickersReorderingHintUsed = preferences.getBoolean("stickersReorderingHintUsed", false); stickersReorderingHintUsed = preferences.getBoolean("stickersReorderingHintUsed", false);
storyReactionsLongPressHint = preferences.getBoolean("storyReactionsLongPressHint", false); storyReactionsLongPressHint = preferences.getBoolean("storyReactionsLongPressHint", false);
storiesIntroShown = preferences.getBoolean("storiesIntroShown", false);
textSelectionHintShows = preferences.getInt("textSelectionHintShows", 0); textSelectionHintShows = preferences.getInt("textSelectionHintShows", 0);
scheduledOrNoSoundHintShows = preferences.getInt("scheduledOrNoSoundHintShows", 0); scheduledOrNoSoundHintShows = preferences.getInt("scheduledOrNoSoundHintShows", 0);
scheduledOrNoSoundHintSeenAt = preferences.getLong("scheduledOrNoSoundHintSeenAt", 0); scheduledOrNoSoundHintSeenAt = preferences.getLong("scheduledOrNoSoundHintSeenAt", 0);
scheduledHintShows = preferences.getInt("scheduledHintShows", 0); scheduledHintShows = preferences.getInt("scheduledHintShows", 0);
scheduledHintSeenAt = preferences.getLong("scheduledHintSeenAt", 0); scheduledHintSeenAt = preferences.getLong("scheduledHintSeenAt", 0);
forwardingOptionsHintShown = preferences.getBoolean("forwardingOptionsHintShown", false); forwardingOptionsHintShown = preferences.getBoolean("forwardingOptionsHintShown", false);
replyingOptionsHintShown = preferences.getBoolean("replyingOptionsHintShown", false);
lockRecordAudioVideoHint = preferences.getInt("lockRecordAudioVideoHint", 0); lockRecordAudioVideoHint = preferences.getInt("lockRecordAudioVideoHint", 0);
disableVoiceAudioEffects = preferences.getBoolean("disableVoiceAudioEffects", false); disableVoiceAudioEffects = preferences.getBoolean("disableVoiceAudioEffects", false);
noiseSupression = preferences.getBoolean("noiseSupression", false); noiseSupression = preferences.getBoolean("noiseSupression", false);
@ -633,6 +647,7 @@ public class SharedConfig {
dayNightWallpaperSwitchHint = preferences.getInt("dayNightWallpaperSwitchHint", 0); dayNightWallpaperSwitchHint = preferences.getInt("dayNightWallpaperSwitchHint", 0);
bigCameraForRound = preferences.getBoolean("bigCameraForRound", false); bigCameraForRound = preferences.getBoolean("bigCameraForRound", false);
useSurfaceInStories = preferences.getBoolean("useSurfaceInStories", Build.VERSION.SDK_INT >= 30); useSurfaceInStories = preferences.getBoolean("useSurfaceInStories", Build.VERSION.SDK_INT >= 30);
payByInvoice = preferences.getBoolean("payByInvoice", false);
photoViewerBlur = preferences.getBoolean("photoViewerBlur", true); photoViewerBlur = preferences.getBoolean("photoViewerBlur", true);
loadDebugConfig(preferences); loadDebugConfig(preferences);
@ -726,7 +741,7 @@ public class SharedConfig {
} }
public static boolean isAppUpdateAvailable() { public static boolean isAppUpdateAvailable() {
if (pendingAppUpdate == null || pendingAppUpdate.document == null || !BuildVars.isStandaloneApp()) { if (pendingAppUpdate == null || pendingAppUpdate.document == null || !ApplicationLoader.isStandaloneBuild()) {
return false; return false;
} }
int currentVersion; int currentVersion;
@ -838,6 +853,7 @@ public class SharedConfig {
scheduledHintSeenAt = 0; scheduledHintSeenAt = 0;
lockRecordAudioVideoHint = 0; lockRecordAudioVideoHint = 0;
forwardingOptionsHintShown = false; forwardingOptionsHintShown = false;
replyingOptionsHintShown = false;
messageSeenHintCount = 3; messageSeenHintCount = 3;
emojiInteractionsHintCount = 3; emojiInteractionsHintCount = 3;
dayNightThemeSwitchHintCount = 3; dayNightThemeSwitchHintCount = 3;
@ -878,6 +894,14 @@ public class SharedConfig {
editor.apply(); editor.apply();
} }
public static void setStoriesIntroShown(boolean isShown) {
storiesIntroShown = isShown;
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("storiesIntroShown", storiesIntroShown);
editor.apply();
}
public static void increaseTextSelectionHintShowed() { public static void increaseTextSelectionHintShowed() {
SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences preferences = MessagesController.getGlobalMainSettings();
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();
@ -925,6 +949,14 @@ public class SharedConfig {
editor.apply(); editor.apply();
} }
public static void replyingOptionsHintHintShowed() {
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
SharedPreferences.Editor editor = preferences.edit();
replyingOptionsHintShown = true;
editor.putBoolean("replyingOptionsHintShown", replyingOptionsHintShown);
editor.apply();
}
public static void removeScheduledOrNoSoundHint() { public static void removeScheduledOrNoSoundHint() {
SharedPreferences preferences = MessagesController.getGlobalMainSettings(); SharedPreferences preferences = MessagesController.getGlobalMainSettings();
SharedPreferences.Editor editor = preferences.edit(); SharedPreferences.Editor editor = preferences.edit();

View file

@ -452,7 +452,7 @@ public class TopicsController extends BaseController {
if (topicId != 0) { if (topicId != 0) {
TLRPC.TL_forumTopic topic = findTopic(chat.id, topicId); TLRPC.TL_forumTopic topic = findTopic(chat.id, topicId);
if (topic != null) { if (topic != null) {
return ForumUtilities.getTopicSpannedName(topic, paint, drawableToSet); return ForumUtilities.getTopicSpannedName(topic, paint, drawableToSet, false);
} }
} }
return null; return null;

View file

@ -11,6 +11,7 @@ import android.view.inputmethod.InputMethodSubtype;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.tgnet.tl.TL_stories;
import org.telegram.ui.Components.Bulletin; import org.telegram.ui.Components.Bulletin;
import org.telegram.ui.Components.TranslateAlert2; import org.telegram.ui.Components.TranslateAlert2;
import org.telegram.ui.RestrictedLanguagesSelectActivity; import org.telegram.ui.RestrictedLanguagesSelectActivity;
@ -1021,7 +1022,7 @@ public class TranslateController extends BaseController {
private final HashSet<StoryKey> translatingStories = new HashSet<>(); private final HashSet<StoryKey> translatingStories = new HashSet<>();
// ensure dialogId in storyItem is valid // ensure dialogId in storyItem is valid
public void detectStoryLanguage(TLRPC.StoryItem storyItem) { public void detectStoryLanguage(TL_stories.StoryItem storyItem) {
if (storyItem == null || storyItem.detectedLng != null || storyItem.caption == null || storyItem.caption.length() == 0 || !LanguageDetector.hasSupport()) { if (storyItem == null || storyItem.detectedLng != null || storyItem.caption == null || storyItem.caption.length() == 0 || !LanguageDetector.hasSupport()) {
return; return;
} }
@ -1043,14 +1044,14 @@ public class TranslateController extends BaseController {
})); }));
} }
public boolean canTranslateStory(TLRPC.StoryItem storyItem) { public boolean canTranslateStory(TL_stories.StoryItem storyItem) {
return storyItem != null && !TextUtils.isEmpty(storyItem.caption) && !Emoji.fullyConsistsOfEmojis(storyItem.caption) && ( return storyItem != null && !TextUtils.isEmpty(storyItem.caption) && !Emoji.fullyConsistsOfEmojis(storyItem.caption) && (
storyItem.detectedLng == null && storyItem.translatedText != null && TextUtils.equals(storyItem.translatedLng, TranslateAlert2.getToLanguage()) || storyItem.detectedLng == null && storyItem.translatedText != null && TextUtils.equals(storyItem.translatedLng, TranslateAlert2.getToLanguage()) ||
storyItem.detectedLng != null && !RestrictedLanguagesSelectActivity.getRestrictedLanguages().contains(storyItem.detectedLng) storyItem.detectedLng != null && !RestrictedLanguagesSelectActivity.getRestrictedLanguages().contains(storyItem.detectedLng)
); );
} }
public void translateStory(TLRPC.StoryItem storyItem, Runnable done) { public void translateStory(TL_stories.StoryItem storyItem, Runnable done) {
if (storyItem == null) { if (storyItem == null) {
return; return;
} }
@ -1120,7 +1121,7 @@ public class TranslateController extends BaseController {
}); });
} }
public boolean isTranslatingStory(TLRPC.StoryItem storyItem) { public boolean isTranslatingStory(TL_stories.StoryItem storyItem) {
if (storyItem == null) { if (storyItem == null) {
return false; return false;
} }
@ -1131,7 +1132,7 @@ public class TranslateController extends BaseController {
public long dialogId; public long dialogId;
public int storyId; public int storyId;
public StoryKey(TLRPC.StoryItem storyItem) { public StoryKey(TL_stories.StoryItem storyItem) {
dialogId = storyItem.dialogId; dialogId = storyItem.dialogId;
storyId = storyItem.id; storyId = storyItem.id;
} }

View file

@ -10,11 +10,15 @@ package org.telegram.messenger;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.NonNull;
import org.telegram.PhoneFormat.PhoneFormat; import org.telegram.PhoneFormat.PhoneFormat;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
public class UserObject { public class UserObject {
public static final long REPLY_BOT = 1271266957L;
public static boolean isDeleted(TLRPC.User user) { public static boolean isDeleted(TLRPC.User user) {
return user == null || user instanceof TLRPC.TL_userDeleted_old2 || user instanceof TLRPC.TL_userEmpty || user.deleted; return user == null || user instanceof TLRPC.TL_userDeleted_old2 || user instanceof TLRPC.TL_userEmpty || user.deleted;
} }
@ -28,13 +32,14 @@ public class UserObject {
} }
public static boolean isReplyUser(TLRPC.User user) { public static boolean isReplyUser(TLRPC.User user) {
return user != null && (user.id == 708513 || user.id == 1271266957); return user != null && (user.id == 708513 || user.id == REPLY_BOT);
} }
public static boolean isReplyUser(long did) { public static boolean isReplyUser(long did) {
return did == 708513 || did == 1271266957; return did == 708513 || did == REPLY_BOT;
} }
@NonNull
public static String getUserName(TLRPC.User user) { public static String getUserName(TLRPC.User user) {
if (user == null || isDeleted(user)) { if (user == null || isDeleted(user)) {
return LocaleController.getString("HiddenName", R.string.HiddenName); return LocaleController.getString("HiddenName", R.string.HiddenName);

View file

@ -522,6 +522,10 @@ public class Utilities {
public ReturnType run(); public ReturnType run();
} }
public static interface Callback0Return<ReturnType> {
public ReturnType run();
}
public static interface CallbackReturn<Arg, ReturnType> { public static interface CallbackReturn<Arg, ReturnType> {
public ReturnType run(Arg arg); public ReturnType run(Arg arg);
} }

View file

@ -17,12 +17,12 @@ import org.telegram.messenger.video.MediaCodecVideoConvertor;
import org.telegram.tgnet.AbstractSerializedData; import org.telegram.tgnet.AbstractSerializedData;
import org.telegram.tgnet.SerializedData; import org.telegram.tgnet.SerializedData;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.tgnet.tl.TL_stories;
import org.telegram.ui.Components.AnimatedFileDrawable; import org.telegram.ui.Components.AnimatedFileDrawable;
import org.telegram.ui.Components.Paint.PaintTypeface; import org.telegram.ui.Components.Paint.PaintTypeface;
import org.telegram.ui.Components.PhotoFilterView; import org.telegram.ui.Components.PhotoFilterView;
import org.telegram.ui.Components.Point; import org.telegram.ui.Components.Point;
import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble; import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble;
import org.telegram.ui.Components.Reactions.ReactionsUtils;
import org.telegram.ui.Stories.recorder.StoryEntry; import org.telegram.ui.Stories.recorder.StoryEntry;
import java.util.ArrayList; import java.util.ArrayList;
@ -154,7 +154,7 @@ public class VideoEditedInfo {
public AnimatedFileDrawable animatedFileDrawable; public AnimatedFileDrawable animatedFileDrawable;
public Canvas roundRadiusCanvas; public Canvas roundRadiusCanvas;
public TLRPC.MediaArea mediaArea; public TL_stories.MediaArea mediaArea;
public TLRPC.MessageMedia mediaGeo; public TLRPC.MessageMedia mediaGeo;
public float density; public float density;
@ -202,7 +202,7 @@ public class VideoEditedInfo {
} }
if (type == TYPE_LOCATION) { if (type == TYPE_LOCATION) {
density = data.readFloat(false); density = data.readFloat(false);
mediaArea = TLRPC.MediaArea.TLdeserialize(data, data.readInt32(false), false); mediaArea = TL_stories.MediaArea.TLdeserialize(data, data.readInt32(false), false);
mediaGeo = TLRPC.MessageMedia.TLdeserialize(data, data.readInt32(false), false); mediaGeo = TLRPC.MessageMedia.TLdeserialize(data, data.readInt32(false), false);
if (data.remaining() > 0) { if (data.remaining() > 0) {
int magic = data.readInt32(false); int magic = data.readInt32(false);
@ -215,7 +215,7 @@ public class VideoEditedInfo {
} }
} }
if (type == TYPE_REACTION) { if (type == TYPE_REACTION) {
mediaArea = TLRPC.MediaArea.TLdeserialize(data, data.readInt32(false), false); mediaArea = TL_stories.MediaArea.TLdeserialize(data, data.readInt32(false), false);
} }
} }

View file

@ -14,34 +14,63 @@ import android.os.IBinder;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import com.google.android.exoplayer2.util.Log;
public class VideoEncodingService extends Service implements NotificationCenter.NotificationCenterDelegate { public class VideoEncodingService extends Service implements NotificationCenter.NotificationCenterDelegate {
private NotificationCompat.Builder builder; private NotificationCompat.Builder builder;
private String path; private MediaController.VideoConvertMessage currentMessage;
private int currentProgress; private static VideoEncodingService instance;
private int currentAccount;
int currentAccount;
String currentPath;
public VideoEncodingService() { public VideoEncodingService() {
super(); super();
NotificationCenter.getGlobalInstance().addObserver(this, NotificationCenter.stopEncodingService); }
public static void start(boolean cancelled) {
if (instance == null) {
Intent intent = new Intent(ApplicationLoader.applicationContext, VideoEncodingService.class);
ApplicationLoader.applicationContext.startService(intent);
} else if (cancelled) {
MediaController.VideoConvertMessage messageInController = MediaController.getInstance().getCurrentForegroundConverMessage();
if (instance.currentMessage != messageInController) {
if (messageInController != null) {
instance.setCurrentMessage(messageInController);
} else {
instance.stopSelf();
}
}
}
}
public static void stop() {
if (instance != null) {
instance.stopSelf();
}
} }
public IBinder onBind(Intent arg2) { public IBinder onBind(Intent arg2) {
return null; return null;
} }
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
instance = null;
try { try {
stopForeground(true); stopForeground(true);
} catch (Throwable ignore) { } catch (Throwable ignore) {
} }
NotificationManagerCompat.from(ApplicationLoader.applicationContext).cancel(4); NotificationManagerCompat.from(ApplicationLoader.applicationContext).cancel(4);
NotificationCenter.getGlobalInstance().removeObserver(this, NotificationCenter.stopEncodingService);
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploadProgressChanged); NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploadProgressChanged);
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploadFailed);
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploaded);
currentMessage = null;
if (BuildVars.LOGS_ENABLED) { if (BuildVars.LOGS_ENABLED) {
FileLog.d("destroy video service"); FileLog.d("VideoEncodingService: destroy video service");
} }
} }
@ -49,12 +78,12 @@ public class VideoEncodingService extends Service implements NotificationCenter.
public void didReceivedNotification(int id, int account, Object... args) { public void didReceivedNotification(int id, int account, Object... args) {
if (id == NotificationCenter.fileUploadProgressChanged) { if (id == NotificationCenter.fileUploadProgressChanged) {
String fileName = (String) args[0]; String fileName = (String) args[0];
if (account == currentAccount && path != null && path.equals(fileName)) { if (account == currentAccount && currentPath != null && currentPath.equals(fileName)) {
Long loadedSize = (Long) args[1]; Long loadedSize = (Long) args[1];
Long totalSize = (Long) args[2]; Long totalSize = (Long) args[2];
float progress = Math.min(1f, loadedSize / (float) totalSize); float progress = Math.min(1f, loadedSize / (float) totalSize);
Boolean enc = (Boolean) args[3]; Boolean enc = (Boolean) args[3];
currentProgress = (int) (progress * 100); int currentProgress = (int) (progress * 100);
builder.setProgress(100, currentProgress, currentProgress == 0); builder.setProgress(100, currentProgress, currentProgress == 0);
try { try {
NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(4, builder.build()); NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(4, builder.build());
@ -62,54 +91,86 @@ public class VideoEncodingService extends Service implements NotificationCenter.
FileLog.e(e); FileLog.e(e);
} }
} }
} else if (id == NotificationCenter.stopEncodingService) { } else if (id == NotificationCenter.fileUploaded || id == NotificationCenter.fileUploadFailed) {
String filepath = (String) args[0]; String fileName = (String) args[0];
account = (Integer) args[1]; if (account == currentAccount && currentPath != null && currentPath.equals(fileName)) {
if (account == currentAccount && (filepath == null || filepath.equals(path))) { AndroidUtilities.runOnUIThread(() -> {
stopSelf(); MediaController.VideoConvertMessage message = MediaController.getInstance().getCurrentForegroundConverMessage();
if (message != null) {
setCurrentMessage(message);
} else {
stopSelf();
}
});
} }
} }
} }
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
path = intent.getStringExtra("path"); if (isRunning()) {
int oldAccount = currentAccount;
currentAccount = intent.getIntExtra("currentAccount", UserConfig.selectedAccount);
if (!UserConfig.isValidAccount(currentAccount)) {
stopSelf();
return Service.START_NOT_STICKY; return Service.START_NOT_STICKY;
} }
if (oldAccount != currentAccount) { instance = this;
NotificationCenter.getInstance(oldAccount).removeObserver(this, NotificationCenter.fileUploadProgressChanged); MediaController.VideoConvertMessage videoConvertMessage = MediaController.getInstance().getCurrentForegroundConverMessage();
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileUploadProgressChanged);
}
boolean isGif = intent.getBooleanExtra("gif", false);
if (path == null) {
stopSelf();
return Service.START_NOT_STICKY;
}
if (BuildVars.LOGS_ENABLED) {
FileLog.d("start video service");
}
if (builder == null) { if (builder == null) {
NotificationsController.checkOtherNotificationsChannel(); NotificationsController.checkOtherNotificationsChannel();
builder = new NotificationCompat.Builder(ApplicationLoader.applicationContext); builder = new NotificationCompat.Builder(ApplicationLoader.applicationContext, NotificationsController.OTHER_NOTIFICATIONS_CHANNEL);
builder.setSmallIcon(android.R.drawable.stat_sys_upload); builder.setSmallIcon(android.R.drawable.stat_sys_upload);
builder.setWhen(System.currentTimeMillis()); builder.setWhen(System.currentTimeMillis());
builder.setChannelId(NotificationsController.OTHER_NOTIFICATIONS_CHANNEL); builder.setChannelId(NotificationsController.OTHER_NOTIFICATIONS_CHANNEL);
builder.setContentTitle(LocaleController.getString("AppName", R.string.AppName)); builder.setContentTitle(LocaleController.getString("AppName", R.string.AppName));
if (isGif) {
builder.setTicker(LocaleController.getString("SendingGif", R.string.SendingGif));
builder.setContentText(LocaleController.getString("SendingGif", R.string.SendingGif));
} else {
builder.setTicker(LocaleController.getString("SendingVideo", R.string.SendingVideo));
builder.setContentText(LocaleController.getString("SendingVideo", R.string.SendingVideo));
}
} }
currentProgress = 0;
builder.setProgress(100, currentProgress, true); setCurrentMessage(videoConvertMessage);
startForeground(4, builder.build()); try {
NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(4, builder.build()); startForeground(4, builder.build());
} catch (Throwable e) {
//ignore ForegroundServiceStartNotAllowedException
FileLog.e(e);
}
AndroidUtilities.runOnUIThread(() -> NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(4, builder.build()));
return Service.START_NOT_STICKY; return Service.START_NOT_STICKY;
} }
private void updateBuilderForMessage(MediaController.VideoConvertMessage videoConvertMessage) {
if (videoConvertMessage == null) {
return;
}
boolean isGif = videoConvertMessage.messageObject != null && MessageObject.isGifMessage(videoConvertMessage.messageObject.messageOwner);
if (isGif) {
builder.setTicker(LocaleController.getString("SendingGif", R.string.SendingGif));
builder.setContentText(LocaleController.getString("SendingGif", R.string.SendingGif));
} else {
builder.setTicker(LocaleController.getString("SendingVideo", R.string.SendingVideo));
builder.setContentText(LocaleController.getString("SendingVideo", R.string.SendingVideo));
}
int currentProgress = 0;
builder.setProgress(100, currentProgress, true);
}
private void setCurrentMessage(MediaController.VideoConvertMessage message) {
if (currentMessage == message) {
return;
}
if (currentMessage != null) {
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploadProgressChanged);
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploadFailed);
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploaded);
}
updateBuilderForMessage(message);
currentMessage = message;
currentAccount = message.currentAccount;
currentPath = message.messageObject.messageOwner.attachPath;
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileUploadProgressChanged);
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileUploadFailed);
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileUploaded);
if (isRunning()) {
NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(4, builder.build());
}
}
public static boolean isRunning() {
return instance != null;
}
} }

View file

@ -20,6 +20,7 @@ import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder; import android.media.MediaRecorder;
import android.os.Build; import android.os.Build;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
import android.util.Log; import android.util.Log;
@ -577,8 +578,8 @@ public class CameraController implements MediaRecorder.OnInfoListener {
} }
camera.setErrorCallback(getErrorListener(session)); camera.setErrorCallback(getErrorListener(session));
Camera.Parameters params = camera.getParameters(); Camera.Parameters params = camera.getParameters();
List<String> rawFlashModes = params.getSupportedFlashModes();
List<String> rawFlashModes = params.getSupportedFlashModes();
session.availableFlashModes.clear(); session.availableFlashModes.clear();
if (rawFlashModes != null) { if (rawFlashModes != null) {
for (int a = 0; a < rawFlashModes.size(); a++) { for (int a = 0; a < rawFlashModes.size(); a++) {
@ -587,7 +588,11 @@ public class CameraController implements MediaRecorder.OnInfoListener {
session.availableFlashModes.add(rawFlashMode); session.availableFlashModes.add(rawFlashMode);
} }
} }
session.checkFlashMode(session.availableFlashModes.get(0)); if (!TextUtils.equals(session.getCurrentFlashMode(), params.getFlashMode()) || !session.availableFlashModes.contains(session.getCurrentFlashMode())) {
session.checkFlashMode(session.availableFlashModes.get(0));
} else {
session.checkFlashMode(session.getCurrentFlashMode());
}
} }
if (prestartCallback != null) { if (prestartCallback != null) {

View file

@ -53,7 +53,7 @@ public class CameraSession {
private boolean isRound; private boolean isRound;
private boolean destroyed; private boolean destroyed;
protected ArrayList<String> availableFlashModes = new ArrayList<>(); public ArrayList<String> availableFlashModes = new ArrayList<>();
private int infoCameraId = -1; private int infoCameraId = -1;
Camera.CameraInfo info = new Camera.CameraInfo(); Camera.CameraInfo info = new Camera.CameraInfo();

View file

@ -99,7 +99,7 @@ import javax.microedition.khronos.opengles.GL;
@SuppressLint("NewApi") @SuppressLint("NewApi")
public class CameraView extends FrameLayout implements TextureView.SurfaceTextureListener, CameraController.ICameraView { public class CameraView extends FrameLayout implements TextureView.SurfaceTextureListener, CameraController.ICameraView {
public boolean WRITE_TO_FILE_IN_BACKGROUND = true; public boolean WRITE_TO_FILE_IN_BACKGROUND = false;
public boolean isStory; public boolean isStory;
private Size[] previewSize = new Size[2]; private Size[] previewSize = new Size[2];
@ -989,20 +989,19 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
private float takePictureProgress = 1f; private float takePictureProgress = 1f;
public void startTakePictureAnimation() { public void startTakePictureAnimation(boolean haptic) {
takePictureProgress = 0; takePictureProgress = 0;
invalidate(); invalidate();
runHaptic(); if (haptic) {
runHaptic();
}
} }
public void runHaptic() { public void runHaptic() {
long[] vibrationWaveFormDurationPattern = {0, 1}; long[] vibrationWaveFormDurationPattern = {0, 1};
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
final Vibrator vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); final Vibrator vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
VibrationEffect vibrationEffect = VibrationEffect.createWaveform(vibrationWaveFormDurationPattern, -1); VibrationEffect vibrationEffect = VibrationEffect.createWaveform(vibrationWaveFormDurationPattern, -1);
vibrator.cancel(); vibrator.cancel();
vibrator.vibrate(vibrationEffect); vibrator.vibrate(vibrationEffect);
} else { } else {
@ -2649,6 +2648,7 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
movie.setRotation(0); movie.setRotation(0);
movie.setSize(videoWidth, videoHeight); movie.setSize(videoWidth, videoHeight);
mediaMuxer = new MP4Builder().createMovie(movie, false, false); mediaMuxer = new MP4Builder().createMovie(movie, false, false);
mediaMuxer.setAllowSyncFiles(false);
} catch (Exception ioe) { } catch (Exception ioe) {
throw new RuntimeException(ioe); throw new RuntimeException(ioe);

View file

@ -19,13 +19,17 @@ import org.telegram.tgnet.SerializedData;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
public class BillingUtilities { public class BillingUtilities {
private static final String CURRENCY_FILE = "currencies.json"; private static final String CURRENCY_FILE = "currencies.json";
private static final String CURRENCY_EXP = "exp"; private static final String CURRENCY_EXP = "exp";
private static TLRPC.InputStorePaymentPurpose remPaymentPurpose;
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
public static void extractCurrencyExp(Map<String, Integer> currencyExpMap) { public static void extractCurrencyExp(Map<String, Integer> currencyExpMap) {
if (!currencyExpMap.isEmpty()) { if (!currencyExpMap.isEmpty()) {
@ -56,7 +60,12 @@ public class BillingUtilities {
paymentPurpose.serializeToStream(serializedData); paymentPurpose.serializeToStream(serializedData);
String obfuscatedData = Base64.encodeToString(serializedData.toByteArray(), Base64.DEFAULT); String obfuscatedData = Base64.encodeToString(serializedData.toByteArray(), Base64.DEFAULT);
serializedData.cleanup(); serializedData.cleanup();
if (paymentPurpose instanceof TLRPC.TL_inputStorePaymentPremiumGiftCode || paymentPurpose instanceof TLRPC.TL_inputStorePaymentPremiumGiveaway) {
remPaymentPurpose = paymentPurpose;
return Pair.create(obfuscatedAccountId, obfuscatedAccountId);
} else {
remPaymentPurpose = null;
}
return Pair.create(obfuscatedAccountId, obfuscatedData); return Pair.create(obfuscatedAccountId, obfuscatedData);
} }
@ -86,10 +95,16 @@ public class BillingUtilities {
} }
try { try {
byte[] obfuscatedDataBytes = Base64.decode(obfuscatedData, Base64.DEFAULT); TLRPC.InputStorePaymentPurpose purpose;
SerializedData data = new SerializedData(obfuscatedDataBytes); if (remPaymentPurpose == null) {
TLRPC.InputStorePaymentPurpose purpose = TLRPC.InputStorePaymentPurpose.TLdeserialize(data, data.readInt32(true), true); byte[] obfuscatedDataBytes = Base64.decode(obfuscatedData, Base64.DEFAULT);
data.cleanup(); SerializedData data = new SerializedData(obfuscatedDataBytes);
purpose = TLRPC.InputStorePaymentPurpose.TLdeserialize(data, data.readInt32(true), true);
data.cleanup();
} else {
purpose = remPaymentPurpose;
remPaymentPurpose = null;
}
byte[] obfuscatedAccountIdBytes = Base64.decode(obfuscatedAccountId, Base64.DEFAULT); byte[] obfuscatedAccountIdBytes = Base64.decode(obfuscatedAccountId, Base64.DEFAULT);
long accountId = Long.parseLong(new String(obfuscatedAccountIdBytes, Charsets.UTF_8)); long accountId = Long.parseLong(new String(obfuscatedAccountIdBytes, Charsets.UTF_8));

View file

@ -7,15 +7,18 @@ import android.text.Html;
import android.text.Spannable; import android.text.Spannable;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.StrikethroughSpan; import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.text.style.URLSpan; import android.text.style.URLSpan;
import android.text.style.UnderlineSpan; import android.text.style.UnderlineSpan;
import org.telegram.messenger.CodeHighlighting;
import org.telegram.messenger.FileLog; import org.telegram.messenger.FileLog;
import org.telegram.messenger.MediaDataController; import org.telegram.messenger.MediaDataController;
import org.telegram.tgnet.TLRPC; import org.telegram.tgnet.TLRPC;
import org.telegram.ui.Components.AnimatedEmojiSpan; import org.telegram.ui.Components.AnimatedEmojiSpan;
import org.telegram.ui.Components.QuoteSpan;
import org.telegram.ui.Components.URLSpanReplacement; import org.telegram.ui.Components.URLSpanReplacement;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler; import org.xml.sax.ContentHandler;
@ -30,6 +33,7 @@ public class CopyUtilities {
private final static int TYPE_SPOILER = 0; private final static int TYPE_SPOILER = 0;
private final static int TYPE_MONO = 1; private final static int TYPE_MONO = 1;
private final static int TYPE_QUOTE = 2;
public static Spannable fromHTML(String html) { public static Spannable fromHTML(String html) {
Spanned spanned; Spanned spanned;
@ -49,6 +53,8 @@ public class CopyUtilities {
Object[] spans = spanned.getSpans(0, spanned.length(), Object.class); Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
ArrayList<TLRPC.MessageEntity> entities = new ArrayList<>(spans.length); ArrayList<TLRPC.MessageEntity> entities = new ArrayList<>(spans.length);
ArrayList<ParsedSpan> codes = new ArrayList<>();
ArrayList<ParsedSpan> quotes = new ArrayList<>();
for (int i = 0; i < spans.length; ++i) { for (int i = 0; i < spans.length; ++i) {
Object span = spans[i]; Object span = spans[i];
int start = spanned.getSpanStart(span); int start = spanned.getSpanStart(span);
@ -70,7 +76,13 @@ public class CopyUtilities {
if (parsedSpan.type == TYPE_SPOILER) { if (parsedSpan.type == TYPE_SPOILER) {
entities.add(setEntityStartEnd(new TLRPC.TL_messageEntitySpoiler(), start, end)); entities.add(setEntityStartEnd(new TLRPC.TL_messageEntitySpoiler(), start, end));
} else if (parsedSpan.type == TYPE_MONO) { } else if (parsedSpan.type == TYPE_MONO) {
entities.add(setEntityStartEnd(new TLRPC.TL_messageEntityPre(), start, end)); if (!TextUtils.isEmpty(parsedSpan.lng)) {
codes.add(parsedSpan);
} else {
entities.add(setEntityStartEnd(new TLRPC.TL_messageEntityPre(), start, end));
}
} else if (parsedSpan.type == TYPE_QUOTE) {
quotes.add(parsedSpan);
} }
} else if (span instanceof AnimatedEmojiSpan) { } else if (span instanceof AnimatedEmojiSpan) {
TLRPC.TL_messageEntityCustomEmoji entity = new TLRPC.TL_messageEntityCustomEmoji(); TLRPC.TL_messageEntityCustomEmoji entity = new TLRPC.TL_messageEntityCustomEmoji();
@ -97,6 +109,25 @@ public class CopyUtilities {
} }
} }
MediaDataController.addAnimatedEmojiSpans(entities, spannable, null); MediaDataController.addAnimatedEmojiSpans(entities, spannable, null);
for (int i = 0; i < codes.size(); ++i) {
ParsedSpan span = codes.get(i);
final int start = spanned.getSpanStart(span);
final int end = spanned.getSpanEnd(span);
spannable.setSpan(new CodeHighlighting.Span(true, 0, null, span.lng, spannable.subSequence(start, end).toString()), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// CodeHighlighting.highlight(
// spannable,
// spanned.getSpanStart(span),
// spanned.getSpanEnd(span),
// span.lng,
// 0,
// null,
// false
// );
}
for (int i = 0; i < quotes.size(); ++i) {
ParsedSpan span = quotes.get(i);
QuoteSpan.putQuote(spannable, spanned.getSpanStart(span), spanned.getSpanEnd(span));
}
return spannable; return spannable;
} }
@ -242,7 +273,8 @@ public class CopyUtilities {
} }
} else if (tag.equals("pre")) { } else if (tag.equals("pre")) {
if (opening) { if (opening) {
output.setSpan(new ParsedSpan(TYPE_MONO), output.length(), output.length(), Spanned.SPAN_MARK_MARK); String lang = HTMLTagAttributesHandler.getValue(attributes, "lang");
output.setSpan(new ParsedSpan(TYPE_MONO, lang), output.length(), output.length(), Spanned.SPAN_MARK_MARK);
return true; return true;
} else { } else {
ParsedSpan obj = getLast(output, ParsedSpan.class, TYPE_MONO); ParsedSpan obj = getLast(output, ParsedSpan.class, TYPE_MONO);
@ -255,6 +287,21 @@ public class CopyUtilities {
return true; return true;
} }
} }
} else if (tag.equals("blockquote")) {
if (opening) {
output.setSpan(new ParsedSpan(TYPE_QUOTE), output.length(), output.length(), Spanned.SPAN_MARK_MARK);
return true;
} else {
ParsedSpan obj = getLast(output, ParsedSpan.class, TYPE_QUOTE);
if (obj != null) {
int where = output.getSpanStart(obj);
output.removeSpan(obj);
if (where != output.length()) {
output.setSpan(obj, where, output.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return true;
}
}
} }
return false; return false;
} }
@ -290,9 +337,15 @@ public class CopyUtilities {
private static class ParsedSpan { private static class ParsedSpan {
final int type; final int type;
final String lng;
private ParsedSpan(int type) { private ParsedSpan(int type) {
this.type = type; this.type = type;
this.lng = null;
}
private ParsedSpan(int type, String lng) {
this.type = type;
this.lng = lng;
} }
} }
} }

View file

@ -2,14 +2,15 @@ package org.telegram.messenger.utils;
import android.text.Spanned; import android.text.Spanned;
import org.telegram.messenger.CodeHighlighting;
import org.telegram.ui.Components.AnimatedEmojiSpan; import org.telegram.ui.Components.AnimatedEmojiSpan;
import org.telegram.ui.Components.QuoteSpan;
import org.telegram.ui.Components.TextStyleSpan; import org.telegram.ui.Components.TextStyleSpan;
import org.telegram.ui.Components.URLSpanMono; import org.telegram.ui.Components.URLSpanMono;
import org.telegram.ui.Components.URLSpanReplacement; import org.telegram.ui.Components.URLSpanReplacement;
public class CustomHtml { public class CustomHtml {
private CustomHtml() { } private CustomHtml() { }
public static String toHtml(Spanned text) { public static String toHtml(Spanned text) {
@ -136,7 +137,8 @@ public class CustomHtml {
} }
} }
toHTML_4_wrapAnimatedEmoji(out, text, i, next); toHTML_4_wrapMonoscape2(out, text, i, next);
if (spans != null) { if (spans != null) {
for (int j = 0; j < spans.length; ++j) { for (int j = 0; j < spans.length; ++j) {
URLSpanMono span = spans[j]; URLSpanMono span = spans[j];
@ -146,11 +148,67 @@ public class CustomHtml {
} }
} }
} }
} }
private static void toHTML_4_wrapAnimatedEmoji(StringBuilder out, Spanned text, int start, int end) { private static void toHTML_4_wrapMonoscape2(StringBuilder out, Spanned text, int start, int end) {
int next;
for (int i = start; i < end; i = next) {
next = text.nextSpanTransition(i, end, CodeHighlighting.Span.class);
if (next < 0) {
next = end;
}
CodeHighlighting.Span[] spans = text.getSpans(i, next, CodeHighlighting.Span.class);
if (spans != null) {
for (int j = 0; j < spans.length; ++j) {
CodeHighlighting.Span span = spans[j];
if (span != null) {
out.append("<pre lang=\"").append(span.lng).append("\">");
}
}
}
toHTML_5_wrapQuote(out, text, i, next);
if (spans != null) {
for (int j = 0; j < spans.length; ++j) {
CodeHighlighting.Span span = spans[j];
if (span != null) {
out.append("</pre>");
}
}
}
}
}
private static void toHTML_5_wrapQuote(StringBuilder out, Spanned text, int start, int end) {
int next;
for (int i = start; i < end; i = next) {
next = text.nextSpanTransition(i, end, QuoteSpan.class);
if (next < 0) {
next = end;
}
QuoteSpan[] spans = text.getSpans(i, next, QuoteSpan.class);
if (spans != null) {
for (int j = 0; j < spans.length; ++j) {
out.append("<blockquote>");
}
}
toHTML_6_wrapAnimatedEmoji(out, text, i, next);
if (spans != null) {
for (int j = 0; j < spans.length; ++j) {
out.append("</blockquote>");
}
}
}
}
private static void toHTML_6_wrapAnimatedEmoji(StringBuilder out, Spanned text, int start, int end) {
int next; int next;
for (int i = start; i < end; i = next) { for (int i = start; i < end; i = next) {
next = text.nextSpanTransition(i, end, AnimatedEmojiSpan.class); next = text.nextSpanTransition(i, end, AnimatedEmojiSpan.class);
@ -168,7 +226,7 @@ public class CustomHtml {
} }
} }
toHTML_5_withinStyle(out, text, i, next); toHTML_7_withinStyle(out, text, i, next);
if (spans != null) { if (spans != null) {
for (int j = 0; j < spans.length; ++j) { for (int j = 0; j < spans.length; ++j) {
@ -181,7 +239,7 @@ public class CustomHtml {
} }
} }
private static void toHTML_5_withinStyle(StringBuilder out, CharSequence text, int start, int end) { private static void toHTML_7_withinStyle(StringBuilder out, CharSequence text, int start, int end) {
for (int i = start; i < end; i++) { for (int i = start; i < end; i++) {
char c = text.charAt(i); char c = text.charAt(i);

View file

@ -211,7 +211,7 @@ public class PhotoUtilities {
File src = FileLoader.getInstance(chatActivity.getCurrentAccount()).getPathToAttach(avatarBig[0], true); File src = FileLoader.getInstance(chatActivity.getCurrentAccount()).getPathToAttach(avatarBig[0], true);
src.renameTo(destFile); src.renameTo(destFile);
} }
chatActivity.getMessagesStorage().addDialogPhoto(user.id, ((TLRPC.TL_photos_photo) response).photo); chatActivity.getMessagesController().getDialogPhotos(user.id).addPhotoAtStart(((TLRPC.TL_photos_photo) response).photo);
ArrayList<TLRPC.User> users = new ArrayList<>(); ArrayList<TLRPC.User> users = new ArrayList<>();
users.add(user); users.add(user);
chatActivity.getMessagesStorage().putUsersAndChats(users, null, false, true); chatActivity.getMessagesStorage().putUsersAndChats(users, null, false, true);

View file

@ -62,6 +62,7 @@ public class MP4Builder {
private ByteBuffer sizeBuffer = null; private ByteBuffer sizeBuffer = null;
private boolean splitMdat; private boolean splitMdat;
private boolean wasFirstVideoFrame; private boolean wasFirstVideoFrame;
private boolean allowSyncFiles = true;
public MP4Builder createMovie(Mp4Movie mp4Movie, boolean split, boolean hevc) throws Exception { public MP4Builder createMovie(Mp4Movie mp4Movie, boolean split, boolean hevc) throws Exception {
currentMp4Movie = mp4Movie; currentMp4Movie = mp4Movie;
@ -90,7 +91,9 @@ public class MP4Builder {
mdat.setDataOffset(0); mdat.setDataOffset(0);
mdat.setContentSize(0); mdat.setContentSize(0);
fos.flush(); fos.flush();
fos.getFD().sync(); if (allowSyncFiles) {
fos.getFD().sync();
}
} }
public long writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo, boolean writeLength) throws Exception { public long writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo, boolean writeLength) throws Exception {
@ -163,7 +166,9 @@ public class MP4Builder {
if (flush) { if (flush) {
fos.flush(); fos.flush();
fos.getFD().sync(); if (allowSyncFiles) {
fos.getFD().sync();
}
return fc.position(); return fc.position();
} }
return 0; return 0;
@ -194,7 +199,9 @@ public class MP4Builder {
Box moov = createMovieBox(currentMp4Movie); Box moov = createMovieBox(currentMp4Movie);
moov.getBox(fc); moov.getBox(fc);
fos.flush(); fos.flush();
fos.getFD().sync(); if (allowSyncFiles) {
fos.getFD().sync();
}
fc.close(); fc.close();
fos.close(); fos.close();
@ -209,6 +216,10 @@ public class MP4Builder {
return new FileTypeBox("isom", 512, minorBrands); return new FileTypeBox("isom", 512, minorBrands);
} }
public void setAllowSyncFiles(boolean allowSyncFiles) {
this.allowSyncFiles = allowSyncFiles;
}
private static class InterleaveChunkMdat implements Box { private static class InterleaveChunkMdat implements Box {
private Container parent; private Container parent;
private long contentSize = 1024 * 1024 * 1024; private long contentSize = 1024 * 1024 * 1024;

View file

@ -6,6 +6,7 @@ import android.graphics.Paint;
import android.graphics.SurfaceTexture; import android.graphics.SurfaceTexture;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.util.Log;
import android.view.SurfaceView; import android.view.SurfaceView;
import android.view.TextureView; import android.view.TextureView;
@ -355,10 +356,14 @@ public class VideoPlayerHolderBase {
} else { } else {
localProgress = currentPosition / (float) playerDuration; localProgress = currentPosition / (float) playerDuration;
} }
if (localProgress < progress) { // if (localProgress < progress) {
return progress; // return progress;
} // }
progress = localProgress; progress = localProgress;
if (!seeking) {
currentSeek = progress;
lastSeek = currentPosition;
}
} }
return progress; return progress;
} }
@ -434,4 +439,63 @@ public class VideoPlayerHolderBase {
}); });
} }
private Runnable onSeekUpdate;
public void setOnSeekUpdate(Runnable onSeekUpdate) {
this.onSeekUpdate = onSeekUpdate;
}
private volatile boolean firstSeek = true;
private volatile long lastSeek = -1;
private long lastBetterSeek = -1;
public float currentSeek = 0;
public volatile float currentSeekThread = 0;
private volatile long duration;
private final Runnable betterSeek = () -> {
if (videoPlayer != null) {
// videoPlayer.seekTo(lastBetterSeek, false);
}
};
private final Runnable updateSeek = () -> {
if (videoPlayer == null) {
return;
}
long position = (long) (currentSeekThread * duration);
if (lastSeek <= -1) {
lastSeek = position;
}
if (Math.abs(position - lastSeek) >= (firstSeek ? 350 : 40)) {
firstSeek = false;
lastBetterSeek = position;
dispatchQueue.cancelRunnable(betterSeek);
dispatchQueue.postRunnable(betterSeek, 300);
videoPlayer.seekTo(lastSeek = position, true);
}
};
private volatile boolean seeking;
public void setSeeking(boolean seeking) {
if (seeking && !this.seeking) {
firstSeek = true;
}
this.seeking = seeking;
if (!seeking) {
dispatchQueue.cancelRunnable(betterSeek);
}
}
public float seek(float delta, final long duration) {
if (videoPlayer == null) {
return currentSeek;
}
this.duration = duration;
currentSeek = Utilities.clamp(currentSeek + delta, 1, 0);
currentSeekThread = currentSeek;
dispatchQueue.cancelRunnable(updateSeek);
dispatchQueue.postRunnable(updateSeek);
return currentSeek;
}
} }

View file

@ -77,6 +77,7 @@ import android.telephony.TelephonyManager;
import android.text.SpannableString; import android.text.SpannableString;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.style.ForegroundColorSpan; import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.util.LruCache; import android.util.LruCache;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
@ -2918,7 +2919,8 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
builder.setSmallIcon(isMicMute() ? R.drawable.voicechat_muted : R.drawable.voicechat_active); builder.setSmallIcon(isMicMute() ? R.drawable.voicechat_muted : R.drawable.voicechat_active);
} else { } else {
builder.setContentTitle(LocaleController.getString("VoipOutgoingCall", R.string.VoipOutgoingCall)); builder.setContentTitle(LocaleController.getString("VoipOutgoingCall", R.string.VoipOutgoingCall));
builder.setSmallIcon(R.drawable.notification); builder.setSmallIcon(R.drawable.ic_call);
builder.setOngoing(true);
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
Intent endIntent = new Intent(this, VoIPActionsReceiver.class); Intent endIntent = new Intent(this, VoIPActionsReceiver.class);
@ -3593,7 +3595,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
if (groupCall != null) { if (groupCall != null) {
bldr.setSmallIcon(isMicMute() ? R.drawable.voicechat_muted : R.drawable.voicechat_active); bldr.setSmallIcon(isMicMute() ? R.drawable.voicechat_muted : R.drawable.voicechat_active);
} else { } else {
bldr.setSmallIcon(R.drawable.notification); bldr.setSmallIcon(R.drawable.ic_call);
} }
startForeground(ID_ONGOING_CALL_NOTIFICATION, bldr.build()); startForeground(ID_ONGOING_CALL_NOTIFICATION, bldr.build());
} }
@ -4035,10 +4037,9 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
Notification.Builder builder = new Notification.Builder(this) Notification.Builder builder = new Notification.Builder(this)
.setContentTitle(video ? LocaleController.getString("VoipInVideoCallBranding", R.string.VoipInVideoCallBranding) : LocaleController.getString("VoipInCallBranding", R.string.VoipInCallBranding)) .setContentTitle(video ? LocaleController.getString("VoipInVideoCallBranding", R.string.VoipInVideoCallBranding) : LocaleController.getString("VoipInCallBranding", R.string.VoipInCallBranding))
.setSmallIcon(R.drawable.notification) .setSmallIcon(R.drawable.ic_call)
.setContentIntent(PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE)); .setContentIntent(PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE));
Uri soundProviderUri = Uri.parse("content://" + ApplicationLoader.getApplicationId() + ".call_sound_provider/start_ringing"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (Build.VERSION.SDK_INT >= 26) {
SharedPreferences nprefs = MessagesController.getGlobalNotificationsSettings(); SharedPreferences nprefs = MessagesController.getGlobalNotificationsSettings();
int chanIndex = nprefs.getInt("calls_notification_channel", 0); int chanIndex = nprefs.getInt("calls_notification_channel", 0);
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
@ -4046,14 +4047,18 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
if (oldChannel != null) { if (oldChannel != null) {
nm.deleteNotificationChannel(oldChannel.getId()); nm.deleteNotificationChannel(oldChannel.getId());
} }
NotificationChannel existingChannel = nm.getNotificationChannel("incoming_calls3" + chanIndex); oldChannel = nm.getNotificationChannel("incoming_calls3" + chanIndex);
if (oldChannel != null) {
nm.deleteNotificationChannel(oldChannel.getId());
}
NotificationChannel existingChannel = nm.getNotificationChannel("incoming_calls4" + chanIndex);
boolean needCreate = true; boolean needCreate = true;
if (existingChannel != null) { if (existingChannel != null) {
if (existingChannel.getImportance() < NotificationManager.IMPORTANCE_HIGH || !soundProviderUri.equals(existingChannel.getSound()) || existingChannel.getVibrationPattern() != null || existingChannel.shouldVibrate()) { if (existingChannel.getImportance() < NotificationManager.IMPORTANCE_HIGH || existingChannel.getSound() != null) {
if (BuildVars.LOGS_ENABLED) { if (BuildVars.LOGS_ENABLED) {
FileLog.d("User messed up the notification channel; deleting it and creating a proper one"); FileLog.d("User messed up the notification channel; deleting it and creating a proper one");
} }
nm.deleteNotificationChannel("incoming_calls3" + chanIndex); nm.deleteNotificationChannel("incoming_calls4" + chanIndex);
chanIndex++; chanIndex++;
nprefs.edit().putInt("calls_notification_channel", chanIndex).commit(); nprefs.edit().putInt("calls_notification_channel", chanIndex).commit();
} else { } else {
@ -4066,8 +4071,13 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
.setLegacyStreamType(AudioManager.STREAM_RING) .setLegacyStreamType(AudioManager.STREAM_RING)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
.build(); .build();
NotificationChannel chan = new NotificationChannel("incoming_calls3" + chanIndex, LocaleController.getString("IncomingCalls", R.string.IncomingCalls), NotificationManager.IMPORTANCE_HIGH); NotificationChannel chan = new NotificationChannel("incoming_calls4" + chanIndex, LocaleController.getString("IncomingCallsSystemSetting", R.string.IncomingCallsSystemSetting), NotificationManager.IMPORTANCE_HIGH);
chan.setSound(soundProviderUri, attrs); try {
chan.setSound(null, attrs);
} catch (Exception e) {
FileLog.e(e);
}
chan.setDescription(LocaleController.getString("IncomingCallsSystemSettingDescription", R.string.IncomingCallsSystemSettingDescription));
chan.enableVibration(false); chan.enableVibration(false);
chan.enableLights(false); chan.enableLights(false);
chan.setBypassDnd(true); chan.setBypassDnd(true);
@ -4079,9 +4089,9 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
return; return;
} }
} }
builder.setChannelId("incoming_calls3" + chanIndex); builder.setChannelId("incoming_calls4" + chanIndex);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
builder.setSound(soundProviderUri, AudioManager.STREAM_RING); builder.setSound(null);
} }
Intent endIntent = new Intent(this, VoIPActionsReceiver.class); Intent endIntent = new Intent(this, VoIPActionsReceiver.class);
endIntent.setAction(getPackageName() + ".DECLINE_CALL"); endIntent.setAction(getPackageName() + ".DECLINE_CALL");
@ -4120,13 +4130,13 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
Notification incomingNotification; Notification incomingNotification;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
Bitmap avatar = getRoundAvatarBitmap(userOrChat); Bitmap avatar = getRoundAvatarBitmap(userOrChat);
String presonName = ContactsController.formatName(userOrChat); String personName = ContactsController.formatName(userOrChat);
if (TextUtils.isEmpty(presonName)) { if (TextUtils.isEmpty(personName)) {
//java.lang.IllegalArgumentException: person must have a non-empty a name //java.lang.IllegalArgumentException: person must have a non-empty a name
presonName = "___"; personName = "___";
} }
Person person = new Person.Builder() Person person = new Person.Builder()
.setName(presonName) .setName(personName)
.setIcon(Icon.createWithAdaptiveBitmap(avatar)).build(); .setIcon(Icon.createWithAdaptiveBitmap(avatar)).build();
Notification.CallStyle notificationStyle = Notification.CallStyle.forIncomingCall(person, endPendingIntent, answerPendingIntent); Notification.CallStyle notificationStyle = Notification.CallStyle.forIncomingCall(person, endPendingIntent, answerPendingIntent);

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