mirror of
https://github.com/DrKLO/Telegram.git
synced 2024-12-21 22:15:16 +01:00
update to 10.2.0 (4056)
This commit is contained in:
parent
fea5ca949a
commit
b4dbcd1639
469 changed files with 40273 additions and 12680 deletions
|
@ -435,7 +435,7 @@ target_include_directories(breakpad PUBLIC
|
|||
#voip
|
||||
include(${CMAKE_HOME_DIRECTORY}/voip/CMakeLists.txt)
|
||||
|
||||
set(NATIVE_LIB "tmessages.46")
|
||||
set(NATIVE_LIB "tmessages.47")
|
||||
|
||||
#tmessages
|
||||
add_library(${NATIVE_LIB} SHARED
|
||||
|
|
|
@ -202,6 +202,10 @@ void setUserId(JNIEnv *env, jclass c, jint instanceNum, int64_t 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) {
|
||||
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 *systemVersionStr = env->GetStringUTFChars(systemVersion, 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 *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) {
|
||||
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_getConnectionState", "(I)I", (void *) getConnectionState},
|
||||
{"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_setRegId", "(ILjava/lang/String;)V", (void *) setRegId},
|
||||
{"native_setSystemLangCode", "(ILjava/lang/String;)V", (void *) setSystemLangCode},
|
||||
|
|
|
@ -483,19 +483,19 @@ void TL_user::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &er
|
|||
lang_code = stream->readString(&error);
|
||||
}
|
||||
if ((flags & 1073741824) != 0) {
|
||||
uint32_t magic = stream->readUint32(&error);
|
||||
if (magic == 0x2de11aae) {
|
||||
emojiStatusMagic = stream->readUint32(&error);
|
||||
if (emojiStatusMagic == 0x2de11aae) {
|
||||
// emojiStatusEmpty
|
||||
} else if (magic == 0x929b619d) {
|
||||
} else if (emojiStatusMagic == 0x929b619d) {
|
||||
// emojiStatus
|
||||
int64_t document_id = stream->readInt64(&error);
|
||||
} else if (magic == 0xfa30a8c7) {
|
||||
emojiStatusDocumentId = stream->readInt64(&error);
|
||||
} else if (emojiStatusMagic == 0xfa30a8c7) {
|
||||
// emojiStatusUntil
|
||||
int64_t document_id = stream->readInt64(&error);
|
||||
int until = stream->readInt32(&error);
|
||||
emojiStatusDocumentId = stream->readInt64(&error);
|
||||
emojiStatusUntil = stream->readInt32(&error);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -518,6 +518,12 @@ void TL_user::readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &er
|
|||
if ((flags2 & 32) != 0) {
|
||||
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) {
|
||||
|
@ -563,6 +569,34 @@ void TL_user::serializeToStream(NativeByteBuffer *stream) {
|
|||
if ((flags & 4194304) != 0) {
|
||||
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) {
|
||||
|
|
|
@ -336,6 +336,11 @@ public:
|
|||
std::string lang_code;
|
||||
std::vector<std::unique_ptr<TL_username>> usernames;
|
||||
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);
|
||||
};
|
||||
|
@ -352,7 +357,7 @@ public:
|
|||
class TL_user : public User {
|
||||
|
||||
public:
|
||||
static const uint32_t constructor = 0xabb5f120;
|
||||
static const uint32_t constructor = 0xeb602f25;
|
||||
|
||||
void readParams(NativeByteBuffer *stream, int32_t instanceNum, bool &error);
|
||||
void serializeToStream(NativeByteBuffer *stream);
|
||||
|
|
|
@ -666,6 +666,7 @@ void ConnectionsManager::cleanUp(bool resetKeys, int32_t datacenterId) {
|
|||
if (datacenterId == -1) {
|
||||
sessionsToDestroy.clear();
|
||||
currentUserId = 0;
|
||||
currentUserPremium = false;
|
||||
registeredForInternalPush = false;
|
||||
}
|
||||
saveConfig();
|
||||
|
@ -1389,6 +1390,7 @@ void ConnectionsManager::processServerResponse(TLObject *message, int64_t messag
|
|||
} else if (datacenter->getDatacenterId() == currentDatacenterId || datacenter->getDatacenterId() == movingToDatacenterId) {
|
||||
if (request->connectionType & ConnectionTypeGeneric && currentUserId) {
|
||||
currentUserId = 0;
|
||||
currentUserPremium = false;
|
||||
if (delegate != nullptr) {
|
||||
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) {
|
||||
scheduleTask([&, userId] {
|
||||
int32_t oldUserId = currentUserId;
|
||||
|
@ -1989,7 +1997,7 @@ bool ConnectionsManager::cancelRequestInternal(int32_t token, int64_t messageId,
|
|||
if (notifyServer) {
|
||||
auto dropAnswer = new TL_rpc_drop_answer();
|
||||
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;
|
||||
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();
|
||||
unauthorizedDatacenters.clear();
|
||||
downloadRunningRequestCount.clear();
|
||||
downloadCancelRunningRequestCount.clear();
|
||||
|
||||
int64_t currentTimeMillis = getCurrentTimeMonotonicMillis();
|
||||
auto currentTime = (int32_t) (currentTimeMillis / 1000);
|
||||
|
@ -2250,14 +2259,15 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
|||
genericRunningRequestCount++;
|
||||
break;
|
||||
case ConnectionTypeDownload: {
|
||||
auto map = request->isCancelRequest() ? downloadCancelRunningRequestCount : downloadRunningRequestCount;
|
||||
uint32_t currentCount;
|
||||
auto dcIter = downloadRunningRequestCount.find(datacenterId);
|
||||
if (dcIter != downloadRunningRequestCount.end()) {
|
||||
auto dcIter = map.find(datacenterId);
|
||||
if (dcIter != map.end()) {
|
||||
currentCount = dcIter->second;
|
||||
} else {
|
||||
currentCount = 0;
|
||||
}
|
||||
downloadRunningRequestCount[datacenterId] = currentCount + 1;
|
||||
map[datacenterId] = currentCount + 1;
|
||||
break;
|
||||
}
|
||||
case ConnectionTypeUpload:
|
||||
|
@ -2473,6 +2483,8 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
|||
Request *request = iter->get();
|
||||
if (request->cancelled) {
|
||||
iter = requestsQueue.erase(iter);
|
||||
if (LOGS_ENABLED)
|
||||
DEBUG_D("skip queue, token = %d: cancelled", request->requestToken);
|
||||
continue;
|
||||
}
|
||||
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 (movingToDatacenterId != DEFAULT_DATACENTER_ID) {
|
||||
iter++;
|
||||
if (LOGS_ENABLED)
|
||||
DEBUG_D("skip queue, token = %d: moving dc", request->requestToken);
|
||||
continue;
|
||||
}
|
||||
datacenterId = currentDatacenterId;
|
||||
|
@ -2535,6 +2549,8 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
|||
if (std::find(unknownDatacenterIds.begin(), unknownDatacenterIds.end(), datacenterId) == unknownDatacenterIds.end()) {
|
||||
unknownDatacenterIds.push_back(datacenterId);
|
||||
}
|
||||
if (LOGS_ENABLED)
|
||||
DEBUG_D("skip queue, token = %d: unknown dc", request->requestToken);
|
||||
iter++;
|
||||
continue;
|
||||
} else {
|
||||
|
@ -2548,12 +2564,16 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
|||
if (std::find(neededDatacenters.begin(), neededDatacenters.end(), pair) == neededDatacenters.end()) {
|
||||
neededDatacenters.push_back(pair);
|
||||
}
|
||||
if (LOGS_ENABLED)
|
||||
DEBUG_D("skip queue, token = %d: no authkey for dc", request->requestToken);
|
||||
iter++;
|
||||
continue;
|
||||
} 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()) {
|
||||
unauthorizedDatacenters.push_back(requestDatacenter);
|
||||
}
|
||||
if (LOGS_ENABLED)
|
||||
DEBUG_D("skip queue, token = %d: dc is unauthorized", request->requestToken);
|
||||
iter++;
|
||||
continue;
|
||||
}
|
||||
|
@ -2563,6 +2583,8 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
|||
|
||||
if (request->connectionType & ConnectionTypeGeneric && connection->getConnectionToken() == 0) {
|
||||
iter++;
|
||||
if (LOGS_ENABLED)
|
||||
DEBUG_D("skip queue, token = %d: generic && connectionToken == 0", request->requestToken);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2571,20 +2593,38 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
|||
case ConnectionTypeGenericMedia:
|
||||
if (!canUseUnboundKey && genericRunningRequestCount >= 60) {
|
||||
iter++;
|
||||
DEBUG_D("skip queue, token = %d: generic type: running generic requests >= 60", request->requestToken);
|
||||
continue;
|
||||
}
|
||||
genericRunningRequestCount++;
|
||||
break;
|
||||
case ConnectionTypeDownload: {
|
||||
uint32_t currentCount;
|
||||
auto dcIter = downloadRunningRequestCount.find(datacenterId);
|
||||
if (dcIter != downloadRunningRequestCount.end()) {
|
||||
auto map = request->isCancelRequest() ? downloadCancelRunningRequestCount : downloadRunningRequestCount;
|
||||
auto dcIter = map.find(datacenterId);
|
||||
if (dcIter != map.end()) {
|
||||
currentCount = dcIter->second;
|
||||
} else {
|
||||
currentCount = 0;
|
||||
}
|
||||
if (!networkAvailable || currentCount >= 16) {
|
||||
if (!networkAvailable) {
|
||||
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;
|
||||
}
|
||||
downloadRunningRequestCount[datacenterId] = currentCount + 1;
|
||||
|
@ -2594,12 +2634,22 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
|||
case ConnectionTypeTemp:
|
||||
if (!networkAvailable) {
|
||||
iter++;
|
||||
if (LOGS_ENABLED)
|
||||
DEBUG_D("skip queue, token = %d: proxy/temp type: network unavailable", request->requestToken);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case ConnectionTypeUpload:
|
||||
if (!networkAvailable || uploadRunningRequestCount >= 10) {
|
||||
if (!networkAvailable) {
|
||||
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;
|
||||
}
|
||||
uploadRunningRequestCount++;
|
||||
|
@ -3057,14 +3107,13 @@ void ConnectionsManager::updateDcSettings(uint32_t dcNum, bool workaround, bool
|
|||
if ((!workaround && !updatingDcSettings) || (workaround && !updatingDcSettingsWorkaround)) {
|
||||
return;
|
||||
}
|
||||
if (!workaround && updatingDcSettingsAgain && updatingDcSettingsAgainDcNum == dcNum) {
|
||||
if (!workaround && updatingDcSettingsAgain) {
|
||||
updatingDcSettings = false;
|
||||
updatingDcSettingsAgain = false;
|
||||
for (auto & datacenter : datacenters) {
|
||||
if (datacenter.first == dcNum) {
|
||||
datacenter.second->resetInitVersion();
|
||||
}
|
||||
datacenter.second->resetInitVersion();
|
||||
}
|
||||
updateDcSettings(updatingDcSettingsAgainDcNum, false, false);
|
||||
updateDcSettings(0, false, false);
|
||||
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;
|
||||
currentLayer = layer;
|
||||
currentApiId = apiId;
|
||||
|
@ -3378,6 +3427,7 @@ void ConnectionsManager::init(uint32_t version, int32_t layer, int32_t apiId, st
|
|||
currentDeviceTimezone = timezoneOffset;
|
||||
currentSystemLangCode = systemLangCode;
|
||||
currentUserId = userId;
|
||||
currentUserPremium = userPremium;
|
||||
currentLogPath = logPath;
|
||||
pushConnectionEnabled = enablePushConnection;
|
||||
currentNetworkType = networkType;
|
||||
|
@ -3675,9 +3725,8 @@ void ConnectionsManager::reconnect(int32_t dcId, int32_t connectionType) {
|
|||
Connection *connection = datacenter->getConnectionByType(connectionType, false,
|
||||
0);
|
||||
if (connection != nullptr) {
|
||||
if (LOGS_ENABLED)
|
||||
DEBUG_D("discard connection dcId=%d connectionType=%d", dcId,
|
||||
connectionType);
|
||||
DEBUG_D("discard connection dcId=%d connectionType=%d", dcId,
|
||||
connectionType);
|
||||
connection->suspendConnection(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,12 +59,13 @@ public:
|
|||
void setDelegate(ConnectiosManagerDelegate *connectiosManagerDelegate);
|
||||
ConnectionState getConnectionState();
|
||||
void setUserId(int64_t userId);
|
||||
void setUserPremium(bool premium);
|
||||
void switchBackend(bool restart);
|
||||
void resumeNetwork(bool partial);
|
||||
void pauseNetwork();
|
||||
void setNetworkAvailable(bool value, int32_t type, bool slow);
|
||||
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 setLangCode(std::string langCode);
|
||||
void setRegId(std::string regId);
|
||||
|
@ -231,6 +232,7 @@ private:
|
|||
std::string currentConfigPath;
|
||||
std::string currentLogPath;
|
||||
int64_t currentUserId = 0;
|
||||
bool currentUserPremium = false;
|
||||
bool registeredForInternalPush = false;
|
||||
bool pushConnectionEnabled = true;
|
||||
int32_t currentPerformanceClass = -1;
|
||||
|
@ -241,6 +243,7 @@ private:
|
|||
std::vector<uint32_t> unknownDatacenterIds;
|
||||
std::vector<std::pair<Datacenter *, ConnectionType>> neededDatacenters;
|
||||
std::map<uint32_t, uint32_t> downloadRunningRequestCount;
|
||||
std::map<uint32_t, uint32_t> downloadCancelRunningRequestCount;
|
||||
std::vector<Datacenter *> unauthorizedDatacenters;
|
||||
NativeByteBuffer *sizeCalculator;
|
||||
|
||||
|
|
|
@ -170,7 +170,8 @@ enum RequestFlag {
|
|||
RequestFlagNeedQuickAck = 128,
|
||||
RequestFlagUseUnboundKey = 256,
|
||||
RequestFlagResendAfter = 512,
|
||||
RequestFlagIgnoreFloodWait = 1024
|
||||
RequestFlagIgnoreFloodWait = 1024,
|
||||
RequestFlagIsCancel = 32768
|
||||
};
|
||||
|
||||
inline std::string to_string_int32(int32_t value) {
|
||||
|
|
|
@ -85,6 +85,10 @@ bool Request::isMediaRequest() {
|
|||
return Connection::isMediaConnectionType(connectionType);
|
||||
}
|
||||
|
||||
bool Request::isCancelRequest() {
|
||||
return (requestFlags & RequestFlagIsCancel) != 0;
|
||||
}
|
||||
|
||||
bool Request::needInitRequest(Datacenter *datacenter, uint32_t currentVersion) {
|
||||
bool media = PFS_ENABLED && datacenter != nullptr && isMediaRequest() && datacenter->hasMediaAddress();
|
||||
return !media && datacenter->lastInitVersion != currentVersion || media && datacenter->lastInitMediaVersion != currentVersion;
|
||||
|
|
|
@ -63,6 +63,7 @@ public:
|
|||
void onQuickAck();
|
||||
void onWriteToSocket();
|
||||
bool isMediaRequest();
|
||||
bool isCancelRequest();
|
||||
bool hasInitFlag();
|
||||
bool needInitRequest(Datacenter *datacenter, uint32_t currentVersion);
|
||||
TLObject *getRpcRequest();
|
||||
|
|
21
TMessagesProj/jni/voip/tgcalls/DirectConnectionChannel.h
Normal file
21
TMessagesProj/jni/voip/tgcalls/DirectConnectionChannel.h
Normal 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
|
|
@ -8,6 +8,7 @@
|
|||
#include <map>
|
||||
|
||||
#include "Stats.h"
|
||||
#include "DirectConnectionChannel.h"
|
||||
|
||||
namespace rtc {
|
||||
template <typename VideoFrameT>
|
||||
|
@ -186,7 +187,7 @@ public:
|
|||
virtual void setEchoCancellationStrength(int strength) = 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 setAudioOutputDevice(std::string id) = 0;
|
||||
|
@ -237,6 +238,7 @@ struct Descriptor {
|
|||
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> createAudioDeviceModule;
|
||||
std::string initialInputDeviceId;
|
||||
std::string initialOutputDeviceId;
|
||||
std::shared_ptr<DirectConnectionChannel> directConnectionChannel;
|
||||
|
||||
std::shared_ptr<PlatformContext> platformContext;
|
||||
};
|
||||
|
|
|
@ -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->setIncomingVideoOutput(sink);
|
||||
});
|
||||
|
@ -166,6 +166,8 @@ PersistentState InstanceImpl::getPersistentState() {
|
|||
}
|
||||
|
||||
void InstanceImpl::stop(std::function<void(FinalState)> completion) {
|
||||
RTC_LOG(LS_INFO) << "Stopping InstanceImpl";
|
||||
|
||||
std::string debugLog = _logSink->result();
|
||||
|
||||
_manager->perform([completion, debugLog = std::move(debugLog)](Manager *manager) {
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
bool supportsVideo() override {
|
||||
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 setEchoCancellationStrength(int strength) override;
|
||||
void setAudioInputDevice(std::string id) override;
|
||||
|
|
|
@ -98,7 +98,7 @@ _remoteBatteryLevelIsLowUpdated(std::move(descriptor.remoteBatteryLevelIsLowUpda
|
|||
_remotePrefferedAspectRatioUpdated(std::move(descriptor.remotePrefferedAspectRatioUpdated)),
|
||||
_signalingDataEmitted(std::move(descriptor.signalingDataEmitted)),
|
||||
_signalBarsUpdated(std::move(descriptor.signalBarsUpdated)),
|
||||
_audioLevelsUpdated(std::move(descriptor.audioLevelsUpdated)),
|
||||
_audioLevelUpdated(std::move(descriptor.audioLevelsUpdated)),
|
||||
_createAudioDeviceModule(std::move(descriptor.createAudioDeviceModule)),
|
||||
_enableHighBitrateVideo(descriptor.config.enableHighBitrateVideo),
|
||||
_dataSaving(descriptor.config.dataSaving),
|
||||
|
@ -223,7 +223,7 @@ void Manager::start() {
|
|||
});
|
||||
}));
|
||||
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(
|
||||
StaticThreads::getMediaThread(),
|
||||
isOutgoing,
|
||||
|
|
|
@ -69,7 +69,7 @@ private:
|
|||
std::function<void(float)> _remotePrefferedAspectRatioUpdated;
|
||||
std::function<void(const std::vector<uint8_t> &)> _signalingDataEmitted;
|
||||
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<uint32_t(const Message &)> _sendSignalingMessage;
|
||||
std::function<void(Message&&)> _sendTransportMessage;
|
||||
|
|
|
@ -256,7 +256,7 @@ _taskQueueFactory(webrtc::CreateDefaultTaskQueueFactory()),
|
|||
_sendSignalingMessage(std::move(sendSignalingMessage)),
|
||||
_sendTransportMessage(std::move(sendTransportMessage)),
|
||||
_signalBarsUpdated(std::move(signalBarsUpdated)),
|
||||
_audioLevelsUpdated(std::move(audioLevelsUpdated)),
|
||||
_audioLevelUpdated(std::move(audioLevelsUpdated)),
|
||||
_createAudioDeviceModule(std::move(createAudioDeviceModule)),
|
||||
_protocolVersion(protocolVersion),
|
||||
_outgoingVideoState(videoCapture ? VideoState::Active : VideoState::Inactive),
|
||||
|
@ -467,7 +467,7 @@ void MediaManager::start() {
|
|||
}
|
||||
|
||||
beginStatsTimer(3000);
|
||||
if (_audioLevelsUpdated != nullptr) {
|
||||
if (_audioLevelUpdated != nullptr) {
|
||||
beginLevelsTimer(100);
|
||||
}
|
||||
}
|
||||
|
@ -595,7 +595,7 @@ void MediaManager::beginLevelsTimer(int timeoutMs) {
|
|||
return;
|
||||
}
|
||||
|
||||
strong->_audioLevelsUpdated(strong->_currentMyAudioLevel, strong->_currentAudioLevel);
|
||||
strong->_audioLevelUpdated(strong->_currentMyAudioLevel, strong->_currentAudioLevel);
|
||||
|
||||
strong->beginLevelsTimer(100);
|
||||
}, webrtc::TimeDelta::Millis(timeoutMs));
|
||||
|
@ -689,7 +689,7 @@ void MediaManager::setSendVideo(std::shared_ptr<VideoCaptureInterface> videoCapt
|
|||
const auto object = GetVideoCaptureAssumingSameThread(_videoCapture.get());
|
||||
_isScreenCapture = object->isScreenCapture();
|
||||
_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) {
|
||||
thread->PostTask([=] {
|
||||
// Checking this special guard instead of weak_ptr(this)
|
||||
|
@ -1040,7 +1040,7 @@ void MediaManager::fillCallStats(CallStats &callStats) {
|
|||
if (_videoCodecOut.has_value()) {
|
||||
callStats.outgoingCodec = _videoCodecOut->name;
|
||||
}
|
||||
callStats.bitrateRecords = std::move(_bitrateRecords);
|
||||
callStats.bitrateRecords = _bitrateRecords;
|
||||
}
|
||||
|
||||
void MediaManager::setAudioInputDevice(std::string id) {
|
||||
|
|
|
@ -47,7 +47,7 @@ public:
|
|||
std::function<void(Message &&)> sendSignalingMessage,
|
||||
std::function<void(Message &&)> sendTransportMessage,
|
||||
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,
|
||||
bool enableHighBitrateVideo,
|
||||
std::vector<std::string> preferredCodecs,
|
||||
|
@ -130,7 +130,7 @@ private:
|
|||
std::function<void(Message &&)> _sendSignalingMessage;
|
||||
std::function<void(Message &&)> _sendTransportMessage;
|
||||
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;
|
||||
|
||||
SSRC _ssrcAudio;
|
||||
|
|
|
@ -269,7 +269,7 @@ TrafficStats NetworkManager::getNetworkStats() {
|
|||
}
|
||||
|
||||
void NetworkManager::fillCallStats(CallStats &callStats) {
|
||||
callStats.networkRecords = std::move(_networkRecords);
|
||||
callStats.networkRecords = _networkRecords;
|
||||
}
|
||||
|
||||
void NetworkManager::logCurrentNetworkState() {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
namespace tgcalls {
|
||||
|
||||
SctpDataChannelProviderInterfaceImpl::SctpDataChannelProviderInterfaceImpl(
|
||||
cricket::DtlsTransport *transportChannel,
|
||||
rtc::PacketTransportInternal *transportChannel,
|
||||
bool isOutgoing,
|
||||
std::function<void(bool)> onStateChanged,
|
||||
std::function<void()> onTerminated,
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace tgcalls {
|
|||
class SctpDataChannelProviderInterfaceImpl : public sigslot::has_slots<>, public webrtc::SctpDataChannelControllerInterface, public webrtc::DataChannelObserver, public webrtc::DataChannelSink {
|
||||
public:
|
||||
SctpDataChannelProviderInterfaceImpl(
|
||||
cricket::DtlsTransport *transportChannel,
|
||||
rtc::PacketTransportInternal *transportChannel,
|
||||
bool isOutgoing,
|
||||
std::function<void(bool)> onStateChanged,
|
||||
std::function<void()> onTerminated,
|
||||
|
|
|
@ -199,7 +199,7 @@ _avIoContext(std::move(fileData)) {
|
|||
|
||||
AudioStreamingPartInternal::~AudioStreamingPartInternal() {
|
||||
if (_frame) {
|
||||
av_frame_unref(_frame);
|
||||
av_frame_free(&_frame);
|
||||
}
|
||||
if (_inputFormatContext) {
|
||||
avformat_close_input(&_inputFormatContext);
|
||||
|
|
|
@ -55,6 +55,7 @@ public:
|
|||
|
||||
~AudioStreamingPartPersistentDecoderState() {
|
||||
if (_codecContext) {
|
||||
avcodec_close(_codecContext);
|
||||
avcodec_free_context(&_codecContext);
|
||||
}
|
||||
}
|
||||
|
@ -85,6 +86,10 @@ AudioStreamingPartPersistentDecoder::AudioStreamingPartPersistentDecoder() {
|
|||
}
|
||||
|
||||
AudioStreamingPartPersistentDecoder::~AudioStreamingPartPersistentDecoder() {
|
||||
if (_state) {
|
||||
delete _state;
|
||||
_state = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioStreamingPartPersistentDecoder::maybeReset(AVCodecParameters const *codecParameters, AVRational timeBase) {
|
||||
|
|
|
@ -103,15 +103,11 @@ static int stringToInt(std::string const &string) {
|
|||
}
|
||||
|
||||
static std::string intToString(int value) {
|
||||
std::ostringstream stringStream;
|
||||
stringStream << value;
|
||||
return stringStream.str();
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
static std::string uint32ToString(uint32_t value) {
|
||||
std::ostringstream stringStream;
|
||||
stringStream << value;
|
||||
return stringStream.str();
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
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) {
|
||||
std::ostringstream stringStream;
|
||||
stringStream.imbue(std::locale::classic());
|
||||
stringStream << std::fixed << std::setprecision(3) << (double)timestamp / 1000.0;
|
||||
return stringStream.str();
|
||||
}
|
||||
|
@ -2845,9 +2842,9 @@ public:
|
|||
|
||||
void setAudioInputDevice(const std::string &id) {
|
||||
#if not defined(WEBRTC_IOS) && not defined(WEBRTC_ANDROID)
|
||||
_threads->getWorkerThread()->BlockingCall([&] {
|
||||
_threads->getWorkerThread()->BlockingCall([&] {
|
||||
SetAudioInputDeviceById(_audioDeviceModule.get(), id);
|
||||
});
|
||||
});
|
||||
#endif // WEBRTC_IOS
|
||||
}
|
||||
|
||||
|
|
|
@ -302,8 +302,20 @@ public:
|
|||
for (auto &videoSegment : segment->video) {
|
||||
videoSegment->isPlaying = true;
|
||||
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 (videoSegment->lastFramePts != frame->pts) {
|
||||
videoSegment->lastFramePts = frame->pts;
|
||||
|
@ -324,8 +336,20 @@ public:
|
|||
|
||||
for (auto &videoSegment : segment->unified) {
|
||||
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 (videoSegment->lastFramePts != frame->pts) {
|
||||
videoSegment->lastFramePts = frame->pts;
|
||||
|
@ -1039,6 +1063,7 @@ private:
|
|||
|
||||
std::map<uint32_t, double> _volumeBySsrc;
|
||||
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, int32_t> _currentEndpointMapping;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "api/video/i420_buffer.h"
|
||||
|
||||
#include "AVIOContextImpl.h"
|
||||
#include "platform/PlatformInterface.h"
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
@ -79,7 +80,7 @@ public:
|
|||
|
||||
~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;
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -322,7 +531,11 @@ public:
|
|||
}
|
||||
|
||||
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) {
|
||||
_codecContext = avcodec_alloc_context3(codec);
|
||||
ret = avcodec_parameters_to_context(_codecContext, videoCodecParameters);
|
||||
|
@ -344,14 +557,13 @@ public:
|
|||
_videoStream = videoStream;
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
~VideoStreamingPartInternal() {
|
||||
if (_codecContext) {
|
||||
avcodec_close(_codecContext);
|
||||
avcodec_free_context(&_codecContext);
|
||||
if (_videoCodecParameters) {
|
||||
avcodec_parameters_free(&_videoCodecParameters);
|
||||
}
|
||||
if (_inputFormatContext) {
|
||||
avformat_close_input(&_inputFormatContext);
|
||||
|
@ -393,32 +605,47 @@ public:
|
|||
}
|
||||
|
||||
absl::optional<VideoStreamingPartFrame> convertCurrentFrame() {
|
||||
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 platformFrameBuffer = PlatformInterface::SharedInstance()->createPlatformFrameFromData(_frame.frame());
|
||||
if (platformFrameBuffer) {
|
||||
auto videoFrame = webrtc::VideoFrame::Builder()
|
||||
.set_video_frame_buffer(i420Buffer)
|
||||
.set_video_frame_buffer(platformFrameBuffer)
|
||||
.set_rotation(_rotation)
|
||||
.build();
|
||||
|
||||
return VideoStreamingPartFrame(_endpointId, videoFrame, _frame.pts(_videoStream, _firstFramePts), _frameIndex);
|
||||
} 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() {
|
||||
if (!_codecContext) {
|
||||
absl::optional<VideoStreamingPartFrame> getNextFrame(VideoStreamingSharedState const *sharedState) {
|
||||
if (!_videoStream) {
|
||||
return {};
|
||||
}
|
||||
if (!_videoCodecParameters) {
|
||||
return {};
|
||||
}
|
||||
|
||||
sharedState->impl()->updateDecoderState(_videoCodecParameters, _videoStream->time_base);
|
||||
|
||||
while (true) {
|
||||
if (_didReadToEnd) {
|
||||
|
@ -432,42 +659,50 @@ public:
|
|||
} else {
|
||||
const auto frame = readNextDecodableFrame();
|
||||
if (frame) {
|
||||
auto status = avcodec_send_packet(_codecContext, frame->packet().packet());
|
||||
if (status == 0) {
|
||||
auto status = avcodec_receive_frame(_codecContext, _frame.frame());
|
||||
if (status == 0) {
|
||||
int sendStatus = sharedState->impl()->sendFrame(frame);
|
||||
if (sendStatus == 0) {
|
||||
int receiveStatus = sharedState->impl()->receiveFrame(_frame);
|
||||
if (receiveStatus == 0) {
|
||||
auto convertedFrame = convertCurrentFrame();
|
||||
if (convertedFrame) {
|
||||
_frameIndex++;
|
||||
return convertedFrame;
|
||||
}
|
||||
} else if (status == AVERROR(EAGAIN)) {
|
||||
} else if (receiveStatus == AVERROR(EAGAIN)) {
|
||||
// more data needed
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "avcodec_receive_frame failed with result: " << receiveStatus;
|
||||
_didReadToEnd = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "avcodec_send_packet failed with result: " << sendStatus;
|
||||
_didReadToEnd = true;
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
_didReadToEnd = true;
|
||||
int status = avcodec_send_packet(_codecContext, nullptr);
|
||||
if (status == 0) {
|
||||
int sendStatus = sharedState->impl()->sendFrame(nullptr);
|
||||
if (sendStatus == 0) {
|
||||
while (true) {
|
||||
auto status = avcodec_receive_frame(_codecContext, _frame.frame());
|
||||
if (status == 0) {
|
||||
int receiveStatus = sharedState->impl()->receiveFrame(_frame);
|
||||
if (receiveStatus == 0) {
|
||||
auto convertedFrame = convertCurrentFrame();
|
||||
if (convertedFrame) {
|
||||
_frameIndex++;
|
||||
_finalFrames.push_back(convertedFrame.value());
|
||||
}
|
||||
} else {
|
||||
if (receiveStatus != AVERROR_EOF) {
|
||||
RTC_LOG(LS_ERROR) << "avcodec_receive_frame (drain) failed with result: " << receiveStatus;
|
||||
}
|
||||
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;
|
||||
|
||||
AVFormatContext *_inputFormatContext = nullptr;
|
||||
AVCodecContext *_codecContext = nullptr;
|
||||
AVStream *_videoStream = nullptr;
|
||||
Frame _frame;
|
||||
|
||||
AVCodecParameters *_videoCodecParameters = nullptr;
|
||||
|
||||
std::vector<VideoStreamingPartFrame> _finalFrames;
|
||||
|
||||
int _frameIndex = 0;
|
||||
|
@ -564,7 +800,7 @@ public:
|
|||
~VideoStreamingPartState() {
|
||||
}
|
||||
|
||||
absl::optional<VideoStreamingPartFrame> getFrameAtRelativeTimestamp(double timestamp) {
|
||||
absl::optional<VideoStreamingPartFrame> getFrameAtRelativeTimestamp(VideoStreamingSharedState const *sharedState, double timestamp) {
|
||||
while (true) {
|
||||
while (_availableFrames.size() >= 2) {
|
||||
if (timestamp >= _availableFrames[1].pts) {
|
||||
|
@ -576,7 +812,7 @@ public:
|
|||
|
||||
if (_availableFrames.size() < 2) {
|
||||
if (!_parsedVideoParts.empty()) {
|
||||
auto result = _parsedVideoParts[0]->getNextFrame();
|
||||
auto result = _parsedVideoParts[0]->getNextFrame(sharedState);
|
||||
if (result) {
|
||||
_availableFrames.push_back(result.value());
|
||||
} else {
|
||||
|
@ -622,7 +858,7 @@ public:
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
std::vector<AudioStreamingPart::StreamingPartChannel> getAudio10msPerChannel(AudioStreamingPartPersistentDecoder &persistentDecoder) {
|
||||
while (!_parsedAudioParts.empty()) {
|
||||
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
|
||||
? _state->getFrameAtRelativeTimestamp(timestamp)
|
||||
? _state->getFrameAtRelativeTimestamp(sharedState, timestamp)
|
||||
: absl::nullopt;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
namespace tgcalls {
|
||||
|
||||
class VideoStreamingPartState;
|
||||
class VideoStreamingSharedStateInternal;
|
||||
|
||||
struct VideoStreamingPartFrame {
|
||||
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 {
|
||||
public:
|
||||
enum class ContentType {
|
||||
|
@ -48,7 +62,7 @@ public:
|
|||
VideoStreamingPart& operator=(const 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;
|
||||
bool hasRemainingFrames() const;
|
||||
|
||||
|
|
|
@ -215,7 +215,7 @@ void InstanceImplLegacy::sendVideoDeviceUpdated() {
|
|||
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) {
|
||||
|
|
|
@ -25,7 +25,7 @@ public:
|
|||
bool supportsVideo() override {
|
||||
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 setEchoCancellationStrength(int strength) override;
|
||||
void setAudioInputDevice(std::string id) override;
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#include "rtc_base/ref_counted_object.h"
|
||||
#include <string>
|
||||
|
||||
struct AVFrame;
|
||||
struct AVCodecContext;
|
||||
|
||||
namespace tgcalls {
|
||||
|
||||
enum class VideoState;
|
||||
|
@ -292,6 +295,14 @@ private:
|
|||
rtc::scoped_refptr<webrtc::AudioDeviceModule> _impl;
|
||||
};
|
||||
|
||||
class PlatformVideoFrame {
|
||||
public:
|
||||
PlatformVideoFrame() {
|
||||
}
|
||||
|
||||
virtual ~PlatformVideoFrame() = default;
|
||||
};
|
||||
|
||||
class PlatformInterface {
|
||||
public:
|
||||
static PlatformInterface *SharedInstance();
|
||||
|
@ -313,6 +324,11 @@ public:
|
|||
virtual rtc::scoped_refptr<WrappedAudioDeviceModule> wrapAudioDeviceModule(rtc::scoped_refptr<webrtc::AudioDeviceModule> 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:
|
||||
bool preferX264 = false;
|
||||
|
|
|
@ -6,12 +6,9 @@
|
|||
namespace tgcalls {
|
||||
|
||||
AndroidContext::AndroidContext(JNIEnv *env, jobject instance, bool screencast) {
|
||||
DEBUG_REF("VideoCapturerDevice");
|
||||
VideoCapturerDeviceClass = (jclass) env->NewGlobalRef(env->FindClass("org/telegram/messenger/voip/VideoCapturerDevice"));
|
||||
jmethodID initMethodId = env->GetMethodID(VideoCapturerDeviceClass, "<init>", "(Z)V");
|
||||
DEBUG_REF("VideoCapturerDevice javaCapturer");
|
||||
javaCapturer = env->NewGlobalRef(env->NewObject(VideoCapturerDeviceClass, initMethodId, screencast));
|
||||
DEBUG_REF("VideoCapturerDevice javaInstance");
|
||||
javaInstance = env->NewGlobalRef(instance);
|
||||
}
|
||||
|
||||
|
@ -20,21 +17,17 @@ AndroidContext::~AndroidContext() {
|
|||
|
||||
jmethodID onDestroyMethodId = env->GetMethodID(VideoCapturerDeviceClass, "onDestroy", "()V");
|
||||
env->CallVoidMethod(javaCapturer, onDestroyMethodId);
|
||||
DEBUG_DELREF("javaCapturer");
|
||||
env->DeleteGlobalRef(javaCapturer);
|
||||
javaCapturer = nullptr;
|
||||
|
||||
DEBUG_DELREF("VideoCapturerDeviceClass");
|
||||
env->DeleteGlobalRef(VideoCapturerDeviceClass);
|
||||
|
||||
if (javaInstance) {
|
||||
DEBUG_DELREF("javaInstance");
|
||||
env->DeleteGlobalRef(javaInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidContext::setJavaInstance(JNIEnv *env, jobject instance) {
|
||||
DEBUG_REF("setJavaInstance");
|
||||
javaInstance = env->NewGlobalRef(instance);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,60 +11,60 @@ namespace {
|
|||
|
||||
signaling::MediaContent convertContentInfoToSingalingContent(cricket::ContentInfo const &content) {
|
||||
signaling::MediaContent mappedContent;
|
||||
|
||||
|
||||
switch (content.media_description()->type()) {
|
||||
case cricket::MediaType::MEDIA_TYPE_AUDIO: {
|
||||
mappedContent.type = signaling::MediaContent::Type::Audio;
|
||||
|
||||
|
||||
for (const auto &codec : content.media_description()->as_audio()->codecs()) {
|
||||
signaling::PayloadType mappedPayloadType;
|
||||
mappedPayloadType.id = codec.id;
|
||||
mappedPayloadType.name = codec.name;
|
||||
mappedPayloadType.clockrate = codec.clockrate;
|
||||
mappedPayloadType.channels = (uint32_t)codec.channels;
|
||||
|
||||
|
||||
for (const auto &feedbackType : codec.feedback_params.params()) {
|
||||
signaling::FeedbackType mappedFeedbackType;
|
||||
mappedFeedbackType.type = feedbackType.id();
|
||||
mappedFeedbackType.subtype = feedbackType.param();
|
||||
mappedPayloadType.feedbackTypes.push_back(std::move(mappedFeedbackType));
|
||||
}
|
||||
|
||||
|
||||
for (const auto ¶meter : codec.params) {
|
||||
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 {
|
||||
return lhs.first < rhs.first;
|
||||
});
|
||||
|
||||
|
||||
mappedContent.payloadTypes.push_back(std::move(mappedPayloadType));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case cricket::MediaType::MEDIA_TYPE_VIDEO: {
|
||||
mappedContent.type = signaling::MediaContent::Type::Video;
|
||||
|
||||
|
||||
for (const auto &codec : content.media_description()->as_video()->codecs()) {
|
||||
signaling::PayloadType mappedPayloadType;
|
||||
mappedPayloadType.id = codec.id;
|
||||
mappedPayloadType.name = codec.name;
|
||||
mappedPayloadType.clockrate = codec.clockrate;
|
||||
mappedPayloadType.channels = 0;
|
||||
|
||||
|
||||
for (const auto &feedbackType : codec.feedback_params.params()) {
|
||||
signaling::FeedbackType mappedFeedbackType;
|
||||
mappedFeedbackType.type = feedbackType.id();
|
||||
mappedFeedbackType.subtype = feedbackType.param();
|
||||
mappedPayloadType.feedbackTypes.push_back(std::move(mappedFeedbackType));
|
||||
}
|
||||
|
||||
|
||||
for (const auto ¶meter : codec.params) {
|
||||
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 {
|
||||
return lhs.first < rhs.first;
|
||||
});
|
||||
|
||||
|
||||
mappedContent.payloadTypes.push_back(std::move(mappedPayloadType));
|
||||
}
|
||||
break;
|
||||
|
@ -74,7 +74,7 @@ signaling::MediaContent convertContentInfoToSingalingContent(cricket::ContentInf
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!content.media_description()->streams().empty()) {
|
||||
mappedContent.ssrc = content.media_description()->streams()[0].first_ssrc();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const auto &extension : content.media_description()->rtp_header_extensions()) {
|
||||
mappedContent.rtpExtensions.push_back(extension);
|
||||
}
|
||||
|
||||
|
||||
return mappedContent;
|
||||
}
|
||||
|
||||
cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &contentId, signaling::MediaContent const &content, webrtc::RtpTransceiverDirection direction) {
|
||||
std::unique_ptr<cricket::MediaContentDescription> contentDescription;
|
||||
|
||||
|
||||
switch (content.type) {
|
||||
case signaling::MediaContent::Type::Audio: {
|
||||
auto audioDescription = std::make_unique<cricket::AudioContentDescription>();
|
||||
|
||||
|
||||
for (const auto &payloadType : content.payloadTypes) {
|
||||
cricket::AudioCodec mappedCodec((int)payloadType.id, payloadType.name, (int)payloadType.clockrate, 0, payloadType.channels);
|
||||
for (const auto ¶meter : payloadType.parameters) {
|
||||
|
@ -109,14 +109,14 @@ cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &con
|
|||
}
|
||||
audioDescription->AddCodec(mappedCodec);
|
||||
}
|
||||
|
||||
|
||||
contentDescription = std::move(audioDescription);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
case signaling::MediaContent::Type::Video: {
|
||||
auto videoDescription = std::make_unique<cricket::VideoContentDescription>();
|
||||
|
||||
|
||||
for (const auto &payloadType : content.payloadTypes) {
|
||||
cricket::VideoCodec mappedCodec((int)payloadType.id, payloadType.name);
|
||||
for (const auto ¶meter : payloadType.parameters) {
|
||||
|
@ -127,9 +127,9 @@ cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &con
|
|||
}
|
||||
videoDescription->AddCodec(mappedCodec);
|
||||
}
|
||||
|
||||
|
||||
contentDescription = std::move(videoDescription);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
@ -137,7 +137,7 @@ cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &con
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cricket::StreamParams streamParams;
|
||||
streamParams.id = contentId;
|
||||
streamParams.set_stream_ids({ contentId });
|
||||
|
@ -151,47 +151,43 @@ cricket::ContentInfo convertSingalingContentToContentInfo(std::string const &con
|
|||
}
|
||||
}
|
||||
contentDescription->AddStream(streamParams);
|
||||
|
||||
|
||||
for (const auto &extension : content.rtpExtensions) {
|
||||
contentDescription->AddRtpHeaderExtension(extension);
|
||||
}
|
||||
|
||||
|
||||
contentDescription->set_direction(direction);
|
||||
contentDescription->set_rtcp_mux(true);
|
||||
|
||||
|
||||
cricket::ContentInfo mappedContentInfo(cricket::MediaProtocolType::kRtp);
|
||||
mappedContentInfo.name = contentId;
|
||||
mappedContentInfo.rejected = false;
|
||||
mappedContentInfo.bundle_only = false;
|
||||
mappedContentInfo.set_media_description(std::move(contentDescription));
|
||||
|
||||
|
||||
return mappedContentInfo;
|
||||
}
|
||||
|
||||
cricket::ContentInfo createInactiveContentInfo(std::string const &contentId) {
|
||||
std::unique_ptr<cricket::MediaContentDescription> contentDescription;
|
||||
|
||||
|
||||
auto audioDescription = std::make_unique<cricket::AudioContentDescription>();
|
||||
contentDescription = std::move(audioDescription);
|
||||
|
||||
|
||||
contentDescription->set_direction(webrtc::RtpTransceiverDirection::kInactive);
|
||||
contentDescription->set_rtcp_mux(true);
|
||||
|
||||
|
||||
cricket::ContentInfo mappedContentInfo(cricket::MediaProtocolType::kRtp);
|
||||
mappedContentInfo.name = contentId;
|
||||
mappedContentInfo.rejected = false;
|
||||
mappedContentInfo.bundle_only = false;
|
||||
mappedContentInfo.set_media_description(std::move(contentDescription));
|
||||
|
||||
|
||||
return mappedContentInfo;
|
||||
}
|
||||
|
||||
std::string contentIdBySsrc(uint32_t ssrc) {
|
||||
std::ostringstream contentIdString;
|
||||
|
||||
contentIdString << ssrc;
|
||||
|
||||
return contentIdString.str();
|
||||
return std::to_string(ssrc);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -200,19 +196,19 @@ ContentNegotiationContext::ContentNegotiationContext(const webrtc::WebRtcKeyValu
|
|||
_isOutgoing(isOutgoing),
|
||||
_uniqueRandomIdGenerator(uniqueRandomIdGenerator) {
|
||||
_transportDescriptionFactory = std::make_unique<cricket::TransportDescriptionFactory>(fieldTrials);
|
||||
|
||||
|
||||
// tempCertificate is only used to fill in the local SDP
|
||||
auto tempCertificate = rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams(rtc::KT_ECDSA), absl::nullopt);
|
||||
_transportDescriptionFactory->set_secure(cricket::SecurePolicy::SEC_REQUIRED);
|
||||
_transportDescriptionFactory->set_certificate(tempCertificate);
|
||||
|
||||
|
||||
_sessionDescriptionFactory = std::make_unique<cricket::MediaSessionDescriptionFactory>(_transportDescriptionFactory.get(), uniqueRandomIdGenerator);
|
||||
|
||||
|
||||
_needNegotiation = true;
|
||||
}
|
||||
|
||||
ContentNegotiationContext::~ContentNegotiationContext() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
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::VideoCodecs videoSendCodecs = mediaEngine->video().send_codecs();
|
||||
cricket::VideoCodecs videoRecvCodecs = mediaEngine->video().recv_codecs();
|
||||
|
||||
|
||||
for (const auto &codec : audioSendCodecs) {
|
||||
if (codec.name == "opus") {
|
||||
audioSendCodecs = { codec };
|
||||
|
@ -228,7 +224,7 @@ void ContentNegotiationContext::copyCodecsFromChannelManager(cricket::MediaEngin
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (randomize) {
|
||||
for (auto &codec : audioSendCodecs) {
|
||||
codec.id += 3;
|
||||
|
@ -243,23 +239,23 @@ void ContentNegotiationContext::copyCodecsFromChannelManager(cricket::MediaEngin
|
|||
codec.id += 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_sessionDescriptionFactory->set_audio_codecs(audioSendCodecs, audioRecvCodecs);
|
||||
_sessionDescriptionFactory->set_video_codecs(videoSendCodecs, videoRecvCodecs);
|
||||
|
||||
|
||||
int absSendTimeUriId = 2;
|
||||
int transportSequenceNumberUriId = 3;
|
||||
int videoRotationUri = 13;
|
||||
|
||||
|
||||
if (randomize) {
|
||||
absSendTimeUriId = 3;
|
||||
transportSequenceNumberUriId = 2;
|
||||
videoRotationUri = 4;
|
||||
}
|
||||
|
||||
|
||||
_rtpAudioExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, absSendTimeUriId);
|
||||
_rtpAudioExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, transportSequenceNumberUriId);
|
||||
|
||||
|
||||
_rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kAbsSendTimeUri, absSendTimeUriId);
|
||||
_rtpVideoExtensions.emplace_back(webrtc::RtpExtension::kTransportSequenceNumberUri, transportSequenceNumberUriId);
|
||||
_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 channelId = takeNextOutgoingChannelId();
|
||||
|
||||
|
||||
cricket::MediaType mappedMediaType;
|
||||
std::vector<webrtc::RtpHeaderExtensionCapability> rtpExtensions;
|
||||
switch (mediaType) {
|
||||
|
@ -288,7 +284,7 @@ std::string ContentNegotiationContext::addOutgoingChannel(signaling::MediaConten
|
|||
}
|
||||
cricket::MediaDescriptionOptions offerDescription(mappedMediaType, channelId, webrtc::RtpTransceiverDirection::kSendOnly, false);
|
||||
offerDescription.header_extensions = rtpExtensions;
|
||||
|
||||
|
||||
switch (mediaType) {
|
||||
case signaling::MediaContent::Type::Audio: {
|
||||
offerDescription.AddAudioSender(channelId, { channelId });
|
||||
|
@ -304,10 +300,10 @@ std::string ContentNegotiationContext::addOutgoingChannel(signaling::MediaConten
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_outgoingChannelDescriptions.emplace_back(std::move(offerDescription));
|
||||
_needNegotiation = true;
|
||||
|
||||
|
||||
return channelId;
|
||||
}
|
||||
|
||||
|
@ -315,9 +311,9 @@ void ContentNegotiationContext::removeOutgoingChannel(std::string const &id) {
|
|||
for (size_t i = 0; i < _outgoingChannels.size(); i++) {
|
||||
if (_outgoingChannelDescriptions[i].description.mid == id) {
|
||||
_outgoingChannelDescriptions.erase(_outgoingChannelDescriptions.begin() + i);
|
||||
|
||||
|
||||
_needNegotiation = true;
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -327,66 +323,66 @@ std::unique_ptr<cricket::SessionDescription> ContentNegotiationContext::currentS
|
|||
if (_channelIdOrder.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
auto sessionDescription = std::make_unique<cricket::SessionDescription>();
|
||||
|
||||
|
||||
for (const auto &id : _channelIdOrder) {
|
||||
bool found = false;
|
||||
|
||||
|
||||
for (const auto &channel : _incomingChannels) {
|
||||
if (contentIdBySsrc(channel.ssrc) == id) {
|
||||
found = true;
|
||||
|
||||
|
||||
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(channel.ssrc), channel, webrtc::RtpTransceiverDirection::kRecvOnly);
|
||||
|
||||
|
||||
cricket::TransportDescription transportDescription;
|
||||
cricket::TransportInfo transportInfo(contentIdBySsrc(channel.ssrc), transportDescription);
|
||||
sessionDescription->AddTransportInfo(transportInfo);
|
||||
|
||||
|
||||
sessionDescription->AddContent(std::move(mappedContent));
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const auto &channel : _outgoingChannels) {
|
||||
if (channel.id == id) {
|
||||
found = true;
|
||||
|
||||
|
||||
auto mappedContent = convertSingalingContentToContentInfo(channel.id, channel.content, webrtc::RtpTransceiverDirection::kSendOnly);
|
||||
|
||||
|
||||
cricket::TransportDescription transportDescription;
|
||||
cricket::TransportInfo transportInfo(mappedContent.name, transportDescription);
|
||||
sessionDescription->AddTransportInfo(transportInfo);
|
||||
|
||||
|
||||
sessionDescription->AddContent(std::move(mappedContent));
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!found) {
|
||||
auto mappedContent = createInactiveContentInfo("_" + id);
|
||||
|
||||
|
||||
cricket::TransportDescription transportDescription;
|
||||
cricket::TransportInfo transportInfo(mappedContent.name, transportDescription);
|
||||
sessionDescription->AddTransportInfo(transportInfo);
|
||||
|
||||
|
||||
sessionDescription->AddContent(std::move(mappedContent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return sessionDescription;
|
||||
}
|
||||
|
||||
static cricket::MediaDescriptionOptions getIncomingContentDescription(signaling::MediaContent const &content) {
|
||||
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly);
|
||||
|
||||
|
||||
cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kRecvOnly, false);
|
||||
for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) {
|
||||
contentDescription.header_extensions.emplace_back(extension.uri, extension.id);
|
||||
}
|
||||
|
||||
|
||||
return contentDescription;
|
||||
}
|
||||
|
||||
|
@ -397,71 +393,73 @@ std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiati
|
|||
if (_pendingOutgoingOffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
_needNegotiation = false;
|
||||
|
||||
_pendingOutgoingOffer = std::make_unique<PendingOutgoingOffer>();
|
||||
_pendingOutgoingOffer->exchangeId = _uniqueRandomIdGenerator->GenerateId();
|
||||
|
||||
|
||||
auto currentSessionDescription = currentSessionDescriptionFromCoordinatedState();
|
||||
|
||||
|
||||
cricket::MediaSessionOptions offerOptions;
|
||||
offerOptions.offer_extmap_allow_mixed = true;
|
||||
offerOptions.bundle_enabled = true;
|
||||
|
||||
|
||||
for (const auto &id : _channelIdOrder) {
|
||||
bool found = false;
|
||||
|
||||
|
||||
for (const auto &channel : _outgoingChannelDescriptions) {
|
||||
if (channel.description.mid == id) {
|
||||
found = true;
|
||||
offerOptions.media_description_options.push_back(channel.description);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const auto &content : _incomingChannels) {
|
||||
if (contentIdBySsrc(content.ssrc) == id) {
|
||||
found = true;
|
||||
offerOptions.media_description_options.push_back(getIncomingContentDescription(content));
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!found) {
|
||||
cricket::MediaDescriptionOptions contentDescription(cricket::MediaType::MEDIA_TYPE_AUDIO, "_" + id, webrtc::RtpTransceiverDirection::kInactive, false);
|
||||
offerOptions.media_description_options.push_back(contentDescription);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const auto &channel : _outgoingChannelDescriptions) {
|
||||
if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), channel.description.mid) == _channelIdOrder.end()) {
|
||||
_channelIdOrder.push_back(channel.description.mid);
|
||||
|
||||
|
||||
offerOptions.media_description_options.push_back(channel.description);
|
||||
}
|
||||
|
||||
|
||||
for (const auto &content : _incomingChannels) {
|
||||
if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), contentIdBySsrc(content.ssrc)) == _channelIdOrder.end()) {
|
||||
_channelIdOrder.push_back(contentIdBySsrc(content.ssrc));
|
||||
|
||||
|
||||
offerOptions.media_description_options.push_back(getIncomingContentDescription(content));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<cricket::SessionDescription> offer = _sessionDescriptionFactory->CreateOffer(offerOptions, currentSessionDescription.get());
|
||||
|
||||
|
||||
auto mappedOffer = std::make_unique<ContentNegotiationContext::NegotiationContents>();
|
||||
|
||||
|
||||
mappedOffer->exchangeId = _pendingOutgoingOffer->exchangeId;
|
||||
|
||||
|
||||
for (const auto &content : offer->contents()) {
|
||||
auto mappedContent = convertContentInfoToSingalingContent(content);
|
||||
|
||||
|
||||
if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kSendOnly) {
|
||||
mappedOffer->contents.push_back(std::move(mappedContent));
|
||||
|
||||
|
||||
for (auto &channel : _outgoingChannelDescriptions) {
|
||||
if (channel.description.mid == content.mid()) {
|
||||
channel.ssrc = mappedContent.ssrc;
|
||||
|
@ -470,7 +468,7 @@ std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiati
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return mappedOffer;
|
||||
}
|
||||
|
||||
|
@ -478,7 +476,7 @@ std::unique_ptr<ContentNegotiationContext::NegotiationContents> ContentNegotiati
|
|||
if (!remoteNegotiationContent) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
if (_pendingOutgoingOffer) {
|
||||
if (remoteNegotiationContent->exchangeId == _pendingOutgoingOffer->exchangeId) {
|
||||
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) {
|
||||
auto currentSessionDescription = currentSessionDescriptionFromCoordinatedState();
|
||||
|
||||
|
||||
auto mappedOffer = std::make_unique<cricket::SessionDescription>();
|
||||
|
||||
|
||||
cricket::MediaSessionOptions answerOptions;
|
||||
answerOptions.offer_extmap_allow_mixed = true;
|
||||
answerOptions.bundle_enabled = true;
|
||||
|
||||
|
||||
for (const auto &id : _channelIdOrder) {
|
||||
bool found = false;
|
||||
|
||||
|
||||
for (const auto &channel : _outgoingChannels) {
|
||||
if (channel.id == id) {
|
||||
found = true;
|
||||
|
||||
|
||||
auto mappedContent = convertSingalingContentToContentInfo(channel.id, channel.content, webrtc::RtpTransceiverDirection::kRecvOnly);
|
||||
|
||||
|
||||
cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kSendOnly, false);
|
||||
for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) {
|
||||
contentDescription.header_extensions.emplace_back(extension.uri, extension.id);
|
||||
}
|
||||
answerOptions.media_description_options.push_back(contentDescription);
|
||||
|
||||
|
||||
cricket::TransportDescription transportDescription;
|
||||
cricket::TransportInfo transportInfo(channel.id, transportDescription);
|
||||
mappedOffer->AddTransportInfo(transportInfo);
|
||||
|
||||
|
||||
mappedOffer->AddContent(std::move(mappedContent));
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const auto &content : offer->contents) {
|
||||
if (contentIdBySsrc(content.ssrc) == id) {
|
||||
found = true;
|
||||
|
||||
|
||||
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly);
|
||||
|
||||
|
||||
cricket::MediaDescriptionOptions contentDescription(mappedContent.media_description()->type(), mappedContent.name, webrtc::RtpTransceiverDirection::kRecvOnly, false);
|
||||
for (const auto &extension : mappedContent.media_description()->rtp_header_extensions()) {
|
||||
contentDescription.header_extensions.emplace_back(extension.uri, extension.id);
|
||||
}
|
||||
answerOptions.media_description_options.push_back(contentDescription);
|
||||
|
||||
|
||||
cricket::TransportDescription transportDescription;
|
||||
cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription);
|
||||
mappedOffer->AddTransportInfo(transportInfo);
|
||||
|
||||
|
||||
mappedOffer->AddContent(std::move(mappedContent));
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!found) {
|
||||
auto mappedContent = createInactiveContentInfo("_" + id);
|
||||
|
||||
|
||||
cricket::MediaDescriptionOptions contentDescription(cricket::MediaType::MEDIA_TYPE_AUDIO, "_" + id, webrtc::RtpTransceiverDirection::kInactive, false);
|
||||
answerOptions.media_description_options.push_back(contentDescription);
|
||||
|
||||
|
||||
cricket::TransportDescription transportDescription;
|
||||
cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription);
|
||||
mappedOffer->AddTransportInfo(transportInfo);
|
||||
|
||||
|
||||
mappedOffer->AddContent(std::move(mappedContent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const auto &content : offer->contents) {
|
||||
if (std::find(_channelIdOrder.begin(), _channelIdOrder.end(), contentIdBySsrc(content.ssrc)) == _channelIdOrder.end()) {
|
||||
_channelIdOrder.push_back(contentIdBySsrc(content.ssrc));
|
||||
|
||||
|
||||
answerOptions.media_description_options.push_back(getIncomingContentDescription(content));
|
||||
|
||||
|
||||
auto mappedContent = convertSingalingContentToContentInfo(contentIdBySsrc(content.ssrc), content, webrtc::RtpTransceiverDirection::kSendOnly);
|
||||
|
||||
|
||||
cricket::TransportDescription transportDescription;
|
||||
cricket::TransportInfo transportInfo(mappedContent.mid(), transportDescription);
|
||||
mappedOffer->AddTransportInfo(transportInfo);
|
||||
|
||||
|
||||
mappedOffer->AddContent(std::move(mappedContent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<cricket::SessionDescription> answer = _sessionDescriptionFactory->CreateAnswer(mappedOffer.get(), answerOptions, currentSessionDescription.get());
|
||||
|
||||
|
||||
auto mappedAnswer = std::make_unique<NegotiationContents>();
|
||||
|
||||
|
||||
mappedAnswer->exchangeId = offer->exchangeId;
|
||||
|
||||
|
||||
std::vector<signaling::MediaContent> incomingChannels;
|
||||
|
||||
|
||||
for (const auto &content : answer->contents()) {
|
||||
auto mappedContent = convertContentInfoToSingalingContent(content);
|
||||
|
||||
|
||||
if (content.media_description()->direction() == webrtc::RtpTransceiverDirection::kRecvOnly) {
|
||||
for (const auto &offerContent : offer->contents) {
|
||||
if (contentIdBySsrc(offerContent.ssrc) == content.mid()) {
|
||||
mappedContent.ssrc = offerContent.ssrc;
|
||||
mappedContent.ssrcGroups = offerContent.ssrcGroups;
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
incomingChannels.push_back(mappedContent);
|
||||
mappedAnswer->contents.push_back(std::move(mappedContent));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_incomingChannels = incomingChannels;
|
||||
|
||||
|
||||
return mappedAnswer;
|
||||
}
|
||||
|
||||
|
@ -621,17 +619,16 @@ void ContentNegotiationContext::setAnswer(std::unique_ptr<ContentNegotiationCont
|
|||
if (_pendingOutgoingOffer->exchangeId != answer->exchangeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
_pendingOutgoingOffer.reset();
|
||||
_needNegotiation = false;
|
||||
|
||||
|
||||
_outgoingChannels.clear();
|
||||
|
||||
|
||||
for (const auto &content : answer->contents) {
|
||||
for (const auto &pendingChannel : _outgoingChannelDescriptions) {
|
||||
if (pendingChannel.ssrc != 0 && content.ssrc == pendingChannel.ssrc) {
|
||||
_outgoingChannels.emplace_back(pendingChannel.description.mid, content);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -639,53 +636,52 @@ void ContentNegotiationContext::setAnswer(std::unique_ptr<ContentNegotiationCont
|
|||
}
|
||||
|
||||
std::string ContentNegotiationContext::takeNextOutgoingChannelId() {
|
||||
std::ostringstream result;
|
||||
result << "m" << _nextOutgoingChannelId;
|
||||
const auto result = "m" + std::to_string(_nextOutgoingChannelId);
|
||||
_nextOutgoingChannelId++;
|
||||
|
||||
return result.str();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentNegotiationContext::CoordinatedState> ContentNegotiationContext::coordinatedState() const {
|
||||
auto result = std::make_unique<ContentNegotiationContext::CoordinatedState>();
|
||||
|
||||
|
||||
result->incomingContents = _incomingChannels;
|
||||
for (const auto &channel : _outgoingChannels) {
|
||||
bool found = false;
|
||||
|
||||
|
||||
for (const auto &channelDescription : _outgoingChannelDescriptions) {
|
||||
if (channelDescription.description.mid == channel.id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (found) {
|
||||
result->outgoingContents.push_back(channel.content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
absl::optional<uint32_t> ContentNegotiationContext::outgoingChannelSsrc(std::string const &id) const {
|
||||
for (const auto &channel : _outgoingChannels) {
|
||||
bool found = false;
|
||||
|
||||
|
||||
for (const auto &channelDescription : _outgoingChannelDescriptions) {
|
||||
if (channelDescription.description.mid == channel.id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (found && channel.id == id) {
|
||||
if (channel.content.ssrc != 0) {
|
||||
return channel.content.ssrc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
|
|
597
TMessagesProj/jni/voip/tgcalls/v2/DirectNetworkingImpl.cpp
Normal file
597
TMessagesProj/jni/voip/tgcalls/v2/DirectNetworkingImpl.cpp
Normal 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
|
119
TMessagesProj/jni/voip/tgcalls/v2/DirectNetworkingImpl.h
Normal file
119
TMessagesProj/jni/voip/tgcalls/v2/DirectNetworkingImpl.h
Normal 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
|
169
TMessagesProj/jni/voip/tgcalls/v2/InstanceNetworking.h
Normal file
169
TMessagesProj/jni/voip/tgcalls/v2/InstanceNetworking.h
Normal 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
|
|
@ -4,6 +4,7 @@
|
|||
#include "VideoCaptureInterfaceImpl.h"
|
||||
#include "VideoCapturerInterface.h"
|
||||
#include "v2/NativeNetworkingImpl.h"
|
||||
#include "v2/DirectNetworkingImpl.h"
|
||||
#include "v2/Signaling.h"
|
||||
#include "v2/ContentNegotiation.h"
|
||||
|
||||
|
@ -77,7 +78,7 @@ SignalingProtocolVersion signalingProtocolVersion(std::string const &version) {
|
|||
} else if (version == "8.0.0") {
|
||||
return SignalingProtocolVersion::V2;
|
||||
} else if (version == "9.0.0") {
|
||||
return SignalingProtocolVersion::V3;
|
||||
return SignalingProtocolVersion::V2;
|
||||
} else {
|
||||
RTC_LOG(LS_ERROR) << "signalingProtocolVersion: unknown version " << version;
|
||||
|
||||
|
@ -136,13 +137,12 @@ public:
|
|||
audioOptions.noise_suppression = true;
|
||||
}
|
||||
|
||||
std::ostringstream contentId;
|
||||
contentId << _ssrc;
|
||||
const auto contentId = std::to_string(_ssrc);
|
||||
|
||||
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([&]() {
|
||||
_outgoingAudioChannel->SetRtpTransport(rtpTransport);
|
||||
});
|
||||
|
@ -272,12 +272,9 @@ public:
|
|||
audioOptions.audio_jitter_buffer_fast_accelerate = true;
|
||||
audioOptions.audio_jitter_buffer_min_delay_ms = 50;
|
||||
|
||||
std::ostringstream contentId;
|
||||
contentId << _ssrc;
|
||||
const auto streamId = std::to_string(_ssrc);
|
||||
|
||||
std::string streamId = contentId.str();
|
||||
|
||||
_audioChannel = _channelManager->CreateVoiceChannel(call, cricket::MediaConfig(), contentId.str(), false, NativeNetworkingImpl::getDefaulCryptoOptions(), audioOptions);
|
||||
_audioChannel = _channelManager->CreateVoiceChannel(call, cricket::MediaConfig(), streamId, false, NativeNetworkingImpl::getDefaulCryptoOptions(), audioOptions);
|
||||
_threads->getNetworkThread()->BlockingCall([&]() {
|
||||
_audioChannel->SetRtpTransport(rtpTransport);
|
||||
});
|
||||
|
@ -401,10 +398,7 @@ public:
|
|||
cricket::VideoOptions videoOptions;
|
||||
videoOptions.is_screencast = isScreencast;
|
||||
|
||||
std::ostringstream contentId;
|
||||
contentId << mediaContent.ssrc;
|
||||
|
||||
_outgoingVideoChannel = _channelManager->CreateVideoChannel(call, cricket::MediaConfig(), contentId.str(), false, NativeNetworkingImpl::getDefaulCryptoOptions(), videoOptions, videoBitrateAllocatorFactory);
|
||||
_outgoingVideoChannel = _channelManager->CreateVideoChannel(call, cricket::MediaConfig(), std::to_string(mediaContent.ssrc), false, NativeNetworkingImpl::getDefaulCryptoOptions(), videoOptions, videoBitrateAllocatorFactory);
|
||||
_threads->getNetworkThread()->BlockingCall([&]() {
|
||||
_outgoingVideoChannel->SetRtpTransport(rtpTransport);
|
||||
});
|
||||
|
@ -702,10 +696,9 @@ public:
|
|||
|
||||
_videoBitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory();
|
||||
|
||||
std::ostringstream contentId;
|
||||
contentId << mediaContent.ssrc;
|
||||
const auto contentId = std::to_string(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([&]() {
|
||||
_videoChannel->SetRtpTransport(rtpTransport);
|
||||
});
|
||||
|
@ -750,7 +743,7 @@ public:
|
|||
videoRecvStreamParams.ssrcs = allSsrcs;
|
||||
|
||||
videoRecvStreamParams.cname = "cname";
|
||||
videoRecvStreamParams.set_stream_ids({ contentId.str() });
|
||||
videoRecvStreamParams.set_stream_ids({ contentId });
|
||||
|
||||
auto incomingVideoDescription = std::make_unique<cricket::VideoContentDescription>();
|
||||
for (const auto &rtpExtension : mediaContent.rtpExtensions) {
|
||||
|
@ -824,8 +817,8 @@ struct StateLogRecord {
|
|||
struct NetworkStateLogRecord {
|
||||
bool isConnected = false;
|
||||
bool isFailed = false;
|
||||
absl::optional<NativeNetworkingImpl::RouteDescription> route;
|
||||
absl::optional<NativeNetworkingImpl::ConnectionDescription> connection;
|
||||
absl::optional<InstanceNetworking::RouteDescription> route;
|
||||
absl::optional<InstanceNetworking::ConnectionDescription> connection;
|
||||
|
||||
bool operator==(NetworkStateLogRecord const &rhs) const {
|
||||
if (isConnected != rhs.isConnected) {
|
||||
|
@ -858,23 +851,25 @@ public:
|
|||
_threads(threads),
|
||||
_rtcServers(descriptor.rtcServers),
|
||||
_proxy(std::move(descriptor.proxy)),
|
||||
_directConnectionChannel(descriptor.directConnectionChannel),
|
||||
_enableP2P(descriptor.config.enableP2P),
|
||||
_encryptionKey(std::move(descriptor.encryptionKey)),
|
||||
_stateUpdated(descriptor.stateUpdated),
|
||||
_signalBarsUpdated(descriptor.signalBarsUpdated),
|
||||
_audioLevelsUpdated(descriptor.audioLevelsUpdated),
|
||||
_audioLevelUpdated(descriptor.audioLevelsUpdated),
|
||||
_remoteBatteryLevelIsLowUpdated(descriptor.remoteBatteryLevelIsLowUpdated),
|
||||
_remoteMediaStateUpdated(descriptor.remoteMediaStateUpdated),
|
||||
_remotePrefferedAspectRatioUpdated(descriptor.remotePrefferedAspectRatioUpdated),
|
||||
_signalingDataEmitted(descriptor.signalingDataEmitted),
|
||||
_createAudioDeviceModule(descriptor.createAudioDeviceModule),
|
||||
_devicesConfig(descriptor.mediaDevicesConfig),
|
||||
_statsLogPath(descriptor.config.statsLogPath),
|
||||
_eventLog(std::make_unique<webrtc::RtcEventLogNull>()),
|
||||
_taskQueueFactory(webrtc::CreateDefaultTaskQueueFactory()),
|
||||
_initialInputDeviceId(std::move(descriptor.initialInputDeviceId)),
|
||||
_initialOutputDeviceId(std::move(descriptor.initialOutputDeviceId)),
|
||||
_videoCapture(descriptor.videoCapture),
|
||||
_platformContext(descriptor.platformContext) {
|
||||
_platformContext(descriptor.platformContext) {
|
||||
webrtc::field_trial::InitFieldTrialsFromString(
|
||||
"WebRTC-DataChannel-Dcsctp/Enabled/"
|
||||
"WebRTC-Audio-MinimizeResamplingOnMobile/Enabled/"
|
||||
|
@ -901,7 +896,7 @@ public:
|
|||
|
||||
_contentNegotiationContext.reset();
|
||||
|
||||
_networking->perform([](NativeNetworkingImpl *networking) {
|
||||
_networking->perform([](InstanceNetworking *networking) {
|
||||
networking->stop();
|
||||
});
|
||||
|
||||
|
@ -957,68 +952,136 @@ public:
|
|||
proxy = *(_proxy.get());
|
||||
}
|
||||
|
||||
_networking.reset(new ThreadLocalObject<NativeNetworkingImpl>(_threads->getNetworkThread(), [weak, threads = _threads, isOutgoing = _encryptionKey.isOutgoing, rtcServers = _rtcServers, proxy, enableP2P = _enableP2P]() {
|
||||
return new NativeNetworkingImpl(NativeNetworkingImpl::Configuration{
|
||||
.isOutgoing = isOutgoing,
|
||||
.enableStunMarking = false,
|
||||
.enableTCP = false,
|
||||
.enableP2P = enableP2P,
|
||||
.rtcServers = rtcServers,
|
||||
.proxy = proxy,
|
||||
.stateUpdated = [threads, weak](const NativeNetworkingImpl::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;
|
||||
}
|
||||
_networking.reset(new ThreadLocalObject<NativeNetworkingImpl>(_threads->getNetworkThread(), [weak, threads = _threads, encryptionKey = _encryptionKey, isOutgoing = _encryptionKey.isOutgoing, rtcServers = _rtcServers, proxy, enableP2P = _enableP2P, directConnectionChannel = _directConnectionChannel]() {
|
||||
if (directConnectionChannel) {
|
||||
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([=] {
|
||||
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();
|
||||
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();
|
||||
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
|
||||
});
|
||||
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,
|
||||
});
|
||||
}
|
||||
}));
|
||||
|
||||
PlatformInterface::SharedInstance()->configurePlatformAudio();
|
||||
|
@ -1033,12 +1096,13 @@ public:
|
|||
mediaDeps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory<webrtc::AudioEncoderOpus, webrtc::AudioEncoderL16>();
|
||||
mediaDeps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory<webrtc::AudioDecoderOpus, webrtc::AudioDecoderL16>();
|
||||
|
||||
mediaDeps.video_encoder_factory = PlatformInterface::SharedInstance()->makeVideoEncoderFactory(_platformContext, true);
|
||||
mediaDeps.video_decoder_factory = PlatformInterface::SharedInstance()->makeVideoDecoderFactory(_platformContext);
|
||||
mediaDeps.video_encoder_factory = PlatformInterface::SharedInstance()->makeVideoEncoderFactory(_platformContext, true);
|
||||
mediaDeps.video_decoder_factory = PlatformInterface::SharedInstance()->makeVideoDecoderFactory(_platformContext);
|
||||
|
||||
mediaDeps.adm = _audioDeviceModule;
|
||||
|
||||
|
||||
webrtc:: AudioProcessingBuilder builder;
|
||||
mediaDeps.audio_processing = builder.Create();
|
||||
|
||||
_availableVideoFormats = mediaDeps.video_encoder_factory->GetSupportedFormats();
|
||||
|
||||
|
@ -1061,6 +1125,9 @@ public:
|
|||
_threads->getWorkerThread()->BlockingCall([&]() {
|
||||
callConfig.audio_state = _channelManager->media_engine()->voice().GetAudioState();
|
||||
_call.reset(webrtc::Call::Create(callConfig));
|
||||
|
||||
// SetAudioInputDeviceById(_audioDeviceModule.get(), _devicesConfig.audioInputId);
|
||||
// SetAudioOutputDeviceById(_audioDeviceModule.get(), _devicesConfig.audioOutputId);
|
||||
});
|
||||
|
||||
_uniqueRandomIdGenerator.reset(new rtc::UniqueRandomIdGenerator());
|
||||
|
@ -1072,7 +1139,7 @@ public:
|
|||
|
||||
_videoBitrateAllocatorFactory = webrtc::CreateBuiltinVideoBitrateAllocatorFactory();
|
||||
|
||||
_networking->perform([](NativeNetworkingImpl *networking) {
|
||||
_networking->perform([](InstanceNetworking *networking) {
|
||||
networking->start();
|
||||
});
|
||||
|
||||
|
@ -1087,7 +1154,7 @@ public:
|
|||
beginQualityTimer(0);
|
||||
beginLogTimer(0);
|
||||
|
||||
NativeNetworkingImpl::State initialNetworkState;
|
||||
InstanceNetworking::State initialNetworkState;
|
||||
initialNetworkState.isReadyToSendData = false;
|
||||
onNetworkStateUpdated(initialNetworkState);
|
||||
}
|
||||
|
@ -1287,6 +1354,7 @@ public:
|
|||
outgoingAudioContent.value(),
|
||||
_threads
|
||||
));
|
||||
_outgoingAudioChannel->setIsMuted(_isMicrophoneMuted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1481,10 +1549,16 @@ public:
|
|||
void sendInitialSetup() {
|
||||
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();
|
||||
std::string hash = localFingerprint->algorithm;
|
||||
std::string fingerprint = localFingerprint->GetRfc4572Fingerprint();
|
||||
std::string hash;
|
||||
std::string fingerprint;
|
||||
|
||||
if (localFingerprint) {
|
||||
hash = localFingerprint->algorithm;
|
||||
fingerprint = localFingerprint->GetRfc4572Fingerprint();
|
||||
}
|
||||
|
||||
std::string setup;
|
||||
if (isOutgoing) {
|
||||
setup = "actpass";
|
||||
|
@ -1497,7 +1571,7 @@ public:
|
|||
std::string pwd = localIceParams.pwd;
|
||||
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();
|
||||
if (!strong) {
|
||||
return;
|
||||
|
@ -1614,7 +1688,7 @@ public:
|
|||
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);
|
||||
});
|
||||
|
||||
|
@ -1726,13 +1800,13 @@ public:
|
|||
if (_pendingIceCandidates.size() == 0) {
|
||||
return;
|
||||
}
|
||||
_networking->perform([threads = _threads, parsedCandidates = _pendingIceCandidates](NativeNetworkingImpl *networking) {
|
||||
_networking->perform([threads = _threads, parsedCandidates = _pendingIceCandidates](InstanceNetworking *networking) {
|
||||
networking->addCandidates(parsedCandidates);
|
||||
});
|
||||
_pendingIceCandidates.clear();
|
||||
}
|
||||
|
||||
void onNetworkStateUpdated(NativeNetworkingImpl::State const &state) {
|
||||
void onNetworkStateUpdated(InstanceNetworking::State const &state) {
|
||||
State mappedState;
|
||||
if (state.isFailed) {
|
||||
mappedState = State::Failed;
|
||||
|
@ -1775,7 +1849,7 @@ public:
|
|||
auto data = message.serialize();
|
||||
std::string stringData(data.begin(), data.end());
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
@ -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;
|
||||
if (_incomingVideoChannel) {
|
||||
_incomingVideoChannel->addSink(sink);
|
||||
|
@ -1978,14 +2052,10 @@ public:
|
|||
for (const auto &record : _networkStateLogRecords) {
|
||||
json11::Json::object jsonRecord;
|
||||
|
||||
std::ostringstream timestampString;
|
||||
|
||||
if (baseTimestamp == 0) {
|
||||
baseTimestamp = record.timestamp;
|
||||
}
|
||||
timestampString << (record.timestamp - baseTimestamp);
|
||||
|
||||
jsonRecord.insert(std::make_pair("t", json11::Json(timestampString.str())));
|
||||
jsonRecord.insert(std::make_pair("t", json11::Json(std::to_string(record.timestamp - baseTimestamp))));
|
||||
jsonRecord.insert(std::make_pair("c", json11::Json(record.record.isConnected ? 1 : 0)));
|
||||
if (record.record.route) {
|
||||
jsonRecord.insert(std::make_pair("local", json11::Json(record.record.route->localDescription)));
|
||||
|
@ -1994,7 +2064,7 @@ public:
|
|||
if (record.record.connection) {
|
||||
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;
|
||||
|
||||
jsonCandidate.insert(std::make_pair("type", json11::Json(candidate.type)));
|
||||
|
@ -2077,16 +2147,18 @@ private:
|
|||
std::shared_ptr<Threads> _threads;
|
||||
std::vector<RtcServer> _rtcServers;
|
||||
std::unique_ptr<Proxy> _proxy;
|
||||
std::shared_ptr<DirectConnectionChannel> _directConnectionChannel;
|
||||
bool _enableP2P = false;
|
||||
EncryptionKey _encryptionKey;
|
||||
std::function<void(State)> _stateUpdated;
|
||||
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(AudioState, VideoState)> _remoteMediaStateUpdated;
|
||||
std::function<void(float)> _remotePrefferedAspectRatioUpdated;
|
||||
std::function<void(const std::vector<uint8_t> &)> _signalingDataEmitted;
|
||||
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule;
|
||||
MediaDevicesConfig _devicesConfig;
|
||||
FilePath _statsLogPath;
|
||||
|
||||
std::unique_ptr<SignalingConnection> _signalingConnection;
|
||||
|
@ -2098,7 +2170,7 @@ private:
|
|||
std::vector<StateLogRecord<NetworkStateLogRecord>> _networkStateLogRecords;
|
||||
std::vector<StateLogRecord<NetworkBitrateLogRecord>> _networkBitrateLogRecords;
|
||||
|
||||
absl::optional<NativeNetworkingImpl::State> _networkState;
|
||||
absl::optional<InstanceNetworking::State> _networkState;
|
||||
|
||||
bool _handshakeCompleted = false;
|
||||
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->setIncomingVideoOutput(sink);
|
||||
});
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
bool supportsVideo() override {
|
||||
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 setEchoCancellationStrength(int strength) override;
|
||||
void setAudioInputDevice(std::string id) override;
|
||||
|
|
|
@ -325,8 +325,8 @@ struct StateLogRecord {
|
|||
struct NetworkStateLogRecord {
|
||||
bool isConnected = false;
|
||||
bool isFailed = false;
|
||||
absl::optional<NativeNetworkingImpl::RouteDescription> route;
|
||||
absl::optional<NativeNetworkingImpl::ConnectionDescription> connection;
|
||||
absl::optional<InstanceNetworking::RouteDescription> route;
|
||||
absl::optional<InstanceNetworking::ConnectionDescription> connection;
|
||||
|
||||
bool operator==(NetworkStateLogRecord const &rhs) const {
|
||||
if (isConnected != rhs.isConnected) {
|
||||
|
@ -363,7 +363,7 @@ public:
|
|||
_encryptionKey(std::move(descriptor.encryptionKey)),
|
||||
_stateUpdated(descriptor.stateUpdated),
|
||||
_signalBarsUpdated(descriptor.signalBarsUpdated),
|
||||
_audioLevelsUpdated(descriptor.audioLevelsUpdated),
|
||||
_audioLevelUpdated(descriptor.audioLevelsUpdated),
|
||||
_remoteBatteryLevelIsLowUpdated(descriptor.remoteBatteryLevelIsLowUpdated),
|
||||
_remoteMediaStateUpdated(descriptor.remoteMediaStateUpdated),
|
||||
_remotePrefferedAspectRatioUpdated(descriptor.remotePrefferedAspectRatioUpdated),
|
||||
|
@ -373,7 +373,7 @@ public:
|
|||
_eventLog(std::make_unique<webrtc::RtcEventLogNull>()),
|
||||
_taskQueueFactory(webrtc::CreateDefaultTaskQueueFactory()),
|
||||
_videoCapture(descriptor.videoCapture),
|
||||
_platformContext(descriptor.platformContext) {
|
||||
_platformContext(descriptor.platformContext) {
|
||||
webrtc::field_trial::InitFieldTrialsFromString(
|
||||
"WebRTC-DataChannel-Dcsctp/Enabled/"
|
||||
"WebRTC-Audio-iOS-Holding/Enabled/"
|
||||
|
@ -459,6 +459,11 @@ public:
|
|||
|
||||
cricket::MediaEngineDependencies mediaDeps;
|
||||
mediaDeps.adm = _audioDeviceModule;
|
||||
|
||||
webrtc:: AudioProcessingBuilder builder;
|
||||
mediaDeps.audio_processing = builder.Create();
|
||||
|
||||
|
||||
mediaDeps.task_queue_factory = peerConnectionFactoryDependencies.task_queue_factory.get();
|
||||
mediaDeps.audio_encoder_factory = webrtc::CreateAudioEncoderFactory<webrtc::AudioEncoderOpus>();
|
||||
mediaDeps.audio_decoder_factory = webrtc::CreateAudioDecoderFactory<webrtc::AudioDecoderOpus>();
|
||||
|
@ -607,10 +612,10 @@ public:
|
|||
return;
|
||||
}
|
||||
|
||||
NativeNetworkingImpl::ConnectionDescription connectionDescription;
|
||||
InstanceNetworking::ConnectionDescription connectionDescription;
|
||||
|
||||
connectionDescription.local = NativeNetworkingImpl::connectionDescriptionFromCandidate(event.selected_candidate_pair.local);
|
||||
connectionDescription.remote = NativeNetworkingImpl::connectionDescriptionFromCandidate(event.selected_candidate_pair.remote);
|
||||
connectionDescription.local = InstanceNetworking::connectionDescriptionFromCandidate(event.selected_candidate_pair.local);
|
||||
connectionDescription.remote = InstanceNetworking::connectionDescriptionFromCandidate(event.selected_candidate_pair.remote);
|
||||
|
||||
if (!strong->_currentConnectionDescription || strong->_currentConnectionDescription.value() != connectionDescription) {
|
||||
strong->_currentConnectionDescription = std::move(connectionDescription);
|
||||
|
@ -649,10 +654,8 @@ public:
|
|||
if (server.isTurn) {
|
||||
webrtc::PeerConnectionInterface::IceServer mappedServer;
|
||||
|
||||
std::ostringstream uri;
|
||||
uri << "turn:" << address.HostAsURIString() << ":" << server.port;
|
||||
|
||||
mappedServer.urls.push_back(uri.str());
|
||||
mappedServer.urls.push_back(
|
||||
"turn:" + address.HostAsURIString() + ":" + std::to_string(server.port));
|
||||
mappedServer.username = server.login;
|
||||
mappedServer.password = server.password;
|
||||
|
||||
|
@ -660,10 +663,8 @@ public:
|
|||
} else {
|
||||
webrtc::PeerConnectionInterface::IceServer mappedServer;
|
||||
|
||||
std::ostringstream uri;
|
||||
uri << "stun:" << address.HostAsURIString() << ":" << server.port;
|
||||
|
||||
mappedServer.urls.push_back(uri.str());
|
||||
mappedServer.urls.push_back(
|
||||
"stun:" + address.HostAsURIString() + ":" + std::to_string(server.port));
|
||||
|
||||
peerConnectionConfiguration.servers.push_back(mappedServer);
|
||||
}
|
||||
|
@ -704,7 +705,7 @@ public:
|
|||
parameters.encodings[0].max_bitrate_bps = 32 * 1024;
|
||||
_outgoingAudioTransceiver->sender()->SetParameters(parameters);
|
||||
|
||||
_outgoingAudioTrack->set_enabled(true);
|
||||
_outgoingAudioTrack->set_enabled(!_isMicrophoneMuted);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1451,14 +1452,10 @@ public:
|
|||
for (const auto &record : _networkStateLogRecords) {
|
||||
json11::Json::object jsonRecord;
|
||||
|
||||
std::ostringstream timestampString;
|
||||
|
||||
if (baseTimestamp == 0) {
|
||||
baseTimestamp = record.timestamp;
|
||||
}
|
||||
timestampString << (record.timestamp - baseTimestamp);
|
||||
|
||||
jsonRecord.insert(std::make_pair("t", json11::Json(timestampString.str())));
|
||||
jsonRecord.insert(std::make_pair("t", json11::Json(std::to_string(record.timestamp - baseTimestamp))));
|
||||
jsonRecord.insert(std::make_pair("c", json11::Json(record.record.isConnected ? 1 : 0)));
|
||||
if (record.record.route) {
|
||||
jsonRecord.insert(std::make_pair("local", json11::Json(record.record.route->localDescription)));
|
||||
|
@ -1467,7 +1464,7 @@ public:
|
|||
if (record.record.connection) {
|
||||
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;
|
||||
|
||||
jsonCandidate.insert(std::make_pair("type", json11::Json(candidate.type)));
|
||||
|
@ -1554,7 +1551,7 @@ private:
|
|||
EncryptionKey _encryptionKey;
|
||||
std::function<void(State)> _stateUpdated;
|
||||
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(AudioState, VideoState)> _remoteMediaStateUpdated;
|
||||
std::function<void(float)> _remotePrefferedAspectRatioUpdated;
|
||||
|
@ -1567,7 +1564,7 @@ private:
|
|||
|
||||
bool _isConnected = false;
|
||||
bool _isFailed = false;
|
||||
absl::optional<NativeNetworkingImpl::ConnectionDescription> _currentConnectionDescription;
|
||||
absl::optional<InstanceNetworking::ConnectionDescription> _currentConnectionDescription;
|
||||
|
||||
absl::optional<NetworkStateLogRecord> _currentNetworkStateLogRecord;
|
||||
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->setIncomingVideoOutput(sink);
|
||||
});
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
bool supportsVideo() override {
|
||||
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 setEchoCancellationStrength(int strength) override;
|
||||
void setAudioInputDevice(std::string id) override;
|
||||
|
|
|
@ -186,8 +186,8 @@ private:
|
|||
|
||||
}
|
||||
|
||||
NativeNetworkingImpl::ConnectionDescription::CandidateDescription NativeNetworkingImpl::connectionDescriptionFromCandidate(cricket::Candidate const &candidate) {
|
||||
NativeNetworkingImpl::ConnectionDescription::CandidateDescription result;
|
||||
InstanceNetworking::ConnectionDescription::CandidateDescription InstanceNetworking::connectionDescriptionFromCandidate(cricket::Candidate const &candidate) {
|
||||
InstanceNetworking::ConnectionDescription::CandidateDescription result;
|
||||
|
||||
result.type = candidate.type();
|
||||
result.protocol = candidate.protocol();
|
||||
|
@ -569,8 +569,8 @@ void NativeNetworkingImpl::transportRouteChanged(absl::optional<rtc::NetworkRout
|
|||
void NativeNetworkingImpl::candidatePairChanged(cricket::CandidatePairChangeEvent const &event) {
|
||||
ConnectionDescription connectionDescription;
|
||||
|
||||
connectionDescription.local = connectionDescriptionFromCandidate(event.selected_candidate_pair.local);
|
||||
connectionDescription.remote = connectionDescriptionFromCandidate(event.selected_candidate_pair.remote);
|
||||
connectionDescription.local = InstanceNetworking::connectionDescriptionFromCandidate(event.selected_candidate_pair.local);
|
||||
connectionDescription.remote = InstanceNetworking::connectionDescriptionFromCandidate(event.selected_candidate_pair.remote);
|
||||
|
||||
if (!_currentConnectionDescription || _currentConnectionDescription.value() != connectionDescription) {
|
||||
_currentConnectionDescription = std::move(connectionDescription);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "InstanceNetworking.h"
|
||||
#include "Message.h"
|
||||
#include "ThreadLocalObject.h"
|
||||
#include "Instance.h"
|
||||
|
@ -50,117 +51,24 @@ struct Message;
|
|||
class SctpDataChannelProviderInterfaceImpl;
|
||||
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:
|
||||
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 ConnectionDescription::CandidateDescription connectionDescriptionFromCandidate(cricket::Candidate const &candidate);
|
||||
|
||||
NativeNetworkingImpl(Configuration &&configuration);
|
||||
~NativeNetworkingImpl();
|
||||
virtual ~NativeNetworkingImpl();
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
virtual void start() override;
|
||||
virtual void stop() override;
|
||||
|
||||
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);
|
||||
virtual PeerIceParameters getLocalIceParameters() override;
|
||||
virtual std::unique_ptr<rtc::SSLFingerprint> getLocalFingerprint() override;
|
||||
virtual void setRemoteParams(PeerIceParameters const &remoteIceParameters, rtc::SSLFingerprint *fingerprint, std::string const &sslSetup) override;
|
||||
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:
|
||||
void resetDtlsSrtpTransport();
|
||||
|
@ -191,7 +99,7 @@ private:
|
|||
std::vector<RtcServer> _rtcServers;
|
||||
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(rtc::CopyOnWriteBuffer const &, bool)> _transportMessageReceived;
|
||||
std::function<void(rtc::CopyOnWriteBuffer const &, int64_t)> _rtcpPacketReceived;
|
||||
|
|
|
@ -11,9 +11,7 @@ namespace tgcalls {
|
|||
namespace signaling {
|
||||
|
||||
static std::string uint32ToString(uint32_t value) {
|
||||
std::ostringstream stringStream;
|
||||
stringStream << value;
|
||||
return stringStream.str();
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
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 object;
|
||||
|
||||
|
||||
std::string mappedType;
|
||||
switch (mediaContent.type) {
|
||||
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) {
|
||||
MediaContent result;
|
||||
|
||||
|
||||
const auto type = object.find("type");
|
||||
if (type == object.end() || !type->second.is_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) {
|
||||
json11::Json::object object;
|
||||
|
||||
|
||||
object.insert(std::make_pair("@type", json11::Json("InitialSetup")));
|
||||
object.insert(std::make_pair("ufrag", json11::Json(message->ufrag)));
|
||||
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";
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
|
||||
DtlsFingerprint parsedFingerprint;
|
||||
parsedFingerprint.hash = hash->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) {
|
||||
json11::Json::object object;
|
||||
|
||||
|
||||
object.insert(std::make_pair("@type", json11::Json("NegotiateChannels")));
|
||||
|
||||
|
||||
object.insert(std::make_pair("exchangeId", json11::Json(uint32ToString(message->exchangeId))));
|
||||
|
||||
|
||||
json11::Json::array contents;
|
||||
for (const auto &content : message->contents) {
|
||||
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) {
|
||||
NegotiateChannelsMessage message;
|
||||
|
||||
|
||||
const auto exchangeId = object.find("exchangeId");
|
||||
|
||||
|
||||
if (exchangeId == object.end()) {
|
||||
RTC_LOG(LS_ERROR) << "Signaling: exchangeId must be present";
|
||||
return absl::nullopt;
|
||||
|
|
|
@ -57,9 +57,7 @@ namespace tgcalls {
|
|||
namespace {
|
||||
|
||||
static std::string intToString(int value) {
|
||||
std::ostringstream stringStream;
|
||||
stringStream << value;
|
||||
return stringStream.str();
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
static VideoCaptureInterfaceObject *GetVideoCaptureAssumingSameThread(VideoCaptureInterface *videoCapture) {
|
||||
|
@ -1214,7 +1212,7 @@ public:
|
|||
_encryptionKey(std::move(descriptor.encryptionKey)),
|
||||
_stateUpdated(descriptor.stateUpdated),
|
||||
_signalBarsUpdated(descriptor.signalBarsUpdated),
|
||||
_audioLevelsUpdated(descriptor.audioLevelsUpdated),
|
||||
_audioLevelUpdated(descriptor.audioLevelsUpdated),
|
||||
_remoteBatteryLevelIsLowUpdated(descriptor.remoteBatteryLevelIsLowUpdated),
|
||||
_remoteMediaStateUpdated(descriptor.remoteMediaStateUpdated),
|
||||
_remotePrefferedAspectRatioUpdated(descriptor.remotePrefferedAspectRatioUpdated),
|
||||
|
@ -1253,14 +1251,15 @@ public:
|
|||
void start() {
|
||||
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]() {
|
||||
return new NativeNetworkingImpl(NativeNetworkingImpl::Configuration{
|
||||
_networking.reset(new ThreadLocalObject<NativeNetworkingImpl>(_threads->getNetworkThread(), [weak, threads = _threads, encryptionKey = _encryptionKey, isOutgoing = _encryptionKey.isOutgoing, rtcServers = _rtcServers, enableP2P = _enableP2P]() {
|
||||
return new NativeNetworkingImpl(InstanceNetworking::Configuration{
|
||||
.encryptionKey = encryptionKey,
|
||||
.isOutgoing = isOutgoing,
|
||||
.enableStunMarking = false,
|
||||
.enableTCP = false,
|
||||
.enableP2P = enableP2P,
|
||||
.rtcServers = rtcServers,
|
||||
.stateUpdated = [threads, weak](const NativeNetworkingImpl::State &state) {
|
||||
.stateUpdated = [threads, weak](const InstanceNetworking::State &state) {
|
||||
threads->getMediaThread()->PostTask([=] {
|
||||
const auto strong = weak.lock();
|
||||
if (!strong) {
|
||||
|
@ -1335,6 +1334,10 @@ public:
|
|||
|
||||
mediaDeps.adm = _audioDeviceModule;
|
||||
|
||||
webrtc:: AudioProcessingBuilder builder;
|
||||
mediaDeps.audio_processing = builder.Create();
|
||||
|
||||
|
||||
_availableVideoFormats = mediaDeps.video_encoder_factory->GetSupportedFormats();
|
||||
|
||||
std::unique_ptr<cricket::MediaEngineInterface> mediaEngine = cricket::CreateMediaEngine(std::move(mediaDeps));
|
||||
|
@ -1474,6 +1477,7 @@ public:
|
|||
_negotiatedOutgoingAudioContent.value(),
|
||||
_threads
|
||||
));
|
||||
_outgoingAudioChannel->setIsMuted(_isMicrophoneMuted);
|
||||
}
|
||||
|
||||
adjustBitratePreferences(true);
|
||||
|
@ -1838,7 +1842,7 @@ public:
|
|||
_pendingIceCandidates.clear();
|
||||
}
|
||||
|
||||
void onNetworkStateUpdated(NativeNetworkingImpl::State const &state) {
|
||||
void onNetworkStateUpdated(InstanceNetworking::State const &state) {
|
||||
State mappedState;
|
||||
if (state.isReadyToSendData) {
|
||||
mappedState = State::Established;
|
||||
|
@ -2078,7 +2082,7 @@ private:
|
|||
EncryptionKey _encryptionKey;
|
||||
std::function<void(State)> _stateUpdated;
|
||||
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(AudioState, VideoState)> _remoteMediaStateUpdated;
|
||||
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->setIncomingVideoOutput(sink);
|
||||
});
|
||||
|
|
|
@ -27,7 +27,7 @@ public:
|
|||
bool supportsVideo() override {
|
||||
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 setEchoCancellationStrength(int strength) override;
|
||||
void setAudioInputDevice(std::string id) override;
|
||||
|
|
|
@ -10,9 +10,7 @@ namespace tgcalls {
|
|||
namespace signaling_4_0_0 {
|
||||
|
||||
static std::string uint32ToString(uint32_t value) {
|
||||
std::ostringstream stringStream;
|
||||
stringStream << value;
|
||||
return stringStream.str();
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
static uint32_t stringToUInt32(std::string const &string) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
chat_BlurAlpha=-1056964608
|
||||
chat_unreadMessagesStartText=-14512671
|
||||
chat_inFileBackgroundSelected=-2823172
|
||||
radioBackgroundChecked=-14638337
|
||||
|
@ -74,6 +75,7 @@ chat_inBubbleShadow=1981626195
|
|||
chat_outAudioProgress=-10907938
|
||||
player_progress=-14441474
|
||||
chat_inReplyLine=-12478487
|
||||
chat_inQuote=-12478487
|
||||
dialogLineProgressBackground=-3152133
|
||||
chat_inReplyNameText=-13464859
|
||||
chat_outAudioPerfomerSelectedText=-1
|
||||
|
|
|
@ -43,6 +43,7 @@ chat_inLoaderSelected=-12277262
|
|||
chat_outLocationIcon=-2105761599
|
||||
chat_outAudioProgress=-6239505
|
||||
chat_inReplyLine=-348807706
|
||||
chat_inQuote=-348807706
|
||||
dialogLineProgressBackground=-2035723
|
||||
chat_inReplyNameText=-14643754
|
||||
chats_onlineCircle=-13192972
|
||||
|
|
BIN
TMessagesProj/src/main/assets/codelng.gzip
Normal file
BIN
TMessagesProj/src/main/assets/codelng.gzip
Normal file
Binary file not shown.
|
@ -22,7 +22,7 @@ chat_attachAudioBackground=-626837
|
|||
location_sendLocationBackground=-9919529
|
||||
actionBarDefaultSubmenuBackground=-14075831
|
||||
switchTrackBlueThumb=-14866637
|
||||
avatar_nameInMessageViolet=-6643205
|
||||
avatar_nameInMessageViolet=-7565846
|
||||
emptyListPlaceholder=-8549479
|
||||
chat_inAudioSelectedProgress=-1
|
||||
chats_nameMessage=-1446156
|
||||
|
@ -118,6 +118,7 @@ chat_outAudioProgress=-1
|
|||
stickers_menu=-10719105
|
||||
player_progress=-10177041
|
||||
chat_inReplyLine=-8796932
|
||||
chat_inQuote=-8796932
|
||||
chat_inAudioPerfomerSelectedText=-7490861
|
||||
dialogBackground=-14602949
|
||||
dialogLineProgressBackground=-13548718
|
||||
|
@ -138,10 +139,10 @@ chat_messagePanelSend=-10177041
|
|||
passport_authorizeBackground=-12352582
|
||||
chat_inSentClock=-10653824
|
||||
chats_menuTopShadow=789516
|
||||
avatar_nameInMessageRed=-21124
|
||||
avatar_nameInMessageRed=-1537928
|
||||
chat_botSwitchToInlineText=-8796932
|
||||
chats_nameMessageArchived=-8549479
|
||||
avatar_nameInMessageOrange=-13984
|
||||
avatar_nameInMessageOrange=-1528998
|
||||
chats_pinnedIcon=-10982016
|
||||
chat_attachActiveTab=-9781249
|
||||
chat_replyPanelLine=1578572317
|
||||
|
@ -166,12 +167,12 @@ avatar_background2Red=-2013369
|
|||
chat_emojiPanelBadgeBackground=-11291403
|
||||
chat_inForwardedNameText=-8796932
|
||||
chats_actionBackground=-10509346
|
||||
avatar_nameInMessageGreen=-7018619
|
||||
avatar_nameInMessageGreen=-8531855
|
||||
chat_outContactNameText=-1
|
||||
chat_inSiteNameText=-8796932
|
||||
chat_linkSelectBackground=1516415459
|
||||
windowBackgroundWhiteBlueText=-10177041
|
||||
avatar_nameInMessageCyan=-10623523
|
||||
avatar_nameInMessageCyan=-11285306
|
||||
chat_inLocationBackground=-13417903
|
||||
radioBackground=-1635939431
|
||||
profile_tabText=2027746559
|
||||
|
@ -278,12 +279,12 @@ key_chat_messagePanelVoiceLockBackground=-13548712
|
|||
chat_outFileNameText=-1
|
||||
picker_enabledButton=-9781249
|
||||
inappPlayerBackground=-14602949
|
||||
avatar_nameInMessagePink=-624741
|
||||
avatar_nameInMessagePink=-1543014
|
||||
windowBackgroundWhiteGrayText=-8549479
|
||||
statisticChartSignature=-1214008894
|
||||
actionBarDefaultSubmenuItemIcon=1859974399
|
||||
chat_attachPollBackground=-2183099
|
||||
avatar_nameInMessageBlue=-8796932
|
||||
avatar_nameInMessageBlue=-10703382
|
||||
dialogTextBlack=-592138
|
||||
actionBarDefault=-14406343
|
||||
location_placeLocationBackground=-9919529
|
||||
|
@ -425,4 +426,7 @@ chat_topPanelBackground=-14602949
|
|||
chat_outSentClock=-8213557
|
||||
dialogBackgroundGray=-14932431
|
||||
chat_searchPanelText=-8796932
|
||||
chat_inContactIcon=-1
|
||||
chat_inContactIcon=-1
|
||||
code_comment=-2130706433
|
||||
chat_outCodeBackground=857487708
|
||||
chat_inCodeBackground=856033549
|
|
@ -79,6 +79,7 @@ chat_outAudioProgress=-12155183
|
|||
player_progress=-14574092
|
||||
chat_stickerReplyMessageText=-7565679
|
||||
chat_inReplyLine=-12676640
|
||||
chat_inQuote=-12676640
|
||||
dialogLineProgressBackground=-3348999
|
||||
chat_inReplyNameText=-13531425
|
||||
chat_outAudioPerfomerSelectedText=-1
|
||||
|
|
|
@ -22,7 +22,7 @@ chat_attachAudioBackground=-626837
|
|||
location_sendLocationBackground=-9919529
|
||||
actionBarDefaultSubmenuBackground=-14145495
|
||||
switchTrackBlueThumb=-14803424
|
||||
avatar_nameInMessageViolet=-6643205
|
||||
avatar_nameInMessageViolet=-7565846
|
||||
emptyListPlaceholder=-8553091
|
||||
chat_inAudioSelectedProgress=-1
|
||||
chats_nameMessage=-1315861
|
||||
|
@ -121,6 +121,7 @@ chat_outAudioProgress=-1
|
|||
stickers_menu=-10724260
|
||||
player_progress=-11292689
|
||||
chat_inReplyLine=-8796932
|
||||
chat_inQuote=-8796932
|
||||
chat_inAudioPerfomerSelectedText=-7490861
|
||||
dialogBackground=-14803426
|
||||
dialogLineProgressBackground=-12303292
|
||||
|
@ -146,10 +147,10 @@ chats_sentReadCheck=-12145165
|
|||
passport_authorizeBackground=-12352582
|
||||
chat_inSentClock=-11050909
|
||||
chats_menuTopShadow=789516
|
||||
avatar_nameInMessageRed=-21124
|
||||
avatar_nameInMessageRed=-1537928
|
||||
chat_botSwitchToInlineText=-8796932
|
||||
chats_nameMessageArchived=-8553091
|
||||
avatar_nameInMessageOrange=-13984
|
||||
avatar_nameInMessageOrange=-1528998
|
||||
chats_pinnedIcon=-10197916
|
||||
chat_attachActiveTab=-9781249
|
||||
chat_replyPanelLine=838860800
|
||||
|
@ -176,14 +177,14 @@ avatar_background2Red=-2530993
|
|||
chat_emojiPanelBadgeBackground=-11291403
|
||||
chat_inForwardedNameText=-8930052
|
||||
chats_actionBackground=-10575653
|
||||
avatar_nameInMessageGreen=-7018619
|
||||
avatar_nameInMessageGreen=-8531855
|
||||
chat_outContactNameText=-1
|
||||
chat_TextSelectionCursor=-11028499
|
||||
chat_inSiteNameText=-8796932
|
||||
chat_linkSelectBackground=1516415459
|
||||
chats_archivePullDownBackground=-14145496
|
||||
windowBackgroundWhiteBlueText=-10177041
|
||||
avatar_nameInMessageCyan=-10623523
|
||||
avatar_nameInMessageCyan=-11285306
|
||||
chat_inLocationBackground=-12829636
|
||||
radioBackground=-10461088
|
||||
profile_tabText=2030043135
|
||||
|
@ -296,12 +297,12 @@ key_chat_messagePanelVoiceLockBackground=-14606046
|
|||
chat_outFileNameText=-1
|
||||
picker_enabledButton=-9781249
|
||||
inappPlayerBackground=-15066597
|
||||
avatar_nameInMessagePink=-624741
|
||||
avatar_nameInMessagePink=-1543014
|
||||
windowBackgroundWhiteGrayText=1862270975
|
||||
statisticChartSignature=-1214008894
|
||||
actionBarDefaultSubmenuItemIcon=2029911551
|
||||
chat_attachPollBackground=-2183099
|
||||
avatar_nameInMessageBlue=-8796932
|
||||
avatar_nameInMessageBlue=-10703382
|
||||
dialogTextBlack=-592138
|
||||
actionBarDefault=-14474458
|
||||
location_placeLocationBackground=-9919529
|
||||
|
@ -451,3 +452,6 @@ chat_outSentClock=-6698513
|
|||
dialogBackgroundGray=-14013910
|
||||
chat_searchPanelText=-10767620
|
||||
chat_inContactIcon=-1
|
||||
code_comment=-2130706433
|
||||
chat_outCodeBackground=859062986
|
||||
chat_inCodeBackground=855638016
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
package org.telegram.PhoneFormat;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.telegram.messenger.ApplicationLoader;
|
||||
import org.telegram.messenger.FileLog;
|
||||
|
||||
|
@ -181,6 +183,7 @@ public class PhoneFormat {
|
|||
return res;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String format(String orig) {
|
||||
if (!initialzed) {
|
||||
return orig;
|
||||
|
|
|
@ -209,6 +209,7 @@ public class AndroidUtilities {
|
|||
|
||||
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_LINKBOLD = 2;
|
||||
|
||||
public final static String TYPEFACE_ROBOTO_MEDIUM = "fonts/rmedium.ttf";
|
||||
public final static String TYPEFACE_ROBOTO_MEDIUM_ITALIC = "fonts/rmediumitalic.ttf";
|
||||
|
@ -453,6 +454,10 @@ public class AndroidUtilities {
|
|||
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) {
|
||||
return replaceSingleTag(str, -1, 0, runnable);
|
||||
}
|
||||
|
@ -473,7 +478,7 @@ public class AndroidUtilities {
|
|||
}
|
||||
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(str);
|
||||
if (index >= 0) {
|
||||
if (type == REPLACING_TAG_TYPE_LINK) {
|
||||
if (type == REPLACING_TAG_TYPE_LINK || type == REPLACING_TAG_TYPE_LINKBOLD) {
|
||||
spannableStringBuilder.setSpan(new ClickableSpan() {
|
||||
|
||||
@Override
|
||||
|
@ -483,6 +488,9 @@ public class AndroidUtilities {
|
|||
if (colorKey >= 0) {
|
||||
ds.setColor(Theme.getColor(colorKey, resourcesProvider));
|
||||
}
|
||||
if (type == REPLACING_TAG_TYPE_LINKBOLD) {
|
||||
ds.setTypeface(AndroidUtilities.getTypeface(AndroidUtilities.TYPEFACE_ROBOTO_MEDIUM));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -593,8 +601,11 @@ public class AndroidUtilities {
|
|||
if (child.getVisibility() != View.VISIBLE || child instanceof PeerStoriesView && child != onlyThisView) {
|
||||
continue;
|
||||
}
|
||||
if (child instanceof StoryMediaAreasView.AreaView && !((StoryMediaAreasView) container).hasSelected() && (x < dp(60) || x > container.getWidth() - dp(60))) {
|
||||
continue;
|
||||
if (child instanceof StoryMediaAreasView.AreaView) {
|
||||
StoryMediaAreasView areasView = (StoryMediaAreasView) container;
|
||||
if (!(areasView.hasSelected() && (x < dp(60) || x > container.getWidth() - dp(60))) && !areasView.hasAreaAboveAt(x, y)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
child.getHitRect(AndroidUtilities.rectTmp2);
|
||||
if (AndroidUtilities.rectTmp2.contains((int) x, (int) y) && child.isClickable()) {
|
||||
|
@ -3454,7 +3465,7 @@ public class AndroidUtilities {
|
|||
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) {
|
||||
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) {
|
||||
if (window != null) {
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
RandomAccessFile reader = null;
|
||||
try {
|
||||
|
|
|
@ -27,6 +27,7 @@ import android.os.Handler;
|
|||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.multidex.MultiDex;
|
||||
|
@ -38,6 +39,8 @@ import org.telegram.messenger.voip.VideoCapturerDevice;
|
|||
import org.telegram.tgnet.ConnectionsManager;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
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 java.io.File;
|
||||
|
@ -123,10 +126,18 @@ public class ApplicationLoader extends Application {
|
|||
return applicationLoaderInstance.isHuaweiBuild();
|
||||
}
|
||||
|
||||
public static boolean isStandaloneBuild() {
|
||||
return applicationLoaderInstance.isStandalone();
|
||||
}
|
||||
|
||||
protected boolean isHuaweiBuild() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isStandalone() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static File getFilesDirFixed() {
|
||||
for (int a = 0; a < 10; a++) {
|
||||
File path = ApplicationLoader.applicationContext.getFilesDir();
|
||||
|
@ -562,4 +573,13 @@ public class ApplicationLoader extends Application {
|
|||
public boolean openApkInstall(Activity activity, TLRPC.Document document) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
|
|||
private String lastPremiumTransaction;
|
||||
private String lastPremiumToken;
|
||||
private boolean isDisconnected;
|
||||
private Runnable onCanceled;
|
||||
|
||||
public static BillingController getInstance() {
|
||||
if (instance == null) {
|
||||
|
@ -74,6 +75,10 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
|
|||
.build();
|
||||
}
|
||||
|
||||
public void setOnCanceled(Runnable onCanceled) {
|
||||
this.onCanceled = onCanceled;
|
||||
}
|
||||
|
||||
public String getLastPremiumTransaction() {
|
||||
return lastPremiumTransaction;
|
||||
}
|
||||
|
@ -177,7 +182,7 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
|
|||
if (purchase.getProducts().contains(productId)) {
|
||||
productsToBeConsumed.incrementAndGet();
|
||||
billingClient.consumeAsync(ConsumeParams.newBuilder()
|
||||
.setPurchaseToken(purchase.getPurchaseToken())
|
||||
.setPurchaseToken(purchase.getPurchaseToken())
|
||||
.build(), (billingResult1, s) -> {
|
||||
if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK) {
|
||||
productsConsumed.add(productId);
|
||||
|
@ -228,6 +233,10 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
|
|||
if (billing.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
|
||||
PremiumPreviewFragment.sentPremiumBuyCanceled();
|
||||
}
|
||||
if (onCanceled != null) {
|
||||
onCanceled.run();
|
||||
onCanceled = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (list == null || list.isEmpty()) {
|
||||
|
@ -269,7 +278,10 @@ public class BillingController implements PurchasesUpdatedListener, BillingClien
|
|||
|
||||
consumeGiftPurchase(purchase, req.purpose);
|
||||
} 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);
|
||||
}
|
||||
}, 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.
|
||||
*/
|
||||
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(
|
||||
ConsumeParams.newBuilder()
|
||||
.setPurchaseToken(purchase.getPurchaseToken())
|
||||
|
|
|
@ -24,14 +24,13 @@ public class BuildVars {
|
|||
public static boolean USE_CLOUD_STRINGS = true;
|
||||
public static boolean CHECK_UPDATES = true;
|
||||
public static boolean NO_SCOPED_STORAGE = Build.VERSION.SDK_INT <= 29;
|
||||
public static int BUILD_VERSION = 3926;
|
||||
public static String BUILD_VERSION_STRING = "10.1.1";
|
||||
public static int BUILD_VERSION = 4056;
|
||||
public static String BUILD_VERSION_STRING = "10.2.0";
|
||||
public static int APP_ID = 4;
|
||||
public static String APP_HASH = "014b35b6184100b085b0d0572f9b5103";
|
||||
|
||||
// SafetyNet key for Google Identity SDK, set it to empty to disable
|
||||
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 HUAWEI_STORE_URL = "https://appgallery.huawei.com/app/C101184875";
|
||||
public static String GOOGLE_AUTH_CLIENT_ID = "760348033671-81kmi3pi84p11ub8hp9a1funsv0rn2p9.apps.googleusercontent.com";
|
||||
|
@ -49,7 +48,7 @@ public class BuildVars {
|
|||
}
|
||||
|
||||
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() {
|
||||
|
@ -68,14 +67,6 @@ public class BuildVars {
|
|||
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;
|
||||
public static boolean isBetaApp() {
|
||||
if (betaApp == null) {
|
||||
|
@ -88,4 +79,8 @@ public class BuildVars {
|
|||
public static boolean isHuaweiStoreApp() {
|
||||
return ApplicationLoader.isHuaweiStoreBuild();
|
||||
}
|
||||
|
||||
public static String getSmsHash() {
|
||||
return ApplicationLoader.isStandaloneBuild() ? "w0lkcmTZkKh" : (DEBUG_VERSION ? "O2P2z+/jBpJ" : "oLeq9AcOZkT");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,13 @@ import com.google.android.exoplayer2.util.Consumer;
|
|||
|
||||
import org.telegram.tgnet.ConnectionsManager;
|
||||
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.Premium.boosts.BoostRepository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ChannelBoostsController {
|
||||
|
||||
|
@ -23,86 +28,123 @@ public class ChannelBoostsController {
|
|||
}
|
||||
|
||||
|
||||
public void getBoostsStats(long dialogId, Consumer<TLRPC.TL_stories_boostsStatus> consumer) {
|
||||
TLRPC.TL_stories_getBoostsStatus req = new TLRPC.TL_stories_getBoostsStatus();
|
||||
public void getBoostsStats(long dialogId, Consumer<TL_stories.TL_premium_boostsStatus> consumer) {
|
||||
TL_stories.TL_premium_getBoostsStatus req = new TL_stories.TL_premium_getBoostsStatus();
|
||||
req.peer = messagesController.getInputPeer(dialogId);
|
||||
connectionsManager.sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
|
||||
if (response != null) {
|
||||
consumer.accept((TLRPC.TL_stories_boostsStatus) response);
|
||||
consumer.accept((TL_stories.TL_premium_boostsStatus) response);
|
||||
} else {
|
||||
BulletinFactory.showForError(error);
|
||||
consumer.accept(null);
|
||||
}
|
||||
}));
|
||||
|
||||
}
|
||||
|
||||
public void userCanBoostChannel(long dialogId, Consumer<CanApplyBoost> consumer) {
|
||||
TLRPC.TL_stories_canApplyBoost req = new TLRPC.TL_stories_canApplyBoost();
|
||||
req.peer = messagesController.getInputPeer(dialogId);
|
||||
connectionsManager.sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
|
||||
CanApplyBoost canApplyBoost = new CanApplyBoost();
|
||||
if (response != null) {
|
||||
canApplyBoost.canApply = true;
|
||||
if (response instanceof TLRPC.TL_stories_canApplyBoostReplace) {
|
||||
TLRPC.TL_stories_canApplyBoostReplace canApplyBoostReplace = (TLRPC.TL_stories_canApplyBoostReplace) response;
|
||||
messagesController.putChats(canApplyBoostReplace.chats, false);
|
||||
canApplyBoost.replaceDialogId = DialogObject.getPeerDialogId(canApplyBoostReplace.current_boost);
|
||||
if (canApplyBoost.replaceDialogId == 0 && canApplyBoostReplace.chats.size() > 0) {
|
||||
canApplyBoost.replaceDialogId = -canApplyBoostReplace.chats.get(0).id;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void userCanBoostChannel(long dialogId, TL_stories.TL_premium_boostsStatus boostsStatus, Consumer<CanApplyBoost> consumer) {
|
||||
CanApplyBoost canApplyBoost = new CanApplyBoost();
|
||||
canApplyBoost.currentPeer = messagesController.getPeer(dialogId);
|
||||
canApplyBoost.currentDialogId = dialogId;
|
||||
canApplyBoost.currentChat = messagesController.getChat(-dialogId);
|
||||
BoostRepository.getMyBoosts(myBoosts -> {
|
||||
canApplyBoost.isMaxLvl = boostsStatus.next_level_boosts <= 0;
|
||||
canApplyBoost.setMyBoosts(myBoosts);
|
||||
consumer.accept(canApplyBoost);
|
||||
}, error -> {
|
||||
if (error.text.startsWith("FLOOD_WAIT")) {
|
||||
canApplyBoost.floodWait = Utilities.parseInt(error.text);
|
||||
} else if (error.text.startsWith("BOOSTS_EMPTY")) {
|
||||
canApplyBoost.empty = true;
|
||||
}
|
||||
canApplyBoost.canApply = false;
|
||||
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) {
|
||||
int count = 0;
|
||||
if (level >= 1) {
|
||||
count += BOOSTS_FOR_LEVEL_1;
|
||||
}
|
||||
if (level >= 2) {
|
||||
count += BOOSTS_FOR_LEVEL_2;
|
||||
}
|
||||
return count;
|
||||
public void applyBoost(long dialogId, int slot, Utilities.Callback<TL_stories.TL_premium_myBoosts> onDone, Utilities.Callback<TLRPC.TL_error> onError) {
|
||||
BoostRepository.applyBoost(-dialogId, Arrays.asList(slot), onDone, onError);
|
||||
}
|
||||
|
||||
public static class CanApplyBoost {
|
||||
public boolean canApply;
|
||||
public boolean empty;
|
||||
public long replaceDialogId;
|
||||
|
||||
public boolean alreadyActive;
|
||||
public boolean needSelector;
|
||||
public int floodWait;
|
||||
public boolean giftedPremium;
|
||||
private long lastCheckTime;
|
||||
public int slot;
|
||||
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() {
|
||||
floodWait -= (System.currentTimeMillis() - lastCheckTime) / 1000;
|
||||
lastCheckTime = System.currentTimeMillis();
|
||||
if (floodWait < 0) {
|
||||
floodWait = 0;
|
||||
public void setMyBoosts(TL_stories.TL_premium_myBoosts myBoosts) {
|
||||
this.myBoosts = myBoosts;
|
||||
boostCount = 0;
|
||||
slot = 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;
|
||||
}
|
||||
if (isMaxLvl) {
|
||||
canApply = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
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.tl.TL_stories;
|
||||
import org.telegram.ui.ChatActivity;
|
||||
import org.telegram.ui.Stories.StoriesStorage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
public class ChatMessagesMetadataController {
|
||||
|
||||
|
@ -53,8 +47,8 @@ public class ChatMessagesMetadataController {
|
|||
extendedMediaToCheck.add(messageObject);
|
||||
}
|
||||
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;
|
||||
if (storyItem == null || storyItem instanceof TLRPC.TL_storyItemDeleted) {
|
||||
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 TL_stories.TL_storyItemDeleted) {
|
||||
continue;
|
||||
}
|
||||
if (currentTime - storyItem.lastUpdateTime > 1000 * 5 * 60) {
|
||||
|
@ -74,9 +68,9 @@ public class ChatMessagesMetadataController {
|
|||
return;
|
||||
}
|
||||
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);
|
||||
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) {
|
||||
storyItem = messageObject.messageOwner.media.storyItem;
|
||||
storyItem.dialogId = messageObject.messageOwner.media.user_id;
|
||||
|
@ -91,18 +85,18 @@ public class ChatMessagesMetadataController {
|
|||
req.id.add(storyItem.id);
|
||||
int storyId = storyItem.id;
|
||||
int reqId = chatActivity.getConnectionsManager().sendRequest(req, (response, error) -> {
|
||||
TLRPC.StoryItem newStoryItem = null;
|
||||
TL_stories.StoryItem newStoryItem = 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) {
|
||||
newStoryItem = stories.stories.get(0);
|
||||
}
|
||||
if (newStoryItem == null) {
|
||||
newStoryItem = new TLRPC.TL_storyItemDeleted();
|
||||
newStoryItem = new TL_stories.TL_storyItemDeleted();
|
||||
}
|
||||
newStoryItem.lastUpdateTime = System.currentTimeMillis();
|
||||
newStoryItem.id = storyId;
|
||||
TLRPC.StoryItem finalNewStoryItem = newStoryItem;
|
||||
TL_stories.StoryItem finalNewStoryItem = newStoryItem;
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
boolean wasExpired = messageObject.isExpiredStory();
|
||||
StoriesStorage.applyStory(chatActivity.getCurrentAccount(), storyDialogId, messageObject, finalNewStoryItem);
|
||||
|
@ -137,7 +131,8 @@ public class ChatMessagesMetadataController {
|
|||
MessageObject messageObject = visibleObjects.get(i);
|
||||
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) {
|
||||
TLRPC.Updates updates = (TLRPC.Updates) response;
|
||||
for (int i = 0; i < updates.updates.size(); i++) {
|
||||
|
@ -147,9 +142,12 @@ public class ChatMessagesMetadataController {
|
|||
}
|
||||
chatActivity.getMessagesController().processUpdates(updates, false);
|
||||
}
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
reactionsRequests.remove((Object) reqId[0]);
|
||||
});
|
||||
});
|
||||
reactionsRequests.add(reqId);
|
||||
if (reactionsRequests.size() > 5) {
|
||||
reactionsRequests.add(reqId[0]);
|
||||
while (reactionsRequests.size() > 4) {
|
||||
chatActivity.getConnectionsManager().cancelRequest(reactionsRequests.remove(0), false);
|
||||
}
|
||||
}
|
||||
|
@ -164,13 +162,17 @@ public class ChatMessagesMetadataController {
|
|||
MessageObject messageObject = visibleObjects.get(i);
|
||||
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) {
|
||||
chatActivity.getMessagesController().processUpdates((TLRPC.Updates) response, false);
|
||||
}
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
extendedMediaRequests.remove((Object) reqId[0]);
|
||||
});
|
||||
});
|
||||
extendedMediaRequests.add(reqId);
|
||||
if (extendedMediaRequests.size() > 10) {
|
||||
extendedMediaRequests.add(reqId[0]);
|
||||
while (extendedMediaRequests.size() > 10) {
|
||||
chatActivity.getConnectionsManager().cancelRequest(extendedMediaRequests.remove(0), false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ import android.provider.ContactsContract;
|
|||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.collection.LongSparseArray;
|
||||
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
|
@ -2214,7 +2215,7 @@ public class ContactsController extends BaseController {
|
|||
if (systemAccount == null || user == null) {
|
||||
return -1;
|
||||
}
|
||||
if (!hasContactsPermission()) {
|
||||
if (!hasContactsWritePermission()) {
|
||||
return -1;
|
||||
}
|
||||
long res = -1;
|
||||
|
@ -2239,8 +2240,8 @@ public class ContactsController extends BaseController {
|
|||
if (result != null && result.length > 0 && result[0].uri != null) {
|
||||
res = Long.parseLong(result[0].uri.getLastPathSegment());
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
|
||||
} catch (Exception e) {
|
||||
FileLog.e(e);
|
||||
}
|
||||
synchronized (observerLock) {
|
||||
ignoreChanges = false;
|
||||
|
@ -2863,6 +2864,7 @@ public class ContactsController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String formatName(TLRPC.User user) {
|
||||
if (user == null) {
|
||||
return "";
|
||||
|
@ -2870,10 +2872,12 @@ public class ContactsController extends BaseController {
|
|||
return formatName(user.first_name, user.last_name, 0);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String formatName(String firstName, String lastName) {
|
||||
return formatName(firstName, lastName, 0);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String formatName(String firstName, String lastName, int maxLength) {
|
||||
/*if ((firstName == null || firstName.length() == 0) && (lastName == null || lastName.length() == 0)) {
|
||||
return LocaleController.getString("HiddenName", R.string.HiddenName);
|
||||
|
|
|
@ -1376,6 +1376,14 @@ public class DatabaseMigrationHelper {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -535,14 +535,14 @@ public class Emoji {
|
|||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,15 +8,12 @@
|
|||
|
||||
package org.telegram.messenger;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
|
||||
import org.telegram.messenger.utils.ImmutableByteArrayOutputStream;
|
||||
import org.telegram.tgnet.ConnectionsManager;
|
||||
import org.telegram.tgnet.NativeByteBuffer;
|
||||
import org.telegram.tgnet.TLObject;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.tgnet.tl.TL_stories;
|
||||
import org.telegram.ui.LaunchActivity;
|
||||
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) {
|
||||
startTime = System.currentTimeMillis();
|
||||
updateParams();
|
||||
isStory = parentObject instanceof TLRPC.TL_storyItem;
|
||||
isStory = parentObject instanceof TL_stories.TL_storyItem;
|
||||
if (currentDownloadChunkSize == 0) {
|
||||
if (forceSmallChunk) {
|
||||
if (BuildVars.LOGS_ENABLED) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import android.util.SparseArray;
|
|||
|
||||
import org.telegram.tgnet.TLObject;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.tgnet.tl.TL_stories;
|
||||
import org.telegram.ui.LaunchActivity;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -69,8 +70,8 @@ public class FileLoader extends BaseController {
|
|||
fileMeta.messageType = messageObject.type;
|
||||
fileMeta.messageSize = messageObject.getSize();
|
||||
return fileMeta;
|
||||
} else if (parentObject instanceof TLRPC.StoryItem) {
|
||||
TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject;
|
||||
} else if (parentObject instanceof TL_stories.StoryItem) {
|
||||
TL_stories.StoryItem storyItem = (TL_stories.StoryItem) parentObject;
|
||||
FilePathDatabase.FileMeta fileMeta = new FilePathDatabase.FileMeta();
|
||||
fileMeta.dialogId = storyItem.dialogId;
|
||||
fileMeta.messageType = MessageObject.TYPE_STORY;
|
||||
|
@ -824,9 +825,10 @@ public class FileLoader extends BaseController {
|
|||
type = MEDIA_DIR_DOCUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
FileLoaderPriorityQueue loaderQueue;
|
||||
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) {
|
||||
loaderQueue = largeFilesQueue[index];
|
||||
} else {
|
||||
|
@ -982,15 +984,15 @@ public class FileLoader extends BaseController {
|
|||
loaderQueue.checkLoadingOperations(operation.isStory && priority >= PRIORITY_HIGH);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private boolean canSaveAsFile(Object parentObject) {
|
||||
public static boolean canSaveAsFile(Object parentObject) {
|
||||
if (parentObject instanceof MessageObject) {
|
||||
MessageObject messageObject = (MessageObject) parentObject;
|
||||
if (!messageObject.isDocument()) {
|
||||
if (!messageObject.isDocument() || messageObject.isRoundVideo() || messageObject.isVoice()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -1169,10 +1171,6 @@ public class FileLoader extends BaseController {
|
|||
}
|
||||
|
||||
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) {
|
||||
return new File("");
|
||||
}
|
||||
|
@ -1188,7 +1186,7 @@ public class FileLoader extends BaseController {
|
|||
}
|
||||
} else {
|
||||
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) {
|
||||
ArrayList<TLRPC.PhotoSize> sizes = MessageObject.getMedia(message).photo.sizes;
|
||||
if (sizes.size() > 0) {
|
||||
|
@ -1225,22 +1223,21 @@ public class FileLoader extends BaseController {
|
|||
}
|
||||
|
||||
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) {
|
||||
return getPathToAttach(attach, null, ext, forceCache, useFileDatabaseQueue, false);
|
||||
return getPathToAttach(attach, null, ext, forceCache, useFileDatabaseQueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
long documentId = 0;
|
||||
int dcId = 0;
|
||||
int type = 0;
|
||||
String fileName = null;
|
||||
if (forceCache) {
|
||||
dir = getDirectory(MEDIA_DIR_CACHE);
|
||||
} else {
|
||||
|
@ -1257,13 +1254,7 @@ public class FileLoader extends BaseController {
|
|||
} else if (MessageObject.isVideoDocument(document)) {
|
||||
type = MEDIA_DIR_VIDEO;
|
||||
} else {
|
||||
String documentFileName = getDocumentFileName(document);
|
||||
if (saveAsFile && !TextUtils.isEmpty(documentFileName)) {
|
||||
fileName = documentFileName;
|
||||
type = MEDIA_DIR_FILES;
|
||||
} else {
|
||||
type = MEDIA_DIR_DOCUMENT;
|
||||
}
|
||||
type = MEDIA_DIR_DOCUMENT;
|
||||
}
|
||||
}
|
||||
documentId = document.id;
|
||||
|
@ -1334,10 +1325,7 @@ public class FileLoader extends BaseController {
|
|||
return new File(path);
|
||||
}
|
||||
}
|
||||
if (fileName == null) {
|
||||
fileName = getAttachFileName(attach, ext);
|
||||
}
|
||||
return new File(dir, fileName);
|
||||
return new File(dir, getAttachFileName(attach, ext));
|
||||
}
|
||||
|
||||
public FilePathDatabase getFileDatabase() {
|
||||
|
|
|
@ -2,15 +2,14 @@ package org.telegram.messenger;
|
|||
|
||||
import android.os.SystemClock;
|
||||
|
||||
import org.checkerframework.checker.units.qual.A;
|
||||
import org.telegram.tgnet.RequestDelegate;
|
||||
import org.telegram.tgnet.TLObject;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.tgnet.tl.TL_stories;
|
||||
import org.telegram.ui.ActionBar.Theme;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class FileRefController extends BaseController {
|
||||
|
@ -70,8 +69,8 @@ public class FileRefController extends BaseController {
|
|||
}
|
||||
|
||||
public static String getKeyForParentObject(Object parentObject) {
|
||||
if (parentObject instanceof TLRPC.StoryItem) {
|
||||
TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject;
|
||||
if (parentObject instanceof TL_stories.StoryItem) {
|
||||
TL_stories.StoryItem storyItem = (TL_stories.StoryItem) parentObject;
|
||||
if (storyItem.dialogId == 0) {
|
||||
FileLog.d("failed request reference can't find dialogId");
|
||||
return null;
|
||||
|
@ -134,8 +133,8 @@ public class FileRefController extends BaseController {
|
|||
if (BuildVars.LOGS_ENABLED) {
|
||||
FileLog.d("start loading request reference parent " + getObjectString(parentObject) + " args = " + args[0]);
|
||||
}
|
||||
if (args[0] instanceof TLRPC.TL_storyItem) {
|
||||
TLRPC.TL_storyItem storyItem = (TLRPC.TL_storyItem) args[0];
|
||||
if (args[0] instanceof TL_stories.TL_storyItem) {
|
||||
TL_stories.TL_storyItem storyItem = (TL_stories.TL_storyItem) args[0];
|
||||
locationKey = "story_" + storyItem.id;
|
||||
location = new TLRPC.TL_inputDocumentFileLocation();
|
||||
location.id = storyItem.media.document.id;
|
||||
|
@ -326,8 +325,8 @@ public class FileRefController extends BaseController {
|
|||
if (parentObject instanceof String) {
|
||||
return (String) parentObject;
|
||||
}
|
||||
if (parentObject instanceof TLRPC.StoryItem) {
|
||||
TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject;
|
||||
if (parentObject instanceof TL_stories.StoryItem) {
|
||||
TL_stories.StoryItem storyItem = (TL_stories.StoryItem) parentObject;
|
||||
return "story(dialogId=" + storyItem.dialogId + " id=" + storyItem.id + ")";
|
||||
}
|
||||
if (parentObject instanceof MessageObject) {
|
||||
|
@ -349,9 +348,9 @@ public class FileRefController extends BaseController {
|
|||
}
|
||||
|
||||
private void requestReferenceFromServer(Object parentObject, String locationKey, String parentKey, Object[] args) {
|
||||
if (parentObject instanceof TLRPC.StoryItem) {
|
||||
TLRPC.StoryItem storyItem = (TLRPC.StoryItem) parentObject;
|
||||
TLRPC.TL_stories_getStoriesByID req = new TLRPC.TL_stories_getStoriesByID();
|
||||
if (parentObject instanceof TL_stories.StoryItem) {
|
||||
TL_stories.StoryItem storyItem = (TL_stories.StoryItem) parentObject;
|
||||
TL_stories.TL_stories_getStoriesByID req = new TL_stories.TL_stories_getStoriesByID();
|
||||
req.peer = getMessagesController().getInputPeer(storyItem.dialogId);
|
||||
req.id.add(storyItem.id);
|
||||
getConnectionsManager().sendRequest(req, (response, error) -> {
|
||||
|
@ -546,8 +545,8 @@ public class FileRefController extends BaseController {
|
|||
if (BuildVars.DEBUG_VERSION) {
|
||||
FileLog.d("fileref updated for " + requester.args[0] + " " + requester.locationKey);
|
||||
}
|
||||
if (requester.args[0] instanceof TLRPC.TL_storyItem) {
|
||||
TLRPC.TL_storyItem storyItem = (TLRPC.TL_storyItem) requester.args[0];
|
||||
if (requester.args[0] instanceof TL_stories.TL_storyItem) {
|
||||
TL_stories.TL_storyItem storyItem = (TL_stories.TL_storyItem) requester.args[0];
|
||||
storyItem.media.document.file_reference = file_reference;
|
||||
return true;
|
||||
} else if (requester.args[0] instanceof TLRPC.TL_inputSingleMedia) {
|
||||
|
@ -915,6 +914,11 @@ public class FileRefController extends BaseController {
|
|||
if (result == null) {
|
||||
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) {
|
||||
result = getFileReference((TLRPC.WebPage) response, requester.location, needReplacement, locationReplacement);
|
||||
} else if (response instanceof TLRPC.TL_account_wallPapers) {
|
||||
|
@ -1042,11 +1046,11 @@ public class FileRefController extends BaseController {
|
|||
break;
|
||||
}
|
||||
}
|
||||
} else if (response instanceof TLRPC.TL_stories_stories) {
|
||||
TLRPC.TL_stories_stories stories = (TLRPC.TL_stories_stories) response;
|
||||
TLRPC.StoryItem newStoryItem = null;
|
||||
} else if (response instanceof TL_stories.TL_stories_stories) {
|
||||
TL_stories.TL_stories_stories stories = (TL_stories.TL_stories_stories) response;
|
||||
TL_stories.StoryItem newStoryItem = null;
|
||||
if (!stories.stories.isEmpty()) {
|
||||
TLRPC.StoryItem storyItem = stories.stories.get(0);
|
||||
TL_stories.StoryItem storyItem = stories.stories.get(0);
|
||||
if (storyItem.media != null) {
|
||||
newStoryItem = storyItem;
|
||||
if (storyItem.media.photo != null) {
|
||||
|
@ -1060,12 +1064,12 @@ public class FileRefController extends BaseController {
|
|||
Object arg = requester.args[1];
|
||||
if (arg instanceof FileLoadOperation) {
|
||||
FileLoadOperation operation = (FileLoadOperation) requester.args[1];
|
||||
if (operation.parentObject instanceof TLRPC.StoryItem) {
|
||||
TLRPC.StoryItem storyItem = (TLRPC.StoryItem) operation.parentObject;
|
||||
if (operation.parentObject instanceof TL_stories.StoryItem) {
|
||||
TL_stories.StoryItem storyItem = (TL_stories.StoryItem) operation.parentObject;
|
||||
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.story = new TLRPC.TL_storyItemDeleted();
|
||||
story.story = new TL_stories.TL_storyItemDeleted();
|
||||
story.story.id = storyItem.id;
|
||||
ArrayList<TLRPC.Update> updates = new ArrayList<>();
|
||||
updates.add(story);
|
||||
|
@ -1077,7 +1081,7 @@ public class FileRefController extends BaseController {
|
|||
}
|
||||
}
|
||||
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.story = newStoryItem;
|
||||
ArrayList<TLRPC.Update> updates = new ArrayList<>();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@ import android.os.Environment;
|
|||
import android.os.SystemClock;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
|
||||
|
|
|
@ -2302,7 +2302,7 @@ public class ImageReceiver implements NotificationCenter.NotificationCenterDeleg
|
|||
}
|
||||
}
|
||||
|
||||
public void setImageX(int x) {
|
||||
public void setImageX(float x) {
|
||||
imageX = x;
|
||||
}
|
||||
|
||||
|
|
|
@ -108,11 +108,12 @@ public class LiteMode {
|
|||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
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);
|
||||
if (batteryManager != null) {
|
||||
lastBatteryLevelCached = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
|
||||
lastBatteryLevelChecked = System.currentTimeMillis();
|
||||
lastBatteryLevelChecked = time;
|
||||
}
|
||||
}
|
||||
return lastBatteryLevelCached;
|
||||
|
|
|
@ -70,6 +70,10 @@ public class LocaleController {
|
|||
public FastDateFormat formatterScheduleDay;
|
||||
public FastDateFormat formatterScheduleYear;
|
||||
public FastDateFormat formatterMonthYear;
|
||||
public FastDateFormat formatterGiveawayCard;
|
||||
public FastDateFormat formatterBoostExpired;
|
||||
public FastDateFormat formatterGiveawayMonthDay;
|
||||
public FastDateFormat formatterGiveawayMonthDayYear;
|
||||
public FastDateFormat[] formatterScheduleSend = new FastDateFormat[15];
|
||||
|
||||
private static HashMap<Integer, String> resourcesCacheMap = new HashMap<>();
|
||||
|
@ -1908,6 +1912,10 @@ public class LocaleController {
|
|||
|| currentLocaleInfo != null && currentLocaleInfo.isRtl;
|
||||
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");
|
||||
formatterDayMonth = createFormatter(locale, getStringInternal("formatterMonth", R.string.formatterMonth), "dd MMM");
|
||||
formatterYear = createFormatter(locale, getStringInternal("formatterYear", R.string.formatterYear), "dd.MM.yy");
|
||||
|
|
|
@ -74,6 +74,7 @@ import org.telegram.tgnet.AbstractSerializedData;
|
|||
import org.telegram.tgnet.ConnectionsManager;
|
||||
import org.telegram.tgnet.TLObject;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.tgnet.tl.TL_stories;
|
||||
import org.telegram.ui.ActionBar.AlertDialog;
|
||||
import org.telegram.ui.ActionBar.BaseFragment;
|
||||
import org.telegram.ui.ActionBar.Theme;
|
||||
|
@ -129,6 +130,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
return false;
|
||||
}
|
||||
|
||||
public VideoConvertMessage getCurrentForegroundConverMessage() {
|
||||
return currentForegroundConvertingVideo;
|
||||
}
|
||||
|
||||
private static class AudioBuffer {
|
||||
public AudioBuffer(int capacity) {
|
||||
buffer = ByteBuffer.allocateDirect(capacity);
|
||||
|
@ -490,6 +495,21 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
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
|
||||
public String getPath() {
|
||||
return path;
|
||||
|
@ -559,6 +579,26 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
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 -> {
|
||||
|
@ -620,7 +660,10 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
private static final int AUDIO_FOCUSED = 2;
|
||||
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 VideoEditedInfo videoEditedInfo;
|
||||
public int currentAccount;
|
||||
|
@ -723,7 +766,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
private long recordDialogId;
|
||||
private MessageObject recordReplyingMsg;
|
||||
private MessageObject recordReplyingTopMsg;
|
||||
private TLRPC.StoryItem recordReplyingStory;
|
||||
private TL_stories.StoryItem recordReplyingStory;
|
||||
private short[] recordSamples = new short[1024];
|
||||
private long samplesCount;
|
||||
|
||||
|
@ -3743,7 +3786,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
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;
|
||||
recordReplyingTopMsg = replyToTopMsg;
|
||||
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;
|
||||
if (playingMessageObject != null && isPlayingMessage(playingMessageObject) && !isMessagePaused()) {
|
||||
paused = true;
|
||||
|
@ -4068,7 +4111,18 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
}
|
||||
}
|
||||
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);
|
||||
if (!sourceFile.exists()) {
|
||||
|
@ -4122,7 +4176,7 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
}
|
||||
}
|
||||
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);
|
||||
if (!sourceFile.exists()) {
|
||||
|
@ -4996,7 +5050,12 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
} else if (isEmpty) {
|
||||
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) {
|
||||
startVideoConvertFromQueue();
|
||||
}
|
||||
|
@ -5015,7 +5074,9 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
videoConvertMessage.videoEditedInfo.canceled = true;
|
||||
}
|
||||
} else {
|
||||
videoConvertQueue.remove(a);
|
||||
VideoConvertMessage convertMessage = videoConvertQueue.remove(a);
|
||||
foregroundConvertingMessages.remove(convertMessage);
|
||||
checkForegroundConvertMessage(true);
|
||||
}
|
||||
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() {
|
||||
if (!videoConvertQueue.isEmpty()) {
|
||||
VideoConvertMessage videoConvertMessage = videoConvertQueue.get(0);
|
||||
|
@ -5033,28 +5105,6 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
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);
|
||||
return true;
|
||||
}
|
||||
|
@ -5156,10 +5206,13 @@ public class MediaController implements AudioManager.OnAudioFocusChangeListener,
|
|||
}
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
if (error || last) {
|
||||
boolean cancelled = message.videoEditedInfo.canceled;
|
||||
synchronized (videoConvertSync) {
|
||||
message.videoEditedInfo.canceled = false;
|
||||
}
|
||||
videoConvertQueue.remove(message);
|
||||
foregroundConvertingMessages.remove(message);
|
||||
checkForegroundConvertMessage(cancelled || error);
|
||||
startVideoConvertFromQueue();
|
||||
}
|
||||
if (error) {
|
||||
|
|
|
@ -60,11 +60,13 @@ import org.telegram.tgnet.TLObject;
|
|||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.ui.ActionBar.BaseFragment;
|
||||
import org.telegram.ui.ActionBar.EmojiThemes;
|
||||
import org.telegram.ui.ChatActivity;
|
||||
import org.telegram.ui.Components.AnimatedEmojiDrawable;
|
||||
import org.telegram.ui.Components.AnimatedEmojiSpan;
|
||||
import org.telegram.ui.Components.AvatarDrawable;
|
||||
import org.telegram.ui.Components.Bulletin;
|
||||
import org.telegram.ui.Components.ChatThemeBottomSheet;
|
||||
import org.telegram.ui.Components.QuoteSpan;
|
||||
import org.telegram.ui.Components.StickerSetBulletinLayout;
|
||||
import org.telegram.ui.Components.StickersArchiveAlert;
|
||||
import org.telegram.ui.Components.TextStyleSpan;
|
||||
|
@ -5444,6 +5446,7 @@ public class MediaDataController extends BaseController {
|
|||
object.replyMessageObject = messageObject;
|
||||
object.applyTimestampsHighlightForReplyMsg();
|
||||
object.messageOwner.reply_to = new TLRPC.TL_messageReplyHeader();
|
||||
object.messageOwner.reply_to.flags |= 16;
|
||||
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;
|
||||
} else if (entity instanceof TLRPC.TL_messageEntityUnderline) {
|
||||
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) {
|
||||
newRun.flags = TextStyleSpan.FLAG_STYLE_BOLD;
|
||||
} 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));
|
||||
if ((flags & TextStyleSpan.FLAG_STYLE_UNDERLINE) != 0)
|
||||
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) {
|
||||
|
@ -6282,8 +6281,16 @@ public class MediaDataController extends BaseController {
|
|||
if (isPre) {
|
||||
int firstChar = start > 0 ? message[0].charAt(start - 1) : 0;
|
||||
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 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;
|
||||
CharSequence endMessage = substring(message[0], index + 3 + (firstChar == ' ' || firstChar == '\n' ? 1 : 0), message[0].length());
|
||||
if (startMessage.length() != 0) {
|
||||
|
@ -6291,15 +6298,18 @@ public class MediaDataController extends BaseController {
|
|||
} else {
|
||||
replacedFirst = true;
|
||||
}
|
||||
if (endMessage.length() != 0) {
|
||||
if (endMessage.length() > 0 && endMessage.charAt(0) != '\n') {
|
||||
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)) {
|
||||
message[0] = AndroidUtilities.concat(startMessage, content, endMessage);
|
||||
TLRPC.TL_messageEntityPre entity = new TLRPC.TL_messageEntityPre();
|
||||
entity.offset = start + (replacedFirst ? 0 : 1);
|
||||
entity.length = index - start - 3 + (replacedFirst ? 0 : 1);
|
||||
entity.language = "";
|
||||
entity.length = index - start - 3 - (language.length() + (!language.isEmpty() ? 1 : 0)) + (replacedFirst ? 0 : 1);
|
||||
entity.language = language;
|
||||
entities.add(entity);
|
||||
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) {
|
||||
AndroidUtilities.addLinks((Spannable) spannable, Linkify.WEB_URLS, false, false);
|
||||
URLSpan[] spansUrl = spannable.getSpans(0, message[0].length(), URLSpan.class);
|
||||
|
@ -6452,6 +6503,17 @@ public class MediaDataController extends BaseController {
|
|||
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) {
|
||||
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) {
|
||||
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;
|
||||
if (getMessagesController().isForum(dialogId) && threadId == 0) {
|
||||
replyToMessage = null;
|
||||
|
@ -6573,8 +6635,28 @@ public class MediaDataController extends BaseController {
|
|||
draftMessage.message = message == null ? "" : message.toString();
|
||||
draftMessage.no_webpage = noWebpage;
|
||||
if (replyToMessage != null) {
|
||||
draftMessage.reply_to_msg_id = replyToMessage.id;
|
||||
draftMessage.flags |= 1;
|
||||
draftMessage.reply_to = new TLRPC.TL_inputReplyToMessage();
|
||||
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()) {
|
||||
draftMessage.entities = entities;
|
||||
|
@ -6584,8 +6666,10 @@ public class MediaDataController extends BaseController {
|
|||
SparseArray<TLRPC.DraftMessage> threads = drafts.get(dialogId);
|
||||
TLRPC.DraftMessage currentDraft = threads == null ? null : threads.get(threadId);
|
||||
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 ||
|
||||
currentDraft == null && TextUtils.isEmpty(draftMessage.message) && draftMessage.reply_to_msg_id == 0) {
|
||||
if (
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -6599,14 +6683,16 @@ public class MediaDataController extends BaseController {
|
|||
if (req.peer == null) {
|
||||
return;
|
||||
}
|
||||
if (threadId != 0) {
|
||||
req.top_msg_id = threadId;
|
||||
}
|
||||
req.message = draftMessage.message;
|
||||
req.no_webpage = draftMessage.no_webpage;
|
||||
req.reply_to_msg_id = draftMessage.reply_to_msg_id;
|
||||
req.entities = draftMessage.entities;
|
||||
req.flags = draftMessage.flags;
|
||||
req.reply_to = draftMessage.reply_to;
|
||||
if (req.reply_to != null) {
|
||||
req.flags |= 16;
|
||||
}
|
||||
if ((draftMessage.flags & 8) != 0) {
|
||||
req.entities = draftMessage.entities;
|
||||
req.flags |= 8;
|
||||
}
|
||||
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) {
|
||||
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();
|
||||
MessagesController messagesController = getMessagesController();
|
||||
|
@ -6667,6 +6809,16 @@ public class MediaDataController extends BaseController {
|
|||
}
|
||||
}
|
||||
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 (threads != null) {
|
||||
threads.remove(threadId);
|
||||
|
@ -6693,7 +6845,7 @@ public class MediaDataController extends BaseController {
|
|||
}
|
||||
editor.commit();
|
||||
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.Chat chat = null;
|
||||
if (DialogObject.isUserDialog(dialogId)) {
|
||||
|
@ -6703,7 +6855,7 @@ public class MediaDataController extends BaseController {
|
|||
}
|
||||
if (user != null || chat != null) {
|
||||
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(() -> {
|
||||
try {
|
||||
|
@ -6763,7 +6915,7 @@ public class MediaDataController extends BaseController {
|
|||
AndroidUtilities.runOnUIThread(() -> {
|
||||
SparseArray<TLRPC.DraftMessage> threads = drafts.get(dialogId);
|
||||
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);
|
||||
if (threads2 == null) {
|
||||
threads2 = new SparseArray<>();
|
||||
|
@ -6822,10 +6974,12 @@ public class MediaDataController extends BaseController {
|
|||
} else {
|
||||
draftPreferences.edit().remove("t_" + dialogId + "_" + threadId).remove("rt_" + dialogId + "_" + threadId).commit();
|
||||
}
|
||||
} else if (draftMessage.reply_to_msg_id != 0) {
|
||||
draftMessage.reply_to_msg_id = 0;
|
||||
} else if (draftMessage.reply_to == null || draftMessage.reply_to.reply_to_msg_id != 0) {
|
||||
if (draftMessage.reply_to != null) {
|
||||
draftMessage.reply_to.reply_to_msg_id = 0;
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
@ -7613,8 +7767,8 @@ public class MediaDataController extends BaseController {
|
|||
}
|
||||
});
|
||||
String aliasFinal = alias;
|
||||
if (allowAnimated && SharedConfig.suggestAnimatedEmoji) {
|
||||
fillWithAnimatedEmoji(result, maxAnimatedPerEmoji, allowTopicIcons, forcePremium, () -> {
|
||||
if (allowAnimated) {
|
||||
fillWithAnimatedEmoji(result, maxAnimatedPerEmoji, allowTopicIcons, forcePremium, includeOnlyTextColor, () -> {
|
||||
if (sync != null) {
|
||||
callback.run(result, aliasFinal);
|
||||
sync.countDown();
|
||||
|
@ -7642,7 +7796,7 @@ public class MediaDataController extends BaseController {
|
|||
|
||||
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 (onDone != null) {
|
||||
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();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package org.telegram.messenger;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.LongSparseArray;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
@ -57,7 +58,8 @@ public class MemberRequestsController extends BaseController {
|
|||
AndroidUtilities.runOnUIThread(() -> {
|
||||
if (error == null) {
|
||||
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);
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,12 +27,13 @@ import android.os.SystemClock;
|
|||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.collection.LongSparseArray;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
@ -52,6 +53,8 @@ import org.telegram.tgnet.RequestDelegate;
|
|||
import org.telegram.tgnet.SerializedData;
|
||||
import org.telegram.tgnet.TLObject;
|
||||
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.BaseFragment;
|
||||
import org.telegram.ui.ActionBar.Theme;
|
||||
|
@ -147,6 +150,11 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
private boolean hasStories;
|
||||
public long storiesChangelogUserId = 777000;
|
||||
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) {
|
||||
if (peer.chat_id != 0) {
|
||||
|
@ -181,13 +189,13 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
public ChatlistUpdatesStat() {
|
||||
this.loading = true;
|
||||
}
|
||||
public ChatlistUpdatesStat(TLRPC.TL_chatlists_chatlistUpdates value) {
|
||||
public ChatlistUpdatesStat(TL_chatlists.TL_chatlists_chatlistUpdates value) {
|
||||
this.lastRequestTime = System.currentTimeMillis();
|
||||
this.lastValue = value;
|
||||
}
|
||||
boolean loading = false;
|
||||
long lastRequestTime;
|
||||
TLRPC.TL_chatlists_chatlistUpdates lastValue;
|
||||
TL_chatlists.TL_chatlists_chatlistUpdates lastValue;
|
||||
}
|
||||
|
||||
private boolean dialogsInTransaction;
|
||||
|
@ -506,6 +514,10 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
public int ringtoneSizeMax;
|
||||
public boolean storiesExportNopublicLink;
|
||||
public int authorizationAutoconfirmPeriod;
|
||||
public int channelColorLevelMin;
|
||||
public int quoteLengthMax;
|
||||
public boolean giveawayGiftsPurchaseAvailable;
|
||||
public PeerColors peerColors;
|
||||
|
||||
public int channelsLimitDefault;
|
||||
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> dialogsForward = new ArrayList<>();
|
||||
|
||||
public ArrayList<TLRPC.TL_exportedChatlistInvite> invites = null;
|
||||
public ArrayList<TL_chatlists.TL_exportedChatlistInvite> invites = null;
|
||||
|
||||
private static int dialogFilterPointer = 10;
|
||||
public int localId = dialogFilterPointer++;
|
||||
|
@ -1383,6 +1395,11 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
largeQueueMaxActiveOperations = mainPreferences.getInt("largeQueueMaxActiveOperations", 2);
|
||||
stealthModeFuture = mainPreferences.getInt("stories_stealth_future_period", 25 * 60);
|
||||
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);
|
||||
stealthModeCooldown = mainPreferences.getInt("stories_stealth_cooldown_period", 60 * 60);
|
||||
boolean isTest = ConnectionsManager.native_isTestBackend(currentAccount) != 0;
|
||||
|
@ -1400,6 +1417,10 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
storiesEntities = mainPreferences.getString("storiesEntities", "premium");
|
||||
storiesExportNopublicLink = mainPreferences.getBoolean("storiesExportNopublicLink", false);
|
||||
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);
|
||||
if (mainPreferences.contains("dcDomainName2")) {
|
||||
dcDomainName = mainPreferences.getString("dcDomainName2", "apv3.stel.com");
|
||||
|
@ -2166,9 +2187,66 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
boolean keelAliveChanged = false;
|
||||
resetAppConfig();
|
||||
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++) {
|
||||
TLRPC.TL_jsonObjectValue value = object.value.get(a);
|
||||
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": {
|
||||
if (value.value instanceof TLRPC.TL_jsonNumber) {
|
||||
storiesChangelogUserId = (long) ((TLRPC.TL_jsonNumber) value.value).value;
|
||||
|
@ -3383,8 +3461,66 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
}
|
||||
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) {
|
||||
editor.apply();
|
||||
}
|
||||
|
@ -3404,6 +3540,196 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
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() {
|
||||
getfileExperimentalParams = false;
|
||||
collectDeviceStats = false;
|
||||
|
@ -3819,7 +4145,8 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
if (bigSize != null) {
|
||||
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<>();
|
||||
users.add(user);
|
||||
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;
|
||||
getMessagesStorage().putUsersAndChats(res.users, res.chats, true, true);
|
||||
getMessagesStorage().updateChatInfo(res.full_chat, false);
|
||||
|
||||
getStoriesController().updateStoriesFromFullPeer(dialogId, res.full_chat.stories);
|
||||
if (ChatObject.isChannel(chat)) {
|
||||
Integer value = dialogs_read_inbox_max.get(dialogId);
|
||||
if (value == null) {
|
||||
|
@ -5211,6 +5538,7 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
putChats(res.chats, false);
|
||||
res.full_user.user = getUser(res.full_user.id);
|
||||
getMessagesStorage().updateUserInfo(userFull, false);
|
||||
getStoriesController().updateStoriesFromFullPeer(dialogId, userFull.stories);
|
||||
ChatThemeController.getInstance(currentAccount).saveChatWallpaper(res.full_user.id, res.full_user.wallpaper);
|
||||
|
||||
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) {
|
||||
if (fromCache) {
|
||||
getMessagesStorage().getDialogPhotos(did, count, maxId, classGuid);
|
||||
} else {
|
||||
if (did > 0) {
|
||||
TLRPC.User user = getUser(did);
|
||||
private LongSparseArray<DialogPhotos> dialogPhotos = new LongSparseArray<>();
|
||||
|
||||
public DialogPhotos getDialogPhotos(long dialogId) {
|
||||
DialogPhotos photos = dialogPhotos.get(dialogId);
|
||||
if (photos == null) {
|
||||
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) {
|
||||
loading = false;
|
||||
return;
|
||||
}
|
||||
TLRPC.TL_photos_getUserPhotos req = new TLRPC.TL_photos_getUserPhotos();
|
||||
req.offset = offset;
|
||||
req.limit = count;
|
||||
req.offset = 0;
|
||||
req.max_id = maxId;
|
||||
req.max_id = 0;
|
||||
req.user_id = getInputUser(user);
|
||||
int reqId = getConnectionsManager().sendRequest(req, (response, error) -> {
|
||||
reqId = getConnectionsManager().sendRequest(req, (response, error) -> {
|
||||
if (error == null) {
|
||||
TLRPC.photos_Photos res = (TLRPC.photos_Photos) response;
|
||||
processLoadedUserPhotos(res, null, did, count, maxId, false, classGuid);
|
||||
final TLRPC.photos_Photos res = (TLRPC.photos_Photos) response;
|
||||
getMessagesStorage().putUsersAndChats(res.users, null, true, true);
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
putUsers(res.users, false);
|
||||
onLoaded(offset, count, res);
|
||||
});
|
||||
}
|
||||
});
|
||||
getConnectionsManager().bindRequestToGuid(reqId, classGuid);
|
||||
} else if (did < 0) {
|
||||
} else {
|
||||
TLRPC.TL_messages_search req = new TLRPC.TL_messages_search();
|
||||
req.filter = new TLRPC.TL_inputMessagesFilterChatPhotos();
|
||||
req.add_offset = offset;
|
||||
req.limit = count;
|
||||
req.offset_id = maxId;
|
||||
req.offset_id = 0;
|
||||
req.q = "";
|
||||
req.peer = getInputPeer(did);
|
||||
int reqId = getConnectionsManager().sendRequest(req, (response, error) -> {
|
||||
req.peer = getInputPeer(dialogId);
|
||||
reqId = getConnectionsManager().sendRequest(req, (response, error) -> {
|
||||
if (error == null) {
|
||||
TLRPC.messages_Messages messages = (TLRPC.messages_Messages) response;
|
||||
TLRPC.TL_photos_photos res = new TLRPC.TL_photos_photos();
|
||||
ArrayList<TLRPC.Message> arrayList = new ArrayList<>();
|
||||
res.count = messages.count;
|
||||
res.users.addAll(messages.users);
|
||||
for (int a = 0; a < messages.messages.size(); a++) {
|
||||
TLRPC.Message message = messages.messages.get(a);
|
||||
if (message.action == null || message.action.photo == null) {
|
||||
continue;
|
||||
getMessagesStorage().putUsersAndChats(messages.users, messages.chats, true, true);
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
putUsers(messages.users, false);
|
||||
putChats(messages.chats, false);
|
||||
TLRPC.photos_Photos res = new TLRPC.TL_photos_photos();
|
||||
res.count = messages.count;
|
||||
for (int a = 0; a < messages.messages.size(); a++) {
|
||||
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);
|
||||
arrayList.add(message);
|
||||
}
|
||||
processLoadedUserPhotos(res, arrayList, did, count, maxId, false, classGuid);
|
||||
onLoaded(offset, count, res);
|
||||
});
|
||||
}
|
||||
});
|
||||
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) {
|
||||
|
@ -6005,7 +6618,17 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
}
|
||||
|
||||
public void deleteUserPhoto(TLRPC.InputPhoto photo) {
|
||||
long dialogId = getUserConfig().getClientUserId();
|
||||
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();
|
||||
req.id = new TLRPC.TL_inputPhotoEmpty();
|
||||
// getUserConfig().getCurrentUser().photo = new TLRPC.TL_userProfilePhotoEmpty();
|
||||
|
@ -6051,7 +6674,7 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
user1.photo = new TLRPC.TL_userProfilePhotoEmpty();
|
||||
}
|
||||
|
||||
TLRPC.UserFull userFull = getUserFull(getUserConfig().getClientUserId());
|
||||
TLRPC.UserFull userFull = getUserFull(dialogId);
|
||||
userFull.profile_photo = photos_photo.photo;
|
||||
getMessagesStorage().updateUserInfo(userFull, false);
|
||||
|
||||
|
@ -6068,25 +6691,13 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
} else {
|
||||
TLRPC.TL_photos_deletePhotos req = new TLRPC.TL_photos_deletePhotos();
|
||||
req.id.add(photo);
|
||||
getDialogPhotos(dialogId).removePhoto(photo.id);
|
||||
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) {
|
||||
if (location == null) {
|
||||
return;
|
||||
|
@ -8425,16 +9036,6 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
reload = ((SystemClock.elapsedRealtime() - lastScheduledServerQueryTime.get(dialogId, 0L)) > 60 * 1000);
|
||||
} else {
|
||||
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) {
|
||||
int hash;
|
||||
|
@ -8556,6 +9157,14 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
if (loaderLogger != null) {
|
||||
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(() -> {
|
||||
putUsers(messagesRes.users, isCache);
|
||||
putChats(messagesRes.chats, isCache);
|
||||
|
@ -8584,7 +9193,7 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
}
|
||||
}
|
||||
if (mode == 1 && count == 1) {
|
||||
getNotificationCenter().postNotificationName(NotificationCenter.scheduledMessagesUpdated, dialogId, objects.size());
|
||||
getNotificationCenter().postNotificationName(NotificationCenter.scheduledMessagesUpdated, dialogId, objects.size(), false);
|
||||
}
|
||||
|
||||
if (!DialogObject.isEncryptedDialog(dialogId)) {
|
||||
|
@ -11992,7 +12601,7 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
File src = new File(videoPath);
|
||||
src.renameTo(destFile);
|
||||
}
|
||||
getMessagesStorage().addDialogPhoto(-chatId, photo);
|
||||
getDialogPhotos(-chatId).addPhotoAtStart(photo);
|
||||
}
|
||||
}
|
||||
processUpdates(updates, false);
|
||||
|
@ -14761,8 +15370,8 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
chatInfoToUpdate = new ArrayList<>();
|
||||
}
|
||||
chatInfoToUpdate.add(update.participants);
|
||||
} if (baseUpdate instanceof TLRPC.TL_updateStory) {
|
||||
getStoriesController().processUpdate((TLRPC.TL_updateStory) baseUpdate);
|
||||
} if (baseUpdate instanceof TL_stories.TL_updateStory) {
|
||||
getStoriesController().processUpdate((TL_stories.TL_updateStory) baseUpdate);
|
||||
} else if (baseUpdate instanceof TLRPC.TL_updateUserStatus) {
|
||||
interfaceUpdateMask |= UPDATE_MASK_STATUS;
|
||||
if (updatesOnMainThread == null) {
|
||||
|
@ -14785,6 +15394,9 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
TLRPC.TL_updateUserPhoto update = (TLRPC.TL_updateUserPhoto) baseUpdate;
|
||||
interfaceUpdateMask |= UPDATE_MASK_AVATAR;
|
||||
getMessagesStorage().clearUserPhotos(update.user_id);
|
||||
if (dialogPhotos.get(update.user_id) != null) {
|
||||
dialogPhotos.get(update.user_id).reset();
|
||||
}
|
||||
if (updatesOnMainThread == null) {
|
||||
updatesOnMainThread = new ArrayList<>();
|
||||
}
|
||||
|
@ -14803,8 +15415,8 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
updatesOnMainThread = new ArrayList<>();
|
||||
}
|
||||
updatesOnMainThread.add(baseUpdate);
|
||||
} else if (baseUpdate instanceof TLRPC.TL_updateReadStories) {
|
||||
TLRPC.TL_updateReadStories updateReadStories = (TLRPC.TL_updateReadStories) baseUpdate;
|
||||
} else if (baseUpdate instanceof TL_stories.TL_updateReadStories) {
|
||||
TL_stories.TL_updateReadStories updateReadStories = (TL_stories.TL_updateReadStories) baseUpdate;
|
||||
long dialogId = DialogObject.getPeerDialogId(updateReadStories.peer);
|
||||
getStoriesController().markStoriesAsReadFromServer(dialogId, updateReadStories.max_id);
|
||||
} else if (baseUpdate instanceof TLRPC.TL_updatePeerSettings) {
|
||||
|
@ -14955,6 +15567,7 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
}
|
||||
if ((update.flags & 2) != 0) {
|
||||
TLRPC.TL_message newMessage = new TLRPC.TL_message();
|
||||
newMessage.invert_media = update.invert_media;
|
||||
newMessage.local_id = newMessage.id = getUserConfig().getNewMessageId();
|
||||
getUserConfig().saveConfig(false);
|
||||
newMessage.unread = true;
|
||||
|
@ -14999,7 +15612,7 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
}
|
||||
pushMessages.add(obj);
|
||||
}
|
||||
} else if (baseUpdate instanceof TLRPC.TL_updateStoriesStealthMode) {
|
||||
} else if (baseUpdate instanceof TL_stories.TL_updateStoriesStealthMode) {
|
||||
if (updatesOnMainThread == null) {
|
||||
updatesOnMainThread = new ArrayList<>();
|
||||
}
|
||||
|
@ -16025,8 +16638,8 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
}
|
||||
int threadId = update.top_msg_id;
|
||||
getMediaDataController().saveDraft(did, threadId, update.draft, null, true);
|
||||
} else if (baseUpdate instanceof TLRPC.TL_updateStoriesStealthMode) {
|
||||
TLRPC.TL_updateStoriesStealthMode storiesStealthModeUpdate = (TLRPC.TL_updateStoriesStealthMode) baseUpdate;
|
||||
} else if (baseUpdate instanceof TL_stories.TL_updateStoriesStealthMode) {
|
||||
TL_stories.TL_updateStoriesStealthMode storiesStealthModeUpdate = (TL_stories.TL_updateStoriesStealthMode) baseUpdate;
|
||||
getStoriesController().setStealthMode(storiesStealthModeUpdate.stealth_mode);
|
||||
} else if (baseUpdate instanceof TLRPC.TL_updateReadFeaturedStickers) {
|
||||
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++) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -18352,12 +18965,12 @@ public class MessagesController extends BaseController implements NotificationCe
|
|||
}
|
||||
final ChatlistUpdatesStat finalStat = stat;
|
||||
finalStat.loading = false;
|
||||
TLRPC.TL_chatlists_getChatlistUpdates req = new TLRPC.TL_chatlists_getChatlistUpdates();
|
||||
req.chatlist = new TLRPC.TL_inputChatlistDialogFilter();
|
||||
TL_chatlists.TL_chatlists_getChatlistUpdates req = new TL_chatlists.TL_chatlists_getChatlistUpdates();
|
||||
req.chatlist = new TL_chatlists.TL_inputChatlistDialogFilter();
|
||||
req.chatlist.filter_id = filterId;
|
||||
getConnectionsManager().sendRequest(req, (res, err) -> AndroidUtilities.runOnUIThread(() -> {
|
||||
if (res instanceof TLRPC.TL_chatlists_chatlistUpdates) {
|
||||
TLRPC.TL_chatlists_chatlistUpdates updates = (TLRPC.TL_chatlists_chatlistUpdates) res;
|
||||
if (res instanceof TL_chatlists.TL_chatlists_chatlistUpdates) {
|
||||
TL_chatlists.TL_chatlists_chatlistUpdates updates = (TL_chatlists.TL_chatlists_chatlistUpdates) res;
|
||||
putChats(updates.chats, false);
|
||||
putUsers(updates.users, false);
|
||||
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);
|
||||
if (stat == null) {
|
||||
return null;
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.telegram.tgnet.NativeByteBuffer;
|
|||
import org.telegram.tgnet.RequestDelegate;
|
||||
import org.telegram.tgnet.TLObject;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.tgnet.tl.TL_stories;
|
||||
import org.telegram.ui.ActionBar.Theme;
|
||||
import org.telegram.ui.Adapters.DialogsSearchAdapter;
|
||||
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;
|
||||
public boolean showClearDatabaseAlert;
|
||||
private LongSparseIntArray dialogIsForum = new LongSparseIntArray();
|
||||
|
@ -461,7 +462,7 @@ public class MessagesStorage extends BaseController {
|
|||
"channel_users_v2",
|
||||
"channel_admins_v3",
|
||||
"contacts",
|
||||
"user_photos",
|
||||
"dialog_photos",
|
||||
"dialog_settings",
|
||||
"web_recent_v3",
|
||||
"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_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 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 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();
|
||||
|
@ -1332,6 +1334,8 @@ public class MessagesStorage extends BaseController {
|
|||
database.executeFast("DELETE FROM chat_pinned_count").stepThis().dispose();
|
||||
database.executeFast("DELETE FROM profile_stories").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");
|
||||
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) {
|
||||
storageQueue.postRunnable(() -> {
|
||||
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) {
|
||||
checkSQLException(e);
|
||||
}
|
||||
|
@ -4099,7 +4063,8 @@ public class MessagesStorage extends BaseController {
|
|||
public void clearUserPhoto(long dialogId, long pid) {
|
||||
storageQueue.postRunnable(() -> {
|
||||
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) {
|
||||
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) {
|
||||
storageQueue.postRunnable(() -> {
|
||||
SQLiteCursor cursor = null;
|
||||
|
@ -7056,6 +6935,19 @@ public class MessagesStorage extends BaseController {
|
|||
if (info != null && info.inviterId != 0) {
|
||||
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));
|
||||
while (cursor.next()) {
|
||||
|
@ -8505,7 +8397,7 @@ public class MessagesStorage extends BaseController {
|
|||
if (!cursor.isNull(6)) {
|
||||
data = cursor.byteBufferValue(6);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -8629,6 +8521,7 @@ public class MessagesStorage extends BaseController {
|
|||
TLRPC.Message object = arrayList.get(a);
|
||||
object.replyMessage = message;
|
||||
if (object.reply_to != null) {
|
||||
object.reply_to.flags |= 16;
|
||||
object.reply_to.reply_to_msg_id = message.id;
|
||||
}
|
||||
}
|
||||
|
@ -9433,6 +9326,31 @@ public class MessagesStorage extends BaseController {
|
|||
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 {
|
||||
if (users == null || users.isEmpty()) {
|
||||
return;
|
||||
|
@ -12256,7 +12174,7 @@ public class MessagesStorage extends BaseController {
|
|||
}
|
||||
cursor.dispose();
|
||||
cursor = null;
|
||||
AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.scheduledMessagesUpdated, did, count));
|
||||
AndroidUtilities.runOnUIThread(() -> getNotificationCenter().postNotificationName(NotificationCenter.scheduledMessagesUpdated, did, count, true));
|
||||
} catch (Exception e) {
|
||||
checkSQLException(e);
|
||||
} finally {
|
||||
|
@ -14180,22 +14098,8 @@ public class MessagesStorage extends BaseController {
|
|||
}
|
||||
if (message.action instanceof TLRPC.TL_messageActionGeoProximityReached) {
|
||||
TLRPC.TL_messageActionGeoProximityReached action = (TLRPC.TL_messageActionGeoProximityReached) message.action;
|
||||
long id = MessageObject.getPeerId(action.from_id);
|
||||
if (DialogObject.isUserDialog(id)) {
|
||||
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);
|
||||
}
|
||||
addLoadPeerInfo(action.from_id, usersToLoad, chatsToLoad);
|
||||
addLoadPeerInfo(action.to_id, usersToLoad, chatsToLoad);
|
||||
}
|
||||
if (!message.action.users.isEmpty()) {
|
||||
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;
|
||||
if (!messageMediaPoll.results.recent_voters.isEmpty()) {
|
||||
for (int i = 0; i < messageMediaPoll.results.recent_voters.size(); i++) {
|
||||
TLRPC.Peer peer = messageMediaPoll.results.recent_voters.get(i);
|
||||
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);
|
||||
}
|
||||
addLoadPeerInfo(messageMediaPoll.results.recent_voters.get(i), usersToLoad, chatsToLoad);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (message.media.peer != null) {
|
||||
long dialogId = DialogObject.getPeerDialogId(message.media.peer);
|
||||
if (dialogId > 0) {
|
||||
usersToLoad.add(dialogId);
|
||||
}
|
||||
if (dialogId < 0) {
|
||||
chatsToLoad.add(-dialogId);
|
||||
}
|
||||
addLoadPeerInfo(message.media.peer, usersToLoad, chatsToLoad);
|
||||
}
|
||||
}
|
||||
if (message.replies != null) {
|
||||
for (int a = 0, N = message.replies.recent_repliers.size(); a < N; a++) {
|
||||
long id = MessageObject.getPeerId(message.replies.recent_repliers.get(a));
|
||||
if (DialogObject.isUserDialog(id)) {
|
||||
if (!usersToLoad.contains(id)) {
|
||||
usersToLoad.add(id);
|
||||
}
|
||||
} else if (DialogObject.isChatDialog(id)) {
|
||||
if (!chatsToLoad.contains(-id)) {
|
||||
chatsToLoad.add(-id);
|
||||
}
|
||||
}
|
||||
addLoadPeerInfo(message.replies.recent_repliers.get(a), usersToLoad, chatsToLoad);
|
||||
}
|
||||
}
|
||||
if (message.reply_to != null && message.reply_to.reply_to_peer_id != null) {
|
||||
long id = MessageObject.getPeerId(message.reply_to.reply_to_peer_id);
|
||||
if (DialogObject.isUserDialog(id)) {
|
||||
if (!usersToLoad.contains(id)) {
|
||||
usersToLoad.add(id);
|
||||
}
|
||||
} else if (DialogObject.isChatDialog(id)) {
|
||||
if (!chatsToLoad.contains(-id)) {
|
||||
chatsToLoad.add(-id);
|
||||
}
|
||||
}
|
||||
addLoadPeerInfo(message.reply_to.reply_to_peer_id, usersToLoad, chatsToLoad);
|
||||
}
|
||||
if (message.fwd_from != null) {
|
||||
if (message.fwd_from.from_id instanceof TLRPC.TL_peerUser) {
|
||||
if (!usersToLoad.contains(message.fwd_from.from_id.user_id)) {
|
||||
usersToLoad.add(message.fwd_from.from_id.user_id);
|
||||
}
|
||||
} else if (message.fwd_from.from_id instanceof TLRPC.TL_peerChannel) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
addLoadPeerInfo(message.fwd_from.from_id, usersToLoad, chatsToLoad);
|
||||
addLoadPeerInfo(message.fwd_from.saved_from_peer, usersToLoad, chatsToLoad);
|
||||
}
|
||||
if (message.reply_to != null && message.reply_to.reply_from != null && message.reply_to.reply_from.from_id != null) {
|
||||
addLoadPeerInfo(message.reply_to.reply_from.from_id, usersToLoad, chatsToLoad);
|
||||
}
|
||||
if (message.params != null) {
|
||||
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) {
|
||||
long[] draftsDialogIds;
|
||||
if (loadDraftsPeersAndFolders) {
|
||||
|
@ -15569,7 +15435,7 @@ public class MessagesStorage extends BaseController {
|
|||
if (dialogsType != 4 && (replies).startsWith(search1) || replies2.startsWith(search1)) {
|
||||
TLRPC.User user = getMessagesController().getUser(708513L);
|
||||
if (user == null) {
|
||||
user = getMessagesController().getUser(1271266957L);
|
||||
user = getMessagesController().getUser(UserObject.REPLY_BOT);
|
||||
}
|
||||
if (user != null) {
|
||||
DialogsSearchAdapter.DialogSearchResult dialogSearchResult = new DialogsSearchAdapter.DialogSearchResult();
|
||||
|
|
|
@ -37,6 +37,7 @@ import android.widget.RemoteViews;
|
|||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
|
||||
import org.telegram.messenger.audioinfo.AudioInfo;
|
||||
import org.telegram.ui.LaunchActivity;
|
||||
|
@ -65,6 +66,7 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
|
|||
private Bitmap albumArtPlaceholder;
|
||||
private int notificationMessageID;
|
||||
private ImageReceiver imageReceiver;
|
||||
private boolean foregroundServiceIsStarted;
|
||||
|
||||
private String loadingFilePath;
|
||||
|
||||
|
@ -311,12 +313,22 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
|
|||
|
||||
notification = bldr.build();
|
||||
|
||||
if (isPlaying) {
|
||||
startForeground(ID_NOTIFICATION, notification);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
if (!foregroundServiceIsStarted) {
|
||||
foregroundServiceIsStarted = true;
|
||||
startForeground(ID_NOTIFICATION, notification);
|
||||
} else {
|
||||
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
nm.notify(ID_NOTIFICATION, notification);
|
||||
}
|
||||
} else {
|
||||
stopForeground(false);
|
||||
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
nm.notify(ID_NOTIFICATION, notification);
|
||||
if (isPlaying) {
|
||||
startForeground(ID_NOTIFICATION, notification);
|
||||
} else {
|
||||
stopForeground(false);
|
||||
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
nm.notify(ID_NOTIFICATION, notification);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -504,6 +516,7 @@ public class MusicPlayerService extends Service implements NotificationCenter.No
|
|||
public void onDestroy() {
|
||||
unregisterReceiver(headsetPlugReceiver);
|
||||
super.onDestroy();
|
||||
stopForeground(true);
|
||||
if (remoteControlClient != null) {
|
||||
RemoteControlClient.MetadataEditor metadataEditor = remoteControlClient.editMetadata(true);
|
||||
metadataEditor.clear();
|
||||
|
|
|
@ -22,7 +22,7 @@ import java.util.zip.ZipFile;
|
|||
|
||||
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_SO_NAME = "lib" + LIB_NAME + ".so";
|
||||
private final static String LOCALE_LIB_SO_NAME = "lib" + LIB_NAME + "loc.so";
|
||||
|
|
|
@ -209,6 +209,8 @@ public class NotificationCenter {
|
|||
|
||||
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 didUpdatePremiumGiftFieldIcon = totalEvents++;
|
||||
public static final int storiesEnabledUpdate = totalEvents++;
|
||||
|
@ -216,10 +218,10 @@ public class NotificationCenter {
|
|||
public static final int storiesLimitUpdate = totalEvents++;
|
||||
public static final int storiesSendAsUpdate = totalEvents++;
|
||||
public static final int unconfirmedAuthUpdate = totalEvents++;
|
||||
public static final int dialogPhotosUpdate = totalEvents++;
|
||||
|
||||
//global
|
||||
public static final int pushMessagesUpdated = totalEvents++;
|
||||
public static final int stopEncodingService = totalEvents++;
|
||||
public static final int wallpapersDidLoad = totalEvents++;
|
||||
public static final int wallpapersNeedReload = totalEvents++;
|
||||
public static final int didReceiveSmsCode = totalEvents++;
|
||||
|
@ -294,7 +296,8 @@ public class NotificationCenter {
|
|||
public static final int uploadStoryEnd = totalEvents++;
|
||||
public static final int customTypefacesLoaded = 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;
|
||||
|
||||
|
|
|
@ -1804,6 +1804,8 @@ public class NotificationsController extends BaseController {
|
|||
}
|
||||
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) {
|
||||
return LocaleController.formatString("NotificationInvitedToGroupByLink", R.string.NotificationInvitedToGroupByLink, name, chat.title);
|
||||
} else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatEditTitle) {
|
||||
|
@ -2116,6 +2118,8 @@ public class NotificationsController extends BaseController {
|
|||
} else {
|
||||
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) {
|
||||
return LocaleController.getString("AttachLocation", R.string.AttachLocation);
|
||||
} 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) {
|
||||
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));
|
||||
} 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) {
|
||||
TLRPC.TL_messageMediaPoll mediaPoll = (TLRPC.TL_messageMediaPoll) messageObject.messageOwner.media;
|
||||
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());
|
||||
}
|
||||
} 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) {
|
||||
msg = LocaleController.formatString("NotificationInvitedToGroupByLink", R.string.NotificationInvitedToGroupByLink, name, chat.title);
|
||||
} else if (messageObject.messageOwner.action instanceof TLRPC.TL_messageActionChatEditTitle) {
|
||||
|
@ -2729,6 +2745,9 @@ public class NotificationsController extends BaseController {
|
|||
} else {
|
||||
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) {
|
||||
msg = LocaleController.formatString("ChannelMessageMap", R.string.ChannelMessageMap, name);
|
||||
} 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) {
|
||||
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) {
|
||||
msg = LocaleController.formatString("NotificationMessageGroupMap", R.string.NotificationMessageGroupMap, name, chat.title);
|
||||
} else if (messageObject.messageOwner.media instanceof TLRPC.TL_messageMediaGeoLive) {
|
||||
|
|
|
@ -622,11 +622,26 @@ public class PushListenerController {
|
|||
localMessage = true;
|
||||
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": {
|
||||
messageText = LocaleController.formatString("NotificationMessageAlbum", R.string.NotificationMessageAlbum, args[0]);
|
||||
localMessage = true;
|
||||
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": {
|
||||
messageText = LocaleController.formatString("ChannelMessageNoText", R.string.ChannelMessageNoText, args[0]);
|
||||
message1 = LocaleController.getString("Message", R.string.Message);
|
||||
|
@ -737,6 +752,11 @@ public class PushListenerController {
|
|||
localMessage = true;
|
||||
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": {
|
||||
messageText = LocaleController.formatString("NotificationMessageGroupText", R.string.NotificationMessageGroupText, args[0], args[1], args[2]);
|
||||
message1 = args[2];
|
||||
|
@ -1038,6 +1058,10 @@ public class PushListenerController {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case "PINNED_GIVEAWAY": {
|
||||
messageText = LocaleController.formatString("NotificationPinnedGiveaway", R.string.NotificationPinnedGiveaway, args[0]);
|
||||
break;
|
||||
}
|
||||
case "PINNED_QUIZ": {
|
||||
if (dialogId > 0) {
|
||||
messageText = LocaleController.formatString("NotificationActionPinnedQuizUser", R.string.NotificationActionPinnedQuizUser, args[0], args[1]);
|
||||
|
@ -1307,6 +1331,12 @@ public class PushListenerController {
|
|||
case "REACT_GIF": {
|
||||
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": {
|
||||
return LocaleController.formatString("PushChatReactText", R.string.PushChatReactText, args);
|
||||
}
|
||||
|
|
|
@ -761,9 +761,6 @@ public class SecretChatHelper extends BaseController {
|
|||
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SENT;
|
||||
getNotificationCenter().postNotificationName(NotificationCenter.messageReceivedByServer, newMsgObj.id, newMsgObj.id, newMsgObj, newMsgObj.dialog_id, 0L, existFlags, false);
|
||||
getSendMessagesHelper().processSentMessage(newMsgObj.id);
|
||||
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj)) {
|
||||
getSendMessagesHelper().stopVideoService(attachPath);
|
||||
}
|
||||
getSendMessagesHelper().removeFromSendingMessages(newMsgObj.id, false);
|
||||
});
|
||||
});
|
||||
|
@ -773,9 +770,6 @@ public class SecretChatHelper extends BaseController {
|
|||
newMsgObj.send_state = MessageObject.MESSAGE_SEND_STATE_SEND_ERROR;
|
||||
getNotificationCenter().postNotificationName(NotificationCenter.messageSendError, 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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import android.provider.OpenableColumns;
|
|||
import android.text.Spannable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
|
@ -56,6 +57,7 @@ import org.telegram.tgnet.RequestDelegate;
|
|||
import org.telegram.tgnet.SerializedData;
|
||||
import org.telegram.tgnet.TLObject;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.tgnet.tl.TL_stories;
|
||||
import org.telegram.ui.ActionBar.AlertDialog;
|
||||
import org.telegram.ui.ActionBar.BaseFragment;
|
||||
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.AnimatedEmojiSpan;
|
||||
import org.telegram.ui.Components.AnimatedFileDrawable;
|
||||
import org.telegram.ui.Components.BotWebViewSheet;
|
||||
import org.telegram.ui.Components.Bulletin;
|
||||
import org.telegram.ui.Components.LayoutHelper;
|
||||
import org.telegram.ui.Components.Point;
|
||||
|
@ -126,27 +129,69 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
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();
|
||||
replyTo.story_id = storyItem.id;
|
||||
replyTo.user_id = getMessagesController().getInputUser(storyItem.dialogId);
|
||||
return replyTo;
|
||||
}
|
||||
|
||||
public static TLRPC.InputReplyTo creteReplyInput(int replyToMsgId) {
|
||||
return creteReplyInput(replyToMsgId, 0);
|
||||
public TLRPC.InputReplyTo createReplyInput(int replyToMsgId) {
|
||||
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();
|
||||
replyTo.reply_to_msg_id = replyToMsgId;
|
||||
if (topMessageId != 0) {
|
||||
replyTo.flags |= 1;
|
||||
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;
|
||||
}
|
||||
|
||||
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 String historyPath;
|
||||
|
@ -942,9 +987,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
media.file = file;
|
||||
MessageObject messageObject = (MessageObject) message.extraHashMap.get(location + "_i");
|
||||
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");
|
||||
if (media.thumb == null && message.photoSize != null && message.photoSize.location != null) {
|
||||
message.performMediaUpload = true;
|
||||
|
@ -973,9 +1015,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
if (index >= 0) {
|
||||
req.files.set(index, encryptedFile);
|
||||
if (inputEncryptedFile.id == 1) {
|
||||
MessageObject messageObject = (MessageObject) message.extraHashMap.get(location + "_i");
|
||||
message.photoSize = (TLRPC.PhotoSize) message.extraHashMap.get(location + "_t");
|
||||
stopVideoService(message.messageObjects.get(index).messageOwner.attachPath);
|
||||
}
|
||||
decryptedMessage = req.messages.get(index);
|
||||
}
|
||||
|
@ -1074,7 +1114,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
boolean isEncrypted = DialogObject.isEncryptedDialog(messageObject.getDialogId());
|
||||
getFileLoader().checkUploadNewDataAvailable(finalPath, isEncrypted, availableSize, finalSize, progress);
|
||||
if (finalSize != 0) {
|
||||
stopVideoService(messageObject.messageOwner.attachPath);
|
||||
ArrayList<DelayedMessage> arr = delayedMessages.get(messageObject.messageOwner.attachPath);
|
||||
if (arr != null) {
|
||||
for (int a = 0; a < arr.size(); a++) {
|
||||
|
@ -1112,7 +1151,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
return;
|
||||
}
|
||||
String finalPath = (String) args[1];
|
||||
stopVideoService(messageObject.messageOwner.attachPath);
|
||||
|
||||
ArrayList<DelayedMessage> arr = delayedMessages.get(finalPath);
|
||||
if (arr != null) {
|
||||
|
@ -1393,7 +1431,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
} else {
|
||||
getFileLoader().cancelFileUpload(key, enc);
|
||||
}
|
||||
stopVideoService(key);
|
||||
delayedMessages.remove(key);
|
||||
}
|
||||
for (int a = 0, N = checkReadyToSendGroups.size(); a < N; a++) {
|
||||
|
@ -1544,7 +1581,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
TLRPC.Message message;
|
||||
if (resendMessage != null) {
|
||||
message = resendMessage;
|
||||
req.reply_to = SendMessagesHelper.creteReplyInput(messageId);
|
||||
req.reply_to = createReplyInput(messageId);
|
||||
req.random_id = resendMessage.random_id;
|
||||
} else {
|
||||
message = new TLRPC.TL_messageService();
|
||||
|
@ -1558,6 +1595,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
message.flags |= 256;
|
||||
message.flags |= 8;
|
||||
message.reply_to = new TLRPC.TL_messageReplyHeader();
|
||||
message.reply_to.flags |= 16;
|
||||
message.reply_to.reply_to_msg_id = messageId;
|
||||
message.peer_id = new TLRPC.TL_peerUser();
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
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.replyToStoryItem = storyItem;
|
||||
sendMessageParams.replyQuote = quote;
|
||||
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.replyToStoryItem = storyItem;
|
||||
sendMessageParams.replyQuote = quote;
|
||||
sendMessage(sendMessageParams);
|
||||
}
|
||||
}
|
||||
|
@ -2050,6 +2090,15 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
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);
|
||||
newMsgObj.scheduled = scheduleDate != 0;
|
||||
newMsgObj.messageOwner.send_state = MessageObject.MESSAGE_SEND_STATE_SENDING;
|
||||
|
@ -2076,6 +2125,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
|
||||
if (replyToTopMsg != null) {
|
||||
newMsg.reply_to = new TLRPC.TL_messageReplyHeader();
|
||||
newMsg.reply_to.flags |= 16;
|
||||
newMsg.reply_to.reply_to_msg_id = replyToTopMsg.getId();
|
||||
if (replyToTopMsg.isTopicMainMessage) {
|
||||
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) {
|
||||
TLRPC.InputMedia inputMedia = null;
|
||||
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) {
|
||||
TLRPC.TL_inputMediaUploadedPhoto uploadedPhoto = new TLRPC.TL_inputMediaUploadedPhoto();
|
||||
uploadedPhoto.spoiler = hasMediaSpoilers;
|
||||
|
@ -2660,6 +2718,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
TLRPC.TL_messages_editMessage request = new TLRPC.TL_messages_editMessage();
|
||||
request.id = messageObject.getId();
|
||||
request.peer = getMessagesController().getInputPeer(peer);
|
||||
request.invert_media = messageObject.messageOwner.invert_media;
|
||||
if (inputMedia != null) {
|
||||
request.flags |= 16384;
|
||||
request.media = inputMedia;
|
||||
|
@ -3211,19 +3270,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
inputInvoice.msg_id = messageObject.getId();
|
||||
inputInvoice.peer = getMessagesController().getInputPeer(messageObject.messageOwner.peer_id);
|
||||
req.invoice = inputInvoice;
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
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));
|
||||
final JSONObject themeParams = BotWebViewSheet.makeThemeParams(null);
|
||||
if (themeParams != null) {
|
||||
req.theme_params = new TLRPC.TL_dataJSON();
|
||||
req.theme_params.data = jsonObject.toString();
|
||||
req.theme_params.data = themeParams.toString();
|
||||
req.flags |= 1;
|
||||
} catch (Exception e) {
|
||||
FileLog.e(e);
|
||||
}
|
||||
getConnectionsManager().sendRequest(req, requestDelegate, ConnectionsManager.RequestFlagFailOnServerErrors);
|
||||
} else {
|
||||
|
@ -3330,6 +3381,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
MessageObject replyToMsg = sendMessageParams.replyToMsg;
|
||||
MessageObject replyToTopMsg = sendMessageParams.replyToTopMsg;
|
||||
TLRPC.WebPage webPage = sendMessageParams.webPage;
|
||||
TLRPC.TL_messageMediaWebPage mediaWebPage = sendMessageParams.mediaWebPage;
|
||||
boolean searchLinks = sendMessageParams.searchLinks;
|
||||
MessageObject retryMessageObject = sendMessageParams.retryMessageObject;
|
||||
ArrayList<TLRPC.MessageEntity> entities = sendMessageParams.entities;
|
||||
|
@ -3342,8 +3394,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
MessageObject.SendAnimationData sendAnimationData = sendMessageParams.sendAnimationData;
|
||||
boolean updateStickersOrder = sendMessageParams.updateStickersOrder;
|
||||
boolean hasMediaSpoilers = sendMessageParams.hasMediaSpoilers;
|
||||
TLRPC.StoryItem replyToStoryItem = sendMessageParams.replyToStoryItem;
|
||||
TLRPC.StoryItem sendingStory = sendMessageParams.sendingStory;
|
||||
TL_stories.StoryItem replyToStoryItem = sendMessageParams.replyToStoryItem;
|
||||
TL_stories.StoryItem sendingStory = sendMessageParams.sendingStory;
|
||||
ChatActivity.ReplyQuote replyQuote = sendMessageParams.replyQuote;
|
||||
boolean invert_media = sendMessageParams.invert_media;
|
||||
|
||||
if (user != null && user.phone == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -3354,6 +3409,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
caption = "";
|
||||
}
|
||||
|
||||
if (replyQuote != null && replyQuote.message != null && replyToMsg != null) {
|
||||
replyToMsg = replyQuote.message;
|
||||
}
|
||||
|
||||
String originalPath = null;
|
||||
if (params != null && params.containsKey("originalPath")) {
|
||||
originalPath = params.get("originalPath");
|
||||
|
@ -3501,7 +3560,9 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
type = MEDIA_TYPE_DICE;
|
||||
caption = "";
|
||||
} else {
|
||||
if (webPage == null) {
|
||||
if (mediaWebPage != null) {
|
||||
newMsg.media = mediaWebPage;
|
||||
} else if (webPage == null) {
|
||||
newMsg.media = new TLRPC.TL_messageMediaEmpty();
|
||||
} else {
|
||||
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.dialog_id = peer;
|
||||
newMsg.invert_media = invert_media;
|
||||
if (replyToStoryItem != null) {
|
||||
newMsg.reply_to = new TLRPC.TL_messageReplyStoryHeader();
|
||||
newMsg.reply_to.story_id = replyToStoryItem.id;
|
||||
|
@ -3782,6 +3844,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
} else {
|
||||
newMsg.flags |= TLRPC.MESSAGE_FLAG_REPLY;
|
||||
}
|
||||
newMsg.reply_to.flags |= 16;
|
||||
newMsg.reply_to.reply_to_msg_id = replyToMsg.getId();
|
||||
if (replyToTopMsg != null && replyToTopMsg != replyToMsg && replyToTopMsg.getId() != 1) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
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)) {
|
||||
newMsg.media_unread = true;
|
||||
}
|
||||
|
@ -3887,7 +4002,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
}
|
||||
|
||||
MessageObject reply = replyToMsg;
|
||||
if (replyToTopMsg != null && replyToTopMsg == reply && replyToTopMsg.getId() == 1) {
|
||||
if (replyToTopMsg != null && replyToTopMsg == reply && replyToTopMsg.getId() == 1 || destroyReply) {
|
||||
reply = null;
|
||||
}
|
||||
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 (encryptedChat == null) {
|
||||
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 = creteReplyInput(replyToStoryItem);
|
||||
reqSend.flags |= 1;
|
||||
} else if (newMsg.reply_to != null) {
|
||||
int topMsgId = replyToTopMsg == null ? 0 : replyToTopMsg.getId();
|
||||
reqSend.reply_to = SendMessagesHelper.creteReplyInput(newMsg.reply_to.reply_to_msg_id, topMsgId);
|
||||
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;
|
||||
}
|
||||
performSendMessageRequest(reqSend, newMsgObj, null, null, parentObject, params, scheduleDate != 0);
|
||||
if (retryMessageObject == null) {
|
||||
getMediaDataController().cleanDraft(peer, replyToTopMsg != null ? replyToTopMsg.getId() : 0, false);
|
||||
if (mediaWebPage != null) {
|
||||
TLRPC.TL_messages_sendMedia reqSend = new TLRPC.TL_messages_sendMedia();
|
||||
reqSend.message = message;
|
||||
reqSend.clear_draft = retryMessageObject == null;
|
||||
reqSend.silent = newMsg.silent;
|
||||
reqSend.peer = sendToPeer;
|
||||
reqSend.random_id = newMsg.random_id;
|
||||
TLRPC.TL_inputMediaWebPage inputWebPage = new TLRPC.TL_inputMediaWebPage();
|
||||
inputWebPage.url = mediaWebPage.webpage.url;
|
||||
inputWebPage.force_large_media = mediaWebPage.force_large_media;
|
||||
inputWebPage.force_small_media = mediaWebPage.force_small_media;
|
||||
reqSend.media = inputWebPage;
|
||||
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 (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 {
|
||||
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 {
|
||||
TLRPC.TL_decryptedMessage reqSend;
|
||||
|
@ -4296,11 +4451,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
|
||||
if (newMsg.replyStory != null) {
|
||||
request.flags |= 1;
|
||||
request.reply_to = creteReplyInput(replyToStoryItem);
|
||||
} else if (newMsg.reply_to != null && newMsg.reply_to.reply_to_msg_id != 0) {
|
||||
int replyToTopMsgId = replyToTopMsg != null ? replyToTopMsg.getId() : 0;
|
||||
request.reply_to = createReplyInput(replyToStoryItem);
|
||||
} else if (newMsg.reply_to instanceof TLRPC.TL_messageReplyHeader) {
|
||||
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) {
|
||||
request.schedule_date = scheduleDate;
|
||||
|
@ -4335,11 +4489,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
replyToTopMsgInt = replyToTopMsg.getId();
|
||||
}
|
||||
if (replyToStoryItem != null) {
|
||||
request.reply_to = creteReplyInput(replyToStoryItem);
|
||||
request.reply_to = createReplyInput(replyToStoryItem);
|
||||
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.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;
|
||||
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();
|
||||
reqSend.peer = sendToPeer;
|
||||
reqSend.random_id = newMsg.random_id;
|
||||
if (replyToTopMsg != null) {
|
||||
reqSend.reply_to = SendMessagesHelper.creteReplyInput(replyToTopMsg.getId());
|
||||
reqSend.flags |= 512;
|
||||
}
|
||||
|
||||
if (newMsg.from_id != null) {
|
||||
reqSend.send_as = getMessagesController().getInputPeer(newMsg.from_id);
|
||||
}
|
||||
reqSend.hide_via = !params.containsKey("bot");
|
||||
if (replyToStoryItem != null) {
|
||||
reqSend.reply_to = creteReplyInput(replyToStoryItem);
|
||||
reqSend.reply_to = createReplyInput(replyToStoryItem);
|
||||
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.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;
|
||||
if (scheduleDate != 0) {
|
||||
|
@ -5273,10 +5423,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
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) {
|
||||
if (Thread.currentThread() != ApplicationLoader.applicationHandler.getLooper().getThread()) {
|
||||
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));
|
||||
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);
|
||||
});
|
||||
}
|
||||
} else if (update instanceof TLRPC.TL_updateNewScheduledMessage) {
|
||||
final TLRPC.TL_updateNewScheduledMessage newMessage = (TLRPC.TL_updateNewScheduledMessage) update;
|
||||
newMessages.put(newMessage.message.id, newMessage.message);
|
||||
|
@ -5695,14 +5848,9 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
removeFromSendingMessages(newMsgObj.id, scheduled);
|
||||
});
|
||||
});
|
||||
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj)) {
|
||||
stopVideoService(attachPath);
|
||||
}
|
||||
|
||||
} else {
|
||||
AlertsCreator.processError(currentAccount, error, null, req);
|
||||
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj)) {
|
||||
stopVideoService(newMsgObj.attachPath);
|
||||
}
|
||||
removeFromSendingMessages(newMsgObj.id, scheduled);
|
||||
revertEditingMessageObject(msgObj);
|
||||
}
|
||||
|
@ -5794,6 +5942,14 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
sentMessages.add(message = newMessage.message);
|
||||
Utilities.stageQueue.postRunnable(() -> getMessagesController().processNewChannelDifferenceParams(newMessage.pts, newMessage.pts_count, newMessage.message.peer_id.channel_id));
|
||||
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;
|
||||
} else if (update instanceof TLRPC.TL_updateNewScheduledMessage) {
|
||||
final TLRPC.TL_updateNewScheduledMessage newMessage = (TLRPC.TL_updateNewScheduledMessage) update;
|
||||
|
@ -5866,9 +6022,6 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
processSentMessage(oldId);
|
||||
removeFromSendingMessages(oldId, scheduled);
|
||||
});
|
||||
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj)) {
|
||||
stopVideoService(attachPath);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
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);
|
||||
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;
|
||||
getNotificationCenter().postNotificationName(NotificationCenter.messageSendError, newMsgObj.id);
|
||||
processSentMessage(newMsgObj.id);
|
||||
if (MessageObject.isVideoMessage(newMsgObj) || MessageObject.isRoundVideoMessage(newMsgObj) || MessageObject.isNewGifMessage(newMsgObj)) {
|
||||
stopVideoService(newMsgObj.attachPath);
|
||||
}
|
||||
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_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) {
|
||||
return ERROR_TYPE_UNSUPPORTED;
|
||||
}
|
||||
|
@ -6681,6 +6828,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
} else {
|
||||
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.replyQuote = quote;
|
||||
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
|
||||
}
|
||||
});
|
||||
|
@ -6712,7 +6860,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
}
|
||||
|
||||
@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) {
|
||||
return;
|
||||
}
|
||||
|
@ -6727,11 +6875,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
paths.add(path);
|
||||
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
|
||||
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(() -> {
|
||||
int count = messageObjects.size();
|
||||
long groupId = 0;
|
||||
|
@ -6824,7 +6972,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
}
|
||||
|
||||
@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()) {
|
||||
return;
|
||||
}
|
||||
|
@ -6849,7 +6997,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
}
|
||||
mediaCount++;
|
||||
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) {
|
||||
mediaCount = 1;
|
||||
}
|
||||
|
@ -6870,7 +7018,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
}
|
||||
mediaCount++;
|
||||
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) {
|
||||
mediaCount = 1;
|
||||
}
|
||||
|
@ -6901,12 +7049,12 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
}
|
||||
|
||||
@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) {
|
||||
prepareSendingPhoto(accountInstance, imageFilePath, null, imageUri, dialogId, replyToMsg, replyToTopMsg, null, entities, stickers, inputContent, ttl, editingMessageObject, null, notify, scheduleDate, false, caption);
|
||||
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, null, entities, stickers, inputContent, ttl, editingMessageObject, null, notify, scheduleDate, false, caption);
|
||||
}
|
||||
|
||||
@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();
|
||||
info.path = imageFilePath;
|
||||
info.thumbPath = thumbFilePath;
|
||||
|
@ -6922,11 +7070,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
info.videoEditedInfo = videoEditedInfo;
|
||||
ArrayList<SendingMediaInfo> infos = new ArrayList<>();
|
||||
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
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
@ -7207,6 +7355,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
}
|
||||
if (params2 != null) {
|
||||
params2.replyToStoryItem = storyItem;
|
||||
params2.replyQuote = quote;
|
||||
accountInstance.getSendMessagesHelper().sendMessage(params2);
|
||||
}
|
||||
});
|
||||
|
@ -7279,6 +7428,11 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
messageMediaInvoice.total_amount = invoice.total_amount;
|
||||
messageMediaInvoice.start_param = "";
|
||||
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
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
|
@ -7481,7 +7635,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
|
||||
boolean isWebP = false;
|
||||
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;
|
||||
} else {
|
||||
info.forceImage = true;
|
||||
|
@ -7490,7 +7644,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
continue;
|
||||
} else if (tempPath == null && info.uri != null) {
|
||||
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;
|
||||
} else {
|
||||
info.forceImage = true;
|
||||
|
@ -7656,6 +7810,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
} 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.replyToStoryItem = storyItem;
|
||||
sendMessageParams.replyQuote = quote;
|
||||
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
|
||||
}
|
||||
});
|
||||
|
@ -7729,6 +7884,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
} 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.replyToStoryItem = storyItem;
|
||||
sendMessageParams.replyQuote = quote;
|
||||
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
|
||||
}
|
||||
});
|
||||
|
@ -7905,6 +8061,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
} 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.replyToStoryItem = storyItem;
|
||||
sendMessageParams.replyQuote = quote;
|
||||
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
|
||||
}
|
||||
});
|
||||
|
@ -8104,6 +8261,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
} 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.replyToStoryItem = storyItem;
|
||||
sendMessageParams.replyQuote = quote;
|
||||
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
|
||||
}
|
||||
});
|
||||
|
@ -8140,7 +8298,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
mediaCount = 0;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -8405,7 +8563,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
}
|
||||
|
||||
@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) {
|
||||
return;
|
||||
}
|
||||
|
@ -8554,11 +8712,12 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
} else {
|
||||
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.replyQuote = quote;
|
||||
accountInstance.getSendMessagesHelper().sendMessage(sendMessageParams);
|
||||
}
|
||||
});
|
||||
} 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();
|
||||
}
|
||||
|
@ -8574,6 +8733,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
public TLRPC.TL_game game;
|
||||
public TLRPC.TL_messageMediaPoll poll;
|
||||
public TLRPC.TL_messageMediaInvoice invoice;
|
||||
public TLRPC.TL_messageMediaWebPage mediaWebPage;
|
||||
public long peer;
|
||||
public String path;
|
||||
public MessageObject replyToMsg;
|
||||
|
@ -8591,9 +8751,10 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
|||
public MessageObject.SendAnimationData sendAnimationData;
|
||||
public boolean updateStickersOrder;
|
||||
public boolean hasMediaSpoilers;
|
||||
public TLRPC.StoryItem replyToStoryItem;
|
||||
public TLRPC.StoryItem sendingStory;
|
||||
|
||||
public TL_stories.StoryItem replyToStoryItem;
|
||||
public TL_stories.StoryItem sendingStory;
|
||||
public ChatActivity.ReplyQuote replyQuote;
|
||||
public boolean invert_media;
|
||||
|
||||
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);
|
||||
|
|
|
@ -21,6 +21,7 @@ import android.os.Environment;
|
|||
import android.os.SystemClock;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
@ -136,6 +137,14 @@ public class SharedConfig {
|
|||
return allowPreparingHevcPlayers;
|
||||
}
|
||||
|
||||
public static void togglePaymentByInvoice() {
|
||||
payByInvoice = !payByInvoice;
|
||||
ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putBoolean("payByInvoice", payByInvoice)
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static void toggleSurfaceInStories() {
|
||||
useSurfaceInStories = !useSurfaceInStories;
|
||||
ApplicationLoader.applicationContext.getSharedPreferences("mainconfig", Activity.MODE_PRIVATE)
|
||||
|
@ -238,17 +247,19 @@ public class SharedConfig {
|
|||
public static int scheduledHintShows;
|
||||
public static long scheduledHintSeenAt;
|
||||
public static int lockRecordAudioVideoHint;
|
||||
public static boolean forwardingOptionsHintShown;
|
||||
public static boolean forwardingOptionsHintShown, replyingOptionsHintShown;
|
||||
public static boolean searchMessagesAsListUsed;
|
||||
public static boolean stickersReorderingHintUsed;
|
||||
public static int dayNightWallpaperSwitchHint;
|
||||
public static boolean storyReactionsLongPressHint;
|
||||
public static boolean storiesIntroShown;
|
||||
public static boolean disableVoiceAudioEffects;
|
||||
public static boolean forceDisableTabletMode;
|
||||
public static boolean updateStickersOrderOnSend = true;
|
||||
public static boolean bigCameraForRound;
|
||||
public static boolean useSurfaceInStories;
|
||||
public static boolean photoViewerBlur = true;
|
||||
public static boolean payByInvoice;
|
||||
public static int stealthModeSendMessageConfirm = 2;
|
||||
private static int lastLocalId = -210000;
|
||||
|
||||
|
@ -437,6 +448,7 @@ public class SharedConfig {
|
|||
editor.putInt("scheduledHintShows", scheduledHintShows);
|
||||
editor.putLong("scheduledHintSeenAt", scheduledHintSeenAt);
|
||||
editor.putBoolean("forwardingOptionsHintShown", forwardingOptionsHintShown);
|
||||
editor.putBoolean("replyingOptionsHintShown", replyingOptionsHintShown);
|
||||
editor.putInt("lockRecordAudioVideoHint", lockRecordAudioVideoHint);
|
||||
editor.putString("storageCacheDir", !TextUtils.isEmpty(storageCacheDir) ? storageCacheDir : "");
|
||||
editor.putBoolean("proxyRotationEnabled", proxyRotationEnabled);
|
||||
|
@ -609,12 +621,14 @@ public class SharedConfig {
|
|||
searchMessagesAsListUsed = preferences.getBoolean("searchMessagesAsListUsed", false);
|
||||
stickersReorderingHintUsed = preferences.getBoolean("stickersReorderingHintUsed", false);
|
||||
storyReactionsLongPressHint = preferences.getBoolean("storyReactionsLongPressHint", false);
|
||||
storiesIntroShown = preferences.getBoolean("storiesIntroShown", false);
|
||||
textSelectionHintShows = preferences.getInt("textSelectionHintShows", 0);
|
||||
scheduledOrNoSoundHintShows = preferences.getInt("scheduledOrNoSoundHintShows", 0);
|
||||
scheduledOrNoSoundHintSeenAt = preferences.getLong("scheduledOrNoSoundHintSeenAt", 0);
|
||||
scheduledHintShows = preferences.getInt("scheduledHintShows", 0);
|
||||
scheduledHintSeenAt = preferences.getLong("scheduledHintSeenAt", 0);
|
||||
forwardingOptionsHintShown = preferences.getBoolean("forwardingOptionsHintShown", false);
|
||||
replyingOptionsHintShown = preferences.getBoolean("replyingOptionsHintShown", false);
|
||||
lockRecordAudioVideoHint = preferences.getInt("lockRecordAudioVideoHint", 0);
|
||||
disableVoiceAudioEffects = preferences.getBoolean("disableVoiceAudioEffects", false);
|
||||
noiseSupression = preferences.getBoolean("noiseSupression", false);
|
||||
|
@ -633,6 +647,7 @@ public class SharedConfig {
|
|||
dayNightWallpaperSwitchHint = preferences.getInt("dayNightWallpaperSwitchHint", 0);
|
||||
bigCameraForRound = preferences.getBoolean("bigCameraForRound", false);
|
||||
useSurfaceInStories = preferences.getBoolean("useSurfaceInStories", Build.VERSION.SDK_INT >= 30);
|
||||
payByInvoice = preferences.getBoolean("payByInvoice", false);
|
||||
photoViewerBlur = preferences.getBoolean("photoViewerBlur", true);
|
||||
|
||||
loadDebugConfig(preferences);
|
||||
|
@ -726,7 +741,7 @@ public class SharedConfig {
|
|||
}
|
||||
|
||||
public static boolean isAppUpdateAvailable() {
|
||||
if (pendingAppUpdate == null || pendingAppUpdate.document == null || !BuildVars.isStandaloneApp()) {
|
||||
if (pendingAppUpdate == null || pendingAppUpdate.document == null || !ApplicationLoader.isStandaloneBuild()) {
|
||||
return false;
|
||||
}
|
||||
int currentVersion;
|
||||
|
@ -838,6 +853,7 @@ public class SharedConfig {
|
|||
scheduledHintSeenAt = 0;
|
||||
lockRecordAudioVideoHint = 0;
|
||||
forwardingOptionsHintShown = false;
|
||||
replyingOptionsHintShown = false;
|
||||
messageSeenHintCount = 3;
|
||||
emojiInteractionsHintCount = 3;
|
||||
dayNightThemeSwitchHintCount = 3;
|
||||
|
@ -878,6 +894,14 @@ public class SharedConfig {
|
|||
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() {
|
||||
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
|
@ -925,6 +949,14 @@ public class SharedConfig {
|
|||
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() {
|
||||
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
|
|
|
@ -452,7 +452,7 @@ public class TopicsController extends BaseController {
|
|||
if (topicId != 0) {
|
||||
TLRPC.TL_forumTopic topic = findTopic(chat.id, topicId);
|
||||
if (topic != null) {
|
||||
return ForumUtilities.getTopicSpannedName(topic, paint, drawableToSet);
|
||||
return ForumUtilities.getTopicSpannedName(topic, paint, drawableToSet, false);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
|
|
@ -11,6 +11,7 @@ import android.view.inputmethod.InputMethodSubtype;
|
|||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.tgnet.tl.TL_stories;
|
||||
import org.telegram.ui.Components.Bulletin;
|
||||
import org.telegram.ui.Components.TranslateAlert2;
|
||||
import org.telegram.ui.RestrictedLanguagesSelectActivity;
|
||||
|
@ -1021,7 +1022,7 @@ public class TranslateController extends BaseController {
|
|||
private final HashSet<StoryKey> translatingStories = new HashSet<>();
|
||||
|
||||
// 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()) {
|
||||
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) && (
|
||||
storyItem.detectedLng == null && storyItem.translatedText != null && TextUtils.equals(storyItem.translatedLng, TranslateAlert2.getToLanguage()) ||
|
||||
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) {
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1131,7 +1132,7 @@ public class TranslateController extends BaseController {
|
|||
public long dialogId;
|
||||
public int storyId;
|
||||
|
||||
public StoryKey(TLRPC.StoryItem storyItem) {
|
||||
public StoryKey(TL_stories.StoryItem storyItem) {
|
||||
dialogId = storyItem.dialogId;
|
||||
storyId = storyItem.id;
|
||||
}
|
||||
|
|
|
@ -10,11 +10,15 @@ package org.telegram.messenger;
|
|||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.telegram.PhoneFormat.PhoneFormat;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
|
||||
public class UserObject {
|
||||
|
||||
public static final long REPLY_BOT = 1271266957L;
|
||||
|
||||
public static boolean isDeleted(TLRPC.User user) {
|
||||
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) {
|
||||
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) {
|
||||
return did == 708513 || did == 1271266957;
|
||||
return did == 708513 || did == REPLY_BOT;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static String getUserName(TLRPC.User user) {
|
||||
if (user == null || isDeleted(user)) {
|
||||
return LocaleController.getString("HiddenName", R.string.HiddenName);
|
||||
|
|
|
@ -522,6 +522,10 @@ public class Utilities {
|
|||
public ReturnType run();
|
||||
}
|
||||
|
||||
public static interface Callback0Return<ReturnType> {
|
||||
public ReturnType run();
|
||||
}
|
||||
|
||||
public static interface CallbackReturn<Arg, ReturnType> {
|
||||
public ReturnType run(Arg arg);
|
||||
}
|
||||
|
|
|
@ -17,12 +17,12 @@ import org.telegram.messenger.video.MediaCodecVideoConvertor;
|
|||
import org.telegram.tgnet.AbstractSerializedData;
|
||||
import org.telegram.tgnet.SerializedData;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.tgnet.tl.TL_stories;
|
||||
import org.telegram.ui.Components.AnimatedFileDrawable;
|
||||
import org.telegram.ui.Components.Paint.PaintTypeface;
|
||||
import org.telegram.ui.Components.PhotoFilterView;
|
||||
import org.telegram.ui.Components.Point;
|
||||
import org.telegram.ui.Components.Reactions.ReactionsLayoutInBubble;
|
||||
import org.telegram.ui.Components.Reactions.ReactionsUtils;
|
||||
import org.telegram.ui.Stories.recorder.StoryEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -154,7 +154,7 @@ public class VideoEditedInfo {
|
|||
public AnimatedFileDrawable animatedFileDrawable;
|
||||
public Canvas roundRadiusCanvas;
|
||||
|
||||
public TLRPC.MediaArea mediaArea;
|
||||
public TL_stories.MediaArea mediaArea;
|
||||
public TLRPC.MessageMedia mediaGeo;
|
||||
public float density;
|
||||
|
||||
|
@ -202,7 +202,7 @@ public class VideoEditedInfo {
|
|||
}
|
||||
if (type == TYPE_LOCATION) {
|
||||
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);
|
||||
if (data.remaining() > 0) {
|
||||
int magic = data.readInt32(false);
|
||||
|
@ -215,7 +215,7 @@ public class VideoEditedInfo {
|
|||
}
|
||||
}
|
||||
if (type == TYPE_REACTION) {
|
||||
mediaArea = TLRPC.MediaArea.TLdeserialize(data, data.readInt32(false), false);
|
||||
mediaArea = TL_stories.MediaArea.TLdeserialize(data, data.readInt32(false), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,34 +14,63 @@ import android.os.IBinder;
|
|||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
|
||||
public class VideoEncodingService extends Service implements NotificationCenter.NotificationCenterDelegate {
|
||||
|
||||
private NotificationCompat.Builder builder;
|
||||
private String path;
|
||||
private int currentProgress;
|
||||
private int currentAccount;
|
||||
private MediaController.VideoConvertMessage currentMessage;
|
||||
private static VideoEncodingService instance;
|
||||
|
||||
int currentAccount;
|
||||
String currentPath;
|
||||
|
||||
public VideoEncodingService() {
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
instance = null;
|
||||
try {
|
||||
stopForeground(true);
|
||||
} catch (Throwable ignore) {
|
||||
|
||||
}
|
||||
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.fileUploadFailed);
|
||||
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileUploaded);
|
||||
currentMessage = null;
|
||||
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) {
|
||||
if (id == NotificationCenter.fileUploadProgressChanged) {
|
||||
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 totalSize = (Long) args[2];
|
||||
float progress = Math.min(1f, loadedSize / (float) totalSize);
|
||||
Boolean enc = (Boolean) args[3];
|
||||
currentProgress = (int) (progress * 100);
|
||||
int currentProgress = (int) (progress * 100);
|
||||
builder.setProgress(100, currentProgress, currentProgress == 0);
|
||||
try {
|
||||
NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(4, builder.build());
|
||||
|
@ -62,54 +91,86 @@ public class VideoEncodingService extends Service implements NotificationCenter.
|
|||
FileLog.e(e);
|
||||
}
|
||||
}
|
||||
} else if (id == NotificationCenter.stopEncodingService) {
|
||||
String filepath = (String) args[0];
|
||||
account = (Integer) args[1];
|
||||
if (account == currentAccount && (filepath == null || filepath.equals(path))) {
|
||||
stopSelf();
|
||||
} else if (id == NotificationCenter.fileUploaded || id == NotificationCenter.fileUploadFailed) {
|
||||
String fileName = (String) args[0];
|
||||
if (account == currentAccount && currentPath != null && currentPath.equals(fileName)) {
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
MediaController.VideoConvertMessage message = MediaController.getInstance().getCurrentForegroundConverMessage();
|
||||
if (message != null) {
|
||||
setCurrentMessage(message);
|
||||
} else {
|
||||
stopSelf();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
path = intent.getStringExtra("path");
|
||||
int oldAccount = currentAccount;
|
||||
currentAccount = intent.getIntExtra("currentAccount", UserConfig.selectedAccount);
|
||||
if (!UserConfig.isValidAccount(currentAccount)) {
|
||||
stopSelf();
|
||||
if (isRunning()) {
|
||||
return Service.START_NOT_STICKY;
|
||||
}
|
||||
if (oldAccount != currentAccount) {
|
||||
NotificationCenter.getInstance(oldAccount).removeObserver(this, NotificationCenter.fileUploadProgressChanged);
|
||||
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");
|
||||
}
|
||||
instance = this;
|
||||
MediaController.VideoConvertMessage videoConvertMessage = MediaController.getInstance().getCurrentForegroundConverMessage();
|
||||
if (builder == null) {
|
||||
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.setWhen(System.currentTimeMillis());
|
||||
builder.setChannelId(NotificationsController.OTHER_NOTIFICATIONS_CHANNEL);
|
||||
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);
|
||||
startForeground(4, builder.build());
|
||||
NotificationManagerCompat.from(ApplicationLoader.applicationContext).notify(4, builder.build());
|
||||
|
||||
setCurrentMessage(videoConvertMessage);
|
||||
try {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.media.MediaMetadataRetriever;
|
|||
import android.media.MediaRecorder;
|
||||
import android.os.Build;
|
||||
import android.provider.MediaStore;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -577,8 +578,8 @@ public class CameraController implements MediaRecorder.OnInfoListener {
|
|||
}
|
||||
camera.setErrorCallback(getErrorListener(session));
|
||||
Camera.Parameters params = camera.getParameters();
|
||||
List<String> rawFlashModes = params.getSupportedFlashModes();
|
||||
|
||||
List<String> rawFlashModes = params.getSupportedFlashModes();
|
||||
session.availableFlashModes.clear();
|
||||
if (rawFlashModes != null) {
|
||||
for (int a = 0; a < rawFlashModes.size(); a++) {
|
||||
|
@ -587,7 +588,11 @@ public class CameraController implements MediaRecorder.OnInfoListener {
|
|||
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) {
|
||||
|
|
|
@ -53,7 +53,7 @@ public class CameraSession {
|
|||
private boolean isRound;
|
||||
private boolean destroyed;
|
||||
|
||||
protected ArrayList<String> availableFlashModes = new ArrayList<>();
|
||||
public ArrayList<String> availableFlashModes = new ArrayList<>();
|
||||
|
||||
private int infoCameraId = -1;
|
||||
Camera.CameraInfo info = new Camera.CameraInfo();
|
||||
|
|
|
@ -99,7 +99,7 @@ import javax.microedition.khronos.opengles.GL;
|
|||
@SuppressLint("NewApi")
|
||||
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;
|
||||
private Size[] previewSize = new Size[2];
|
||||
|
@ -989,20 +989,19 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
|
|||
|
||||
private float takePictureProgress = 1f;
|
||||
|
||||
public void startTakePictureAnimation() {
|
||||
public void startTakePictureAnimation(boolean haptic) {
|
||||
takePictureProgress = 0;
|
||||
invalidate();
|
||||
runHaptic();
|
||||
if (haptic) {
|
||||
runHaptic();
|
||||
}
|
||||
}
|
||||
|
||||
public void runHaptic() {
|
||||
long[] vibrationWaveFormDurationPattern = {0, 1};
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
|
||||
final Vibrator vibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
VibrationEffect vibrationEffect = VibrationEffect.createWaveform(vibrationWaveFormDurationPattern, -1);
|
||||
|
||||
vibrator.cancel();
|
||||
vibrator.vibrate(vibrationEffect);
|
||||
} else {
|
||||
|
@ -2649,6 +2648,7 @@ public class CameraView extends FrameLayout implements TextureView.SurfaceTextur
|
|||
movie.setRotation(0);
|
||||
movie.setSize(videoWidth, videoHeight);
|
||||
mediaMuxer = new MP4Builder().createMovie(movie, false, false);
|
||||
mediaMuxer.setAllowSyncFiles(false);
|
||||
|
||||
} catch (Exception ioe) {
|
||||
throw new RuntimeException(ioe);
|
||||
|
|
|
@ -19,13 +19,17 @@ import org.telegram.tgnet.SerializedData;
|
|||
import org.telegram.tgnet.TLRPC;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BillingUtilities {
|
||||
private static final String CURRENCY_FILE = "currencies.json";
|
||||
private static final String CURRENCY_EXP = "exp";
|
||||
|
||||
private static TLRPC.InputStorePaymentPurpose remPaymentPurpose;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static void extractCurrencyExp(Map<String, Integer> currencyExpMap) {
|
||||
if (!currencyExpMap.isEmpty()) {
|
||||
|
@ -56,7 +60,12 @@ public class BillingUtilities {
|
|||
paymentPurpose.serializeToStream(serializedData);
|
||||
String obfuscatedData = Base64.encodeToString(serializedData.toByteArray(), Base64.DEFAULT);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -86,10 +95,16 @@ public class BillingUtilities {
|
|||
}
|
||||
|
||||
try {
|
||||
byte[] obfuscatedDataBytes = Base64.decode(obfuscatedData, Base64.DEFAULT);
|
||||
SerializedData data = new SerializedData(obfuscatedDataBytes);
|
||||
TLRPC.InputStorePaymentPurpose purpose = TLRPC.InputStorePaymentPurpose.TLdeserialize(data, data.readInt32(true), true);
|
||||
data.cleanup();
|
||||
TLRPC.InputStorePaymentPurpose purpose;
|
||||
if (remPaymentPurpose == null) {
|
||||
byte[] obfuscatedDataBytes = Base64.decode(obfuscatedData, Base64.DEFAULT);
|
||||
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);
|
||||
long accountId = Long.parseLong(new String(obfuscatedAccountIdBytes, Charsets.UTF_8));
|
||||
|
|
|
@ -7,15 +7,18 @@ import android.text.Html;
|
|||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.StrikethroughSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.text.style.URLSpan;
|
||||
import android.text.style.UnderlineSpan;
|
||||
|
||||
import org.telegram.messenger.CodeHighlighting;
|
||||
import org.telegram.messenger.FileLog;
|
||||
import org.telegram.messenger.MediaDataController;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.ui.Components.AnimatedEmojiSpan;
|
||||
import org.telegram.ui.Components.QuoteSpan;
|
||||
import org.telegram.ui.Components.URLSpanReplacement;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.ContentHandler;
|
||||
|
@ -30,6 +33,7 @@ public class CopyUtilities {
|
|||
|
||||
private final static int TYPE_SPOILER = 0;
|
||||
private final static int TYPE_MONO = 1;
|
||||
private final static int TYPE_QUOTE = 2;
|
||||
|
||||
public static Spannable fromHTML(String html) {
|
||||
Spanned spanned;
|
||||
|
@ -49,6 +53,8 @@ public class CopyUtilities {
|
|||
|
||||
Object[] spans = spanned.getSpans(0, spanned.length(), Object.class);
|
||||
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) {
|
||||
Object span = spans[i];
|
||||
int start = spanned.getSpanStart(span);
|
||||
|
@ -70,7 +76,13 @@ public class CopyUtilities {
|
|||
if (parsedSpan.type == TYPE_SPOILER) {
|
||||
entities.add(setEntityStartEnd(new TLRPC.TL_messageEntitySpoiler(), start, end));
|
||||
} 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) {
|
||||
TLRPC.TL_messageEntityCustomEmoji entity = new TLRPC.TL_messageEntityCustomEmoji();
|
||||
|
@ -97,6 +109,25 @@ public class CopyUtilities {
|
|||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -242,7 +273,8 @@ public class CopyUtilities {
|
|||
}
|
||||
} else if (tag.equals("pre")) {
|
||||
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;
|
||||
} else {
|
||||
ParsedSpan obj = getLast(output, ParsedSpan.class, TYPE_MONO);
|
||||
|
@ -255,6 +287,21 @@ public class CopyUtilities {
|
|||
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;
|
||||
}
|
||||
|
@ -290,9 +337,15 @@ public class CopyUtilities {
|
|||
|
||||
private static class ParsedSpan {
|
||||
final int type;
|
||||
final String lng;
|
||||
|
||||
private ParsedSpan(int type) {
|
||||
this.type = type;
|
||||
this.lng = null;
|
||||
}
|
||||
private ParsedSpan(int type, String lng) {
|
||||
this.type = type;
|
||||
this.lng = lng;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,15 @@ package org.telegram.messenger.utils;
|
|||
|
||||
import android.text.Spanned;
|
||||
|
||||
import org.telegram.messenger.CodeHighlighting;
|
||||
import org.telegram.ui.Components.AnimatedEmojiSpan;
|
||||
import org.telegram.ui.Components.QuoteSpan;
|
||||
import org.telegram.ui.Components.TextStyleSpan;
|
||||
import org.telegram.ui.Components.URLSpanMono;
|
||||
import org.telegram.ui.Components.URLSpanReplacement;
|
||||
|
||||
public class CustomHtml {
|
||||
|
||||
|
||||
private CustomHtml() { }
|
||||
|
||||
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) {
|
||||
for (int j = 0; j < spans.length; ++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;
|
||||
for (int i = start; i < end; i = next) {
|
||||
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) {
|
||||
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++) {
|
||||
char c = text.charAt(i);
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@ public class PhotoUtilities {
|
|||
File src = FileLoader.getInstance(chatActivity.getCurrentAccount()).getPathToAttach(avatarBig[0], true);
|
||||
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<>();
|
||||
users.add(user);
|
||||
chatActivity.getMessagesStorage().putUsersAndChats(users, null, false, true);
|
||||
|
|
|
@ -62,6 +62,7 @@ public class MP4Builder {
|
|||
private ByteBuffer sizeBuffer = null;
|
||||
private boolean splitMdat;
|
||||
private boolean wasFirstVideoFrame;
|
||||
private boolean allowSyncFiles = true;
|
||||
|
||||
public MP4Builder createMovie(Mp4Movie mp4Movie, boolean split, boolean hevc) throws Exception {
|
||||
currentMp4Movie = mp4Movie;
|
||||
|
@ -90,7 +91,9 @@ public class MP4Builder {
|
|||
mdat.setDataOffset(0);
|
||||
mdat.setContentSize(0);
|
||||
fos.flush();
|
||||
fos.getFD().sync();
|
||||
if (allowSyncFiles) {
|
||||
fos.getFD().sync();
|
||||
}
|
||||
}
|
||||
|
||||
public long writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo, boolean writeLength) throws Exception {
|
||||
|
@ -163,7 +166,9 @@ public class MP4Builder {
|
|||
|
||||
if (flush) {
|
||||
fos.flush();
|
||||
fos.getFD().sync();
|
||||
if (allowSyncFiles) {
|
||||
fos.getFD().sync();
|
||||
}
|
||||
return fc.position();
|
||||
}
|
||||
return 0;
|
||||
|
@ -194,7 +199,9 @@ public class MP4Builder {
|
|||
Box moov = createMovieBox(currentMp4Movie);
|
||||
moov.getBox(fc);
|
||||
fos.flush();
|
||||
fos.getFD().sync();
|
||||
if (allowSyncFiles) {
|
||||
fos.getFD().sync();
|
||||
}
|
||||
|
||||
fc.close();
|
||||
fos.close();
|
||||
|
@ -209,6 +216,10 @@ public class MP4Builder {
|
|||
return new FileTypeBox("isom", 512, minorBrands);
|
||||
}
|
||||
|
||||
public void setAllowSyncFiles(boolean allowSyncFiles) {
|
||||
this.allowSyncFiles = allowSyncFiles;
|
||||
}
|
||||
|
||||
private static class InterleaveChunkMdat implements Box {
|
||||
private Container parent;
|
||||
private long contentSize = 1024 * 1024 * 1024;
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.graphics.Paint;
|
|||
import android.graphics.SurfaceTexture;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.TextureView;
|
||||
|
||||
|
@ -355,10 +356,14 @@ public class VideoPlayerHolderBase {
|
|||
} else {
|
||||
localProgress = currentPosition / (float) playerDuration;
|
||||
}
|
||||
if (localProgress < progress) {
|
||||
return progress;
|
||||
}
|
||||
// if (localProgress < progress) {
|
||||
// return progress;
|
||||
// }
|
||||
progress = localProgress;
|
||||
if (!seeking) {
|
||||
currentSeek = progress;
|
||||
lastSeek = currentPosition;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ import android.telephony.TelephonyManager;
|
|||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.util.Log;
|
||||
import android.util.LruCache;
|
||||
import android.view.KeyEvent;
|
||||
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);
|
||||
} else {
|
||||
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) {
|
||||
Intent endIntent = new Intent(this, VoIPActionsReceiver.class);
|
||||
|
@ -3593,7 +3595,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
|||
if (groupCall != null) {
|
||||
bldr.setSmallIcon(isMicMute() ? R.drawable.voicechat_muted : R.drawable.voicechat_active);
|
||||
} else {
|
||||
bldr.setSmallIcon(R.drawable.notification);
|
||||
bldr.setSmallIcon(R.drawable.ic_call);
|
||||
}
|
||||
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)
|
||||
.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));
|
||||
Uri soundProviderUri = Uri.parse("content://" + ApplicationLoader.getApplicationId() + ".call_sound_provider/start_ringing");
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
SharedPreferences nprefs = MessagesController.getGlobalNotificationsSettings();
|
||||
int chanIndex = nprefs.getInt("calls_notification_channel", 0);
|
||||
NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
|
@ -4046,14 +4047,18 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
|||
if (oldChannel != null) {
|
||||
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;
|
||||
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) {
|
||||
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++;
|
||||
nprefs.edit().putInt("calls_notification_channel", chanIndex).commit();
|
||||
} else {
|
||||
|
@ -4066,8 +4071,13 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
|||
.setLegacyStreamType(AudioManager.STREAM_RING)
|
||||
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
|
||||
.build();
|
||||
NotificationChannel chan = new NotificationChannel("incoming_calls3" + chanIndex, LocaleController.getString("IncomingCalls", R.string.IncomingCalls), NotificationManager.IMPORTANCE_HIGH);
|
||||
chan.setSound(soundProviderUri, attrs);
|
||||
NotificationChannel chan = new NotificationChannel("incoming_calls4" + chanIndex, LocaleController.getString("IncomingCallsSystemSetting", R.string.IncomingCallsSystemSetting), NotificationManager.IMPORTANCE_HIGH);
|
||||
try {
|
||||
chan.setSound(null, attrs);
|
||||
} catch (Exception e) {
|
||||
FileLog.e(e);
|
||||
}
|
||||
chan.setDescription(LocaleController.getString("IncomingCallsSystemSettingDescription", R.string.IncomingCallsSystemSettingDescription));
|
||||
chan.enableVibration(false);
|
||||
chan.enableLights(false);
|
||||
chan.setBypassDnd(true);
|
||||
|
@ -4079,9 +4089,9 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
|||
return;
|
||||
}
|
||||
}
|
||||
builder.setChannelId("incoming_calls3" + chanIndex);
|
||||
builder.setChannelId("incoming_calls4" + chanIndex);
|
||||
} 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);
|
||||
endIntent.setAction(getPackageName() + ".DECLINE_CALL");
|
||||
|
@ -4120,13 +4130,13 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
|||
Notification incomingNotification;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
Bitmap avatar = getRoundAvatarBitmap(userOrChat);
|
||||
String presonName = ContactsController.formatName(userOrChat);
|
||||
if (TextUtils.isEmpty(presonName)) {
|
||||
String personName = ContactsController.formatName(userOrChat);
|
||||
if (TextUtils.isEmpty(personName)) {
|
||||
//java.lang.IllegalArgumentException: person must have a non-empty a name
|
||||
presonName = "___";
|
||||
personName = "___";
|
||||
}
|
||||
Person person = new Person.Builder()
|
||||
.setName(presonName)
|
||||
.setName(personName)
|
||||
.setIcon(Icon.createWithAdaptiveBitmap(avatar)).build();
|
||||
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
Loading…
Reference in a new issue