mirror of
https://github.com/DrKLO/Telegram.git
synced 2024-12-21 22:15:16 +01:00
Update to 8.0.0 (2406)
This commit is contained in:
parent
ab221dafad
commit
368822d20f
191 changed files with 12004 additions and 2919 deletions
|
@ -1,4 +1,4 @@
|
||||||
FROM gradle:6.7.1-jdk11
|
FROM gradle:7.0.2-jdk11
|
||||||
|
|
||||||
ENV ANDROID_SDK_URL https://dl.google.com/android/repository/commandlinetools-linux-7302050_latest.zip
|
ENV ANDROID_SDK_URL https://dl.google.com/android/repository/commandlinetools-linux-7302050_latest.zip
|
||||||
ENV ANDROID_API_LEVEL android-31
|
ENV ANDROID_API_LEVEL android-31
|
||||||
|
|
|
@ -25,7 +25,7 @@ dependencies {
|
||||||
compileOnly 'org.checkerframework:checker-qual:2.5.2'
|
compileOnly 'org.checkerframework:checker-qual:2.5.2'
|
||||||
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
|
compileOnly 'org.checkerframework:checker-compat-qual:2.5.0'
|
||||||
implementation 'com.google.firebase:firebase-messaging:22.0.0'
|
implementation 'com.google.firebase:firebase-messaging:22.0.0'
|
||||||
implementation 'com.google.firebase:firebase-config:21.0.0'
|
implementation 'com.google.firebase:firebase-config:21.0.1'
|
||||||
implementation 'com.google.firebase:firebase-datatransport:18.0.1'
|
implementation 'com.google.firebase:firebase-datatransport:18.0.1'
|
||||||
implementation 'com.google.firebase:firebase-appindexing:20.0.0'
|
implementation 'com.google.firebase:firebase-appindexing:20.0.0'
|
||||||
implementation 'com.google.android.gms:play-services-maps:17.0.1'
|
implementation 'com.google.android.gms:play-services-maps:17.0.1'
|
||||||
|
@ -299,7 +299,7 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig.versionCode = 2390
|
defaultConfig.versionCode = 2406
|
||||||
|
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.all { variant ->
|
||||||
variant.outputs.all { output ->
|
variant.outputs.all { output ->
|
||||||
|
@ -318,7 +318,7 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionName "7.9.3"
|
versionName "8.0.0"
|
||||||
|
|
||||||
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
|
vectorDrawables.generatedDensities = ['mdpi', 'hdpi', 'xhdpi', 'xxhdpi']
|
||||||
|
|
||||||
|
|
|
@ -1224,7 +1224,7 @@ JNIEXPORT void Java_org_telegram_messenger_Utilities_generateGradient(JNIEnv *en
|
||||||
float directPixelY;
|
float directPixelY;
|
||||||
float centerDistanceY;
|
float centerDistanceY;
|
||||||
float centerDistanceY2;
|
float centerDistanceY2;
|
||||||
int32_t colorsCount = colorsArray[12] == 0 ? 3 : 4;
|
int32_t colorsCount = colorsArray[12] == 0 && colorsArray[13] == 0 && colorsArray[14] == 0 && colorsArray[15] == 0 ? 3 : 4;
|
||||||
|
|
||||||
for (int y = 0; y < height; y++) {
|
for (int y = 0; y < height; y++) {
|
||||||
if (pixelCache == nullptr) {
|
if (pixelCache == nullptr) {
|
||||||
|
|
|
@ -2499,7 +2499,7 @@ void ConnectionsManager::processRequestQueue(uint32_t connectionTypes, uint32_t
|
||||||
} else {
|
} else {
|
||||||
currentCount = 0;
|
currentCount = 0;
|
||||||
}
|
}
|
||||||
if (!networkAvailable || currentCount >= 12) {
|
if (!networkAvailable || currentCount >= 16) {
|
||||||
iter++;
|
iter++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -918,7 +918,9 @@ add_library(tgcalls STATIC
|
||||||
voip/tgcalls/group/GroupNetworkManager.cpp
|
voip/tgcalls/group/GroupNetworkManager.cpp
|
||||||
voip/tgcalls/group/GroupInstanceCustomImpl.cpp
|
voip/tgcalls/group/GroupInstanceCustomImpl.cpp
|
||||||
voip/tgcalls/group/GroupJoinPayloadInternal.cpp
|
voip/tgcalls/group/GroupJoinPayloadInternal.cpp
|
||||||
voip/tgcalls/group/StreamingPart.cpp
|
voip/tgcalls/group/AudioStreamingPart.cpp
|
||||||
|
voip/tgcalls/group/VideoStreamingPart.cpp
|
||||||
|
voip/tgcalls/group/StreamingMediaContext.cpp
|
||||||
voip/tgcalls/third-party/json11.cpp
|
voip/tgcalls/third-party/json11.cpp
|
||||||
|
|
||||||
voip/webrtc/rtc_base/async_invoker.cc
|
voip/webrtc/rtc_base/async_invoker.cc
|
||||||
|
|
|
@ -76,10 +76,12 @@ class BroadcastPartTaskJava : public BroadcastPartTask {
|
||||||
public:
|
public:
|
||||||
BroadcastPartTaskJava(std::shared_ptr<PlatformContext> platformContext,
|
BroadcastPartTaskJava(std::shared_ptr<PlatformContext> platformContext,
|
||||||
std::function<void(BroadcastPart &&)> callback,
|
std::function<void(BroadcastPart &&)> callback,
|
||||||
int64_t timestamp) :
|
int64_t timestamp, int32_t videoChannel, VideoChannelDescription::Quality quality) :
|
||||||
_platformContext(std::move(platformContext)),
|
_platformContext(std::move(platformContext)),
|
||||||
_callback(std::move(callback)),
|
_callback(std::move(callback)),
|
||||||
_timestamp(timestamp) {
|
_timestamp(timestamp),
|
||||||
|
_videoChannel(videoChannel),
|
||||||
|
_quality(quality) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void call(int64_t ts, int64_t responseTs, BroadcastPart::Status status, uint8_t *data, int32_t len) {
|
void call(int64_t ts, int64_t responseTs, BroadcastPart::Status status, uint8_t *data, int32_t len) {
|
||||||
|
@ -91,22 +93,48 @@ public:
|
||||||
part.responseTimestamp = responseTs / 1000.0;
|
part.responseTimestamp = responseTs / 1000.0;
|
||||||
part.status = status;
|
part.status = status;
|
||||||
if (data != nullptr) {
|
if (data != nullptr) {
|
||||||
part.oggData = std::vector<uint8_t>(data, data + len);
|
part.data = std::vector<uint8_t>(data, data + len);
|
||||||
}
|
}
|
||||||
_callback(std::move<>(part));
|
_callback(std::move<>(part));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isValidTaskFor(int64_t timestamp, int32_t videoChannel, VideoChannelDescription::Quality quality) {
|
||||||
|
if (_videoChannel == 0) {
|
||||||
|
return _timestamp == timestamp;
|
||||||
|
} else {
|
||||||
|
return _timestamp == timestamp && _videoChannel == videoChannel && _quality == quality;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void cancel() override {
|
void cancel() override {
|
||||||
tgvoip::jni::DoWithJNI([&](JNIEnv *env) {
|
tgvoip::jni::DoWithJNI([&](JNIEnv *env) {
|
||||||
jobject globalRef = ((AndroidContext *) _platformContext.get())->getJavaInstance();
|
auto context = (AndroidContext *) _platformContext.get();
|
||||||
env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onCancelRequestBroadcastPart", "(J)V"), _timestamp);
|
jobject globalRef = context->getJavaInstance();
|
||||||
|
env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onCancelRequestBroadcastPart", "(JII)V"), _timestamp, _videoChannel, (jint) _quality);
|
||||||
|
if (_videoChannel != 0) {
|
||||||
|
for (auto videoTaskIter = context->videoStreamTasks.begin(); videoTaskIter != context->videoStreamTasks.end(); videoTaskIter++) {
|
||||||
|
if (((BroadcastPartTaskJava *) videoTaskIter->get())->isValidTaskFor(_timestamp, _videoChannel, _quality)) {
|
||||||
|
context->videoStreamTasks.erase(videoTaskIter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (auto audioTaskIter = context->audioStreamTasks.begin(); audioTaskIter != context->audioStreamTasks.end(); audioTaskIter++) {
|
||||||
|
if (((BroadcastPartTaskJava *) audioTaskIter->get())->isValidTaskFor(_timestamp, _videoChannel, _quality)) {
|
||||||
|
context->audioStreamTasks.erase(audioTaskIter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<PlatformContext> _platformContext;
|
std::shared_ptr<PlatformContext> _platformContext;
|
||||||
std::function<void(BroadcastPart &&)> _callback;
|
std::function<void(BroadcastPart &&)> _callback;
|
||||||
int64_t _timestamp;
|
int64_t _timestamp;
|
||||||
|
int32_t _videoChannel;
|
||||||
|
VideoChannelDescription::Quality _quality;
|
||||||
};
|
};
|
||||||
|
|
||||||
class JavaObject {
|
class JavaObject {
|
||||||
|
@ -399,12 +427,21 @@ JNIEXPORT jlong JNICALL Java_org_telegram_messenger_voip_NativeInstance_makeGrou
|
||||||
.platformContext = platformContext
|
.platformContext = platformContext
|
||||||
};
|
};
|
||||||
if (!screencast) {
|
if (!screencast) {
|
||||||
descriptor.requestBroadcastPart = [](std::shared_ptr<PlatformContext> platformContext, int64_t timestamp, int64_t duration, std::function<void(BroadcastPart &&)> callback) -> std::shared_ptr<BroadcastPartTask> {
|
descriptor.requestAudioBroadcastPart = [](std::shared_ptr<PlatformContext> platformContext, int64_t timestamp, int64_t duration, std::function<void(BroadcastPart &&)> callback) -> std::shared_ptr<BroadcastPartTask> {
|
||||||
std::shared_ptr<BroadcastPartTask> task = std::make_shared<BroadcastPartTaskJava>(platformContext, callback, timestamp);
|
std::shared_ptr<BroadcastPartTask> task = std::make_shared<BroadcastPartTaskJava>(platformContext, callback, timestamp, 0, VideoChannelDescription::Quality::Full);
|
||||||
((AndroidContext *) platformContext.get())->streamTask = task;
|
((AndroidContext *) platformContext.get())->audioStreamTasks.push_back(task);
|
||||||
tgvoip::jni::DoWithJNI([platformContext, timestamp, duration, task](JNIEnv *env) {
|
tgvoip::jni::DoWithJNI([platformContext, timestamp, duration, task](JNIEnv *env) {
|
||||||
jobject globalRef = ((AndroidContext *) platformContext.get())->getJavaInstance();
|
jobject globalRef = ((AndroidContext *) platformContext.get())->getJavaInstance();
|
||||||
env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onRequestBroadcastPart", "(JJ)V"), timestamp, duration);
|
env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onRequestBroadcastPart", "(JJII)V"), timestamp, duration, 0, 0);
|
||||||
|
});
|
||||||
|
return task;
|
||||||
|
};
|
||||||
|
descriptor.requestVideoBroadcastPart = [](std::shared_ptr<PlatformContext> platformContext, int64_t timestamp, int64_t duration, int32_t video_channel, VideoChannelDescription::Quality quality, std::function<void(BroadcastPart &&)> callback) -> std::shared_ptr<BroadcastPartTask> {
|
||||||
|
std::shared_ptr<BroadcastPartTask> task = std::make_shared<BroadcastPartTaskJava>(platformContext, callback, timestamp, video_channel, quality);
|
||||||
|
((AndroidContext *) platformContext.get())->videoStreamTasks.push_back(task);
|
||||||
|
tgvoip::jni::DoWithJNI([platformContext, timestamp, duration, task, video_channel, quality](JNIEnv *env) {
|
||||||
|
jobject globalRef = ((AndroidContext *) platformContext.get())->getJavaInstance();
|
||||||
|
env->CallVoidMethod(globalRef, env->GetMethodID(NativeInstanceClass, "onRequestBroadcastPart", "(JJII)V"), timestamp, duration, video_channel, (jint) quality);
|
||||||
});
|
});
|
||||||
return task;
|
return task;
|
||||||
};
|
};
|
||||||
|
@ -812,20 +849,37 @@ JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_stopGroup
|
||||||
delete instance;
|
delete instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_onStreamPartAvailable(JNIEnv *env, jobject obj, jlong ts, jobject byteBuffer, jint size, jlong responseTs) {
|
JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_onStreamPartAvailable(JNIEnv *env, jobject obj, jlong ts, jobject byteBuffer, jint size, jlong responseTs, jint videoChannel, jint quality) {
|
||||||
InstanceHolder *instance = getInstanceHolder(env, obj);
|
InstanceHolder *instance = getInstanceHolder(env, obj);
|
||||||
if (instance->groupNativeInstance == nullptr) {
|
if (instance->groupNativeInstance == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto context = (AndroidContext *) instance->_platformContext.get();
|
auto context = (AndroidContext *) instance->_platformContext.get();
|
||||||
std::shared_ptr<BroadcastPartTask> streamTask = context->streamTask;
|
std::shared_ptr<BroadcastPartTask> task;
|
||||||
auto task = (BroadcastPartTaskJava *) streamTask.get();
|
auto q = (VideoChannelDescription::Quality) quality;
|
||||||
|
if (videoChannel != 0) {
|
||||||
|
for (auto videoTaskIter = context->videoStreamTasks.begin(); videoTaskIter != context->videoStreamTasks.end(); videoTaskIter++) {
|
||||||
|
if (((BroadcastPartTaskJava *) videoTaskIter->get())->isValidTaskFor(ts, videoChannel, q)) {
|
||||||
|
task = *videoTaskIter;
|
||||||
|
context->videoStreamTasks.erase(videoTaskIter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (auto audioTaskIter = context->audioStreamTasks.begin(); audioTaskIter != context->audioStreamTasks.end(); audioTaskIter++) {
|
||||||
|
if (((BroadcastPartTaskJava *) audioTaskIter->get())->isValidTaskFor(ts, 0, q)) {
|
||||||
|
task = *audioTaskIter;
|
||||||
|
context->audioStreamTasks.erase(audioTaskIter);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (task != nullptr) {
|
if (task != nullptr) {
|
||||||
if (byteBuffer != nullptr) {
|
if (byteBuffer != nullptr) {
|
||||||
auto buf = (uint8_t *) env->GetDirectBufferAddress(byteBuffer);
|
auto buf = (uint8_t *) env->GetDirectBufferAddress(byteBuffer);
|
||||||
task->call(ts, responseTs, BroadcastPart::Status::Success, buf, size);
|
((BroadcastPartTaskJava *) task.get())->call(ts, responseTs, BroadcastPart::Status::Success, buf, size);
|
||||||
} else {
|
} else {
|
||||||
task->call(ts, responseTs, size == 0 ? BroadcastPart::Status::NotReady : BroadcastPart::Status::ResyncNeeded, nullptr, 0);
|
((BroadcastPartTaskJava *) task.get())->call(ts, responseTs, size == 0 ? BroadcastPart::Status::NotReady : BroadcastPart::Status::ResyncNeeded, nullptr, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -886,7 +940,7 @@ JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_destroyVi
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_switchCameraCapturer(JNIEnv *env, jclass clazz, jlong videoCapturer, jboolean front) {
|
JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_switchCameraCapturer(JNIEnv *env, jclass clazz, jlong videoCapturer, jboolean front) {
|
||||||
auto capturer = reinterpret_cast<VideoCaptureInterface *>(videoCapturer);
|
auto capturer = reinterpret_cast<VideoCaptureInterface *>(videoCapturer);
|
||||||
capturer->switchToDevice(front ? "front" : "back");
|
capturer->switchToDevice(front ? "front" : "back", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_setVideoStateCapturer(JNIEnv *env, jclass clazz, jlong videoCapturer, jint videoState) {
|
JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_setVideoStateCapturer(JNIEnv *env, jclass clazz, jlong videoCapturer, jint videoState) {
|
||||||
|
@ -899,7 +953,7 @@ JNIEXPORT void JNICALL Java_org_telegram_messenger_voip_NativeInstance_switchCam
|
||||||
if (instance->_videoCapture == nullptr) {
|
if (instance->_videoCapture == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
instance->_videoCapture->switchToDevice(front ? "front" : "back");
|
instance->_videoCapture->switchToDevice(front ? "front" : "back", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jboolean JNICALL Java_org_telegram_messenger_voip_NativeInstance_hasVideoCapturer(JNIEnv *env, jobject obj) {
|
JNIEXPORT jboolean JNICALL Java_org_telegram_messenger_voip_NativeInstance_hasVideoCapturer(JNIEnv *env, jobject obj) {
|
||||||
|
|
|
@ -204,6 +204,7 @@ public:
|
||||||
|
|
||||||
virtual void receiveSignalingData(const std::vector<uint8_t> &data) = 0;
|
virtual void receiveSignalingData(const std::vector<uint8_t> &data) = 0;
|
||||||
virtual void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) = 0;
|
virtual void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) = 0;
|
||||||
|
virtual void sendVideoDeviceUpdated() = 0;
|
||||||
virtual void setRequestedVideoAspect(float aspect) = 0;
|
virtual void setRequestedVideoAspect(float aspect) = 0;
|
||||||
|
|
||||||
virtual void stop(std::function<void(FinalState)> completion) = 0;
|
virtual void stop(std::function<void(FinalState)> completion) = 0;
|
||||||
|
|
|
@ -58,6 +58,12 @@ void InstanceImpl::setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoC
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceImpl::sendVideoDeviceUpdated() {
|
||||||
|
_manager->perform(RTC_FROM_HERE, [](Manager *manager) {
|
||||||
|
manager->sendVideoDeviceUpdated();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceImpl::setRequestedVideoAspect(float aspect) {
|
void InstanceImpl::setRequestedVideoAspect(float aspect) {
|
||||||
_manager->perform(RTC_FROM_HERE, [aspect](Manager *manager) {
|
_manager->perform(RTC_FROM_HERE, [aspect](Manager *manager) {
|
||||||
manager->setRequestedVideoAspect(aspect);
|
manager->setRequestedVideoAspect(aspect);
|
||||||
|
|
|
@ -21,6 +21,7 @@ public:
|
||||||
|
|
||||||
void receiveSignalingData(const std::vector<uint8_t> &data) override;
|
void receiveSignalingData(const std::vector<uint8_t> &data) override;
|
||||||
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) override;
|
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) override;
|
||||||
|
void sendVideoDeviceUpdated() override;
|
||||||
void setRequestedVideoAspect(float aspect) override;
|
void setRequestedVideoAspect(float aspect) override;
|
||||||
void setNetworkType(NetworkType networkType) override;
|
void setNetworkType(NetworkType networkType) override;
|
||||||
void setMuteMicrophone(bool muteMicrophone) override;
|
void setMuteMicrophone(bool muteMicrophone) override;
|
||||||
|
|
|
@ -316,6 +316,12 @@ void Manager::setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCaptur
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Manager::sendVideoDeviceUpdated() {
|
||||||
|
_mediaManager->perform(RTC_FROM_HERE, [](MediaManager *mediaManager) {
|
||||||
|
mediaManager->sendVideoDeviceUpdated();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void Manager::setRequestedVideoAspect(float aspect) {
|
void Manager::setRequestedVideoAspect(float aspect) {
|
||||||
_mediaManager->perform(RTC_FROM_HERE, [aspect](MediaManager *mediaManager) {
|
_mediaManager->perform(RTC_FROM_HERE, [aspect](MediaManager *mediaManager) {
|
||||||
mediaManager->setRequestedVideoAspect(aspect);
|
mediaManager->setRequestedVideoAspect(aspect);
|
||||||
|
|
|
@ -29,6 +29,7 @@ public:
|
||||||
void start();
|
void start();
|
||||||
void receiveSignalingData(const std::vector<uint8_t> &data);
|
void receiveSignalingData(const std::vector<uint8_t> &data);
|
||||||
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture);
|
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture);
|
||||||
|
void sendVideoDeviceUpdated();
|
||||||
void setRequestedVideoAspect(float aspect);
|
void setRequestedVideoAspect(float aspect);
|
||||||
void setMuteOutgoingAudio(bool mute);
|
void setMuteOutgoingAudio(bool mute);
|
||||||
void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink);
|
void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink);
|
||||||
|
|
|
@ -641,17 +641,18 @@ void MediaManager::setSendVideo(std::shared_ptr<VideoCaptureInterface> videoCapt
|
||||||
_videoCapture = videoCapture;
|
_videoCapture = videoCapture;
|
||||||
if (_videoCapture) {
|
if (_videoCapture) {
|
||||||
_videoCapture->setPreferredAspectRatio(_preferredAspectRatio);
|
_videoCapture->setPreferredAspectRatio(_preferredAspectRatio);
|
||||||
_isScreenCapture = _videoCapture->isScreenCapture();
|
|
||||||
|
|
||||||
const auto thread = _thread;
|
const auto thread = _thread;
|
||||||
const auto weak = std::weak_ptr<MediaManager>(shared_from_this());
|
const auto weak = std::weak_ptr<MediaManager>(shared_from_this());
|
||||||
GetVideoCaptureAssumingSameThread(_videoCapture.get())->setStateUpdated([=](VideoState state) {
|
const auto object = GetVideoCaptureAssumingSameThread(_videoCapture.get());
|
||||||
thread->PostTask(RTC_FROM_HERE, [=] {
|
_isScreenCapture = object->isScreenCapture();
|
||||||
if (const auto strong = weak.lock()) {
|
object->setStateUpdated([=](VideoState state) {
|
||||||
strong->setOutgoingVideoState(state);
|
thread->PostTask(RTC_FROM_HERE, [=] {
|
||||||
}
|
if (const auto strong = weak.lock()) {
|
||||||
});
|
strong->setOutgoingVideoState(state);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
setOutgoingVideoState(VideoState::Active);
|
setOutgoingVideoState(VideoState::Active);
|
||||||
} else {
|
} else {
|
||||||
_isScreenCapture = false;
|
_isScreenCapture = false;
|
||||||
|
@ -681,6 +682,18 @@ void MediaManager::setSendVideo(std::shared_ptr<VideoCaptureInterface> videoCapt
|
||||||
checkIsReceivingVideoChanged(wasReceiving);
|
checkIsReceivingVideoChanged(wasReceiving);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MediaManager::sendVideoDeviceUpdated() {
|
||||||
|
if (!computeIsSendingVideo()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto wasScreenCapture = _isScreenCapture;
|
||||||
|
const auto object = GetVideoCaptureAssumingSameThread(_videoCapture.get());
|
||||||
|
_isScreenCapture = object->isScreenCapture();
|
||||||
|
if (_isScreenCapture != wasScreenCapture) {
|
||||||
|
adjustBitratePreferences(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MediaManager::setRequestedVideoAspect(float aspect) {
|
void MediaManager::setRequestedVideoAspect(float aspect) {
|
||||||
if (_localPreferredVideoAspectRatio != aspect) {
|
if (_localPreferredVideoAspectRatio != aspect) {
|
||||||
_localPreferredVideoAspectRatio = aspect;
|
_localPreferredVideoAspectRatio = aspect;
|
||||||
|
|
|
@ -58,6 +58,7 @@ public:
|
||||||
void setIsConnected(bool isConnected);
|
void setIsConnected(bool isConnected);
|
||||||
void notifyPacketSent(const rtc::SentPacket &sentPacket);
|
void notifyPacketSent(const rtc::SentPacket &sentPacket);
|
||||||
void setSendVideo(std::shared_ptr<VideoCaptureInterface> videoCapture);
|
void setSendVideo(std::shared_ptr<VideoCaptureInterface> videoCapture);
|
||||||
|
void sendVideoDeviceUpdated();
|
||||||
void setRequestedVideoAspect(float aspect);
|
void setRequestedVideoAspect(float aspect);
|
||||||
void setMuteOutgoingAudio(bool mute);
|
void setMuteOutgoingAudio(bool mute);
|
||||||
void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink);
|
void setIncomingVideoOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink);
|
||||||
|
|
|
@ -39,8 +39,7 @@ public:
|
||||||
|
|
||||||
virtual ~VideoCaptureInterface();
|
virtual ~VideoCaptureInterface();
|
||||||
|
|
||||||
virtual bool isScreenCapture() = 0;
|
virtual void switchToDevice(std::string deviceId, bool isScreenCapture) = 0;
|
||||||
virtual void switchToDevice(std::string deviceId) = 0;
|
|
||||||
virtual void setState(VideoState state) = 0;
|
virtual void setState(VideoState state) = 0;
|
||||||
virtual void setPreferredAspectRatio(float aspectRatio) = 0;
|
virtual void setPreferredAspectRatio(float aspectRatio) = 0;
|
||||||
virtual void setOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) = 0;
|
virtual void setOutput(std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) = 0;
|
||||||
|
|
|
@ -9,11 +9,11 @@
|
||||||
|
|
||||||
namespace tgcalls {
|
namespace tgcalls {
|
||||||
|
|
||||||
VideoCaptureInterfaceObject::VideoCaptureInterfaceObject(std::string deviceId, std::shared_ptr<PlatformContext> platformContext, Threads &threads)
|
VideoCaptureInterfaceObject::VideoCaptureInterfaceObject(std::string deviceId, bool isScreenCapture, std::shared_ptr<PlatformContext> platformContext, Threads &threads)
|
||||||
: _videoSource(PlatformInterface::SharedInstance()->makeVideoSource(threads.getMediaThread(), threads.getWorkerThread(), deviceId == "screen")) {
|
: _videoSource(PlatformInterface::SharedInstance()->makeVideoSource(threads.getMediaThread(), threads.getWorkerThread(), isScreenCapture)) {
|
||||||
_platformContext = platformContext;
|
_platformContext = platformContext;
|
||||||
|
|
||||||
switchToDevice(deviceId);
|
switchToDevice(deviceId, isScreenCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoCaptureInterfaceObject::~VideoCaptureInterfaceObject() {
|
VideoCaptureInterfaceObject::~VideoCaptureInterfaceObject() {
|
||||||
|
@ -34,13 +34,18 @@ int VideoCaptureInterfaceObject::getRotation() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoCaptureInterfaceObject::switchToDevice(std::string deviceId) {
|
bool VideoCaptureInterfaceObject::isScreenCapture() {
|
||||||
if (_videoCapturer && _currentUncroppedSink != nullptr) {
|
return _isScreenCapture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoCaptureInterfaceObject::switchToDevice(std::string deviceId, bool isScreenCapture) {
|
||||||
|
if (_videoCapturer) {
|
||||||
_videoCapturer->setUncroppedOutput(nullptr);
|
_videoCapturer->setUncroppedOutput(nullptr);
|
||||||
}
|
}
|
||||||
|
_isScreenCapture = isScreenCapture;
|
||||||
if (_videoSource) {
|
if (_videoSource) {
|
||||||
//this should outlive the capturer
|
//this should outlive the capturer
|
||||||
_videoCapturer = NULL;
|
_videoCapturer = nullptr;
|
||||||
_videoCapturer = PlatformInterface::SharedInstance()->makeVideoCapturer(_videoSource, deviceId, [this](VideoState state) {
|
_videoCapturer = PlatformInterface::SharedInstance()->makeVideoCapturer(_videoSource, deviceId, [this](VideoState state) {
|
||||||
if (this->_stateUpdated) {
|
if (this->_stateUpdated) {
|
||||||
this->_stateUpdated(state);
|
this->_stateUpdated(state);
|
||||||
|
@ -164,23 +169,19 @@ void VideoCaptureInterfaceObject::setRotationUpdated(std::function<void(int)> ro
|
||||||
|
|
||||||
VideoCaptureInterfaceImpl::VideoCaptureInterfaceImpl(std::string deviceId, bool isScreenCapture, std::shared_ptr<PlatformContext> platformContext, std::shared_ptr<Threads> threads) :
|
VideoCaptureInterfaceImpl::VideoCaptureInterfaceImpl(std::string deviceId, bool isScreenCapture, std::shared_ptr<PlatformContext> platformContext, std::shared_ptr<Threads> threads) :
|
||||||
_platformContext(platformContext),
|
_platformContext(platformContext),
|
||||||
_impl(threads->getMediaThread(), [deviceId, platformContext, threads]() {
|
_impl(threads->getMediaThread(), [deviceId, isScreenCapture, platformContext, threads]() {
|
||||||
return new VideoCaptureInterfaceObject(deviceId, platformContext, *threads);
|
return new VideoCaptureInterfaceObject(deviceId, isScreenCapture, platformContext, *threads);
|
||||||
}), _isScreenCapture(isScreenCapture) {
|
}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoCaptureInterfaceImpl::~VideoCaptureInterfaceImpl() = default;
|
VideoCaptureInterfaceImpl::~VideoCaptureInterfaceImpl() = default;
|
||||||
|
|
||||||
void VideoCaptureInterfaceImpl::switchToDevice(std::string deviceId) {
|
void VideoCaptureInterfaceImpl::switchToDevice(std::string deviceId, bool isScreenCapture) {
|
||||||
_impl.perform(RTC_FROM_HERE, [deviceId](VideoCaptureInterfaceObject *impl) {
|
_impl.perform(RTC_FROM_HERE, [deviceId, isScreenCapture](VideoCaptureInterfaceObject *impl) {
|
||||||
impl->switchToDevice(deviceId);
|
impl->switchToDevice(deviceId, isScreenCapture);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoCaptureInterfaceImpl::isScreenCapture() {
|
|
||||||
return _isScreenCapture;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoCaptureInterfaceImpl::withNativeImplementation(std::function<void(void *)> completion) {
|
void VideoCaptureInterfaceImpl::withNativeImplementation(std::function<void(void *)> completion) {
|
||||||
_impl.perform(RTC_FROM_HERE, [completion](VideoCaptureInterfaceObject *impl) {
|
_impl.perform(RTC_FROM_HERE, [completion](VideoCaptureInterfaceObject *impl) {
|
||||||
impl->withNativeImplementation(completion);
|
impl->withNativeImplementation(completion);
|
||||||
|
|
|
@ -14,10 +14,10 @@ class Threads;
|
||||||
|
|
||||||
class VideoCaptureInterfaceObject {
|
class VideoCaptureInterfaceObject {
|
||||||
public:
|
public:
|
||||||
VideoCaptureInterfaceObject(std::string deviceId, std::shared_ptr<PlatformContext> platformContext, Threads &threads);
|
VideoCaptureInterfaceObject(std::string deviceId, bool isScreenCapture, std::shared_ptr<PlatformContext> platformContext, Threads &threads);
|
||||||
~VideoCaptureInterfaceObject();
|
~VideoCaptureInterfaceObject();
|
||||||
|
|
||||||
void switchToDevice(std::string deviceId);
|
void switchToDevice(std::string deviceId, bool isScreenCapture);
|
||||||
void withNativeImplementation(std::function<void(void *)> completion);
|
void withNativeImplementation(std::function<void(void *)> completion);
|
||||||
void setState(VideoState state);
|
void setState(VideoState state);
|
||||||
void setPreferredAspectRatio(float aspectRatio);
|
void setPreferredAspectRatio(float aspectRatio);
|
||||||
|
@ -29,10 +29,11 @@ public:
|
||||||
void setOnIsActiveUpdated(std::function<void(bool)> onIsActiveUpdated);
|
void setOnIsActiveUpdated(std::function<void(bool)> onIsActiveUpdated);
|
||||||
webrtc::VideoTrackSourceInterface *source();
|
webrtc::VideoTrackSourceInterface *source();
|
||||||
int getRotation();
|
int getRotation();
|
||||||
|
bool isScreenCapture();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateAspectRateAdaptation();
|
void updateAspectRateAdaptation();
|
||||||
|
|
||||||
rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> _videoSource;
|
rtc::scoped_refptr<webrtc::VideoTrackSourceInterface> _videoSource;
|
||||||
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> _currentUncroppedSink;
|
std::shared_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> _currentUncroppedSink;
|
||||||
std::shared_ptr<PlatformContext> _platformContext;
|
std::shared_ptr<PlatformContext> _platformContext;
|
||||||
|
@ -46,6 +47,7 @@ private:
|
||||||
VideoState _state = VideoState::Active;
|
VideoState _state = VideoState::Active;
|
||||||
float _preferredAspectRatio = 0.0f;
|
float _preferredAspectRatio = 0.0f;
|
||||||
bool _shouldBeAdaptedToReceiverAspectRate = true;
|
bool _shouldBeAdaptedToReceiverAspectRate = true;
|
||||||
|
bool _isScreenCapture = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VideoCaptureInterfaceImpl : public VideoCaptureInterface {
|
class VideoCaptureInterfaceImpl : public VideoCaptureInterface {
|
||||||
|
@ -53,8 +55,7 @@ public:
|
||||||
VideoCaptureInterfaceImpl(std::string deviceId, bool isScreenCapture, std::shared_ptr<PlatformContext> platformContext, std::shared_ptr<Threads> threads);
|
VideoCaptureInterfaceImpl(std::string deviceId, bool isScreenCapture, std::shared_ptr<PlatformContext> platformContext, std::shared_ptr<Threads> threads);
|
||||||
virtual ~VideoCaptureInterfaceImpl();
|
virtual ~VideoCaptureInterfaceImpl();
|
||||||
|
|
||||||
bool isScreenCapture() override;
|
void switchToDevice(std::string deviceId, bool isScreenCapture) override;
|
||||||
void switchToDevice(std::string deviceId) override;
|
|
||||||
void withNativeImplementation(std::function<void(void *)> completion) override;
|
void withNativeImplementation(std::function<void(void *)> completion) override;
|
||||||
void setState(VideoState state) override;
|
void setState(VideoState state) override;
|
||||||
void setPreferredAspectRatio(float aspectRatio) override;
|
void setPreferredAspectRatio(float aspectRatio) override;
|
||||||
|
@ -68,8 +69,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ThreadLocalObject<VideoCaptureInterfaceObject> _impl;
|
ThreadLocalObject<VideoCaptureInterfaceObject> _impl;
|
||||||
|
|
||||||
bool _isScreenCapture = false;
|
|
||||||
|
|
||||||
std::shared_ptr<PlatformContext> _platformContext;
|
std::shared_ptr<PlatformContext> _platformContext;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "StreamingPart.h"
|
#include "AudioStreamingPart.h"
|
||||||
|
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
#include "rtc_base/third_party/base64/base64.h"
|
#include "rtc_base/third_party/base64/base64.h"
|
||||||
|
@ -10,6 +10,7 @@ extern "C" {
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <bitset>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
@ -17,6 +18,28 @@ namespace tgcalls {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
uint32_t stringToUInt32(std::string const &string) {
|
||||||
|
std::stringstream stringStream(string);
|
||||||
|
uint32_t value = 0;
|
||||||
|
stringStream >> value;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Out>
|
||||||
|
void splitString(const std::string &s, char delim, Out result) {
|
||||||
|
std::istringstream iss(s);
|
||||||
|
std::string item;
|
||||||
|
while (std::getline(iss, item, delim)) {
|
||||||
|
*result++ = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> splitString(const std::string &s, char delim) {
|
||||||
|
std::vector<std::string> elems;
|
||||||
|
splitString(s, delim, std::back_inserter(elems));
|
||||||
|
return elems;
|
||||||
|
}
|
||||||
|
|
||||||
static absl::optional<uint32_t> readInt32(std::string const &data, int &offset) {
|
static absl::optional<uint32_t> readInt32(std::string const &data, int &offset) {
|
||||||
if (offset + 4 > data.length()) {
|
if (offset + 4 > data.length()) {
|
||||||
return absl::nullopt;
|
return absl::nullopt;
|
||||||
|
@ -139,9 +162,9 @@ struct ReadPcmResult {
|
||||||
int numChannels = 0;
|
int numChannels = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StreamingPartInternal {
|
class AudioStreamingPartInternal {
|
||||||
public:
|
public:
|
||||||
StreamingPartInternal(std::vector<uint8_t> &&fileData) :
|
AudioStreamingPartInternal(std::vector<uint8_t> &&fileData) :
|
||||||
_avIoContext(std::move(fileData)) {
|
_avIoContext(std::move(fileData)) {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
@ -201,6 +224,31 @@ public:
|
||||||
_channelUpdates = parseChannelUpdates(result, offset);
|
_channelUpdates = parseChannelUpdates(result, offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t videoChannelMask = 0;
|
||||||
|
entry = av_dict_get(inStream->metadata, "ACTIVE_MASK", nullptr, 0);
|
||||||
|
if (entry && entry->value) {
|
||||||
|
std::string sourceString = (const char *)entry->value;
|
||||||
|
videoChannelMask = stringToUInt32(sourceString);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> endpointList;
|
||||||
|
entry = av_dict_get(inStream->metadata, "ENDPOINTS", nullptr, 0);
|
||||||
|
if (entry && entry->value) {
|
||||||
|
std::string sourceString = (const char *)entry->value;
|
||||||
|
endpointList = splitString(sourceString, ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
std::bitset<32> videoChannels(videoChannelMask);
|
||||||
|
size_t endpointIndex = 0;
|
||||||
|
if (videoChannels.count() == endpointList.size()) {
|
||||||
|
for (size_t i = 0; i < videoChannels.size(); i++) {
|
||||||
|
if (videoChannels[i]) {
|
||||||
|
_endpointMapping.insert(std::make_pair(endpointList[endpointIndex], i));
|
||||||
|
endpointIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -233,7 +281,7 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~StreamingPartInternal() {
|
~AudioStreamingPartInternal() {
|
||||||
if (_frame) {
|
if (_frame) {
|
||||||
av_frame_unref(_frame);
|
av_frame_unref(_frame);
|
||||||
}
|
}
|
||||||
|
@ -283,10 +331,14 @@ public:
|
||||||
return _channelCount;
|
return _channelCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ChannelUpdate> const &getChannelUpdates() {
|
std::vector<ChannelUpdate> const &getChannelUpdates() const {
|
||||||
return _channelUpdates;
|
return _channelUpdates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<std::string, int32_t> getEndpointMapping() const {
|
||||||
|
return _endpointMapping;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int16_t sampleFloatToInt16(float sample) {
|
static int16_t sampleFloatToInt16(float sample) {
|
||||||
return av_clip_int16 (static_cast<int32_t>(lrint(sample*32767)));
|
return av_clip_int16 (static_cast<int32_t>(lrint(sample*32767)));
|
||||||
|
@ -399,13 +451,14 @@ private:
|
||||||
int _channelCount = 0;
|
int _channelCount = 0;
|
||||||
|
|
||||||
std::vector<ChannelUpdate> _channelUpdates;
|
std::vector<ChannelUpdate> _channelUpdates;
|
||||||
|
std::map<std::string, int32_t> _endpointMapping;
|
||||||
|
|
||||||
std::vector<int16_t> _pcmBuffer;
|
std::vector<int16_t> _pcmBuffer;
|
||||||
int _pcmBufferSampleOffset = 0;
|
int _pcmBufferSampleOffset = 0;
|
||||||
int _pcmBufferSampleSize = 0;
|
int _pcmBufferSampleSize = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class StreamingPartState {
|
class AudioStreamingPartState {
|
||||||
struct ChannelMapping {
|
struct ChannelMapping {
|
||||||
uint32_t ssrc = 0;
|
uint32_t ssrc = 0;
|
||||||
int channelIndex = 0;
|
int channelIndex = 0;
|
||||||
|
@ -416,7 +469,7 @@ class StreamingPartState {
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StreamingPartState(std::vector<uint8_t> &&data) :
|
AudioStreamingPartState(std::vector<uint8_t> &&data) :
|
||||||
_parsedPart(std::move(data)) {
|
_parsedPart(std::move(data)) {
|
||||||
if (_parsedPart.getChannelUpdates().size() == 0) {
|
if (_parsedPart.getChannelUpdates().size() == 0) {
|
||||||
_didReadToEnd = true;
|
_didReadToEnd = true;
|
||||||
|
@ -431,14 +484,18 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~StreamingPartState() {
|
~AudioStreamingPartState() {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, int32_t> getEndpointMapping() const {
|
||||||
|
return _parsedPart.getEndpointMapping();
|
||||||
}
|
}
|
||||||
|
|
||||||
int getRemainingMilliseconds() const {
|
int getRemainingMilliseconds() const {
|
||||||
return _remainingMilliseconds;
|
return _remainingMilliseconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StreamingPart::StreamingPartChannel> get10msPerChannel() {
|
std::vector<AudioStreamingPart::StreamingPartChannel> get10msPerChannel() {
|
||||||
if (_didReadToEnd) {
|
if (_didReadToEnd) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -455,9 +512,9 @@ public:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StreamingPart::StreamingPartChannel> resultChannels;
|
std::vector<AudioStreamingPart::StreamingPartChannel> resultChannels;
|
||||||
for (const auto ssrc : _allSsrcs) {
|
for (const auto ssrc : _allSsrcs) {
|
||||||
StreamingPart::StreamingPartChannel emptyPart;
|
AudioStreamingPart::StreamingPartChannel emptyPart;
|
||||||
emptyPart.ssrc = ssrc;
|
emptyPart.ssrc = ssrc;
|
||||||
resultChannels.push_back(emptyPart);
|
resultChannels.push_back(emptyPart);
|
||||||
}
|
}
|
||||||
|
@ -509,7 +566,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
StreamingPartInternal _parsedPart;
|
AudioStreamingPartInternal _parsedPart;
|
||||||
std::set<uint32_t> _allSsrcs;
|
std::set<uint32_t> _allSsrcs;
|
||||||
|
|
||||||
std::vector<int16_t> _pcm10ms;
|
std::vector<int16_t> _pcm10ms;
|
||||||
|
@ -520,26 +577,30 @@ private:
|
||||||
bool _didReadToEnd = false;
|
bool _didReadToEnd = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
StreamingPart::StreamingPart(std::vector<uint8_t> &&data) {
|
AudioStreamingPart::AudioStreamingPart(std::vector<uint8_t> &&data) {
|
||||||
if (!data.empty()) {
|
if (!data.empty()) {
|
||||||
_state = new StreamingPartState(std::move(data));
|
_state = new AudioStreamingPartState(std::move(data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamingPart::~StreamingPart() {
|
AudioStreamingPart::~AudioStreamingPart() {
|
||||||
if (_state) {
|
if (_state) {
|
||||||
delete _state;
|
delete _state;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int StreamingPart::getRemainingMilliseconds() const {
|
std::map<std::string, int32_t> AudioStreamingPart::getEndpointMapping() const {
|
||||||
|
return _state ? _state->getEndpointMapping() : std::map<std::string, int32_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AudioStreamingPart::getRemainingMilliseconds() const {
|
||||||
return _state ? _state->getRemainingMilliseconds() : 0;
|
return _state ? _state->getRemainingMilliseconds() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StreamingPart::StreamingPartChannel> StreamingPart::get10msPerChannel() {
|
std::vector<AudioStreamingPart::StreamingPartChannel> AudioStreamingPart::get10msPerChannel() {
|
||||||
return _state
|
return _state
|
||||||
? _state->get10msPerChannel()
|
? _state->get10msPerChannel()
|
||||||
: std::vector<StreamingPart::StreamingPartChannel>();
|
: std::vector<AudioStreamingPart::StreamingPartChannel>();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
41
TMessagesProj/jni/voip/tgcalls/group/AudioStreamingPart.h
Normal file
41
TMessagesProj/jni/voip/tgcalls/group/AudioStreamingPart.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef TGCALLS_AUDIO_STREAMING_PART_H
|
||||||
|
#define TGCALLS_AUDIO_STREAMING_PART_H
|
||||||
|
|
||||||
|
#include "absl/types/optional.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace tgcalls {
|
||||||
|
|
||||||
|
class AudioStreamingPartState;
|
||||||
|
|
||||||
|
class AudioStreamingPart {
|
||||||
|
public:
|
||||||
|
struct StreamingPartChannel {
|
||||||
|
uint32_t ssrc = 0;
|
||||||
|
std::vector<int16_t> pcmData;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit AudioStreamingPart(std::vector<uint8_t> &&data);
|
||||||
|
~AudioStreamingPart();
|
||||||
|
|
||||||
|
AudioStreamingPart(const AudioStreamingPart&) = delete;
|
||||||
|
AudioStreamingPart(AudioStreamingPart&& other) {
|
||||||
|
_state = other._state;
|
||||||
|
other._state = nullptr;
|
||||||
|
}
|
||||||
|
AudioStreamingPart& operator=(const AudioStreamingPart&) = delete;
|
||||||
|
AudioStreamingPart& operator=(AudioStreamingPart&&) = delete;
|
||||||
|
|
||||||
|
std::map<std::string, int32_t> getEndpointMapping() const;
|
||||||
|
int getRemainingMilliseconds() const;
|
||||||
|
std::vector<StreamingPartChannel> get10msPerChannel();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AudioStreamingPartState *_state = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -35,6 +35,7 @@
|
||||||
#include "modules/audio_coding/neteq/default_neteq_factory.h"
|
#include "modules/audio_coding/neteq/default_neteq_factory.h"
|
||||||
#include "modules/audio_coding/include/audio_coding_module.h"
|
#include "modules/audio_coding/include/audio_coding_module.h"
|
||||||
#include "common_audio/include/audio_util.h"
|
#include "common_audio/include/audio_util.h"
|
||||||
|
#include "modules/audio_device/include/audio_device_data_observer.h"
|
||||||
|
|
||||||
#include "AudioFrame.h"
|
#include "AudioFrame.h"
|
||||||
#include "ThreadLocalObject.h"
|
#include "ThreadLocalObject.h"
|
||||||
|
@ -44,9 +45,11 @@
|
||||||
#include "platform/PlatformInterface.h"
|
#include "platform/PlatformInterface.h"
|
||||||
#include "LogSinkImpl.h"
|
#include "LogSinkImpl.h"
|
||||||
#include "CodecSelectHelper.h"
|
#include "CodecSelectHelper.h"
|
||||||
#include "StreamingPart.h"
|
#include "AudioStreamingPart.h"
|
||||||
|
#include "VideoStreamingPart.h"
|
||||||
#include "AudioDeviceHelper.h"
|
#include "AudioDeviceHelper.h"
|
||||||
#include "FakeAudioDeviceModule.h"
|
#include "FakeAudioDeviceModule.h"
|
||||||
|
#include "StreamingMediaContext.h"
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
@ -1238,6 +1241,83 @@ std::function<webrtc::VideoTrackSourceInterface*()> videoCaptureToGetVideoSource
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AudioDeviceDataObserverShared {
|
||||||
|
public:
|
||||||
|
AudioDeviceDataObserverShared() {
|
||||||
|
}
|
||||||
|
|
||||||
|
~AudioDeviceDataObserverShared() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void setStreamingContext(std::shared_ptr<StreamingMediaContext> streamingContext) {
|
||||||
|
_mutex.Lock();
|
||||||
|
_streamingContext = streamingContext;
|
||||||
|
_mutex.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mixAudio(int16_t *audio_samples, const size_t num_samples, const size_t num_channels, const uint32_t samples_per_sec) {
|
||||||
|
const auto numSamplesOut = num_samples * num_channels;
|
||||||
|
const auto numBytesOut = sizeof(int16_t) * numSamplesOut;
|
||||||
|
if (samples_per_sec != 48000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_buffer.size() < numSamplesOut) {
|
||||||
|
_buffer.resize(numSamplesOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
_mutex.Lock();
|
||||||
|
const auto context = _streamingContext;
|
||||||
|
_mutex.Unlock();
|
||||||
|
|
||||||
|
if (context) {
|
||||||
|
context->getAudio(_buffer.data(), num_samples, num_channels, samples_per_sec);
|
||||||
|
memcpy(audio_samples, _buffer.data(), numBytesOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
webrtc::Mutex _mutex;
|
||||||
|
std::vector<int16_t> _buffer;
|
||||||
|
std::shared_ptr<StreamingMediaContext> _streamingContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioDeviceDataObserverImpl : public webrtc::AudioDeviceDataObserver {
|
||||||
|
public:
|
||||||
|
AudioDeviceDataObserverImpl(std::shared_ptr<AudioDeviceDataObserverShared> shared) :
|
||||||
|
_shared(shared) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~AudioDeviceDataObserverImpl() {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnCaptureData(const void* audio_samples,
|
||||||
|
const size_t num_samples,
|
||||||
|
const size_t bytes_per_sample,
|
||||||
|
const size_t num_channels,
|
||||||
|
const uint32_t samples_per_sec) override {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void OnRenderData(const void* audio_samples,
|
||||||
|
const size_t num_samples,
|
||||||
|
const size_t bytes_per_sample,
|
||||||
|
const size_t num_channels,
|
||||||
|
const uint32_t samples_per_sec) override {
|
||||||
|
if (samples_per_sec != 48000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (bytes_per_sample != num_channels * 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_shared) {
|
||||||
|
_shared->mixAudio((int16_t *)audio_samples, num_samples, num_channels, samples_per_sec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<AudioDeviceDataObserverShared> _shared;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class GroupInstanceCustomInternal : public sigslot::has_slots<>, public std::enable_shared_from_this<GroupInstanceCustomInternal> {
|
class GroupInstanceCustomInternal : public sigslot::has_slots<>, public std::enable_shared_from_this<GroupInstanceCustomInternal> {
|
||||||
|
@ -1248,7 +1328,9 @@ public:
|
||||||
_audioLevelsUpdated(descriptor.audioLevelsUpdated),
|
_audioLevelsUpdated(descriptor.audioLevelsUpdated),
|
||||||
_onAudioFrame(descriptor.onAudioFrame),
|
_onAudioFrame(descriptor.onAudioFrame),
|
||||||
_requestMediaChannelDescriptions(descriptor.requestMediaChannelDescriptions),
|
_requestMediaChannelDescriptions(descriptor.requestMediaChannelDescriptions),
|
||||||
_requestBroadcastPart(descriptor.requestBroadcastPart),
|
_requestCurrentTime(descriptor.requestCurrentTime),
|
||||||
|
_requestAudioBroadcastPart(descriptor.requestAudioBroadcastPart),
|
||||||
|
_requestVideoBroadcastPart(descriptor.requestVideoBroadcastPart),
|
||||||
_videoCapture(descriptor.videoCapture),
|
_videoCapture(descriptor.videoCapture),
|
||||||
_videoCaptureSink(new VideoSinkImpl("VideoCapture")),
|
_videoCaptureSink(new VideoSinkImpl("VideoCapture")),
|
||||||
_getVideoSource(descriptor.getVideoSource),
|
_getVideoSource(descriptor.getVideoSource),
|
||||||
|
@ -1407,6 +1489,8 @@ public:
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
_audioDeviceDataObserverShared = std::make_shared<AudioDeviceDataObserverShared>();
|
||||||
|
|
||||||
_audioDeviceModule = createAudioDeviceModule();
|
_audioDeviceModule = createAudioDeviceModule();
|
||||||
if (!_audioDeviceModule) {
|
if (!_audioDeviceModule) {
|
||||||
return;
|
return;
|
||||||
|
@ -1763,8 +1847,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateSsrcAudioLevel(uint32_t ssrc, uint8_t audioLevel, bool isSpeech) {
|
void updateSsrcAudioLevel(uint32_t ssrc, uint8_t audioLevel, bool isSpeech) {
|
||||||
float mappedLevel = ((float)audioLevel) / (float)(0x7f);
|
float mappedLevelDb = ((float)audioLevel) / (float)(0x7f);
|
||||||
mappedLevel = (fabs(1.0f - mappedLevel)) * 1.0f;
|
|
||||||
|
//mappedLevelDb = fabs(1.0f - mappedLevelDb);
|
||||||
|
//float mappedLevel = pow(10.0f, mappedLevelDb * 0.1f);
|
||||||
|
|
||||||
|
//printf("mappedLevelDb: %f, mappedLevel: %f\n", mappedLevelDb, mappedLevel);
|
||||||
|
|
||||||
|
float mappedLevel = (fabs(1.0f - mappedLevelDb)) * 1.0f;
|
||||||
|
|
||||||
auto it = _audioLevels.find(ChannelId(ssrc));
|
auto it = _audioLevels.find(ChannelId(ssrc));
|
||||||
if (it != _audioLevels.end()) {
|
if (it != _audioLevels.end()) {
|
||||||
|
@ -1795,18 +1885,18 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t timestamp = rtc::TimeMillis();
|
//int64_t timestamp = rtc::TimeMillis();
|
||||||
int64_t maxSampleTimeout = 400;
|
//int64_t maxSampleTimeout = 400;
|
||||||
|
|
||||||
GroupLevelsUpdate levelsUpdate;
|
GroupLevelsUpdate levelsUpdate;
|
||||||
levelsUpdate.updates.reserve(strong->_audioLevels.size() + 1);
|
levelsUpdate.updates.reserve(strong->_audioLevels.size() + 1);
|
||||||
for (auto &it : strong->_audioLevels) {
|
for (auto &it : strong->_audioLevels) {
|
||||||
if (it.second.value.level < 0.001f) {
|
/*if (it.second.value.level < 0.001f) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (it.second.timestamp <= timestamp - maxSampleTimeout) {
|
if (it.second.timestamp <= timestamp - maxSampleTimeout) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
uint32_t effectiveSsrc = it.first.actualSsrc;
|
uint32_t effectiveSsrc = it.first.actualSsrc;
|
||||||
if (std::find_if(levelsUpdate.updates.begin(), levelsUpdate.updates.end(), [&](GroupLevelUpdate const &item) {
|
if (std::find_if(levelsUpdate.updates.begin(), levelsUpdate.updates.end(), [&](GroupLevelUpdate const &item) {
|
||||||
|
@ -1825,10 +1915,12 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it.second.value.level *= 0.5f;
|
//it.second.value.level *= 0.5f;
|
||||||
it.second.value.voice = false;
|
//it.second.value.voice = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strong->_audioLevels.clear();
|
||||||
|
|
||||||
auto myAudioLevel = strong->_myAudioLevel;
|
auto myAudioLevel = strong->_myAudioLevel;
|
||||||
myAudioLevel.isMuted = strong->_isMuted;
|
myAudioLevel.isMuted = strong->_isMuted;
|
||||||
levelsUpdate.updates.push_back(GroupLevelUpdate{ 0, myAudioLevel });
|
levelsUpdate.updates.push_back(GroupLevelUpdate{ 0, myAudioLevel });
|
||||||
|
@ -1906,26 +1998,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateBroadcastNetworkStatus() {
|
void updateBroadcastNetworkStatus() {
|
||||||
auto timestamp = rtc::TimeMillis();
|
|
||||||
|
|
||||||
bool isBroadcastConnected = true;
|
bool isBroadcastConnected = true;
|
||||||
if (_lastBroadcastPartReceivedTimestamp < timestamp - 3000) {
|
|
||||||
isBroadcastConnected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_broadcastEnabledUntilRtcIsConnectedAtTimestamp) {
|
|
||||||
auto timestamp = rtc::TimeMillis();
|
|
||||||
if (std::abs(timestamp - _broadcastEnabledUntilRtcIsConnectedAtTimestamp.value()) > 3000) {
|
|
||||||
_broadcastEnabledUntilRtcIsConnectedAtTimestamp = absl::nullopt;
|
|
||||||
if (_currentRequestedBroadcastPart) {
|
|
||||||
if (_currentRequestedBroadcastPart->task) {
|
|
||||||
_currentRequestedBroadcastPart->task->cancel();
|
|
||||||
}
|
|
||||||
_currentRequestedBroadcastPart.reset();
|
|
||||||
}
|
|
||||||
isBroadcastConnected = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBroadcastConnected != _isBroadcastConnected) {
|
if (isBroadcastConnected != _isBroadcastConnected) {
|
||||||
_isBroadcastConnected = isBroadcastConnected;
|
_isBroadcastConnected = isBroadcastConnected;
|
||||||
|
@ -1933,214 +2006,6 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::optional<DecodedBroadcastPart> getNextBroadcastPart() {
|
|
||||||
while (true) {
|
|
||||||
if (_sourceBroadcastParts.size() != 0) {
|
|
||||||
auto readChannels = _sourceBroadcastParts[0]->get10msPerChannel();
|
|
||||||
if (readChannels.size() == 0 || readChannels[0].pcmData.size() == 0) {
|
|
||||||
_sourceBroadcastParts.erase(_sourceBroadcastParts.begin());
|
|
||||||
} else {
|
|
||||||
std::vector<DecodedBroadcastPart::DecodedBroadcastPartChannel> channels;
|
|
||||||
|
|
||||||
int numSamples = (int)readChannels[0].pcmData.size();
|
|
||||||
|
|
||||||
for (auto &readChannel : readChannels) {
|
|
||||||
DecodedBroadcastPart::DecodedBroadcastPartChannel channel;
|
|
||||||
channel.ssrc = readChannel.ssrc;
|
|
||||||
channel.pcmData = std::move(readChannel.pcmData);
|
|
||||||
channels.push_back(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::optional<DecodedBroadcastPart> decodedPart;
|
|
||||||
decodedPart.emplace(numSamples, std::move(channels));
|
|
||||||
|
|
||||||
return decodedPart;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return absl::nullopt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return absl::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void commitBroadcastPackets() {
|
|
||||||
int numMillisecondsInQueue = 0;
|
|
||||||
for (const auto &part : _sourceBroadcastParts) {
|
|
||||||
numMillisecondsInQueue += part->getRemainingMilliseconds();
|
|
||||||
}
|
|
||||||
|
|
||||||
int commitMilliseconds = 20;
|
|
||||||
if (numMillisecondsInQueue > 1000) {
|
|
||||||
commitMilliseconds = numMillisecondsInQueue - 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<ChannelId> channelsWithActivity;
|
|
||||||
|
|
||||||
for (int msIndex = 0; msIndex < commitMilliseconds; msIndex += 10) {
|
|
||||||
auto packetData = getNextBroadcastPart();
|
|
||||||
if (!packetData) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &decodedChannel : packetData->channels) {
|
|
||||||
if (decodedChannel.ssrc == _outgoingAudioSsrc) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChannelId channelSsrc = ChannelId(decodedChannel.ssrc + 1000, decodedChannel.ssrc);
|
|
||||||
if (_incomingAudioChannels.find(channelSsrc) == _incomingAudioChannels.end()) {
|
|
||||||
addIncomingAudioChannel(channelSsrc, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
webrtc::RtpPacket packet(nullptr, 12 + decodedChannel.pcmData.size() * 2);
|
|
||||||
|
|
||||||
packet.SetMarker(false);
|
|
||||||
packet.SetPayloadType(112);
|
|
||||||
|
|
||||||
uint16_t packetSeq = 0;
|
|
||||||
|
|
||||||
auto it = _broadcastSeqBySsrc.find(channelSsrc.networkSsrc);
|
|
||||||
if (it == _broadcastSeqBySsrc.end()) {
|
|
||||||
packetSeq = 1000;
|
|
||||||
_broadcastSeqBySsrc.insert(std::make_pair(channelSsrc.networkSsrc, packetSeq));
|
|
||||||
} else {
|
|
||||||
it->second++;
|
|
||||||
packetSeq = it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
packet.SetSequenceNumber(packetSeq);
|
|
||||||
|
|
||||||
packet.SetTimestamp(_broadcastTimestamp);
|
|
||||||
|
|
||||||
packet.SetSsrc(channelSsrc.networkSsrc);
|
|
||||||
|
|
||||||
uint8_t *payload = packet.SetPayloadSize(decodedChannel.pcmData.size() * 2);
|
|
||||||
memcpy(payload, decodedChannel.pcmData.data(), decodedChannel.pcmData.size() * 2);
|
|
||||||
|
|
||||||
for (int i = 0; i < decodedChannel.pcmData.size() * 2; i += 2) {
|
|
||||||
auto temp = payload[i];
|
|
||||||
payload[i] = payload[i + 1];
|
|
||||||
payload[i + 1] = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto buffer = packet.Buffer();
|
|
||||||
_threads->getWorkerThread()->Invoke<void>(RTC_FROM_HERE, [this, buffer]() {
|
|
||||||
_call->Receiver()->DeliverPacket(webrtc::MediaType::AUDIO, buffer, -1);
|
|
||||||
});
|
|
||||||
|
|
||||||
channelsWithActivity.insert(ChannelId(channelSsrc));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto channelId : channelsWithActivity) {
|
|
||||||
const auto it = _incomingAudioChannels.find(channelId);
|
|
||||||
if (it != _incomingAudioChannels.end()) {
|
|
||||||
it->second->updateActivity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_broadcastTimestamp += packetData->numSamples;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void requestNextBroadcastPart() {
|
|
||||||
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
|
|
||||||
auto requestedPartId = _nextBroadcastTimestampMilliseconds;
|
|
||||||
auto task = _requestBroadcastPart(_platformContext, requestedPartId, _broadcastPartDurationMilliseconds, [weak, threads = _threads, requestedPartId](BroadcastPart &&part) {
|
|
||||||
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, part = std::move(part), requestedPartId]() mutable {
|
|
||||||
auto strong = weak.lock();
|
|
||||||
if (!strong) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (strong->_currentRequestedBroadcastPart && strong->_currentRequestedBroadcastPart->timestamp == requestedPartId) {
|
|
||||||
strong->onReceivedNextBroadcastPart(std::move(part));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
if (_currentRequestedBroadcastPart) {
|
|
||||||
if (_currentRequestedBroadcastPart->task) {
|
|
||||||
_currentRequestedBroadcastPart->task->cancel();
|
|
||||||
}
|
|
||||||
_currentRequestedBroadcastPart.reset();
|
|
||||||
}
|
|
||||||
_currentRequestedBroadcastPart.emplace(requestedPartId, task);
|
|
||||||
}
|
|
||||||
|
|
||||||
void requestNextBroadcastPartWithDelay(int timeoutMs) {
|
|
||||||
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
|
|
||||||
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
|
||||||
auto strong = weak.lock();
|
|
||||||
if (!strong) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
strong->requestNextBroadcastPart();
|
|
||||||
}, timeoutMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void onReceivedNextBroadcastPart(BroadcastPart &&part) {
|
|
||||||
_currentRequestedBroadcastPart.reset();
|
|
||||||
|
|
||||||
if (_connectionMode != GroupConnectionMode::GroupConnectionModeBroadcast && !_broadcastEnabledUntilRtcIsConnectedAtTimestamp) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t responseTimestampMilliseconds = (int64_t)(part.responseTimestamp * 1000.0);
|
|
||||||
|
|
||||||
int64_t responseTimestampBoundary = (responseTimestampMilliseconds / _broadcastPartDurationMilliseconds) * _broadcastPartDurationMilliseconds;
|
|
||||||
|
|
||||||
switch (part.status) {
|
|
||||||
case BroadcastPart::Status::Success: {
|
|
||||||
_lastBroadcastPartReceivedTimestamp = rtc::TimeMillis();
|
|
||||||
updateBroadcastNetworkStatus();
|
|
||||||
|
|
||||||
if (std::abs((int64_t)(part.responseTimestamp * 1000.0) - part.timestampMilliseconds) > 2000) {
|
|
||||||
_nextBroadcastTimestampMilliseconds = std::max(part.timestampMilliseconds + _broadcastPartDurationMilliseconds, responseTimestampBoundary);
|
|
||||||
} else {
|
|
||||||
_nextBroadcastTimestampMilliseconds = part.timestampMilliseconds + _broadcastPartDurationMilliseconds;
|
|
||||||
}
|
|
||||||
_sourceBroadcastParts.emplace_back(new StreamingPart(std::move(part.oggData)));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BroadcastPart::Status::NotReady: {
|
|
||||||
_nextBroadcastTimestampMilliseconds = part.timestampMilliseconds;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case BroadcastPart::Status::ResyncNeeded: {
|
|
||||||
_nextBroadcastTimestampMilliseconds = responseTimestampBoundary;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
//RTC_FATAL() << "Unknown part.status";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t nextDelay = _nextBroadcastTimestampMilliseconds - responseTimestampMilliseconds;
|
|
||||||
int clippedDelay = std::max((int)nextDelay, 100);
|
|
||||||
|
|
||||||
//RTC_LOG(LS_INFO) << "requestNextBroadcastPartWithDelay(" << clippedDelay << ") (from " << nextDelay << ")";
|
|
||||||
|
|
||||||
requestNextBroadcastPartWithDelay(clippedDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
void beginBroadcastPartsDecodeTimer(int timeoutMs) {
|
|
||||||
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
|
|
||||||
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
|
||||||
auto strong = weak.lock();
|
|
||||||
if (!strong) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strong->_connectionMode != GroupConnectionMode::GroupConnectionModeBroadcast && !strong->_broadcastEnabledUntilRtcIsConnectedAtTimestamp) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
strong->commitBroadcastPackets();
|
|
||||||
|
|
||||||
strong->beginBroadcastPartsDecodeTimer(20);
|
|
||||||
}, timeoutMs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void configureVideoParams() {
|
void configureVideoParams() {
|
||||||
if (!_sharedVideoInformation) {
|
if (!_sharedVideoInformation) {
|
||||||
|
@ -2320,11 +2185,10 @@ public:
|
||||||
|
|
||||||
if (_broadcastEnabledUntilRtcIsConnectedAtTimestamp) {
|
if (_broadcastEnabledUntilRtcIsConnectedAtTimestamp) {
|
||||||
_broadcastEnabledUntilRtcIsConnectedAtTimestamp = absl::nullopt;
|
_broadcastEnabledUntilRtcIsConnectedAtTimestamp = absl::nullopt;
|
||||||
if (_currentRequestedBroadcastPart) {
|
|
||||||
if (_currentRequestedBroadcastPart->task) {
|
if (_streamingContext) {
|
||||||
_currentRequestedBroadcastPart->task->cancel();
|
_streamingContext.reset();
|
||||||
}
|
_audioDeviceDataObserverShared->setStreamingContext(nullptr);
|
||||||
_currentRequestedBroadcastPart.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2710,11 +2574,9 @@ public:
|
||||||
if (keepBroadcastIfWasEnabled) {
|
if (keepBroadcastIfWasEnabled) {
|
||||||
_broadcastEnabledUntilRtcIsConnectedAtTimestamp = rtc::TimeMillis();
|
_broadcastEnabledUntilRtcIsConnectedAtTimestamp = rtc::TimeMillis();
|
||||||
} else {
|
} else {
|
||||||
if (_currentRequestedBroadcastPart) {
|
if (_streamingContext) {
|
||||||
if (_currentRequestedBroadcastPart->task) {
|
_streamingContext.reset();
|
||||||
_currentRequestedBroadcastPart->task->cancel();
|
_audioDeviceDataObserverShared->setStreamingContext(nullptr);
|
||||||
}
|
|
||||||
_currentRequestedBroadcastPart.reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2743,12 +2605,50 @@ public:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GroupConnectionMode::GroupConnectionModeBroadcast: {
|
case GroupConnectionMode::GroupConnectionModeBroadcast: {
|
||||||
_broadcastTimestamp = 100001;
|
|
||||||
|
|
||||||
_isBroadcastConnected = false;
|
_isBroadcastConnected = false;
|
||||||
|
|
||||||
beginBroadcastPartsDecodeTimer(0);
|
if (!_streamingContext) {
|
||||||
requestNextBroadcastPart();
|
StreamingMediaContext::StreamingMediaContextArguments arguments;
|
||||||
|
const auto weak = std::weak_ptr<GroupInstanceCustomInternal>(shared_from_this());
|
||||||
|
arguments.threads = _threads;
|
||||||
|
arguments.platformContext = _platformContext;
|
||||||
|
arguments.requestCurrentTime = _requestCurrentTime;
|
||||||
|
arguments.requestAudioBroadcastPart = _requestAudioBroadcastPart;
|
||||||
|
arguments.requestVideoBroadcastPart = _requestVideoBroadcastPart;
|
||||||
|
arguments.updateAudioLevel = [weak, threads = _threads](uint32_t ssrc, float level, bool isSpeech) {
|
||||||
|
assert(threads->getMediaThread()->IsCurrent());
|
||||||
|
|
||||||
|
auto strong = weak.lock();
|
||||||
|
if (!strong) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InternalGroupLevelValue updated;
|
||||||
|
updated.value.level = level;
|
||||||
|
updated.value.voice = isSpeech;
|
||||||
|
updated.timestamp = rtc::TimeMillis();
|
||||||
|
strong->_audioLevels.insert(std::make_pair(ChannelId(ssrc), std::move(updated)));
|
||||||
|
};
|
||||||
|
_streamingContext = std::make_shared<StreamingMediaContext>(std::move(arguments));
|
||||||
|
|
||||||
|
for (const auto &it : _pendingVideoSinks) {
|
||||||
|
for (const auto &sink : it.second) {
|
||||||
|
_streamingContext->addVideoSink(it.first.endpointId, sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &it : _volumeBySsrc) {
|
||||||
|
_streamingContext->setVolume(it.first, it.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<StreamingMediaContext::VideoChannel> streamingVideoChannels;
|
||||||
|
for (const auto &it : _pendingRequestedVideo) {
|
||||||
|
streamingVideoChannels.emplace_back(it.maxQuality, it.endpointId);
|
||||||
|
}
|
||||||
|
_streamingContext->setActiveVideoChannels(streamingVideoChannels);
|
||||||
|
|
||||||
|
_audioDeviceDataObserverShared->setStreamingContext(_streamingContext);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3033,6 +2933,10 @@ public:
|
||||||
} else {
|
} else {
|
||||||
_pendingVideoSinks[VideoChannelId(endpointId)].push_back(sink);
|
_pendingVideoSinks[VideoChannelId(endpointId)].push_back(sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_streamingContext) {
|
||||||
|
_streamingContext->addVideoSink(endpointId, sink);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3233,9 +3137,21 @@ public:
|
||||||
if (it != _incomingAudioChannels.end()) {
|
if (it != _incomingAudioChannels.end()) {
|
||||||
it->second->setVolume(volume);
|
it->second->setVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_streamingContext) {
|
||||||
|
_streamingContext->setVolume(ssrc, volume);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setRequestedVideoChannels(std::vector<VideoChannelDescription> &&requestedVideoChannels) {
|
void setRequestedVideoChannels(std::vector<VideoChannelDescription> &&requestedVideoChannels) {
|
||||||
|
if (_streamingContext) {
|
||||||
|
std::vector<StreamingMediaContext::VideoChannel> streamingVideoChannels;
|
||||||
|
for (const auto &it : requestedVideoChannels) {
|
||||||
|
streamingVideoChannels.emplace_back(it.maxQuality, it.endpointId);
|
||||||
|
}
|
||||||
|
_streamingContext->setActiveVideoChannels(streamingVideoChannels);
|
||||||
|
}
|
||||||
|
|
||||||
if (!_sharedVideoInformation) {
|
if (!_sharedVideoInformation) {
|
||||||
_pendingRequestedVideo = std::move(requestedVideoChannels);
|
_pendingRequestedVideo = std::move(requestedVideoChannels);
|
||||||
return;
|
return;
|
||||||
|
@ -3312,14 +3228,22 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
rtc::scoped_refptr<WrappedAudioDeviceModule> createAudioDeviceModule() {
|
rtc::scoped_refptr<WrappedAudioDeviceModule> createAudioDeviceModule() {
|
||||||
|
auto audioDeviceDataObserverShared = _audioDeviceDataObserverShared;
|
||||||
const auto create = [&](webrtc::AudioDeviceModule::AudioLayer layer) {
|
const auto create = [&](webrtc::AudioDeviceModule::AudioLayer layer) {
|
||||||
return webrtc::AudioDeviceModule::Create(
|
return webrtc::AudioDeviceModule::Create(
|
||||||
layer,
|
layer,
|
||||||
_taskQueueFactory.get());
|
_taskQueueFactory.get());
|
||||||
};
|
};
|
||||||
const auto check = [&](const rtc::scoped_refptr<webrtc::AudioDeviceModule> &result) -> rtc::scoped_refptr<WrappedAudioDeviceModule> {
|
const auto check = [&](const rtc::scoped_refptr<webrtc::AudioDeviceModule> &result) -> rtc::scoped_refptr<WrappedAudioDeviceModule> {
|
||||||
if (result && result->Init() == 0) {
|
if (!result) {
|
||||||
return PlatformInterface::SharedInstance()->wrapAudioDeviceModule(result);
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto audioDeviceObserver = std::make_unique<AudioDeviceDataObserverImpl>(audioDeviceDataObserverShared);
|
||||||
|
auto module = webrtc::CreateAudioDeviceWithDataObserver(result, std::move(audioDeviceObserver));
|
||||||
|
|
||||||
|
if (module->Init() == 0) {
|
||||||
|
return PlatformInterface::SharedInstance()->wrapAudioDeviceModule(module);
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -3348,7 +3272,9 @@ private:
|
||||||
std::function<void(GroupLevelsUpdate const &)> _audioLevelsUpdated;
|
std::function<void(GroupLevelsUpdate const &)> _audioLevelsUpdated;
|
||||||
std::function<void(uint32_t, const AudioFrame &)> _onAudioFrame;
|
std::function<void(uint32_t, const AudioFrame &)> _onAudioFrame;
|
||||||
std::function<std::shared_ptr<RequestMediaChannelDescriptionTask>(std::vector<uint32_t> const &, std::function<void(std::vector<MediaChannelDescription> &&)>)> _requestMediaChannelDescriptions;
|
std::function<std::shared_ptr<RequestMediaChannelDescriptionTask>(std::vector<uint32_t> const &, std::function<void(std::vector<MediaChannelDescription> &&)>)> _requestMediaChannelDescriptions;
|
||||||
std::function<std::shared_ptr<BroadcastPartTask>(std::shared_ptr<PlatformContext>, int64_t, int64_t, std::function<void(BroadcastPart &&)>)> _requestBroadcastPart;
|
std::function<std::shared_ptr<BroadcastPartTask>(std::function<void(int64_t)>)> _requestCurrentTime;
|
||||||
|
std::function<std::shared_ptr<BroadcastPartTask>(std::shared_ptr<PlatformContext>, int64_t, int64_t, std::function<void(BroadcastPart &&)>)> _requestAudioBroadcastPart;
|
||||||
|
std::function<std::shared_ptr<BroadcastPartTask>(std::shared_ptr<PlatformContext>, int64_t, int64_t, int32_t, VideoChannelDescription::Quality, std::function<void(BroadcastPart &&)>)> _requestVideoBroadcastPart;
|
||||||
std::shared_ptr<VideoCaptureInterface> _videoCapture;
|
std::shared_ptr<VideoCaptureInterface> _videoCapture;
|
||||||
std::shared_ptr<VideoSinkImpl> _videoCaptureSink;
|
std::shared_ptr<VideoSinkImpl> _videoCaptureSink;
|
||||||
std::function<webrtc::VideoTrackSourceInterface*()> _getVideoSource;
|
std::function<webrtc::VideoTrackSourceInterface*()> _getVideoSource;
|
||||||
|
@ -3371,6 +3297,7 @@ private:
|
||||||
std::unique_ptr<webrtc::Call> _call;
|
std::unique_ptr<webrtc::Call> _call;
|
||||||
webrtc::FieldTrialBasedConfig _fieldTrials;
|
webrtc::FieldTrialBasedConfig _fieldTrials;
|
||||||
webrtc::LocalAudioSinkAdapter _audioSource;
|
webrtc::LocalAudioSinkAdapter _audioSource;
|
||||||
|
std::shared_ptr<AudioDeviceDataObserverShared> _audioDeviceDataObserverShared;
|
||||||
rtc::scoped_refptr<WrappedAudioDeviceModule> _audioDeviceModule;
|
rtc::scoped_refptr<WrappedAudioDeviceModule> _audioDeviceModule;
|
||||||
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule;
|
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> _createAudioDeviceModule;
|
||||||
std::string _initialInputDeviceId;
|
std::string _initialInputDeviceId;
|
||||||
|
@ -3419,14 +3346,6 @@ private:
|
||||||
|
|
||||||
absl::optional<GroupJoinVideoInformation> _sharedVideoInformation;
|
absl::optional<GroupJoinVideoInformation> _sharedVideoInformation;
|
||||||
|
|
||||||
int64_t _broadcastPartDurationMilliseconds = 500;
|
|
||||||
std::vector<std::unique_ptr<StreamingPart>> _sourceBroadcastParts;
|
|
||||||
std::map<uint32_t, uint16_t> _broadcastSeqBySsrc;
|
|
||||||
uint32_t _broadcastTimestamp = 0;
|
|
||||||
int64_t _nextBroadcastTimestampMilliseconds = 0;
|
|
||||||
absl::optional<RequestedBroadcastPart> _currentRequestedBroadcastPart;
|
|
||||||
int64_t _lastBroadcastPartReceivedTimestamp = 0;
|
|
||||||
|
|
||||||
std::vector<float> _externalAudioSamples;
|
std::vector<float> _externalAudioSamples;
|
||||||
webrtc::Mutex _externalAudioSamplesMutex;
|
webrtc::Mutex _externalAudioSamplesMutex;
|
||||||
std::shared_ptr<ExternalAudioRecorder> _externalAudioRecorder;
|
std::shared_ptr<ExternalAudioRecorder> _externalAudioRecorder;
|
||||||
|
@ -3437,6 +3356,8 @@ private:
|
||||||
bool _isDataChannelOpen = false;
|
bool _isDataChannelOpen = false;
|
||||||
GroupNetworkState _effectiveNetworkState;
|
GroupNetworkState _effectiveNetworkState;
|
||||||
|
|
||||||
|
std::shared_ptr<StreamingMediaContext> _streamingContext;
|
||||||
|
|
||||||
rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> _workerThreadSafery;
|
rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> _workerThreadSafery;
|
||||||
rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> _networkThreadSafery;
|
rtc::scoped_refptr<webrtc::PendingTaskSafetyFlag> _networkThreadSafery;
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,9 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BroadcastPart {
|
struct BroadcastPart {
|
||||||
|
struct VideoParams {
|
||||||
|
};
|
||||||
|
|
||||||
enum class Status {
|
enum class Status {
|
||||||
Success,
|
Success,
|
||||||
NotReady,
|
NotReady,
|
||||||
|
@ -66,7 +69,7 @@ struct BroadcastPart {
|
||||||
int64_t timestampMilliseconds = 0;
|
int64_t timestampMilliseconds = 0;
|
||||||
double responseTimestamp = 0;
|
double responseTimestamp = 0;
|
||||||
Status status = Status::NotReady;
|
Status status = Status::NotReady;
|
||||||
std::vector<uint8_t> oggData;
|
std::vector<uint8_t> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class GroupConnectionMode {
|
enum class GroupConnectionMode {
|
||||||
|
@ -150,7 +153,9 @@ struct GroupInstanceDescriptor {
|
||||||
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> createAudioDeviceModule;
|
std::function<rtc::scoped_refptr<webrtc::AudioDeviceModule>(webrtc::TaskQueueFactory*)> createAudioDeviceModule;
|
||||||
std::shared_ptr<VideoCaptureInterface> videoCapture; // deprecated
|
std::shared_ptr<VideoCaptureInterface> videoCapture; // deprecated
|
||||||
std::function<webrtc::VideoTrackSourceInterface*()> getVideoSource;
|
std::function<webrtc::VideoTrackSourceInterface*()> getVideoSource;
|
||||||
std::function<std::shared_ptr<BroadcastPartTask>(std::shared_ptr<PlatformContext>, int64_t, int64_t, std::function<void(BroadcastPart &&)>)> requestBroadcastPart;
|
std::function<std::shared_ptr<BroadcastPartTask>(std::function<void(int64_t)>)> requestCurrentTime;
|
||||||
|
std::function<std::shared_ptr<BroadcastPartTask>(std::shared_ptr<PlatformContext>, int64_t, int64_t, std::function<void(BroadcastPart &&)>)> requestAudioBroadcastPart;
|
||||||
|
std::function<std::shared_ptr<BroadcastPartTask>(std::shared_ptr<PlatformContext>, int64_t, int64_t, int32_t, VideoChannelDescription::Quality, std::function<void(BroadcastPart &&)>)> requestVideoBroadcastPart;
|
||||||
int outgoingAudioBitrateKbit{32};
|
int outgoingAudioBitrateKbit{32};
|
||||||
bool disableOutgoingAudioProcessing{false};
|
bool disableOutgoingAudioProcessing{false};
|
||||||
VideoContentType videoContentType{VideoContentType::None};
|
VideoContentType videoContentType{VideoContentType::None};
|
||||||
|
|
869
TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.cpp
Normal file
869
TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.cpp
Normal file
|
@ -0,0 +1,869 @@
|
||||||
|
#include "StreamingMediaContext.h"
|
||||||
|
|
||||||
|
#include "AudioStreamingPart.h"
|
||||||
|
#include "VideoStreamingPart.h"
|
||||||
|
|
||||||
|
#include "absl/types/optional.h"
|
||||||
|
#include "rtc_base/thread.h"
|
||||||
|
#include "rtc_base/time_utils.h"
|
||||||
|
#include "absl/types/variant.h"
|
||||||
|
#include "rtc_base/logging.h"
|
||||||
|
#include "rtc_base/synchronization/mutex.h"
|
||||||
|
#include "common_audio/ring_buffer.h"
|
||||||
|
#include "modules/audio_mixer/frame_combiner.h"
|
||||||
|
#include "modules/audio_processing/agc2/vad_with_level.h"
|
||||||
|
#include "modules/audio_processing/audio_buffer.h"
|
||||||
|
#include "api/video/video_sink_interface.h"
|
||||||
|
#include "audio/utility/audio_frame_operations.h"
|
||||||
|
|
||||||
|
namespace tgcalls {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct PendingAudioSegmentData {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PendingVideoSegmentData {
|
||||||
|
int32_t channelId = 0;
|
||||||
|
VideoChannelDescription::Quality quality = VideoChannelDescription::Quality::Thumbnail;
|
||||||
|
|
||||||
|
PendingVideoSegmentData(int32_t channelId_, VideoChannelDescription::Quality quality_) :
|
||||||
|
channelId(channelId_),
|
||||||
|
quality(quality_) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PendingMediaSegmentPartResult {
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
|
||||||
|
explicit PendingMediaSegmentPartResult(std::vector<uint8_t> &&data_) :
|
||||||
|
data(std::move(data_)) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PendingMediaSegmentPart {
|
||||||
|
absl::variant<PendingAudioSegmentData, PendingVideoSegmentData> typeData;
|
||||||
|
|
||||||
|
int64_t minRequestTimestamp = 0;
|
||||||
|
|
||||||
|
std::shared_ptr<BroadcastPartTask> task;
|
||||||
|
std::shared_ptr<PendingMediaSegmentPartResult> result;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PendingMediaSegment {
|
||||||
|
int64_t timestamp = 0;
|
||||||
|
std::vector<std::shared_ptr<PendingMediaSegmentPart>> parts;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VideoSegment {
|
||||||
|
VideoChannelDescription::Quality quality;
|
||||||
|
std::shared_ptr<VideoStreamingPart> part;
|
||||||
|
double lastFramePts = -1.0;
|
||||||
|
int _displayedFrames = 0;
|
||||||
|
bool isPlaying = false;
|
||||||
|
std::shared_ptr<PendingMediaSegmentPart> pendingVideoQualityUpdatePart;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MediaSegment {
|
||||||
|
int64_t timestamp = 0;
|
||||||
|
int64_t duration = 0;
|
||||||
|
std::shared_ptr<AudioStreamingPart> audio;
|
||||||
|
std::vector<std::shared_ptr<VideoSegment>> video;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SampleRingBuffer {
|
||||||
|
public:
|
||||||
|
SampleRingBuffer(size_t size) {
|
||||||
|
_buffer = WebRtc_CreateBuffer(size, sizeof(int16_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
~SampleRingBuffer() {
|
||||||
|
if (_buffer) {
|
||||||
|
WebRtc_FreeBuffer(_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t availableForWriting() {
|
||||||
|
return WebRtc_available_write(_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t write(int16_t const *samples, size_t count) {
|
||||||
|
return WebRtc_WriteBuffer(_buffer, samples, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t read(int16_t *samples, size_t count) {
|
||||||
|
return WebRtc_ReadBuffer(_buffer, nullptr, samples, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RingBuffer *_buffer = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int kVadResultHistoryLength = 8;
|
||||||
|
|
||||||
|
class VadHistory {
|
||||||
|
private:
|
||||||
|
float _vadResultHistory[kVadResultHistoryLength];
|
||||||
|
|
||||||
|
public:
|
||||||
|
VadHistory() {
|
||||||
|
for (int i = 0; i < kVadResultHistoryLength; i++) {
|
||||||
|
_vadResultHistory[i] = 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~VadHistory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool update(float vadProbability) {
|
||||||
|
for (int i = 1; i < kVadResultHistoryLength; i++) {
|
||||||
|
_vadResultHistory[i - 1] = _vadResultHistory[i];
|
||||||
|
}
|
||||||
|
_vadResultHistory[kVadResultHistoryLength - 1] = vadProbability;
|
||||||
|
|
||||||
|
float movingAverage = 0.0f;
|
||||||
|
for (int i = 0; i < kVadResultHistoryLength; i++) {
|
||||||
|
movingAverage += _vadResultHistory[i];
|
||||||
|
}
|
||||||
|
movingAverage /= (float)kVadResultHistoryLength;
|
||||||
|
|
||||||
|
bool vadResult = false;
|
||||||
|
if (movingAverage > 0.8f) {
|
||||||
|
vadResult = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vadResult;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CombinedVad {
|
||||||
|
private:
|
||||||
|
std::unique_ptr<webrtc::VadLevelAnalyzer> _vadWithLevel;
|
||||||
|
VadHistory _history;
|
||||||
|
|
||||||
|
public:
|
||||||
|
CombinedVad() {
|
||||||
|
_vadWithLevel = std::make_unique<webrtc::VadLevelAnalyzer>(500, webrtc::GetAvailableCpuFeatures());
|
||||||
|
}
|
||||||
|
|
||||||
|
~CombinedVad() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool update(webrtc::AudioBuffer *buffer) {
|
||||||
|
if (buffer->num_channels() <= 0) {
|
||||||
|
return _history.update(0.0f);
|
||||||
|
}
|
||||||
|
webrtc::AudioFrameView<float> frameView(buffer->channels(), buffer->num_channels(), buffer->num_frames());
|
||||||
|
float peak = 0.0f;
|
||||||
|
for (const auto &x : frameView.channel(0)) {
|
||||||
|
peak = std::max(std::fabs(x), peak);
|
||||||
|
}
|
||||||
|
if (peak <= 0.01f) {
|
||||||
|
return _history.update(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = _vadWithLevel->AnalyzeFrame(frameView);
|
||||||
|
|
||||||
|
return _history.update(result.speech_probability);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool update() {
|
||||||
|
return _history.update(0.0f);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SparseVad {
|
||||||
|
public:
|
||||||
|
SparseVad() {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<float, bool> update(webrtc::AudioBuffer *buffer) {
|
||||||
|
_sampleCount += buffer->num_frames();
|
||||||
|
if (_sampleCount >= 400) {
|
||||||
|
_sampleCount = 0;
|
||||||
|
_currentValue = _vad.update(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
float currentPeak = 0.0;
|
||||||
|
float *samples = buffer->channels()[0];
|
||||||
|
for (int i = 0; i < buffer->num_frames(); i++) {
|
||||||
|
float sample = samples[i];
|
||||||
|
if (sample < 0.0f) {
|
||||||
|
sample = -sample;
|
||||||
|
}
|
||||||
|
if (_peak < sample) {
|
||||||
|
_peak = sample;
|
||||||
|
}
|
||||||
|
if (currentPeak < sample) {
|
||||||
|
currentPeak = sample;
|
||||||
|
}
|
||||||
|
_peakCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_peakCount >= 4400) {
|
||||||
|
float norm = 8000.0f;
|
||||||
|
_currentLevel = ((float)(_peak)) / norm;
|
||||||
|
_peak = 0;
|
||||||
|
_peakCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(_currentLevel, _currentValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
CombinedVad _vad;
|
||||||
|
bool _currentValue = false;
|
||||||
|
size_t _sampleCount = 0;
|
||||||
|
|
||||||
|
int _peakCount = 0;
|
||||||
|
float _peak = 0.0;
|
||||||
|
float _currentLevel = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class StreamingMediaContextPrivate : public std::enable_shared_from_this<StreamingMediaContextPrivate> {
|
||||||
|
public:
|
||||||
|
StreamingMediaContextPrivate(StreamingMediaContext::StreamingMediaContextArguments &&arguments) :
|
||||||
|
_threads(arguments.threads),
|
||||||
|
_requestCurrentTime(arguments.requestCurrentTime),
|
||||||
|
_requestAudioBroadcastPart(arguments.requestAudioBroadcastPart),
|
||||||
|
_requestVideoBroadcastPart(arguments.requestVideoBroadcastPart),
|
||||||
|
_updateAudioLevel(arguments.updateAudioLevel),
|
||||||
|
_audioRingBuffer(_audioDataRingBufferMaxSize),
|
||||||
|
_audioFrameCombiner(false),
|
||||||
|
_platformContext(arguments.platformContext) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~StreamingMediaContextPrivate() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void start() {
|
||||||
|
beginRenderTimer(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void beginRenderTimer(int timeoutMs) {
|
||||||
|
const auto weak = std::weak_ptr<StreamingMediaContextPrivate>(shared_from_this());
|
||||||
|
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||||
|
auto strong = weak.lock();
|
||||||
|
if (!strong) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong->render();
|
||||||
|
|
||||||
|
strong->beginRenderTimer((int)(1.0 * 1000.0 / 120.0));
|
||||||
|
}, timeoutMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render() {
|
||||||
|
int64_t absoluteTimestamp = rtc::TimeMillis();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (_waitForBufferredMillisecondsBeforeRendering) {
|
||||||
|
if (getAvailableBufferDuration() < _waitForBufferredMillisecondsBeforeRendering.value()) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
_waitForBufferredMillisecondsBeforeRendering = absl::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_availableSegments.empty()) {
|
||||||
|
_playbackReferenceTimestamp = 0;
|
||||||
|
|
||||||
|
_waitForBufferredMillisecondsBeforeRendering = _segmentBufferDuration + _segmentDuration;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_playbackReferenceTimestamp == 0) {
|
||||||
|
_playbackReferenceTimestamp = absoluteTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
double relativeTimestamp = ((double)(absoluteTimestamp - _playbackReferenceTimestamp)) / 1000.0;
|
||||||
|
|
||||||
|
auto segment = _availableSegments[0];
|
||||||
|
double segmentDuration = ((double)segment->duration) / 1000.0;
|
||||||
|
|
||||||
|
for (auto &videoSegment : segment->video) {
|
||||||
|
videoSegment->isPlaying = true;
|
||||||
|
cancelPendingVideoQualityUpdate(videoSegment);
|
||||||
|
|
||||||
|
auto frame = videoSegment->part->getFrameAtRelativeTimestamp(relativeTimestamp);
|
||||||
|
if (frame) {
|
||||||
|
if (videoSegment->lastFramePts != frame->pts) {
|
||||||
|
videoSegment->lastFramePts = frame->pts;
|
||||||
|
videoSegment->_displayedFrames += 1;
|
||||||
|
|
||||||
|
auto sinkList = _videoSinks.find(frame->endpointId);
|
||||||
|
if (sinkList != _videoSinks.end()) {
|
||||||
|
for (const auto &weakSink : sinkList->second) {
|
||||||
|
auto sink = weakSink.lock();
|
||||||
|
if (sink) {
|
||||||
|
sink->OnFrame(frame->frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment->audio) {
|
||||||
|
const auto available = [&] {
|
||||||
|
_audioDataMutex.Lock();
|
||||||
|
const auto result = (_audioRingBuffer.availableForWriting() >= 480);
|
||||||
|
_audioDataMutex.Unlock();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
while (available()) {
|
||||||
|
auto audioChannels = segment->audio->get10msPerChannel();
|
||||||
|
if (audioChannels.empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<webrtc::AudioFrame *> audioFrames;
|
||||||
|
|
||||||
|
for (const auto &audioChannel : audioChannels) {
|
||||||
|
webrtc::AudioFrame *frame = new webrtc::AudioFrame();
|
||||||
|
frame->UpdateFrame(0, audioChannel.pcmData.data(), audioChannel.pcmData.size(), 48000, webrtc::AudioFrame::SpeechType::kNormalSpeech, webrtc::AudioFrame::VADActivity::kVadActive);
|
||||||
|
|
||||||
|
auto volumeIt = _volumeBySsrc.find(audioChannel.ssrc);
|
||||||
|
if (volumeIt != _volumeBySsrc.end()) {
|
||||||
|
double outputGain = volumeIt->second;
|
||||||
|
if (outputGain < 0.99f || outputGain > 1.01f) {
|
||||||
|
webrtc::AudioFrameOperations::ScaleWithSat(outputGain, frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audioFrames.push_back(frame);
|
||||||
|
processAudioLevel(audioChannel.ssrc, audioChannel.pcmData);
|
||||||
|
}
|
||||||
|
|
||||||
|
webrtc::AudioFrame frameOut;
|
||||||
|
_audioFrameCombiner.Combine(audioFrames, 1, 48000, audioFrames.size(), &frameOut);
|
||||||
|
|
||||||
|
for (webrtc::AudioFrame *frame : audioFrames) {
|
||||||
|
delete frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
_audioDataMutex.Lock();
|
||||||
|
_audioRingBuffer.write(frameOut.data(), frameOut.samples_per_channel());
|
||||||
|
_audioDataMutex.Unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relativeTimestamp >= segmentDuration) {
|
||||||
|
_playbackReferenceTimestamp += segment->duration;
|
||||||
|
|
||||||
|
if (segment->audio && segment->audio->getRemainingMilliseconds() > 0) {
|
||||||
|
RTC_LOG(LS_INFO) << "render: discarding " << segment->audio->getRemainingMilliseconds() << " ms of audio at the end of a segment";
|
||||||
|
}
|
||||||
|
if (!segment->video.empty()) {
|
||||||
|
if (segment->video[0]->part->getActiveEndpointId()) {
|
||||||
|
RTC_LOG(LS_INFO) << "render: discarding video frames at the end of a segment (displayed " << segment->video[0]->_displayedFrames << " frames)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_availableSegments.erase(_availableSegments.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestSegmentsIfNeeded();
|
||||||
|
checkPendingSegments();
|
||||||
|
}
|
||||||
|
|
||||||
|
void processAudioLevel(uint32_t ssrc, std::vector<int16_t> const &samples) {
|
||||||
|
if (!_updateAudioLevel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
webrtc::AudioBuffer buffer(48000, 1, 48000, 1, 48000, 1);
|
||||||
|
webrtc::StreamConfig config(48000, 1);
|
||||||
|
buffer.CopyFrom(samples.data(), config);
|
||||||
|
|
||||||
|
std::pair<float, bool> vadResult = std::make_pair(0.0f, false);
|
||||||
|
auto vad = _audioVadMap.find(ssrc);
|
||||||
|
if (vad == _audioVadMap.end()) {
|
||||||
|
auto newVad = std::make_unique<SparseVad>();
|
||||||
|
vadResult = newVad->update(&buffer);
|
||||||
|
_audioVadMap.insert(std::make_pair(ssrc, std::move(newVad)));
|
||||||
|
} else {
|
||||||
|
vadResult = vad->second->update(&buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateAudioLevel(ssrc, vadResult.first, vadResult.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void getAudio(int16_t *audio_samples, const size_t num_samples, const size_t num_channels, const uint32_t samples_per_sec) {
|
||||||
|
int16_t *buffer = nullptr;
|
||||||
|
|
||||||
|
if (num_channels == 1) {
|
||||||
|
buffer = audio_samples;
|
||||||
|
} else {
|
||||||
|
if (_tempAudioBuffer.size() < num_samples) {
|
||||||
|
_tempAudioBuffer.resize(num_samples);
|
||||||
|
}
|
||||||
|
buffer = _tempAudioBuffer.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
_audioDataMutex.Lock();
|
||||||
|
size_t readSamples = _audioRingBuffer.read(buffer, num_samples);
|
||||||
|
_audioDataMutex.Unlock();
|
||||||
|
|
||||||
|
if (num_channels != 1) {
|
||||||
|
for (size_t sampleIndex = 0; sampleIndex < readSamples; sampleIndex++) {
|
||||||
|
for (size_t channelIndex = 0; channelIndex < num_channels; channelIndex++) {
|
||||||
|
audio_samples[sampleIndex * num_channels + channelIndex] = _tempAudioBuffer[sampleIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (readSamples < num_samples) {
|
||||||
|
memset(audio_samples + readSamples * num_channels, 0, (num_samples - readSamples) * num_channels * sizeof(int16_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t getAvailableBufferDuration() {
|
||||||
|
int64_t result = 0;
|
||||||
|
|
||||||
|
for (const auto &segment : _availableSegments) {
|
||||||
|
result += segment->duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void discardAllPendingSegments() {
|
||||||
|
for (size_t i = 0; i < _pendingSegments.size(); i++) {
|
||||||
|
for (const auto &it : _pendingSegments[i]->parts) {
|
||||||
|
if (it->task) {
|
||||||
|
it->task->cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingSegments.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void requestSegmentsIfNeeded() {
|
||||||
|
while (true) {
|
||||||
|
if (_nextSegmentTimestamp == 0) {
|
||||||
|
if (_pendingSegments.size() >= 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int64_t availableAndRequestedSegmentsDuration = 0;
|
||||||
|
availableAndRequestedSegmentsDuration += getAvailableBufferDuration();
|
||||||
|
availableAndRequestedSegmentsDuration += _pendingSegments.size() * _segmentDuration;
|
||||||
|
|
||||||
|
if (availableAndRequestedSegmentsDuration > _segmentBufferDuration) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pendingSegment = std::make_shared<PendingMediaSegment>();
|
||||||
|
pendingSegment->timestamp = _nextSegmentTimestamp;
|
||||||
|
|
||||||
|
if (_nextSegmentTimestamp != 0) {
|
||||||
|
_nextSegmentTimestamp += _segmentDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto audio = std::make_shared<PendingMediaSegmentPart>();
|
||||||
|
audio->typeData = PendingAudioSegmentData();
|
||||||
|
audio->minRequestTimestamp = 0;
|
||||||
|
pendingSegment->parts.push_back(audio);
|
||||||
|
|
||||||
|
for (const auto &videoChannel : _activeVideoChannels) {
|
||||||
|
auto channelIdIt = _currentEndpointMapping.find(videoChannel.endpoint);
|
||||||
|
if (channelIdIt == _currentEndpointMapping.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t channelId = channelIdIt->second + 1;
|
||||||
|
|
||||||
|
auto video = std::make_shared<PendingMediaSegmentPart>();
|
||||||
|
video->typeData = PendingVideoSegmentData(channelId, videoChannel.quality);
|
||||||
|
video->minRequestTimestamp = 0;
|
||||||
|
pendingSegment->parts.push_back(video);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pendingSegments.push_back(pendingSegment);
|
||||||
|
|
||||||
|
if (_nextSegmentTimestamp == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void requestPendingVideoQualityUpdate(std::shared_ptr<VideoSegment> segment, int64_t timestamp) {
|
||||||
|
if (segment->isPlaying) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto segmentEndpointId = segment->part->getActiveEndpointId();
|
||||||
|
if (!segmentEndpointId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<int32_t> updatedChannelId;
|
||||||
|
absl::optional<VideoChannelDescription::Quality> updatedQuality;
|
||||||
|
|
||||||
|
for (const auto &videoChannel : _activeVideoChannels) {
|
||||||
|
auto channelIdIt = _currentEndpointMapping.find(videoChannel.endpoint);
|
||||||
|
if (channelIdIt == _currentEndpointMapping.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedChannelId = channelIdIt->second + 1;
|
||||||
|
updatedQuality = videoChannel.quality;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updatedChannelId && updatedQuality) {
|
||||||
|
if (segment->pendingVideoQualityUpdatePart) {
|
||||||
|
const auto typeData = &segment->pendingVideoQualityUpdatePart->typeData;
|
||||||
|
if (const auto videoData = absl::get_if<PendingVideoSegmentData>(typeData)) {
|
||||||
|
if (videoData->channelId == updatedChannelId.value() && videoData->quality == updatedQuality.value()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cancelPendingVideoQualityUpdate(segment);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto video = std::make_shared<PendingMediaSegmentPart>();
|
||||||
|
|
||||||
|
video->typeData = PendingVideoSegmentData(updatedChannelId.value(), updatedQuality.value());
|
||||||
|
video->minRequestTimestamp = 0;
|
||||||
|
|
||||||
|
segment->pendingVideoQualityUpdatePart = video;
|
||||||
|
|
||||||
|
const auto weak = std::weak_ptr<StreamingMediaContextPrivate>(shared_from_this());
|
||||||
|
const auto weakSegment = std::weak_ptr<VideoSegment>(segment);
|
||||||
|
beginPartTask(video, timestamp, [weak, weakSegment]() {
|
||||||
|
auto strong = weak.lock();
|
||||||
|
if (!strong) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto strongSegment = weakSegment.lock();
|
||||||
|
if (!strongSegment) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strongSegment->pendingVideoQualityUpdatePart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = strongSegment->pendingVideoQualityUpdatePart->result;
|
||||||
|
if (result) {
|
||||||
|
strongSegment->part = std::make_shared<VideoStreamingPart>(std::move(result->data));
|
||||||
|
}
|
||||||
|
|
||||||
|
strongSegment->pendingVideoQualityUpdatePart.reset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancelPendingVideoQualityUpdate(std::shared_ptr<VideoSegment> segment) {
|
||||||
|
if (!segment->pendingVideoQualityUpdatePart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segment->pendingVideoQualityUpdatePart->task) {
|
||||||
|
segment->pendingVideoQualityUpdatePart->task->cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
segment->pendingVideoQualityUpdatePart.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkPendingSegments() {
|
||||||
|
const auto weak = std::weak_ptr<StreamingMediaContextPrivate>(shared_from_this());
|
||||||
|
|
||||||
|
int64_t absoluteTimestamp = rtc::TimeMillis();
|
||||||
|
int64_t minDelayedRequestTimeout = INT_MAX;
|
||||||
|
|
||||||
|
bool shouldRequestMoreSegments = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < _pendingSegments.size(); i++) {
|
||||||
|
auto pendingSegment = _pendingSegments[i];
|
||||||
|
auto segmentTimestamp = pendingSegment->timestamp;
|
||||||
|
|
||||||
|
bool allPartsDone = true;
|
||||||
|
|
||||||
|
for (auto &part : pendingSegment->parts) {
|
||||||
|
if (!part->result) {
|
||||||
|
allPartsDone = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!part->result && !part->task) {
|
||||||
|
if (part->minRequestTimestamp != 0) {
|
||||||
|
if (i != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (part->minRequestTimestamp > absoluteTimestamp) {
|
||||||
|
minDelayedRequestTimeout = std::min(minDelayedRequestTimeout, part->minRequestTimestamp - absoluteTimestamp);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto weakSegment = std::weak_ptr<PendingMediaSegment>(pendingSegment);
|
||||||
|
const auto weakPart = std::weak_ptr<PendingMediaSegmentPart>(part);
|
||||||
|
|
||||||
|
std::function<void(BroadcastPart &&)> handleResult = [weak, weakSegment, weakPart, threads = _threads, segmentTimestamp](BroadcastPart &&part) {
|
||||||
|
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, weakSegment, weakPart, part = std::move(part), segmentTimestamp]() mutable {
|
||||||
|
auto strong = weak.lock();
|
||||||
|
if (!strong) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto strongSegment = weakSegment.lock();
|
||||||
|
if (!strongSegment) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pendingPart = weakPart.lock();
|
||||||
|
if (!pendingPart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingPart->task.reset();
|
||||||
|
|
||||||
|
switch (part.status) {
|
||||||
|
case BroadcastPart::Status::Success: {
|
||||||
|
pendingPart->result = std::make_shared<PendingMediaSegmentPartResult>(std::move(part.data));
|
||||||
|
if (strong->_nextSegmentTimestamp == 0) {
|
||||||
|
strong->_nextSegmentTimestamp = part.timestampMilliseconds + strong->_segmentDuration;
|
||||||
|
}
|
||||||
|
strong->checkPendingSegments();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BroadcastPart::Status::NotReady: {
|
||||||
|
if (segmentTimestamp == 0) {
|
||||||
|
int64_t responseTimestampMilliseconds = (int64_t)(part.responseTimestamp * 1000.0);
|
||||||
|
int64_t responseTimestampBoundary = (responseTimestampMilliseconds / strong->_segmentDuration) * strong->_segmentDuration;
|
||||||
|
|
||||||
|
strong->_nextSegmentTimestamp = responseTimestampBoundary;
|
||||||
|
strong->discardAllPendingSegments();
|
||||||
|
strong->requestSegmentsIfNeeded();
|
||||||
|
strong->checkPendingSegments();
|
||||||
|
} else {
|
||||||
|
pendingPart->minRequestTimestamp = rtc::TimeMillis() + 100;
|
||||||
|
strong->checkPendingSegments();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BroadcastPart::Status::ResyncNeeded: {
|
||||||
|
int64_t responseTimestampMilliseconds = (int64_t)(part.responseTimestamp * 1000.0);
|
||||||
|
int64_t responseTimestampBoundary = (responseTimestampMilliseconds / strong->_segmentDuration) * strong->_segmentDuration;
|
||||||
|
|
||||||
|
strong->_nextSegmentTimestamp = responseTimestampBoundary;
|
||||||
|
strong->discardAllPendingSegments();
|
||||||
|
strong->requestSegmentsIfNeeded();
|
||||||
|
strong->checkPendingSegments();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
RTC_FATAL() << "Unknown part.status";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto typeData = &part->typeData;
|
||||||
|
if (const auto audioData = absl::get_if<PendingAudioSegmentData>(typeData)) {
|
||||||
|
part->task = _requestAudioBroadcastPart(_platformContext, segmentTimestamp, _segmentDuration, handleResult);
|
||||||
|
} else if (const auto videoData = absl::get_if<PendingVideoSegmentData>(typeData)) {
|
||||||
|
part->task = _requestVideoBroadcastPart(_platformContext, segmentTimestamp, _segmentDuration, videoData->channelId, videoData->quality, handleResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allPartsDone && i == 0) {
|
||||||
|
std::shared_ptr<MediaSegment> segment = std::make_shared<MediaSegment>();
|
||||||
|
segment->timestamp = pendingSegment->timestamp;
|
||||||
|
segment->duration = _segmentDuration;
|
||||||
|
for (auto &part : pendingSegment->parts) {
|
||||||
|
const auto typeData = &part->typeData;
|
||||||
|
if (const auto audioData = absl::get_if<PendingAudioSegmentData>(typeData)) {
|
||||||
|
segment->audio = std::make_shared<AudioStreamingPart>(std::move(part->result->data));
|
||||||
|
_currentEndpointMapping = segment->audio->getEndpointMapping();
|
||||||
|
} else if (const auto videoData = absl::get_if<PendingVideoSegmentData>(typeData)) {
|
||||||
|
auto videoSegment = std::make_shared<VideoSegment>();
|
||||||
|
videoSegment->quality = videoData->quality;
|
||||||
|
if (part->result->data.empty()) {
|
||||||
|
RTC_LOG(LS_INFO) << "Video part " << segment->timestamp << " is empty";
|
||||||
|
}
|
||||||
|
videoSegment->part = std::make_shared<VideoStreamingPart>(std::move(part->result->data));
|
||||||
|
segment->video.push_back(videoSegment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_availableSegments.push_back(segment);
|
||||||
|
|
||||||
|
shouldRequestMoreSegments = true;
|
||||||
|
|
||||||
|
_pendingSegments.erase(_pendingSegments.begin() + i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (minDelayedRequestTimeout < INT32_MAX) {
|
||||||
|
const auto weak = std::weak_ptr<StreamingMediaContextPrivate>(shared_from_this());
|
||||||
|
_threads->getMediaThread()->PostDelayedTask(RTC_FROM_HERE, [weak]() {
|
||||||
|
auto strong = weak.lock();
|
||||||
|
if (!strong) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strong->checkPendingSegments();
|
||||||
|
}, std::max((int32_t)minDelayedRequestTimeout, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldRequestMoreSegments) {
|
||||||
|
requestSegmentsIfNeeded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void beginPartTask(std::shared_ptr<PendingMediaSegmentPart> part, int64_t segmentTimestamp, std::function<void()> completion) {
|
||||||
|
const auto weak = std::weak_ptr<StreamingMediaContextPrivate>(shared_from_this());
|
||||||
|
const auto weakPart = std::weak_ptr<PendingMediaSegmentPart>(part);
|
||||||
|
|
||||||
|
std::function<void(BroadcastPart &&)> handleResult = [weak, weakPart, threads = _threads, completion](BroadcastPart &&part) {
|
||||||
|
threads->getMediaThread()->PostTask(RTC_FROM_HERE, [weak, weakPart, part = std::move(part), completion]() mutable {
|
||||||
|
auto strong = weak.lock();
|
||||||
|
if (!strong) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pendingPart = weakPart.lock();
|
||||||
|
if (!pendingPart) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingPart->task.reset();
|
||||||
|
|
||||||
|
switch (part.status) {
|
||||||
|
case BroadcastPart::Status::Success: {
|
||||||
|
pendingPart->result = std::make_shared<PendingMediaSegmentPartResult>(std::move(part.data));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BroadcastPart::Status::NotReady: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BroadcastPart::Status::ResyncNeeded: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
RTC_FATAL() << "Unknown part.status";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
completion();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto typeData = &part->typeData;
|
||||||
|
if (const auto audioData = absl::get_if<PendingAudioSegmentData>(typeData)) {
|
||||||
|
part->task = _requestAudioBroadcastPart(_platformContext, segmentTimestamp, _segmentDuration, handleResult);
|
||||||
|
} else if (const auto videoData = absl::get_if<PendingVideoSegmentData>(typeData)) {
|
||||||
|
part->task = _requestVideoBroadcastPart(_platformContext, segmentTimestamp, _segmentDuration, videoData->channelId, videoData->quality, handleResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVolume(uint32_t ssrc, double volume) {
|
||||||
|
_volumeBySsrc[ssrc] = volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setActiveVideoChannels(std::vector<StreamingMediaContext::VideoChannel> const &videoChannels) {
|
||||||
|
_activeVideoChannels = videoChannels;
|
||||||
|
|
||||||
|
/*#if DEBUG
|
||||||
|
for (auto &updatedVideoChannel : _activeVideoChannels) {
|
||||||
|
if (updatedVideoChannel.quality == VideoChannelDescription::Quality::Medium) {
|
||||||
|
updatedVideoChannel.quality = VideoChannelDescription::Quality::Thumbnail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif*/
|
||||||
|
|
||||||
|
for (const auto &updatedVideoChannel : _activeVideoChannels) {
|
||||||
|
for (const auto &segment : _availableSegments) {
|
||||||
|
for (const auto &video : segment->video) {
|
||||||
|
if (video->part->getActiveEndpointId() == updatedVideoChannel.endpoint) {
|
||||||
|
if (video->quality != updatedVideoChannel.quality) {
|
||||||
|
requestPendingVideoQualityUpdate(video, segment->timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addVideoSink(std::string const &endpointId, std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
|
||||||
|
auto it = _videoSinks.find(endpointId);
|
||||||
|
if (it == _videoSinks.end()) {
|
||||||
|
_videoSinks.insert(std::make_pair(endpointId, std::vector<std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>>>()));
|
||||||
|
}
|
||||||
|
_videoSinks[endpointId].push_back(sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<Threads> _threads;
|
||||||
|
std::function<std::shared_ptr<BroadcastPartTask>(std::function<void(int64_t)>)> _requestCurrentTime;
|
||||||
|
std::function<std::shared_ptr<BroadcastPartTask>(std::shared_ptr<PlatformContext>, int64_t, int64_t, std::function<void(BroadcastPart &&)>)> _requestAudioBroadcastPart;
|
||||||
|
std::function<std::shared_ptr<BroadcastPartTask>(std::shared_ptr<PlatformContext>, int64_t, int64_t, int32_t, VideoChannelDescription::Quality, std::function<void(BroadcastPart &&)>)> _requestVideoBroadcastPart;
|
||||||
|
std::function<void(uint32_t, float, bool)> _updateAudioLevel;
|
||||||
|
|
||||||
|
const int _segmentDuration = 1000;
|
||||||
|
const int _segmentBufferDuration = 2000;
|
||||||
|
|
||||||
|
int64_t _nextSegmentTimestamp = 0;
|
||||||
|
|
||||||
|
absl::optional<int> _waitForBufferredMillisecondsBeforeRendering;
|
||||||
|
std::vector<std::shared_ptr<MediaSegment>> _availableSegments;
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<PendingMediaSegment>> _pendingSegments;
|
||||||
|
|
||||||
|
int64_t _playbackReferenceTimestamp = 0;
|
||||||
|
|
||||||
|
const size_t _audioDataRingBufferMaxSize = 4800;
|
||||||
|
webrtc::Mutex _audioDataMutex;
|
||||||
|
SampleRingBuffer _audioRingBuffer;
|
||||||
|
std::vector<int16_t> _tempAudioBuffer;
|
||||||
|
webrtc::FrameCombiner _audioFrameCombiner;
|
||||||
|
std::map<uint32_t, std::unique_ptr<SparseVad>> _audioVadMap;
|
||||||
|
|
||||||
|
std::map<uint32_t, double> _volumeBySsrc;
|
||||||
|
std::vector<StreamingMediaContext::VideoChannel> _activeVideoChannels;
|
||||||
|
std::map<std::string, std::vector<std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>>>> _videoSinks;
|
||||||
|
|
||||||
|
std::map<std::string, int32_t> _currentEndpointMapping;
|
||||||
|
|
||||||
|
std::shared_ptr<PlatformContext> _platformContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
StreamingMediaContext::StreamingMediaContext(StreamingMediaContextArguments &&arguments) {
|
||||||
|
_private = std::make_shared<StreamingMediaContextPrivate>(std::move(arguments));
|
||||||
|
_private->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamingMediaContext::~StreamingMediaContext() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamingMediaContext::setActiveVideoChannels(std::vector<VideoChannel> const &videoChannels) {
|
||||||
|
_private->setActiveVideoChannels(videoChannels);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamingMediaContext::setVolume(uint32_t ssrc, double volume) {
|
||||||
|
_private->setVolume(ssrc, volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamingMediaContext::addVideoSink(std::string const &endpointId, std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink) {
|
||||||
|
_private->addVideoSink(endpointId, sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StreamingMediaContext::getAudio(int16_t *audio_samples, const size_t num_samples, const size_t num_channels, const uint32_t samples_per_sec) {
|
||||||
|
_private->getAudio(audio_samples, num_samples, num_channels, samples_per_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.h
Normal file
53
TMessagesProj/jni/voip/tgcalls/group/StreamingMediaContext.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef TGCALLS_STREAMING_MEDIA_CONTEXT_H
|
||||||
|
#define TGCALLS_STREAMING_MEDIA_CONTEXT_H
|
||||||
|
|
||||||
|
#include "GroupInstanceImpl.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "../StaticThreads.h"
|
||||||
|
|
||||||
|
namespace tgcalls {
|
||||||
|
|
||||||
|
class StreamingMediaContextPrivate;
|
||||||
|
|
||||||
|
class StreamingMediaContext {
|
||||||
|
public:
|
||||||
|
struct VideoChannel {
|
||||||
|
VideoChannelDescription::Quality quality = VideoChannelDescription::Quality::Thumbnail;
|
||||||
|
std::string endpoint;
|
||||||
|
|
||||||
|
VideoChannel(VideoChannelDescription::Quality quality_, std::string endpoint_) :
|
||||||
|
quality(quality_),
|
||||||
|
endpoint(endpoint_) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct StreamingMediaContextArguments {
|
||||||
|
std::shared_ptr<Threads> threads;
|
||||||
|
std::function<std::shared_ptr<BroadcastPartTask>(std::function<void(int64_t)>)> requestCurrentTime;
|
||||||
|
std::function<std::shared_ptr<BroadcastPartTask>(std::shared_ptr<PlatformContext>, int64_t, int64_t, std::function<void(BroadcastPart &&)>)> requestAudioBroadcastPart;
|
||||||
|
std::function<std::shared_ptr<BroadcastPartTask>(std::shared_ptr<PlatformContext>, int64_t, int64_t, int32_t, VideoChannelDescription::Quality, std::function<void(BroadcastPart &&)>)> requestVideoBroadcastPart;
|
||||||
|
std::function<void(uint32_t, float, bool)> updateAudioLevel;
|
||||||
|
std::shared_ptr<PlatformContext> platformContext;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
StreamingMediaContext(StreamingMediaContextArguments &&arguments);
|
||||||
|
~StreamingMediaContext();
|
||||||
|
|
||||||
|
StreamingMediaContext& operator=(const StreamingMediaContext&) = delete;
|
||||||
|
StreamingMediaContext& operator=(StreamingMediaContext&&) = delete;
|
||||||
|
|
||||||
|
void setActiveVideoChannels(std::vector<VideoChannel> const &videoChannels);
|
||||||
|
void setVolume(uint32_t ssrc, double volume);
|
||||||
|
void addVideoSink(std::string const &endpointId, std::weak_ptr<rtc::VideoSinkInterface<webrtc::VideoFrame>> sink);
|
||||||
|
|
||||||
|
void getAudio(int16_t *audio_samples, const size_t num_samples, const size_t num_channels, const uint32_t samples_per_sec);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<StreamingMediaContextPrivate> _private;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,39 +0,0 @@
|
||||||
#ifndef TGCALLS_STREAMING_PART_H
|
|
||||||
#define TGCALLS_STREAMING_PART_H
|
|
||||||
|
|
||||||
#include "absl/types/optional.h"
|
|
||||||
#include <vector>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
namespace tgcalls {
|
|
||||||
|
|
||||||
class StreamingPartState;
|
|
||||||
|
|
||||||
class StreamingPart {
|
|
||||||
public:
|
|
||||||
struct StreamingPartChannel {
|
|
||||||
uint32_t ssrc = 0;
|
|
||||||
std::vector<int16_t> pcmData;
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit StreamingPart(std::vector<uint8_t> &&data);
|
|
||||||
~StreamingPart();
|
|
||||||
|
|
||||||
StreamingPart(const StreamingPart&) = delete;
|
|
||||||
StreamingPart(StreamingPart&& other) {
|
|
||||||
_state = other._state;
|
|
||||||
other._state = nullptr;
|
|
||||||
}
|
|
||||||
StreamingPart& operator=(const StreamingPart&) = delete;
|
|
||||||
StreamingPart& operator=(StreamingPart&&) = delete;
|
|
||||||
|
|
||||||
int getRemainingMilliseconds() const;
|
|
||||||
std::vector<StreamingPartChannel> get10msPerChannel();
|
|
||||||
|
|
||||||
private:
|
|
||||||
StreamingPartState *_state = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
659
TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.cpp
Normal file
659
TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.cpp
Normal file
|
@ -0,0 +1,659 @@
|
||||||
|
#include "VideoStreamingPart.h"
|
||||||
|
|
||||||
|
#include "rtc_base/logging.h"
|
||||||
|
#include "rtc_base/third_party/base64/base64.h"
|
||||||
|
#include "api/video/i420_buffer.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <libavutil/timestamp.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace tgcalls {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class AVIOContextImpl {
|
||||||
|
public:
|
||||||
|
AVIOContextImpl(std::vector<uint8_t> &&fileData) :
|
||||||
|
_fileData(std::move(fileData)) {
|
||||||
|
_buffer.resize(4 * 1024);
|
||||||
|
_context = avio_alloc_context(_buffer.data(), (int)_buffer.size(), 0, this, &AVIOContextImpl::read, NULL, &AVIOContextImpl::seek);
|
||||||
|
}
|
||||||
|
|
||||||
|
~AVIOContextImpl() {
|
||||||
|
av_free(_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read(void *opaque, unsigned char *buffer, int bufferSize) {
|
||||||
|
AVIOContextImpl *instance = static_cast<AVIOContextImpl *>(opaque);
|
||||||
|
|
||||||
|
int bytesToRead = std::min(bufferSize, ((int)instance->_fileData.size()) - instance->_fileReadPosition);
|
||||||
|
if (bytesToRead < 0) {
|
||||||
|
bytesToRead = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytesToRead > 0) {
|
||||||
|
memcpy(buffer, instance->_fileData.data() + instance->_fileReadPosition, bytesToRead);
|
||||||
|
instance->_fileReadPosition += bytesToRead;
|
||||||
|
|
||||||
|
return bytesToRead;
|
||||||
|
} else {
|
||||||
|
return AVERROR_EOF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64_t seek(void *opaque, int64_t offset, int whence) {
|
||||||
|
AVIOContextImpl *instance = static_cast<AVIOContextImpl *>(opaque);
|
||||||
|
|
||||||
|
if (whence == 0x10000) {
|
||||||
|
return (int64_t)instance->_fileData.size();
|
||||||
|
} else {
|
||||||
|
int64_t seekOffset = std::min(offset, (int64_t)instance->_fileData.size());
|
||||||
|
if (seekOffset < 0) {
|
||||||
|
seekOffset = 0;
|
||||||
|
}
|
||||||
|
instance->_fileReadPosition = (int)seekOffset;
|
||||||
|
return seekOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AVIOContext *getContext() {
|
||||||
|
return _context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<uint8_t> _fileData;
|
||||||
|
int _fileReadPosition = 0;
|
||||||
|
|
||||||
|
std::vector<uint8_t> _buffer;
|
||||||
|
AVIOContext *_context = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MediaDataPacket {
|
||||||
|
public:
|
||||||
|
MediaDataPacket() : _packet(av_packet_alloc()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaDataPacket(MediaDataPacket &&other) : _packet(other._packet) {
|
||||||
|
other._packet = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
~MediaDataPacket() {
|
||||||
|
if (_packet) {
|
||||||
|
av_packet_free(&_packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AVPacket *packet() {
|
||||||
|
return _packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AVPacket *_packet = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DecodableFrame {
|
||||||
|
public:
|
||||||
|
DecodableFrame(MediaDataPacket packet, int64_t pts, int64_t dts):
|
||||||
|
_packet(std::move(packet)),
|
||||||
|
_pts(pts),
|
||||||
|
_dts(dts) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~DecodableFrame() {
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaDataPacket &packet() {
|
||||||
|
return _packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t pts() {
|
||||||
|
return _pts;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t dts() {
|
||||||
|
return _dts;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MediaDataPacket _packet;
|
||||||
|
int64_t _pts = 0;
|
||||||
|
int64_t _dts = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Frame {
|
||||||
|
public:
|
||||||
|
Frame() {
|
||||||
|
_frame = av_frame_alloc();
|
||||||
|
}
|
||||||
|
|
||||||
|
Frame(Frame &&other) {
|
||||||
|
_frame = other._frame;
|
||||||
|
other._frame = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
~Frame() {
|
||||||
|
if (_frame) {
|
||||||
|
av_frame_unref(_frame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AVFrame *frame() {
|
||||||
|
return _frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
double pts(AVStream *stream) {
|
||||||
|
int64_t framePts = _frame->pts;
|
||||||
|
double spf = av_q2d(stream->time_base);
|
||||||
|
return ((double)framePts) * spf;
|
||||||
|
}
|
||||||
|
|
||||||
|
double duration(AVStream *stream) {
|
||||||
|
int64_t frameDuration = _frame->pkt_duration;
|
||||||
|
double spf = av_q2d(stream->time_base);
|
||||||
|
if (frameDuration != 0) {
|
||||||
|
return ((double)frameDuration) * spf;
|
||||||
|
} else {
|
||||||
|
return spf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AVFrame *_frame = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VideoStreamEvent {
|
||||||
|
int32_t offset = 0;
|
||||||
|
std::string endpointId;
|
||||||
|
int32_t rotation = 0;
|
||||||
|
int32_t extra = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VideoStreamInfo {
|
||||||
|
std::string container;
|
||||||
|
int32_t activeMask = 0;
|
||||||
|
std::vector<VideoStreamEvent> events;
|
||||||
|
};
|
||||||
|
|
||||||
|
absl::optional<int32_t> readInt32(std::vector<uint8_t> const &data, int &offset) {
|
||||||
|
if (offset + 4 > data.size()) {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t value = 0;
|
||||||
|
memcpy(&value, data.data() + offset, 4);
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<uint8_t> readBytesAsInt32(std::vector<uint8_t> const &data, int &offset, int count) {
|
||||||
|
if (offset + count > data.size()) {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count <= 4) {
|
||||||
|
int32_t value = 0;
|
||||||
|
memcpy(&value, data.data() + offset, count);
|
||||||
|
offset += count;
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t roundUp(int32_t numToRound, int32_t multiple) {
|
||||||
|
if (multiple == 0) {
|
||||||
|
return numToRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t remainder = numToRound % multiple;
|
||||||
|
if (remainder == 0) {
|
||||||
|
return numToRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
return numToRound + multiple - remainder;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<std::string> readSerializedString(std::vector<uint8_t> const &data, int &offset) {
|
||||||
|
if (const auto tmp = readBytesAsInt32(data, offset, 1)) {
|
||||||
|
int paddingBytes = 0;
|
||||||
|
int length = 0;
|
||||||
|
if (tmp.value() == 254) {
|
||||||
|
if (const auto len = readBytesAsInt32(data, offset, 3)) {
|
||||||
|
length = len.value();
|
||||||
|
paddingBytes = roundUp(length, 4) - length;
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
length = tmp.value();
|
||||||
|
paddingBytes = roundUp(length + 1, 4) - (length + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset + length > data.size()) {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string result(data.data() + offset, data.data() + offset + length);
|
||||||
|
|
||||||
|
offset += length;
|
||||||
|
offset += paddingBytes;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<VideoStreamEvent> readVideoStreamEvent(std::vector<uint8_t> const &data, int &offset) {
|
||||||
|
VideoStreamEvent event;
|
||||||
|
|
||||||
|
if (const auto offsetValue = readInt32(data, offset)) {
|
||||||
|
event.offset = offsetValue.value();
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto endpointId = readSerializedString(data, offset)) {
|
||||||
|
event.endpointId = endpointId.value();
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto rotation = readInt32(data, offset)) {
|
||||||
|
event.rotation = rotation.value();
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto extra = readInt32(data, offset)) {
|
||||||
|
event.extra = extra.value();
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<VideoStreamInfo> consumeVideoStreamInfo(std::vector<uint8_t> &data) {
|
||||||
|
int offset = 0;
|
||||||
|
if (const auto signature = readInt32(data, offset)) {
|
||||||
|
if (signature.value() != 0xa12e810d) {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoStreamInfo info;
|
||||||
|
|
||||||
|
if (const auto container = readSerializedString(data, offset)) {
|
||||||
|
info.container = container.value();
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto activeMask = readInt32(data, offset)) {
|
||||||
|
info.activeMask = activeMask.value();
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto eventCount = readInt32(data, offset)) {
|
||||||
|
if (const auto event = readVideoStreamEvent(data, offset)) {
|
||||||
|
info.events.push_back(event.value());
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.erase(data.begin(), data.begin() + offset);
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class VideoStreamingPartInternal {
|
||||||
|
public:
|
||||||
|
VideoStreamingPartInternal(std::string endpointId, webrtc::VideoRotation rotation, std::vector<uint8_t> &&fileData, std::string const &container) :
|
||||||
|
_endpointId(endpointId),
|
||||||
|
_rotation(rotation) {
|
||||||
|
_avIoContext = std::make_unique<AVIOContextImpl>(std::move(fileData));
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
AVInputFormat *inputFormat = av_find_input_format(container.c_str());
|
||||||
|
if (!inputFormat) {
|
||||||
|
_didReadToEnd = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_inputFormatContext = avformat_alloc_context();
|
||||||
|
if (!_inputFormatContext) {
|
||||||
|
_didReadToEnd = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_inputFormatContext->pb = _avIoContext->getContext();
|
||||||
|
|
||||||
|
if ((ret = avformat_open_input(&_inputFormatContext, "", inputFormat, nullptr)) < 0) {
|
||||||
|
_didReadToEnd = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ret = avformat_find_stream_info(_inputFormatContext, nullptr)) < 0) {
|
||||||
|
_didReadToEnd = true;
|
||||||
|
|
||||||
|
avformat_close_input(&_inputFormatContext);
|
||||||
|
_inputFormatContext = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AVCodecParameters *videoCodecParameters = nullptr;
|
||||||
|
AVStream *videoStream = nullptr;
|
||||||
|
for (int i = 0; i < _inputFormatContext->nb_streams; i++) {
|
||||||
|
AVStream *inStream = _inputFormatContext->streams[i];
|
||||||
|
|
||||||
|
AVCodecParameters *inCodecpar = inStream->codecpar;
|
||||||
|
if (inCodecpar->codec_type != AVMEDIA_TYPE_VIDEO) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
videoCodecParameters = inCodecpar;
|
||||||
|
videoStream = inStream;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoCodecParameters && videoStream) {
|
||||||
|
AVCodec *codec = avcodec_find_decoder(videoCodecParameters->codec_id);
|
||||||
|
if (codec) {
|
||||||
|
_codecContext = avcodec_alloc_context3(codec);
|
||||||
|
ret = avcodec_parameters_to_context(_codecContext, videoCodecParameters);
|
||||||
|
if (ret < 0) {
|
||||||
|
_didReadToEnd = true;
|
||||||
|
|
||||||
|
avcodec_free_context(&_codecContext);
|
||||||
|
_codecContext = nullptr;
|
||||||
|
} else {
|
||||||
|
_codecContext->pkt_timebase = videoStream->time_base;
|
||||||
|
|
||||||
|
ret = avcodec_open2(_codecContext, codec, nullptr);
|
||||||
|
if (ret < 0) {
|
||||||
|
_didReadToEnd = true;
|
||||||
|
|
||||||
|
avcodec_free_context(&_codecContext);
|
||||||
|
_codecContext = nullptr;
|
||||||
|
} else {
|
||||||
|
_videoStream = videoStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~VideoStreamingPartInternal() {
|
||||||
|
if (_codecContext) {
|
||||||
|
avcodec_close(_codecContext);
|
||||||
|
avcodec_free_context(&_codecContext);
|
||||||
|
}
|
||||||
|
if (_inputFormatContext) {
|
||||||
|
avformat_close_input(&_inputFormatContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string endpointId() {
|
||||||
|
return _endpointId;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<MediaDataPacket> readPacket() {
|
||||||
|
if (_didReadToEnd) {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
if (!_inputFormatContext) {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaDataPacket packet;
|
||||||
|
int result = av_read_frame(_inputFormatContext, packet.packet());
|
||||||
|
if (result < 0) {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<DecodableFrame> readNextDecodableFrame() {
|
||||||
|
while (true) {
|
||||||
|
absl::optional<MediaDataPacket> packet = readPacket();
|
||||||
|
if (packet) {
|
||||||
|
if (_videoStream && packet->packet()->stream_index == _videoStream->index) {
|
||||||
|
return std::make_shared<DecodableFrame>(std::move(packet.value()), packet->packet()->pts, packet->packet()->dts);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 videoFrame = webrtc::VideoFrame::Builder()
|
||||||
|
.set_video_frame_buffer(i420Buffer)
|
||||||
|
.set_rotation(_rotation)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return VideoStreamingPartFrame(_endpointId, videoFrame, _frame.pts(_videoStream), _frame.duration(_videoStream), _frameIndex);
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<VideoStreamingPartFrame> getNextFrame() {
|
||||||
|
if (!_codecContext) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (_didReadToEnd) {
|
||||||
|
if (!_finalFrames.empty()) {
|
||||||
|
auto frame = _finalFrames[0];
|
||||||
|
_finalFrames.erase(_finalFrames.begin());
|
||||||
|
return frame;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
auto convertedFrame = convertCurrentFrame();
|
||||||
|
if (convertedFrame) {
|
||||||
|
_frameIndex++;
|
||||||
|
return convertedFrame;
|
||||||
|
}
|
||||||
|
} else if (status == -35) {
|
||||||
|
// more data needed
|
||||||
|
} else {
|
||||||
|
_didReadToEnd = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_didReadToEnd = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_didReadToEnd = true;
|
||||||
|
int status = avcodec_send_packet(_codecContext, nullptr);
|
||||||
|
if (status == 0) {
|
||||||
|
while (true) {
|
||||||
|
auto status = avcodec_receive_frame(_codecContext, _frame.frame());
|
||||||
|
if (status == 0) {
|
||||||
|
auto convertedFrame = convertCurrentFrame();
|
||||||
|
if (convertedFrame) {
|
||||||
|
_frameIndex++;
|
||||||
|
_finalFrames.push_back(convertedFrame.value());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _endpointId;
|
||||||
|
webrtc::VideoRotation _rotation = webrtc::VideoRotation::kVideoRotation_0;
|
||||||
|
|
||||||
|
std::unique_ptr<AVIOContextImpl> _avIoContext;
|
||||||
|
|
||||||
|
AVFormatContext *_inputFormatContext = nullptr;
|
||||||
|
AVCodecContext *_codecContext = nullptr;
|
||||||
|
AVStream *_videoStream = nullptr;
|
||||||
|
Frame _frame;
|
||||||
|
|
||||||
|
std::vector<VideoStreamingPartFrame> _finalFrames;
|
||||||
|
|
||||||
|
int _frameIndex = 0;
|
||||||
|
bool _didReadToEnd = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VideoStreamingPartState {
|
||||||
|
public:
|
||||||
|
VideoStreamingPartState(std::vector<uint8_t> &&data) {
|
||||||
|
_videoStreamInfo = consumeVideoStreamInfo(data);
|
||||||
|
if (!_videoStreamInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < _videoStreamInfo->events.size(); i++) {
|
||||||
|
std::vector<uint8_t> dataSlice(data.begin() + _videoStreamInfo->events[i].offset, i == (_videoStreamInfo->events.size() - 1) ? data.end() : (data.begin() + _videoStreamInfo->events[i + 1].offset));
|
||||||
|
webrtc::VideoRotation rotation = webrtc::VideoRotation::kVideoRotation_0;
|
||||||
|
switch (_videoStreamInfo->events[i].rotation) {
|
||||||
|
case 0: {
|
||||||
|
rotation = webrtc::VideoRotation::kVideoRotation_0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 90: {
|
||||||
|
rotation = webrtc::VideoRotation::kVideoRotation_90;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 180: {
|
||||||
|
rotation = webrtc::VideoRotation::kVideoRotation_180;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 270: {
|
||||||
|
rotation = webrtc::VideoRotation::kVideoRotation_270;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto part = std::make_unique<VideoStreamingPartInternal>(_videoStreamInfo->events[i].endpointId, rotation, std::move(dataSlice), _videoStreamInfo->container);
|
||||||
|
_parsedParts.push_back(std::move(part));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~VideoStreamingPartState() {
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<VideoStreamingPartFrame> getFrameAtRelativeTimestamp(double timestamp) {
|
||||||
|
while (true) {
|
||||||
|
if (!_currentFrame) {
|
||||||
|
if (!_parsedParts.empty()) {
|
||||||
|
auto result = _parsedParts[0]->getNextFrame();
|
||||||
|
if (result) {
|
||||||
|
_currentFrame = result;
|
||||||
|
_relativeTimestamp += result->duration;
|
||||||
|
} else {
|
||||||
|
_parsedParts.erase(_parsedParts.begin());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_currentFrame) {
|
||||||
|
if (timestamp <= _relativeTimestamp) {
|
||||||
|
return _currentFrame;
|
||||||
|
} else {
|
||||||
|
_currentFrame = absl::nullopt;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<std::string> getActiveEndpointId() const {
|
||||||
|
if (!_parsedParts.empty()) {
|
||||||
|
return _parsedParts[0]->endpointId();
|
||||||
|
} else {
|
||||||
|
return absl::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
absl::optional<VideoStreamInfo> _videoStreamInfo;
|
||||||
|
std::vector<std::unique_ptr<VideoStreamingPartInternal>> _parsedParts;
|
||||||
|
absl::optional<VideoStreamingPartFrame> _currentFrame;
|
||||||
|
double _relativeTimestamp = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
VideoStreamingPart::VideoStreamingPart(std::vector<uint8_t> &&data) {
|
||||||
|
if (!data.empty()) {
|
||||||
|
_state = new VideoStreamingPartState(std::move(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoStreamingPart::~VideoStreamingPart() {
|
||||||
|
if (_state) {
|
||||||
|
delete _state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<VideoStreamingPartFrame> VideoStreamingPart::getFrameAtRelativeTimestamp(double timestamp) {
|
||||||
|
return _state
|
||||||
|
? _state->getFrameAtRelativeTimestamp(timestamp)
|
||||||
|
: absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::optional<std::string> VideoStreamingPart::getActiveEndpointId() const {
|
||||||
|
return _state
|
||||||
|
? _state->getActiveEndpointId()
|
||||||
|
: absl::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.h
Normal file
53
TMessagesProj/jni/voip/tgcalls/group/VideoStreamingPart.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#ifndef TGCALLS_VIDEO_STREAMING_PART_H
|
||||||
|
#define TGCALLS_VIDEO_STREAMING_PART_H
|
||||||
|
|
||||||
|
#include "absl/types/optional.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "api/video/video_frame.h"
|
||||||
|
#include "absl/types/optional.h"
|
||||||
|
|
||||||
|
namespace tgcalls {
|
||||||
|
|
||||||
|
class VideoStreamingPartState;
|
||||||
|
|
||||||
|
struct VideoStreamingPartFrame {
|
||||||
|
std::string endpointId;
|
||||||
|
webrtc::VideoFrame frame;
|
||||||
|
double pts = 0;
|
||||||
|
double duration = 0.0;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
VideoStreamingPartFrame(std::string endpointId_, webrtc::VideoFrame const &frame_, double pts_, double duration_, int index_) :
|
||||||
|
endpointId(endpointId_),
|
||||||
|
frame(frame_),
|
||||||
|
pts(pts_),
|
||||||
|
duration(duration_),
|
||||||
|
index(index_) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VideoStreamingPart {
|
||||||
|
public:
|
||||||
|
explicit VideoStreamingPart(std::vector<uint8_t> &&data);
|
||||||
|
~VideoStreamingPart();
|
||||||
|
|
||||||
|
VideoStreamingPart(const VideoStreamingPart&) = delete;
|
||||||
|
VideoStreamingPart(VideoStreamingPart&& other) {
|
||||||
|
_state = other._state;
|
||||||
|
other._state = nullptr;
|
||||||
|
}
|
||||||
|
VideoStreamingPart& operator=(const VideoStreamingPart&) = delete;
|
||||||
|
VideoStreamingPart& operator=(VideoStreamingPart&&) = delete;
|
||||||
|
|
||||||
|
absl::optional<VideoStreamingPartFrame> getFrameAtRelativeTimestamp(double timestamp);
|
||||||
|
absl::optional<std::string> getActiveEndpointId() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
VideoStreamingPartState *_state = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -209,6 +209,9 @@ void InstanceImplLegacy::receiveSignalingData(const std::vector<uint8_t> &data)
|
||||||
void InstanceImplLegacy::setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) {
|
void InstanceImplLegacy::setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InstanceImplLegacy::sendVideoDeviceUpdated() {
|
||||||
|
}
|
||||||
|
|
||||||
void InstanceImplLegacy::setRequestedVideoAspect(float aspect) {
|
void InstanceImplLegacy::setRequestedVideoAspect(float aspect) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ public:
|
||||||
void setNetworkType(NetworkType networkType) override;
|
void setNetworkType(NetworkType networkType) override;
|
||||||
void setMuteMicrophone(bool muteMicrophone) override;
|
void setMuteMicrophone(bool muteMicrophone) override;
|
||||||
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) override;
|
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) override;
|
||||||
|
void sendVideoDeviceUpdated() override;
|
||||||
void setRequestedVideoAspect(float aspect) override;
|
void setRequestedVideoAspect(float aspect) override;
|
||||||
bool supportsVideo() override {
|
bool supportsVideo() override {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -19,7 +19,8 @@ public:
|
||||||
|
|
||||||
void setJavaInstance(JNIEnv *env, jobject instance);
|
void setJavaInstance(JNIEnv *env, jobject instance);
|
||||||
|
|
||||||
std::shared_ptr<BroadcastPartTask> streamTask;
|
std::vector<std::shared_ptr<BroadcastPartTask>> audioStreamTasks;
|
||||||
|
std::vector<std::shared_ptr<BroadcastPartTask>> videoStreamTasks;
|
||||||
std::vector<std::shared_ptr<RequestMediaChannelDescriptionTask>> descriptionTasks;
|
std::vector<std::shared_ptr<RequestMediaChannelDescriptionTask>> descriptionTasks;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -406,7 +406,10 @@ public:
|
||||||
}
|
}
|
||||||
beginSendingVideo();
|
beginSendingVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sendVideoDeviceUpdated() {
|
||||||
|
}
|
||||||
|
|
||||||
void setRequestedVideoAspect(float aspect) {
|
void setRequestedVideoAspect(float aspect) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1015,12 +1018,12 @@ PersistentState InstanceImplReference::getPersistentState() {
|
||||||
|
|
||||||
void InstanceImplReference::stop(std::function<void(FinalState)> completion) {
|
void InstanceImplReference::stop(std::function<void(FinalState)> completion) {
|
||||||
auto result = FinalState();
|
auto result = FinalState();
|
||||||
|
|
||||||
result.persistentState = getPersistentState();
|
result.persistentState = getPersistentState();
|
||||||
result.debugLog = logSink_->result();
|
result.debugLog = logSink_->result();
|
||||||
result.trafficStats = getTrafficStats();
|
result.trafficStats = getTrafficStats();
|
||||||
result.isRatingSuggested = false;
|
result.isRatingSuggested = false;
|
||||||
|
|
||||||
completion(result);
|
completion(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@ public:
|
||||||
void setNetworkType(NetworkType networkType) override;
|
void setNetworkType(NetworkType networkType) override;
|
||||||
void setMuteMicrophone(bool muteMicrophone) override;
|
void setMuteMicrophone(bool muteMicrophone) override;
|
||||||
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) override;
|
void setVideoCapture(std::shared_ptr<VideoCaptureInterface> videoCapture) override;
|
||||||
|
void sendVideoDeviceUpdated() override {
|
||||||
|
}
|
||||||
void setRequestedVideoAspect(float aspect) override;
|
void setRequestedVideoAspect(float aspect) override;
|
||||||
bool supportsVideo() override {
|
bool supportsVideo() override {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -44,6 +44,8 @@ public:
|
||||||
TrafficStats getTrafficStats() override;
|
TrafficStats getTrafficStats() override;
|
||||||
PersistentState getPersistentState() override;
|
PersistentState getPersistentState() override;
|
||||||
void stop(std::function<void(FinalState)> completion) override;
|
void stop(std::function<void(FinalState)> completion) override;
|
||||||
|
void sendVideoDeviceUpdated() override {
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Threads> _threads;
|
std::shared_ptr<Threads> _threads;
|
||||||
|
|
|
@ -62,9 +62,7 @@ public class ChatListItemAnimator extends DefaultItemAnimator {
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.recyclerListView = listView;
|
this.recyclerListView = listView;
|
||||||
translationInterpolator = DEFAULT_INTERPOLATOR;
|
translationInterpolator = DEFAULT_INTERPOLATOR;
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
setSupportsChangeAnimations(false);
|
||||||
listView.getElevation();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -153,6 +153,8 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
|
||||||
// time.
|
// time.
|
||||||
private int[] mReusableIntPair = new int[2];
|
private int[] mReusableIntPair = new int[2];
|
||||||
|
|
||||||
|
private boolean needFixGap = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a vertical LinearLayoutManager
|
* Creates a vertical LinearLayoutManager
|
||||||
*
|
*
|
||||||
|
@ -945,6 +947,9 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
|
||||||
*/
|
*/
|
||||||
private int fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler,
|
private int fixLayoutEndGap(int endOffset, RecyclerView.Recycler recycler,
|
||||||
RecyclerView.State state, boolean canOffsetChildren) {
|
RecyclerView.State state, boolean canOffsetChildren) {
|
||||||
|
if (!needFixGap) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int gap = mOrientationHelper.getEndAfterPadding() - endOffset;
|
int gap = mOrientationHelper.getEndAfterPadding() - endOffset;
|
||||||
int fixOffset = 0;
|
int fixOffset = 0;
|
||||||
if (gap > 0) {
|
if (gap > 0) {
|
||||||
|
@ -974,6 +979,9 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
|
||||||
*/
|
*/
|
||||||
private int fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler,
|
private int fixLayoutStartGap(int startOffset, RecyclerView.Recycler recycler,
|
||||||
RecyclerView.State state, boolean canOffsetChildren) {
|
RecyclerView.State state, boolean canOffsetChildren) {
|
||||||
|
if (!needFixGap) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int gap = startOffset - getStarForFixGap();
|
int gap = startOffset - getStarForFixGap();
|
||||||
int fixOffset = 0;
|
int fixOffset = 0;
|
||||||
if (gap > 0) {
|
if (gap > 0) {
|
||||||
|
@ -2578,4 +2586,8 @@ public class LinearLayoutManager extends RecyclerView.LayoutManager implements
|
||||||
mFocusable = false;
|
mFocusable = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setNeedFixGap(boolean needFixGap) {
|
||||||
|
this.needFixGap = needFixGap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ public class LinearSmoothScrollerCustom extends RecyclerView.SmoothScroller {
|
||||||
|
|
||||||
public static final int POSITION_MIDDLE = 0;
|
public static final int POSITION_MIDDLE = 0;
|
||||||
public static final int POSITION_END = 1;
|
public static final int POSITION_END = 1;
|
||||||
|
public static final int POSITION_TOP = 2;
|
||||||
|
|
||||||
public LinearSmoothScrollerCustom(Context context, int position) {
|
public LinearSmoothScrollerCustom(Context context, int position) {
|
||||||
MILLISECONDS_PER_PX = MILLISECONDS_PER_INCH / context.getResources().getDisplayMetrics().densityDpi;
|
MILLISECONDS_PER_PX = MILLISECONDS_PER_INCH / context.getResources().getDisplayMetrics().densityDpi;
|
||||||
|
@ -125,7 +126,9 @@ public class LinearSmoothScrollerCustom extends RecyclerView.SmoothScroller {
|
||||||
|
|
||||||
int boxSize = end - start;
|
int boxSize = end - start;
|
||||||
int viewSize = bottom - top;
|
int viewSize = bottom - top;
|
||||||
if (viewSize > boxSize) {
|
if (scrollPosition == POSITION_TOP) {
|
||||||
|
start = layoutManager.getPaddingTop();
|
||||||
|
} else if (viewSize > boxSize) {
|
||||||
start = 0;
|
start = 0;
|
||||||
} else if (scrollPosition == POSITION_MIDDLE) {
|
} else if (scrollPosition == POSITION_MIDDLE) {
|
||||||
start = (boxSize - viewSize) / 2;
|
start = (boxSize - viewSize) / 2;
|
||||||
|
|
|
@ -3717,38 +3717,27 @@ public class AndroidUtilities {
|
||||||
animated = false;
|
animated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (show && view.getTag() == null) {
|
if (!animated) {
|
||||||
view.animate().setListener(null).cancel();
|
|
||||||
if (animated) {
|
|
||||||
if (view.getVisibility() != View.VISIBLE) {
|
|
||||||
view.setVisibility(View.VISIBLE);
|
|
||||||
view.setAlpha(0f);
|
|
||||||
view.setScaleX(scaleFactor);
|
|
||||||
view.setScaleY(scaleFactor);
|
|
||||||
}
|
|
||||||
view.animate().alpha(1f).scaleY(1f).scaleX(1f).setDuration(150).start();
|
|
||||||
} else {
|
|
||||||
view.setVisibility(View.VISIBLE);
|
|
||||||
view.setAlpha(1f);
|
|
||||||
view.setScaleX(1f);
|
|
||||||
view.setScaleY(1f);
|
|
||||||
}
|
|
||||||
view.setTag(1);
|
|
||||||
} else if (!show && view.getTag() != null){
|
|
||||||
view.animate().setListener(null).cancel();
|
|
||||||
if (animated) {
|
|
||||||
view.animate().alpha(0).scaleY(scaleFactor).scaleX(scaleFactor).setListener(new HideViewAfterAnimation(view)).setDuration(150).start();
|
|
||||||
} else {
|
|
||||||
view.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
view.setTag(null);
|
|
||||||
} else if (!animated) {
|
|
||||||
view.animate().setListener(null).cancel();
|
view.animate().setListener(null).cancel();
|
||||||
view.setVisibility(show ? View.VISIBLE : View.GONE);
|
view.setVisibility(show ? View.VISIBLE : View.GONE);
|
||||||
view.setTag(show ? 1 : null);
|
view.setTag(show ? 1 : null);
|
||||||
view.setAlpha(1f);
|
view.setAlpha(1f);
|
||||||
view.setScaleX(1f);
|
view.setScaleX(1f);
|
||||||
view.setScaleY(1f);
|
view.setScaleY(1f);
|
||||||
|
} else if (show && view.getTag() == null) {
|
||||||
|
view.animate().setListener(null).cancel();
|
||||||
|
if (view.getVisibility() != View.VISIBLE) {
|
||||||
|
view.setVisibility(View.VISIBLE);
|
||||||
|
view.setAlpha(0f);
|
||||||
|
view.setScaleX(scaleFactor);
|
||||||
|
view.setScaleY(scaleFactor);
|
||||||
|
}
|
||||||
|
view.animate().alpha(1f).scaleY(1f).scaleX(1f).setDuration(150).start();
|
||||||
|
view.setTag(1);
|
||||||
|
} else if (!show && view.getTag() != null) {
|
||||||
|
view.animate().setListener(null).cancel();
|
||||||
|
view.animate().alpha(0).scaleY(scaleFactor).scaleX(scaleFactor).setListener(new HideViewAfterAnimation(view)).setDuration(150).start();
|
||||||
|
view.setTag(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -265,8 +265,10 @@ public class ApplicationLoader extends Application {
|
||||||
}
|
}
|
||||||
Utilities.globalQueue.postRunnable(() -> {
|
Utilities.globalQueue.postRunnable(() -> {
|
||||||
try {
|
try {
|
||||||
|
SharedConfig.pushStringGetTimeStart = SystemClock.elapsedRealtime();
|
||||||
FirebaseMessaging.getInstance().getToken()
|
FirebaseMessaging.getInstance().getToken()
|
||||||
.addOnCompleteListener(task -> {
|
.addOnCompleteListener(task -> {
|
||||||
|
SharedConfig.pushStringGetTimeEnd = SystemClock.elapsedRealtime();
|
||||||
if (!task.isSuccessful()) {
|
if (!task.isSuccessful()) {
|
||||||
if (BuildVars.LOGS_ENABLED) {
|
if (BuildVars.LOGS_ENABLED) {
|
||||||
FileLog.d("Failed to get regid");
|
FileLog.d("Failed to get regid");
|
||||||
|
|
|
@ -19,8 +19,8 @@ public class BuildVars {
|
||||||
public static boolean USE_CLOUD_STRINGS = true;
|
public static boolean USE_CLOUD_STRINGS = true;
|
||||||
public static boolean CHECK_UPDATES = true;
|
public static boolean CHECK_UPDATES = true;
|
||||||
public static boolean NO_SCOPED_STORAGE = true/* || Build.VERSION.SDK_INT <= 28*/;
|
public static boolean NO_SCOPED_STORAGE = true/* || Build.VERSION.SDK_INT <= 28*/;
|
||||||
public static int BUILD_VERSION = 2390;
|
public static int BUILD_VERSION = 2406;
|
||||||
public static String BUILD_VERSION_STRING = "7.9.3";
|
public static String BUILD_VERSION_STRING = "8.0.0";
|
||||||
public static int APP_ID = 4;
|
public static int APP_ID = 4;
|
||||||
public static String APP_HASH = "014b35b6184100b085b0d0572f9b5103";
|
public static String APP_HASH = "014b35b6184100b085b0d0572f9b5103";
|
||||||
public static String APPCENTER_HASH = "a5b5c4f5-51da-dedc-9918-d9766a22ca7c";
|
public static String APPCENTER_HASH = "a5b5c4f5-51da-dedc-9918-d9766a22ca7c";
|
||||||
|
|
|
@ -1035,7 +1035,7 @@ public class ChatObject {
|
||||||
int selfId = getSelfId();
|
int selfId = getSelfId();
|
||||||
VoIPService service = VoIPService.getSharedInstance();
|
VoIPService service = VoIPService.getSharedInstance();
|
||||||
TLRPC.TL_groupCallParticipant selfParticipant = participants.get(selfId);
|
TLRPC.TL_groupCallParticipant selfParticipant = participants.get(selfId);
|
||||||
canStreamVideo = selfParticipant != null && selfParticipant.video_joined;
|
canStreamVideo = true;//selfParticipant != null && selfParticipant.video_joined || BuildVars.DEBUG_PRIVATE_VERSION;
|
||||||
boolean allowedVideoCount;
|
boolean allowedVideoCount;
|
||||||
boolean hasAnyVideo = false;
|
boolean hasAnyVideo = false;
|
||||||
activeVideos = 0;
|
activeVideos = 0;
|
||||||
|
@ -1243,7 +1243,7 @@ public class ChatObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void toggleRecord(String title) {
|
public void toggleRecord(String title, int type) {
|
||||||
recording = !recording;
|
recording = !recording;
|
||||||
TLRPC.TL_phone_toggleGroupCallRecord req = new TLRPC.TL_phone_toggleGroupCallRecord();
|
TLRPC.TL_phone_toggleGroupCallRecord req = new TLRPC.TL_phone_toggleGroupCallRecord();
|
||||||
req.call = getInputGroupCall();
|
req.call = getInputGroupCall();
|
||||||
|
@ -1252,6 +1252,11 @@ public class ChatObject {
|
||||||
req.title = title;
|
req.title = title;
|
||||||
req.flags |= 2;
|
req.flags |= 2;
|
||||||
}
|
}
|
||||||
|
if (type == 1 || type == 2) {
|
||||||
|
req.flags |= 4;
|
||||||
|
req.video = true;
|
||||||
|
req.video_portrait = type == 1;
|
||||||
|
}
|
||||||
currentAccount.getConnectionsManager().sendRequest(req, (response, error) -> {
|
currentAccount.getConnectionsManager().sendRequest(req, (response, error) -> {
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
final TLRPC.Updates res = (TLRPC.Updates) response;
|
final TLRPC.Updates res = (TLRPC.Updates) response;
|
||||||
|
@ -1434,6 +1439,10 @@ public class ChatObject {
|
||||||
return chat instanceof TLRPC.TL_channel || chat instanceof TLRPC.TL_channelForbidden;
|
return chat instanceof TLRPC.TL_channel || chat instanceof TLRPC.TL_channelForbidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isChannelOrGiga(TLRPC.Chat chat) {
|
||||||
|
return (chat instanceof TLRPC.TL_channel || chat instanceof TLRPC.TL_channelForbidden) && (!chat.megagroup || chat.gigagroup);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isMegagroup(TLRPC.Chat chat) {
|
public static boolean isMegagroup(TLRPC.Chat chat) {
|
||||||
return (chat instanceof TLRPC.TL_channel || chat instanceof TLRPC.TL_channelForbidden) && chat.megagroup;
|
return (chat instanceof TLRPC.TL_channel || chat instanceof TLRPC.TL_channelForbidden) && chat.megagroup;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ public class DispatchQueue extends Thread {
|
||||||
private volatile Handler handler = null;
|
private volatile Handler handler = null;
|
||||||
private CountDownLatch syncLatch = new CountDownLatch(1);
|
private CountDownLatch syncLatch = new CountDownLatch(1);
|
||||||
private long lastTaskTime;
|
private long lastTaskTime;
|
||||||
|
private static int indexPointer = 0;
|
||||||
|
public final int index = indexPointer++;
|
||||||
|
|
||||||
public DispatchQueue(final String threadName) {
|
public DispatchQueue(final String threadName) {
|
||||||
this(threadName, true);
|
this(threadName, true);
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
package org.telegram.messenger;
|
package org.telegram.messenger;
|
||||||
|
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.util.SparseIntArray;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
import androidx.annotation.UiThread;
|
import androidx.annotation.UiThread;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
public class DispatchQueuePool {
|
public class DispatchQueuePool {
|
||||||
|
|
||||||
private LinkedList<DispatchQueue> queues = new LinkedList<>();
|
private LinkedList<DispatchQueue> queues = new LinkedList<>();
|
||||||
private HashMap<DispatchQueue, Integer> busyQueuesMap = new HashMap<>();
|
private SparseIntArray busyQueuesMap = new SparseIntArray();
|
||||||
private LinkedList<DispatchQueue> busyQueues = new LinkedList<>();
|
private LinkedList<DispatchQueue> busyQueues = new LinkedList<>();
|
||||||
private int maxCount;
|
private int maxCount;
|
||||||
private int createdCount;
|
private int createdCount;
|
||||||
|
@ -66,22 +66,19 @@ public class DispatchQueuePool {
|
||||||
}
|
}
|
||||||
totalTasksCount++;
|
totalTasksCount++;
|
||||||
busyQueues.add(queue);
|
busyQueues.add(queue);
|
||||||
Integer count = busyQueuesMap.get(queue);
|
int count = busyQueuesMap.get(queue.index, 0);
|
||||||
if (count == null) {
|
busyQueuesMap.put(queue.index, count + 1);
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
busyQueuesMap.put(queue, count + 1);
|
|
||||||
queue.postRunnable(() -> {
|
queue.postRunnable(() -> {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
AndroidUtilities.runOnUIThread(() -> {
|
AndroidUtilities.runOnUIThread(() -> {
|
||||||
totalTasksCount--;
|
totalTasksCount--;
|
||||||
int remainingTasksCount = busyQueuesMap.get(queue) - 1;
|
int remainingTasksCount = busyQueuesMap.get(queue.index) - 1;
|
||||||
if (remainingTasksCount == 0) {
|
if (remainingTasksCount == 0) {
|
||||||
busyQueuesMap.remove(queue);
|
busyQueuesMap.delete(queue.index);
|
||||||
busyQueues.remove(queue);
|
busyQueues.remove(queue);
|
||||||
queues.add(queue);
|
queues.add(queue);
|
||||||
} else {
|
} else {
|
||||||
busyQueuesMap.put(queue, remainingTasksCount);
|
busyQueuesMap.put(queue.index, remainingTasksCount);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -27,6 +27,7 @@ import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.text.Spannable;
|
import android.text.Spannable;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.text.style.DynamicDrawableSpan;
|
import android.text.style.DynamicDrawableSpan;
|
||||||
import android.text.style.ImageSpan;
|
import android.text.style.ImageSpan;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -189,6 +190,9 @@ public class Emoji {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isValidEmoji(CharSequence code) {
|
public static boolean isValidEmoji(CharSequence code) {
|
||||||
|
if (TextUtils.isEmpty(code)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
DrawableInfo info = rects.get(code);
|
DrawableInfo info = rects.get(code);
|
||||||
if (info == null) {
|
if (info == null) {
|
||||||
CharSequence newCode = EmojiData.emojiAliasMap.get(code);
|
CharSequence newCode = EmojiData.emojiAliasMap.get(code);
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
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 ArrayList<TLRPC.TL_pollAnswerVoters> pollChoosenAnswers = new ArrayList();
|
||||||
|
|
||||||
|
public ForwardingMessagesParams(ArrayList<MessageObject> messages, long newDialogId) {
|
||||||
|
this.messages = messages;
|
||||||
|
hasCaption = false;
|
||||||
|
hasSenders = false;
|
||||||
|
isSecret = DialogObject.isSecretDialogId(newDialogId);
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
TLRPC.MessageFwdHeader header = null;
|
||||||
|
|
||||||
|
int 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<Integer> uids = new ArrayList<>();
|
||||||
|
for (int a = 1; a < messages.size(); a++) {
|
||||||
|
MessageObject object = messages.get(a);
|
||||||
|
int 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -134,12 +134,21 @@ public class GcmPushListenerService extends FirebaseMessagingService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int account = UserConfig.selectedAccount;
|
int account = UserConfig.selectedAccount;
|
||||||
|
boolean foundAccount = false;
|
||||||
for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) {
|
for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) {
|
||||||
if (UserConfig.getInstance(a).getClientUserId() == accountUserId) {
|
if (UserConfig.getInstance(a).getClientUserId() == accountUserId) {
|
||||||
account = a;
|
account = a;
|
||||||
|
foundAccount = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!foundAccount) {
|
||||||
|
if (BuildVars.LOGS_ENABLED) {
|
||||||
|
FileLog.d("GCM ACCOUNT NOT FOUND");
|
||||||
|
}
|
||||||
|
countDownLatch.countDown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
final int accountFinal = currentAccount = account;
|
final int accountFinal = currentAccount = account;
|
||||||
if (!UserConfig.getInstance(currentAccount).isClientActivated()) {
|
if (!UserConfig.getInstance(currentAccount).isClientActivated()) {
|
||||||
if (BuildVars.LOGS_ENABLED) {
|
if (BuildVars.LOGS_ENABLED) {
|
||||||
|
@ -1109,6 +1118,11 @@ public class GcmPushListenerService extends FirebaseMessagingService {
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
boolean sendStat = false;
|
||||||
|
if (SharedConfig.pushStringGetTimeStart != 0 && SharedConfig.pushStringGetTimeEnd != 0 && (!SharedConfig.pushStatSent || !TextUtils.equals(SharedConfig.pushString, token))) {
|
||||||
|
sendStat = true;
|
||||||
|
SharedConfig.pushStatSent = false;
|
||||||
|
}
|
||||||
SharedConfig.pushString = token;
|
SharedConfig.pushString = token;
|
||||||
for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) {
|
for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) {
|
||||||
UserConfig userConfig = UserConfig.getInstance(a);
|
UserConfig userConfig = UserConfig.getInstance(a);
|
||||||
|
@ -1116,6 +1130,30 @@ public class GcmPushListenerService extends FirebaseMessagingService {
|
||||||
userConfig.saveConfig(false);
|
userConfig.saveConfig(false);
|
||||||
if (userConfig.getClientUserId() != 0) {
|
if (userConfig.getClientUserId() != 0) {
|
||||||
final int currentAccount = a;
|
final int currentAccount = a;
|
||||||
|
if (sendStat) {
|
||||||
|
TLRPC.TL_help_saveAppLog req = new TLRPC.TL_help_saveAppLog();
|
||||||
|
TLRPC.TL_inputAppEvent event = new TLRPC.TL_inputAppEvent();
|
||||||
|
event.time = SharedConfig.pushStringGetTimeStart;
|
||||||
|
event.type = "fcm_token_request";
|
||||||
|
event.peer = 0;
|
||||||
|
event.data = new TLRPC.TL_jsonNull();
|
||||||
|
req.events.add(event);
|
||||||
|
|
||||||
|
event = new TLRPC.TL_inputAppEvent();
|
||||||
|
event.time = SharedConfig.pushStringGetTimeEnd;
|
||||||
|
event.type = "fcm_token_response";
|
||||||
|
event.peer = SharedConfig.pushStringGetTimeEnd - SharedConfig.pushStringGetTimeStart;
|
||||||
|
event.data = new TLRPC.TL_jsonNull();
|
||||||
|
req.events.add(event);
|
||||||
|
|
||||||
|
sendStat = false;
|
||||||
|
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
|
||||||
|
if (error != null) {
|
||||||
|
SharedConfig.pushStatSent = true;
|
||||||
|
SharedConfig.saveConfig();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
AndroidUtilities.runOnUIThread(() -> MessagesController.getInstance(currentAccount).registerForPush(token));
|
AndroidUtilities.runOnUIThread(() -> MessagesController.getInstance(currentAccount).registerForPush(token));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -342,7 +342,8 @@ public class ImageLocation {
|
||||||
} else if (document != null) {
|
} else if (document != null) {
|
||||||
if (!url && document instanceof DocumentObject.ThemeDocument) {
|
if (!url && document instanceof DocumentObject.ThemeDocument) {
|
||||||
DocumentObject.ThemeDocument themeDocument = (DocumentObject.ThemeDocument) document;
|
DocumentObject.ThemeDocument themeDocument = (DocumentObject.ThemeDocument) document;
|
||||||
return document.dc_id + "_" + document.id + "_" + Theme.getBaseThemeKey(themeDocument.themeSettings) + "_" + themeDocument.themeSettings.accent_color + "_" + themeDocument.themeSettings.message_top_color + "_" + themeDocument.themeSettings.message_bottom_color;
|
return document.dc_id + "_" + document.id + "_" + Theme.getBaseThemeKey(themeDocument.themeSettings) + "_" + themeDocument.themeSettings.accent_color + "_" +
|
||||||
|
(themeDocument.themeSettings.message_colors.size() > 1 ? themeDocument.themeSettings.message_colors.get(1) : 0) + "_" + (themeDocument.themeSettings.message_colors.size() > 0 ? themeDocument.themeSettings.message_colors.get(0) : 0);
|
||||||
} else if (document.id != 0 && document.dc_id != 0) {
|
} else if (document.id != 0 && document.dc_id != 0) {
|
||||||
return document.dc_id + "_" + document.id;
|
return document.dc_id + "_" + document.id;
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,7 +203,6 @@ public class MediaDataController extends BaseController {
|
||||||
loadingStickers[a] = false;
|
loadingStickers[a] = false;
|
||||||
stickersLoaded[a] = false;
|
stickersLoaded[a] = false;
|
||||||
}
|
}
|
||||||
featuredStickerSets.clear();
|
|
||||||
loadingPinnedMessages.clear();
|
loadingPinnedMessages.clear();
|
||||||
loadFeaturedDate = 0;
|
loadFeaturedDate = 0;
|
||||||
loadFeaturedHash = 0;
|
loadFeaturedHash = 0;
|
||||||
|
|
|
@ -122,6 +122,9 @@ public class MessageObject {
|
||||||
public boolean isRestrictedMessage;
|
public boolean isRestrictedMessage;
|
||||||
public long loadedFileSize;
|
public long loadedFileSize;
|
||||||
|
|
||||||
|
public byte[] sponsoredId;
|
||||||
|
public String botStartParam;
|
||||||
|
|
||||||
public boolean animateComments;
|
public boolean animateComments;
|
||||||
|
|
||||||
public boolean loadingCancelled;
|
public boolean loadingCancelled;
|
||||||
|
@ -135,6 +138,7 @@ public class MessageObject {
|
||||||
public boolean cancelEditing;
|
public boolean cancelEditing;
|
||||||
|
|
||||||
public boolean scheduled;
|
public boolean scheduled;
|
||||||
|
public boolean preview;
|
||||||
|
|
||||||
public ArrayList<TLRPC.TL_pollAnswer> checkedVotes;
|
public ArrayList<TLRPC.TL_pollAnswer> checkedVotes;
|
||||||
|
|
||||||
|
@ -179,6 +183,7 @@ public class MessageObject {
|
||||||
|
|
||||||
public ArrayList<String> highlightedWords;
|
public ArrayList<String> highlightedWords;
|
||||||
public String messageTrimmedToHighlight;
|
public String messageTrimmedToHighlight;
|
||||||
|
public int parentWidth;
|
||||||
|
|
||||||
static final String[] excludeWords = new String[] {
|
static final String[] excludeWords = new String[] {
|
||||||
" vs. ",
|
" vs. ",
|
||||||
|
@ -1579,6 +1584,27 @@ public class MessageObject {
|
||||||
} else {
|
} else {
|
||||||
message.media = new TLRPC.TL_messageMediaEmpty();
|
message.media = new TLRPC.TL_messageMediaEmpty();
|
||||||
}
|
}
|
||||||
|
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionChangeTheme) {
|
||||||
|
messageText = replaceWithLink(chat.megagroup ? LocaleController.getString("EventLogEditedGroupTheme", R.string.EventLogEditedGroupTheme) : LocaleController.getString("EventLogEditedChannelTheme", R.string.EventLogEditedChannelTheme), "un1", fromUser);
|
||||||
|
message = new TLRPC.TL_message();
|
||||||
|
message.out = false;
|
||||||
|
message.unread = false;
|
||||||
|
message.from_id = new TLRPC.TL_peerUser();
|
||||||
|
message.from_id.user_id = event.user_id;
|
||||||
|
message.peer_id = peer_id;
|
||||||
|
message.date = event.date;
|
||||||
|
message.message = ((TLRPC.TL_channelAdminLogEventActionChangeTheme) event.action).new_value;
|
||||||
|
if (!TextUtils.isEmpty(((TLRPC.TL_channelAdminLogEventActionChangeTheme) event.action).prev_value)) {
|
||||||
|
message.media = new TLRPC.TL_messageMediaWebPage();
|
||||||
|
message.media.webpage = new TLRPC.TL_webPage();
|
||||||
|
message.media.webpage.flags = 10;
|
||||||
|
message.media.webpage.display_url = "";
|
||||||
|
message.media.webpage.url = "";
|
||||||
|
message.media.webpage.site_name = LocaleController.getString("EventLogPreviousGroupTheme", R.string.EventLogPreviousGroupTheme);
|
||||||
|
message.media.webpage.description = ((TLRPC.TL_channelAdminLogEventActionChangeTheme) event.action).prev_value;
|
||||||
|
} else {
|
||||||
|
message.media = new TLRPC.TL_messageMediaEmpty();
|
||||||
|
}
|
||||||
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionChangeUsername) {
|
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionChangeUsername) {
|
||||||
String newLink = ((TLRPC.TL_channelAdminLogEventActionChangeUsername) event.action).new_value;
|
String newLink = ((TLRPC.TL_channelAdminLogEventActionChangeUsername) event.action).new_value;
|
||||||
if (!TextUtils.isEmpty(newLink)) {
|
if (!TextUtils.isEmpty(newLink)) {
|
||||||
|
@ -1710,9 +1736,17 @@ public class MessageObject {
|
||||||
messageText = replaceWithLink(LocaleController.formatString("EventLogToggledSlowmodeOn", R.string.EventLogToggledSlowmodeOn, string), "un1", fromUser);
|
messageText = replaceWithLink(LocaleController.formatString("EventLogToggledSlowmodeOn", R.string.EventLogToggledSlowmodeOn, string), "un1", fromUser);
|
||||||
}
|
}
|
||||||
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionStartGroupCall) {
|
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionStartGroupCall) {
|
||||||
messageText = replaceWithLink(LocaleController.getString("EventLogStartedVoiceChat", R.string.EventLogStartedVoiceChat), "un1", fromUser);
|
if (ChatObject.isChannel(chat) && (!chat.megagroup || chat.gigagroup)) {
|
||||||
|
messageText = replaceWithLink(LocaleController.getString("EventLogStartedLiveStream", R.string.EventLogStartedLiveStream), "un1", fromUser);
|
||||||
|
} else {
|
||||||
|
messageText = replaceWithLink(LocaleController.getString("EventLogStartedVoiceChat", R.string.EventLogStartedVoiceChat), "un1", fromUser);
|
||||||
|
}
|
||||||
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionDiscardGroupCall) {
|
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionDiscardGroupCall) {
|
||||||
messageText = replaceWithLink(LocaleController.getString("EventLogEndedVoiceChat", R.string.EventLogEndedVoiceChat), "un1", fromUser);
|
if (ChatObject.isChannel(chat) && (!chat.megagroup || chat.gigagroup)) {
|
||||||
|
messageText = replaceWithLink(LocaleController.getString("EventLogEndedLiveStream", R.string.EventLogEndedLiveStream), "un1", fromUser);
|
||||||
|
} else {
|
||||||
|
messageText = replaceWithLink(LocaleController.getString("EventLogEndedVoiceChat", R.string.EventLogEndedVoiceChat), "un1", fromUser);
|
||||||
|
}
|
||||||
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionParticipantMute) {
|
} else if (event.action instanceof TLRPC.TL_channelAdminLogEventActionParticipantMute) {
|
||||||
TLRPC.TL_channelAdminLogEventActionParticipantMute action = (TLRPC.TL_channelAdminLogEventActionParticipantMute) event.action;
|
TLRPC.TL_channelAdminLogEventActionParticipantMute action = (TLRPC.TL_channelAdminLogEventActionParticipantMute) event.action;
|
||||||
int id = getPeerId(action.participant.peer);
|
int id = getPeerId(action.participant.peer);
|
||||||
|
@ -2211,6 +2245,10 @@ public class MessageObject {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSponsored() {
|
||||||
|
return sponsoredId != null;
|
||||||
|
}
|
||||||
|
|
||||||
public long getPollId() {
|
public long getPollId() {
|
||||||
if (type != TYPE_POLL) {
|
if (type != TYPE_POLL) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2466,7 +2504,11 @@ public class MessageObject {
|
||||||
if (messageOwner.action != null) {
|
if (messageOwner.action != null) {
|
||||||
if (messageOwner.action instanceof TLRPC.TL_messageActionGroupCallScheduled) {
|
if (messageOwner.action instanceof TLRPC.TL_messageActionGroupCallScheduled) {
|
||||||
TLRPC.TL_messageActionGroupCallScheduled action = (TLRPC.TL_messageActionGroupCallScheduled) messageOwner.action;
|
TLRPC.TL_messageActionGroupCallScheduled action = (TLRPC.TL_messageActionGroupCallScheduled) messageOwner.action;
|
||||||
messageText = LocaleController.formatString("ActionGroupCallScheduled", R.string.ActionGroupCallScheduled, LocaleController.formatStartsTime(action.schedule_date, 3, false));
|
if (messageOwner.peer_id instanceof TLRPC.TL_peerChat || isSupergroup()) {
|
||||||
|
messageText = LocaleController.formatString("ActionGroupCallScheduled", R.string.ActionGroupCallScheduled, LocaleController.formatStartsTime(action.schedule_date, 3, false));
|
||||||
|
} else {
|
||||||
|
messageText = LocaleController.formatString("ActionChannelCallScheduled", R.string.ActionChannelCallScheduled, LocaleController.formatStartsTime(action.schedule_date, 3, false));
|
||||||
|
}
|
||||||
} else if (messageOwner.action instanceof TLRPC.TL_messageActionGroupCall) {
|
} else if (messageOwner.action instanceof TLRPC.TL_messageActionGroupCall) {
|
||||||
if (messageOwner.action.duration != 0) {
|
if (messageOwner.action.duration != 0) {
|
||||||
String time;
|
String time;
|
||||||
|
@ -2494,7 +2536,7 @@ public class MessageObject {
|
||||||
messageText = replaceWithLink(LocaleController.formatString("ActionGroupCallEndedBy", R.string.ActionGroupCallEndedBy, time), "un1", fromObject);
|
messageText = replaceWithLink(LocaleController.formatString("ActionGroupCallEndedBy", R.string.ActionGroupCallEndedBy, time), "un1", fromObject);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageText = LocaleController.formatString("ActionGroupCallEnded", R.string.ActionGroupCallEnded, time);
|
messageText = LocaleController.formatString("ActionChannelCallEnded", R.string.ActionChannelCallEnded, time);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (messageOwner.peer_id instanceof TLRPC.TL_peerChat || isSupergroup()) {
|
if (messageOwner.peer_id instanceof TLRPC.TL_peerChat || isSupergroup()) {
|
||||||
|
@ -2504,7 +2546,7 @@ public class MessageObject {
|
||||||
messageText = replaceWithLink(LocaleController.getString("ActionGroupCallStarted", R.string.ActionGroupCallStarted), "un1", fromObject);
|
messageText = replaceWithLink(LocaleController.getString("ActionGroupCallStarted", R.string.ActionGroupCallStarted), "un1", fromObject);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
messageText = LocaleController.getString("ActionGroupCallJustStarted", R.string.ActionGroupCallJustStarted);
|
messageText = LocaleController.getString("ActionChannelCallJustStarted", R.string.ActionChannelCallJustStarted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (messageOwner.action instanceof TLRPC.TL_messageActionInviteToGroupCall) {
|
} else if (messageOwner.action instanceof TLRPC.TL_messageActionInviteToGroupCall) {
|
||||||
|
@ -4113,7 +4155,9 @@ public class MessageObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean needDrawShareButton() {
|
public boolean needDrawShareButton() {
|
||||||
if (scheduled) {
|
if (preview) {
|
||||||
|
return false;
|
||||||
|
} else if (scheduled) {
|
||||||
return false;
|
return false;
|
||||||
} else if (eventId != 0) {
|
} else if (eventId != 0) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -4160,7 +4204,7 @@ public class MessageObject {
|
||||||
if (AndroidUtilities.isTablet() && eventId != 0) {
|
if (AndroidUtilities.isTablet() && eventId != 0) {
|
||||||
generatedWithMinSize = AndroidUtilities.dp(530);
|
generatedWithMinSize = AndroidUtilities.dp(530);
|
||||||
} else {
|
} else {
|
||||||
generatedWithMinSize = AndroidUtilities.isTablet() ? AndroidUtilities.getMinTabletSide() : AndroidUtilities.displaySize.x;
|
generatedWithMinSize = AndroidUtilities.isTablet() ? AndroidUtilities.getMinTabletSide() : getParentWidth();
|
||||||
}
|
}
|
||||||
generatedWithDensity = AndroidUtilities.density;
|
generatedWithDensity = AndroidUtilities.density;
|
||||||
if (messageOwner.media instanceof TLRPC.TL_messageMediaWebPage && messageOwner.media.webpage != null && "telegram_background".equals(messageOwner.media.webpage.type)) {
|
if (messageOwner.media instanceof TLRPC.TL_messageMediaWebPage && messageOwner.media.webpage != null && "telegram_background".equals(messageOwner.media.webpage.type)) {
|
||||||
|
@ -4464,6 +4508,9 @@ public class MessageObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOutOwner() {
|
public boolean isOutOwner() {
|
||||||
|
if (preview) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
TLRPC.Chat chat = messageOwner.peer_id != null && messageOwner.peer_id.channel_id != 0 ? getChat(null, null, messageOwner.peer_id.channel_id) : null;
|
TLRPC.Chat chat = messageOwner.peer_id != null && messageOwner.peer_id.channel_id != 0 ? getChat(null, null, messageOwner.peer_id.channel_id) : null;
|
||||||
if (!messageOwner.out || !(messageOwner.from_id instanceof TLRPC.TL_peerUser) && (!(messageOwner.from_id instanceof TLRPC.TL_peerChannel) || ChatObject.isChannel(chat) && !chat.megagroup) || messageOwner.post) {
|
if (!messageOwner.out || !(messageOwner.from_id instanceof TLRPC.TL_peerUser) && (!(messageOwner.from_id instanceof TLRPC.TL_peerChannel) || ChatObject.isChannel(chat) && !chat.megagroup) || messageOwner.post) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -4480,11 +4527,11 @@ public class MessageObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean needDrawAvatar() {
|
public boolean needDrawAvatar() {
|
||||||
return isFromUser() || isFromGroup() || eventId != 0 || messageOwner.fwd_from != null && messageOwner.fwd_from.saved_from_peer != null;
|
return !isSponsored() && (isFromUser() || isFromGroup() || eventId != 0 || messageOwner.fwd_from != null && messageOwner.fwd_from.saved_from_peer != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean needDrawAvatarInternal() {
|
private boolean needDrawAvatarInternal() {
|
||||||
return isFromChat() && isFromUser() || isFromGroup() || eventId != 0 || messageOwner.fwd_from != null && messageOwner.fwd_from.saved_from_peer != null;
|
return !isSponsored() && (isFromChat() && isFromUser() || isFromGroup() || eventId != 0 || messageOwner.fwd_from != null && messageOwner.fwd_from.saved_from_peer != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFromChat() {
|
public boolean isFromChat() {
|
||||||
|
@ -4541,7 +4588,7 @@ public class MessageObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isForwardedChannelPost() {
|
public boolean isForwardedChannelPost() {
|
||||||
return messageOwner.from_id instanceof TLRPC.TL_peerChannel && messageOwner.fwd_from != null && messageOwner.fwd_from.channel_post != 0;
|
return messageOwner.from_id instanceof TLRPC.TL_peerChannel && messageOwner.fwd_from != null && messageOwner.fwd_from.channel_post != 0 && messageOwner.fwd_from.saved_from_peer instanceof TLRPC.TL_peerChannel && messageOwner.from_id.channel_id == messageOwner.fwd_from.saved_from_peer.channel_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUnread() {
|
public boolean isUnread() {
|
||||||
|
@ -5202,6 +5249,10 @@ public class MessageObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getParentWidth() {
|
||||||
|
return (preview && parentWidth > 0) ? parentWidth : AndroidUtilities.displaySize.x;
|
||||||
|
}
|
||||||
|
|
||||||
public String getStickerEmoji() {
|
public String getStickerEmoji() {
|
||||||
TLRPC.Document document = getDocument();
|
TLRPC.Document document = getDocument();
|
||||||
if (document == null) {
|
if (document == null) {
|
||||||
|
@ -5604,7 +5655,7 @@ public class MessageObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canForwardMessage() {
|
public boolean canForwardMessage() {
|
||||||
return !(messageOwner instanceof TLRPC.TL_message_secret) && !needDrawBluredPreview() && !isLiveLocation() && type != 16;
|
return !(messageOwner instanceof TLRPC.TL_message_secret) && !needDrawBluredPreview() && !isLiveLocation() && type != 16 && !isSponsored();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canEditMedia() {
|
public boolean canEditMedia() {
|
||||||
|
@ -5712,7 +5763,7 @@ public class MessageObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canDeleteMessage(boolean inScheduleMode, TLRPC.Chat chat) {
|
public boolean canDeleteMessage(boolean inScheduleMode, TLRPC.Chat chat) {
|
||||||
return eventId == 0 && canDeleteMessage(currentAccount, inScheduleMode, messageOwner, chat);
|
return eventId == 0 && sponsoredId == null && canDeleteMessage(currentAccount, inScheduleMode, messageOwner, chat);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean canDeleteMessage(int currentAccount, boolean inScheduleMode, TLRPC.Message message, TLRPC.Chat chat) {
|
public static boolean canDeleteMessage(int currentAccount, boolean inScheduleMode, TLRPC.Message message, TLRPC.Chat chat) {
|
||||||
|
|
|
@ -78,7 +78,7 @@ public class MessagesController extends BaseController implements NotificationCe
|
||||||
private SparseArray<TLRPC.TL_chatInviteExported> exportedChats = new SparseArray<>();
|
private SparseArray<TLRPC.TL_chatInviteExported> exportedChats = new SparseArray<>();
|
||||||
|
|
||||||
public ArrayList<TLRPC.RecentMeUrl> hintDialogs = new ArrayList<>();
|
public ArrayList<TLRPC.RecentMeUrl> hintDialogs = new ArrayList<>();
|
||||||
private SparseArray<ArrayList<TLRPC.Dialog>> dialogsByFolder = new SparseArray<>();
|
public SparseArray<ArrayList<TLRPC.Dialog>> dialogsByFolder = new SparseArray<>();
|
||||||
protected ArrayList<TLRPC.Dialog> allDialogs = new ArrayList<>();
|
protected ArrayList<TLRPC.Dialog> allDialogs = new ArrayList<>();
|
||||||
public ArrayList<TLRPC.Dialog> dialogsForward = new ArrayList<>();
|
public ArrayList<TLRPC.Dialog> dialogsForward = new ArrayList<>();
|
||||||
public ArrayList<TLRPC.Dialog> dialogsServerOnly = new ArrayList<>();
|
public ArrayList<TLRPC.Dialog> dialogsServerOnly = new ArrayList<>();
|
||||||
|
@ -100,7 +100,7 @@ public class MessagesController extends BaseController implements NotificationCe
|
||||||
public ConcurrentHashMap<Long, ConcurrentHashMap<Integer, ArrayList<PrintingUser>>> printingUsers = new ConcurrentHashMap<>(20, 1.0f, 2);
|
public ConcurrentHashMap<Long, ConcurrentHashMap<Integer, ArrayList<PrintingUser>>> printingUsers = new ConcurrentHashMap<>(20, 1.0f, 2);
|
||||||
public LongSparseArray<SparseArray<CharSequence>> printingStrings = new LongSparseArray<>();
|
public LongSparseArray<SparseArray<CharSequence>> printingStrings = new LongSparseArray<>();
|
||||||
public LongSparseArray<SparseArray<Integer>> printingStringsTypes = new LongSparseArray<>();
|
public LongSparseArray<SparseArray<Integer>> printingStringsTypes = new LongSparseArray<>();
|
||||||
public LongSparseArray<SparseArray<Boolean>>[] sendingTypings = new LongSparseArray[10];
|
public LongSparseArray<SparseArray<Boolean>>[] sendingTypings = new LongSparseArray[11];
|
||||||
public ConcurrentHashMap<Integer, Integer> onlinePrivacy = new ConcurrentHashMap<>(20, 1.0f, 2);
|
public ConcurrentHashMap<Integer, Integer> onlinePrivacy = new ConcurrentHashMap<>(20, 1.0f, 2);
|
||||||
private int lastPrintingStringCount;
|
private int lastPrintingStringCount;
|
||||||
|
|
||||||
|
@ -171,6 +171,8 @@ public class MessagesController extends BaseController implements NotificationCe
|
||||||
|
|
||||||
private SparseIntArray migratedChats = new SparseIntArray();
|
private SparseIntArray migratedChats = new SparseIntArray();
|
||||||
|
|
||||||
|
private LongSparseArray<SponsoredMessagesInfo> sponsoredMessages = new LongSparseArray<>();
|
||||||
|
|
||||||
private HashMap<String, ArrayList<MessageObject>> reloadingWebpages = new HashMap<>();
|
private HashMap<String, ArrayList<MessageObject>> reloadingWebpages = new HashMap<>();
|
||||||
private LongSparseArray<ArrayList<MessageObject>> reloadingWebpagesPending = new LongSparseArray<>();
|
private LongSparseArray<ArrayList<MessageObject>> reloadingWebpagesPending = new LongSparseArray<>();
|
||||||
private HashMap<String, ArrayList<MessageObject>> reloadingScheduledWebpages = new HashMap<>();
|
private HashMap<String, ArrayList<MessageObject>> reloadingScheduledWebpages = new HashMap<>();
|
||||||
|
@ -322,6 +324,12 @@ public class MessagesController extends BaseController implements NotificationCe
|
||||||
|
|
||||||
public volatile boolean ignoreSetOnline;
|
public volatile boolean ignoreSetOnline;
|
||||||
|
|
||||||
|
private class SponsoredMessagesInfo {
|
||||||
|
private ArrayList<MessageObject> messages;
|
||||||
|
private long loadTime;
|
||||||
|
private boolean loading;
|
||||||
|
}
|
||||||
|
|
||||||
public static class FaqSearchResult {
|
public static class FaqSearchResult {
|
||||||
|
|
||||||
public String title;
|
public String title;
|
||||||
|
@ -2287,14 +2295,18 @@ public class MessagesController extends BaseController implements NotificationCe
|
||||||
settings.base_theme = Theme.getBaseThemeByKey(themeInfo.name);
|
settings.base_theme = Theme.getBaseThemeByKey(themeInfo.name);
|
||||||
settings.accent_color = accent.accentColor;
|
settings.accent_color = accent.accentColor;
|
||||||
if (accent.myMessagesAccentColor != 0) {
|
if (accent.myMessagesAccentColor != 0) {
|
||||||
settings.message_bottom_color = accent.myMessagesAccentColor;
|
settings.message_colors.add(accent.myMessagesAccentColor);
|
||||||
settings.flags |= 1;
|
settings.flags |= 1;
|
||||||
}
|
if (accent.myMessagesGradientAccentColor1 != 0) {
|
||||||
if (accent.myMessagesGradientAccentColor != 0) {
|
settings.message_colors.add(accent.myMessagesGradientAccentColor1);
|
||||||
settings.message_top_color = accent.myMessagesGradientAccentColor;
|
if (accent.myMessagesGradientAccentColor2 != 0) {
|
||||||
settings.flags |= 1;
|
settings.message_colors.add(accent.myMessagesGradientAccentColor2);
|
||||||
} else if (settings.message_bottom_color != 0) {
|
if (accent.myMessagesGradientAccentColor3 != 0) {
|
||||||
settings.message_top_color = settings.message_bottom_color;
|
settings.message_colors.add(accent.myMessagesGradientAccentColor3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
settings.message_colors_animated = accent.myMessagesAnimated;
|
||||||
}
|
}
|
||||||
settings.flags |= 2;
|
settings.flags |= 2;
|
||||||
settings.wallpaper_settings = new TLRPC.TL_wallPaperSettings();
|
settings.wallpaper_settings = new TLRPC.TL_wallPaperSettings();
|
||||||
|
@ -2538,6 +2550,7 @@ public class MessagesController extends BaseController implements NotificationCe
|
||||||
reloadingWebpagesPending.clear();
|
reloadingWebpagesPending.clear();
|
||||||
reloadingScheduledWebpages.clear();
|
reloadingScheduledWebpages.clear();
|
||||||
reloadingScheduledWebpagesPending.clear();
|
reloadingScheduledWebpagesPending.clear();
|
||||||
|
sponsoredMessages.clear();
|
||||||
dialogs_dict.clear();
|
dialogs_dict.clear();
|
||||||
dialogs_read_inbox_max.clear();
|
dialogs_read_inbox_max.clear();
|
||||||
loadingPinnedDialogs.clear();
|
loadingPinnedDialogs.clear();
|
||||||
|
@ -3865,7 +3878,7 @@ public class MessagesController extends BaseController implements NotificationCe
|
||||||
if (currentDeletingTaskMedia) {
|
if (currentDeletingTaskMedia) {
|
||||||
getMessagesStorage().emptyMessagesMedia(mids);
|
getMessagesStorage().emptyMessagesMedia(mids);
|
||||||
} else {
|
} else {
|
||||||
deleteMessages(mids, null, null, 0, 0, false, false, !mids.isEmpty() && mids.get(0) > 0);
|
deleteMessages(mids, null, null, 0, 0, true, false, !mids.isEmpty() && mids.get(0) > 0);
|
||||||
}
|
}
|
||||||
Utilities.stageQueue.postRunnable(() -> {
|
Utilities.stageQueue.postRunnable(() -> {
|
||||||
getNewDeleteTask(mids, currentDeletingTaskChannelId, currentDeletingTaskMedia);
|
getNewDeleteTask(mids, currentDeletingTaskChannelId, currentDeletingTaskMedia);
|
||||||
|
@ -5945,6 +5958,13 @@ public class MessagesController extends BaseController implements NotificationCe
|
||||||
newPrintingStrings.put(threadId, LocaleController.getString("SelectingContact", R.string.SelectingContact));
|
newPrintingStrings.put(threadId, LocaleController.getString("SelectingContact", R.string.SelectingContact));
|
||||||
}
|
}
|
||||||
newPrintingStringsTypes.put(threadId, 0);
|
newPrintingStringsTypes.put(threadId, 0);
|
||||||
|
} else if (pu.action instanceof TLRPC.TL_sendMessageChooseStickerAction) {
|
||||||
|
if (lower_id < 0) {
|
||||||
|
newPrintingStrings.put(threadId, LocaleController.formatString("IsChoosingSticker", R.string.IsChoosingSticker, getUserNameForTyping(user)));
|
||||||
|
} else {
|
||||||
|
newPrintingStrings.put(threadId, LocaleController.getString("ChoosingSticker", R.string.ChoosingSticker));
|
||||||
|
}
|
||||||
|
newPrintingStringsTypes.put(threadId, 5);
|
||||||
} else {
|
} else {
|
||||||
if (lower_id < 0) {
|
if (lower_id < 0) {
|
||||||
newPrintingStrings.put(threadId, LocaleController.formatString("IsTypingGroup", R.string.IsTypingGroup, getUserNameForTyping(user)));
|
newPrintingStrings.put(threadId, LocaleController.formatString("IsTypingGroup", R.string.IsTypingGroup, getUserNameForTyping(user)));
|
||||||
|
@ -6084,6 +6104,8 @@ public class MessagesController extends BaseController implements NotificationCe
|
||||||
req.action = new TLRPC.TL_sendMessageUploadRoundAction();
|
req.action = new TLRPC.TL_sendMessageUploadRoundAction();
|
||||||
} else if (action == 9) {
|
} else if (action == 9) {
|
||||||
req.action = new TLRPC.TL_sendMessageUploadAudioAction();
|
req.action = new TLRPC.TL_sendMessageUploadAudioAction();
|
||||||
|
} else if (action == 10) {
|
||||||
|
req.action = new TLRPC.TL_sendMessageChooseStickerAction();
|
||||||
}
|
}
|
||||||
threads.put(threadMsgId, true);
|
threads.put(threadMsgId, true);
|
||||||
int reqId = getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> cancelTyping(action, dialogId, threadMsgId)), ConnectionsManager.RequestFlagFailOnServerErrors);
|
int reqId = getConnectionsManager().sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> cancelTyping(action, dialogId, threadMsgId)), ConnectionsManager.RequestFlagFailOnServerErrors);
|
||||||
|
@ -13753,6 +13775,81 @@ public class MessagesController extends BaseController implements NotificationCe
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ArrayList<MessageObject> getSponsoredMessages(long dialogId) {
|
||||||
|
SponsoredMessagesInfo info = sponsoredMessages.get(dialogId);
|
||||||
|
if (info != null && (info.loading || Math.abs(SystemClock.elapsedRealtime() - info.loadTime) <= 5 * 60 * 1000)) {
|
||||||
|
return info.messages;
|
||||||
|
}
|
||||||
|
TLRPC.Chat chat = getChat((int) -dialogId); //TODO long
|
||||||
|
if (!ChatObject.isChannel(chat) || chat.megagroup || chat.gigagroup) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
info = new SponsoredMessagesInfo();
|
||||||
|
info.loading = true;
|
||||||
|
sponsoredMessages.put(dialogId, info);
|
||||||
|
SponsoredMessagesInfo infoFinal = info;
|
||||||
|
TLRPC.TL_channels_getSponsoredMessages req = new TLRPC.TL_channels_getSponsoredMessages();
|
||||||
|
req.channel = getInputChannel(chat);
|
||||||
|
getConnectionsManager().sendRequest(req, (response, error) -> {
|
||||||
|
ArrayList<MessageObject> result;
|
||||||
|
if (response != null) {
|
||||||
|
TLRPC.TL_messages_sponsoredMessages res = (TLRPC.TL_messages_sponsoredMessages) response;
|
||||||
|
if (res.messages.isEmpty()) {
|
||||||
|
result = null;
|
||||||
|
} else {
|
||||||
|
result = new ArrayList<>();
|
||||||
|
AndroidUtilities.runOnUIThread(() -> {
|
||||||
|
putUsers(res.users, false);
|
||||||
|
putChats(res.chats, false);
|
||||||
|
});
|
||||||
|
final SparseArray<TLRPC.User> usersDict = new SparseArray<>();
|
||||||
|
final SparseArray<TLRPC.Chat> chatsDict = new SparseArray<>();
|
||||||
|
|
||||||
|
for (int a = 0; a < res.users.size(); a++) {
|
||||||
|
TLRPC.User u = res.users.get(a);
|
||||||
|
usersDict.put(u.id, u);
|
||||||
|
}
|
||||||
|
for (int a = 0; a < res.chats.size(); a++) {
|
||||||
|
TLRPC.Chat c = res.chats.get(a);
|
||||||
|
chatsDict.put(c.id, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
int messageId = -10000000;
|
||||||
|
for (int a = 0, N = res.messages.size(); a < N; a++) {
|
||||||
|
TLRPC.TL_sponsoredMessage sponsoredMessage = res.messages.get(a);
|
||||||
|
TLRPC.TL_message message = new TLRPC.TL_message();
|
||||||
|
message.message = sponsoredMessage.message;
|
||||||
|
if (!sponsoredMessage.entities.isEmpty()) {
|
||||||
|
message.entities = sponsoredMessage.entities;
|
||||||
|
message.flags |= 128;
|
||||||
|
}
|
||||||
|
message.peer_id = getPeer((int) dialogId); //TODO long
|
||||||
|
message.from_id = sponsoredMessage.from_id;
|
||||||
|
message.flags |= 256;
|
||||||
|
message.date = getConnectionsManager().getCurrentTime();
|
||||||
|
message.id = messageId--;
|
||||||
|
MessageObject messageObject = new MessageObject(currentAccount, message, usersDict, chatsDict, true, true);
|
||||||
|
messageObject.sponsoredId = sponsoredMessage.random_id;
|
||||||
|
messageObject.botStartParam = sponsoredMessage.start_param;
|
||||||
|
result.add(messageObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
AndroidUtilities.runOnUIThread(() -> {
|
||||||
|
if (result == null) {
|
||||||
|
sponsoredMessages.remove(dialogId);
|
||||||
|
} else {
|
||||||
|
infoFinal.loadTime = SystemClock.elapsedRealtime();
|
||||||
|
infoFinal.messages = result;
|
||||||
|
getNotificationCenter().postNotificationName(NotificationCenter.didLoadSponsoredMessages, dialogId, result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public CharSequence getPrintingString(long dialogId, int threadId, boolean isDialog) {
|
public CharSequence getPrintingString(long dialogId, int threadId, boolean isDialog) {
|
||||||
if (isDialog && (int) dialogId > 0) {
|
if (isDialog && (int) dialogId > 0) {
|
||||||
TLRPC.User user = getUser((int) dialogId);
|
TLRPC.User user = getUser((int) dialogId);
|
||||||
|
|
|
@ -6743,7 +6743,11 @@ public class MessagesStorage extends BaseController {
|
||||||
}
|
}
|
||||||
if (MessageObject.isSecretMedia(message)) {
|
if (MessageObject.isSecretMedia(message)) {
|
||||||
try {
|
try {
|
||||||
SQLiteCursor cursor2 = database.queryFinalized(String.format(Locale.US, "SELECT date FROM enc_tasks_v3 WHERE mid = %d", message.id));
|
long messageId = message.id;
|
||||||
|
if (message.peer_id.channel_id != 0) {
|
||||||
|
messageId |= ((long) message.peer_id.channel_id) << 32;
|
||||||
|
}
|
||||||
|
SQLiteCursor cursor2 = database.queryFinalized(String.format(Locale.US, "SELECT date FROM enc_tasks_v3 WHERE mid = %d AND media = 1", messageId));
|
||||||
if (cursor2.next()) {
|
if (cursor2.next()) {
|
||||||
message.destroyTime = cursor2.intValue(0);
|
message.destroyTime = cursor2.intValue(0);
|
||||||
}
|
}
|
||||||
|
@ -10732,6 +10736,15 @@ public class MessagesStorage extends BaseController {
|
||||||
chatsToLoad.add(-message.ttl);
|
chatsToLoad.add(-message.ttl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (message.params != null) {
|
||||||
|
String peerIdStr = message.params.get("fwd_peer");
|
||||||
|
if (peerIdStr != null) {
|
||||||
|
int peerId = Utilities.parseInt(peerIdStr);
|
||||||
|
if (peerId < 0) {
|
||||||
|
chatsToLoad.add(-peerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getDialogs(final int folderId, final int offset, final int count, final boolean loadDraftsPeersAndFolders) {
|
public void getDialogs(final int folderId, final int offset, final int count, final boolean loadDraftsPeersAndFolders) {
|
||||||
|
|
|
@ -32,6 +32,7 @@ public class NotificationCenter {
|
||||||
public static final int commentsRead = totalEvents++;
|
public static final int commentsRead = totalEvents++;
|
||||||
public static final int changeRepliesCounter = totalEvents++;
|
public static final int changeRepliesCounter = totalEvents++;
|
||||||
public static final int messagesDidLoad = totalEvents++;
|
public static final int messagesDidLoad = totalEvents++;
|
||||||
|
public static final int didLoadSponsoredMessages = totalEvents++;
|
||||||
public static final int messagesDidLoadWithoutProcess = totalEvents++;
|
public static final int messagesDidLoadWithoutProcess = totalEvents++;
|
||||||
public static final int loadingMessagesFailed = totalEvents++;
|
public static final int loadingMessagesFailed = totalEvents++;
|
||||||
public static final int messageReceivedByAck = totalEvents++;
|
public static final int messageReceivedByAck = totalEvents++;
|
||||||
|
@ -188,6 +189,7 @@ public class NotificationCenter {
|
||||||
public static final int didReceiveSmsCode = totalEvents++;
|
public static final int didReceiveSmsCode = totalEvents++;
|
||||||
public static final int didReceiveCall = totalEvents++;
|
public static final int didReceiveCall = totalEvents++;
|
||||||
public static final int emojiLoaded = totalEvents++;
|
public static final int emojiLoaded = totalEvents++;
|
||||||
|
public static final int invalidateMotionBackground = totalEvents++;
|
||||||
public static final int closeOtherAppActivities = totalEvents++;
|
public static final int closeOtherAppActivities = totalEvents++;
|
||||||
public static final int cameraInitied = totalEvents++;
|
public static final int cameraInitied = totalEvents++;
|
||||||
public static final int didReplacedPhotoInMemCache = totalEvents++;
|
public static final int didReplacedPhotoInMemCache = totalEvents++;
|
||||||
|
@ -411,7 +413,7 @@ public class NotificationCenter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void postNotificationName(int id, Object... args) {
|
public void postNotificationName(int id, Object... args) {
|
||||||
boolean allowDuringAnimation = id == startAllHeavyOperations || id == stopAllHeavyOperations || id == didReplacedPhotoInMemCache || id == closeChats;
|
boolean allowDuringAnimation = id == startAllHeavyOperations || id == stopAllHeavyOperations || id == didReplacedPhotoInMemCache || id == closeChats || id == invalidateMotionBackground;
|
||||||
ArrayList<Integer> expiredIndices = null;
|
ArrayList<Integer> expiredIndices = null;
|
||||||
if (!allowDuringAnimation && !allowedNotifications.isEmpty()) {
|
if (!allowDuringAnimation && !allowedNotifications.isEmpty()) {
|
||||||
int size = allowedNotifications.size();
|
int size = allowedNotifications.size();
|
||||||
|
|
|
@ -1448,7 +1448,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
||||||
} else if ((int) did != 0) {
|
} else if ((int) did != 0) {
|
||||||
ArrayList<MessageObject> arrayList = new ArrayList<>();
|
ArrayList<MessageObject> arrayList = new ArrayList<>();
|
||||||
arrayList.add(messageObject);
|
arrayList.add(messageObject);
|
||||||
sendMessage(arrayList, did, true, 0);
|
sendMessage(arrayList, did, true, false, true, 0);
|
||||||
}
|
}
|
||||||
} else if (messageObject.messageOwner.message != null) {
|
} else if (messageObject.messageOwner.message != null) {
|
||||||
TLRPC.WebPage webPage = null;
|
TLRPC.WebPage webPage = null;
|
||||||
|
@ -1475,7 +1475,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
||||||
} else if ((int) did != 0) {
|
} else if ((int) did != 0) {
|
||||||
ArrayList<MessageObject> arrayList = new ArrayList<>();
|
ArrayList<MessageObject> arrayList = new ArrayList<>();
|
||||||
arrayList.add(messageObject);
|
arrayList.add(messageObject);
|
||||||
sendMessage(arrayList, did, true, 0);
|
sendMessage(arrayList, did, true, false, true, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1633,7 +1633,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int sendMessage(ArrayList<MessageObject> messages, final long peer, boolean notify, int scheduleDate) {
|
public int sendMessage(ArrayList<MessageObject> messages, final long peer, boolean forwardFromMyName, boolean hideCaption, boolean notify, int scheduleDate) {
|
||||||
if (messages == null || messages.isEmpty()) {
|
if (messages == null || messages.isEmpty()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1716,68 +1716,74 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
||||||
}
|
}
|
||||||
|
|
||||||
final TLRPC.Message newMsg = new TLRPC.TL_message();
|
final TLRPC.Message newMsg = new TLRPC.TL_message();
|
||||||
boolean forwardFromSaved = msgObj.getDialogId() == myId && msgObj.isFromUser() && msgObj.messageOwner.from_id.user_id == myId;
|
if (!forwardFromMyName) {
|
||||||
if (msgObj.isForwarded()) {
|
boolean forwardFromSaved = msgObj.getDialogId() == myId && msgObj.isFromUser() && msgObj.messageOwner.from_id.user_id == myId;
|
||||||
newMsg.fwd_from = new TLRPC.TL_messageFwdHeader();
|
if (msgObj.isForwarded()) {
|
||||||
if ((msgObj.messageOwner.fwd_from.flags & 1) != 0) {
|
newMsg.fwd_from = new TLRPC.TL_messageFwdHeader();
|
||||||
newMsg.fwd_from.flags |= 1;
|
if ((msgObj.messageOwner.fwd_from.flags & 1) != 0) {
|
||||||
newMsg.fwd_from.from_id = msgObj.messageOwner.fwd_from.from_id;
|
newMsg.fwd_from.flags |= 1;
|
||||||
}
|
newMsg.fwd_from.from_id = msgObj.messageOwner.fwd_from.from_id;
|
||||||
if ((msgObj.messageOwner.fwd_from.flags & 32) != 0) {
|
|
||||||
newMsg.fwd_from.flags |= 32;
|
|
||||||
newMsg.fwd_from.from_name = msgObj.messageOwner.fwd_from.from_name;
|
|
||||||
}
|
|
||||||
if ((msgObj.messageOwner.fwd_from.flags & 4) != 0) {
|
|
||||||
newMsg.fwd_from.flags |= 4;
|
|
||||||
newMsg.fwd_from.channel_post = msgObj.messageOwner.fwd_from.channel_post;
|
|
||||||
}
|
|
||||||
if ((msgObj.messageOwner.fwd_from.flags & 8) != 0) {
|
|
||||||
newMsg.fwd_from.flags |= 8;
|
|
||||||
newMsg.fwd_from.post_author = msgObj.messageOwner.fwd_from.post_author;
|
|
||||||
}
|
|
||||||
if ((peer == myId || isChannel) && (msgObj.messageOwner.fwd_from.flags & 16) != 0 && !UserObject.isReplyUser(msgObj.getDialogId())) {
|
|
||||||
newMsg.fwd_from.flags |= 16;
|
|
||||||
newMsg.fwd_from.saved_from_peer = msgObj.messageOwner.fwd_from.saved_from_peer;
|
|
||||||
newMsg.fwd_from.saved_from_msg_id = msgObj.messageOwner.fwd_from.saved_from_msg_id;
|
|
||||||
}
|
|
||||||
newMsg.fwd_from.date = msgObj.messageOwner.fwd_from.date;
|
|
||||||
newMsg.flags = TLRPC.MESSAGE_FLAG_FWD;
|
|
||||||
} else if (!forwardFromSaved) { //if (!toMyself || !msgObj.isOutOwner())
|
|
||||||
int fromId = msgObj.getFromChatId();
|
|
||||||
newMsg.fwd_from = new TLRPC.TL_messageFwdHeader();
|
|
||||||
newMsg.fwd_from.channel_post = msgObj.getId();
|
|
||||||
newMsg.fwd_from.flags |= 4;
|
|
||||||
if (msgObj.isFromUser()) {
|
|
||||||
newMsg.fwd_from.from_id = msgObj.messageOwner.from_id;
|
|
||||||
newMsg.fwd_from.flags |= 1;
|
|
||||||
} else {
|
|
||||||
newMsg.fwd_from.from_id = new TLRPC.TL_peerChannel();
|
|
||||||
newMsg.fwd_from.from_id.channel_id = msgObj.messageOwner.peer_id.channel_id;
|
|
||||||
newMsg.fwd_from.flags |= 1;
|
|
||||||
if (msgObj.messageOwner.post && fromId > 0) {
|
|
||||||
newMsg.fwd_from.from_id = msgObj.messageOwner.from_id != null ? msgObj.messageOwner.from_id : msgObj.messageOwner.peer_id;
|
|
||||||
}
|
}
|
||||||
}
|
if ((msgObj.messageOwner.fwd_from.flags & 32) != 0) {
|
||||||
if (msgObj.messageOwner.post_author != null) {
|
newMsg.fwd_from.flags |= 32;
|
||||||
|
newMsg.fwd_from.from_name = msgObj.messageOwner.fwd_from.from_name;
|
||||||
|
}
|
||||||
|
if ((msgObj.messageOwner.fwd_from.flags & 4) != 0) {
|
||||||
|
newMsg.fwd_from.flags |= 4;
|
||||||
|
newMsg.fwd_from.channel_post = msgObj.messageOwner.fwd_from.channel_post;
|
||||||
|
}
|
||||||
|
if ((msgObj.messageOwner.fwd_from.flags & 8) != 0) {
|
||||||
|
newMsg.fwd_from.flags |= 8;
|
||||||
|
newMsg.fwd_from.post_author = msgObj.messageOwner.fwd_from.post_author;
|
||||||
|
}
|
||||||
|
if ((peer == myId || isChannel) && (msgObj.messageOwner.fwd_from.flags & 16) != 0 && !UserObject.isReplyUser(msgObj.getDialogId())) {
|
||||||
|
newMsg.fwd_from.flags |= 16;
|
||||||
|
newMsg.fwd_from.saved_from_peer = msgObj.messageOwner.fwd_from.saved_from_peer;
|
||||||
|
newMsg.fwd_from.saved_from_msg_id = msgObj.messageOwner.fwd_from.saved_from_msg_id;
|
||||||
|
}
|
||||||
|
newMsg.fwd_from.date = msgObj.messageOwner.fwd_from.date;
|
||||||
|
newMsg.flags = TLRPC.MESSAGE_FLAG_FWD;
|
||||||
|
} else if (!forwardFromSaved) { //if (!toMyself || !msgObj.isOutOwner())
|
||||||
|
int fromId = msgObj.getFromChatId();
|
||||||
|
newMsg.fwd_from = new TLRPC.TL_messageFwdHeader();
|
||||||
|
newMsg.fwd_from.channel_post = msgObj.getId();
|
||||||
|
newMsg.fwd_from.flags |= 4;
|
||||||
|
if (msgObj.isFromUser()) {
|
||||||
|
newMsg.fwd_from.from_id = msgObj.messageOwner.from_id;
|
||||||
|
newMsg.fwd_from.flags |= 1;
|
||||||
|
} else {
|
||||||
|
newMsg.fwd_from.from_id = new TLRPC.TL_peerChannel();
|
||||||
|
newMsg.fwd_from.from_id.channel_id = msgObj.messageOwner.peer_id.channel_id;
|
||||||
|
newMsg.fwd_from.flags |= 1;
|
||||||
|
if (msgObj.messageOwner.post && fromId > 0) {
|
||||||
|
newMsg.fwd_from.from_id = msgObj.messageOwner.from_id != null ? msgObj.messageOwner.from_id : msgObj.messageOwner.peer_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msgObj.messageOwner.post_author != null) {
|
||||||
/*newMsg.fwd_from.post_author = msgObj.messageOwner.post_author;
|
/*newMsg.fwd_from.post_author = msgObj.messageOwner.post_author;
|
||||||
newMsg.fwd_from.flags |= 8;*/
|
newMsg.fwd_from.flags |= 8;*/
|
||||||
} else if (!msgObj.isOutOwner() && fromId > 0 && msgObj.messageOwner.post) {
|
} else if (!msgObj.isOutOwner() && fromId > 0 && msgObj.messageOwner.post) {
|
||||||
TLRPC.User signUser = getMessagesController().getUser(fromId);
|
TLRPC.User signUser = getMessagesController().getUser(fromId);
|
||||||
if (signUser != null) {
|
if (signUser != null) {
|
||||||
newMsg.fwd_from.post_author = ContactsController.formatName(signUser.first_name, signUser.last_name);
|
newMsg.fwd_from.post_author = ContactsController.formatName(signUser.first_name, signUser.last_name);
|
||||||
newMsg.fwd_from.flags |= 8;
|
newMsg.fwd_from.flags |= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newMsg.date = msgObj.messageOwner.date;
|
||||||
|
newMsg.flags = TLRPC.MESSAGE_FLAG_FWD;
|
||||||
|
}
|
||||||
|
if (peer == myId && newMsg.fwd_from != null) {
|
||||||
|
newMsg.fwd_from.flags |= 16;
|
||||||
|
newMsg.fwd_from.saved_from_msg_id = msgObj.getId();
|
||||||
|
newMsg.fwd_from.saved_from_peer = msgObj.messageOwner.peer_id;
|
||||||
|
if (newMsg.fwd_from.saved_from_peer.user_id == myId) {
|
||||||
|
newMsg.fwd_from.saved_from_peer.user_id = (int) msgObj.getDialogId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
newMsg.date = msgObj.messageOwner.date;
|
} else {
|
||||||
newMsg.flags = TLRPC.MESSAGE_FLAG_FWD;
|
newMsg.params = new HashMap<>();
|
||||||
}
|
newMsg.params.put("fwd_id", "" + msgObj.getId());
|
||||||
if (peer == myId && newMsg.fwd_from != null) {
|
newMsg.params.put("fwd_peer", "" + msgObj.getDialogId());
|
||||||
newMsg.fwd_from.flags |= 16;
|
|
||||||
newMsg.fwd_from.saved_from_msg_id = msgObj.getId();
|
|
||||||
newMsg.fwd_from.saved_from_peer = msgObj.messageOwner.peer_id;
|
|
||||||
if (newMsg.fwd_from.saved_from_peer.user_id == myId) {
|
|
||||||
newMsg.fwd_from.saved_from_peer.user_id = (int) msgObj.getDialogId();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!msgObj.messageOwner.restriction_reason.isEmpty()) {
|
if (!msgObj.messageOwner.restriction_reason.isEmpty()) {
|
||||||
newMsg.restriction_reason = msgObj.messageOwner.restriction_reason;
|
newMsg.restriction_reason = msgObj.messageOwner.restriction_reason;
|
||||||
|
@ -1803,7 +1809,9 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
||||||
|
|
||||||
newMsg.flags |= 8388608;
|
newMsg.flags |= 8388608;
|
||||||
}
|
}
|
||||||
newMsg.message = msgObj.messageOwner.message;
|
if (!hideCaption || newMsg.media == null) {
|
||||||
|
newMsg.message = msgObj.messageOwner.message;
|
||||||
|
}
|
||||||
if (newMsg.message == null) {
|
if (newMsg.message == null) {
|
||||||
newMsg.message = "";
|
newMsg.message = "";
|
||||||
}
|
}
|
||||||
|
@ -1960,6 +1968,8 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
||||||
}
|
}
|
||||||
req.random_id = randomIds;
|
req.random_id = randomIds;
|
||||||
req.id = ids;
|
req.id = ids;
|
||||||
|
req.drop_author = forwardFromMyName;
|
||||||
|
req.drop_media_captions = hideCaption;
|
||||||
req.with_my_score = messages.size() == 1 && messages.get(0).messageOwner.with_my_score;
|
req.with_my_score = messages.size() == 1 && messages.get(0).messageOwner.with_my_score;
|
||||||
|
|
||||||
final ArrayList<TLRPC.Message> newMsgObjArr = arr;
|
final ArrayList<TLRPC.Message> newMsgObjArr = arr;
|
||||||
|
@ -3135,7 +3145,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
||||||
if (parentObject == null && params != null && params.containsKey("parentObject")) {
|
if (parentObject == null && params != null && params.containsKey("parentObject")) {
|
||||||
parentObject = params.get("parentObject");
|
parentObject = params.get("parentObject");
|
||||||
}
|
}
|
||||||
if (retryMessageObject.isForwarded()) {
|
if (retryMessageObject.isForwarded() || params != null && params.containsKey("fwd_id")) {
|
||||||
type = 4;
|
type = 4;
|
||||||
} else {
|
} else {
|
||||||
if (retryMessageObject.isDice()) {
|
if (retryMessageObject.isDice()) {
|
||||||
|
@ -4331,15 +4341,34 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
||||||
TLRPC.TL_messages_forwardMessages reqSend = new TLRPC.TL_messages_forwardMessages();
|
TLRPC.TL_messages_forwardMessages reqSend = new TLRPC.TL_messages_forwardMessages();
|
||||||
reqSend.to_peer = sendToPeer;
|
reqSend.to_peer = sendToPeer;
|
||||||
reqSend.with_my_score = retryMessageObject.messageOwner.with_my_score;
|
reqSend.with_my_score = retryMessageObject.messageOwner.with_my_score;
|
||||||
if (retryMessageObject.messageOwner.ttl != 0) {
|
if (params != null && params.containsKey("fwd_id")) {
|
||||||
TLRPC.Chat chat = getMessagesController().getChat(-retryMessageObject.messageOwner.ttl);
|
int fwdId = Utilities.parseInt(params.get("fwd_id"));
|
||||||
reqSend.from_peer = new TLRPC.TL_inputPeerChannel();
|
reqSend.drop_author = true;
|
||||||
reqSend.from_peer.channel_id = -retryMessageObject.messageOwner.ttl;
|
long peerId = Utilities.parseLong(params.get("fwd_peer"));
|
||||||
if (chat != null) {
|
if (peerId < 0) {
|
||||||
reqSend.from_peer.access_hash = chat.access_hash;
|
TLRPC.Chat chat = getMessagesController().getChat((int) -peerId);
|
||||||
|
if (ChatObject.isChannel(chat)) {
|
||||||
|
reqSend.from_peer = new TLRPC.TL_inputPeerChannel();
|
||||||
|
reqSend.from_peer.channel_id = chat.id;
|
||||||
|
reqSend.from_peer.access_hash = chat.access_hash;
|
||||||
|
} else {
|
||||||
|
reqSend.from_peer = new TLRPC.TL_inputPeerEmpty();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reqSend.from_peer = new TLRPC.TL_inputPeerEmpty();
|
||||||
}
|
}
|
||||||
|
reqSend.id.add(fwdId);
|
||||||
} else {
|
} else {
|
||||||
reqSend.from_peer = new TLRPC.TL_inputPeerEmpty();
|
if (retryMessageObject.messageOwner.ttl != 0) {
|
||||||
|
TLRPC.Chat chat = getMessagesController().getChat(-retryMessageObject.messageOwner.ttl);
|
||||||
|
reqSend.from_peer = new TLRPC.TL_inputPeerChannel();
|
||||||
|
reqSend.from_peer.channel_id = -retryMessageObject.messageOwner.ttl;
|
||||||
|
if (chat != null) {
|
||||||
|
reqSend.from_peer.access_hash = chat.access_hash;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reqSend.from_peer = new TLRPC.TL_inputPeerEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
reqSend.silent = newMsg.silent;
|
reqSend.silent = newMsg.silent;
|
||||||
if (scheduleDate != 0) {
|
if (scheduleDate != 0) {
|
||||||
|
@ -6921,7 +6950,7 @@ public class SendMessagesHelper extends BaseController implements NotificationCe
|
||||||
return String.format(Locale.US, blur ? "%d_%d@%d_%d_b" : "%d_%d@%d_%d", photoSize.location.volume_id, photoSize.location.local_id, (int) (point.x / AndroidUtilities.density), (int) (point.y / AndroidUtilities.density));
|
return String.format(Locale.US, blur ? "%d_%d@%d_%d_b" : "%d_%d@%d_%d", photoSize.location.volume_id, photoSize.location.local_id, (int) (point.x / AndroidUtilities.density), (int) (point.y / AndroidUtilities.density));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean shouldSendWebPAsSticker(String path, Uri uri) {
|
public static boolean shouldSendWebPAsSticker(String path, Uri uri) {
|
||||||
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
|
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
|
||||||
bmOptions.inJustDecodeBounds = true;
|
bmOptions.inJustDecodeBounds = true;
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -38,6 +38,9 @@ public class SharedConfig {
|
||||||
|
|
||||||
public static String pushString = "";
|
public static String pushString = "";
|
||||||
public static String pushStringStatus = "";
|
public static String pushStringStatus = "";
|
||||||
|
public static long pushStringGetTimeStart;
|
||||||
|
public static long pushStringGetTimeEnd;
|
||||||
|
public static boolean pushStatSent;
|
||||||
public static byte[] pushAuthKey;
|
public static byte[] pushAuthKey;
|
||||||
public static byte[] pushAuthKeyId;
|
public static byte[] pushAuthKeyId;
|
||||||
|
|
||||||
|
@ -66,6 +69,7 @@ public class SharedConfig {
|
||||||
public static int textSelectionHintShows;
|
public static int textSelectionHintShows;
|
||||||
public static int scheduledOrNoSoundHintShows;
|
public static int scheduledOrNoSoundHintShows;
|
||||||
public static int lockRecordAudioVideoHint;
|
public static int lockRecordAudioVideoHint;
|
||||||
|
public static boolean forwardingOptionsHintShown;
|
||||||
public static boolean searchMessagesAsListUsed;
|
public static boolean searchMessagesAsListUsed;
|
||||||
public static boolean stickersReorderingHintUsed;
|
public static boolean stickersReorderingHintUsed;
|
||||||
public static boolean disableVoiceAudioEffects;
|
public static boolean disableVoiceAudioEffects;
|
||||||
|
@ -189,6 +193,7 @@ public class SharedConfig {
|
||||||
editor.putBoolean("useFingerprint", useFingerprint);
|
editor.putBoolean("useFingerprint", useFingerprint);
|
||||||
editor.putBoolean("allowScreenCapture", allowScreenCapture);
|
editor.putBoolean("allowScreenCapture", allowScreenCapture);
|
||||||
editor.putString("pushString2", pushString);
|
editor.putString("pushString2", pushString);
|
||||||
|
editor.putBoolean("pushStatSent", pushStatSent);
|
||||||
editor.putString("pushAuthKey", pushAuthKey != null ? Base64.encodeToString(pushAuthKey, Base64.DEFAULT) : "");
|
editor.putString("pushAuthKey", pushAuthKey != null ? Base64.encodeToString(pushAuthKey, Base64.DEFAULT) : "");
|
||||||
editor.putInt("lastLocalId", lastLocalId);
|
editor.putInt("lastLocalId", lastLocalId);
|
||||||
editor.putString("passportConfigJson", passportConfigJson);
|
editor.putString("passportConfigJson", passportConfigJson);
|
||||||
|
@ -197,6 +202,7 @@ public class SharedConfig {
|
||||||
editor.putBoolean("sortFilesByName", sortFilesByName);
|
editor.putBoolean("sortFilesByName", sortFilesByName);
|
||||||
editor.putInt("textSelectionHintShows", textSelectionHintShows);
|
editor.putInt("textSelectionHintShows", textSelectionHintShows);
|
||||||
editor.putInt("scheduledOrNoSoundHintShows", scheduledOrNoSoundHintShows);
|
editor.putInt("scheduledOrNoSoundHintShows", scheduledOrNoSoundHintShows);
|
||||||
|
editor.putBoolean("forwardingOptionsHintShown", forwardingOptionsHintShown);
|
||||||
editor.putInt("lockRecordAudioVideoHint", lockRecordAudioVideoHint);
|
editor.putInt("lockRecordAudioVideoHint", lockRecordAudioVideoHint);
|
||||||
editor.putString("storageCacheDir", !TextUtils.isEmpty(storageCacheDir) ? storageCacheDir : "");
|
editor.putString("storageCacheDir", !TextUtils.isEmpty(storageCacheDir) ? storageCacheDir : "");
|
||||||
|
|
||||||
|
@ -252,6 +258,7 @@ public class SharedConfig {
|
||||||
allowScreenCapture = preferences.getBoolean("allowScreenCapture", false);
|
allowScreenCapture = preferences.getBoolean("allowScreenCapture", false);
|
||||||
lastLocalId = preferences.getInt("lastLocalId", -210000);
|
lastLocalId = preferences.getInt("lastLocalId", -210000);
|
||||||
pushString = preferences.getString("pushString2", "");
|
pushString = preferences.getString("pushString2", "");
|
||||||
|
pushStatSent = preferences.getBoolean("pushStatSent", false);
|
||||||
passportConfigJson = preferences.getString("passportConfigJson", "");
|
passportConfigJson = preferences.getString("passportConfigJson", "");
|
||||||
passportConfigHash = preferences.getInt("passportConfigHash", 0);
|
passportConfigHash = preferences.getInt("passportConfigHash", 0);
|
||||||
storageCacheDir = preferences.getString("storageCacheDir", null);
|
storageCacheDir = preferences.getString("storageCacheDir", null);
|
||||||
|
@ -352,6 +359,7 @@ public class SharedConfig {
|
||||||
stickersReorderingHintUsed = preferences.getBoolean("stickersReorderingHintUsed", false);
|
stickersReorderingHintUsed = preferences.getBoolean("stickersReorderingHintUsed", false);
|
||||||
textSelectionHintShows = preferences.getInt("textSelectionHintShows", 0);
|
textSelectionHintShows = preferences.getInt("textSelectionHintShows", 0);
|
||||||
scheduledOrNoSoundHintShows = preferences.getInt("scheduledOrNoSoundHintShows", 0);
|
scheduledOrNoSoundHintShows = preferences.getInt("scheduledOrNoSoundHintShows", 0);
|
||||||
|
forwardingOptionsHintShown = preferences.getBoolean("forwardingOptionsHintShown", false);
|
||||||
lockRecordAudioVideoHint = preferences.getInt("lockRecordAudioVideoHint", 0);
|
lockRecordAudioVideoHint = preferences.getInt("lockRecordAudioVideoHint", 0);
|
||||||
disableVoiceAudioEffects = preferences.getBoolean("disableVoiceAudioEffects", false);
|
disableVoiceAudioEffects = preferences.getBoolean("disableVoiceAudioEffects", false);
|
||||||
noiseSupression = preferences.getBoolean("noiseSupression", false);
|
noiseSupression = preferences.getBoolean("noiseSupression", false);
|
||||||
|
@ -513,6 +521,7 @@ public class SharedConfig {
|
||||||
textSelectionHintShows = 0;
|
textSelectionHintShows = 0;
|
||||||
scheduledOrNoSoundHintShows = 0;
|
scheduledOrNoSoundHintShows = 0;
|
||||||
lockRecordAudioVideoHint = 0;
|
lockRecordAudioVideoHint = 0;
|
||||||
|
forwardingOptionsHintShown = false;
|
||||||
saveConfig();
|
saveConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,6 +570,14 @@ public class SharedConfig {
|
||||||
editor.commit();
|
editor.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void forwardingOptionsHintHintShowed() {
|
||||||
|
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
|
||||||
|
SharedPreferences.Editor editor = preferences.edit();
|
||||||
|
forwardingOptionsHintShown = true;
|
||||||
|
editor.putBoolean("forwardingOptionsHintShown", forwardingOptionsHintShown);
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
|
||||||
public static void removeScheduledOrNoSuoundHint() {
|
public static void removeScheduledOrNoSuoundHint() {
|
||||||
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
|
SharedPreferences preferences = MessagesController.getGlobalMainSettings();
|
||||||
SharedPreferences.Editor editor = preferences.edit();
|
SharedPreferences.Editor editor = preferences.edit();
|
||||||
|
|
|
@ -122,6 +122,8 @@ public class SvgHelper {
|
||||||
private float colorAlpha;
|
private float colorAlpha;
|
||||||
private float crossfadeAlpha = 1.0f;
|
private float crossfadeAlpha = 1.0f;
|
||||||
|
|
||||||
|
private boolean aspectFill = true;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getIntrinsicHeight() {
|
public int getIntrinsicHeight() {
|
||||||
return width;
|
return width;
|
||||||
|
@ -132,6 +134,16 @@ public class SvgHelper {
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAspectFill(boolean value) {
|
||||||
|
aspectFill = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void overrideWidthAndHeight(int w, int h) {
|
||||||
|
width = w;
|
||||||
|
height = h;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Canvas canvas) {
|
public void draw(Canvas canvas) {
|
||||||
if (currentColorKey != null) {
|
if (currentColorKey != null) {
|
||||||
|
@ -140,9 +152,12 @@ public class SvgHelper {
|
||||||
Rect bounds = getBounds();
|
Rect bounds = getBounds();
|
||||||
float scaleX = bounds.width() / (float) width;
|
float scaleX = bounds.width() / (float) width;
|
||||||
float scaleY = bounds.height() / (float) height;
|
float scaleY = bounds.height() / (float) height;
|
||||||
float scale = Math.max(scaleX, scaleY);
|
float scale = aspectFill ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY);
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.translate(bounds.left, bounds.top);
|
canvas.translate(bounds.left, bounds.top);
|
||||||
|
if (!aspectFill) {
|
||||||
|
canvas.translate((bounds.width() - width * scale) / 2, (bounds.height() - height * scale) / 2);
|
||||||
|
}
|
||||||
canvas.scale(scale, scale);
|
canvas.scale(scale, scale);
|
||||||
for (int a = 0, N = commands.size(); a < N; a++) {
|
for (int a = 0, N = commands.size(); a < N; a++) {
|
||||||
Object object = commands.get(a);
|
Object object = commands.get(a);
|
||||||
|
@ -1719,7 +1734,7 @@ public class SvgHelper {
|
||||||
int num = encoded[i] & 0xff;
|
int num = encoded[i] & 0xff;
|
||||||
if (num >= 128 + 64) {
|
if (num >= 128 + 64) {
|
||||||
int start = num - 128 - 64;
|
int start = num - 128 - 64;
|
||||||
path.append("AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,".substring(start, start + 1));
|
path.append("AACAAAAHAAALMAAAQASTAVAAAZaacaaaahaaalmaaaqastava.az0123456789-,".charAt(start));
|
||||||
} else {
|
} else {
|
||||||
if (num >= 128) {
|
if (num >= 128) {
|
||||||
path.append(',');
|
path.append(',');
|
||||||
|
|
|
@ -103,10 +103,6 @@ public class UserConfig extends BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveConfig(boolean withFile) {
|
public void saveConfig(boolean withFile) {
|
||||||
saveConfig(withFile, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveConfig(boolean withFile, File oldFile) {
|
|
||||||
NotificationCenter.getInstance(currentAccount).doOnIdle(() -> {
|
NotificationCenter.getInstance(currentAccount).doOnIdle(() -> {
|
||||||
synchronized (sync) {
|
synchronized (sync) {
|
||||||
try {
|
try {
|
||||||
|
@ -184,9 +180,6 @@ public class UserConfig extends BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.commit();
|
editor.commit();
|
||||||
if (oldFile != null) {
|
|
||||||
oldFile.delete();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FileLog.e(e);
|
FileLog.e(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class NativeInstance {
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface RequestBroadcastPartCallback {
|
public interface RequestBroadcastPartCallback {
|
||||||
void run(long timestamp, long duration);
|
void run(long timestamp, long duration, int videoChannel, int quality);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NativeInstance make(String version, Instance.Config config, String path, Instance.Endpoint[] endpoints, Instance.Proxy proxy, int networkType, Instance.EncryptionKey encryptionKey, VideoSink remoteSink, long videoCapturer, AudioLevelsCallback audioLevelsCallback) {
|
public static NativeInstance make(String version, Instance.Config config, String path, Instance.Endpoint[] endpoints, Instance.Proxy proxy, int networkType, Instance.EncryptionKey encryptionKey, VideoSink remoteSink, long videoCapturer, AudioLevelsCallback audioLevelsCallback) {
|
||||||
|
@ -152,12 +152,12 @@ public class NativeInstance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onRequestBroadcastPart(long timestamp, long duration) {
|
private void onRequestBroadcastPart(long timestamp, long duration, int videoChannel, int quality) {
|
||||||
requestBroadcastPartCallback.run(timestamp, duration);
|
requestBroadcastPartCallback.run(timestamp, duration, videoChannel, quality);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onCancelRequestBroadcastPart(long timestamp) {
|
private void onCancelRequestBroadcastPart(long timestamp, int videoChannel, int quality) {
|
||||||
cancelRequestBroadcastPartCallback.run(timestamp, 0);
|
cancelRequestBroadcastPartCallback.run(timestamp, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public native void setJoinResponsePayload(String payload);
|
public native void setJoinResponsePayload(String payload);
|
||||||
|
@ -222,6 +222,6 @@ public class NativeInstance {
|
||||||
public native void switchCamera(boolean front);
|
public native void switchCamera(boolean front);
|
||||||
public native void setVideoState(int videoState);
|
public native void setVideoState(int videoState);
|
||||||
public native void onSignalingDataReceive(byte[] data);
|
public native void onSignalingDataReceive(byte[] data);
|
||||||
public native void onStreamPartAvailable(long ts, ByteBuffer buffer, int size, long timestamp);
|
public native void onStreamPartAvailable(long ts, ByteBuffer buffer, int size, long timestamp, int videoChannel, int quality);
|
||||||
public native boolean hasVideoCapturer();
|
public native boolean hasVideoCapturer();
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,8 +187,6 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
||||||
|
|
||||||
private boolean reconnectScreenCapture;
|
private boolean reconnectScreenCapture;
|
||||||
|
|
||||||
private int currentStreamRequestId;
|
|
||||||
|
|
||||||
private TLRPC.Chat chat;
|
private TLRPC.Chat chat;
|
||||||
|
|
||||||
private boolean isVideoAvailable;
|
private boolean isVideoAvailable;
|
||||||
|
@ -306,7 +304,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
||||||
|
|
||||||
private int classGuid;
|
private int classGuid;
|
||||||
|
|
||||||
private long currentStreamRequestTimestamp;
|
private HashMap<String, Integer> currentStreamRequestTimestamp = new HashMap<>();
|
||||||
public boolean micSwitching;
|
public boolean micSwitching;
|
||||||
|
|
||||||
private Runnable afterSoundRunnable = new Runnable() {
|
private Runnable afterSoundRunnable = new Runnable() {
|
||||||
|
@ -1272,32 +1270,15 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestFullScreen(TLRPC.TL_groupCallParticipant participant, boolean screencast) {
|
public void requestFullScreen(TLRPC.TL_groupCallParticipant participant, boolean full, boolean screencast) {
|
||||||
if (currentBackgroundSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] != null) {
|
|
||||||
currentBackgroundSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA].setBackground(null);
|
|
||||||
}
|
|
||||||
if (participant == null) {
|
|
||||||
currentBackgroundSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = null;
|
|
||||||
currentBackgroundEndpointId[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String endpointId = screencast ? participant.presentationEndpoint : participant.videoEndpoint;
|
String endpointId = screencast ? participant.presentationEndpoint : participant.videoEndpoint;
|
||||||
if (endpointId == null) {
|
if (endpointId == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ProxyVideoSink sink = remoteSinks.get(endpointId);
|
if (full) {
|
||||||
if (sink == null) {
|
tgVoip[CAPTURE_DEVICE_CAMERA].setVideoEndpointQuality(endpointId, QUALITY_FULL);
|
||||||
sink = addRemoteSink(participant, screencast, null, null);
|
|
||||||
}
|
|
||||||
if (sink != null) {
|
|
||||||
sink.setBackground(remoteSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA]);
|
|
||||||
//tgVoip[CAPTURE_DEVICE_CAMERA].setVideoEndpointQuality(endpointId, QUALITY_FULL); TODO
|
|
||||||
currentBackgroundSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = sink;
|
|
||||||
currentBackgroundEndpointId[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = endpointId;
|
|
||||||
} else {
|
} else {
|
||||||
//tgVoip[CAPTURE_DEVICE_CAMERA].setVideoEndpointQuality(endpointId, QUALITY_MEDIUM); TODO
|
tgVoip[CAPTURE_DEVICE_CAMERA].setVideoEndpointQuality(endpointId, QUALITY_MEDIUM);
|
||||||
currentBackgroundSink[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = null;
|
|
||||||
currentBackgroundEndpointId[screencast ? CAPTURE_DEVICE_SCREEN : CAPTURE_DEVICE_CAMERA] = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2084,7 +2065,7 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
||||||
}
|
}
|
||||||
broadcastUnknownParticipants(taskPtr, unknown);
|
broadcastUnknownParticipants(taskPtr, unknown);
|
||||||
});
|
});
|
||||||
}, (timestamp, duration) -> {
|
}, (timestamp, duration, videoChannel, quality) -> {
|
||||||
if (type != CAPTURE_DEVICE_CAMERA) {
|
if (type != CAPTURE_DEVICE_CAMERA) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2096,15 +2077,21 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
||||||
if (duration == 500) {
|
if (duration == 500) {
|
||||||
inputGroupCallStream.scale = 1;
|
inputGroupCallStream.scale = 1;
|
||||||
}
|
}
|
||||||
|
if (videoChannel != 0) {
|
||||||
|
inputGroupCallStream.flags |= 1;
|
||||||
|
inputGroupCallStream.video_channel = videoChannel;
|
||||||
|
inputGroupCallStream.video_quality = quality;
|
||||||
|
}
|
||||||
req.location = inputGroupCallStream;
|
req.location = inputGroupCallStream;
|
||||||
currentStreamRequestTimestamp = timestamp;
|
String key = videoChannel == 0 ? ("" + timestamp) : (videoChannel + "_" + timestamp + "_" + quality);
|
||||||
currentStreamRequestId = AccountInstance.getInstance(currentAccount).getConnectionsManager().sendRequest(req, (response, error, responseTime) -> {
|
int reqId = AccountInstance.getInstance(currentAccount).getConnectionsManager().sendRequest(req, (response, error, responseTime) -> {
|
||||||
|
AndroidUtilities.runOnUIThread(() -> currentStreamRequestTimestamp.remove(key));
|
||||||
if (tgVoip[type] == null) {
|
if (tgVoip[type] == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
TLRPC.TL_upload_file res = (TLRPC.TL_upload_file) response;
|
TLRPC.TL_upload_file res = (TLRPC.TL_upload_file) response;
|
||||||
tgVoip[type].onStreamPartAvailable(timestamp, res.bytes.buffer, res.bytes.limit(), responseTime);
|
tgVoip[type].onStreamPartAvailable(timestamp, res.bytes.buffer, res.bytes.limit(), responseTime, videoChannel, quality);
|
||||||
} else {
|
} else {
|
||||||
if ("GROUPCALL_JOIN_MISSING".equals(error.text)) {
|
if ("GROUPCALL_JOIN_MISSING".equals(error.text)) {
|
||||||
AndroidUtilities.runOnUIThread(() -> createGroupInstance(type, false));
|
AndroidUtilities.runOnUIThread(() -> createGroupInstance(type, false));
|
||||||
|
@ -2115,18 +2102,23 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
||||||
} else {
|
} else {
|
||||||
status = -1;
|
status = -1;
|
||||||
}
|
}
|
||||||
tgVoip[type].onStreamPartAvailable(timestamp, null, status, responseTime);
|
tgVoip[type].onStreamPartAvailable(timestamp, null, status, responseTime, videoChannel, quality);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, ConnectionsManager.RequestFlagFailOnServerErrors, ConnectionsManager.ConnectionTypeDownload, groupCall.call.stream_dc_id);
|
}, ConnectionsManager.RequestFlagFailOnServerErrors, ConnectionsManager.ConnectionTypeDownload, groupCall.call.stream_dc_id);
|
||||||
}, (timestamp, duration) -> {
|
AndroidUtilities.runOnUIThread(() -> currentStreamRequestTimestamp.put(key, reqId));
|
||||||
|
}, (timestamp, duration, videoChannel, quality) -> {
|
||||||
if (type != CAPTURE_DEVICE_CAMERA) {
|
if (type != CAPTURE_DEVICE_CAMERA) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (currentStreamRequestTimestamp == timestamp) {
|
AndroidUtilities.runOnUIThread(() -> {
|
||||||
AccountInstance.getInstance(currentAccount).getConnectionsManager().cancelRequest(currentStreamRequestId, true);
|
String key = videoChannel == 0 ? ("" + timestamp) : (videoChannel + "_" + timestamp + "_" + quality);
|
||||||
currentStreamRequestId = 0;
|
Integer reqId = currentStreamRequestTimestamp.get(key);
|
||||||
}
|
if (reqId != null) {
|
||||||
|
AccountInstance.getInstance(currentAccount).getConnectionsManager().cancelRequest(reqId, true);
|
||||||
|
currentStreamRequestTimestamp.remove(key);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
tgVoip[type].setOnStateUpdatedListener((state, inTransition) -> updateConnectionState(type, state, inTransition));
|
tgVoip[type].setOnStateUpdatedListener((state, inTransition) -> updateConnectionState(type, state, inTransition));
|
||||||
}
|
}
|
||||||
|
@ -2819,18 +2811,23 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
||||||
intent.putExtra("currentAccount", currentAccount);
|
intent.putExtra("currentAccount", currentAccount);
|
||||||
}
|
}
|
||||||
Notification.Builder builder = new Notification.Builder(this)
|
Notification.Builder builder = new Notification.Builder(this)
|
||||||
.setContentTitle(groupCall != null ? LocaleController.getString("VoipVoiceChat", R.string.VoipVoiceChat) : LocaleController.getString("VoipOutgoingCall", R.string.VoipOutgoingCall))
|
|
||||||
.setContentText(name)
|
.setContentText(name)
|
||||||
.setContentIntent(PendingIntent.getActivity(this, 50, intent, 0));
|
.setContentIntent(PendingIntent.getActivity(this, 50, intent, 0));
|
||||||
if (groupCall != null) {
|
if (groupCall != null) {
|
||||||
|
builder.setContentTitle(ChatObject.isChannelOrGiga(chat) ? LocaleController.getString("VoipLiveStream", R.string.VoipLiveStream) : LocaleController.getString("VoipVoiceChat", R.string.VoipVoiceChat));
|
||||||
builder.setSmallIcon(isMicMute() ? R.drawable.voicechat_muted : R.drawable.voicechat_active);
|
builder.setSmallIcon(isMicMute() ? R.drawable.voicechat_muted : R.drawable.voicechat_active);
|
||||||
} else {
|
} else {
|
||||||
|
builder.setContentTitle(LocaleController.getString("VoipOutgoingCall", R.string.VoipOutgoingCall));
|
||||||
builder.setSmallIcon(R.drawable.notification);
|
builder.setSmallIcon(R.drawable.notification);
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
Intent endIntent = new Intent(this, VoIPActionsReceiver.class);
|
Intent endIntent = new Intent(this, VoIPActionsReceiver.class);
|
||||||
endIntent.setAction(getPackageName() + ".END_CALL");
|
endIntent.setAction(getPackageName() + ".END_CALL");
|
||||||
builder.addAction(R.drawable.ic_call_end_white_24dp, groupCall != null ? LocaleController.getString("VoipGroupLeaveAlertTitle", R.string.VoipGroupLeaveAlertTitle) : LocaleController.getString("VoipEndCall", R.string.VoipEndCall), PendingIntent.getBroadcast(this, 0, endIntent, PendingIntent.FLAG_UPDATE_CURRENT));
|
if (groupCall != null) {
|
||||||
|
builder.addAction(R.drawable.ic_call_end_white_24dp, ChatObject.isChannelOrGiga(chat) ? LocaleController.getString("VoipChannelLeaveAlertTitle", R.string.VoipChannelLeaveAlertTitle) : LocaleController.getString("VoipGroupLeaveAlertTitle", R.string.VoipGroupLeaveAlertTitle), PendingIntent.getBroadcast(this, 0, endIntent, PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
|
} else {
|
||||||
|
builder.addAction(R.drawable.ic_call_end_white_24dp, LocaleController.getString("VoipEndCall", R.string.VoipEndCall), PendingIntent.getBroadcast(this, 0, endIntent, PendingIntent.FLAG_UPDATE_CURRENT));
|
||||||
|
}
|
||||||
builder.setPriority(Notification.PRIORITY_MAX);
|
builder.setPriority(Notification.PRIORITY_MAX);
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
@ -2954,8 +2951,10 @@ public class VoIPService extends Service implements SensorEventListener, AudioMa
|
||||||
if (tgVoip[CAPTURE_DEVICE_CAMERA].isGroup()) {
|
if (tgVoip[CAPTURE_DEVICE_CAMERA].isGroup()) {
|
||||||
NativeInstance instance = tgVoip[CAPTURE_DEVICE_CAMERA];
|
NativeInstance instance = tgVoip[CAPTURE_DEVICE_CAMERA];
|
||||||
Utilities.globalQueue.postRunnable(instance::stopGroup);
|
Utilities.globalQueue.postRunnable(instance::stopGroup);
|
||||||
AccountInstance.getInstance(currentAccount).getConnectionsManager().cancelRequest(currentStreamRequestId, true);
|
for (HashMap.Entry<String, Integer> entry : currentStreamRequestTimestamp.entrySet()) {
|
||||||
currentStreamRequestId = 0;
|
AccountInstance.getInstance(currentAccount).getConnectionsManager().cancelRequest(entry.getValue(), true);
|
||||||
|
}
|
||||||
|
currentStreamRequestTimestamp.clear();
|
||||||
} else {
|
} else {
|
||||||
Instance.FinalState state = tgVoip[CAPTURE_DEVICE_CAMERA].stop();
|
Instance.FinalState state = tgVoip[CAPTURE_DEVICE_CAMERA].stop();
|
||||||
updateTrafficStats(tgVoip[CAPTURE_DEVICE_CAMERA], state.trafficStats);
|
updateTrafficStats(tgVoip[CAPTURE_DEVICE_CAMERA], state.trafficStats);
|
||||||
|
|
|
@ -201,10 +201,7 @@ public class ConnectionsManager extends BaseController {
|
||||||
systemVersion = "SDK Unknown";
|
systemVersion = "SDK Unknown";
|
||||||
}
|
}
|
||||||
getUserConfig().loadConfig();
|
getUserConfig().loadConfig();
|
||||||
String pushString = SharedConfig.pushString;
|
String pushString = getRegId();
|
||||||
if (TextUtils.isEmpty(pushString) && !TextUtils.isEmpty(SharedConfig.pushStringStatus)) {
|
|
||||||
pushString = SharedConfig.pushStringStatus;
|
|
||||||
}
|
|
||||||
String fingerprint = AndroidUtilities.getCertificateSHA256Fingerprint();
|
String fingerprint = AndroidUtilities.getCertificateSHA256Fingerprint();
|
||||||
|
|
||||||
int timezoneOffset = (TimeZone.getDefault().getRawOffset() + TimeZone.getDefault().getDSTSavings()) / 1000;
|
int timezoneOffset = (TimeZone.getDefault().getRawOffset() + TimeZone.getDefault().getDSTSavings()) / 1000;
|
||||||
|
@ -212,6 +209,17 @@ public class ConnectionsManager extends BaseController {
|
||||||
init(BuildVars.BUILD_VERSION, TLRPC.LAYER, BuildVars.APP_ID, deviceModel, systemVersion, appVersion, langCode, systemLangCode, configPath, FileLog.getNetworkLogPath(), pushString, fingerprint, timezoneOffset, getUserConfig().getClientUserId(), enablePushConnection);
|
init(BuildVars.BUILD_VERSION, TLRPC.LAYER, BuildVars.APP_ID, deviceModel, systemVersion, appVersion, langCode, systemLangCode, configPath, FileLog.getNetworkLogPath(), pushString, fingerprint, timezoneOffset, getUserConfig().getClientUserId(), enablePushConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getRegId() {
|
||||||
|
String pushString = SharedConfig.pushString;
|
||||||
|
if (TextUtils.isEmpty(pushString) && !TextUtils.isEmpty(SharedConfig.pushStringStatus)) {
|
||||||
|
pushString = SharedConfig.pushStringStatus;
|
||||||
|
}
|
||||||
|
if (TextUtils.isEmpty(pushString)) {
|
||||||
|
pushString = SharedConfig.pushStringStatus = "__FIREBASE_GENERATING_SINCE_" + getCurrentTime() + "__";
|
||||||
|
}
|
||||||
|
return pushString;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isPushConnectionEnabled() {
|
public boolean isPushConnectionEnabled() {
|
||||||
SharedPreferences preferences = MessagesController.getGlobalNotificationsSettings();
|
SharedPreferences preferences = MessagesController.getGlobalNotificationsSettings();
|
||||||
if (preferences.contains("pushConnection")) {
|
if (preferences.contains("pushConnection")) {
|
||||||
|
@ -402,6 +410,9 @@ public class ConnectionsManager extends BaseController {
|
||||||
if (TextUtils.isEmpty(pushString) && !TextUtils.isEmpty(status)) {
|
if (TextUtils.isEmpty(pushString) && !TextUtils.isEmpty(status)) {
|
||||||
pushString = status;
|
pushString = status;
|
||||||
}
|
}
|
||||||
|
if (TextUtils.isEmpty(pushString)) {
|
||||||
|
pushString = SharedConfig.pushStringStatus = "__FIREBASE_GENERATING_SINCE_" + getInstance(0).getCurrentTime() + "__";
|
||||||
|
}
|
||||||
for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) {
|
for (int a = 0; a < UserConfig.MAX_ACCOUNT_COUNT; a++) {
|
||||||
native_setRegId(a, pushString);
|
native_setRegId(a, pushString);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -40,6 +40,8 @@ import android.view.animation.AccelerateDecelerateInterpolator;
|
||||||
import android.view.animation.DecelerateInterpolator;
|
import android.view.animation.DecelerateInterpolator;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.util.Log;
|
||||||
|
|
||||||
import org.telegram.messenger.AndroidUtilities;
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
import org.telegram.messenger.ApplicationLoader;
|
import org.telegram.messenger.ApplicationLoader;
|
||||||
import org.telegram.messenger.FileLog;
|
import org.telegram.messenger.FileLog;
|
||||||
|
@ -207,6 +209,12 @@ public class ActionBarLayout extends FrameLayout {
|
||||||
this.fragmentPanTranslationOffset = fragmentPanTranslationOffset;
|
this.fragmentPanTranslationOffset = fragmentPanTranslationOffset;
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTranslationX(float translationX) {
|
||||||
|
Log.d("kek", "set translationX" + translationX);
|
||||||
|
super.setTranslationX(translationX);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Drawable headerShadowDrawable;
|
private static Drawable headerShadowDrawable;
|
||||||
|
@ -982,7 +990,7 @@ public class ActionBarLayout extends FrameLayout {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
fragment.setInPreviewMode(preview);
|
fragment.setInPreviewMode(preview);
|
||||||
if (parentActivity.getCurrentFocus() != null && fragment.hideKeyboardOnShow()) {
|
if (parentActivity.getCurrentFocus() != null && fragment.hideKeyboardOnShow() && !preview) {
|
||||||
AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus());
|
AndroidUtilities.hideKeyboard(parentActivity.getCurrentFocus());
|
||||||
}
|
}
|
||||||
boolean needAnimation = preview || !forceWithoutAnimation && MessagesController.getGlobalMainSettings().getBoolean("view_animations", true);
|
boolean needAnimation = preview || !forceWithoutAnimation && MessagesController.getGlobalMainSettings().getBoolean("view_animations", true);
|
||||||
|
@ -1158,7 +1166,7 @@ public class ActionBarLayout extends FrameLayout {
|
||||||
containerView.setScaleY(1.0f);
|
containerView.setScaleY(1.0f);
|
||||||
}
|
}
|
||||||
if (containerView.isKeyboardVisible || containerViewBack.isKeyboardVisible) {
|
if (containerView.isKeyboardVisible || containerViewBack.isKeyboardVisible) {
|
||||||
if (currentFragment != null) {
|
if (currentFragment != null && !preview) {
|
||||||
currentFragment.saveKeyboardPositionBeforeTransition();
|
currentFragment.saveKeyboardPositionBeforeTransition();
|
||||||
}
|
}
|
||||||
waitingForKeyboardCloseRunnable = new Runnable() {
|
waitingForKeyboardCloseRunnable = new Runnable() {
|
||||||
|
@ -1218,7 +1226,7 @@ public class ActionBarLayout extends FrameLayout {
|
||||||
startLayoutAnimation(true, true, preview);
|
startLayoutAnimation(true, true, preview);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (containerView.isKeyboardVisible || containerViewBack.isKeyboardVisible && currentFragment != null) {
|
if (!preview && containerView.isKeyboardVisible || containerViewBack.isKeyboardVisible && currentFragment != null) {
|
||||||
currentFragment.saveKeyboardPositionBeforeTransition();
|
currentFragment.saveKeyboardPositionBeforeTransition();
|
||||||
}
|
}
|
||||||
currentAnimation = animation;
|
currentAnimation = animation;
|
||||||
|
@ -1443,7 +1451,7 @@ public class ActionBarLayout extends FrameLayout {
|
||||||
animation = currentFragment.onCustomTransitionAnimation(false, () -> onAnimationEndCheck(false));
|
animation = currentFragment.onCustomTransitionAnimation(false, () -> onAnimationEndCheck(false));
|
||||||
}
|
}
|
||||||
if (animation == null) {
|
if (animation == null) {
|
||||||
if (containerView.isKeyboardVisible || containerViewBack.isKeyboardVisible) {
|
if (!inPreviewMode && (containerView.isKeyboardVisible || containerViewBack.isKeyboardVisible)) {
|
||||||
waitingForKeyboardCloseRunnable = new Runnable() {
|
waitingForKeyboardCloseRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
|
@ -107,6 +107,12 @@ public class ActionBarMenuSubItem extends FrameLayout {
|
||||||
setTextAndIcon(text, icon, null);
|
setTextAndIcon(text, icon, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setMultiline() {
|
||||||
|
textView.setLines(2);
|
||||||
|
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||||
|
textView.setSingleLine(false);
|
||||||
|
}
|
||||||
|
|
||||||
public void setTextAndIcon(CharSequence text, int icon, Drawable iconDrawable) {
|
public void setTextAndIcon(CharSequence text, int icon, Drawable iconDrawable) {
|
||||||
textView.setText(text);
|
textView.setText(text);
|
||||||
if (icon != 0 || iconDrawable != null || checkView != null) {
|
if (icon != 0 || iconDrawable != null || checkView != null) {
|
||||||
|
|
|
@ -92,8 +92,8 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
private boolean animationEnabled = allowAnimation;
|
private boolean animationEnabled = allowAnimation;
|
||||||
private ArrayList<AnimatorSet> itemAnimators;
|
private ArrayList<AnimatorSet> itemAnimators;
|
||||||
private HashMap<View, Integer> positions = new HashMap<>();
|
private HashMap<View, Integer> positions = new HashMap<>();
|
||||||
private int gapStartY = Integer.MIN_VALUE;
|
private int gapStartY = -1000000;
|
||||||
private int gapEndY = Integer.MIN_VALUE;
|
private int gapEndY = -1000000;
|
||||||
private Rect bgPaddings = new Rect();
|
private Rect bgPaddings = new Rect();
|
||||||
|
|
||||||
private ScrollView scrollView;
|
private ScrollView scrollView;
|
||||||
|
@ -105,9 +105,13 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
private boolean fitItems;
|
private boolean fitItems;
|
||||||
|
|
||||||
public ActionBarPopupWindowLayout(Context context) {
|
public ActionBarPopupWindowLayout(Context context) {
|
||||||
|
this(context, R.drawable.popup_fixed_alert2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActionBarPopupWindowLayout(Context context, int resId) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
backgroundDrawable = getResources().getDrawable(R.drawable.popup_fixed_alert2).mutate();
|
backgroundDrawable = getResources().getDrawable(resId).mutate();
|
||||||
if (backgroundDrawable != null) {
|
if (backgroundDrawable != null) {
|
||||||
backgroundDrawable.getPadding(bgPaddings);
|
backgroundDrawable.getPadding(bgPaddings);
|
||||||
}
|
}
|
||||||
|
@ -130,8 +134,8 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
if (fitItems) {
|
if (fitItems) {
|
||||||
int maxWidth = 0;
|
int maxWidth = 0;
|
||||||
int fixWidth = 0;
|
int fixWidth = 0;
|
||||||
gapStartY = Integer.MIN_VALUE;
|
gapStartY = -1000000;
|
||||||
gapEndY = Integer.MIN_VALUE;
|
gapEndY = -1000000;
|
||||||
ArrayList<View> viewsToFix = null;
|
ArrayList<View> viewsToFix = null;
|
||||||
for (int a = 0, N = getChildCount(); a < N; a++) {
|
for (int a = 0, N = getChildCount(); a < N; a++) {
|
||||||
View view = getChildAt(a);
|
View view = getChildAt(a);
|
||||||
|
@ -326,7 +330,7 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
if (a == 1 && start < -AndroidUtilities.dp(16)) {
|
if (a == 1 && start < -AndroidUtilities.dp(16)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (gapStartY != Integer.MIN_VALUE) {
|
if (gapStartY != -1000000) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.clipRect(0, bgPaddings.top, getMeasuredWidth(), getMeasuredHeight());
|
canvas.clipRect(0, bgPaddings.top, getMeasuredWidth(), getMeasuredHeight());
|
||||||
}
|
}
|
||||||
|
@ -338,10 +342,10 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
if (start > -AndroidUtilities.dp(16)) {
|
if (start > -AndroidUtilities.dp(16)) {
|
||||||
int h = (int) (getMeasuredHeight() * backScaleY);
|
int h = (int) (getMeasuredHeight() * backScaleY);
|
||||||
if (a == 0) {
|
if (a == 0) {
|
||||||
backgroundDrawable.setBounds(0, -scrollView.getScrollY(), (int) (getMeasuredWidth() * backScaleX), Math.min(h, start + AndroidUtilities.dp(16)));
|
backgroundDrawable.setBounds(0, -scrollView.getScrollY() + (gapStartY != -1000000 ? AndroidUtilities.dp(1) : 0), (int) (getMeasuredWidth() * backScaleX), (gapStartY != -1000000 ? Math.min(h, start + AndroidUtilities.dp(16)) : h));
|
||||||
} else {
|
} else {
|
||||||
if (h < end) {
|
if (h < end) {
|
||||||
if (gapStartY != Integer.MIN_VALUE) {
|
if (gapStartY != -1000000) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -353,7 +357,7 @@ public class ActionBarPopupWindow extends PopupWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
backgroundDrawable.draw(canvas);
|
backgroundDrawable.draw(canvas);
|
||||||
if (gapStartY != Integer.MIN_VALUE) {
|
if (gapStartY != -1000000) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import android.view.View;
|
||||||
import android.view.accessibility.AccessibilityNodeInfo;
|
import android.view.accessibility.AccessibilityNodeInfo;
|
||||||
|
|
||||||
import org.telegram.messenger.AndroidUtilities;
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
|
import org.telegram.ui.Cells.DialogCell;
|
||||||
import org.telegram.ui.Components.EmptyStubSpan;
|
import org.telegram.ui.Components.EmptyStubSpan;
|
||||||
import org.telegram.ui.Components.StaticLayoutEx;
|
import org.telegram.ui.Components.StaticLayoutEx;
|
||||||
|
|
||||||
|
@ -44,6 +45,10 @@ public class SimpleTextView extends View implements Drawable.Callback {
|
||||||
private SpannableStringBuilder spannableStringBuilder;
|
private SpannableStringBuilder spannableStringBuilder;
|
||||||
private Drawable leftDrawable;
|
private Drawable leftDrawable;
|
||||||
private Drawable rightDrawable;
|
private Drawable rightDrawable;
|
||||||
|
private Drawable replacedDrawable;
|
||||||
|
private String replacedText;
|
||||||
|
private int replacingDrawableTextIndex;
|
||||||
|
private float replacingDrawableTextOffset;
|
||||||
private float rightDrawableScale = 1.0f;
|
private float rightDrawableScale = 1.0f;
|
||||||
private int drawablePadding = AndroidUtilities.dp(4);
|
private int drawablePadding = AndroidUtilities.dp(4);
|
||||||
private int leftDrawableTopPadding;
|
private int leftDrawableTopPadding;
|
||||||
|
@ -211,9 +216,17 @@ public class SimpleTextView extends View implements Drawable.Callback {
|
||||||
fullLayoutLeftCharactersOffset = fullLayout.getPrimaryHorizontal(0) - firstLineLayout.getPrimaryHorizontal(0);
|
fullLayoutLeftCharactersOffset = fullLayout.getPrimaryHorizontal(0) - firstLineLayout.getPrimaryHorizontal(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (replacingDrawableTextIndex >= 0) {
|
||||||
|
replacingDrawableTextOffset = layout.getPrimaryHorizontal(replacingDrawableTextIndex);
|
||||||
|
} else {
|
||||||
|
replacingDrawableTextOffset = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean createLayout(int width) {
|
protected boolean createLayout(int width) {
|
||||||
|
CharSequence text = this.text;
|
||||||
|
replacingDrawableTextIndex = -1;
|
||||||
if (text != null) {
|
if (text != null) {
|
||||||
try {
|
try {
|
||||||
if (leftDrawable != null) {
|
if (leftDrawable != null) {
|
||||||
|
@ -225,6 +238,17 @@ public class SimpleTextView extends View implements Drawable.Callback {
|
||||||
width -= dw;
|
width -= dw;
|
||||||
width -= drawablePadding;
|
width -= drawablePadding;
|
||||||
}
|
}
|
||||||
|
if (replacedText != null && replacedDrawable != null) {
|
||||||
|
replacingDrawableTextIndex = text.toString().indexOf(replacedText);
|
||||||
|
if (replacingDrawableTextIndex >= 0) {
|
||||||
|
SpannableStringBuilder builder = SpannableStringBuilder.valueOf(text);
|
||||||
|
builder.setSpan(new DialogCell.FixedWidthSpan(replacedDrawable.getIntrinsicWidth()), replacingDrawableTextIndex, replacingDrawableTextIndex + replacedText.length(), 0);
|
||||||
|
text = builder;
|
||||||
|
} else {
|
||||||
|
width -= replacedDrawable.getIntrinsicWidth();
|
||||||
|
width -= drawablePadding;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (buildFullLayout) {
|
if (buildFullLayout) {
|
||||||
CharSequence string = TextUtils.ellipsize(text, textPaint, width, TextUtils.TruncateAt.END);
|
CharSequence string = TextUtils.ellipsize(text, textPaint, width, TextUtils.TruncateAt.END);
|
||||||
if (!string.equals(text)) {
|
if (!string.equals(text)) {
|
||||||
|
@ -379,6 +403,23 @@ public class SimpleTextView extends View implements Drawable.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void replaceTextWithDrawable(Drawable drawable, String replacedText) {
|
||||||
|
if (replacedDrawable == drawable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (replacedDrawable != null) {
|
||||||
|
replacedDrawable.setCallback(null);
|
||||||
|
}
|
||||||
|
replacedDrawable = drawable;
|
||||||
|
if (drawable != null) {
|
||||||
|
drawable.setCallback(this);
|
||||||
|
}
|
||||||
|
if (!recreateLayoutMaybe()) {
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
this.replacedText = replacedText;
|
||||||
|
}
|
||||||
|
|
||||||
public void setMinusWidth(int value) {
|
public void setMinusWidth(int value) {
|
||||||
if (value == minusWidth) {
|
if (value == minusWidth) {
|
||||||
return;
|
return;
|
||||||
|
@ -486,6 +527,11 @@ public class SimpleTextView extends View implements Drawable.Callback {
|
||||||
textOffsetX += drawablePadding + leftDrawable.getIntrinsicWidth();
|
textOffsetX += drawablePadding + leftDrawable.getIntrinsicWidth();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (replacedDrawable != null && replacingDrawableTextIndex < 0) {
|
||||||
|
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT) {
|
||||||
|
textOffsetX += drawablePadding + replacedDrawable.getIntrinsicWidth();
|
||||||
|
}
|
||||||
|
}
|
||||||
return (int) getX() + offsetX + textOffsetX;
|
return (int) getX() + offsetX + textOffsetX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,6 +576,26 @@ public class SimpleTextView extends View implements Drawable.Callback {
|
||||||
}
|
}
|
||||||
totalWidth += drawablePadding + leftDrawable.getIntrinsicWidth();
|
totalWidth += drawablePadding + leftDrawable.getIntrinsicWidth();
|
||||||
}
|
}
|
||||||
|
if (replacedDrawable != null && replacedText != null) {
|
||||||
|
int x = (int) (-scrollingOffset + replacingDrawableTextOffset);
|
||||||
|
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL) {
|
||||||
|
x += offsetX;
|
||||||
|
}
|
||||||
|
int y;
|
||||||
|
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.CENTER_VERTICAL) {
|
||||||
|
y = (getMeasuredHeight() - replacedDrawable.getIntrinsicHeight()) / 2 + leftDrawableTopPadding;
|
||||||
|
} else {
|
||||||
|
y = (textHeight - replacedDrawable.getIntrinsicHeight()) / 2 + leftDrawableTopPadding;
|
||||||
|
}
|
||||||
|
replacedDrawable.setBounds(x, y, x + replacedDrawable.getIntrinsicWidth(), y + replacedDrawable.getIntrinsicHeight());
|
||||||
|
replacedDrawable.draw(canvas);
|
||||||
|
if (replacingDrawableTextIndex < 0) {
|
||||||
|
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT || (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL) {
|
||||||
|
textOffsetX += drawablePadding + replacedDrawable.getIntrinsicWidth();
|
||||||
|
}
|
||||||
|
totalWidth += drawablePadding + replacedDrawable.getIntrinsicWidth();
|
||||||
|
}
|
||||||
|
}
|
||||||
if (rightDrawable != null) {
|
if (rightDrawable != null) {
|
||||||
int x = textOffsetX + textWidth + drawablePadding + (int) -scrollingOffset;
|
int x = textOffsetX + textWidth + drawablePadding + (int) -scrollingOffset;
|
||||||
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL) {
|
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.CENTER_HORIZONTAL) {
|
||||||
|
@ -681,6 +747,8 @@ public class SimpleTextView extends View implements Drawable.Callback {
|
||||||
invalidate(leftDrawable.getBounds());
|
invalidate(leftDrawable.getBounds());
|
||||||
} else if (who == rightDrawable) {
|
} else if (who == rightDrawable) {
|
||||||
invalidate(rightDrawable.getBounds());
|
invalidate(rightDrawable.getBounds());
|
||||||
|
} else if (who == replacedDrawable) {
|
||||||
|
invalidate(replacedDrawable.getBounds());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,7 @@ import org.telegram.tgnet.TLRPC;
|
||||||
import org.telegram.ui.Cells.ThemesHorizontalListCell;
|
import org.telegram.ui.Cells.ThemesHorizontalListCell;
|
||||||
import org.telegram.ui.Components.AudioVisualizerDrawable;
|
import org.telegram.ui.Components.AudioVisualizerDrawable;
|
||||||
import org.telegram.ui.Components.BackgroundGradientDrawable;
|
import org.telegram.ui.Components.BackgroundGradientDrawable;
|
||||||
|
import org.telegram.ui.Components.ChoosingStickerStatusDrawable;
|
||||||
import org.telegram.ui.Components.CombinedDrawable;
|
import org.telegram.ui.Components.CombinedDrawable;
|
||||||
import org.telegram.ui.Components.FragmentContextViewWavesDrawable;
|
import org.telegram.ui.Components.FragmentContextViewWavesDrawable;
|
||||||
import org.telegram.ui.Components.MotionBackgroundDrawable;
|
import org.telegram.ui.Components.MotionBackgroundDrawable;
|
||||||
|
@ -132,12 +133,15 @@ public class Theme {
|
||||||
|
|
||||||
public static class MessageDrawable extends Drawable {
|
public static class MessageDrawable extends Drawable {
|
||||||
|
|
||||||
private LinearGradient gradientShader;
|
private Shader gradientShader;
|
||||||
private int currentBackgroundHeight;
|
private int currentBackgroundHeight;
|
||||||
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
private Paint selectedPaint;
|
private Paint selectedPaint;
|
||||||
private int currentColor;
|
private int currentColor;
|
||||||
private int currentGradientColor;
|
private int currentGradientColor1;
|
||||||
|
private int currentGradientColor2;
|
||||||
|
private int currentGradientColor3;
|
||||||
|
private boolean currentAnimateGradient;
|
||||||
|
|
||||||
private RectF rect = new RectF();
|
private RectF rect = new RectF();
|
||||||
private Matrix matrix = new Matrix();
|
private Matrix matrix = new Matrix();
|
||||||
|
@ -153,6 +157,8 @@ public class Theme {
|
||||||
private boolean isTopNear;
|
private boolean isTopNear;
|
||||||
private boolean isBottomNear;
|
private boolean isBottomNear;
|
||||||
|
|
||||||
|
public static MotionBackgroundDrawable[] motionBackground = new MotionBackgroundDrawable[2];
|
||||||
|
|
||||||
private int[] currentShadowDrawableRadius = new int[]{-1, -1, -1, -1};
|
private int[] currentShadowDrawableRadius = new int[]{-1, -1, -1, -1};
|
||||||
private Drawable[] shadowDrawable = new Drawable[4];
|
private Drawable[] shadowDrawable = new Drawable[4];
|
||||||
private int[] shadowDrawableColor = new int[]{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
|
private int[] shadowDrawableColor = new int[]{0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff};
|
||||||
|
@ -172,6 +178,7 @@ public class Theme {
|
||||||
Drawable transitionDrawable;
|
Drawable transitionDrawable;
|
||||||
int transitionDrawableColor;
|
int transitionDrawableColor;
|
||||||
private int alpha;
|
private int alpha;
|
||||||
|
private boolean drawFullBubble;
|
||||||
|
|
||||||
public MessageDrawable(int type, boolean out, boolean selected) {
|
public MessageDrawable(int type, boolean out, boolean selected) {
|
||||||
super();
|
super();
|
||||||
|
@ -187,7 +194,18 @@ public class Theme {
|
||||||
return gradientShader != null && shouldDrawGradientIcons;
|
return gradientShader != null && shouldDrawGradientIcons;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LinearGradient getGradientShader() {
|
public void applyMatrixScale() {
|
||||||
|
if (gradientShader instanceof BitmapShader) {
|
||||||
|
int num = currentType == TYPE_PREVIEW ? 1 : 0;
|
||||||
|
Bitmap bitmap = motionBackground[num].getBitmap();
|
||||||
|
float scaleW = (bitmap.getWidth() / (float) motionBackground[num].getBounds().width());
|
||||||
|
float scaleH = (bitmap.getHeight() / (float) motionBackground[num].getBounds().height());
|
||||||
|
float scale = 1.0f / Math.min(scaleW, scaleH);
|
||||||
|
matrix.postScale(scale, scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Shader getGradientShader() {
|
||||||
return gradientShader;
|
return gradientShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,38 +221,85 @@ public class Theme {
|
||||||
return Theme.currentColors.get(key);
|
return Theme.currentColors.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTop(int top, int backgroundHeight, boolean topNear, boolean bottomNear) {
|
public void setTop(int top, int backgroundWidth, int backgroundHeight, boolean topNear, boolean bottomNear) {
|
||||||
|
setTop(top, backgroundWidth, backgroundHeight, backgroundHeight, topNear, bottomNear);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTop(int top, int backgroundWidth, int backgroundHeight, int heightOffset, boolean topNear, boolean bottomNear) {
|
||||||
int color;
|
int color;
|
||||||
Integer gradientColor;
|
Integer gradientColor1;
|
||||||
|
Integer gradientColor2;
|
||||||
|
Integer gradientColor3;
|
||||||
|
boolean animatedGradient;
|
||||||
if (isOut) {
|
if (isOut) {
|
||||||
color = getColor(isSelected ? key_chat_outBubbleSelected : key_chat_outBubble);
|
color = getColor(isSelected ? key_chat_outBubbleSelected : key_chat_outBubble);
|
||||||
gradientColor = getCurrentColor(key_chat_outBubbleGradient);
|
gradientColor1 = getCurrentColor(key_chat_outBubbleGradient1);
|
||||||
|
gradientColor2 = getCurrentColor(key_chat_outBubbleGradient2);
|
||||||
|
gradientColor3 = getCurrentColor(key_chat_outBubbleGradient3);
|
||||||
|
Integer val = getCurrentColor(key_chat_outBubbleGradientAnimated);
|
||||||
|
animatedGradient = val != null && val != 0;
|
||||||
} else {
|
} else {
|
||||||
color = getColor(isSelected ? key_chat_inBubbleSelected : key_chat_inBubble);
|
color = getColor(isSelected ? key_chat_inBubbleSelected : key_chat_inBubble);
|
||||||
gradientColor = null;
|
gradientColor1 = null;
|
||||||
|
gradientColor2 = null;
|
||||||
|
gradientColor3 = null;
|
||||||
|
animatedGradient = false;
|
||||||
}
|
}
|
||||||
if (gradientColor != null) {
|
if (gradientColor1 != null) {
|
||||||
color = getColor(key_chat_outBubble);
|
color = getColor(key_chat_outBubble);
|
||||||
}
|
}
|
||||||
if (gradientColor == null) {
|
if (gradientColor1 == null) {
|
||||||
gradientColor = 0;
|
gradientColor1 = 0;
|
||||||
}
|
}
|
||||||
if (gradientColor != 0 && (gradientShader == null || backgroundHeight != currentBackgroundHeight || currentColor != color || currentGradientColor != gradientColor)) {
|
if (gradientColor2 == null) {
|
||||||
gradientShader = new LinearGradient(0, 0, 0, backgroundHeight, new int[]{gradientColor, color}, null, Shader.TileMode.CLAMP);
|
gradientColor2 = 0;
|
||||||
|
}
|
||||||
|
if (gradientColor3 == null) {
|
||||||
|
gradientColor3 = 0;
|
||||||
|
}
|
||||||
|
int num = currentType == TYPE_PREVIEW ? 1 : 0;
|
||||||
|
if (gradientColor1 != 0 && (gradientShader == null || backgroundHeight != currentBackgroundHeight || currentColor != color || currentGradientColor1 != gradientColor1 || currentGradientColor2 != gradientColor2 || currentGradientColor3 != gradientColor3 || currentAnimateGradient != animatedGradient)) {
|
||||||
|
if (gradientColor2 != 0 && animatedGradient) {
|
||||||
|
if (motionBackground[num] == null) {
|
||||||
|
motionBackground[num] = new MotionBackgroundDrawable();
|
||||||
|
if (currentType != TYPE_PREVIEW) {
|
||||||
|
motionBackground[num].setPostInvalidateParent(true);
|
||||||
|
}
|
||||||
|
motionBackground[num].setRoundRadius(AndroidUtilities.dp(1));
|
||||||
|
}
|
||||||
|
motionBackground[num].setColors(color, gradientColor1, gradientColor2, gradientColor3);
|
||||||
|
gradientShader = motionBackground[num].getBitmapShader();
|
||||||
|
} else {
|
||||||
|
if (gradientColor2 != 0) {
|
||||||
|
if (gradientColor3 != 0) {
|
||||||
|
gradientShader = new LinearGradient(0, 0, 0, backgroundHeight, new int[]{gradientColor3, gradientColor2, gradientColor1, color}, null, Shader.TileMode.CLAMP);
|
||||||
|
} else {
|
||||||
|
gradientShader = new LinearGradient(0, 0, 0, backgroundHeight, new int[]{gradientColor2, gradientColor1, color}, null, Shader.TileMode.CLAMP);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gradientShader = new LinearGradient(0, 0, 0, backgroundHeight, new int[]{gradientColor1, color}, null, Shader.TileMode.CLAMP);
|
||||||
|
}
|
||||||
|
}
|
||||||
paint.setShader(gradientShader);
|
paint.setShader(gradientShader);
|
||||||
currentColor = color;
|
currentColor = color;
|
||||||
currentGradientColor = gradientColor;
|
currentAnimateGradient = animatedGradient;
|
||||||
|
currentGradientColor1 = gradientColor1;
|
||||||
|
currentGradientColor2 = gradientColor2;
|
||||||
|
currentGradientColor3 = gradientColor3;
|
||||||
paint.setColor(0xffffffff);
|
paint.setColor(0xffffffff);
|
||||||
} else if (gradientColor == 0) {
|
} else if (gradientColor1 == 0) {
|
||||||
if (gradientShader != null) {
|
if (gradientShader != null) {
|
||||||
gradientShader = null;
|
gradientShader = null;
|
||||||
paint.setShader(null);
|
paint.setShader(null);
|
||||||
}
|
}
|
||||||
paint.setColor(color);
|
paint.setColor(color);
|
||||||
}
|
}
|
||||||
|
if (gradientShader instanceof BitmapShader) {
|
||||||
|
motionBackground[num].setBounds(0, 0, backgroundWidth, backgroundHeight - (gradientShader instanceof BitmapShader ? heightOffset : 0));
|
||||||
|
}
|
||||||
currentBackgroundHeight = backgroundHeight;
|
currentBackgroundHeight = backgroundHeight;
|
||||||
|
|
||||||
topY = top;
|
topY = top - (gradientShader instanceof BitmapShader ? heightOffset : 0);
|
||||||
isTopNear = topNear;
|
isTopNear = topNear;
|
||||||
isBottomNear = bottomNear;
|
isBottomNear = bottomNear;
|
||||||
}
|
}
|
||||||
|
@ -358,6 +423,10 @@ public class Theme {
|
||||||
return transitionDrawable;
|
return transitionDrawable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MotionBackgroundDrawable getMotionBackgroundDrawable() {
|
||||||
|
return motionBackground[currentType == TYPE_PREVIEW ? 1 : 0];
|
||||||
|
}
|
||||||
|
|
||||||
public Drawable getShadowDrawable() {
|
public Drawable getShadowDrawable() {
|
||||||
if (gradientShader == null && !isSelected) {
|
if (gradientShader == null && !isSelected) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -482,6 +551,7 @@ public class Theme {
|
||||||
|
|
||||||
if (paintToUse == null && gradientShader != null) {
|
if (paintToUse == null && gradientShader != null) {
|
||||||
matrix.reset();
|
matrix.reset();
|
||||||
|
applyMatrixScale();
|
||||||
matrix.postTranslate(0, -topY);
|
matrix.postTranslate(0, -topY);
|
||||||
gradientShader.setLocalMatrix(matrix);
|
gradientShader.setLocalMatrix(matrix);
|
||||||
}
|
}
|
||||||
|
@ -489,7 +559,7 @@ public class Theme {
|
||||||
int top = Math.max(bounds.top, 0);
|
int top = Math.max(bounds.top, 0);
|
||||||
path.reset();
|
path.reset();
|
||||||
if (isOut) {
|
if (isOut) {
|
||||||
if (currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - rad < currentBackgroundHeight) {
|
if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - rad < currentBackgroundHeight) {
|
||||||
if (currentType == TYPE_MEDIA) {
|
if (currentType == TYPE_MEDIA) {
|
||||||
path.moveTo(bounds.right - dp(8) - rad, bounds.bottom - padding);
|
path.moveTo(bounds.right - dp(8) - rad, bounds.bottom - padding);
|
||||||
} else {
|
} else {
|
||||||
|
@ -502,7 +572,7 @@ public class Theme {
|
||||||
path.moveTo(bounds.right - dp(8), top - topY + currentBackgroundHeight);
|
path.moveTo(bounds.right - dp(8), top - topY + currentBackgroundHeight);
|
||||||
path.lineTo(bounds.left + padding, top - topY + currentBackgroundHeight);
|
path.lineTo(bounds.left + padding, top - topY + currentBackgroundHeight);
|
||||||
}
|
}
|
||||||
if (currentType == TYPE_PREVIEW || paintToUse != null || topY + rad * 2 >= 0) {
|
if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + rad * 2 >= 0) {
|
||||||
path.lineTo(bounds.left + padding, bounds.top + padding + rad);
|
path.lineTo(bounds.left + padding, bounds.top + padding + rad);
|
||||||
rect.set(bounds.left + padding, bounds.top + padding, bounds.left + padding + rad * 2, bounds.top + padding + rad * 2);
|
rect.set(bounds.left + padding, bounds.top + padding, bounds.left + padding + rad * 2, bounds.top + padding + rad * 2);
|
||||||
path.arcTo(rect, 180, 90, false);
|
path.arcTo(rect, 180, 90, false);
|
||||||
|
@ -535,7 +605,7 @@ public class Theme {
|
||||||
path.lineTo(bounds.right - padding, top - topY + currentBackgroundHeight);
|
path.lineTo(bounds.right - padding, top - topY + currentBackgroundHeight);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - smallRad * 2 < currentBackgroundHeight) {
|
if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - smallRad * 2 < currentBackgroundHeight) {
|
||||||
path.lineTo(bounds.right - dp(8), bounds.bottom - padding - smallRad - dp(3));
|
path.lineTo(bounds.right - dp(8), bounds.bottom - padding - smallRad - dp(3));
|
||||||
rect.set(bounds.right - dp(8), bounds.bottom - padding - smallRad * 2 - dp(9), bounds.right - dp(7) + smallRad * 2, bounds.bottom - padding - dp(1));
|
rect.set(bounds.right - dp(8), bounds.bottom - padding - smallRad * 2 - dp(9), bounds.right - dp(7) + smallRad * 2, bounds.bottom - padding - dp(1));
|
||||||
path.arcTo(rect, 180, -83, false);
|
path.arcTo(rect, 180, -83, false);
|
||||||
|
@ -544,7 +614,7 @@ public class Theme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - rad < currentBackgroundHeight) {
|
if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - rad < currentBackgroundHeight) {
|
||||||
if (currentType == TYPE_MEDIA) {
|
if (currentType == TYPE_MEDIA) {
|
||||||
path.moveTo(bounds.left + dp(8) + rad, bounds.bottom - padding);
|
path.moveTo(bounds.left + dp(8) + rad, bounds.bottom - padding);
|
||||||
} else {
|
} else {
|
||||||
|
@ -557,7 +627,7 @@ public class Theme {
|
||||||
path.moveTo(bounds.left + dp(8), top - topY + currentBackgroundHeight);
|
path.moveTo(bounds.left + dp(8), top - topY + currentBackgroundHeight);
|
||||||
path.lineTo(bounds.right - padding, top - topY + currentBackgroundHeight);
|
path.lineTo(bounds.right - padding, top - topY + currentBackgroundHeight);
|
||||||
}
|
}
|
||||||
if (currentType == TYPE_PREVIEW || paintToUse != null || topY + rad * 2 >= 0) {
|
if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + rad * 2 >= 0) {
|
||||||
path.lineTo(bounds.right - padding, bounds.top + padding + rad);
|
path.lineTo(bounds.right - padding, bounds.top + padding + rad);
|
||||||
rect.set(bounds.right - padding - rad * 2, bounds.top + padding, bounds.right - padding, bounds.top + padding + rad * 2);
|
rect.set(bounds.right - padding - rad * 2, bounds.top + padding, bounds.right - padding, bounds.top + padding + rad * 2);
|
||||||
path.arcTo(rect, 0, -90, false);
|
path.arcTo(rect, 0, -90, false);
|
||||||
|
@ -590,7 +660,7 @@ public class Theme {
|
||||||
path.lineTo(bounds.left + padding, top - topY + currentBackgroundHeight);
|
path.lineTo(bounds.left + padding, top - topY + currentBackgroundHeight);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - smallRad * 2 < currentBackgroundHeight) {
|
if (drawFullBubble || currentType == TYPE_PREVIEW || paintToUse != null || topY + bounds.bottom - smallRad * 2 < currentBackgroundHeight) {
|
||||||
path.lineTo(bounds.left + dp(8), bounds.bottom - padding - smallRad - dp(3));
|
path.lineTo(bounds.left + dp(8), bounds.bottom - padding - smallRad - dp(3));
|
||||||
rect.set(bounds.left + dp(7) - smallRad * 2, bounds.bottom - padding - smallRad * 2 - dp(9), bounds.left + dp(8), bounds.bottom - padding - dp(1));
|
rect.set(bounds.left + dp(7) - smallRad * 2, bounds.bottom - padding - smallRad * 2 - dp(9), bounds.left + dp(8), bounds.bottom - padding - dp(1));
|
||||||
path.arcTo(rect, 0, 83, false);
|
path.arcTo(rect, 0, 83, false);
|
||||||
|
@ -609,6 +679,10 @@ public class Theme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setDrawFullBubble(boolean drawFullBuble) {
|
||||||
|
this.drawFullBubble = drawFullBuble;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAlpha(int alpha) {
|
public void setAlpha(int alpha) {
|
||||||
if (this.alpha != alpha) {
|
if (this.alpha != alpha) {
|
||||||
|
@ -949,7 +1023,10 @@ public class Theme {
|
||||||
|
|
||||||
public int accentColor;
|
public int accentColor;
|
||||||
public int myMessagesAccentColor;
|
public int myMessagesAccentColor;
|
||||||
public int myMessagesGradientAccentColor;
|
public int myMessagesGradientAccentColor1;
|
||||||
|
public int myMessagesGradientAccentColor2;
|
||||||
|
public int myMessagesGradientAccentColor3;
|
||||||
|
public boolean myMessagesAnimated;
|
||||||
public long backgroundOverrideColor;
|
public long backgroundOverrideColor;
|
||||||
public long backgroundGradientOverrideColor1;
|
public long backgroundGradientOverrideColor1;
|
||||||
public long backgroundGradientOverrideColor2;
|
public long backgroundGradientOverrideColor2;
|
||||||
|
@ -1005,7 +1082,7 @@ public class Theme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int myMessagesAccent = myMessagesAccentColor;
|
int myMessagesAccent = myMessagesAccentColor;
|
||||||
if ((myMessagesAccentColor != 0 || accentColor != 0) && myMessagesGradientAccentColor != 0) {
|
if ((myMessagesAccentColor != 0 || accentColor != 0) && myMessagesGradientAccentColor1 != 0) {
|
||||||
int firstColor = myMessagesAccentColor != 0 ? myMessagesAccentColor : accentColor;
|
int firstColor = myMessagesAccentColor != 0 ? myMessagesAccentColor : accentColor;
|
||||||
Integer color = currentColorsNoAccent.get(key_chat_outBubble);
|
Integer color = currentColorsNoAccent.get(key_chat_outBubble);
|
||||||
if (color == null) {
|
if (color == null) {
|
||||||
|
@ -1013,7 +1090,7 @@ public class Theme {
|
||||||
}
|
}
|
||||||
int newColor = changeColorAccent(hsvTemp1, hsvTemp2, color, isDarkTheme);
|
int newColor = changeColorAccent(hsvTemp1, hsvTemp2, color, isDarkTheme);
|
||||||
int distance1 = AndroidUtilities.getColorDistance(firstColor, newColor);
|
int distance1 = AndroidUtilities.getColorDistance(firstColor, newColor);
|
||||||
int distance2 = AndroidUtilities.getColorDistance(firstColor, myMessagesGradientAccentColor);
|
int distance2 = AndroidUtilities.getColorDistance(firstColor, myMessagesGradientAccentColor1);
|
||||||
isMyMessagesGradientColorsNear = distance1 <= 35000 && distance2 <= 35000;
|
isMyMessagesGradientColorsNear = distance1 <= 35000 && distance2 <= 35000;
|
||||||
myMessagesAccent = getAccentColor(hsvTemp1, color, firstColor);
|
myMessagesAccent = getAccentColor(hsvTemp1, color, firstColor);
|
||||||
}
|
}
|
||||||
|
@ -1041,11 +1118,22 @@ public class Theme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isMyMessagesGradientColorsNear) {
|
if (!isMyMessagesGradientColorsNear) {
|
||||||
if (myMessagesGradientAccentColor != 0) {
|
if (myMessagesGradientAccentColor1 != 0) {
|
||||||
int textColor;
|
int textColor;
|
||||||
int subTextColor;
|
int subTextColor;
|
||||||
int seekbarColor;
|
int seekbarColor;
|
||||||
if (useBlackText(myMessagesAccentColor, myMessagesGradientAccentColor)) {
|
boolean useBlackText;
|
||||||
|
if (myMessagesGradientAccentColor2 != 0) {
|
||||||
|
int color = AndroidUtilities.getAverageColor(myMessagesAccentColor, myMessagesGradientAccentColor1);
|
||||||
|
color = AndroidUtilities.getAverageColor(color, myMessagesGradientAccentColor2);
|
||||||
|
if (myMessagesGradientAccentColor3 != 0) {
|
||||||
|
color = AndroidUtilities.getAverageColor(color, myMessagesGradientAccentColor3);
|
||||||
|
}
|
||||||
|
useBlackText = AndroidUtilities.computePerceivedBrightness(color) > 0.705f;
|
||||||
|
} else {
|
||||||
|
useBlackText = useBlackText(myMessagesAccentColor, myMessagesGradientAccentColor1);
|
||||||
|
}
|
||||||
|
if (useBlackText) {
|
||||||
textColor = 0xff212121;
|
textColor = 0xff212121;
|
||||||
subTextColor = 0xff555555;
|
subTextColor = 0xff555555;
|
||||||
seekbarColor = 0x4d000000;
|
seekbarColor = 0x4d000000;
|
||||||
|
@ -1135,9 +1223,16 @@ public class Theme {
|
||||||
isMyMessagesGradientColorsNear = false;
|
isMyMessagesGradientColorsNear = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (myMessagesAccentColor != 0 && myMessagesGradientAccentColor != 0) {
|
if (myMessagesAccentColor != 0 && myMessagesGradientAccentColor1 != 0) {
|
||||||
currentColors.put(key_chat_outBubble, myMessagesAccentColor);
|
currentColors.put(key_chat_outBubble, myMessagesAccentColor);
|
||||||
currentColors.put(key_chat_outBubbleGradient, myMessagesGradientAccentColor);
|
currentColors.put(key_chat_outBubbleGradient1, myMessagesGradientAccentColor1);
|
||||||
|
if (myMessagesGradientAccentColor2 != 0) {
|
||||||
|
currentColors.put(key_chat_outBubbleGradient2, myMessagesGradientAccentColor2);
|
||||||
|
if (myMessagesGradientAccentColor3 != 0) {
|
||||||
|
currentColors.put(key_chat_outBubbleGradient3, myMessagesGradientAccentColor3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentColors.put(key_chat_outBubbleGradientAnimated, myMessagesAnimated ? 1 : 0);
|
||||||
}
|
}
|
||||||
int backgroundOverride = (int) backgroundOverrideColor;
|
int backgroundOverride = (int) backgroundOverrideColor;
|
||||||
if (backgroundOverride != 0) {
|
if (backgroundOverride != 0) {
|
||||||
|
@ -1613,7 +1708,11 @@ public class Theme {
|
||||||
if (defaultAccent == null || accent == null) {
|
if (defaultAccent == null || accent == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return defaultAccent.myMessagesAccentColor == accent.myMessagesAccentColor && defaultAccent.myMessagesGradientAccentColor == accent.myMessagesGradientAccentColor;
|
return defaultAccent.myMessagesAccentColor == accent.myMessagesAccentColor &&
|
||||||
|
defaultAccent.myMessagesGradientAccentColor1 == accent.myMessagesGradientAccentColor1 &&
|
||||||
|
defaultAccent.myMessagesGradientAccentColor2 == accent.myMessagesGradientAccentColor2 &&
|
||||||
|
defaultAccent.myMessagesGradientAccentColor3 == accent.myMessagesGradientAccentColor3 &&
|
||||||
|
defaultAccent.myMessagesAnimated == accent.myMessagesAnimated;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isDefaultMainAccent() {
|
private boolean isDefaultMainAccent() {
|
||||||
|
@ -1708,7 +1807,7 @@ public class Theme {
|
||||||
themeAccent.myMessagesAccentColor = myMessages[a];
|
themeAccent.myMessagesAccentColor = myMessages[a];
|
||||||
}
|
}
|
||||||
if (myMessagesGradient != null) {
|
if (myMessagesGradient != null) {
|
||||||
themeAccent.myMessagesGradientAccentColor = myMessagesGradient[a];
|
themeAccent.myMessagesGradientAccentColor1 = myMessagesGradient[a];
|
||||||
}
|
}
|
||||||
if (background != null) {
|
if (background != null) {
|
||||||
themeAccent.backgroundOverrideColor = background[a];
|
themeAccent.backgroundOverrideColor = background[a];
|
||||||
|
@ -1781,10 +1880,13 @@ public class Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean accentEquals(ThemeAccent accent, TLRPC.TL_themeSettings settings) {
|
public static boolean accentEquals(ThemeAccent accent, TLRPC.TL_themeSettings settings) {
|
||||||
int myMessagesGradientAccentColor = settings.message_top_color;
|
int bottomColor = settings.message_colors.size() > 0 ? settings.message_colors.get(0) | 0xff000000 : 0;
|
||||||
if (settings.message_bottom_color == myMessagesGradientAccentColor) {
|
int myMessagesGradientAccentColor1 = settings.message_colors.size() > 1 ? settings.message_colors.get(1) | 0xff000000 : 0;
|
||||||
myMessagesGradientAccentColor = 0;
|
if (bottomColor == myMessagesGradientAccentColor1) {
|
||||||
|
myMessagesGradientAccentColor1 = 0;
|
||||||
}
|
}
|
||||||
|
int myMessagesGradientAccentColor2 = settings.message_colors.size() > 2 ? settings.message_colors.get(2) | 0xff000000 : 0;
|
||||||
|
int myMessagesGradientAccentColor3 = settings.message_colors.size() > 3 ? settings.message_colors.get(3) | 0xff000000 : 0;
|
||||||
int backgroundOverrideColor = 0;
|
int backgroundOverrideColor = 0;
|
||||||
long backgroundGradientOverrideColor1 = 0;
|
long backgroundGradientOverrideColor1 = 0;
|
||||||
long backgroundGradientOverrideColor2 = 0;
|
long backgroundGradientOverrideColor2 = 0;
|
||||||
|
@ -1816,8 +1918,11 @@ public class Theme {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return settings.accent_color == accent.accentColor &&
|
return settings.accent_color == accent.accentColor &&
|
||||||
settings.message_bottom_color == accent.myMessagesAccentColor &&
|
bottomColor == accent.myMessagesAccentColor &&
|
||||||
myMessagesGradientAccentColor == accent.myMessagesGradientAccentColor &&
|
myMessagesGradientAccentColor1 == accent.myMessagesGradientAccentColor1 &&
|
||||||
|
myMessagesGradientAccentColor2 == accent.myMessagesGradientAccentColor2 &&
|
||||||
|
myMessagesGradientAccentColor3 == accent.myMessagesGradientAccentColor3 &&
|
||||||
|
settings.message_colors_animated == accent.myMessagesAnimated &&
|
||||||
backgroundOverrideColor == accent.backgroundOverrideColor &&
|
backgroundOverrideColor == accent.backgroundOverrideColor &&
|
||||||
backgroundGradientOverrideColor1 == accent.backgroundGradientOverrideColor1 &&
|
backgroundGradientOverrideColor1 == accent.backgroundGradientOverrideColor1 &&
|
||||||
backgroundGradientOverrideColor2 == accent.backgroundGradientOverrideColor2 &&
|
backgroundGradientOverrideColor2 == accent.backgroundGradientOverrideColor2 &&
|
||||||
|
@ -1829,11 +1934,14 @@ public class Theme {
|
||||||
|
|
||||||
public static void fillAccentValues(ThemeAccent themeAccent, TLRPC.TL_themeSettings settings) {
|
public static void fillAccentValues(ThemeAccent themeAccent, TLRPC.TL_themeSettings settings) {
|
||||||
themeAccent.accentColor = settings.accent_color;
|
themeAccent.accentColor = settings.accent_color;
|
||||||
themeAccent.myMessagesAccentColor = settings.message_bottom_color;
|
themeAccent.myMessagesAccentColor = settings.message_colors.size() > 0 ? settings.message_colors.get(0) | 0xff000000 : 0;
|
||||||
themeAccent.myMessagesGradientAccentColor = settings.message_top_color;
|
themeAccent.myMessagesGradientAccentColor1 = settings.message_colors.size() > 1 ? settings.message_colors.get(1) | 0xff000000 : 0;
|
||||||
if (themeAccent.myMessagesAccentColor == themeAccent.myMessagesGradientAccentColor) {
|
if (themeAccent.myMessagesAccentColor == themeAccent.myMessagesGradientAccentColor1) {
|
||||||
themeAccent.myMessagesGradientAccentColor = 0;
|
themeAccent.myMessagesGradientAccentColor1 = 0;
|
||||||
}
|
}
|
||||||
|
themeAccent.myMessagesGradientAccentColor2 = settings.message_colors.size() > 2 ? settings.message_colors.get(2) | 0xff000000 : 0;
|
||||||
|
themeAccent.myMessagesGradientAccentColor3 = settings.message_colors.size() > 3 ? settings.message_colors.get(3) | 0xff000000 : 0;
|
||||||
|
themeAccent.myMessagesAnimated = settings.message_colors_animated;
|
||||||
if (settings.wallpaper != null && settings.wallpaper.settings != null) {
|
if (settings.wallpaper != null && settings.wallpaper.settings != null) {
|
||||||
themeAccent.backgroundOverrideColor = getWallpaperColor(settings.wallpaper.settings.background_color);
|
themeAccent.backgroundOverrideColor = getWallpaperColor(settings.wallpaper.settings.background_color);
|
||||||
if ((settings.wallpaper.settings.flags & 16) != 0 && settings.wallpaper.settings.second_background_color == 0) {
|
if ((settings.wallpaper.settings.flags & 16) != 0 && settings.wallpaper.settings.second_background_color == 0) {
|
||||||
|
@ -1896,7 +2004,10 @@ public class Theme {
|
||||||
ThemeAccent themeAccent = new ThemeAccent();
|
ThemeAccent themeAccent = new ThemeAccent();
|
||||||
themeAccent.accentColor = accent.accentColor;
|
themeAccent.accentColor = accent.accentColor;
|
||||||
themeAccent.myMessagesAccentColor = accent.myMessagesAccentColor;
|
themeAccent.myMessagesAccentColor = accent.myMessagesAccentColor;
|
||||||
themeAccent.myMessagesGradientAccentColor = accent.myMessagesGradientAccentColor;
|
themeAccent.myMessagesGradientAccentColor1 = accent.myMessagesGradientAccentColor1;
|
||||||
|
themeAccent.myMessagesGradientAccentColor2 = accent.myMessagesGradientAccentColor2;
|
||||||
|
themeAccent.myMessagesGradientAccentColor3 = accent.myMessagesGradientAccentColor3;
|
||||||
|
themeAccent.myMessagesAnimated = accent.myMessagesAnimated;
|
||||||
themeAccent.backgroundOverrideColor = accent.backgroundOverrideColor;
|
themeAccent.backgroundOverrideColor = accent.backgroundOverrideColor;
|
||||||
themeAccent.backgroundGradientOverrideColor1 = accent.backgroundGradientOverrideColor1;
|
themeAccent.backgroundGradientOverrideColor1 = accent.backgroundGradientOverrideColor1;
|
||||||
themeAccent.backgroundGradientOverrideColor2 = accent.backgroundGradientOverrideColor2;
|
themeAccent.backgroundGradientOverrideColor2 = accent.backgroundGradientOverrideColor2;
|
||||||
|
@ -2256,6 +2367,7 @@ public class Theme {
|
||||||
|
|
||||||
public static Drawable chat_msgNoSoundDrawable;
|
public static Drawable chat_msgNoSoundDrawable;
|
||||||
public static Drawable chat_composeShadowDrawable;
|
public static Drawable chat_composeShadowDrawable;
|
||||||
|
public static Drawable chat_composeShadowRoundDrawable;
|
||||||
public static Drawable chat_roundVideoShadow;
|
public static Drawable chat_roundVideoShadow;
|
||||||
public static MessageDrawable chat_msgInDrawable;
|
public static MessageDrawable chat_msgInDrawable;
|
||||||
public static MessageDrawable chat_msgInSelectedDrawable;
|
public static MessageDrawable chat_msgInSelectedDrawable;
|
||||||
|
@ -2265,7 +2377,7 @@ public class Theme {
|
||||||
public static MessageDrawable chat_msgInMediaSelectedDrawable;
|
public static MessageDrawable chat_msgInMediaSelectedDrawable;
|
||||||
public static MessageDrawable chat_msgOutMediaDrawable;
|
public static MessageDrawable chat_msgOutMediaDrawable;
|
||||||
public static MessageDrawable chat_msgOutMediaSelectedDrawable;
|
public static MessageDrawable chat_msgOutMediaSelectedDrawable;
|
||||||
private static StatusDrawable[] chat_status_drawables = new StatusDrawable[5];
|
private static StatusDrawable[] chat_status_drawables = new StatusDrawable[6];
|
||||||
|
|
||||||
public static PathAnimator playPauseAnimator;
|
public static PathAnimator playPauseAnimator;
|
||||||
public static Drawable chat_msgOutCheckDrawable;
|
public static Drawable chat_msgOutCheckDrawable;
|
||||||
|
@ -2670,7 +2782,10 @@ public class Theme {
|
||||||
public static final String key_chat_inBubbleSelected = "chat_inBubbleSelected";
|
public static final String key_chat_inBubbleSelected = "chat_inBubbleSelected";
|
||||||
public static final String key_chat_inBubbleShadow = "chat_inBubbleShadow";
|
public static final String key_chat_inBubbleShadow = "chat_inBubbleShadow";
|
||||||
public static final String key_chat_outBubble = "chat_outBubble";
|
public static final String key_chat_outBubble = "chat_outBubble";
|
||||||
public static final String key_chat_outBubbleGradient = "chat_outBubbleGradient";
|
public static final String key_chat_outBubbleGradient1 = "chat_outBubbleGradient";
|
||||||
|
public static final String key_chat_outBubbleGradient2 = "chat_outBubbleGradient2";
|
||||||
|
public static final String key_chat_outBubbleGradient3 = "chat_outBubbleGradient3";
|
||||||
|
public static final String key_chat_outBubbleGradientAnimated = "chat_outBubbleGradientAnimated";
|
||||||
public static final String key_chat_outBubbleGradientSelectedOverlay = "chat_outBubbleGradientSelectedOverlay";
|
public static final String key_chat_outBubbleGradientSelectedOverlay = "chat_outBubbleGradientSelectedOverlay";
|
||||||
public static final String key_chat_outBubbleSelected = "chat_outBubbleSelected";
|
public static final String key_chat_outBubbleSelected = "chat_outBubbleSelected";
|
||||||
public static final String key_chat_outBubbleShadow = "chat_outBubbleShadow";
|
public static final String key_chat_outBubbleShadow = "chat_outBubbleShadow";
|
||||||
|
@ -4162,7 +4277,7 @@ public class Theme {
|
||||||
myMessagesColorKeys.add(key_chat_outBubble);
|
myMessagesColorKeys.add(key_chat_outBubble);
|
||||||
myMessagesColorKeys.add(key_chat_outBubbleSelected);
|
myMessagesColorKeys.add(key_chat_outBubbleSelected);
|
||||||
myMessagesColorKeys.add(key_chat_outBubbleShadow);
|
myMessagesColorKeys.add(key_chat_outBubbleShadow);
|
||||||
myMessagesColorKeys.add(key_chat_outBubbleGradient);
|
myMessagesColorKeys.add(key_chat_outBubbleGradient1);
|
||||||
myMessagesColorKeys.add(key_chat_outSentCheck);
|
myMessagesColorKeys.add(key_chat_outSentCheck);
|
||||||
myMessagesColorKeys.add(key_chat_outSentCheckSelected);
|
myMessagesColorKeys.add(key_chat_outSentCheckSelected);
|
||||||
myMessagesColorKeys.add(key_chat_outSentCheckRead);
|
myMessagesColorKeys.add(key_chat_outSentCheckRead);
|
||||||
|
@ -4456,7 +4571,14 @@ public class Theme {
|
||||||
accent.accentColor = data.readInt32(true);
|
accent.accentColor = data.readInt32(true);
|
||||||
accent.parentTheme = info;
|
accent.parentTheme = info;
|
||||||
accent.myMessagesAccentColor = data.readInt32(true);
|
accent.myMessagesAccentColor = data.readInt32(true);
|
||||||
accent.myMessagesGradientAccentColor = data.readInt32(true);
|
accent.myMessagesGradientAccentColor1 = data.readInt32(true);
|
||||||
|
if (version >= 7) {
|
||||||
|
accent.myMessagesGradientAccentColor2 = data.readInt32(true);
|
||||||
|
accent.myMessagesGradientAccentColor3 = data.readInt32(true);
|
||||||
|
}
|
||||||
|
if (version >= 8) {
|
||||||
|
accent.myMessagesAnimated = data.readBool(true);
|
||||||
|
}
|
||||||
if (version >= 3) {
|
if (version >= 3) {
|
||||||
accent.backgroundOverrideColor = data.readInt64(true);
|
accent.backgroundOverrideColor = data.readInt64(true);
|
||||||
} else {
|
} else {
|
||||||
|
@ -4527,13 +4649,16 @@ public class Theme {
|
||||||
info.lastAccentId = 101;
|
info.lastAccentId = 101;
|
||||||
|
|
||||||
SerializedData data = new SerializedData(4 * (15 + 2));
|
SerializedData data = new SerializedData(4 * (15 + 2));
|
||||||
data.writeInt32(6);
|
data.writeInt32(8);
|
||||||
data.writeInt32(1);
|
data.writeInt32(1);
|
||||||
|
|
||||||
data.writeInt32(accent.id);
|
data.writeInt32(accent.id);
|
||||||
data.writeInt32(accent.accentColor);
|
data.writeInt32(accent.accentColor);
|
||||||
data.writeInt32(accent.myMessagesAccentColor);
|
data.writeInt32(accent.myMessagesAccentColor);
|
||||||
data.writeInt32(accent.myMessagesGradientAccentColor);
|
data.writeInt32(accent.myMessagesGradientAccentColor1);
|
||||||
|
data.writeInt32(accent.myMessagesGradientAccentColor2);
|
||||||
|
data.writeInt32(accent.myMessagesGradientAccentColor3);
|
||||||
|
data.writeBool(accent.myMessagesAnimated);
|
||||||
data.writeInt64(accent.backgroundOverrideColor);
|
data.writeInt64(accent.backgroundOverrideColor);
|
||||||
data.writeInt64(accent.backgroundGradientOverrideColor1);
|
data.writeInt64(accent.backgroundGradientOverrideColor1);
|
||||||
data.writeInt64(accent.backgroundGradientOverrideColor2);
|
data.writeInt64(accent.backgroundGradientOverrideColor2);
|
||||||
|
@ -5644,10 +5769,10 @@ public class Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void refreshThemeColors() {
|
public static void refreshThemeColors() {
|
||||||
refreshThemeColors(false);
|
refreshThemeColors(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void refreshThemeColors(boolean bg) {
|
public static void refreshThemeColors(boolean bg, boolean messages) {
|
||||||
currentColors.clear();
|
currentColors.clear();
|
||||||
currentColors.putAll(currentColorsNoAccent);
|
currentColors.putAll(currentColorsNoAccent);
|
||||||
shouldDrawGradientIcons = true;
|
shouldDrawGradientIcons = true;
|
||||||
|
@ -5655,7 +5780,9 @@ public class Theme {
|
||||||
if (accent != null) {
|
if (accent != null) {
|
||||||
shouldDrawGradientIcons = accent.fillAccentColors(currentColorsNoAccent, currentColors);
|
shouldDrawGradientIcons = accent.fillAccentColors(currentColorsNoAccent, currentColors);
|
||||||
}
|
}
|
||||||
reloadWallpaper();
|
if (!messages) {
|
||||||
|
reloadWallpaper();
|
||||||
|
}
|
||||||
applyCommonTheme();
|
applyCommonTheme();
|
||||||
applyDialogsTheme();
|
applyDialogsTheme();
|
||||||
applyProfileTheme();
|
applyProfileTheme();
|
||||||
|
@ -5814,7 +5941,7 @@ public class Theme {
|
||||||
int N = theme.themeAccents.size();
|
int N = theme.themeAccents.size();
|
||||||
int count = Math.max(0, N - theme.defaultAccentCount);
|
int count = Math.max(0, N - theme.defaultAccentCount);
|
||||||
SerializedData data = new SerializedData(4 * (count * 15 + 2));
|
SerializedData data = new SerializedData(4 * (count * 15 + 2));
|
||||||
data.writeInt32(6);
|
data.writeInt32(8);
|
||||||
data.writeInt32(count);
|
data.writeInt32(count);
|
||||||
for (int a = 0; a < N; a++) {
|
for (int a = 0; a < N; a++) {
|
||||||
ThemeAccent accent = theme.themeAccents.get(a);
|
ThemeAccent accent = theme.themeAccents.get(a);
|
||||||
|
@ -5824,7 +5951,10 @@ public class Theme {
|
||||||
data.writeInt32(accent.id);
|
data.writeInt32(accent.id);
|
||||||
data.writeInt32(accent.accentColor);
|
data.writeInt32(accent.accentColor);
|
||||||
data.writeInt32(accent.myMessagesAccentColor);
|
data.writeInt32(accent.myMessagesAccentColor);
|
||||||
data.writeInt32(accent.myMessagesGradientAccentColor);
|
data.writeInt32(accent.myMessagesGradientAccentColor1);
|
||||||
|
data.writeInt32(accent.myMessagesGradientAccentColor2);
|
||||||
|
data.writeInt32(accent.myMessagesGradientAccentColor3);
|
||||||
|
data.writeBool(accent.myMessagesAnimated);
|
||||||
data.writeInt64(accent.backgroundOverrideColor);
|
data.writeInt64(accent.backgroundOverrideColor);
|
||||||
data.writeInt64(accent.backgroundGradientOverrideColor1);
|
data.writeInt64(accent.backgroundGradientOverrideColor1);
|
||||||
data.writeInt64(accent.backgroundGradientOverrideColor2);
|
data.writeInt64(accent.backgroundGradientOverrideColor2);
|
||||||
|
@ -5969,6 +6099,10 @@ public class Theme {
|
||||||
return currentTheme == currentNightTheme;
|
return currentTheme == currentNightTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isCurrentThemeDark() {
|
||||||
|
return currentTheme.isDark();
|
||||||
|
}
|
||||||
|
|
||||||
public static ThemeInfo getActiveTheme() {
|
public static ThemeInfo getActiveTheme() {
|
||||||
return currentTheme;
|
return currentTheme;
|
||||||
}
|
}
|
||||||
|
@ -6290,10 +6424,19 @@ public class Theme {
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
if (colorsMap != defaultColors) {
|
if (colorsMap != defaultColors) {
|
||||||
int outBubbleColor = accent != null ? accent.myMessagesAccentColor : 0;
|
int outBubbleColor = accent != null ? accent.myMessagesAccentColor : 0;
|
||||||
int outBubbleGradient = accent != null ? accent.myMessagesGradientAccentColor : 0;
|
int outBubbleGradient1 = accent != null ? accent.myMessagesGradientAccentColor1 : 0;
|
||||||
if (outBubbleColor != 0 && outBubbleGradient != 0) {
|
int outBubbleGradient2 = accent != null ? accent.myMessagesGradientAccentColor2 : 0;
|
||||||
|
int outBubbleGradient3 = accent != null ? accent.myMessagesGradientAccentColor3 : 0;
|
||||||
|
if (outBubbleColor != 0 && outBubbleGradient1 != 0) {
|
||||||
colorsMap.put(key_chat_outBubble, outBubbleColor);
|
colorsMap.put(key_chat_outBubble, outBubbleColor);
|
||||||
colorsMap.put(key_chat_outBubbleGradient, outBubbleGradient);
|
colorsMap.put(key_chat_outBubbleGradient1, outBubbleGradient1);
|
||||||
|
if (outBubbleGradient2 != 0) {
|
||||||
|
colorsMap.put(key_chat_outBubbleGradient2, outBubbleGradient2);
|
||||||
|
if (outBubbleGradient3 != 0) {
|
||||||
|
colorsMap.put(key_chat_outBubbleGradient3, outBubbleGradient3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
colorsMap.put(key_chat_outBubbleGradientAnimated, accent != null && accent.myMessagesAnimated ? 1 : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (HashMap.Entry<String, Integer> entry : colorsMap.entrySet()) {
|
for (HashMap.Entry<String, Integer> entry : colorsMap.entrySet()) {
|
||||||
|
@ -6737,7 +6880,7 @@ public class Theme {
|
||||||
int messageFieldIconColor = getPreviewColor(colors, key_chat_messagePanelIcons);
|
int messageFieldIconColor = getPreviewColor(colors, key_chat_messagePanelIcons);
|
||||||
int messageInColor = getPreviewColor(colors, key_chat_inBubble);
|
int messageInColor = getPreviewColor(colors, key_chat_inBubble);
|
||||||
int messageOutColor = getPreviewColor(colors, key_chat_outBubble);
|
int messageOutColor = getPreviewColor(colors, key_chat_outBubble);
|
||||||
Integer messageOutGradientColor = colors.get(key_chat_outBubbleGradient);
|
Integer messageOutGradientColor = colors.get(key_chat_outBubbleGradient1);
|
||||||
Integer backgroundColor = colors.get(key_chat_wallpaper);
|
Integer backgroundColor = colors.get(key_chat_wallpaper);
|
||||||
Integer gradientToColor1 = colors.get(key_chat_wallpaper_gradient_to1);
|
Integer gradientToColor1 = colors.get(key_chat_wallpaper_gradient_to1);
|
||||||
Integer gradientToColor2 = colors.get(key_chat_wallpaper_gradient_to2);
|
Integer gradientToColor2 = colors.get(key_chat_wallpaper_gradient_to2);
|
||||||
|
@ -6960,15 +7103,15 @@ public class Theme {
|
||||||
otherDrawable.draw(canvas);
|
otherDrawable.draw(canvas);
|
||||||
}
|
}
|
||||||
msgDrawable[1].setBounds(161, 216, bitmap.getWidth() - 20, 216 + 92);
|
msgDrawable[1].setBounds(161, 216, bitmap.getWidth() - 20, 216 + 92);
|
||||||
msgDrawable[1].setTop(0, 522, false, false);
|
msgDrawable[1].setTop(0, 560, 522, false, false);
|
||||||
msgDrawable[1].draw(canvas);
|
msgDrawable[1].draw(canvas);
|
||||||
|
|
||||||
msgDrawable[1].setBounds(161, 430, bitmap.getWidth() - 20, 430 + 92);
|
msgDrawable[1].setBounds(161, 430, bitmap.getWidth() - 20, 430 + 92);
|
||||||
msgDrawable[1].setTop(430, 522, false, false);
|
msgDrawable[1].setTop(430, 560, 522, false, false);
|
||||||
msgDrawable[1].draw(canvas);
|
msgDrawable[1].draw(canvas);
|
||||||
|
|
||||||
msgDrawable[0].setBounds(20, 323, 399, 323 + 92);
|
msgDrawable[0].setBounds(20, 323, 399, 323 + 92);
|
||||||
msgDrawable[0].setTop(323, 522, false, false);
|
msgDrawable[0].setTop(323, 560, 522, false, false);
|
||||||
msgDrawable[0].draw(canvas);
|
msgDrawable[0].draw(canvas);
|
||||||
|
|
||||||
paint.setColor(messageFieldColor);
|
paint.setColor(messageFieldColor);
|
||||||
|
@ -7695,7 +7838,8 @@ public class Theme {
|
||||||
chat_locationDrawable[0] = resources.getDrawable(R.drawable.msg_location).mutate();
|
chat_locationDrawable[0] = resources.getDrawable(R.drawable.msg_location).mutate();
|
||||||
chat_locationDrawable[1] = resources.getDrawable(R.drawable.msg_location).mutate();
|
chat_locationDrawable[1] = resources.getDrawable(R.drawable.msg_location).mutate();
|
||||||
|
|
||||||
chat_composeShadowDrawable = context.getResources().getDrawable(R.drawable.compose_panel_shadow);
|
chat_composeShadowDrawable = context.getResources().getDrawable(R.drawable.compose_panel_shadow).mutate();
|
||||||
|
chat_composeShadowRoundDrawable = context.getResources().getDrawable(R.drawable.sheet_shadow_round).mutate();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int bitmapSize = AndroidUtilities.roundMessageSize + AndroidUtilities.dp(6);
|
int bitmapSize = AndroidUtilities.roundMessageSize + AndroidUtilities.dp(6);
|
||||||
|
@ -7750,7 +7894,6 @@ public class Theme {
|
||||||
chat_instantViewPaint.setTextSize(AndroidUtilities.dp(13));
|
chat_instantViewPaint.setTextSize(AndroidUtilities.dp(13));
|
||||||
chat_instantViewRectPaint.setStrokeWidth(AndroidUtilities.dp(1));
|
chat_instantViewRectPaint.setStrokeWidth(AndroidUtilities.dp(1));
|
||||||
chat_pollTimerPaint.setStrokeWidth(AndroidUtilities.dp(1.1f));
|
chat_pollTimerPaint.setStrokeWidth(AndroidUtilities.dp(1.1f));
|
||||||
chat_statusRecordPaint.setStrokeWidth(AndroidUtilities.dp(2));
|
|
||||||
chat_actionTextPaint.setTextSize(AndroidUtilities.dp(Math.max(16, SharedConfig.fontSize) - 2));
|
chat_actionTextPaint.setTextSize(AndroidUtilities.dp(Math.max(16, SharedConfig.fontSize) - 2));
|
||||||
chat_contextResult_titleTextPaint.setTextSize(AndroidUtilities.dp(15));
|
chat_contextResult_titleTextPaint.setTextSize(AndroidUtilities.dp(15));
|
||||||
chat_contextResult_descriptionTextPaint.setTextSize(AndroidUtilities.dp(13));
|
chat_contextResult_descriptionTextPaint.setTextSize(AndroidUtilities.dp(13));
|
||||||
|
@ -7961,6 +8104,7 @@ public class Theme {
|
||||||
setDrawableColor(chat_psaHelpDrawable[1], getColor(key_chat_outViews));
|
setDrawableColor(chat_psaHelpDrawable[1], getColor(key_chat_outViews));
|
||||||
|
|
||||||
setDrawableColorByKey(chat_composeShadowDrawable, key_chat_messagePanelShadow);
|
setDrawableColorByKey(chat_composeShadowDrawable, key_chat_messagePanelShadow);
|
||||||
|
setDrawableColorByKey(chat_composeShadowRoundDrawable, key_chat_messagePanelBackground);
|
||||||
|
|
||||||
int color = getColor(key_chat_outAudioSeekbarFill);
|
int color = getColor(key_chat_outAudioSeekbarFill);
|
||||||
if (color == 0xffffffff) {
|
if (color == 0xffffffff) {
|
||||||
|
@ -8993,7 +9137,7 @@ public class Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StatusDrawable getChatStatusDrawable(int type) {
|
public static StatusDrawable getChatStatusDrawable(int type) {
|
||||||
if (type < 0 || type > 4) {
|
if (type < 0 || type > 5) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
StatusDrawable statusDrawable = chat_status_drawables[type];
|
StatusDrawable statusDrawable = chat_status_drawables[type];
|
||||||
|
@ -9016,6 +9160,9 @@ public class Theme {
|
||||||
case 4:
|
case 4:
|
||||||
chat_status_drawables[4] = new RoundStatusDrawable(true);
|
chat_status_drawables[4] = new RoundStatusDrawable(true);
|
||||||
break;
|
break;
|
||||||
|
case 5:
|
||||||
|
chat_status_drawables[5] = new ChoosingStickerStatusDrawable(true);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
statusDrawable = chat_status_drawables[type];
|
statusDrawable = chat_status_drawables[type];
|
||||||
statusDrawable.start();
|
statusDrawable.start();
|
||||||
|
|
|
@ -144,6 +144,10 @@ public class DialogsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getDialogsType() {
|
||||||
|
return dialogsType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
MessagesController messagesController = MessagesController.getInstance(currentAccount);
|
MessagesController messagesController = MessagesController.getInstance(currentAccount);
|
||||||
|
|
|
@ -106,10 +106,10 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
public CharSequence name;
|
public CharSequence name;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class RecentSearchObject {
|
public static class RecentSearchObject {
|
||||||
TLObject object;
|
public TLObject object;
|
||||||
int date;
|
public int date;
|
||||||
long did;
|
public long did;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface DialogsSearchAdapterDelegate {
|
public interface DialogsSearchAdapterDelegate {
|
||||||
|
@ -121,7 +121,17 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
boolean isSelected(long dialogId);
|
boolean isSelected(long dialogId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CategoryAdapterRecycler extends RecyclerListView.SelectionAdapter {
|
public static class CategoryAdapterRecycler extends RecyclerListView.SelectionAdapter {
|
||||||
|
|
||||||
|
private final Context mContext;
|
||||||
|
private final int currentAccount;
|
||||||
|
private boolean drawChecked;
|
||||||
|
|
||||||
|
public CategoryAdapterRecycler(Context context, int account, boolean drawChecked) {
|
||||||
|
this.drawChecked = drawChecked;
|
||||||
|
mContext = context;
|
||||||
|
currentAccount = account;
|
||||||
|
}
|
||||||
|
|
||||||
public void setIndex(int value) {
|
public void setIndex(int value) {
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
|
@ -129,7 +139,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
View view = new HintDialogCell(mContext);
|
View view = new HintDialogCell(mContext, drawChecked);
|
||||||
view.setLayoutParams(new RecyclerView.LayoutParams(AndroidUtilities.dp(80), AndroidUtilities.dp(86)));
|
view.setLayoutParams(new RecyclerView.LayoutParams(AndroidUtilities.dp(80), AndroidUtilities.dp(86)));
|
||||||
return new RecyclerListView.Holder(view);
|
return new RecyclerListView.Holder(view);
|
||||||
}
|
}
|
||||||
|
@ -371,6 +381,12 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadRecentSearch() {
|
public void loadRecentSearch() {
|
||||||
|
loadRecentSearch(currentAccount, dialogsType, (arrayList, hashMap) -> {
|
||||||
|
DialogsSearchAdapter.this.setRecentSearch(arrayList, hashMap);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadRecentSearch(int currentAccount, int dialogsType, OnRecentSearchLoaded callback) {
|
||||||
MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> {
|
MessagesStorage.getInstance(currentAccount).getStorageQueue().postRunnable(() -> {
|
||||||
try {
|
try {
|
||||||
SQLiteCursor cursor = MessagesStorage.getInstance(currentAccount).getDatabase().queryFinalized("SELECT did, date FROM search_recent WHERE 1");
|
SQLiteCursor cursor = MessagesStorage.getInstance(currentAccount).getDatabase().queryFinalized("SELECT did, date FROM search_recent WHERE 1");
|
||||||
|
@ -465,7 +481,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
AndroidUtilities.runOnUIThread(() -> setRecentSearch(arrayList, hashMap));
|
AndroidUtilities.runOnUIThread(() -> callback.setRecentSearch(arrayList, hashMap));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
FileLog.e(e);
|
FileLog.e(e);
|
||||||
}
|
}
|
||||||
|
@ -934,7 +950,7 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
|
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
|
||||||
horizontalListView.setLayoutManager(layoutManager);
|
horizontalListView.setLayoutManager(layoutManager);
|
||||||
//horizontalListView.setDisallowInterceptTouchEvents(true);
|
//horizontalListView.setDisallowInterceptTouchEvents(true);
|
||||||
horizontalListView.setAdapter(new CategoryAdapterRecycler());
|
horizontalListView.setAdapter(new CategoryAdapterRecycler(mContext, currentAccount, false));
|
||||||
horizontalListView.setOnItemClickListener((view1, position) -> {
|
horizontalListView.setOnItemClickListener((view1, position) -> {
|
||||||
if (delegate != null) {
|
if (delegate != null) {
|
||||||
delegate.didPressedOnSubDialog((Integer) view1.getTag());
|
delegate.didPressedOnSubDialog((Integer) view1.getTag());
|
||||||
|
@ -1223,4 +1239,8 @@ public class DialogsSearchAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
public int getCurrentItemCount() {
|
public int getCurrentItemCount() {
|
||||||
return currentItemCount;
|
return currentItemCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface OnRecentSearchLoaded {
|
||||||
|
void setRecentSearch(ArrayList<RecentSearchObject> arrayList, LongSparseArray<RecentSearchObject> hashMap);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,13 +24,18 @@ import android.widget.TextView;
|
||||||
import org.telegram.messenger.AndroidUtilities;
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
import org.telegram.messenger.ChatObject;
|
import org.telegram.messenger.ChatObject;
|
||||||
import org.telegram.messenger.ContactsController;
|
import org.telegram.messenger.ContactsController;
|
||||||
|
import org.telegram.messenger.Emoji;
|
||||||
|
import org.telegram.messenger.FileLoader;
|
||||||
|
import org.telegram.messenger.ImageLocation;
|
||||||
import org.telegram.messenger.MediaDataController;
|
import org.telegram.messenger.MediaDataController;
|
||||||
import org.telegram.messenger.LocaleController;
|
import org.telegram.messenger.LocaleController;
|
||||||
import org.telegram.messenger.MessageObject;
|
import org.telegram.messenger.MessageObject;
|
||||||
import org.telegram.messenger.MessagesController;
|
import org.telegram.messenger.MessagesController;
|
||||||
import org.telegram.messenger.MessagesStorage;
|
import org.telegram.messenger.MessagesStorage;
|
||||||
|
import org.telegram.messenger.NotificationCenter;
|
||||||
import org.telegram.messenger.R;
|
import org.telegram.messenger.R;
|
||||||
import org.telegram.messenger.SendMessagesHelper;
|
import org.telegram.messenger.SendMessagesHelper;
|
||||||
|
import org.telegram.messenger.SharedConfig;
|
||||||
import org.telegram.messenger.UserConfig;
|
import org.telegram.messenger.UserConfig;
|
||||||
import org.telegram.messenger.UserObject;
|
import org.telegram.messenger.UserObject;
|
||||||
import org.telegram.tgnet.ConnectionsManager;
|
import org.telegram.tgnet.ConnectionsManager;
|
||||||
|
@ -42,9 +47,11 @@ import org.telegram.ui.ActionBar.Theme;
|
||||||
import org.telegram.ui.Cells.BotSwitchCell;
|
import org.telegram.ui.Cells.BotSwitchCell;
|
||||||
import org.telegram.ui.Cells.ContextLinkCell;
|
import org.telegram.ui.Cells.ContextLinkCell;
|
||||||
import org.telegram.ui.Cells.MentionCell;
|
import org.telegram.ui.Cells.MentionCell;
|
||||||
|
import org.telegram.ui.Cells.StickerCell;
|
||||||
import org.telegram.ui.ChatActivity;
|
import org.telegram.ui.ChatActivity;
|
||||||
import org.telegram.ui.Components.RecyclerListView;
|
import org.telegram.ui.Components.RecyclerListView;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -53,7 +60,7 @@ import java.util.HashMap;
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
public class MentionsAdapter extends RecyclerListView.SelectionAdapter implements NotificationCenter.NotificationCenterDelegate {
|
||||||
|
|
||||||
public interface MentionsAdapterDelegate {
|
public interface MentionsAdapterDelegate {
|
||||||
void needChangePanelVisibility(boolean show);
|
void needChangePanelVisibility(boolean show);
|
||||||
|
@ -95,6 +102,8 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
private int channelReqId;
|
private int channelReqId;
|
||||||
private boolean isSearchingMentions;
|
private boolean isSearchingMentions;
|
||||||
|
|
||||||
|
private boolean visibleByStickersSearch;
|
||||||
|
|
||||||
private final static String punctuationsChars = " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\n";
|
private final static String punctuationsChars = " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\n";
|
||||||
|
|
||||||
private Runnable cancelDelayRunnable;
|
private Runnable cancelDelayRunnable;
|
||||||
|
@ -110,8 +119,25 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
private Runnable contextQueryRunnable;
|
private Runnable contextQueryRunnable;
|
||||||
private Location lastKnownLocation;
|
private Location lastKnownLocation;
|
||||||
|
|
||||||
|
private ArrayList<StickerResult> stickers;
|
||||||
|
private HashMap<String, TLRPC.Document> stickersMap;
|
||||||
|
private ArrayList<String> stickersToLoad = new ArrayList<>();
|
||||||
|
private String lastSticker;
|
||||||
|
private int lastReqId;
|
||||||
|
private boolean delayLocalResults;
|
||||||
|
|
||||||
private ChatActivity parentFragment;
|
private ChatActivity parentFragment;
|
||||||
|
|
||||||
|
private static class StickerResult {
|
||||||
|
public TLRPC.Document sticker;
|
||||||
|
public Object parent;
|
||||||
|
|
||||||
|
public StickerResult(TLRPC.Document s, Object p) {
|
||||||
|
sticker = s;
|
||||||
|
parent = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private SendMessagesHelper.LocationProvider locationProvider = new SendMessagesHelper.LocationProvider(new SendMessagesHelper.LocationProvider.LocationProviderDelegate() {
|
private SendMessagesHelper.LocationProvider locationProvider = new SendMessagesHelper.LocationProvider(new SendMessagesHelper.LocationProvider.LocationProviderDelegate() {
|
||||||
@Override
|
@Override
|
||||||
public void onLocationAcquired(Location location) {
|
public void onLocationAcquired(Location location) {
|
||||||
|
@ -152,6 +178,134 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (!darkTheme) {
|
||||||
|
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileLoaded);
|
||||||
|
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileLoadFailed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void didReceivedNotification(int id, int account, final Object... args) {
|
||||||
|
if (id == NotificationCenter.fileLoaded || id == NotificationCenter.fileLoadFailed) {
|
||||||
|
if (stickers != null && !stickers.isEmpty() && !stickersToLoad.isEmpty() && visibleByStickersSearch) {
|
||||||
|
String fileName = (String) args[0];
|
||||||
|
stickersToLoad.remove(fileName);
|
||||||
|
if (stickersToLoad.isEmpty()) {
|
||||||
|
delegate.needChangePanelVisibility(stickers != null && !stickers.isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addStickerToResult(TLRPC.Document document, Object parent) {
|
||||||
|
if (document == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String key = document.dc_id + "_" + document.id;
|
||||||
|
if (stickersMap != null && stickersMap.containsKey(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stickers == null) {
|
||||||
|
stickers = new ArrayList<>();
|
||||||
|
stickersMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
stickers.add(new StickerResult(document, parent));
|
||||||
|
stickersMap.put(key, document);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addStickersToResult(ArrayList<TLRPC.Document> documents, Object parent) {
|
||||||
|
if (documents == null || documents.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int a = 0, size = documents.size(); a < size; a++) {
|
||||||
|
TLRPC.Document document = documents.get(a);
|
||||||
|
String key = document.dc_id + "_" + document.id;
|
||||||
|
if (stickersMap != null && stickersMap.containsKey(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (stickers == null) {
|
||||||
|
stickers = new ArrayList<>();
|
||||||
|
stickersMap = new HashMap<>();
|
||||||
|
}
|
||||||
|
for (int b = 0, size2 = document.attributes.size(); b < size2; b++) {
|
||||||
|
TLRPC.DocumentAttribute attribute = document.attributes.get(b);
|
||||||
|
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
|
||||||
|
parent = attribute.stickerset;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stickers.add(new StickerResult(document, parent));
|
||||||
|
stickersMap.put(key, document);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkStickerFilesExistAndDownload() {
|
||||||
|
if (stickers == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
stickersToLoad.clear();
|
||||||
|
int size = Math.min(6, stickers.size());
|
||||||
|
for (int a = 0; a < size; a++) {
|
||||||
|
StickerResult result = stickers.get(a);
|
||||||
|
TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(result.sticker.thumbs, 90);
|
||||||
|
if (thumb instanceof TLRPC.TL_photoSize || thumb instanceof TLRPC.TL_photoSizeProgressive) {
|
||||||
|
File f = FileLoader.getPathToAttach(thumb, "webp", true);
|
||||||
|
if (!f.exists()) {
|
||||||
|
stickersToLoad.add(FileLoader.getAttachFileName(thumb, "webp"));
|
||||||
|
FileLoader.getInstance(currentAccount).loadFile(ImageLocation.getForDocument(thumb, result.sticker), result.parent, "webp", 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stickersToLoad.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidSticker(TLRPC.Document document, String emoji) {
|
||||||
|
for (int b = 0, size2 = document.attributes.size(); b < size2; b++) {
|
||||||
|
TLRPC.DocumentAttribute attribute = document.attributes.get(b);
|
||||||
|
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
|
||||||
|
if (attribute.alt != null && attribute.alt.contains(emoji)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void searchServerStickers(final String emoji, final String originalEmoji) {
|
||||||
|
TLRPC.TL_messages_getStickers req = new TLRPC.TL_messages_getStickers();
|
||||||
|
req.emoticon = originalEmoji;
|
||||||
|
req.hash = 0;
|
||||||
|
lastReqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
|
||||||
|
lastReqId = 0;
|
||||||
|
if (!emoji.equals(lastSticker) || !(response instanceof TLRPC.TL_messages_stickers)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delayLocalResults = false;
|
||||||
|
TLRPC.TL_messages_stickers res = (TLRPC.TL_messages_stickers) response;
|
||||||
|
int oldCount = stickers != null ? stickers.size() : 0;
|
||||||
|
addStickersToResult(res.stickers, "sticker_search_" + emoji);
|
||||||
|
int newCount = stickers != null ? stickers.size() : 0;
|
||||||
|
if (!visibleByStickersSearch && stickers != null && !stickers.isEmpty()) {
|
||||||
|
checkStickerFilesExistAndDownload();
|
||||||
|
delegate.needChangePanelVisibility(stickersToLoad.isEmpty());
|
||||||
|
visibleByStickersSearch = true;
|
||||||
|
}
|
||||||
|
if (oldCount != newCount) {
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearStickers() {
|
||||||
|
lastSticker = null;
|
||||||
|
stickers = null;
|
||||||
|
stickersMap = null;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
if (lastReqId != 0) {
|
||||||
|
ConnectionsManager.getInstance(currentAccount).cancelRequest(lastReqId, true);
|
||||||
|
lastReqId = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
|
@ -175,6 +329,10 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
searchingContextUsername = null;
|
searchingContextUsername = null;
|
||||||
searchingContextQuery = null;
|
searchingContextQuery = null;
|
||||||
noUserName = false;
|
noUserName = false;
|
||||||
|
if (!isDarkTheme) {
|
||||||
|
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileLoaded);
|
||||||
|
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileLoadFailed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setParentFragment(ChatActivity fragment) {
|
public void setParentFragment(ChatActivity fragment) {
|
||||||
|
@ -510,6 +668,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
cancelDelayRunnable = null;
|
cancelDelayRunnable = null;
|
||||||
}
|
}
|
||||||
searchResultHashtags = null;
|
searchResultHashtags = null;
|
||||||
|
stickers = null;
|
||||||
searchResultUsernames = null;
|
searchResultUsernames = null;
|
||||||
searchResultUsernamesMap = null;
|
searchResultUsernamesMap = null;
|
||||||
searchResultCommands = null;
|
searchResultCommands = null;
|
||||||
|
@ -568,6 +727,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
searchForContextBot(null, null);
|
searchForContextBot(null, null);
|
||||||
delegate.needChangePanelVisibility(false);
|
delegate.needChangePanelVisibility(false);
|
||||||
lastText = null;
|
lastText = null;
|
||||||
|
clearStickers();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int searchPostion = position;
|
int searchPostion = position;
|
||||||
|
@ -579,7 +739,136 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
lastForSearch = forSearch;
|
lastForSearch = forSearch;
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
int foundType = -1;
|
int foundType = -1;
|
||||||
if (!usernameOnly && needBotContext && text.charAt(0) == '@') {
|
|
||||||
|
boolean searchEmoji = !usernameOnly && text != null && text.length() > 0 && text.length() <= 14;
|
||||||
|
String originalEmoji = "";
|
||||||
|
if (searchEmoji) {
|
||||||
|
CharSequence emoji = originalEmoji = text;
|
||||||
|
int length = emoji.length();
|
||||||
|
for (int a = 0; a < length; a++) {
|
||||||
|
char ch = emoji.charAt(a);
|
||||||
|
char nch = a < length - 1 ? emoji.charAt(a + 1) : 0;
|
||||||
|
if (a < length - 1 && ch == 0xD83C && nch >= 0xDFFB && nch <= 0xDFFF) {
|
||||||
|
emoji = TextUtils.concat(emoji.subSequence(0, a), emoji.subSequence(a + 2, emoji.length()));
|
||||||
|
length -= 2;
|
||||||
|
a--;
|
||||||
|
} else if (ch == 0xfe0f) {
|
||||||
|
emoji = TextUtils.concat(emoji.subSequence(0, a), emoji.subSequence(a + 1, emoji.length()));
|
||||||
|
length--;
|
||||||
|
a--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastSticker = emoji.toString().trim();
|
||||||
|
}
|
||||||
|
boolean isValidEmoji = searchEmoji && (Emoji.isValidEmoji(originalEmoji) || Emoji.isValidEmoji(lastSticker));
|
||||||
|
|
||||||
|
if (isValidEmoji && parentFragment != null && (parentFragment.getCurrentChat() == null || ChatObject.canSendStickers(parentFragment.getCurrentChat()))) {
|
||||||
|
stickersToLoad.clear();
|
||||||
|
if (SharedConfig.suggestStickers == 2 || !isValidEmoji) {
|
||||||
|
if (visibleByStickersSearch && SharedConfig.suggestStickers == 2) {
|
||||||
|
visibleByStickersSearch = false;
|
||||||
|
delegate.needChangePanelVisibility(false);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stickers = null;
|
||||||
|
stickersMap = null;
|
||||||
|
foundType = 4;
|
||||||
|
if (lastReqId != 0) {
|
||||||
|
ConnectionsManager.getInstance(currentAccount).cancelRequest(lastReqId, true);
|
||||||
|
lastReqId = 0;
|
||||||
|
}
|
||||||
|
boolean serverStickersOnly = MessagesController.getInstance(currentAccount).suggestStickersApiOnly;
|
||||||
|
|
||||||
|
delayLocalResults = false;
|
||||||
|
if (!serverStickersOnly) {
|
||||||
|
final ArrayList<TLRPC.Document> recentStickers = MediaDataController.getInstance(currentAccount).getRecentStickersNoCopy(MediaDataController.TYPE_IMAGE);
|
||||||
|
final ArrayList<TLRPC.Document> favsStickers = MediaDataController.getInstance(currentAccount).getRecentStickersNoCopy(MediaDataController.TYPE_FAVE);
|
||||||
|
int recentsAdded = 0;
|
||||||
|
for (int a = 0, size = Math.min(20, recentStickers.size()); a < size; a++) {
|
||||||
|
TLRPC.Document document = recentStickers.get(a);
|
||||||
|
if (isValidSticker(document, lastSticker)) {
|
||||||
|
addStickerToResult(document, "recent");
|
||||||
|
recentsAdded++;
|
||||||
|
if (recentsAdded >= 5) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int a = 0, size = favsStickers.size(); a < size; a++) {
|
||||||
|
TLRPC.Document document = favsStickers.get(a);
|
||||||
|
if (isValidSticker(document, lastSticker)) {
|
||||||
|
addStickerToResult(document, "fav");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap<String, ArrayList<TLRPC.Document>> allStickers = MediaDataController.getInstance(currentAccount).getAllStickers();
|
||||||
|
ArrayList<TLRPC.Document> newStickers = allStickers != null ? allStickers.get(lastSticker) : null;
|
||||||
|
if (newStickers != null && !newStickers.isEmpty()) {
|
||||||
|
addStickersToResult(newStickers, null);
|
||||||
|
}
|
||||||
|
if (stickers != null) {
|
||||||
|
Collections.sort(stickers, new Comparator<StickerResult>() {
|
||||||
|
private int getIndex(StickerResult result) {
|
||||||
|
for (int a = 0; a < favsStickers.size(); a++) {
|
||||||
|
if (favsStickers.get(a).id == result.sticker.id) {
|
||||||
|
return a + 2000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int a = 0; a < Math.min(20, recentStickers.size()); a++) {
|
||||||
|
if (recentStickers.get(a).id == result.sticker.id) {
|
||||||
|
return recentStickers.size() - a + 1000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(StickerResult lhs, StickerResult rhs) {
|
||||||
|
boolean isAnimated1 = MessageObject.isAnimatedStickerDocument(lhs.sticker, true);
|
||||||
|
boolean isAnimated2 = MessageObject.isAnimatedStickerDocument(rhs.sticker, true);
|
||||||
|
if (isAnimated1 == isAnimated2) {
|
||||||
|
int idx1 = getIndex(lhs);
|
||||||
|
int idx2 = getIndex(rhs);
|
||||||
|
if (idx1 > idx2) {
|
||||||
|
return -1;
|
||||||
|
} else if (idx1 < idx2) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
if (isAnimated1) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SharedConfig.suggestStickers == 0 || serverStickersOnly) {
|
||||||
|
searchServerStickers(lastSticker, originalEmoji);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stickers != null && !stickers.isEmpty()) {
|
||||||
|
if (SharedConfig.suggestStickers == 0 && stickers.size() < 5) {
|
||||||
|
delayLocalResults = true;
|
||||||
|
delegate.needChangePanelVisibility(false);
|
||||||
|
visibleByStickersSearch = false;
|
||||||
|
} else {
|
||||||
|
checkStickerFilesExistAndDownload();
|
||||||
|
boolean show = stickersToLoad.isEmpty();
|
||||||
|
delegate.needChangePanelVisibility(show);
|
||||||
|
visibleByStickersSearch = true;
|
||||||
|
}
|
||||||
|
notifyDataSetChanged();
|
||||||
|
} else if (visibleByStickersSearch) {
|
||||||
|
delegate.needChangePanelVisibility(false);
|
||||||
|
visibleByStickersSearch = false;
|
||||||
|
}
|
||||||
|
} else if (!usernameOnly && needBotContext && text.charAt(0) == '@') {
|
||||||
int index = text.indexOf(' ');
|
int index = text.indexOf(' ');
|
||||||
int len = text.length();
|
int len = text.length();
|
||||||
String username = null;
|
String username = null;
|
||||||
|
@ -801,6 +1090,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
searchResultHashtags = null;
|
searchResultHashtags = null;
|
||||||
|
stickers = null;
|
||||||
searchResultCommands = null;
|
searchResultCommands = null;
|
||||||
searchResultCommandsHelp = null;
|
searchResultCommandsHelp = null;
|
||||||
searchResultCommandsUsers = null;
|
searchResultCommandsUsers = null;
|
||||||
|
@ -887,6 +1177,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
searchResultHashtags = newResult;
|
searchResultHashtags = newResult;
|
||||||
|
stickers = null;
|
||||||
searchResultUsernames = null;
|
searchResultUsernames = null;
|
||||||
searchResultUsernamesMap = null;
|
searchResultUsernamesMap = null;
|
||||||
searchResultCommands = null;
|
searchResultCommands = null;
|
||||||
|
@ -912,6 +1203,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
searchResultHashtags = null;
|
searchResultHashtags = null;
|
||||||
|
stickers = null;
|
||||||
searchResultUsernames = null;
|
searchResultUsernames = null;
|
||||||
searchResultUsernamesMap = null;
|
searchResultUsernamesMap = null;
|
||||||
searchResultSuggestions = null;
|
searchResultSuggestions = null;
|
||||||
|
@ -929,6 +1221,7 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
MediaDataController.getInstance(currentAccount).getEmojiSuggestions(lastSearchKeyboardLanguage, result.toString(), false, (param, alias) -> {
|
MediaDataController.getInstance(currentAccount).getEmojiSuggestions(lastSearchKeyboardLanguage, result.toString(), false, (param, alias) -> {
|
||||||
searchResultSuggestions = param;
|
searchResultSuggestions = param;
|
||||||
searchResultHashtags = null;
|
searchResultHashtags = null;
|
||||||
|
stickers = null;
|
||||||
searchResultUsernames = null;
|
searchResultUsernames = null;
|
||||||
searchResultUsernamesMap = null;
|
searchResultUsernamesMap = null;
|
||||||
searchResultCommands = null;
|
searchResultCommands = null;
|
||||||
|
@ -937,6 +1230,14 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
delegate.needChangePanelVisibility(searchResultSuggestions != null && !searchResultSuggestions.isEmpty());
|
delegate.needChangePanelVisibility(searchResultSuggestions != null && !searchResultSuggestions.isEmpty());
|
||||||
});
|
});
|
||||||
|
} else if (foundType == 4) {
|
||||||
|
searchResultHashtags = null;
|
||||||
|
searchResultUsernames = null;
|
||||||
|
searchResultUsernamesMap = null;
|
||||||
|
searchResultSuggestions = null;
|
||||||
|
searchResultCommands = null;
|
||||||
|
searchResultCommandsHelp = null;
|
||||||
|
searchResultCommandsUsers = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -970,7 +1271,9 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
if (foundContextBot != null && !inlineMediaEnabled) {
|
if (foundContextBot != null && !inlineMediaEnabled) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (searchResultBotContext != null) {
|
if (stickers != null) {
|
||||||
|
return stickers.size();
|
||||||
|
}else if (searchResultBotContext != null) {
|
||||||
return searchResultBotContext.size() + (searchResultBotContextSwitch != null ? 1 : 0);
|
return searchResultBotContext.size() + (searchResultBotContextSwitch != null ? 1 : 0);
|
||||||
} else if (searchResultUsernames != null) {
|
} else if (searchResultUsernames != null) {
|
||||||
return searchResultUsernames.size();
|
return searchResultUsernames.size();
|
||||||
|
@ -986,7 +1289,9 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemViewType(int position) {
|
public int getItemViewType(int position) {
|
||||||
if (foundContextBot != null && !inlineMediaEnabled) {
|
if (stickers != null) {
|
||||||
|
return 4;
|
||||||
|
} else if (foundContextBot != null && !inlineMediaEnabled) {
|
||||||
return 3;
|
return 3;
|
||||||
} else if (searchResultBotContext != null) {
|
} else if (searchResultBotContext != null) {
|
||||||
if (position == 0 && searchResultBotContextSwitch != null) {
|
if (position == 0 && searchResultBotContextSwitch != null) {
|
||||||
|
@ -1009,8 +1314,14 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getItemParent(int i) {
|
||||||
|
return stickers != null && i >= 0 && i < stickers.size() ? stickers.get(i).parent : null;
|
||||||
|
}
|
||||||
|
|
||||||
public Object getItem(int i) {
|
public Object getItem(int i) {
|
||||||
if (searchResultBotContext != null) {
|
if (stickers != null) {
|
||||||
|
return i >= 0 && i < stickers.size() ? stickers.get(i).sticker : null;
|
||||||
|
} else if (searchResultBotContext != null) {
|
||||||
if (searchResultBotContextSwitch != null) {
|
if (searchResultBotContextSwitch != null) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
return searchResultBotContextSwitch;
|
return searchResultBotContextSwitch;
|
||||||
|
@ -1061,6 +1372,10 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
return searchResultCommands != null;
|
return searchResultCommands != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isStickers() {
|
||||||
|
return stickers != null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isBotContext() {
|
public boolean isBotContext() {
|
||||||
return searchResultBotContext != null;
|
return searchResultBotContext != null;
|
||||||
}
|
}
|
||||||
|
@ -1070,12 +1385,12 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMediaLayout() {
|
public boolean isMediaLayout() {
|
||||||
return contextMedia;
|
return contextMedia || stickers != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled(RecyclerView.ViewHolder holder) {
|
public boolean isEnabled(RecyclerView.ViewHolder holder) {
|
||||||
return foundContextBot == null || inlineMediaEnabled;
|
return (foundContextBot == null || inlineMediaEnabled) && stickers == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1094,20 +1409,29 @@ public class MentionsAdapter extends RecyclerListView.SelectionAdapter {
|
||||||
view = new BotSwitchCell(mContext);
|
view = new BotSwitchCell(mContext);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
default:
|
|
||||||
TextView textView = new TextView(mContext);
|
TextView textView = new TextView(mContext);
|
||||||
textView.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8));
|
textView.setPadding(AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8), AndroidUtilities.dp(8));
|
||||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||||
textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2));
|
textView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2));
|
||||||
view = textView;
|
view = textView;
|
||||||
break;
|
break;
|
||||||
|
case 4:
|
||||||
|
default:
|
||||||
|
view = new StickerCell(mContext);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return new RecyclerListView.Holder(view);
|
return new RecyclerListView.Holder(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||||
if (holder.getItemViewType() == 3) {
|
int type = holder.getItemViewType();
|
||||||
|
if (type == 4) {
|
||||||
|
StickerCell stickerCell = (StickerCell) holder.itemView;
|
||||||
|
StickerResult result = stickers.get(position);
|
||||||
|
stickerCell.setSticker(result.sticker, result.parent);
|
||||||
|
stickerCell.setClearsInputField(true);
|
||||||
|
} else if (type == 3) {
|
||||||
TextView textView = (TextView) holder.itemView;
|
TextView textView = (TextView) holder.itemView;
|
||||||
TLRPC.Chat chat = parentFragment.getCurrentChat();
|
TLRPC.Chat chat = parentFragment.getCurrentChat();
|
||||||
if (chat != null) {
|
if (chat != null) {
|
||||||
|
|
|
@ -10,57 +10,36 @@ package org.telegram.ui.Adapters;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.telegram.messenger.AndroidUtilities;
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
import org.telegram.messenger.MediaDataController;
|
import org.telegram.messenger.MediaDataController;
|
||||||
import org.telegram.messenger.Emoji;
|
import org.telegram.messenger.Emoji;
|
||||||
import org.telegram.messenger.ImageLocation;
|
import org.telegram.messenger.ImageLocation;
|
||||||
import org.telegram.messenger.MessageObject;
|
|
||||||
import org.telegram.messenger.MessagesController;
|
|
||||||
import org.telegram.messenger.NotificationCenter;
|
import org.telegram.messenger.NotificationCenter;
|
||||||
import org.telegram.messenger.SharedConfig;
|
|
||||||
import org.telegram.messenger.UserConfig;
|
import org.telegram.messenger.UserConfig;
|
||||||
import org.telegram.messenger.FileLoader;
|
import org.telegram.messenger.FileLoader;
|
||||||
import org.telegram.tgnet.ConnectionsManager;
|
|
||||||
import org.telegram.tgnet.TLRPC;
|
import org.telegram.tgnet.TLRPC;
|
||||||
import org.telegram.ui.Cells.EmojiReplacementCell;
|
import org.telegram.ui.Cells.EmojiReplacementCell;
|
||||||
import org.telegram.ui.Cells.StickerCell;
|
|
||||||
import org.telegram.ui.Components.RecyclerListView;
|
import org.telegram.ui.Components.RecyclerListView;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
public class StickersAdapter extends RecyclerListView.SelectionAdapter implements NotificationCenter.NotificationCenterDelegate {
|
public class StickersAdapter extends RecyclerListView.SelectionAdapter implements NotificationCenter.NotificationCenterDelegate {
|
||||||
|
|
||||||
private static class StickerResult {
|
|
||||||
public TLRPC.Document sticker;
|
|
||||||
public Object parent;
|
|
||||||
|
|
||||||
public StickerResult(TLRPC.Document s, Object p) {
|
|
||||||
sticker = s;
|
|
||||||
parent = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int currentAccount = UserConfig.selectedAccount;
|
private int currentAccount = UserConfig.selectedAccount;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private ArrayList<MediaDataController.KeywordResult> keywordResults;
|
private ArrayList<MediaDataController.KeywordResult> keywordResults;
|
||||||
private ArrayList<StickerResult> stickers;
|
|
||||||
private HashMap<String, TLRPC.Document> stickersMap;
|
|
||||||
private ArrayList<String> stickersToLoad = new ArrayList<>();
|
|
||||||
private StickersAdapterDelegate delegate;
|
private StickersAdapterDelegate delegate;
|
||||||
private String lastSticker;
|
|
||||||
private boolean visible;
|
private boolean visible;
|
||||||
private int lastReqId;
|
|
||||||
private boolean delayLocalResults;
|
private String lastSearch;
|
||||||
|
|
||||||
private String[] lastSearchKeyboardLanguage;
|
private String[] lastSearchKeyboardLanguage;
|
||||||
private Runnable searchRunnable;
|
private Runnable searchRunnable;
|
||||||
|
|
||||||
|
@ -74,114 +53,23 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement
|
||||||
MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_IMAGE);
|
MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_IMAGE);
|
||||||
MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_MASK);
|
MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_MASK);
|
||||||
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.newEmojiSuggestionsAvailable);
|
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.newEmojiSuggestionsAvailable);
|
||||||
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileLoaded);
|
|
||||||
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.fileLoadFailed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.newEmojiSuggestionsAvailable);
|
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.newEmojiSuggestionsAvailable);
|
||||||
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileLoaded);
|
|
||||||
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.fileLoadFailed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void didReceivedNotification(int id, int account, final Object... args) {
|
public void didReceivedNotification(int id, int account, final Object... args) {
|
||||||
if (id == NotificationCenter.fileLoaded || id == NotificationCenter.fileLoadFailed) {
|
if (id == NotificationCenter.newEmojiSuggestionsAvailable) {
|
||||||
if (stickers != null && !stickers.isEmpty() && !stickersToLoad.isEmpty() && visible) {
|
if ((keywordResults == null || keywordResults.isEmpty()) && !TextUtils.isEmpty(lastSearch) && getItemCount() == 0) {
|
||||||
String fileName = (String) args[0];
|
|
||||||
stickersToLoad.remove(fileName);
|
|
||||||
if (stickersToLoad.isEmpty()) {
|
|
||||||
boolean show = stickers != null && !stickers.isEmpty();
|
|
||||||
if (show) {
|
|
||||||
keywordResults = null;
|
|
||||||
}
|
|
||||||
delegate.needChangePanelVisibility(show);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (id == NotificationCenter.newEmojiSuggestionsAvailable) {
|
|
||||||
if ((keywordResults == null || keywordResults.isEmpty()) && !TextUtils.isEmpty(lastSticker) && getItemCount() == 0) {
|
|
||||||
searchEmojiByKeyword();
|
searchEmojiByKeyword();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkStickerFilesExistAndDownload() {
|
|
||||||
if (stickers == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
stickersToLoad.clear();
|
|
||||||
int size = Math.min(6, stickers.size());
|
|
||||||
for (int a = 0; a < size; a++) {
|
|
||||||
StickerResult result = stickers.get(a);
|
|
||||||
TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(result.sticker.thumbs, 90);
|
|
||||||
if (thumb instanceof TLRPC.TL_photoSize || thumb instanceof TLRPC.TL_photoSizeProgressive) {
|
|
||||||
File f = FileLoader.getPathToAttach(thumb, "webp", true);
|
|
||||||
if (!f.exists()) {
|
|
||||||
stickersToLoad.add(FileLoader.getAttachFileName(thumb, "webp"));
|
|
||||||
FileLoader.getInstance(currentAccount).loadFile(ImageLocation.getForDocument(thumb, result.sticker), result.parent, "webp", 1, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stickersToLoad.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isValidSticker(TLRPC.Document document, String emoji) {
|
|
||||||
for (int b = 0, size2 = document.attributes.size(); b < size2; b++) {
|
|
||||||
TLRPC.DocumentAttribute attribute = document.attributes.get(b);
|
|
||||||
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
|
|
||||||
if (attribute.alt != null && attribute.alt.contains(emoji)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addStickerToResult(TLRPC.Document document, Object parent) {
|
|
||||||
if (document == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String key = document.dc_id + "_" + document.id;
|
|
||||||
if (stickersMap != null && stickersMap.containsKey(key)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (stickers == null) {
|
|
||||||
stickers = new ArrayList<>();
|
|
||||||
stickersMap = new HashMap<>();
|
|
||||||
}
|
|
||||||
stickers.add(new StickerResult(document, parent));
|
|
||||||
stickersMap.put(key, document);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addStickersToResult(ArrayList<TLRPC.Document> documents, Object parent) {
|
|
||||||
if (documents == null || documents.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int a = 0, size = documents.size(); a < size; a++) {
|
|
||||||
TLRPC.Document document = documents.get(a);
|
|
||||||
String key = document.dc_id + "_" + document.id;
|
|
||||||
if (stickersMap != null && stickersMap.containsKey(key)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (stickers == null) {
|
|
||||||
stickers = new ArrayList<>();
|
|
||||||
stickersMap = new HashMap<>();
|
|
||||||
}
|
|
||||||
for (int b = 0, size2 = document.attributes.size(); b < size2; b++) {
|
|
||||||
TLRPC.DocumentAttribute attribute = document.attributes.get(b);
|
|
||||||
if (attribute instanceof TLRPC.TL_documentAttributeSticker) {
|
|
||||||
parent = attribute.stickerset;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stickers.add(new StickerResult(document, parent));
|
|
||||||
stickersMap.put(key, document);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void hide() {
|
public void hide() {
|
||||||
if (visible && (stickers != null || keywordResults != null && !keywordResults.isEmpty())) {
|
if (visible && keywordResults != null && !keywordResults.isEmpty()) {
|
||||||
visible = false;
|
visible = false;
|
||||||
delegate.needChangePanelVisibility(false);
|
delegate.needChangePanelVisibility(false);
|
||||||
}
|
}
|
||||||
|
@ -200,10 +88,10 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement
|
||||||
MediaDataController.getInstance(currentAccount).fetchNewEmojiKeywords(newLanguage);
|
MediaDataController.getInstance(currentAccount).fetchNewEmojiKeywords(newLanguage);
|
||||||
}
|
}
|
||||||
lastSearchKeyboardLanguage = newLanguage;
|
lastSearchKeyboardLanguage = newLanguage;
|
||||||
String query = lastSticker;
|
String query = lastSearch;
|
||||||
cancelEmojiSearch();
|
cancelEmojiSearch();
|
||||||
searchRunnable = () -> MediaDataController.getInstance(currentAccount).getEmojiSuggestions(lastSearchKeyboardLanguage, query, true, (param, alias) -> {
|
searchRunnable = () -> MediaDataController.getInstance(currentAccount).getEmojiSuggestions(lastSearchKeyboardLanguage, query, true, (param, alias) -> {
|
||||||
if (query.equals(lastSticker)) {
|
if (query.equals(lastSearch)) {
|
||||||
if (!param.isEmpty()) {
|
if (!param.isEmpty()) {
|
||||||
keywordResults = param;
|
keywordResults = param;
|
||||||
}
|
}
|
||||||
|
@ -218,7 +106,7 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadStikersForEmoji(CharSequence emoji, boolean emojiOnly) {
|
public void searchEmojiByKeyword(CharSequence emoji) {
|
||||||
boolean searchEmoji = emoji != null && emoji.length() > 0 && emoji.length() <= 14;
|
boolean searchEmoji = emoji != null && emoji.length() > 0 && emoji.length() <= 14;
|
||||||
|
|
||||||
String originalEmoji = "";
|
String originalEmoji = "";
|
||||||
|
@ -239,9 +127,8 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastSticker = emoji.toString().trim();
|
lastSearch = emoji.toString().trim();
|
||||||
stickersToLoad.clear();
|
boolean isValidEmoji = searchEmoji && (Emoji.isValidEmoji(originalEmoji) || Emoji.isValidEmoji(lastSearch));
|
||||||
boolean isValidEmoji = searchEmoji && (Emoji.isValidEmoji(originalEmoji) || Emoji.isValidEmoji(lastSticker));
|
|
||||||
if (isValidEmoji) {
|
if (isValidEmoji) {
|
||||||
TLRPC.Document animatedSticker = MediaDataController.getInstance(currentAccount).getEmojiAnimatedSticker(emoji);
|
TLRPC.Document animatedSticker = MediaDataController.getInstance(currentAccount).getEmojiAnimatedSticker(emoji);
|
||||||
if (animatedSticker != null) {
|
if (animatedSticker != null) {
|
||||||
|
@ -252,166 +139,28 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (emojiOnly || SharedConfig.suggestStickers == 2 || !isValidEmoji) {
|
|
||||||
if (visible && (emojiOnly || SharedConfig.suggestStickers == 2 || keywordResults == null || keywordResults.isEmpty())) {
|
|
||||||
visible = false;
|
|
||||||
delegate.needChangePanelVisibility(false);
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
if (!isValidEmoji) {
|
|
||||||
searchEmojiByKeyword();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cancelEmojiSearch();
|
|
||||||
stickers = null;
|
|
||||||
stickersMap = null;
|
|
||||||
if (lastReqId != 0) {
|
|
||||||
ConnectionsManager.getInstance(currentAccount).cancelRequest(lastReqId, true);
|
|
||||||
lastReqId = 0;
|
|
||||||
}
|
|
||||||
boolean serverStickersOnly = MessagesController.getInstance(currentAccount).suggestStickersApiOnly;
|
|
||||||
|
|
||||||
delayLocalResults = false;
|
if (visible && (keywordResults == null || keywordResults.isEmpty())) {
|
||||||
if (!serverStickersOnly) {
|
|
||||||
final ArrayList<TLRPC.Document> recentStickers = MediaDataController.getInstance(currentAccount).getRecentStickersNoCopy(MediaDataController.TYPE_IMAGE);
|
|
||||||
final ArrayList<TLRPC.Document> favsStickers = MediaDataController.getInstance(currentAccount).getRecentStickersNoCopy(MediaDataController.TYPE_FAVE);
|
|
||||||
int recentsAdded = 0;
|
|
||||||
for (int a = 0, size = Math.min(20, recentStickers.size()); a < size; a++) {
|
|
||||||
TLRPC.Document document = recentStickers.get(a);
|
|
||||||
if (isValidSticker(document, lastSticker)) {
|
|
||||||
addStickerToResult(document, "recent");
|
|
||||||
recentsAdded++;
|
|
||||||
if (recentsAdded >= 5) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int a = 0, size = favsStickers.size(); a < size; a++) {
|
|
||||||
TLRPC.Document document = favsStickers.get(a);
|
|
||||||
if (isValidSticker(document, lastSticker)) {
|
|
||||||
addStickerToResult(document, "fav");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HashMap<String, ArrayList<TLRPC.Document>> allStickers = MediaDataController.getInstance(currentAccount).getAllStickers();
|
|
||||||
ArrayList<TLRPC.Document> newStickers = allStickers != null ? allStickers.get(lastSticker) : null;
|
|
||||||
if (newStickers != null && !newStickers.isEmpty()) {
|
|
||||||
addStickersToResult(newStickers, null);
|
|
||||||
}
|
|
||||||
if (stickers != null) {
|
|
||||||
Collections.sort(stickers, new Comparator<StickerResult>() {
|
|
||||||
private int getIndex(StickerResult result) {
|
|
||||||
for (int a = 0; a < favsStickers.size(); a++) {
|
|
||||||
if (favsStickers.get(a).id == result.sticker.id) {
|
|
||||||
return a + 2000000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int a = 0; a < Math.min(20, recentStickers.size()); a++) {
|
|
||||||
if (recentStickers.get(a).id == result.sticker.id) {
|
|
||||||
return recentStickers.size() - a + 1000000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(StickerResult lhs, StickerResult rhs) {
|
|
||||||
boolean isAnimated1 = MessageObject.isAnimatedStickerDocument(lhs.sticker, true);
|
|
||||||
boolean isAnimated2 = MessageObject.isAnimatedStickerDocument(rhs.sticker, true);
|
|
||||||
if (isAnimated1 == isAnimated2) {
|
|
||||||
int idx1 = getIndex(lhs);
|
|
||||||
int idx2 = getIndex(rhs);
|
|
||||||
if (idx1 > idx2) {
|
|
||||||
return -1;
|
|
||||||
} else if (idx1 < idx2) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
if (isAnimated1) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (SharedConfig.suggestStickers == 0 || serverStickersOnly) {
|
|
||||||
searchServerStickers(lastSticker, originalEmoji);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stickers != null && !stickers.isEmpty()) {
|
|
||||||
if (SharedConfig.suggestStickers == 0 && stickers.size() < 5) {
|
|
||||||
delayLocalResults = true;
|
|
||||||
delegate.needChangePanelVisibility(false);
|
|
||||||
visible = false;
|
|
||||||
} else {
|
|
||||||
checkStickerFilesExistAndDownload();
|
|
||||||
boolean show = stickersToLoad.isEmpty();
|
|
||||||
if (show) {
|
|
||||||
keywordResults = null;
|
|
||||||
}
|
|
||||||
delegate.needChangePanelVisibility(show);
|
|
||||||
visible = true;
|
|
||||||
}
|
|
||||||
notifyDataSetChanged();
|
|
||||||
} else if (visible) {
|
|
||||||
delegate.needChangePanelVisibility(false);
|
|
||||||
visible = false;
|
visible = false;
|
||||||
|
delegate.needChangePanelVisibility(false);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
if (!isValidEmoji) {
|
||||||
|
searchEmojiByKeyword();
|
||||||
|
} else {
|
||||||
|
clearSearch();
|
||||||
|
delegate.needChangePanelVisibility(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void searchServerStickers(final String emoji, final String originalEmoji) {
|
public void clearSearch() {
|
||||||
TLRPC.TL_messages_getStickers req = new TLRPC.TL_messages_getStickers();
|
lastSearch = null;
|
||||||
req.emoticon = originalEmoji;
|
|
||||||
req.hash = 0;
|
|
||||||
lastReqId = ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> AndroidUtilities.runOnUIThread(() -> {
|
|
||||||
lastReqId = 0;
|
|
||||||
if (!emoji.equals(lastSticker) || !(response instanceof TLRPC.TL_messages_stickers)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
delayLocalResults = false;
|
|
||||||
TLRPC.TL_messages_stickers res = (TLRPC.TL_messages_stickers) response;
|
|
||||||
int oldCount = stickers != null ? stickers.size() : 0;
|
|
||||||
addStickersToResult(res.stickers, "sticker_search_" + emoji);
|
|
||||||
int newCount = stickers != null ? stickers.size() : 0;
|
|
||||||
if (!visible && stickers != null && !stickers.isEmpty()) {
|
|
||||||
checkStickerFilesExistAndDownload();
|
|
||||||
boolean show = stickersToLoad.isEmpty();
|
|
||||||
if (show) {
|
|
||||||
keywordResults = null;
|
|
||||||
}
|
|
||||||
delegate.needChangePanelVisibility(show);
|
|
||||||
visible = true;
|
|
||||||
}
|
|
||||||
if (oldCount != newCount) {
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearStickers() {
|
|
||||||
if (delayLocalResults || lastReqId != 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (stickersToLoad.isEmpty()) {
|
|
||||||
lastSticker = null;
|
|
||||||
stickers = null;
|
|
||||||
stickersMap = null;
|
|
||||||
}
|
|
||||||
keywordResults = null;
|
keywordResults = null;
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
if (lastReqId != 0) {
|
|
||||||
ConnectionsManager.getInstance(currentAccount).cancelRequest(lastReqId, true);
|
|
||||||
lastReqId = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getQuery() {
|
public String getQuery() {
|
||||||
return lastSticker;
|
return lastSearch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isShowingKeywords() {
|
public boolean isShowingKeywords() {
|
||||||
|
@ -423,21 +172,14 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement
|
||||||
if (keywordResults != null && !keywordResults.isEmpty()) {
|
if (keywordResults != null && !keywordResults.isEmpty()) {
|
||||||
return keywordResults.size();
|
return keywordResults.size();
|
||||||
}
|
}
|
||||||
return !delayLocalResults && stickers != null ? stickers.size() : 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getItem(int i) {
|
public Object getItem(int i) {
|
||||||
if (keywordResults != null && !keywordResults.isEmpty()) {
|
if (keywordResults != null && !keywordResults.isEmpty()) {
|
||||||
return i >= 0 && i < keywordResults.size() ? keywordResults.get(i).emoji : null;
|
return i >= 0 && i < keywordResults.size() ? keywordResults.get(i).emoji : null;
|
||||||
}
|
}
|
||||||
return stickers != null && i >= 0 && i < stickers.size() ? stickers.get(i).sticker : null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
public Object getItemParent(int i) {
|
|
||||||
if (keywordResults != null && !keywordResults.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return stickers != null && i >= 0 && i < stickers.size() ? stickers.get(i).parent : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -447,61 +189,27 @@ public class StickersAdapter extends RecyclerListView.SelectionAdapter implement
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
|
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
|
||||||
View view;
|
return new RecyclerListView.Holder(new EmojiReplacementCell(mContext));
|
||||||
switch (viewType) {
|
|
||||||
case 0:
|
|
||||||
view = new StickerCell(mContext);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
default:
|
|
||||||
view = new EmojiReplacementCell(mContext);
|
|
||||||
}
|
|
||||||
return new RecyclerListView.Holder(view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemViewType(int position) {
|
public int getItemViewType(int position) {
|
||||||
if (keywordResults != null && !keywordResults.isEmpty()) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||||
switch (holder.getItemViewType()) {
|
int side = 0;
|
||||||
case 0: {
|
if (position == 0) {
|
||||||
int side = 0;
|
if (keywordResults.size() == 1) {
|
||||||
if (position == 0) {
|
side = 2;
|
||||||
if (stickers.size() == 1) {
|
} else {
|
||||||
side = 2;
|
side = -1;
|
||||||
} else {
|
|
||||||
side = -1;
|
|
||||||
}
|
|
||||||
} else if (position == stickers.size() - 1) {
|
|
||||||
side = 1;
|
|
||||||
}
|
|
||||||
StickerCell stickerCell = (StickerCell) holder.itemView;
|
|
||||||
StickerResult result = stickers.get(position);
|
|
||||||
stickerCell.setSticker(result.sticker, result.parent, side);
|
|
||||||
stickerCell.setClearsInputField(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 1: {
|
|
||||||
int side = 0;
|
|
||||||
if (position == 0) {
|
|
||||||
if (keywordResults.size() == 1) {
|
|
||||||
side = 2;
|
|
||||||
} else {
|
|
||||||
side = -1;
|
|
||||||
}
|
|
||||||
} else if (position == keywordResults.size() - 1) {
|
|
||||||
side = 1;
|
|
||||||
}
|
|
||||||
EmojiReplacementCell cell = (EmojiReplacementCell) holder.itemView;
|
|
||||||
cell.setEmoji(keywordResults.get(position).emoji, side);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} else if (position == keywordResults.size() - 1) {
|
||||||
|
side = 1;
|
||||||
}
|
}
|
||||||
|
EmojiReplacementCell cell = (EmojiReplacementCell) holder.itemView;
|
||||||
|
cell.setEmoji(keywordResults.get(position).emoji, side);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -626,6 +626,8 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private boolean closeAnimationInProgress;
|
||||||
|
|
||||||
private class WindowView extends FrameLayout {
|
private class WindowView extends FrameLayout {
|
||||||
|
|
||||||
private final Paint blackPaint = new Paint();
|
private final Paint blackPaint = new Paint();
|
||||||
|
@ -640,7 +642,6 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
|
||||||
private int startedTrackingX;
|
private int startedTrackingX;
|
||||||
private int startedTrackingY;
|
private int startedTrackingY;
|
||||||
private VelocityTracker tracker;
|
private VelocityTracker tracker;
|
||||||
private boolean closeAnimationInProgress;
|
|
||||||
private float innerTranslationX;
|
private float innerTranslationX;
|
||||||
private float alpha;
|
private float alpha;
|
||||||
|
|
||||||
|
@ -4491,7 +4492,7 @@ public class ArticleViewer implements NotificationCenter.NotificationCenterDeleg
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close(boolean byBackPress, boolean force) {
|
public void close(boolean byBackPress, boolean force) {
|
||||||
if (parentActivity == null || !isVisible || checkAnimation()) {
|
if (parentActivity == null || closeAnimationInProgress || !isVisible || checkAnimation()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (fullscreenVideoContainer.getVisibility() == View.VISIBLE) {
|
if (fullscreenVideoContainer.getVisibility() == View.VISIBLE) {
|
||||||
|
|
|
@ -556,7 +556,7 @@ public class CancelAccountDeletionActivity extends BaseFragment {
|
||||||
|
|
||||||
Intent mailer = new Intent(Intent.ACTION_SENDTO);
|
Intent mailer = new Intent(Intent.ACTION_SENDTO);
|
||||||
mailer.setData(Uri.parse("mailto:"));
|
mailer.setData(Uri.parse("mailto:"));
|
||||||
mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"sms@stel.com"});
|
mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"reports@stel.com"});
|
||||||
mailer.putExtra(Intent.EXTRA_SUBJECT, "Android cancel account deletion issue " + version + " " + phone);
|
mailer.putExtra(Intent.EXTRA_SUBJECT, "Android cancel account deletion issue " + version + " " + phone);
|
||||||
mailer.putExtra(Intent.EXTRA_TEXT, "Phone: " + phone + "\nApp version: " + version + "\nOS version: SDK " + Build.VERSION.SDK_INT + "\nDevice Name: " + Build.MANUFACTURER + Build.MODEL + "\nLocale: " + Locale.getDefault() + "\nError: " + lastError);
|
mailer.putExtra(Intent.EXTRA_TEXT, "Phone: " + phone + "\nApp version: " + version + "\nOS version: SDK " + Build.VERSION.SDK_INT + "\nDevice Name: " + Build.MANUFACTURER + Build.MODEL + "\nLocale: " + Locale.getDefault() + "\nError: " + lastError);
|
||||||
getContext().startActivity(Intent.createChooser(mailer, "Send email..."));
|
getContext().startActivity(Intent.createChooser(mailer, "Send email..."));
|
||||||
|
|
|
@ -207,12 +207,14 @@ public class BotHelpCell extends View {
|
||||||
shadowDrawable.setBounds(x, y, width + x, height + y);
|
shadowDrawable.setBounds(x, y, width + x, height + y);
|
||||||
shadowDrawable.draw(canvas);
|
shadowDrawable.draw(canvas);
|
||||||
}
|
}
|
||||||
|
int w = AndroidUtilities.displaySize.x;
|
||||||
int h = AndroidUtilities.displaySize.y;
|
int h = AndroidUtilities.displaySize.y;
|
||||||
if (getParent() instanceof View) {
|
if (getParent() instanceof View) {
|
||||||
View view = (View) getParent();
|
View view = (View) getParent();
|
||||||
|
w = view.getMeasuredWidth();
|
||||||
h = view.getMeasuredHeight();
|
h = view.getMeasuredHeight();
|
||||||
}
|
}
|
||||||
Theme.chat_msgInMediaDrawable.setTop((int) getY(), h, false, false);
|
Theme.chat_msgInMediaDrawable.setTop((int) getY(), w, h, false, false);
|
||||||
Theme.chat_msgInMediaDrawable.setBounds(x, y, width + x, height + y);
|
Theme.chat_msgInMediaDrawable.setBounds(x, y, width + x, height + y);
|
||||||
Theme.chat_msgInMediaDrawable.draw(canvas);
|
Theme.chat_msgInMediaDrawable.draw(canvas);
|
||||||
Theme.chat_msgTextPaint.setColor(Theme.getColor(Theme.key_chat_messageTextIn));
|
Theme.chat_msgTextPaint.setColor(Theme.getColor(Theme.key_chat_messageTextIn));
|
||||||
|
|
|
@ -68,6 +68,8 @@ import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.core.graphics.ColorUtils;
|
import androidx.core.graphics.ColorUtils;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.util.Log;
|
||||||
|
|
||||||
import org.telegram.PhoneFormat.PhoneFormat;
|
import org.telegram.PhoneFormat.PhoneFormat;
|
||||||
import org.telegram.messenger.AndroidUtilities;
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
import org.telegram.messenger.ChatObject;
|
import org.telegram.messenger.ChatObject;
|
||||||
|
@ -348,6 +350,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
private boolean needNewVisiblePart;
|
private boolean needNewVisiblePart;
|
||||||
private boolean fullyDraw;
|
private boolean fullyDraw;
|
||||||
|
|
||||||
|
private int parentWidth;
|
||||||
private int parentHeight;
|
private int parentHeight;
|
||||||
public float parentViewTopOffset;
|
public float parentViewTopOffset;
|
||||||
|
|
||||||
|
@ -1518,7 +1521,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkInstantButtonMotionEvent(MotionEvent event) {
|
private boolean checkInstantButtonMotionEvent(MotionEvent event) {
|
||||||
if (!drawInstantView || currentMessageObject.type == 0) {
|
if (!currentMessageObject.isSponsored() && (!drawInstantView || currentMessageObject.type == 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int x = (int) event.getX();
|
int x = (int) event.getX();
|
||||||
|
@ -2555,7 +2558,18 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
fullyDraw = draw;
|
fullyDraw = draw;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVisiblePart(int position, int height, int parent, float parentOffset, float visibleTop, int parentH) {
|
public void setParentViewSize(int parentW, int parentH) {
|
||||||
|
parentWidth = parentW;
|
||||||
|
parentHeight = parentH;
|
||||||
|
backgroundHeight = parentH;
|
||||||
|
|
||||||
|
if (currentMessageObject != null && (Theme.hasGradientService() && currentMessageObject.shouldDrawWithoutBackground() || drawSideButton != 0 || !botButtons.isEmpty()) || currentBackgroundDrawable != null && currentBackgroundDrawable.getGradientShader() != null) {
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVisiblePart(int position, int height, int parent, float parentOffset, float visibleTop, int parentW, int parentH) {
|
||||||
|
parentWidth = parentW;
|
||||||
parentHeight = parentH;
|
parentHeight = parentH;
|
||||||
backgroundHeight = parentH;
|
backgroundHeight = parentH;
|
||||||
viewTop = visibleTop;
|
viewTop = visibleTop;
|
||||||
|
@ -3032,9 +3046,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (messageObject.checkLayout() || currentPosition != null && lastHeight != AndroidUtilities.displaySize.y) {
|
if (messageObject.checkLayout() || currentPosition != null && lastHeight != AndroidUtilities.displaySize.y) {
|
||||||
currentMessageObject = null;
|
currentMessageObject = null;
|
||||||
}
|
}
|
||||||
boolean widthChanged = lastWidth != AndroidUtilities.displaySize.x;
|
boolean widthChanged = lastWidth != getParentWidth();
|
||||||
lastHeight = AndroidUtilities.displaySize.y;
|
lastHeight = AndroidUtilities.displaySize.y;
|
||||||
lastWidth = AndroidUtilities.displaySize.x;
|
lastWidth = getParentWidth();
|
||||||
isRoundVideo = messageObject != null && messageObject.isRoundVideo();
|
isRoundVideo = messageObject != null && messageObject.isRoundVideo();
|
||||||
TLRPC.Message newReply = messageObject.hasValidReplyMessageObject() ? messageObject.replyMessageObject.messageOwner : null;
|
TLRPC.Message newReply = messageObject.hasValidReplyMessageObject() ? messageObject.replyMessageObject.messageOwner : null;
|
||||||
boolean messageIdChanged = currentMessageObject == null || currentMessageObject.getId() != messageObject.getId();
|
boolean messageIdChanged = currentMessageObject == null || currentMessageObject.getId() != messageObject.getId();
|
||||||
|
@ -3272,7 +3286,6 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (currentMessageObject != null && currentMessageObject.textLayoutBlocks != null && currentMessageObject.textLayoutBlocks.size() > 1) {
|
if (currentMessageObject != null && currentMessageObject.textLayoutBlocks != null && currentMessageObject.textLayoutBlocks.size() > 1) {
|
||||||
needNewVisiblePart = true;
|
needNewVisiblePart = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean linked = false;
|
boolean linked = false;
|
||||||
|
@ -3383,14 +3396,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
maxWidth = AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(122);
|
maxWidth = AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(122);
|
||||||
} else {
|
} else {
|
||||||
maxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(122);
|
maxWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(122);
|
||||||
}
|
}
|
||||||
drawName = true;
|
drawName = true;
|
||||||
} else {
|
} else {
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
maxWidth = AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(80);
|
maxWidth = AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(80);
|
||||||
} else {
|
} else {
|
||||||
maxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(80);
|
maxWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(80);
|
||||||
}
|
}
|
||||||
drawName = isPinnedChat || messageObject.messageOwner.peer_id.channel_id != 0 && (!messageObject.isOutOwner() || messageObject.isSupergroup()) || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null;
|
drawName = isPinnedChat || messageObject.messageOwner.peer_id.channel_id != 0 && (!messageObject.isOutOwner() || messageObject.isSupergroup()) || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null;
|
||||||
}
|
}
|
||||||
|
@ -3417,7 +3430,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
TLRPC.Document androidThemeDocument = null;
|
TLRPC.Document androidThemeDocument = null;
|
||||||
TLRPC.TL_themeSettings androidThemeSettings = null;
|
TLRPC.TL_themeSettings androidThemeSettings = null;
|
||||||
if (!drawInstantView) {
|
if (!drawInstantView) {
|
||||||
if ("telegram_voicechat".equals(webpageType)) {
|
if ("telegram_livestream".equals(webpageType)) {
|
||||||
|
drawInstantView = true;
|
||||||
|
drawInstantViewType = 11;
|
||||||
|
} else if ("telegram_voicechat".equals(webpageType)) {
|
||||||
drawInstantView = true;
|
drawInstantView = true;
|
||||||
drawInstantViewType = 9;
|
drawInstantViewType = 9;
|
||||||
} else if ("telegram_channel".equals(webpageType)) {
|
} else if ("telegram_channel".equals(webpageType)) {
|
||||||
|
@ -3583,9 +3599,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (drawAvatar) {
|
if (drawAvatar) {
|
||||||
linkPreviewMaxWidth = AndroidUtilities.displaySize.x - AndroidUtilities.dp(132);
|
linkPreviewMaxWidth = getParentWidth() - AndroidUtilities.dp(132);
|
||||||
} else {
|
} else {
|
||||||
linkPreviewMaxWidth = AndroidUtilities.displaySize.x - AndroidUtilities.dp(80);
|
linkPreviewMaxWidth = getParentWidth() - AndroidUtilities.dp(80);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (drawSideButton != 0) {
|
if (drawSideButton != 0) {
|
||||||
|
@ -3625,8 +3641,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
boolean isSmallImageType = "app".equals(type) || "profile".equals(type) || "article".equals(type) ||
|
boolean isSmallImageType = "app".equals(type) || "profile".equals(type) || "article".equals(type) ||
|
||||||
"telegram_bot".equals(type) || "telegram_user".equals(type) || "telegram_channel".equals(type) || "telegram_megagroup".equals(type) || "telegram_voicechat".equals(type);
|
"telegram_bot".equals(type) || "telegram_user".equals(type) || "telegram_channel".equals(type) || "telegram_megagroup".equals(type) || "telegram_voicechat".equals(type);
|
||||||
smallImage = !slideshow && (!drawInstantView || drawInstantViewType == 9) && document == null && isSmallImageType;
|
smallImage = !slideshow && (!drawInstantView || drawInstantViewType == 9 || drawInstantViewType == 11) && document == null && isSmallImageType;
|
||||||
isSmallImage = !slideshow && (!drawInstantView || drawInstantViewType == 9) && document == null && description != null && type != null && isSmallImageType && currentMessageObject.photoThumbs != null;
|
isSmallImage = !slideshow && (!drawInstantView || drawInstantViewType == 9 || drawInstantViewType == 11) && document == null && description != null && type != null && isSmallImageType && currentMessageObject.photoThumbs != null;
|
||||||
} else if (hasInvoicePreview) {
|
} else if (hasInvoicePreview) {
|
||||||
TLRPC.TL_messageMediaInvoice invoice = (TLRPC.TL_messageMediaInvoice) messageObject.messageOwner.media;
|
TLRPC.TL_messageMediaInvoice invoice = (TLRPC.TL_messageMediaInvoice) messageObject.messageOwner.media;
|
||||||
site_name = messageObject.messageOwner.media.title;
|
site_name = messageObject.messageOwner.media.title;
|
||||||
|
@ -3658,9 +3674,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
isSmallImage = false;
|
isSmallImage = false;
|
||||||
smallImage = false;
|
smallImage = false;
|
||||||
}
|
}
|
||||||
if (drawInstantViewType == 9) {
|
if (drawInstantViewType == 11) {
|
||||||
|
site_name = LocaleController.getString("VoipChannelVoiceChat", R.string.VoipChannelVoiceChat);
|
||||||
|
} else if (drawInstantViewType == 9) {
|
||||||
site_name = LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat);
|
site_name = LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat);
|
||||||
TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) messageObject.messageOwner.media.webpage;
|
|
||||||
} else if (drawInstantViewType == 6) {
|
} else if (drawInstantViewType == 6) {
|
||||||
site_name = LocaleController.getString("ChatBackground", R.string.ChatBackground);
|
site_name = LocaleController.getString("ChatBackground", R.string.ChatBackground);
|
||||||
} else if ("telegram_theme".equals(webpageType)) {
|
} else if ("telegram_theme".equals(webpageType)) {
|
||||||
|
@ -4001,7 +4018,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
maxChildWidth = Math.max(maxChildWidth, Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 52 : 0), AndroidUtilities.dp(220)) - AndroidUtilities.dp(30) + additinalWidth);
|
maxChildWidth = Math.max(maxChildWidth, Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 52 : 0), AndroidUtilities.dp(220)) - AndroidUtilities.dp(30) + additinalWidth);
|
||||||
} else {
|
} else {
|
||||||
maxChildWidth = Math.max(maxChildWidth, Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 52 : 0), AndroidUtilities.dp(220)) - AndroidUtilities.dp(30) + additinalWidth);
|
maxChildWidth = Math.max(maxChildWidth, Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 52 : 0), AndroidUtilities.dp(220)) - AndroidUtilities.dp(30) + additinalWidth);
|
||||||
}
|
}
|
||||||
calcBackgroundWidth(maxWidth, timeMore, maxChildWidth);
|
calcBackgroundWidth(maxWidth, timeMore, maxChildWidth);
|
||||||
} else if (MessageObject.isMusicDocument(document)) {
|
} else if (MessageObject.isMusicDocument(document)) {
|
||||||
|
@ -4072,7 +4089,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
maxPhotoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.5f);
|
maxPhotoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.5f);
|
||||||
} else {
|
} else {
|
||||||
maxPhotoWidth = (int) (AndroidUtilities.displaySize.x * 0.5f);
|
maxPhotoWidth = (int) (getParentWidth() * 0.5f);
|
||||||
}
|
}
|
||||||
} else if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND) {
|
} else if (documentAttachType == DOCUMENT_ATTACH_TYPE_ROUND) {
|
||||||
maxPhotoWidth = AndroidUtilities.roundMessageSize;
|
maxPhotoWidth = AndroidUtilities.roundMessageSize;
|
||||||
|
@ -4314,7 +4331,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
||||||
} else {
|
} else {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
||||||
}
|
}
|
||||||
availableTimeWidth = backgroundWidth - AndroidUtilities.dp(31);
|
availableTimeWidth = backgroundWidth - AndroidUtilities.dp(31);
|
||||||
|
|
||||||
|
@ -4384,7 +4401,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
||||||
} else {
|
} else {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
||||||
}
|
}
|
||||||
availableTimeWidth = backgroundWidth - AndroidUtilities.dp(31);
|
availableTimeWidth = backgroundWidth - AndroidUtilities.dp(31);
|
||||||
|
|
||||||
|
@ -4463,7 +4480,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
||||||
} else {
|
} else {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
||||||
}
|
}
|
||||||
createDocumentLayout(backgroundWidth, messageObject);
|
createDocumentLayout(backgroundWidth, messageObject);
|
||||||
|
|
||||||
|
@ -4478,7 +4495,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
||||||
} else {
|
} else {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(270));
|
||||||
}
|
}
|
||||||
|
|
||||||
createDocumentLayout(backgroundWidth, messageObject);
|
createDocumentLayout(backgroundWidth, messageObject);
|
||||||
|
@ -4768,7 +4785,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
drawForwardedName = messageObject.messageOwner.fwd_from != null && !messageObject.isAnyKindOfSticker();
|
drawForwardedName = messageObject.messageOwner.fwd_from != null && !(messageObject.isAnyKindOfSticker() && messageObject.isDice());
|
||||||
if (!messageObject.isAnyKindOfSticker() && messageObject.type != MessageObject.TYPE_ROUND_VIDEO) {
|
if (!messageObject.isAnyKindOfSticker() && messageObject.type != MessageObject.TYPE_ROUND_VIDEO) {
|
||||||
drawName = (messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null) && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_TOP) != 0);
|
drawName = (messageObject.isFromGroup() && messageObject.isSupergroup() || messageObject.isImportedForward() && messageObject.messageOwner.fwd_from.from_id == null) && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_TOP) != 0);
|
||||||
}
|
}
|
||||||
|
@ -4799,7 +4816,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(300));
|
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(300));
|
||||||
} else {
|
} else {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(300));
|
backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(300));
|
||||||
}
|
}
|
||||||
if (checkNeedDrawShareButton(messageObject)) {
|
if (checkNeedDrawShareButton(messageObject)) {
|
||||||
backgroundWidth -= AndroidUtilities.dp(20);
|
backgroundWidth -= AndroidUtilities.dp(20);
|
||||||
|
@ -4896,7 +4913,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
||||||
} else {
|
} else {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
||||||
}
|
}
|
||||||
backgroundWidth -= AndroidUtilities.dp(4);
|
backgroundWidth -= AndroidUtilities.dp(4);
|
||||||
if (checkNeedDrawShareButton(messageObject)) {
|
if (checkNeedDrawShareButton(messageObject)) {
|
||||||
|
@ -4946,7 +4963,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
||||||
} else {
|
} else {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
||||||
}
|
}
|
||||||
backgroundWidth -= AndroidUtilities.dp(4);
|
backgroundWidth -= AndroidUtilities.dp(4);
|
||||||
if (checkNeedDrawShareButton(messageObject)) {
|
if (checkNeedDrawShareButton(messageObject)) {
|
||||||
|
@ -4980,7 +4997,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
backgroundWidth = Math.min(AndroidUtilities.getMinTabletSide() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
||||||
} else {
|
} else {
|
||||||
backgroundWidth = Math.min(AndroidUtilities.displaySize.x - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
backgroundWidth = Math.min(getParentWidth() - AndroidUtilities.dp(drawAvatar ? 102 : 50), AndroidUtilities.dp(252 + 37));
|
||||||
}
|
}
|
||||||
backgroundWidth -= AndroidUtilities.dp(4);
|
backgroundWidth -= AndroidUtilities.dp(4);
|
||||||
if (checkNeedDrawShareButton(messageObject)) {
|
if (checkNeedDrawShareButton(messageObject)) {
|
||||||
|
@ -5042,7 +5059,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
maxHeight = maxWidth = AndroidUtilities.getMinTabletSide() * 0.4f;
|
maxHeight = maxWidth = AndroidUtilities.getMinTabletSide() * 0.4f;
|
||||||
} else {
|
} else {
|
||||||
maxHeight = maxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f;
|
maxHeight = maxWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y) * 0.5f;
|
||||||
}
|
}
|
||||||
String filter;
|
String filter;
|
||||||
if (messageObject.isAnimatedEmoji() || messageObject.isDice()) {
|
if (messageObject.isAnimatedEmoji() || messageObject.isDice()) {
|
||||||
|
@ -5130,10 +5147,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
photoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.7f);
|
photoWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.7f);
|
||||||
} else {
|
} else {
|
||||||
if (currentPhotoObject != null && (messageObject.type == MessageObject.TYPE_PHOTO || messageObject.type == MessageObject.TYPE_VIDEO || messageObject.type == 8) && currentPhotoObject.w >= currentPhotoObject.h) {
|
if (currentPhotoObject != null && (messageObject.type == MessageObject.TYPE_PHOTO || messageObject.type == MessageObject.TYPE_VIDEO || messageObject.type == 8) && currentPhotoObject.w >= currentPhotoObject.h) {
|
||||||
photoWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(64 + (checkNeedDrawShareButton(messageObject) ? 10 : 0));
|
photoWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(64 + (checkNeedDrawShareButton(messageObject) ? 10 : 0));
|
||||||
useFullWidth = true;
|
useFullWidth = true;
|
||||||
} else {
|
} else {
|
||||||
photoWidth = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.7f);
|
photoWidth = (int) (Math.min(getParentWidth(), AndroidUtilities.displaySize.y) * 0.7f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5289,14 +5306,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
w = h = (int) (AndroidUtilities.getMinTabletSide() * 0.5f);
|
w = h = (int) (AndroidUtilities.getMinTabletSide() * 0.5f);
|
||||||
} else {
|
} else {
|
||||||
w = h = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f);
|
w = h = (int) (Math.min(getParentWidth(), AndroidUtilities.displaySize.y) * 0.5f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int widthForCaption = 0;
|
int widthForCaption = 0;
|
||||||
boolean fixPhotoWidth = false;
|
boolean fixPhotoWidth = false;
|
||||||
if (currentMessagesGroup != null) {
|
if (currentMessagesGroup != null) {
|
||||||
float maxHeight = Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f;
|
float maxHeight = Math.max(getParentWidth(), AndroidUtilities.displaySize.y) * 0.5f;
|
||||||
int dWidth = getGroupPhotosWidth();
|
int dWidth = getGroupPhotosWidth();
|
||||||
w = (int) Math.ceil(currentPosition.pw / 1000.0f * dWidth);
|
w = (int) Math.ceil(currentPosition.pw / 1000.0f * dWidth);
|
||||||
if (currentPosition.minY != 0 && (messageObject.isOutOwner() && (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0 || !messageObject.isOutOwner() && (currentPosition.flags & MessageObject.POSITION_FLAG_RIGHT) != 0)) {
|
if (currentPosition.minY != 0 && (messageObject.isOutOwner() && (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0 || !messageObject.isOutOwner() && (currentPosition.flags & MessageObject.POSITION_FLAG_RIGHT) != 0)) {
|
||||||
|
@ -5407,7 +5424,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
minCaptionWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.65f);
|
minCaptionWidth = (int) (AndroidUtilities.getMinTabletSide() * 0.65f);
|
||||||
} else {
|
} else {
|
||||||
minCaptionWidth = (int) (Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.65f);
|
minCaptionWidth = (int) (Math.min(getParentWidth(), AndroidUtilities.displaySize.y) * 0.65f);
|
||||||
}
|
}
|
||||||
if (!messageObject.needDrawBluredPreview() && currentCaption != null && photoWidth < minCaptionWidth) {
|
if (!messageObject.needDrawBluredPreview() && currentCaption != null && photoWidth < minCaptionWidth) {
|
||||||
widthForCaption = minCaptionWidth;
|
widthForCaption = minCaptionWidth;
|
||||||
|
@ -5801,6 +5818,19 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (messageObject.isSponsored()) {
|
||||||
|
drawInstantView = true;
|
||||||
|
drawInstantViewType = 1;
|
||||||
|
long id = MessageObject.getPeerId(messageObject.messageOwner.from_id);
|
||||||
|
if (id > 0) {
|
||||||
|
TLRPC.User user = MessagesController.getInstance(currentAccount).getUser((int) id); //TODO long
|
||||||
|
if (user != null && user.bot) {
|
||||||
|
drawInstantViewType = 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
createInstantViewButton();
|
||||||
|
}
|
||||||
|
|
||||||
botButtons.clear();
|
botButtons.clear();
|
||||||
if (messageIdChanged) {
|
if (messageIdChanged) {
|
||||||
botButtonsByData.clear();
|
botButtonsByData.clear();
|
||||||
|
@ -5823,7 +5853,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
maxButtonWidth += AndroidUtilities.getMinTabletSide();
|
maxButtonWidth += AndroidUtilities.getMinTabletSide();
|
||||||
} else {
|
} else {
|
||||||
maxButtonWidth += Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(5);
|
maxButtonWidth += Math.min(getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(5);
|
||||||
}
|
}
|
||||||
widthForButtons = Math.max(backgroundWidth, Math.min(messageObject.wantedBotKeyboardWidth, maxButtonWidth));
|
widthForButtons = Math.max(backgroundWidth, Math.min(messageObject.wantedBotKeyboardWidth, maxButtonWidth));
|
||||||
}
|
}
|
||||||
|
@ -6804,6 +6834,8 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
instantWidth = AndroidUtilities.dp(12 + 9 + 12);
|
instantWidth = AndroidUtilities.dp(12 + 9 + 12);
|
||||||
if (drawInstantViewType == 1) {
|
if (drawInstantViewType == 1) {
|
||||||
str = LocaleController.getString("OpenChannel", R.string.OpenChannel);
|
str = LocaleController.getString("OpenChannel", R.string.OpenChannel);
|
||||||
|
} else if (drawInstantViewType == 10) {
|
||||||
|
str = LocaleController.getString("OpenBot", R.string.OpenBot);
|
||||||
} else if (drawInstantViewType == 2) {
|
} else if (drawInstantViewType == 2) {
|
||||||
str = LocaleController.getString("OpenGroup", R.string.OpenGroup);
|
str = LocaleController.getString("OpenGroup", R.string.OpenGroup);
|
||||||
} else if (drawInstantViewType == 3) {
|
} else if (drawInstantViewType == 3) {
|
||||||
|
@ -6820,7 +6852,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
} else {
|
} else {
|
||||||
str = LocaleController.getString("PollSubmitVotes", R.string.PollSubmitVotes);
|
str = LocaleController.getString("PollSubmitVotes", R.string.PollSubmitVotes);
|
||||||
}
|
}
|
||||||
} else if (drawInstantViewType == 9) {
|
} else if (drawInstantViewType == 9 || drawInstantViewType == 11) {
|
||||||
TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) currentMessageObject.messageOwner.media.webpage;
|
TLRPC.TL_webPage webPage = (TLRPC.TL_webPage) currentMessageObject.messageOwner.media.webpage;
|
||||||
if (webPage != null && webPage.url.contains("voicechat=")) {
|
if (webPage != null && webPage.url.contains("voicechat=")) {
|
||||||
str = LocaleController.getString("VoipGroupJoinAsSpeaker", R.string.VoipGroupJoinAsSpeaker);
|
str = LocaleController.getString("VoipGroupJoinAsSpeaker", R.string.VoipGroupJoinAsSpeaker);
|
||||||
|
@ -6877,14 +6909,18 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getGroupPhotosWidth() {
|
private int getGroupPhotosWidth() {
|
||||||
|
int width = getParentWidth();
|
||||||
|
if (currentMessageObject != null && currentMessageObject.preview) {
|
||||||
|
width = parentWidth;
|
||||||
|
}
|
||||||
if (!AndroidUtilities.isInMultiwindow && AndroidUtilities.isTablet() && (!AndroidUtilities.isSmallTablet() || getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)) {
|
if (!AndroidUtilities.isInMultiwindow && AndroidUtilities.isTablet() && (!AndroidUtilities.isSmallTablet() || getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)) {
|
||||||
int leftWidth = AndroidUtilities.displaySize.x / 100 * 35;
|
int leftWidth = width / 100 * 35;
|
||||||
if (leftWidth < AndroidUtilities.dp(320)) {
|
if (leftWidth < AndroidUtilities.dp(320)) {
|
||||||
leftWidth = AndroidUtilities.dp(320);
|
leftWidth = AndroidUtilities.dp(320);
|
||||||
}
|
}
|
||||||
return AndroidUtilities.displaySize.x - leftWidth;
|
return width - leftWidth;
|
||||||
} else {
|
} else {
|
||||||
return AndroidUtilities.displaySize.x;
|
return width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7309,7 +7345,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
private void drawContent(Canvas canvas) {
|
private void drawContent(Canvas canvas) {
|
||||||
if (needNewVisiblePart && currentMessageObject.type == 0) {
|
if (needNewVisiblePart && currentMessageObject.type == 0) {
|
||||||
getLocalVisibleRect(scrollRect);
|
getLocalVisibleRect(scrollRect);
|
||||||
setVisiblePart(scrollRect.top, scrollRect.bottom - scrollRect.top, parentHeight, parentViewTopOffset, viewTop, backgroundHeight);
|
setVisiblePart(scrollRect.top, scrollRect.bottom - scrollRect.top, parentHeight, parentViewTopOffset, viewTop, parentWidth, backgroundHeight);
|
||||||
needNewVisiblePart = false;
|
needNewVisiblePart = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7387,7 +7423,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
textX += diff - getExtraTimeX();
|
textX += diff - getExtraTimeX();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!enterTransitionInPorgress) {
|
if (!enterTransitionInPorgress && currentMessageObject != null && !currentMessageObject.preview) {
|
||||||
if (transitionParams.animateChangeProgress != 1.0f && transitionParams.animateMessageText) {
|
if (transitionParams.animateChangeProgress != 1.0f && transitionParams.animateMessageText) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
if (currentBackgroundDrawable != null) {
|
if (currentBackgroundDrawable != null) {
|
||||||
|
@ -7687,7 +7723,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (captionLayout != null) {
|
if (captionLayout != null) {
|
||||||
updateCaptionLayout();
|
updateCaptionLayout();
|
||||||
}
|
}
|
||||||
if ((currentPosition == null || currentMessagesGroup != null && currentMessagesGroup.isDocuments) && !transitionParams.transformGroupToSingleMessage && !(enterTransitionInPorgress && currentMessageObject.isVoice())) {
|
if (!currentMessageObject.preview && (currentPosition == null || currentMessagesGroup != null && currentMessagesGroup.isDocuments) && !transitionParams.transformGroupToSingleMessage && !(enterTransitionInPorgress && currentMessageObject.isVoice())) {
|
||||||
drawCaptionLayout(canvas, false, 1f);
|
drawCaptionLayout(canvas, false, 1f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7864,7 +7900,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawLinkPreview(Canvas canvas, float alpha) {
|
public void drawLinkPreview(Canvas canvas, float alpha) {
|
||||||
if (!hasLinkPreview && !hasGamePreview && !hasInvoicePreview) {
|
if (!currentMessageObject.isSponsored() && !hasLinkPreview && !hasGamePreview && !hasInvoicePreview) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int startY;
|
int startY;
|
||||||
|
@ -7875,6 +7911,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
} else if (hasInvoicePreview) {
|
} else if (hasInvoicePreview) {
|
||||||
startY = AndroidUtilities.dp(14) + namesOffset;
|
startY = AndroidUtilities.dp(14) + namesOffset;
|
||||||
linkX = unmovedTextX + AndroidUtilities.dp(1);
|
linkX = unmovedTextX + AndroidUtilities.dp(1);
|
||||||
|
} else if (currentMessageObject.isSponsored()) {
|
||||||
|
startY = textY + currentMessageObject.textHeight - AndroidUtilities.dp(2);
|
||||||
|
linkX = unmovedTextX + AndroidUtilities.dp(1);
|
||||||
} else {
|
} else {
|
||||||
startY = textY + currentMessageObject.textHeight + AndroidUtilities.dp(8);
|
startY = textY + currentMessageObject.textHeight + AndroidUtilities.dp(8);
|
||||||
linkX = unmovedTextX + AndroidUtilities.dp(1);
|
linkX = unmovedTextX + AndroidUtilities.dp(1);
|
||||||
|
@ -7882,7 +7921,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
int linkPreviewY = startY;
|
int linkPreviewY = startY;
|
||||||
int smallImageStartY = 0;
|
int smallImageStartY = 0;
|
||||||
|
|
||||||
if (!hasInvoicePreview) {
|
if (!hasInvoicePreview && !currentMessageObject.isSponsored()) {
|
||||||
Theme.chat_replyLinePaint.setColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outPreviewLine : Theme.key_chat_inPreviewLine));
|
Theme.chat_replyLinePaint.setColor(Theme.getColor(currentMessageObject.isOutOwner() ? Theme.key_chat_outPreviewLine : Theme.key_chat_inPreviewLine));
|
||||||
if (alpha != 1f) {
|
if (alpha != 1f) {
|
||||||
Theme.chat_replyLinePaint.setAlpha((int) (alpha * Theme.chat_replyLinePaint.getAlpha()));
|
Theme.chat_replyLinePaint.setAlpha((int) (alpha * Theme.chat_replyLinePaint.getAlpha()));
|
||||||
|
@ -8048,7 +8087,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
linkPreviewY += descriptionLayout.getLineBottom(descriptionLayout.getLineCount() - 1);
|
linkPreviewY += descriptionLayout.getLineBottom(descriptionLayout.getLineCount() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drawPhotoImage && (!drawInstantView || drawInstantViewType == 9)) {
|
if (drawPhotoImage && (!drawInstantView || drawInstantViewType == 9 || drawInstantViewType == 11)) {
|
||||||
if (linkPreviewY != startY) {
|
if (linkPreviewY != startY) {
|
||||||
linkPreviewY += AndroidUtilities.dp(2);
|
linkPreviewY += AndroidUtilities.dp(2);
|
||||||
}
|
}
|
||||||
|
@ -8267,8 +8306,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("Range")
|
||||||
public void drawMessageText(Canvas canvas, ArrayList<MessageObject.TextLayoutBlock> textLayoutBlocks, boolean origin, float alpha, boolean drawOnlyText) {
|
public void drawMessageText(Canvas canvas, ArrayList<MessageObject.TextLayoutBlock> textLayoutBlocks, boolean origin, float alpha, boolean drawOnlyText) {
|
||||||
if (textLayoutBlocks == null || textLayoutBlocks.isEmpty()) {
|
if (textLayoutBlocks == null || textLayoutBlocks.isEmpty() || alpha == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int firstVisibleBlockNum;
|
int firstVisibleBlockNum;
|
||||||
|
@ -8285,12 +8325,19 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
lastVisibleBlockNum = textLayoutBlocks.size();
|
lastVisibleBlockNum = textLayoutBlocks.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float textY = this.textY;
|
||||||
|
if (transitionParams.animateText) {
|
||||||
|
textY = transitionParams.animateFromTextY * (1f - transitionParams.animateChangeProgress) + this.textY * transitionParams.animateChangeProgress;
|
||||||
|
}
|
||||||
|
textY += transitionYOffsetForDrawables;
|
||||||
if (firstVisibleBlockNum >= 0) {
|
if (firstVisibleBlockNum >= 0) {
|
||||||
int restore = Integer.MIN_VALUE;
|
int restore = Integer.MIN_VALUE;
|
||||||
int oldAlpha = -1;
|
int oldAlpha = 0;
|
||||||
int oldLinkAlpha = -1;
|
int oldLinkAlpha = 0;
|
||||||
|
boolean needRestoreColor = false;
|
||||||
if (alpha != 1.0f) {
|
if (alpha != 1.0f) {
|
||||||
if (drawOnlyText) {
|
if (drawOnlyText) {
|
||||||
|
needRestoreColor = true;
|
||||||
oldAlpha = Theme.chat_msgTextPaint.getAlpha();
|
oldAlpha = Theme.chat_msgTextPaint.getAlpha();
|
||||||
oldLinkAlpha = Color.alpha(Theme.chat_msgTextPaint.linkColor);
|
oldLinkAlpha = Color.alpha(Theme.chat_msgTextPaint.linkColor);
|
||||||
Theme.chat_msgTextPaint.setAlpha((int) (oldAlpha * alpha));
|
Theme.chat_msgTextPaint.setAlpha((int) (oldAlpha * alpha));
|
||||||
|
@ -8344,7 +8391,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
if (oldAlpha >= 0) {
|
if (needRestoreColor) {
|
||||||
Theme.chat_msgTextPaint.setAlpha(oldAlpha);
|
Theme.chat_msgTextPaint.setAlpha(oldAlpha);
|
||||||
Theme.chat_msgTextPaint.linkColor = ColorUtils.setAlphaComponent(Theme.chat_msgTextPaint.linkColor, oldLinkAlpha);
|
Theme.chat_msgTextPaint.linkColor = ColorUtils.setAlphaComponent(Theme.chat_msgTextPaint.linkColor, oldLinkAlpha);
|
||||||
}
|
}
|
||||||
|
@ -8478,9 +8525,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isChat && !isThreadPost && !currentMessageObject.isOutOwner() && currentMessageObject.needDrawAvatar()) {
|
if (isChat && !isThreadPost && !currentMessageObject.isOutOwner() && currentMessageObject.needDrawAvatar()) {
|
||||||
maxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) - AndroidUtilities.dp(42);
|
maxWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y) - AndroidUtilities.dp(42);
|
||||||
} else {
|
} else {
|
||||||
maxWidth = Math.min(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y);
|
maxWidth = Math.min(getParentWidth(), AndroidUtilities.displaySize.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isPlayingRound) {
|
if (isPlayingRound) {
|
||||||
|
@ -8494,7 +8541,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
dWidth = AndroidUtilities.getMinTabletSide();
|
dWidth = AndroidUtilities.getMinTabletSide();
|
||||||
} else {
|
} else {
|
||||||
dWidth = AndroidUtilities.displaySize.x;
|
dWidth = getParentWidth();
|
||||||
}
|
}
|
||||||
int firstLineWidth = 0;
|
int firstLineWidth = 0;
|
||||||
for (int a = 0; a < currentMessagesGroup.posArray.size(); a++) {
|
for (int a = 0; a < currentMessagesGroup.posArray.size(); a++) {
|
||||||
|
@ -9445,7 +9492,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (currentMessageObject.scheduled && currentMessageObject.messageOwner.date == 0x7FFFFFFE) {
|
if (currentMessageObject.isSponsored()) {
|
||||||
|
timeString = LocaleController.getString("SponsoredMessage", R.string.SponsoredMessage);
|
||||||
|
} else if (currentMessageObject.scheduled && currentMessageObject.messageOwner.date == 0x7FFFFFFE) {
|
||||||
timeString = "";
|
timeString = "";
|
||||||
} else if (edited) {
|
} else if (edited) {
|
||||||
timeString = LocaleController.getString("EditedMessage", R.string.EditedMessage) + " " + LocaleController.getInstance().formatterDay.format((long) (messageObject.messageOwner.date) * 1000);
|
timeString = LocaleController.getString("EditedMessage", R.string.EditedMessage) + " " + LocaleController.getInstance().formatterDay.format((long) (messageObject.messageOwner.date) * 1000);
|
||||||
|
@ -9583,7 +9632,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setMessageObjectInternal(MessageObject messageObject) {
|
private void setMessageObjectInternal(MessageObject messageObject) {
|
||||||
if (((messageObject.messageOwner.flags & TLRPC.MESSAGE_FLAG_HAS_VIEWS) != 0 || messageObject.messageOwner.replies != null) && !currentMessageObject.scheduled) {
|
if (((messageObject.messageOwner.flags & TLRPC.MESSAGE_FLAG_HAS_VIEWS) != 0 || messageObject.messageOwner.replies != null) && !currentMessageObject.scheduled && !currentMessageObject.isSponsored()) {
|
||||||
if (!currentMessageObject.viewsReloaded) {
|
if (!currentMessageObject.viewsReloaded) {
|
||||||
MessagesController.getInstance(currentAccount).addToViewsQueue(currentMessageObject);
|
MessagesController.getInstance(currentAccount).addToViewsQueue(currentMessageObject);
|
||||||
currentMessageObject.viewsReloaded = true;
|
currentMessageObject.viewsReloaded = true;
|
||||||
|
@ -9657,7 +9706,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
} else if (isMegagroup && currentChat != null && currentMessageObject.isForwardedChannelPost()) {
|
} else if (isMegagroup && currentChat != null && currentMessageObject.isForwardedChannelPost()) {
|
||||||
adminString = LocaleController.getString("DiscussChannel", R.string.DiscussChannel);
|
adminString = LocaleController.getString("DiscussChannel", R.string.DiscussChannel);
|
||||||
adminWidth = (int) Math.ceil(Theme.chat_adminPaint.measureText(adminString));
|
adminWidth = (int) Math.ceil(Theme.chat_adminPaint.measureText(adminString));
|
||||||
nameWidth -= adminWidth;
|
nameWidth -= adminWidth; //TODO
|
||||||
} else if (currentUser != null && !currentMessageObject.isOutOwner() && !currentMessageObject.isAnyKindOfSticker() && currentMessageObject.type != 5 && delegate != null && (adminLabel = delegate.getAdminRank(currentUser.id)) != null) {
|
} else if (currentUser != null && !currentMessageObject.isOutOwner() && !currentMessageObject.isAnyKindOfSticker() && currentMessageObject.type != 5 && delegate != null && (adminLabel = delegate.getAdminRank(currentUser.id)) != null) {
|
||||||
if (adminLabel.length() == 0) {
|
if (adminLabel.length() == 0) {
|
||||||
adminLabel = LocaleController.getString("ChatAdmin", R.string.ChatAdmin);
|
adminLabel = LocaleController.getString("ChatAdmin", R.string.ChatAdmin);
|
||||||
|
@ -9808,7 +9857,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
forwardNameOffsetX[0] = forwardedNameLayout[0].getLineLeft(0);
|
forwardNameOffsetX[0] = forwardedNameLayout[0].getLineLeft(0);
|
||||||
forwardNameOffsetX[1] = forwardedNameLayout[1].getLineLeft(0);
|
forwardNameOffsetX[1] = forwardedNameLayout[1].getLineLeft(0);
|
||||||
if (messageObject.type != 5) {
|
if (messageObject.type != 5 && !messageObject.isAnyKindOfSticker()) {
|
||||||
namesOffset += AndroidUtilities.dp(36);
|
namesOffset += AndroidUtilities.dp(36);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -10076,7 +10125,13 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
public void drawCheckBox(Canvas canvas) {
|
public void drawCheckBox(Canvas canvas) {
|
||||||
if (currentMessageObject != null && !currentMessageObject.isSending() && !currentMessageObject.isSendError() && checkBox != null && (checkBoxVisible || checkBoxAnimationInProgress) && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) != 0 && (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0)) {
|
if (currentMessageObject != null && !currentMessageObject.isSending() && !currentMessageObject.isSendError() && checkBox != null && (checkBoxVisible || checkBoxAnimationInProgress) && (currentPosition == null || (currentPosition.flags & MessageObject.POSITION_FLAG_BOTTOM) != 0 && (currentPosition.flags & MessageObject.POSITION_FLAG_LEFT) != 0)) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.translate(0, getTop());
|
float y = getY();
|
||||||
|
if (currentMessagesGroup != null && currentMessagesGroup.messages.size() > 1) {
|
||||||
|
y = getTop() + currentMessagesGroup.transitionParams.offsetTop - getTranslationY();
|
||||||
|
} else {
|
||||||
|
y += transitionParams.deltaTop;
|
||||||
|
}
|
||||||
|
canvas.translate(0, y + transitionYOffsetForDrawables);
|
||||||
checkBox.draw(canvas);
|
checkBox.draw(canvas);
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
@ -10091,20 +10146,41 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
if (drawable == null) {
|
if (drawable == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
int w = parentWidth;
|
||||||
int h = parentHeight;
|
int h = parentHeight;
|
||||||
if (h == 0) {
|
if (h == 0) {
|
||||||
|
w = getParentWidth();
|
||||||
h = AndroidUtilities.displaySize.y;
|
h = AndroidUtilities.displaySize.y;
|
||||||
if (getParent() instanceof View) {
|
if (getParent() instanceof View) {
|
||||||
View view = (View) getParent();
|
View view = (View) getParent();
|
||||||
|
w = view.getMeasuredWidth();
|
||||||
h = view.getMeasuredHeight();
|
h = view.getMeasuredHeight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drawable.setTop((int) ((fromParent ? getY() : getTop()) + parentViewTopOffset), h, pinnedTop, pinnedBottom || transitionParams.changePinnedBottomProgress != 1);
|
drawable.setTop((int) ((fromParent ? getY() : getTop()) + parentViewTopOffset), w, h, (int) parentViewTopOffset, pinnedTop, pinnedBottom || transitionParams.changePinnedBottomProgress != 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBackgroundTopY(int offset) {
|
||||||
|
Theme.MessageDrawable drawable = currentBackgroundDrawable;
|
||||||
|
int w = parentWidth;
|
||||||
|
int h = parentHeight;
|
||||||
|
if (h == 0) {
|
||||||
|
w = getParentWidth();
|
||||||
|
h = AndroidUtilities.displaySize.y;
|
||||||
|
if (getParent() instanceof View) {
|
||||||
|
View view = (View) getParent();
|
||||||
|
w = view.getMeasuredWidth();
|
||||||
|
h = view.getMeasuredHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawable.setTop((int) (getTop() + parentViewTopOffset + offset), w, h, (int) parentViewTopOffset, pinnedTop, pinnedBottom || transitionParams.changePinnedBottomProgress != 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
float transitionYOffsetForDrawables;
|
||||||
public void setDrawableBoundsInner(Drawable drawable, int x, int y, int w, int h) {
|
public void setDrawableBoundsInner(Drawable drawable, int x, int y, int w, int h) {
|
||||||
if (drawable != null) {
|
if (drawable != null) {
|
||||||
|
transitionYOffsetForDrawables = (y + h + transitionParams.deltaBottom) - ((int) (y + h + transitionParams.deltaBottom));
|
||||||
drawable.setBounds((int) (x + transitionParams.deltaLeft), (int) (y + transitionParams.deltaTop), (int) (x + w + transitionParams.deltaRight), (int) (y + h + transitionParams.deltaBottom));
|
drawable.setBounds((int) (x + transitionParams.deltaLeft), (int) (y + transitionParams.deltaTop), (int) (x + w + transitionParams.deltaRight), (int) (y + h + transitionParams.deltaBottom));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10384,6 +10460,12 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean needRestore = false;
|
||||||
|
if (transitionYOffsetForDrawables != 0) {
|
||||||
|
needRestore = true;
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(0, transitionYOffsetForDrawables);
|
||||||
|
}
|
||||||
if (drawBackground && currentBackgroundDrawable != null && (currentPosition == null || isDrawSelectionBackground() && (currentMessageObject.isMusic() || currentMessageObject.isDocument())) && !(enterTransitionInPorgress && !currentMessageObject.isVoice())) {
|
if (drawBackground && currentBackgroundDrawable != null && (currentPosition == null || isDrawSelectionBackground() && (currentMessageObject.isMusic() || currentMessageObject.isDocument())) && !(enterTransitionInPorgress && !currentMessageObject.isVoice())) {
|
||||||
if (isHighlightedAnimated) {
|
if (isHighlightedAnimated) {
|
||||||
currentBackgroundDrawable.setAlpha((int) (255 * alphaInternal));
|
currentBackgroundDrawable.setAlpha((int) (255 * alphaInternal));
|
||||||
|
@ -10433,15 +10515,18 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
drawable.setBounds(rect.left, rect.top, rect.right + AndroidUtilities.dp(6), rect.bottom);
|
drawable.setBounds(rect.left, rect.top, rect.right + AndroidUtilities.dp(6), rect.bottom);
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.clipRect(rect.right - AndroidUtilities.dp(12), rect.bottom - AndroidUtilities.dp(16), rect.right + AndroidUtilities.dp(12), rect.bottom);
|
canvas.clipRect(rect.right - AndroidUtilities.dp(12), rect.bottom - AndroidUtilities.dp(16), rect.right + AndroidUtilities.dp(12), rect.bottom);
|
||||||
|
int w = parentWidth;
|
||||||
int h = parentHeight;
|
int h = parentHeight;
|
||||||
if (h == 0) {
|
if (h == 0) {
|
||||||
|
w = getParentWidth();
|
||||||
h = AndroidUtilities.displaySize.y;
|
h = AndroidUtilities.displaySize.y;
|
||||||
if (getParent() instanceof View) {
|
if (getParent() instanceof View) {
|
||||||
View view = (View) getParent();
|
View view = (View) getParent();
|
||||||
|
w = view.getMeasuredWidth();
|
||||||
h = view.getMeasuredHeight();
|
h = view.getMeasuredHeight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drawable.setTop((int) (getY() + parentViewTopOffset), h, pinnedTop, pinnedBottom);
|
drawable.setTop((int) (getY() + parentViewTopOffset), w, h, (int) parentViewTopOffset, pinnedTop, pinnedBottom);
|
||||||
float alpha = !mediaBackground && !pinnedBottom ? transitionParams.changePinnedBottomProgress : (1f - transitionParams.changePinnedBottomProgress);
|
float alpha = !mediaBackground && !pinnedBottom ? transitionParams.changePinnedBottomProgress : (1f - transitionParams.changePinnedBottomProgress);
|
||||||
drawable.setAlpha((int) (255 * alpha));
|
drawable.setAlpha((int) (255 * alpha));
|
||||||
drawable.draw(canvas);
|
drawable.draw(canvas);
|
||||||
|
@ -10466,6 +10551,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (needRestore) {
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
if (isHighlightedAnimated) {
|
if (isHighlightedAnimated) {
|
||||||
long newTime = System.currentTimeMillis();
|
long newTime = System.currentTimeMillis();
|
||||||
long dt = Math.abs(newTime - lastHighlightProgressTime);
|
long dt = Math.abs(newTime - lastHighlightProgressTime);
|
||||||
|
@ -10802,7 +10890,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
return getBackgroundDrawableTop() + layoutHeight - offsetBottom + additionalBottom;
|
return getBackgroundDrawableTop() + layoutHeight - offsetBottom + additionalBottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void drawBackground(Canvas canvas, int left, int top, int right, int bottom, boolean pinnedTop, boolean pinnedBottom, boolean selected) {
|
public void drawBackground(Canvas canvas, int left, int top, int right, int bottom, boolean pinnedTop, boolean pinnedBottom, boolean selected, int keyboardHeight) {
|
||||||
if (currentMessageObject.isOutOwner()) {
|
if (currentMessageObject.isOutOwner()) {
|
||||||
if (!mediaBackground && !pinnedBottom) {
|
if (!mediaBackground && !pinnedBottom) {
|
||||||
currentBackgroundDrawable = selected ? Theme.chat_msgOutSelectedDrawable : Theme.chat_msgOutDrawable;
|
currentBackgroundDrawable = selected ? Theme.chat_msgOutSelectedDrawable : Theme.chat_msgOutDrawable;
|
||||||
|
@ -10817,17 +10905,20 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int w = parentWidth;
|
||||||
int h = parentHeight;
|
int h = parentHeight;
|
||||||
if (h == 0) {
|
if (h == 0) {
|
||||||
|
w = getParentWidth();
|
||||||
h = AndroidUtilities.displaySize.y;
|
h = AndroidUtilities.displaySize.y;
|
||||||
if (getParent() instanceof View) {
|
if (getParent() instanceof View) {
|
||||||
View view = (View) getParent();
|
View view = (View) getParent();
|
||||||
|
w = view.getMeasuredWidth();
|
||||||
h = view.getMeasuredHeight();
|
h = view.getMeasuredHeight();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentBackgroundDrawable != null) {
|
if (currentBackgroundDrawable != null) {
|
||||||
currentBackgroundDrawable.setTop(0, h, pinnedTop, pinnedBottom);
|
currentBackgroundDrawable.setTop(keyboardHeight, w, h, (int) parentViewTopOffset, pinnedTop, pinnedBottom);
|
||||||
Drawable currentBackgroundShadowDrawable = currentBackgroundDrawable.getShadowDrawable();
|
Drawable currentBackgroundShadowDrawable = currentBackgroundDrawable.getShadowDrawable();
|
||||||
if (currentBackgroundShadowDrawable != null) {
|
if (currentBackgroundShadowDrawable != null) {
|
||||||
currentBackgroundShadowDrawable.setAlpha((int) (getAlpha() * 255));
|
currentBackgroundShadowDrawable.setAlpha((int) (getAlpha() * 255));
|
||||||
|
@ -11006,24 +11097,62 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drawForwardedName && forwardedNameLayout[0] != null && forwardedNameLayout[1] != null && (currentPosition == null || currentPosition.minY == 0 && currentPosition.minX == 0)) {
|
boolean drawForwardedNameLocal = drawForwardedName;
|
||||||
if (currentMessageObject.type == MessageObject.TYPE_ROUND_VIDEO) {
|
StaticLayout[] forwardedNameLayoutLocal = forwardedNameLayout;
|
||||||
|
float animatingAlpha = 1f;
|
||||||
|
int forwardedNameWidthLocal = forwardedNameWidth;
|
||||||
|
if (transitionParams.animateForwardedLayout) {
|
||||||
|
if (!currentMessageObject.needDrawForwarded()) {
|
||||||
|
drawForwardedNameLocal = true;
|
||||||
|
forwardedNameLayoutLocal = transitionParams.animatingForwardedNameLayout;
|
||||||
|
animatingAlpha = 1f - transitionParams.animateChangeProgress;
|
||||||
|
forwardedNameWidthLocal = transitionParams.animateForwardNameWidth;
|
||||||
|
} else {
|
||||||
|
animatingAlpha = transitionParams.animateChangeProgress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float forwardNameXLocal;
|
||||||
|
if (drawForwardedNameLocal && forwardedNameLayoutLocal[0] != null && forwardedNameLayoutLocal[1] != null && (currentPosition == null || currentPosition.minY == 0 && currentPosition.minX == 0)) {
|
||||||
|
if (currentMessageObject.type == MessageObject.TYPE_ROUND_VIDEO || currentMessageObject.isAnyKindOfSticker()) {
|
||||||
Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_stickerReplyNameText));
|
Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_stickerReplyNameText));
|
||||||
if (currentMessageObject.isOutOwner()) {
|
if (currentMessageObject.needDrawForwarded()) {
|
||||||
forwardNameX = AndroidUtilities.dp(23);
|
if (currentMessageObject.isOutOwner()) {
|
||||||
|
forwardNameXLocal = forwardNameX = AndroidUtilities.dp(23);
|
||||||
|
} else {
|
||||||
|
forwardNameXLocal = forwardNameX = backgroundDrawableLeft + backgroundDrawableRight + AndroidUtilities.dp(17);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
forwardNameX = backgroundDrawableLeft + backgroundDrawableRight + AndroidUtilities.dp(17);
|
forwardNameXLocal = transitionParams.animateForwardNameX;
|
||||||
|
}
|
||||||
|
if (currentMessageObject.isOutOwner() && currentMessageObject.type == MessageObject.TYPE_ROUND_VIDEO && transitionParams.animatePlayingRound || isPlayingRound) {
|
||||||
|
forwardNameXLocal -= AndroidUtilities.dp (78) * (isPlayingRound ? transitionParams.animateChangeProgress : (1f - transitionParams.animateChangeProgress));
|
||||||
}
|
}
|
||||||
forwardNameY = AndroidUtilities.dp(12);
|
forwardNameY = AndroidUtilities.dp(12);
|
||||||
|
|
||||||
int backWidth = forwardedNameWidth + AndroidUtilities.dp(14);
|
int backWidth = forwardedNameWidthLocal + AndroidUtilities.dp(14);
|
||||||
|
|
||||||
rect.set((int) forwardNameX - AndroidUtilities.dp(7), forwardNameY - AndroidUtilities.dp(6), (int) forwardNameX - AndroidUtilities.dp(7) + backWidth, forwardNameY + AndroidUtilities.dp(38));
|
rect.set((int) forwardNameXLocal - AndroidUtilities.dp(7), forwardNameY - AndroidUtilities.dp(6), (int) forwardNameXLocal - AndroidUtilities.dp(7) + backWidth, forwardNameY + AndroidUtilities.dp(38));
|
||||||
Theme.applyServiceShaderMatrix(getMeasuredWidth(), backgroundHeight, getX(), viewTop);
|
Theme.applyServiceShaderMatrix(getMeasuredWidth(), backgroundHeight, getX(), viewTop);
|
||||||
|
int oldAlpha1 = -1, oldAlpha2 = -1;
|
||||||
|
if (animatingAlpha != 1f) {
|
||||||
|
oldAlpha1 = Theme.chat_actionBackgroundPaint.getAlpha();
|
||||||
|
Theme.chat_actionBackgroundPaint.setAlpha((int) (oldAlpha1 * animatingAlpha));
|
||||||
|
}
|
||||||
canvas.drawRoundRect(rect, AndroidUtilities.dp(6), AndroidUtilities.dp(6), Theme.chat_actionBackgroundPaint);
|
canvas.drawRoundRect(rect, AndroidUtilities.dp(6), AndroidUtilities.dp(6), Theme.chat_actionBackgroundPaint);
|
||||||
if (Theme.hasGradientService()) {
|
if (Theme.hasGradientService()) {
|
||||||
|
if (animatingAlpha != 1f) {
|
||||||
|
oldAlpha2 = Theme.chat_actionBackgroundGradientDarkenPaint.getAlpha();
|
||||||
|
Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha((int) (oldAlpha2 * animatingAlpha));
|
||||||
|
}
|
||||||
canvas.drawRoundRect(rect, AndroidUtilities.dp(6), AndroidUtilities.dp(6), Theme.chat_actionBackgroundGradientDarkenPaint);
|
canvas.drawRoundRect(rect, AndroidUtilities.dp(6), AndroidUtilities.dp(6), Theme.chat_actionBackgroundGradientDarkenPaint);
|
||||||
}
|
}
|
||||||
|
if (oldAlpha1 >= 0) {
|
||||||
|
Theme.chat_actionBackgroundPaint.setAlpha(oldAlpha1);
|
||||||
|
}
|
||||||
|
if (oldAlpha2 >= 0) {
|
||||||
|
Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha(oldAlpha2);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
forwardNameY = AndroidUtilities.dp(10 + (drawNameLayout ? 19 : 0));
|
forwardNameY = AndroidUtilities.dp(10 + (drawNameLayout ? 19 : 0));
|
||||||
if (currentMessageObject.isOutOwner()) {
|
if (currentMessageObject.isOutOwner()) {
|
||||||
|
@ -11032,24 +11161,62 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
} else {
|
} else {
|
||||||
Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_outForwardedNameText));
|
Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_outForwardedNameText));
|
||||||
}
|
}
|
||||||
forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(11) + getExtraTextX();
|
if (currentMessageObject.needDrawForwarded()) {
|
||||||
|
forwardNameXLocal = forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(11) + getExtraTextX();
|
||||||
|
} else {
|
||||||
|
forwardNameXLocal = transitionParams.animateForwardNameX;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (hasPsaHint) {
|
if (hasPsaHint) {
|
||||||
Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_inPsaNameText));
|
Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_inPsaNameText));
|
||||||
} else {
|
} else {
|
||||||
Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_inForwardedNameText));
|
Theme.chat_forwardNamePaint.setColor(Theme.getColor(Theme.key_chat_inForwardedNameText));
|
||||||
}
|
}
|
||||||
if (mediaBackground) {
|
if (currentMessageObject.needDrawForwarded()) {
|
||||||
forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(11) + getExtraTextX();
|
if (mediaBackground) {
|
||||||
|
forwardNameXLocal = forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(11) + getExtraTextX();
|
||||||
|
} else {
|
||||||
|
forwardNameXLocal = forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(drawPinnedBottom ? 11 : 17) + getExtraTextX();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
forwardNameX = backgroundDrawableLeft + AndroidUtilities.dp(drawPinnedBottom ? 11 : 17) + getExtraTextX();
|
forwardNameXLocal = transitionParams.animateForwardNameX;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
boolean clipContent = false;
|
||||||
|
if (transitionParams.animateForwardedLayout) {
|
||||||
|
if (currentBackgroundDrawable != null && currentMessagesGroup == null && currentMessageObject.type != MessageObject.TYPE_ROUND_VIDEO && !currentMessageObject.isAnyKindOfSticker()) {
|
||||||
|
Rect r = currentBackgroundDrawable.getBounds();
|
||||||
|
canvas.save();
|
||||||
|
if (currentMessageObject.isOutOwner() && !mediaBackground && !pinnedBottom) {
|
||||||
|
canvas.clipRect(
|
||||||
|
r.left + AndroidUtilities.dp(4), r.top + AndroidUtilities.dp(4),
|
||||||
|
r.right - AndroidUtilities.dp(10), r.bottom - AndroidUtilities.dp(4)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
canvas.clipRect(
|
||||||
|
r.left + AndroidUtilities.dp(4), r.top + AndroidUtilities.dp(4),
|
||||||
|
r.right - AndroidUtilities.dp(4), r.bottom - AndroidUtilities.dp(4)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
clipContent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int a = 0; a < 2; a++) {
|
for (int a = 0; a < 2; a++) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.translate(forwardNameX - forwardNameOffsetX[a], forwardNameY + AndroidUtilities.dp(16) * a);
|
canvas.translate(forwardNameXLocal - forwardNameOffsetX[a], forwardNameY + AndroidUtilities.dp(16) * a);
|
||||||
forwardedNameLayout[a].draw(canvas);
|
if (animatingAlpha != 1f) {
|
||||||
|
int oldAlpha = forwardedNameLayoutLocal[a].getPaint().getAlpha();
|
||||||
|
forwardedNameLayoutLocal[a].getPaint().setAlpha((int) (oldAlpha * animatingAlpha));
|
||||||
|
forwardedNameLayoutLocal[a].draw(canvas);
|
||||||
|
forwardedNameLayoutLocal[a].getPaint().setAlpha(oldAlpha);
|
||||||
|
} else {
|
||||||
|
forwardedNameLayoutLocal[a].draw(canvas);
|
||||||
|
}
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
if (clipContent) {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11209,7 +11376,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
checkBox.onAttachedToWindow();
|
checkBox.onAttachedToWindow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (visible && mediaCheckBox == null && currentMessagesGroup != null && currentMessagesGroup.messages.size() > 1) {
|
if (visible && mediaCheckBox == null && ((currentMessagesGroup != null && currentMessagesGroup.messages.size() > 1) || (groupedMessagesToSet != null && groupedMessagesToSet.messages.size() > 1))) {
|
||||||
mediaCheckBox = new CheckBoxBase(this, 21);
|
mediaCheckBox = new CheckBoxBase(this, 21);
|
||||||
mediaCheckBox.setUseDefaultCheck(true);
|
mediaCheckBox.setUseDefaultCheck(true);
|
||||||
if (attachedToWindow) {
|
if (attachedToWindow) {
|
||||||
|
@ -11780,7 +11947,11 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
Theme.chat_timePaint.setAlpha(255);
|
Theme.chat_timePaint.setAlpha(255);
|
||||||
} else {
|
} else {
|
||||||
timeYOffset = -(drawCommentButton ? AndroidUtilities.dp(43) : 0);
|
if (currentMessageObject.isSponsored()) {
|
||||||
|
timeYOffset = -AndroidUtilities.dp(48);
|
||||||
|
} else {
|
||||||
|
timeYOffset = -(drawCommentButton ? AndroidUtilities.dp(43) : 0);
|
||||||
|
}
|
||||||
float additionalX = -timeLayout.getLineLeft(0);
|
float additionalX = -timeLayout.getLineLeft(0);
|
||||||
if (ChatObject.isChannel(currentChat) && !currentChat.megagroup || (currentMessageObject.messageOwner.flags & TLRPC.MESSAGE_FLAG_HAS_VIEWS) != 0 || (repliesLayout != null || transitionParams.animateReplies) || (isPinned || transitionParams.animatePinned)) {
|
if (ChatObject.isChannel(currentChat) && !currentChat.megagroup || (currentMessageObject.messageOwner.flags & TLRPC.MESSAGE_FLAG_HAS_VIEWS) != 0 || (repliesLayout != null || transitionParams.animateReplies) || (isPinned || transitionParams.animatePinned)) {
|
||||||
additionalX += this.timeWidth - timeLayout.getLineWidth(0);
|
additionalX += this.timeWidth - timeLayout.getLineWidth(0);
|
||||||
|
@ -11870,6 +12041,12 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
boolean drawClock = (currentStatus & 4) != 0;
|
boolean drawClock = (currentStatus & 4) != 0;
|
||||||
boolean drawError = (currentStatus & 8) != 0;
|
boolean drawError = (currentStatus & 8) != 0;
|
||||||
boolean isBroadcast = (currentStatus & 16) != 0;
|
boolean isBroadcast = (currentStatus & 16) != 0;
|
||||||
|
boolean needRestore = false;
|
||||||
|
if (transitionYOffsetForDrawables != 0) {
|
||||||
|
needRestore = true;
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(0, transitionYOffsetForDrawables);
|
||||||
|
}
|
||||||
if (statusDrawableAnimationInProgress) {
|
if (statusDrawableAnimationInProgress) {
|
||||||
boolean outDrawCheck1 = (animateFromStatusDrawableParams & 1) != 0;
|
boolean outDrawCheck1 = (animateFromStatusDrawableParams & 1) != 0;
|
||||||
boolean outDrawCheck2 = (animateFromStatusDrawableParams & 2) != 0;
|
boolean outDrawCheck2 = (animateFromStatusDrawableParams & 2) != 0;
|
||||||
|
@ -11885,6 +12062,9 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
} else {
|
} else {
|
||||||
drawStatusDrawable(canvas, drawCheck1, drawCheck2, drawClock, drawError, isBroadcast, alpha, bigRadius, timeYOffset, layoutHeight, 1, false, drawSelectionBackground);
|
drawStatusDrawable(canvas, drawCheck1, drawCheck2, drawClock, drawError, isBroadcast, alpha, bigRadius, timeYOffset, layoutHeight, 1, false, drawSelectionBackground);
|
||||||
}
|
}
|
||||||
|
if (needRestore) {
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
transitionParams.lastStatusDrawableParams = transitionParams.createStatusDrawableParams();
|
transitionParams.lastStatusDrawableParams = transitionParams.createStatusDrawableParams();
|
||||||
if (fromParent && drawClock && getParent() != null) {
|
if (fromParent && drawClock && getParent() != null) {
|
||||||
((View) getParent()).invalidate();
|
((View) getParent()).invalidate();
|
||||||
|
@ -12787,6 +12967,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
Theme.chat_livePaint.setColor(color2);
|
Theme.chat_livePaint.setColor(color2);
|
||||||
Theme.chat_locationAddressPaint.setColor(color2);
|
Theme.chat_locationAddressPaint.setColor(color2);
|
||||||
|
|
||||||
|
canvas.save();
|
||||||
|
if (transitionParams.animateForwardedLayout) {
|
||||||
|
float y = namesOffset * transitionParams.animateChangeProgress + transitionParams.animateForwardedNamesOffset * (1f - transitionParams.animateChangeProgress);
|
||||||
|
if (currentMessageObject.needDrawForwarded()) {
|
||||||
|
y -= namesOffset;
|
||||||
|
}
|
||||||
|
canvas.translate(0, y);
|
||||||
|
}
|
||||||
int x;
|
int x;
|
||||||
if (currentMessageObject.isOutOwner()) {
|
if (currentMessageObject.isOutOwner()) {
|
||||||
x = layoutWidth - backgroundWidth + AndroidUtilities.dp(11);
|
x = layoutWidth - backgroundWidth + AndroidUtilities.dp(11);
|
||||||
|
@ -13060,6 +13248,7 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
updatePollAnimations(dt);
|
updatePollAnimations(dt);
|
||||||
|
canvas.restore();
|
||||||
} else if (currentMessageObject.type == 12) {
|
} else if (currentMessageObject.type == 12) {
|
||||||
if (currentMessageObject.isOutOwner()) {
|
if (currentMessageObject.isOutOwner()) {
|
||||||
Theme.chat_contactNamePaint.setColor(Theme.getColor(Theme.key_chat_outContactNameText));
|
Theme.chat_contactNamePaint.setColor(Theme.getColor(Theme.key_chat_outContactNameText));
|
||||||
|
@ -14236,6 +14425,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
return isRoundVideo && isPlayingRound;
|
return isRoundVideo && isPlayingRound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getParentWidth() {
|
||||||
|
MessageObject object = currentMessageObject == null ? messageObjectToSet : currentMessageObject;
|
||||||
|
if (object != null && object.preview && parentWidth > 0) {
|
||||||
|
return parentWidth;
|
||||||
|
}
|
||||||
|
return AndroidUtilities.displaySize.x;
|
||||||
|
}
|
||||||
|
|
||||||
public class TransitionParams {
|
public class TransitionParams {
|
||||||
|
|
||||||
public float lastDrawingImageX, lastDrawingImageY, lastDrawingImageW, lastDrawingImageH;
|
public float lastDrawingImageX, lastDrawingImageY, lastDrawingImageW, lastDrawingImageH;
|
||||||
|
@ -14354,8 +14551,24 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
private StaticLayout lastTimeLayout;
|
private StaticLayout lastTimeLayout;
|
||||||
private boolean lastIsPlayingRound;
|
private boolean lastIsPlayingRound;
|
||||||
public boolean animatePlayingRound;
|
public boolean animatePlayingRound;
|
||||||
|
public boolean animateText;
|
||||||
|
|
||||||
|
public float lastDrawingTextY;
|
||||||
|
public float lastDrawingTextX;
|
||||||
|
|
||||||
|
public float animateFromTextY;
|
||||||
|
|
||||||
public int lastTopOffset;
|
public int lastTopOffset;
|
||||||
|
public boolean animateForwardedLayout;
|
||||||
|
public int animateForwardedNamesOffset;
|
||||||
|
public int lastForwardedNamesOffset;
|
||||||
|
public boolean lastDrawnForwardedName;
|
||||||
|
public StaticLayout[] lastDrawnForwardedNameLayout = new StaticLayout[2];
|
||||||
|
public StaticLayout[] animatingForwardedNameLayout = new StaticLayout[2];
|
||||||
|
float animateForwardNameX;
|
||||||
|
float lastForwardNameX;
|
||||||
|
int animateForwardNameWidth;
|
||||||
|
int lastForwardNameWidth;
|
||||||
|
|
||||||
public void recordDrawingState() {
|
public void recordDrawingState() {
|
||||||
wasDraw = true;
|
wasDraw = true;
|
||||||
|
@ -14414,8 +14627,26 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
|
|
||||||
lastLocatinIsExpired = locationExpired;
|
lastLocatinIsExpired = locationExpired;
|
||||||
lastIsPlayingRound = isPlayingRound;
|
lastIsPlayingRound = isPlayingRound;
|
||||||
|
|
||||||
|
lastDrawingTextY = textY;
|
||||||
|
lastDrawingTextX = textX;
|
||||||
|
|
||||||
|
lastDrawnForwardedNameLayout[0] = forwardedNameLayout[0];
|
||||||
|
lastDrawnForwardedNameLayout[1] = forwardedNameLayout[1];
|
||||||
|
lastDrawnForwardedName = currentMessageObject.needDrawForwarded();
|
||||||
|
lastForwardNameX = forwardNameX;
|
||||||
|
lastForwardedNamesOffset = namesOffset;
|
||||||
|
lastForwardNameWidth = forwardedNameWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void recordDrawingStatePreview() {
|
||||||
|
lastDrawnForwardedNameLayout[0] = forwardedNameLayout[0];
|
||||||
|
lastDrawnForwardedNameLayout[1] = forwardedNameLayout[1];
|
||||||
|
lastDrawnForwardedName = currentMessageObject.needDrawForwarded();
|
||||||
|
lastForwardNameX = forwardNameX;
|
||||||
|
lastForwardedNamesOffset = namesOffset;
|
||||||
|
lastForwardNameWidth = forwardedNameWidth;
|
||||||
|
}
|
||||||
public boolean animateChange() {
|
public boolean animateChange() {
|
||||||
if (!wasDraw) {
|
if (!wasDraw) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -14473,6 +14704,14 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
animateReplaceCaptionLayout = true;
|
animateReplaceCaptionLayout = true;
|
||||||
animateOutCaptionLayout = lastDrawingCaptionLayout;
|
animateOutCaptionLayout = lastDrawingCaptionLayout;
|
||||||
changed = true;
|
changed = true;
|
||||||
|
} else {
|
||||||
|
updateCaptionLayout();
|
||||||
|
if (lastDrawingCaptionX != captionX || lastDrawingCaptionY != captionY) {
|
||||||
|
moveCaption = true;
|
||||||
|
captionFromX = lastDrawingCaptionX;
|
||||||
|
captionFromY = lastDrawingCaptionY;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (captionLayout != null && lastDrawingCaptionLayout != null) {
|
} else if (captionLayout != null && lastDrawingCaptionLayout != null) {
|
||||||
updateCaptionLayout();
|
updateCaptionLayout();
|
||||||
|
@ -14571,6 +14810,24 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
animatePlayingRound = true;
|
animatePlayingRound = true;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastDrawingTextY != textY) {
|
||||||
|
animateText = true;
|
||||||
|
animateFromTextY = lastDrawingTextY;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMessageObject != null) {
|
||||||
|
if (lastDrawnForwardedName != currentMessageObject.needDrawForwarded()) {
|
||||||
|
animateForwardedLayout = true;
|
||||||
|
animatingForwardedNameLayout[0] = lastDrawnForwardedNameLayout[0];
|
||||||
|
animatingForwardedNameLayout[1] = lastDrawnForwardedNameLayout[1];
|
||||||
|
animateForwardNameX = lastForwardNameX;
|
||||||
|
animateForwardedNamesOffset = lastForwardedNamesOffset;
|
||||||
|
animateForwardNameWidth = lastForwardNameWidth;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14627,6 +14884,10 @@ public class ChatMessageCell extends BaseCell implements SeekBar.SeekBarDelegate
|
||||||
animateDrawingTimeAlpha = false;
|
animateDrawingTimeAlpha = false;
|
||||||
animateLocationIsExpired = false;
|
animateLocationIsExpired = false;
|
||||||
animatePlayingRound = false;
|
animatePlayingRound = false;
|
||||||
|
animateText = false;
|
||||||
|
animateForwardedLayout = false;
|
||||||
|
animatingForwardedNameLayout[0] = null;
|
||||||
|
animatingForwardedNameLayout[1] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean supportChangeAnimation() {
|
public boolean supportChangeAnimation() {
|
||||||
|
|
|
@ -377,10 +377,25 @@ public class DialogCell extends BaseCell {
|
||||||
user = newUser;
|
user = newUser;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean isOnline = user != null && !user.self && (user.status != null && user.status.expires > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || MessagesController.getInstance(currentAccount).onlinePrivacy.containsKey(user.id));
|
boolean isOnline = isOnline();
|
||||||
onlineProgress = isOnline ? 1.0f : 0.0f;
|
onlineProgress = isOnline ? 1.0f : 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isOnline() {
|
||||||
|
if (user == null || user.self) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (user.status != null && user.status.expires <= 0) {
|
||||||
|
if (MessagesController.getInstance(currentAccount).onlinePrivacy.containsKey(user.id)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (user.status != null && user.status.expires > ConnectionsManager.getInstance(currentAccount).getCurrentTime()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void checkGroupCall() {
|
private void checkGroupCall() {
|
||||||
hasCall = chat != null && chat.call_active && chat.call_not_empty;
|
hasCall = chat != null && chat.call_active && chat.call_not_empty;
|
||||||
chatCallProgress = hasCall ? 1.0f : 0.0f;
|
chatCallProgress = hasCall ? 1.0f : 0.0f;
|
||||||
|
@ -598,6 +613,7 @@ public class DialogCell extends BaseCell {
|
||||||
boolean showChecks = !UserObject.isUserSelf(user) && !useMeForMyMessages;
|
boolean showChecks = !UserObject.isUserSelf(user) && !useMeForMyMessages;
|
||||||
boolean drawTime = true;
|
boolean drawTime = true;
|
||||||
printingStringType = -1;
|
printingStringType = -1;
|
||||||
|
int printigStingReplaceIndex = -1;
|
||||||
|
|
||||||
String messageFormat;
|
String messageFormat;
|
||||||
boolean hasNameInMessage;
|
boolean hasNameInMessage;
|
||||||
|
@ -875,7 +891,15 @@ public class DialogCell extends BaseCell {
|
||||||
startPadding = statusDrawable.getIntrinsicWidth() + AndroidUtilities.dp(3);
|
startPadding = statusDrawable.getIntrinsicWidth() + AndroidUtilities.dp(3);
|
||||||
}
|
}
|
||||||
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
|
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder();
|
||||||
spannableStringBuilder.append(" ").append(TextUtils.replace(printingString, new String[]{"..."}, new String[]{""})).setSpan(new FixedWidthSpan(startPadding), 0, 1, 0);
|
|
||||||
|
if (printingStringType == 5) {
|
||||||
|
printigStingReplaceIndex = printingString.toString().indexOf("**oo**");
|
||||||
|
}
|
||||||
|
if (printigStingReplaceIndex > 0) {
|
||||||
|
spannableStringBuilder.append(TextUtils.replace(printingString, new String[]{"..."}, new String[]{""})).setSpan(new FixedWidthSpan(Theme.getChatStatusDrawable(printingStringType).getIntrinsicWidth()), printigStingReplaceIndex, printigStingReplaceIndex + 6, 0);
|
||||||
|
} else {
|
||||||
|
spannableStringBuilder.append(" ").append(TextUtils.replace(printingString, new String[]{"..."}, new String[]{""})).setSpan(new FixedWidthSpan(startPadding), 0, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
messageString = spannableStringBuilder;
|
messageString = spannableStringBuilder;
|
||||||
currentMessagePaint = Theme.dialogs_messagePrintingPaint[paintIndex];
|
currentMessagePaint = Theme.dialogs_messagePrintingPaint[paintIndex];
|
||||||
|
@ -1725,8 +1749,14 @@ public class DialogCell extends BaseCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (messageLayout != null && printingStringType >= 0) {
|
if (messageLayout != null && printingStringType >= 0) {
|
||||||
float x1 = messageLayout.getPrimaryHorizontal(0);
|
float x1, x2;
|
||||||
float x2 = messageLayout.getPrimaryHorizontal(1);
|
if (printigStingReplaceIndex >= 0){
|
||||||
|
x1 = messageLayout.getPrimaryHorizontal(printigStingReplaceIndex);
|
||||||
|
x2 = messageLayout.getPrimaryHorizontal(printigStingReplaceIndex + 1);
|
||||||
|
} else {
|
||||||
|
x1 = messageLayout.getPrimaryHorizontal(0);
|
||||||
|
x2 = messageLayout.getPrimaryHorizontal(1);
|
||||||
|
}
|
||||||
if (x1 < x2) {
|
if (x1 < x2) {
|
||||||
statusDrawableLeft = (int) (messageLeft + x1);
|
statusDrawableLeft = (int) (messageLeft + x1);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2802,7 +2832,7 @@ public class DialogCell extends BaseCell {
|
||||||
|
|
||||||
if (isDialogCell && currentDialogFolderId == 0) {
|
if (isDialogCell && currentDialogFolderId == 0) {
|
||||||
if (user != null && !MessagesController.isSupportUser(user) && !user.bot) {
|
if (user != null && !MessagesController.isSupportUser(user) && !user.bot) {
|
||||||
boolean isOnline = !user.self && (user.status != null && user.status.expires > ConnectionsManager.getInstance(currentAccount).getCurrentTime() || MessagesController.getInstance(currentAccount).onlinePrivacy.containsKey(user.id));
|
boolean isOnline = isOnline();
|
||||||
if (isOnline || onlineProgress != 0) {
|
if (isOnline || onlineProgress != 0) {
|
||||||
int top = (int) (avatarImage.getImageY2() - AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 6 : 8));
|
int top = (int) (avatarImage.getImageY2() - AndroidUtilities.dp(useForceThreeLines || SharedConfig.useThreeLinesLayout ? 6 : 8));
|
||||||
int left;
|
int left;
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.telegram.tgnet.TLRPC;
|
||||||
import org.telegram.ui.ActionBar.Theme;
|
import org.telegram.ui.ActionBar.Theme;
|
||||||
import org.telegram.ui.Components.AvatarDrawable;
|
import org.telegram.ui.Components.AvatarDrawable;
|
||||||
import org.telegram.ui.Components.BackupImageView;
|
import org.telegram.ui.Components.BackupImageView;
|
||||||
|
import org.telegram.ui.Components.CheckBox2;
|
||||||
import org.telegram.ui.Components.CounterView;
|
import org.telegram.ui.Components.CounterView;
|
||||||
import org.telegram.ui.Components.LayoutHelper;
|
import org.telegram.ui.Components.LayoutHelper;
|
||||||
|
|
||||||
|
@ -46,9 +47,12 @@ public class HintDialogCell extends FrameLayout {
|
||||||
boolean wasDraw;
|
boolean wasDraw;
|
||||||
|
|
||||||
CounterView counterView;
|
CounterView counterView;
|
||||||
|
CheckBox2 checkBox;
|
||||||
|
private final boolean drawCheckbox;
|
||||||
|
|
||||||
public HintDialogCell(Context context) {
|
public HintDialogCell(Context context, boolean drawCheckbox) {
|
||||||
super(context);
|
super(context);
|
||||||
|
this.drawCheckbox = drawCheckbox;
|
||||||
|
|
||||||
imageView = new BackupImageView(context);
|
imageView = new BackupImageView(context);
|
||||||
imageView.setRoundRadius(AndroidUtilities.dp(27));
|
imageView.setRoundRadius(AndroidUtilities.dp(27));
|
||||||
|
@ -67,12 +71,28 @@ public class HintDialogCell extends FrameLayout {
|
||||||
addView(counterView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 28, Gravity.TOP,0 ,4,0,0));
|
addView(counterView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 28, Gravity.TOP,0 ,4,0,0));
|
||||||
counterView.setColors(Theme.key_chats_unreadCounterText, Theme.key_chats_unreadCounter);
|
counterView.setColors(Theme.key_chats_unreadCounterText, Theme.key_chats_unreadCounter);
|
||||||
counterView.setGravity(Gravity.RIGHT);
|
counterView.setGravity(Gravity.RIGHT);
|
||||||
|
|
||||||
|
if (drawCheckbox) {
|
||||||
|
checkBox = new CheckBox2(context, 21);
|
||||||
|
checkBox.setColor(Theme.key_dialogRoundCheckBox, Theme.key_dialogBackground, Theme.key_dialogRoundCheckBoxCheck);
|
||||||
|
checkBox.setDrawUnchecked(false);
|
||||||
|
checkBox.setDrawBackgroundAsArc(4);
|
||||||
|
checkBox.setProgressDelegate(progress -> {
|
||||||
|
float scale = 1.0f - (1.0f - 0.857f) * checkBox.getProgress();
|
||||||
|
imageView.setScaleX(scale);
|
||||||
|
imageView.setScaleY(scale);
|
||||||
|
invalidate();
|
||||||
|
});
|
||||||
|
addView(checkBox, LayoutHelper.createFrame(24, 24, Gravity.CENTER_HORIZONTAL | Gravity.TOP, 19, 42, 0, 0));
|
||||||
|
checkBox.setChecked(true, false);
|
||||||
|
setWillNotDraw(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(86), MeasureSpec.EXACTLY));
|
super.onMeasure(MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(86), MeasureSpec.EXACTLY));
|
||||||
counterView.horizontalPadding = AndroidUtilities.dp(13);
|
counterView.counterDrawable.horizontalPadding = AndroidUtilities.dp(13);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(int mask) {
|
public void update(int mask) {
|
||||||
|
@ -181,4 +201,25 @@ public class HintDialogCell extends FrameLayout {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
if (drawCheckbox) {
|
||||||
|
int cx = imageView.getLeft() + imageView.getMeasuredWidth() / 2;
|
||||||
|
int cy = imageView.getTop() + imageView.getMeasuredHeight() / 2;
|
||||||
|
Theme.checkboxSquare_checkPaint.setColor(Theme.getColor(Theme.key_dialogRoundCheckBox));
|
||||||
|
Theme.checkboxSquare_checkPaint.setAlpha((int) (checkBox.getProgress() * 255));
|
||||||
|
canvas.drawCircle(cx, cy, AndroidUtilities.dp(28), Theme.checkboxSquare_checkPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChecked(boolean checked, boolean animated) {
|
||||||
|
if (drawCheckbox) {
|
||||||
|
checkBox.setChecked(checked, animated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getDialogId() {
|
||||||
|
return dialog_id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,9 @@ public class LanguageCell extends FrameLayout {
|
||||||
|
|
||||||
public LanguageCell(Context context, boolean dialog) {
|
public LanguageCell(Context context, boolean dialog) {
|
||||||
super(context);
|
super(context);
|
||||||
|
if (Theme.dividerPaint == null) {
|
||||||
|
Theme.createCommonResources(context);
|
||||||
|
}
|
||||||
|
|
||||||
setWillNotDraw(false);
|
setWillNotDraw(false);
|
||||||
isDialog = dialog;
|
isDialog = dialog;
|
||||||
|
|
|
@ -9,16 +9,12 @@ import android.text.SpannableStringBuilder;
|
||||||
import android.text.StaticLayout;
|
import android.text.StaticLayout;
|
||||||
import android.text.TextPaint;
|
import android.text.TextPaint;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.style.ReplacementSpan;
|
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.SoundEffectConstants;
|
import android.view.SoundEffectConstants;
|
||||||
import android.view.accessibility.AccessibilityNodeInfo;
|
import android.view.accessibility.AccessibilityNodeInfo;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.telegram.messenger.AndroidUtilities;
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
import org.telegram.messenger.DownloadController;
|
import org.telegram.messenger.DownloadController;
|
||||||
import org.telegram.messenger.Emoji;
|
import org.telegram.messenger.Emoji;
|
||||||
|
@ -206,7 +202,7 @@ public class SharedAudioCell extends FrameLayout implements DownloadController.F
|
||||||
currentMessageObject = messageObject;
|
currentMessageObject = messageObject;
|
||||||
TLRPC.Document document = messageObject.getDocument();
|
TLRPC.Document document = messageObject.getDocument();
|
||||||
|
|
||||||
TLRPC.PhotoSize thumb = document != null ? FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 240) : null;
|
TLRPC.PhotoSize thumb = document != null ? FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 360) : null;
|
||||||
if (thumb instanceof TLRPC.TL_photoSize || thumb instanceof TLRPC.TL_photoSizeProgressive) {
|
if (thumb instanceof TLRPC.TL_photoSize || thumb instanceof TLRPC.TL_photoSizeProgressive) {
|
||||||
radialProgress.setImageOverlay(thumb, document, messageObject);
|
radialProgress.setImageOverlay(thumb, document, messageObject);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -77,7 +77,7 @@ public class StickerCell extends FrameLayout {
|
||||||
return clearsInputField;
|
return clearsInputField;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSticker(TLRPC.Document document, Object parent, int side) {
|
public void setSticker(TLRPC.Document document, Object parent) {
|
||||||
parentObject = parent;
|
parentObject = parent;
|
||||||
if (document != null) {
|
if (document != null) {
|
||||||
TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90);
|
TLRPC.PhotoSize thumb = FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 90);
|
||||||
|
@ -103,19 +103,6 @@ public class StickerCell extends FrameLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sticker = document;
|
sticker = document;
|
||||||
if (side == -1) {
|
|
||||||
setBackgroundResource(R.drawable.stickers_back_left);
|
|
||||||
setPadding(AndroidUtilities.dp(7), 0, 0, 0);
|
|
||||||
} else if (side == 0) {
|
|
||||||
setBackgroundResource(R.drawable.stickers_back_center);
|
|
||||||
setPadding(0, 0, 0, 0);
|
|
||||||
} else if (side == 1) {
|
|
||||||
setBackgroundResource(R.drawable.stickers_back_right);
|
|
||||||
setPadding(0, 0, AndroidUtilities.dp(7), 0);
|
|
||||||
} else if (side == 2) {
|
|
||||||
setBackgroundResource(R.drawable.stickers_back_all);
|
|
||||||
setPadding(AndroidUtilities.dp(3), 0, AndroidUtilities.dp(3), 0);
|
|
||||||
}
|
|
||||||
Drawable background = getBackground();
|
Drawable background = getBackground();
|
||||||
if (background != null) {
|
if (background != null) {
|
||||||
background.setAlpha(230);
|
background.setAlpha(230);
|
||||||
|
|
|
@ -140,11 +140,11 @@ public class StickerEmojiCell extends FrameLayout {
|
||||||
SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(document, fromEmojiPanel ? Theme.key_emptyListPlaceholder : Theme.key_windowBackgroundGray, fromEmojiPanel ? 0.2f : 1.0f);
|
SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(document, fromEmojiPanel ? Theme.key_emptyListPlaceholder : Theme.key_windowBackgroundGray, fromEmojiPanel ? 0.2f : 1.0f);
|
||||||
if (MessageObject.canAutoplayAnimatedSticker(document)) {
|
if (MessageObject.canAutoplayAnimatedSticker(document)) {
|
||||||
if (svgThumb != null) {
|
if (svgThumb != null) {
|
||||||
imageView.setImage(ImageLocation.getForDocument(document), "80_80", null, svgThumb, parentObject);
|
imageView.setImage(ImageLocation.getForDocument(document), "66_66", null, svgThumb, parentObject);
|
||||||
} else if (thumb != null) {
|
} else if (thumb != null) {
|
||||||
imageView.setImage(ImageLocation.getForDocument(document), "80_80", ImageLocation.getForDocument(thumb, document), null, 0, parentObject);
|
imageView.setImage(ImageLocation.getForDocument(document), "66_66", ImageLocation.getForDocument(thumb, document), null, 0, parentObject);
|
||||||
} else {
|
} else {
|
||||||
imageView.setImage(ImageLocation.getForDocument(document), "80_80", null, null, parentObject);
|
imageView.setImage(ImageLocation.getForDocument(document), "66_66", null, null, parentObject);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (svgThumb != null) {
|
if (svgThumb != null) {
|
||||||
|
|
|
@ -985,7 +985,7 @@ public class ChangePhoneActivity extends BaseFragment {
|
||||||
|
|
||||||
Intent mailer = new Intent(Intent.ACTION_SENDTO);
|
Intent mailer = new Intent(Intent.ACTION_SENDTO);
|
||||||
mailer.setData(Uri.parse("mailto:"));
|
mailer.setData(Uri.parse("mailto:"));
|
||||||
mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"sms@stel.com"});
|
mailer.putExtra(Intent.EXTRA_EMAIL, new String[]{"reports@stel.com"});
|
||||||
mailer.putExtra(Intent.EXTRA_SUBJECT, "Android registration/login issue " + version + " " + emailPhone);
|
mailer.putExtra(Intent.EXTRA_SUBJECT, "Android registration/login issue " + version + " " + emailPhone);
|
||||||
mailer.putExtra(Intent.EXTRA_TEXT, "Phone: " + requestPhone + "\nApp version: " + version + "\nOS version: SDK " + Build.VERSION.SDK_INT + "\nDevice Name: " + Build.MANUFACTURER + Build.MODEL + "\nLocale: " + Locale.getDefault() + "\nError: " + lastError);
|
mailer.putExtra(Intent.EXTRA_TEXT, "Phone: " + requestPhone + "\nApp version: " + version + "\nOS version: SDK " + Build.VERSION.SDK_INT + "\nDevice Name: " + Build.MANUFACTURER + Build.MODEL + "\nLocale: " + Locale.getDefault() + "\nError: " + lastError);
|
||||||
getContext().startActivity(Intent.createChooser(mailer, "Send email..."));
|
getContext().startActivity(Intent.createChooser(mailer, "Send email..."));
|
||||||
|
|
|
@ -1755,7 +1755,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
|
||||||
if (viewBottom > height) {
|
if (viewBottom > height) {
|
||||||
viewBottom = viewTop + height;
|
viewBottom = viewTop + height;
|
||||||
}
|
}
|
||||||
messageCell.setVisiblePart(viewTop, viewBottom - viewTop, contentView.getHeightWithKeyboard() - AndroidUtilities.dp(48) - chatListView.getTop(), 0, view.getY() + actionBar.getMeasuredHeight() - contentView.getBackgroundTranslationY(), contentView.getBackgroundSizeY());
|
messageCell.setVisiblePart(viewTop, viewBottom - viewTop, contentView.getHeightWithKeyboard() - AndroidUtilities.dp(48) - chatListView.getTop(), 0, view.getY() + actionBar.getMeasuredHeight() - contentView.getBackgroundTranslationY(), contentView.getMeasuredWidth(), contentView.getBackgroundSizeY());
|
||||||
|
|
||||||
MessageObject messageObject = messageCell.getMessageObject();
|
MessageObject messageObject = messageCell.getMessageObject();
|
||||||
if (roundVideoContainer != null && messageObject.isRoundVideo() && MediaController.getInstance().isPlayingMessage(messageObject)) {
|
if (roundVideoContainer != null && messageObject.isRoundVideo() && MediaController.getInstance().isPlayingMessage(messageObject)) {
|
||||||
|
@ -2518,7 +2518,7 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
|
||||||
}
|
}
|
||||||
|
|
||||||
if (holder.itemView instanceof ChatMessageCell) {
|
if (holder.itemView instanceof ChatMessageCell) {
|
||||||
((ChatMessageCell) view).setVisiblePart(viewTop, viewBottom - viewTop, contentView.getHeightWithKeyboard() - AndroidUtilities.dp(48) - chatListView.getTop(), 0, view.getY() + actionBar.getMeasuredHeight() - contentView.getBackgroundTranslationY(), contentView.getBackgroundSizeY());
|
((ChatMessageCell) view).setVisiblePart(viewTop, viewBottom - viewTop, contentView.getHeightWithKeyboard() - AndroidUtilities.dp(48) - chatListView.getTop(), 0, view.getY() + actionBar.getMeasuredHeight() - contentView.getBackgroundTranslationY(), contentView.getMeasuredWidth(), contentView.getBackgroundSizeY());
|
||||||
} else if (holder.itemView instanceof ChatActionCell) {
|
} else if (holder.itemView instanceof ChatActionCell) {
|
||||||
if (actionBar != null && contentView != null) {
|
if (actionBar != null && contentView != null) {
|
||||||
((ChatActionCell) view).setVisiblePart(view.getY() + actionBar.getMeasuredHeight() - contentView.getBackgroundTranslationY(), contentView.getBackgroundSizeY());
|
((ChatActionCell) view).setVisiblePart(view.getY() + actionBar.getMeasuredHeight() - contentView.getBackgroundTranslationY(), contentView.getBackgroundSizeY());
|
||||||
|
@ -2750,7 +2750,9 @@ public class ChannelAdminLogActivity extends BaseFragment implements Notificatio
|
||||||
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgOutDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow));
|
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgOutDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow));
|
||||||
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgOutMediaDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow));
|
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, Theme.chat_msgOutMediaDrawable.getShadowDrawables(), null, Theme.key_chat_outBubbleShadow));
|
||||||
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubble));
|
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubble));
|
||||||
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient));
|
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient1));
|
||||||
|
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient2));
|
||||||
|
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutDrawable, Theme.chat_msgOutMediaDrawable}, null, Theme.key_chat_outBubbleGradient3));
|
||||||
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutSelectedDrawable, Theme.chat_msgOutMediaSelectedDrawable}, null, Theme.key_chat_outBubbleSelected));
|
themeDescriptions.add(new ThemeDescription(chatListView, 0, new Class[]{ChatMessageCell.class}, null, new Drawable[]{Theme.chat_msgOutSelectedDrawable, Theme.chat_msgOutMediaSelectedDrawable}, null, Theme.key_chat_outBubbleSelected));
|
||||||
themeDescriptions.add(new ThemeDescription(chatListView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{ChatActionCell.class}, Theme.chat_actionTextPaint, null, null, Theme.key_chat_serviceText));
|
themeDescriptions.add(new ThemeDescription(chatListView, ThemeDescription.FLAG_TEXTCOLOR, new Class[]{ChatActionCell.class}, Theme.chat_actionTextPaint, null, null, Theme.key_chat_serviceText));
|
||||||
themeDescriptions.add(new ThemeDescription(chatListView, ThemeDescription.FLAG_LINKCOLOR, new Class[]{ChatActionCell.class}, Theme.chat_actionTextPaint, null, null, Theme.key_chat_serviceLink));
|
themeDescriptions.add(new ThemeDescription(chatListView, ThemeDescription.FLAG_LINKCOLOR, new Class[]{ChatActionCell.class}, Theme.chat_actionTextPaint, null, null, Theme.key_chat_serviceLink));
|
||||||
|
|
|
@ -499,8 +499,12 @@ public class ChannelCreateActivity extends BaseFragment implements NotificationC
|
||||||
avatarEditor.setAnimation(cameraDrawable);
|
avatarEditor.setAnimation(cameraDrawable);
|
||||||
cameraDrawable.setCurrentFrame(0);
|
cameraDrawable.setCurrentFrame(0);
|
||||||
}, dialog -> {
|
}, dialog -> {
|
||||||
cameraDrawable.setCustomEndFrame(86);
|
if (!imageUpdater.isUploadingImage()) {
|
||||||
avatarEditor.playAnimation();
|
cameraDrawable.setCustomEndFrame(86);
|
||||||
|
avatarEditor.playAnimation();
|
||||||
|
} else {
|
||||||
|
cameraDrawable.setCurrentFrame(0, false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
cameraDrawable.setCurrentFrame(0);
|
cameraDrawable.setCurrentFrame(0);
|
||||||
cameraDrawable.setCustomEndFrame(43);
|
cameraDrawable.setCustomEndFrame(43);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -592,8 +592,13 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image
|
||||||
cameraDrawable.setCurrentFrame(0);
|
cameraDrawable.setCurrentFrame(0);
|
||||||
setAvatarCell.imageView.playAnimation();
|
setAvatarCell.imageView.playAnimation();
|
||||||
}, dialogInterface -> {
|
}, dialogInterface -> {
|
||||||
cameraDrawable.setCustomEndFrame(86);
|
if (!imageUpdater.isUploadingImage()) {
|
||||||
setAvatarCell.imageView.playAnimation();
|
cameraDrawable.setCustomEndFrame(86);
|
||||||
|
setAvatarCell.imageView.playAnimation();
|
||||||
|
} else {
|
||||||
|
cameraDrawable.setCurrentFrame(0, false);
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
cameraDrawable.setCurrentFrame(0);
|
cameraDrawable.setCurrentFrame(0);
|
||||||
cameraDrawable.setCustomEndFrame(43);
|
cameraDrawable.setCustomEndFrame(43);
|
||||||
|
@ -1037,6 +1042,12 @@ public class ChatEditActivity extends BaseFragment implements ImageUpdater.Image
|
||||||
} else {
|
} else {
|
||||||
avatarImage.setImage(ImageLocation.getForLocal(avatar), "50_50", avatarDrawable, currentChat);
|
avatarImage.setImage(ImageLocation.getForLocal(avatar), "50_50", avatarDrawable, currentChat);
|
||||||
setAvatarCell.setTextAndIcon(LocaleController.getString("ChatSetNewPhoto", R.string.ChatSetNewPhoto), R.drawable.menu_camera2, true);
|
setAvatarCell.setTextAndIcon(LocaleController.getString("ChatSetNewPhoto", R.string.ChatSetNewPhoto), R.drawable.menu_camera2, true);
|
||||||
|
if (cameraDrawable == null) {
|
||||||
|
cameraDrawable = new RLottieDrawable(R.raw.camera_outline, "" + R.raw.camera_outline, AndroidUtilities.dp(50), AndroidUtilities.dp(50), false, null);
|
||||||
|
}
|
||||||
|
setAvatarCell.imageView.setTranslationY(-AndroidUtilities.dp(9));
|
||||||
|
setAvatarCell.imageView.setTranslationX(-AndroidUtilities.dp(8));
|
||||||
|
setAvatarCell.imageView.setAnimation(cameraDrawable);
|
||||||
showAvatarProgress(true, false);
|
showAvatarProgress(true, false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,671 @@
|
||||||
|
package org.telegram.ui;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.AnimatorSet;
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.PorterDuffXfermode;
|
||||||
|
import android.graphics.RectF;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.text.StaticLayout;
|
||||||
|
import android.text.TextPaint;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.HapticFeedbackConstants;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.util.Log;
|
||||||
|
|
||||||
|
import org.telegram.messenger.AccountInstance;
|
||||||
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
|
import org.telegram.messenger.DialogObject;
|
||||||
|
import org.telegram.messenger.ImageLocation;
|
||||||
|
import org.telegram.messenger.ImageReceiver;
|
||||||
|
import org.telegram.messenger.LocaleController;
|
||||||
|
import org.telegram.messenger.MessagesController;
|
||||||
|
import org.telegram.messenger.NotificationCenter;
|
||||||
|
import org.telegram.messenger.R;
|
||||||
|
import org.telegram.messenger.UserConfig;
|
||||||
|
import org.telegram.tgnet.TLRPC;
|
||||||
|
import org.telegram.ui.ActionBar.Theme;
|
||||||
|
import org.telegram.ui.Components.AvatarDrawable;
|
||||||
|
import org.telegram.ui.Components.CounterView;
|
||||||
|
import org.telegram.ui.Components.CubicBezierInterpolator;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class ChatPullingDownDrawable implements NotificationCenter.NotificationCenterDelegate {
|
||||||
|
|
||||||
|
public int dialogFolderId;
|
||||||
|
public int dialogFilterId;
|
||||||
|
int lastWidth;
|
||||||
|
float circleRadius;
|
||||||
|
|
||||||
|
Paint arrowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
TextPaint textPaint2 = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
private Paint xRefPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
Path path = new Path();
|
||||||
|
|
||||||
|
StaticLayout chatNameLayout;
|
||||||
|
StaticLayout layout1;
|
||||||
|
StaticLayout layout2;
|
||||||
|
|
||||||
|
int chatNameWidth;
|
||||||
|
int layout1Width;
|
||||||
|
int layout2Width;
|
||||||
|
|
||||||
|
ImageReceiver imageReceiver = new ImageReceiver();
|
||||||
|
TLRPC.Chat nextChat;
|
||||||
|
|
||||||
|
AnimatorSet showReleaseAnimator;
|
||||||
|
|
||||||
|
float swipeToReleaseProgress;
|
||||||
|
float bounceProgress;
|
||||||
|
|
||||||
|
boolean animateSwipeToRelease;
|
||||||
|
boolean animateCheck;
|
||||||
|
float checkProgress;
|
||||||
|
long lastHapticTime;
|
||||||
|
float lastProgress;
|
||||||
|
boolean emptyStub;
|
||||||
|
float progressToBottomPannel;
|
||||||
|
private final View fragmentView;
|
||||||
|
|
||||||
|
boolean drawFolderBackground;
|
||||||
|
Runnable onAnimationFinishRunnable;
|
||||||
|
public long nextDialogId;
|
||||||
|
View parentView;
|
||||||
|
|
||||||
|
CounterView.CounterDrawable counterDrawable = new CounterView.CounterDrawable(null);
|
||||||
|
int params[] = new int[3];
|
||||||
|
private final int currentAccount;
|
||||||
|
private final int folderId;
|
||||||
|
private final int filterId;
|
||||||
|
private final long currentDialog;
|
||||||
|
|
||||||
|
public ChatPullingDownDrawable(int currentAccount, View fragmentView, long currentDialog, int folderId, int filterId) {
|
||||||
|
this.fragmentView = fragmentView;
|
||||||
|
this.currentAccount = currentAccount;
|
||||||
|
this.currentDialog = currentDialog;
|
||||||
|
this.folderId = folderId;
|
||||||
|
this.filterId = filterId;
|
||||||
|
|
||||||
|
arrowPaint.setStrokeWidth(AndroidUtilities.dpf2(2.8f));
|
||||||
|
arrowPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||||
|
counterDrawable.gravity = Gravity.LEFT;
|
||||||
|
counterDrawable.setType(CounterView.CounterDrawable.TYPE_CHAT_PULLING_DOWN);
|
||||||
|
counterDrawable.circlePaint = Theme.chat_actionBackgroundPaint;
|
||||||
|
counterDrawable.textPaint = textPaint;
|
||||||
|
|
||||||
|
textPaint.setTextSize(AndroidUtilities.dp(13));
|
||||||
|
textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||||
|
textPaint2.setTextSize(AndroidUtilities.dp(14));
|
||||||
|
|
||||||
|
xRefPaint.setColor(0xff000000);
|
||||||
|
xRefPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||||
|
|
||||||
|
updateDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateDialog() {
|
||||||
|
TLRPC.Dialog dialog = getNextUnreadDialog(currentDialog, folderId, filterId, true, params);
|
||||||
|
if (dialog != null) {
|
||||||
|
nextDialogId = dialog.id;
|
||||||
|
drawFolderBackground = params[0] == 1;
|
||||||
|
dialogFolderId = params[1];
|
||||||
|
dialogFilterId = params[2];
|
||||||
|
emptyStub = false;
|
||||||
|
nextChat = MessagesController.getInstance(currentAccount).getChat((int) -dialog.id);
|
||||||
|
if (nextChat == null) {
|
||||||
|
MessagesController.getInstance(currentAccount).getChat((int) dialog.id);
|
||||||
|
}
|
||||||
|
AvatarDrawable avatarDrawable = new AvatarDrawable();
|
||||||
|
avatarDrawable.setInfo(nextChat);
|
||||||
|
imageReceiver.setImage(ImageLocation.getForChat(nextChat, ImageLocation.TYPE_SMALL), "50_50", avatarDrawable, null, UserConfig.getInstance(0).getCurrentUser(), 0);
|
||||||
|
MessagesController.getInstance(currentAccount).ensureMessagesLoaded(dialog.id, 0, null);
|
||||||
|
counterDrawable.setCount(dialog.unread_count, false);
|
||||||
|
} else {
|
||||||
|
drawFolderBackground = false;
|
||||||
|
emptyStub = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWidth(int width) {
|
||||||
|
if (width != lastWidth) {
|
||||||
|
circleRadius = AndroidUtilities.dp(56) / 2f;
|
||||||
|
lastWidth = width;
|
||||||
|
|
||||||
|
String nameStr = nextChat != null ? nextChat.title : LocaleController.getString("SwipeToGoNextChannelEnd", R.string.SwipeToGoNextChannelEnd);
|
||||||
|
chatNameWidth = (int) textPaint.measureText(nameStr);
|
||||||
|
chatNameWidth = Math.min(chatNameWidth, lastWidth - AndroidUtilities.dp(60));
|
||||||
|
chatNameLayout = new StaticLayout(nameStr, textPaint, chatNameWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
||||||
|
|
||||||
|
String str1 = null;
|
||||||
|
String str2 = null;
|
||||||
|
|
||||||
|
if (drawFolderBackground && dialogFolderId != folderId && dialogFolderId != 0) {
|
||||||
|
str1 = LocaleController.getString("SwipeToGoNextArchive", R.string.SwipeToGoNextArchive);
|
||||||
|
str2 = LocaleController.getString("ReleaseToGoNextArchive", R.string.ReleaseToGoNextArchive);
|
||||||
|
} else if (drawFolderBackground) {
|
||||||
|
str1 = LocaleController.getString("SwipeToGoNextFolder", R.string.SwipeToGoNextFolder);
|
||||||
|
str2 = LocaleController.getString("ReleaseToGoNextFolder", R.string.ReleaseToGoNextFolder);
|
||||||
|
} else {
|
||||||
|
str1 = LocaleController.getString("SwipeToGoNextChannel", R.string.SwipeToGoNextChannel);
|
||||||
|
str2 = LocaleController.getString("ReleaseToGoNextChannel", R.string.ReleaseToGoNextChannel);
|
||||||
|
}
|
||||||
|
layout1Width = (int) textPaint2.measureText(str1);
|
||||||
|
layout1Width = Math.min(layout1Width, lastWidth - AndroidUtilities.dp(60));
|
||||||
|
layout1 = new StaticLayout(str1, textPaint2, layout1Width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
||||||
|
|
||||||
|
|
||||||
|
layout2Width = (int) textPaint2.measureText(str2);
|
||||||
|
layout2Width = Math.min(layout2Width, lastWidth - AndroidUtilities.dp(60));
|
||||||
|
layout2 = new StaticLayout(str2, textPaint2, layout2Width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
|
||||||
|
|
||||||
|
|
||||||
|
float cx = lastWidth / 2f;
|
||||||
|
float cy = AndroidUtilities.dp(12) + circleRadius;
|
||||||
|
imageReceiver.setImageCoords(cx - AndroidUtilities.dp(40) / 2f, cy - AndroidUtilities.dp(40) / 2f, AndroidUtilities.dp(40), AndroidUtilities.dp(40));
|
||||||
|
imageReceiver.setRoundRadius((int) (AndroidUtilities.dp(40) / 2f));
|
||||||
|
|
||||||
|
counterDrawable.setSize(AndroidUtilities.dp(28), AndroidUtilities.dp(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw(Canvas canvas, View parent, float progress, float alpha) {
|
||||||
|
this.parentView = parent;
|
||||||
|
counterDrawable.setParent(parent);
|
||||||
|
int oldAlpha, oldAlpha1, oldAlpha2, oldAlpha3;
|
||||||
|
float offset = AndroidUtilities.dp(110) * progress;
|
||||||
|
if (offset < AndroidUtilities.dp(8)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress < 0.2f) {
|
||||||
|
alpha *= progress * 5f;
|
||||||
|
}
|
||||||
|
Theme.applyServiceShaderMatrix(lastWidth, parent.getMeasuredHeight(), 0, parent.getMeasuredHeight() - offset);
|
||||||
|
|
||||||
|
textPaint.setColor(Theme.getColor(Theme.key_chat_serviceText));
|
||||||
|
arrowPaint.setColor(Theme.getColor(Theme.key_chat_serviceText));
|
||||||
|
textPaint2.setColor(Theme.getColor(Theme.key_chat_messagePanelHint));
|
||||||
|
|
||||||
|
oldAlpha = Theme.chat_actionBackgroundPaint.getAlpha();
|
||||||
|
oldAlpha1 = Theme.chat_actionBackgroundGradientDarkenPaint.getAlpha();
|
||||||
|
oldAlpha2 = textPaint.getAlpha();
|
||||||
|
oldAlpha3 = arrowPaint.getAlpha();
|
||||||
|
Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha((int) (oldAlpha1 * alpha));
|
||||||
|
Theme.chat_actionBackgroundPaint.setAlpha((int) (oldAlpha * alpha));
|
||||||
|
textPaint.setAlpha((int) (oldAlpha2 * alpha));
|
||||||
|
imageReceiver.setAlpha(alpha);
|
||||||
|
|
||||||
|
if ((progress >= 1f && lastProgress < 1f) || (progress < 1f && lastProgress == 1f)) {
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
if (time - lastHapticTime > 100) {
|
||||||
|
parent.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
|
||||||
|
lastHapticTime = time;
|
||||||
|
}
|
||||||
|
lastProgress = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progress == 1f && !animateSwipeToRelease) {
|
||||||
|
animateSwipeToRelease = true;
|
||||||
|
animateCheck = true;
|
||||||
|
showReleaseState(true, parent);
|
||||||
|
} else if (progress != 1f && animateSwipeToRelease) {
|
||||||
|
animateSwipeToRelease = false;
|
||||||
|
showReleaseState(false, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
float cx = lastWidth / 2f;
|
||||||
|
float bounceOffset = bounceProgress * -AndroidUtilities.dp(4);
|
||||||
|
|
||||||
|
if (emptyStub) {
|
||||||
|
offset -= bounceOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float widthRadius = Math.max(0, Math.min(circleRadius, offset / 2f - AndroidUtilities.dp(16) * progress - AndroidUtilities.dp(4)));
|
||||||
|
float widthRadius2 = Math.max(0, Math.min(circleRadius * progress, offset / 2f - AndroidUtilities.dp(8) * progress));
|
||||||
|
float size = (widthRadius2 * 2 - AndroidUtilities.dp2(16)) * (1f - swipeToReleaseProgress) + AndroidUtilities.dp(56) * swipeToReleaseProgress;
|
||||||
|
|
||||||
|
if (swipeToReleaseProgress < 1f || emptyStub) {
|
||||||
|
float bottom = -AndroidUtilities.dp(8) * (1f - swipeToReleaseProgress) + (-offset + AndroidUtilities.dp(56)) * swipeToReleaseProgress;
|
||||||
|
AndroidUtilities.rectTmp.set(cx - widthRadius, -offset, cx + widthRadius, bottom);
|
||||||
|
if (swipeToReleaseProgress > 0 && !emptyStub) {
|
||||||
|
float inset = AndroidUtilities.dp(16) * swipeToReleaseProgress;
|
||||||
|
AndroidUtilities.rectTmp.inset(inset, inset);
|
||||||
|
}
|
||||||
|
drawBackground(canvas, AndroidUtilities.rectTmp);
|
||||||
|
|
||||||
|
float arrowCy = -offset + AndroidUtilities.dp(24) + AndroidUtilities.dp(8) * (1f - progress) - AndroidUtilities.dp(36) * swipeToReleaseProgress;
|
||||||
|
canvas.save();
|
||||||
|
AndroidUtilities.rectTmp.inset(AndroidUtilities.dp(1), AndroidUtilities.dp(1));
|
||||||
|
canvas.clipRect(AndroidUtilities.rectTmp);
|
||||||
|
if (swipeToReleaseProgress > 0f) {
|
||||||
|
arrowPaint.setAlpha((int) ((1f - swipeToReleaseProgress) * 255));
|
||||||
|
}
|
||||||
|
drawArrow(canvas, cx, arrowCy, AndroidUtilities.dp(24) * progress);
|
||||||
|
|
||||||
|
if (emptyStub) {
|
||||||
|
float top = (-AndroidUtilities.dp(8) - AndroidUtilities.dp2(8) * progress - size) * (1f - swipeToReleaseProgress) + (-offset - AndroidUtilities.dp(2)) * swipeToReleaseProgress + bounceOffset;
|
||||||
|
arrowPaint.setAlpha(oldAlpha3);
|
||||||
|
drawCheck(canvas, cx, top + AndroidUtilities.dp(28));
|
||||||
|
}
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chatNameLayout != null && swipeToReleaseProgress > 0) {
|
||||||
|
Theme.chat_actionBackgroundPaint.setAlpha((int) (oldAlpha * alpha));
|
||||||
|
textPaint.setAlpha((int) (oldAlpha2 * alpha));
|
||||||
|
|
||||||
|
float y = AndroidUtilities.dp(20) * (1f - swipeToReleaseProgress) - AndroidUtilities.dp(36) * swipeToReleaseProgress + bounceOffset;
|
||||||
|
AndroidUtilities.rectTmp.set((lastWidth - chatNameWidth) / 2f, y, lastWidth - (lastWidth - chatNameWidth) / 2f, y + chatNameLayout.getHeight());
|
||||||
|
AndroidUtilities.rectTmp.inset(-AndroidUtilities.dp(8), -AndroidUtilities.dp(4));
|
||||||
|
canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(15), AndroidUtilities.dp(15), Theme.chat_actionBackgroundPaint);
|
||||||
|
if (Theme.hasGradientService()) {
|
||||||
|
canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(15), AndroidUtilities.dp(15), Theme.chat_actionBackgroundGradientDarkenPaint);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate((lastWidth - chatNameWidth) / 2f, y);
|
||||||
|
chatNameLayout.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emptyStub && size > 0) {
|
||||||
|
float top = (-AndroidUtilities.dp(8) - AndroidUtilities.dp2(8) * progress - size) * (1f - swipeToReleaseProgress) + (-offset + AndroidUtilities.dp(4)) * swipeToReleaseProgress + bounceOffset;
|
||||||
|
imageReceiver.setRoundRadius((int) (size / 2f));
|
||||||
|
imageReceiver.setImageCoords(cx - size / 2f, top, size, size);
|
||||||
|
|
||||||
|
if (swipeToReleaseProgress > 0) {
|
||||||
|
canvas.saveLayerAlpha(imageReceiver.getImageX(), imageReceiver.getImageY(), imageReceiver.getImageX() + imageReceiver.getImageWidth(), imageReceiver.getImageY() + imageReceiver.getImageHeight(), 255, Canvas.ALL_SAVE_FLAG);
|
||||||
|
imageReceiver.draw(canvas);
|
||||||
|
canvas.scale(swipeToReleaseProgress, swipeToReleaseProgress, cx + AndroidUtilities.dp(12) + counterDrawable.getCenterX(), top - AndroidUtilities.dp(6) + AndroidUtilities.dp(14));
|
||||||
|
canvas.translate(cx + AndroidUtilities.dp(12), top - AndroidUtilities.dp(6));
|
||||||
|
counterDrawable.updateBackgroundRect();
|
||||||
|
counterDrawable.rectF.inset(-AndroidUtilities.dp(2), -AndroidUtilities.dp(2));
|
||||||
|
canvas.drawRoundRect(counterDrawable.rectF, counterDrawable.rectF.height() / 2f, counterDrawable.rectF.height() / 2f, xRefPaint);
|
||||||
|
canvas.restore();
|
||||||
|
|
||||||
|
canvas.save();
|
||||||
|
canvas.scale(swipeToReleaseProgress, swipeToReleaseProgress, cx + AndroidUtilities.dp(12) + counterDrawable.getCenterX(), top - AndroidUtilities.dp(6) + AndroidUtilities.dp(14));
|
||||||
|
canvas.translate(cx + AndroidUtilities.dp(12), top - AndroidUtilities.dp(6));
|
||||||
|
counterDrawable.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
imageReceiver.draw(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Theme.chat_actionBackgroundPaint.setAlpha(oldAlpha);
|
||||||
|
Theme.chat_actionBackgroundGradientDarkenPaint.setAlpha(oldAlpha1);
|
||||||
|
textPaint.setAlpha(oldAlpha2);
|
||||||
|
arrowPaint.setAlpha(oldAlpha3);
|
||||||
|
imageReceiver.setAlpha(1f);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawCheck(Canvas canvas, float cx, float cy) {
|
||||||
|
if (!animateCheck) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (checkProgress < 1f) {
|
||||||
|
checkProgress += 16 / 220f;
|
||||||
|
if (checkProgress > 1f) {
|
||||||
|
checkProgress = 1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
float p1 = checkProgress > 0.5f ? 1f : checkProgress / 0.5f;
|
||||||
|
float p2 = checkProgress < 0.5f ? 0 : (checkProgress - 0.5f) / 0.5f;
|
||||||
|
canvas.save();
|
||||||
|
canvas.clipRect(AndroidUtilities.rectTmp);
|
||||||
|
canvas.translate(cx - AndroidUtilities.dp(24), cy - AndroidUtilities.dp(24));
|
||||||
|
float x1 = AndroidUtilities.dp(16);
|
||||||
|
float y1 = AndroidUtilities.dp(26);
|
||||||
|
float x2 = AndroidUtilities.dp(22);
|
||||||
|
float y2 = AndroidUtilities.dp(32);
|
||||||
|
float x3 = AndroidUtilities.dp(32);
|
||||||
|
float y3 = AndroidUtilities.dp(20);
|
||||||
|
canvas.drawLine(x1, y1, x1 * (1f - p1) + x2 * p1, y1 * (1f - p1) + y2 * p1, arrowPaint);
|
||||||
|
if (p2 > 0) {
|
||||||
|
canvas.drawLine(x2, y2, x2 * (1f - p2) + x3 * p2, y2 * (1f - p2) + y3 * p2, arrowPaint);
|
||||||
|
}
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawBackground(Canvas canvas, RectF rectTmp) {
|
||||||
|
if (drawFolderBackground) {
|
||||||
|
path.reset();
|
||||||
|
float roundRadius = rectTmp.width() * 0.2f;
|
||||||
|
float folderOffset = rectTmp.width() * 0.1f;
|
||||||
|
float folderOffset2 = rectTmp.width() * 0.03f;
|
||||||
|
float roundRadius2 = folderOffset / 2f;
|
||||||
|
|
||||||
|
float h = rectTmp.height() - folderOffset;
|
||||||
|
|
||||||
|
path.moveTo(rectTmp.right, rectTmp.top + roundRadius + folderOffset);
|
||||||
|
path.rQuadTo(0, -roundRadius, -roundRadius, -roundRadius);
|
||||||
|
path.rLineTo(-(rectTmp.width() - (2 * roundRadius)) / 2 + roundRadius2 * 2 - folderOffset2, 0);
|
||||||
|
path.rQuadTo(-roundRadius2 / 2, 0, -roundRadius2 * 2, -folderOffset / 2);
|
||||||
|
path.rQuadTo(-roundRadius2 / 2, -folderOffset / 2, -roundRadius2 * 2, -folderOffset / 2);
|
||||||
|
path.rLineTo(-(rectTmp.width() - (2 * roundRadius)) / 2 + roundRadius2 * 2 + folderOffset2, 0);
|
||||||
|
path.rQuadTo(-roundRadius, 0, -roundRadius, roundRadius);
|
||||||
|
path.rLineTo(0, (h + folderOffset - (2 * roundRadius)));
|
||||||
|
path.rQuadTo(0, roundRadius, roundRadius, roundRadius);
|
||||||
|
path.rLineTo((rectTmp.width() - (2 * roundRadius)), 0);
|
||||||
|
path.rQuadTo(roundRadius, 0, roundRadius, -roundRadius);
|
||||||
|
path.rLineTo(0, -(h - (2 * roundRadius)));
|
||||||
|
path.close();
|
||||||
|
canvas.drawPath(path, Theme.chat_actionBackgroundPaint);
|
||||||
|
if (Theme.hasGradientService()) {
|
||||||
|
canvas.drawPath(path, Theme.chat_actionBackgroundGradientDarkenPaint);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
canvas.drawRoundRect(AndroidUtilities.rectTmp, circleRadius, circleRadius, Theme.chat_actionBackgroundPaint);
|
||||||
|
if (Theme.hasGradientService()) {
|
||||||
|
canvas.drawRoundRect(AndroidUtilities.rectTmp, circleRadius, circleRadius, Theme.chat_actionBackgroundGradientDarkenPaint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showReleaseState(boolean show, View parent) {
|
||||||
|
if (showReleaseAnimator != null) {
|
||||||
|
showReleaseAnimator.removeAllListeners();
|
||||||
|
showReleaseAnimator.cancel();
|
||||||
|
}
|
||||||
|
if (show) {
|
||||||
|
ValueAnimator out = ValueAnimator.ofFloat(swipeToReleaseProgress, 1f);
|
||||||
|
out.addUpdateListener(animation -> {
|
||||||
|
swipeToReleaseProgress = (float) animation.getAnimatedValue();
|
||||||
|
parent.invalidate();
|
||||||
|
fragmentView.invalidate();
|
||||||
|
});
|
||||||
|
|
||||||
|
out.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT);
|
||||||
|
out.setDuration(250);
|
||||||
|
|
||||||
|
bounceProgress = 0;
|
||||||
|
|
||||||
|
ValueAnimator bounceUp = ValueAnimator.ofFloat(0f, 1f);
|
||||||
|
bounceUp.addUpdateListener(animation -> {
|
||||||
|
bounceProgress = (float) animation.getAnimatedValue();
|
||||||
|
parent.invalidate();
|
||||||
|
});
|
||||||
|
|
||||||
|
bounceUp.setInterpolator(CubicBezierInterpolator.EASE_BOTH);
|
||||||
|
bounceUp.setDuration(180);
|
||||||
|
|
||||||
|
ValueAnimator bounceDown = ValueAnimator.ofFloat(1f, -0.5f);
|
||||||
|
bounceDown.addUpdateListener(animation -> {
|
||||||
|
bounceProgress = (float) animation.getAnimatedValue();
|
||||||
|
parent.invalidate();
|
||||||
|
});
|
||||||
|
|
||||||
|
bounceDown.setInterpolator(CubicBezierInterpolator.EASE_BOTH);
|
||||||
|
bounceDown.setDuration(120);
|
||||||
|
|
||||||
|
ValueAnimator bounceOut = ValueAnimator.ofFloat(-0.5f, 0f);
|
||||||
|
bounceOut.addUpdateListener(animation -> {
|
||||||
|
bounceProgress = (float) animation.getAnimatedValue();
|
||||||
|
parent.invalidate();
|
||||||
|
});
|
||||||
|
|
||||||
|
bounceOut.setInterpolator(CubicBezierInterpolator.EASE_BOTH);
|
||||||
|
bounceOut.setDuration(100);
|
||||||
|
|
||||||
|
showReleaseAnimator = new AnimatorSet();
|
||||||
|
showReleaseAnimator.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
bounceProgress = 0f;
|
||||||
|
swipeToReleaseProgress = 1f;
|
||||||
|
parent.invalidate();
|
||||||
|
fragmentView.invalidate();
|
||||||
|
if (onAnimationFinishRunnable != null) {
|
||||||
|
onAnimationFinishRunnable.run();
|
||||||
|
onAnimationFinishRunnable = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AnimatorSet bounce = new AnimatorSet();
|
||||||
|
bounce.playSequentially(bounceUp, bounceDown, bounceOut);
|
||||||
|
|
||||||
|
showReleaseAnimator.playTogether(out, bounce);
|
||||||
|
showReleaseAnimator.start();
|
||||||
|
} else {
|
||||||
|
ValueAnimator out = ValueAnimator.ofFloat(swipeToReleaseProgress, 0f);
|
||||||
|
out.addUpdateListener(animation -> {
|
||||||
|
swipeToReleaseProgress = (float) animation.getAnimatedValue();
|
||||||
|
fragmentView.invalidate();
|
||||||
|
parent.invalidate();
|
||||||
|
});
|
||||||
|
|
||||||
|
out.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
out.setDuration(220);
|
||||||
|
showReleaseAnimator = new AnimatorSet();
|
||||||
|
showReleaseAnimator.playTogether(out);
|
||||||
|
showReleaseAnimator.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawArrow(Canvas canvas, float cx, float cy, float size) {
|
||||||
|
canvas.save();
|
||||||
|
float s = size / AndroidUtilities.dpf2(24);
|
||||||
|
canvas.scale(s, s, cx, cy - AndroidUtilities.dp(20));
|
||||||
|
canvas.translate(cx - AndroidUtilities.dp2(12), cy - AndroidUtilities.dp(12));
|
||||||
|
canvas.drawLine(AndroidUtilities.dpf2(12.5f), AndroidUtilities.dpf2(4f), AndroidUtilities.dpf2(12.5f), AndroidUtilities.dpf2(22), arrowPaint);
|
||||||
|
canvas.drawLine(AndroidUtilities.dpf2(3.5f), AndroidUtilities.dpf2(12), AndroidUtilities.dpf2(12.5f), AndroidUtilities.dpf2(3.5f), arrowPaint);
|
||||||
|
canvas.drawLine(AndroidUtilities.dpf2(25 - 3.5f), AndroidUtilities.dpf2(12), AndroidUtilities.dpf2(12.5f), AndroidUtilities.dpf2(3.5f), arrowPaint);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAttach() {
|
||||||
|
imageReceiver.onAttachedToWindow();
|
||||||
|
NotificationCenter.getInstance(currentAccount).addObserver(this, NotificationCenter.updateInterfaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onDetach() {
|
||||||
|
NotificationCenter.getInstance(currentAccount).removeObserver(this, NotificationCenter.updateInterfaces);
|
||||||
|
imageReceiver.onDetachedFromWindow();
|
||||||
|
lastProgress = 0;
|
||||||
|
lastHapticTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void didReceivedNotification(int id, int account, Object... args) {
|
||||||
|
if (nextDialogId !=0 ) {
|
||||||
|
TLRPC.Dialog dialog = MessagesController.getInstance(currentAccount).dialogs_dict.get(nextDialogId);
|
||||||
|
if (dialog != null) {
|
||||||
|
counterDrawable.setCount(dialog.unread_count, true);
|
||||||
|
if (parentView != null) {
|
||||||
|
parentView.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static TLRPC.Dialog getNextUnreadDialog(long currentDialogId, int folderId, int filterId) {
|
||||||
|
return getNextUnreadDialog(currentDialogId, folderId, filterId, true, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TLRPC.Dialog getNextUnreadDialog(long currentDialogId, int folderId, int filterId, boolean searchNext, int[] params) {
|
||||||
|
MessagesController messagesController = AccountInstance.getInstance(UserConfig.selectedAccount).getMessagesController();
|
||||||
|
if (params != null) {
|
||||||
|
params[0] = 0;
|
||||||
|
params[1] = folderId;
|
||||||
|
params[2] = filterId;
|
||||||
|
}
|
||||||
|
ArrayList<TLRPC.Dialog> dialogs = null;
|
||||||
|
if (filterId != 0) {
|
||||||
|
dialogs = messagesController.dialogFiltersById.get(filterId).dialogs;
|
||||||
|
} else {
|
||||||
|
dialogs = messagesController.getDialogs(folderId);
|
||||||
|
}
|
||||||
|
if (dialogs == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < dialogs.size(); i++) {
|
||||||
|
TLRPC.Dialog dialog = dialogs.get(i);
|
||||||
|
int lower_id = (int) dialog.id;
|
||||||
|
TLRPC.Chat chat = messagesController.getChat(-lower_id);
|
||||||
|
if (chat != null && dialog.id != currentDialogId && dialog.unread_count > 0 && DialogObject.isChannel(dialog) && !chat.megagroup && !messagesController.isPromoDialog(dialog.id, false)) {
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (searchNext) {
|
||||||
|
if (filterId != 0) {
|
||||||
|
for (int i = 0; i < messagesController.dialogFilters.size(); i++) {
|
||||||
|
int newFilterId = messagesController.dialogFilters.get(i).id;
|
||||||
|
if (filterId != newFilterId) {
|
||||||
|
TLRPC.Dialog dialog = getNextUnreadDialog(currentDialogId, folderId, newFilterId, false, params);
|
||||||
|
if (dialog != null) {
|
||||||
|
if (params != null) {
|
||||||
|
params[0] = 1;
|
||||||
|
}
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < messagesController.dialogsByFolder.size(); i++) {
|
||||||
|
int newFolderId = messagesController.dialogsByFolder.keyAt(i);
|
||||||
|
if (folderId != newFolderId) {
|
||||||
|
TLRPC.Dialog dialog = getNextUnreadDialog(currentDialogId, newFolderId, 0, false, params);
|
||||||
|
if (dialog != null) {
|
||||||
|
if (params != null) {
|
||||||
|
params[0] = 1;
|
||||||
|
}
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChatId() {
|
||||||
|
return nextChat.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void drawBottomPanel(Canvas canvas, int top, int bottom, int width) {
|
||||||
|
if (showBottomPanel && progressToBottomPannel != 1f) {
|
||||||
|
progressToBottomPannel += 16f / 150f;
|
||||||
|
if (progressToBottomPannel > 1f) {
|
||||||
|
progressToBottomPannel = 1f;
|
||||||
|
} else {
|
||||||
|
fragmentView.invalidate();
|
||||||
|
}
|
||||||
|
} else if (!showBottomPanel && progressToBottomPannel != 0) {
|
||||||
|
progressToBottomPannel -= 16f / 150f;
|
||||||
|
if (progressToBottomPannel < 0) {
|
||||||
|
progressToBottomPannel = 0;
|
||||||
|
} else {
|
||||||
|
fragmentView.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int oldAlpha = Theme.chat_composeBackgroundPaint.getAlpha();
|
||||||
|
int oldAlphaText = textPaint2.getAlpha();
|
||||||
|
|
||||||
|
Theme.chat_composeBackgroundPaint.setAlpha((int) (oldAlpha * progressToBottomPannel));
|
||||||
|
canvas.drawRect(0, top, width, bottom, Theme.chat_composeBackgroundPaint);
|
||||||
|
|
||||||
|
|
||||||
|
if (layout1 != null && swipeToReleaseProgress < 1f) {
|
||||||
|
textPaint2.setAlpha((int) (oldAlphaText * (1f - swipeToReleaseProgress) * progressToBottomPannel));
|
||||||
|
float y = top + AndroidUtilities.dp(18) - AndroidUtilities.dp(10) * swipeToReleaseProgress;
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate((lastWidth - layout1Width) / 2f, y);
|
||||||
|
layout1.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layout2 != null && swipeToReleaseProgress > 0) {
|
||||||
|
textPaint2.setAlpha((int) (oldAlphaText * swipeToReleaseProgress * progressToBottomPannel));
|
||||||
|
float y = top + AndroidUtilities.dp(18) + AndroidUtilities.dp(10) * (1f - swipeToReleaseProgress);
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate((lastWidth - layout2Width) / 2f, y);
|
||||||
|
layout2.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
textPaint2.setAlpha(oldAlpha);
|
||||||
|
Theme.chat_composeBackgroundPaint.setAlpha(oldAlpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean showBottomPanel;
|
||||||
|
|
||||||
|
public void showBottomPanel(boolean b) {
|
||||||
|
showBottomPanel = b;
|
||||||
|
fragmentView.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean needDrawBottomPanel() {
|
||||||
|
return (showBottomPanel || progressToBottomPannel > 0) && !emptyStub;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean animationIsRunning() {
|
||||||
|
return swipeToReleaseProgress != 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runOnAnimationFinish(Runnable runnable) {
|
||||||
|
if (showReleaseAnimator != null) {
|
||||||
|
showReleaseAnimator.removeAllListeners();
|
||||||
|
showReleaseAnimator.cancel();
|
||||||
|
}
|
||||||
|
onAnimationFinishRunnable = runnable;
|
||||||
|
showReleaseAnimator = new AnimatorSet();
|
||||||
|
|
||||||
|
ValueAnimator out = ValueAnimator.ofFloat(swipeToReleaseProgress, 1f);
|
||||||
|
out.addUpdateListener(animation -> {
|
||||||
|
swipeToReleaseProgress = (float) animation.getAnimatedValue();
|
||||||
|
fragmentView.invalidate();
|
||||||
|
if (parentView != null) {
|
||||||
|
parentView.invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ValueAnimator bounceOut = ValueAnimator.ofFloat(bounceProgress, 0f);
|
||||||
|
bounceOut.addUpdateListener(animation -> {
|
||||||
|
bounceProgress = (float) animation.getAnimatedValue();
|
||||||
|
if (parentView != null) {
|
||||||
|
parentView.invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
showReleaseAnimator.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
bounceProgress = 0f;
|
||||||
|
swipeToReleaseProgress = 1f;
|
||||||
|
if (parentView != null) {
|
||||||
|
parentView.invalidate();
|
||||||
|
}
|
||||||
|
fragmentView.invalidate();
|
||||||
|
if (onAnimationFinishRunnable != null) {
|
||||||
|
onAnimationFinishRunnable.run();
|
||||||
|
onAnimationFinishRunnable = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
showReleaseAnimator.playTogether(out, bounceOut);
|
||||||
|
showReleaseAnimator.setDuration(120);
|
||||||
|
showReleaseAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
showReleaseAnimator.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
checkProgress = 0;
|
||||||
|
animateCheck = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1355,7 +1355,7 @@ public class AudioPlayerAlert extends BottomSheet implements NotificationCenter.
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
SendMessagesHelper.getInstance(currentAccount).sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null);
|
SendMessagesHelper.getInstance(currentAccount).sendMessage(message.toString(), did, null, null, null, true, null, null, null, true, 0, null);
|
||||||
}
|
}
|
||||||
SendMessagesHelper.getInstance(currentAccount).sendMessage(fmessages, did, true, 0);
|
SendMessagesHelper.getInstance(currentAccount).sendMessage(fmessages, did, false, false, true, 0);
|
||||||
}
|
}
|
||||||
fragment1.finishFragment();
|
fragment1.finishFragment();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1935,7 +1935,7 @@ public class AudioPlayerAlert extends BottomSheet implements NotificationCenter.
|
||||||
|
|
||||||
private ImageLocation getArtworkThumbImageLocation(MessageObject messageObject) {
|
private ImageLocation getArtworkThumbImageLocation(MessageObject messageObject) {
|
||||||
final TLRPC.Document document = messageObject.getDocument();
|
final TLRPC.Document document = messageObject.getDocument();
|
||||||
TLRPC.PhotoSize thumb = document != null ? FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 240) : null;
|
TLRPC.PhotoSize thumb = document != null ? FileLoader.getClosestPhotoSizeWithSize(document.thumbs, 360) : null;
|
||||||
if (!(thumb instanceof TLRPC.TL_photoSize) && !(thumb instanceof TLRPC.TL_photoSizeProgressive)) {
|
if (!(thumb instanceof TLRPC.TL_photoSize) && !(thumb instanceof TLRPC.TL_photoSizeProgressive)) {
|
||||||
thumb = null;
|
thumb = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -381,7 +381,10 @@ public class AvatarDrawable extends Drawable {
|
||||||
Theme.avatarDrawables[1].draw(canvas);
|
Theme.avatarDrawables[1].draw(canvas);
|
||||||
} else {
|
} else {
|
||||||
if (textLayout != null) {
|
if (textLayout != null) {
|
||||||
|
float scale = size / (float) AndroidUtilities.dp(50);
|
||||||
|
canvas.scale(scale, scale, size / 2f, size / 2f) ;
|
||||||
canvas.translate((size - textWidth) / 2 - textLeft, (size - textHeight) / 2);
|
canvas.translate((size - textWidth) / 2 - textLeft, (size - textHeight) / 2);
|
||||||
|
|
||||||
textLayout.draw(canvas);
|
textLayout.draw(canvas);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ public class BlurBehindDrawable {
|
||||||
|
|
||||||
DispatchQueue queue;
|
DispatchQueue queue;
|
||||||
|
|
||||||
|
private final int type;
|
||||||
public static final int TAG_DRAWING_AS_BACKGROUND = (1 << 26) + 3;
|
public static final int TAG_DRAWING_AS_BACKGROUND = (1 << 26) + 3;
|
||||||
|
|
||||||
public static final int STATIC_CONTENT = 0;
|
public static final int STATIC_CONTENT = 0;
|
||||||
|
@ -41,6 +42,7 @@ public class BlurBehindDrawable {
|
||||||
private float blurAlpha;
|
private float blurAlpha;
|
||||||
private boolean show;
|
private boolean show;
|
||||||
private boolean error;
|
private boolean error;
|
||||||
|
private boolean animateAlpha = true;
|
||||||
|
|
||||||
private final float DOWN_SCALE = 6f;
|
private final float DOWN_SCALE = 6f;
|
||||||
private int lastH;
|
private int lastH;
|
||||||
|
@ -57,7 +59,8 @@ public class BlurBehindDrawable {
|
||||||
Paint emptyPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
|
Paint emptyPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
|
||||||
Paint errorBlackoutPaint = new Paint();
|
Paint errorBlackoutPaint = new Paint();
|
||||||
|
|
||||||
public BlurBehindDrawable(View behindView, View parentView) {
|
public BlurBehindDrawable(View behindView, View parentView, int type) {
|
||||||
|
this.type = type;
|
||||||
this.behindView = behindView;
|
this.behindView = behindView;
|
||||||
this.parentView = parentView;
|
this.parentView = parentView;
|
||||||
|
|
||||||
|
@ -66,8 +69,12 @@ public class BlurBehindDrawable {
|
||||||
|
|
||||||
|
|
||||||
public void draw(Canvas canvas) {
|
public void draw(Canvas canvas) {
|
||||||
|
if (type == 1 && !wasDraw && !animateAlpha) {
|
||||||
|
generateBlurredBitmaps();
|
||||||
|
invalidate = false;
|
||||||
|
}
|
||||||
final Bitmap[] bitmap = renderingBitmap;
|
final Bitmap[] bitmap = renderingBitmap;
|
||||||
if (bitmap != null || error) {
|
if ((bitmap != null || error) && animateAlpha) {
|
||||||
if (show && blurAlpha != 1f) {
|
if (show && blurAlpha != 1f) {
|
||||||
blurAlpha += 0.09f;
|
blurAlpha += 0.09f;
|
||||||
if (blurAlpha > 1f) {
|
if (blurAlpha > 1f) {
|
||||||
|
@ -83,18 +90,28 @@ public class BlurBehindDrawable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float alpha = animateAlpha ? blurAlpha : 1f;
|
||||||
if (bitmap == null && error) {
|
if (bitmap == null && error) {
|
||||||
errorBlackoutPaint.setAlpha((int) (50 * blurAlpha));
|
errorBlackoutPaint.setAlpha((int) (50 * alpha));
|
||||||
canvas.drawPaint(errorBlackoutPaint);
|
canvas.drawPaint(errorBlackoutPaint);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas.saveLayerAlpha(0, 0, parentView.getMeasuredWidth(), parentView.getMeasuredHeight(), (int) (blurAlpha * 255), ALL_SAVE_FLAG);
|
if (alpha == 1f) {
|
||||||
|
canvas.save();
|
||||||
|
} else {
|
||||||
|
canvas.saveLayerAlpha(0, 0, parentView.getMeasuredWidth(), parentView.getMeasuredHeight(), (int) (alpha * 255), ALL_SAVE_FLAG);
|
||||||
|
}
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
emptyPaint.setAlpha((int) (255 * blurAlpha));
|
emptyPaint.setAlpha((int) (255 * alpha));
|
||||||
|
if (type == 1) {
|
||||||
|
canvas.translate(0, panTranslationY);
|
||||||
|
}
|
||||||
canvas.drawBitmap(bitmap[1], 0, 0, null);
|
canvas.drawBitmap(bitmap[1], 0, 0, null);
|
||||||
canvas.save();
|
canvas.save();
|
||||||
canvas.translate(0, panTranslationY);
|
if (type == 0) {
|
||||||
|
canvas.translate(0, panTranslationY);
|
||||||
|
}
|
||||||
canvas.drawBitmap(bitmap[0], 0, 0, null);
|
canvas.drawBitmap(bitmap[0], 0, 0, null);
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
wasDraw = true;
|
wasDraw = true;
|
||||||
|
@ -126,8 +143,12 @@ public class BlurBehindDrawable {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
blurredBitmapTmp[i].eraseColor(Color.TRANSPARENT);
|
||||||
|
}
|
||||||
|
if (i == 1) {
|
||||||
|
blurredBitmapTmp[i].eraseColor(Theme.getColor(Theme.key_windowBackgroundWhite));
|
||||||
}
|
}
|
||||||
|
|
||||||
blurCanvas[i].save();
|
blurCanvas[i].save();
|
||||||
blurCanvas[i].scale(1f / DOWN_SCALE, 1f / DOWN_SCALE, 0, 0);
|
blurCanvas[i].scale(1f / DOWN_SCALE, 1f / DOWN_SCALE, 0, 0);
|
||||||
Drawable backDrawable = behindView.getBackground();
|
Drawable backDrawable = behindView.getBackground();
|
||||||
|
@ -220,7 +241,7 @@ public class BlurBehindDrawable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFullyDrawing() {
|
public boolean isFullyDrawing() {
|
||||||
return !skipDraw && wasDraw && blurAlpha == 1f && show;
|
return !skipDraw && wasDraw && (blurAlpha == 1f || !animateAlpha) && show && parentView.getAlpha() == 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void checkSizes() {
|
public void checkSizes() {
|
||||||
|
@ -228,6 +249,22 @@ public class BlurBehindDrawable {
|
||||||
if (bitmap == null || parentView.getMeasuredHeight() == 0 || parentView.getMeasuredWidth() == 0) {
|
if (bitmap == null || parentView.getMeasuredHeight() == 0 || parentView.getMeasuredWidth() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
generateBlurredBitmaps();
|
||||||
|
|
||||||
|
lastH = parentView.getMeasuredHeight();
|
||||||
|
lastW = parentView.getMeasuredWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateBlurredBitmaps() {
|
||||||
|
Bitmap[] bitmap = renderingBitmap;
|
||||||
|
if (bitmap == null) {
|
||||||
|
bitmap = renderingBitmap = new Bitmap[2];
|
||||||
|
renderingBitmapCanvas = new Canvas[2];
|
||||||
|
}
|
||||||
|
if (blurredBitmapTmp == null) {
|
||||||
|
blurredBitmapTmp = new Bitmap[2];
|
||||||
|
blurCanvas = new Canvas[2];
|
||||||
|
}
|
||||||
blurBackgroundTask.canceled = true;
|
blurBackgroundTask.canceled = true;
|
||||||
blurBackgroundTask = new BlurBackgroundTask();
|
blurBackgroundTask = new BlurBackgroundTask();
|
||||||
|
|
||||||
|
@ -237,17 +274,20 @@ public class BlurBehindDrawable {
|
||||||
toolbarH = AndroidUtilities.statusBarHeight + AndroidUtilities.dp(100);
|
toolbarH = AndroidUtilities.statusBarHeight + AndroidUtilities.dp(100);
|
||||||
int h = i == 0 ? toolbarH : lastH;
|
int h = i == 0 ? toolbarH : lastH;
|
||||||
|
|
||||||
if (bitmap[i].getHeight() != h || bitmap[i].getWidth() != parentView.getMeasuredWidth()) {
|
if (bitmap[i] == null || bitmap[i].getHeight() != h || bitmap[i].getWidth() != parentView.getMeasuredWidth()) {
|
||||||
if (queue != null) {
|
if (queue != null) {
|
||||||
queue.cleanupQueue();
|
queue.cleanupQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
blurredBitmapTmp[i] = Bitmap.createBitmap((int) (lastW / DOWN_SCALE), (int) (h / DOWN_SCALE), Bitmap.Config.ARGB_8888);
|
blurredBitmapTmp[i] = Bitmap.createBitmap((int) (lastW / DOWN_SCALE), (int) (h / DOWN_SCALE), Bitmap.Config.ARGB_8888);
|
||||||
|
if (i == 1) {
|
||||||
|
blurredBitmapTmp[i].eraseColor(Theme.getColor(Theme.key_windowBackgroundWhite));
|
||||||
|
}
|
||||||
blurCanvas[i] = new Canvas(blurredBitmapTmp[i]);
|
blurCanvas[i] = new Canvas(blurredBitmapTmp[i]);
|
||||||
|
|
||||||
renderingBitmap[i] = Bitmap.createBitmap(lastW, i == 0 ? toolbarH : lastH, Bitmap.Config.ARGB_8888);
|
renderingBitmap[i] = Bitmap.createBitmap(lastW, i == 0 ? toolbarH : lastH, Bitmap.Config.ARGB_8888);
|
||||||
renderingBitmapCanvas[i] = new Canvas(renderingBitmap[i]);
|
renderingBitmapCanvas[i] = new Canvas(renderingBitmap[i]);
|
||||||
renderingBitmapCanvas[i].scale(DOWN_SCALE, DOWN_SCALE);
|
renderingBitmapCanvas[i].scale((float) renderingBitmap[i].getWidth() / (float) blurredBitmapTmp[i].getWidth(), (float) renderingBitmap[i].getHeight() / (float) blurredBitmapTmp[i].getHeight());
|
||||||
|
|
||||||
blurCanvas[i].save();
|
blurCanvas[i].save();
|
||||||
blurCanvas[i].scale(1f / DOWN_SCALE, 1f / DOWN_SCALE, 0, 0);
|
blurCanvas[i].scale(1f / DOWN_SCALE, 1f / DOWN_SCALE, 0, 0);
|
||||||
|
@ -274,18 +314,22 @@ public class BlurBehindDrawable {
|
||||||
|
|
||||||
Utilities.stackBlurBitmap(blurredBitmapTmp[i], getBlurRadius());
|
Utilities.stackBlurBitmap(blurredBitmapTmp[i], getBlurRadius());
|
||||||
emptyPaint.setAlpha(255);
|
emptyPaint.setAlpha(255);
|
||||||
|
if (i == 1) {
|
||||||
|
renderingBitmap[i].eraseColor(Theme.getColor(Theme.key_windowBackgroundWhite));
|
||||||
|
}
|
||||||
renderingBitmapCanvas[i].drawBitmap(blurredBitmapTmp[i], 0, 0, emptyPaint);
|
renderingBitmapCanvas[i].drawBitmap(blurredBitmapTmp[i], 0, 0, emptyPaint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lastH = parentView.getMeasuredHeight();
|
|
||||||
lastW = parentView.getMeasuredWidth();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void show(boolean show) {
|
public void show(boolean show) {
|
||||||
this.show = show;
|
this.show = show;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAnimateAlpha(boolean animateAlpha) {
|
||||||
|
this.animateAlpha = animateAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
public void onPanTranslationUpdate(float y) {
|
public void onPanTranslationUpdate(float y) {
|
||||||
panTranslationY = y;
|
panTranslationY = y;
|
||||||
parentView.invalidate();
|
parentView.invalidate();
|
||||||
|
@ -317,13 +361,19 @@ public class BlurBehindDrawable {
|
||||||
try {
|
try {
|
||||||
backgroundBitmap[i] = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
|
backgroundBitmap[i] = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
|
||||||
backgroundBitmapCanvas[i] = new Canvas(backgroundBitmap[i]);
|
backgroundBitmapCanvas[i] = new Canvas(backgroundBitmap[i]);
|
||||||
backgroundBitmapCanvas[i].scale(DOWN_SCALE, DOWN_SCALE);
|
backgroundBitmapCanvas[i].scale(width / (float) blurredBitmapTmp[i].getWidth(), h / (float) blurredBitmapTmp[i].getHeight());
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
FileLog.e(e);
|
FileLog.e(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (i == 1) {
|
||||||
|
backgroundBitmap[i].eraseColor(Theme.getColor(Theme.key_windowBackgroundWhite));
|
||||||
|
} else {
|
||||||
|
backgroundBitmap[i].eraseColor(Color.TRANSPARENT);
|
||||||
|
}
|
||||||
emptyPaint.setAlpha(255);
|
emptyPaint.setAlpha(255);
|
||||||
Utilities.stackBlurBitmap(blurredBitmapTmp[i], getBlurRadius());
|
Utilities.stackBlurBitmap(blurredBitmapTmp[i], getBlurRadius());
|
||||||
|
|
||||||
if (backgroundBitmapCanvas[i] != null) {
|
if (backgroundBitmapCanvas[i] != null) {
|
||||||
backgroundBitmapCanvas[i].drawBitmap(blurredBitmapTmp[i], 0, 0, emptyPaint);
|
backgroundBitmapCanvas[i].drawBitmap(blurredBitmapTmp[i], 0, 0, emptyPaint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package org.telegram.ui.Components;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
public class BluredView extends View {
|
||||||
|
|
||||||
|
public final BlurBehindDrawable drawable;
|
||||||
|
|
||||||
|
public BluredView(Context context, View parentView) {
|
||||||
|
super(context);
|
||||||
|
drawable = new BlurBehindDrawable(parentView, this, 1);
|
||||||
|
drawable.setAnimateAlpha(false);
|
||||||
|
drawable.show(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
drawable.draw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
drawable.checkSizes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
drawable.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean fullyDrawing() {
|
||||||
|
return drawable.isFullyDrawing() && getVisibility() == View.VISIBLE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -243,6 +243,9 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
public BotCommandsMenuContainer botCommandsMenuContainer;
|
public BotCommandsMenuContainer botCommandsMenuContainer;
|
||||||
private BotCommandsMenuView.BotCommandsAdapter botCommandsAdapter;
|
private BotCommandsMenuView.BotCommandsAdapter botCommandsAdapter;
|
||||||
|
|
||||||
|
private ValueAnimator searchAnimator;
|
||||||
|
private float searchToOpenProgress;
|
||||||
|
|
||||||
private HashMap<View, Float> animationParamsX = new HashMap<>();
|
private HashMap<View, Float> animationParamsX = new HashMap<>();
|
||||||
|
|
||||||
private class SeekBarWaveformView extends View {
|
private class SeekBarWaveformView extends View {
|
||||||
|
@ -379,6 +382,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
private ReplaceableIconDrawable botButtonDrawable;
|
private ReplaceableIconDrawable botButtonDrawable;
|
||||||
|
|
||||||
private CharSequence draftMessage;
|
private CharSequence draftMessage;
|
||||||
|
private boolean draftSearchWebpage;
|
||||||
|
|
||||||
private boolean isPaste;
|
private boolean isPaste;
|
||||||
|
|
||||||
|
@ -502,7 +506,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
emojiTabOpen = curPage == 0;
|
emojiTabOpen = curPage == 0;
|
||||||
if (stickersExpanded) {
|
if (stickersExpanded) {
|
||||||
if (searchingType != 0) {
|
if (searchingType != 0) {
|
||||||
searchingType = curPage == 0 ? 2 : 1;
|
setSearchingTypeInternal(curPage == 0 ? 2 : 1, true);
|
||||||
checkStickresExpandHeight();
|
checkStickresExpandHeight();
|
||||||
} else if (!stickersTabOpen) {
|
} else if (!stickersTabOpen) {
|
||||||
setStickersExpanded(false, true, false);
|
setStickersExpanded(false, true, false);
|
||||||
|
@ -1742,7 +1746,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
emojiView.onOpen(messageEditText.length() > 0);
|
emojiView.onOpen(messageEditText.length() > 0);
|
||||||
} else {
|
} else {
|
||||||
if (searchingType != 0) {
|
if (searchingType != 0) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, true);
|
||||||
if (emojiView != null) {
|
if (emojiView != null) {
|
||||||
emojiView.closeSearch(false);
|
emojiView.closeSearch(false);
|
||||||
}
|
}
|
||||||
|
@ -1809,11 +1813,14 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (inputContentInfo.getDescription().hasMimeType("image/gif") || SendMessagesHelper.shouldSendWebPAsSticker(null, inputContentInfo.getContentUri())) {
|
||||||
if (isInScheduleMode()) {
|
if (isInScheduleMode()) {
|
||||||
AlertsCreator.createScheduleDatePickerDialog(parentActivity, parentFragment.getDialogId(), (notify, scheduleDate) -> send(inputContentInfo, notify, scheduleDate));
|
AlertsCreator.createScheduleDatePickerDialog(parentActivity, parentFragment.getDialogId(), (notify, scheduleDate) -> send(inputContentInfo, notify, scheduleDate));
|
||||||
|
} else {
|
||||||
|
send(inputContentInfo, true, 0);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
send(inputContentInfo, true, 0);
|
editPhoto(inputContentInfo.getContentUri(), inputContentInfo.getDescription().getMimeType(0));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
@ -1832,7 +1839,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
if (isPopupShowing() && event.getAction() == MotionEvent.ACTION_DOWN) {
|
if (isPopupShowing() && event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
boolean rez = false;
|
boolean rez = false;
|
||||||
if (searchingType != 0) {
|
if (searchingType != 0) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, false);
|
||||||
emojiView.closeSearch(false);
|
emojiView.closeSearch(false);
|
||||||
requestFocus();
|
requestFocus();
|
||||||
rez = true;
|
rez = true;
|
||||||
|
@ -1907,70 +1914,76 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
ClipData clipData = clipboard.getPrimaryClip();
|
ClipData clipData = clipboard.getPrimaryClip();
|
||||||
if (clipData != null) {
|
if (clipData != null) {
|
||||||
if (clipData.getItemCount() == 1 && clipData.getDescription().hasMimeType("image/*")) {
|
if (clipData.getItemCount() == 1 && clipData.getDescription().hasMimeType("image/*")) {
|
||||||
final File file = AndroidUtilities.generatePicturePath(fragment.isSecretChat(), MimeTypeMap.getSingleton().getExtensionFromMimeType(clipData.getDescription().getMimeType(0)));
|
editPhoto(clipData.getItemAt(0).getUri(), clipData.getDescription().getMimeType(0));
|
||||||
Uri uri = clipData.getItemAt(0).getUri();
|
|
||||||
Utilities.globalQueue.postRunnable(() -> {
|
|
||||||
try {
|
|
||||||
InputStream in = context.getContentResolver().openInputStream(uri);
|
|
||||||
FileOutputStream fos = new FileOutputStream(file);
|
|
||||||
byte[] buffer = new byte[1024];
|
|
||||||
int lengthRead;
|
|
||||||
while ((lengthRead = in.read(buffer)) > 0) {
|
|
||||||
fos.write(buffer, 0, lengthRead);
|
|
||||||
fos.flush();
|
|
||||||
}
|
|
||||||
in.close();
|
|
||||||
fos.close();
|
|
||||||
MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, -1, 0, file.getAbsolutePath(), 0, false, 0, 0, 0);
|
|
||||||
ArrayList<Object> entries = new ArrayList<>();
|
|
||||||
entries.add(photoEntry);
|
|
||||||
AndroidUtilities.runOnUIThread(() -> {
|
|
||||||
PhotoViewer.getInstance().setParentActivity(parentActivity);
|
|
||||||
PhotoViewer.getInstance().openPhotoForSelect(entries, 0, 2, false, new PhotoViewer.EmptyPhotoViewerProvider() {
|
|
||||||
boolean sending;
|
|
||||||
@Override
|
|
||||||
public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, boolean forceDocument) {
|
|
||||||
ArrayList<SendMessagesHelper.SendingMediaInfo> photos = new ArrayList<>();
|
|
||||||
SendMessagesHelper.SendingMediaInfo info = new SendMessagesHelper.SendingMediaInfo();
|
|
||||||
if (!photoEntry.isVideo && photoEntry.imagePath != null) {
|
|
||||||
info.path = photoEntry.imagePath;
|
|
||||||
} else if (photoEntry.path != null) {
|
|
||||||
info.path = photoEntry.path;
|
|
||||||
}
|
|
||||||
info.thumbPath = photoEntry.thumbPath;
|
|
||||||
info.isVideo = photoEntry.isVideo;
|
|
||||||
info.caption = photoEntry.caption != null ? photoEntry.caption.toString() : null;
|
|
||||||
info.entities = photoEntry.entities;
|
|
||||||
info.masks = photoEntry.stickers;
|
|
||||||
info.ttl = photoEntry.ttl;
|
|
||||||
info.videoEditedInfo = videoEditedInfo;
|
|
||||||
info.canDeleteAfter = true;
|
|
||||||
photos.add(info);
|
|
||||||
photoEntry.reset();
|
|
||||||
sending = true;
|
|
||||||
SendMessagesHelper.prepareSendingMedia(accountInstance, photos, dialog_id, replyingMessageObject, getThreadMessage(), null, false, false, editingMessageObject, notify, scheduleDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void willHidePhotoViewer() {
|
|
||||||
if (!sending) {
|
|
||||||
try {
|
|
||||||
file.delete();
|
|
||||||
} catch (Throwable ignore) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, parentFragment);
|
|
||||||
});
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.onTextContextMenuItem(id);
|
return super.onTextContextMenuItem(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void editPhoto(Uri uri, String mime) {
|
||||||
|
final File file = AndroidUtilities.generatePicturePath(fragment.isSecretChat(), MimeTypeMap.getSingleton().getExtensionFromMimeType(mime));
|
||||||
|
Utilities.globalQueue.postRunnable(() -> {
|
||||||
|
try {
|
||||||
|
InputStream in = context.getContentResolver().openInputStream(uri);
|
||||||
|
FileOutputStream fos = new FileOutputStream(file);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int lengthRead;
|
||||||
|
while ((lengthRead = in.read(buffer)) > 0) {
|
||||||
|
fos.write(buffer, 0, lengthRead);
|
||||||
|
fos.flush();
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
fos.close();
|
||||||
|
MediaController.PhotoEntry photoEntry = new MediaController.PhotoEntry(0, -1, 0, file.getAbsolutePath(), 0, false, 0, 0, 0);
|
||||||
|
ArrayList<Object> entries = new ArrayList<>();
|
||||||
|
entries.add(photoEntry);
|
||||||
|
AndroidUtilities.runOnUIThread(() -> {
|
||||||
|
PhotoViewer.getInstance().setParentActivity(parentActivity);
|
||||||
|
PhotoViewer.getInstance().openPhotoForSelect(entries, 0, 2, false, new PhotoViewer.EmptyPhotoViewerProvider() {
|
||||||
|
boolean sending;
|
||||||
|
@Override
|
||||||
|
public void sendButtonPressed(int index, VideoEditedInfo videoEditedInfo, boolean notify, int scheduleDate, boolean forceDocument) {
|
||||||
|
ArrayList<SendMessagesHelper.SendingMediaInfo> photos = new ArrayList<>();
|
||||||
|
SendMessagesHelper.SendingMediaInfo info = new SendMessagesHelper.SendingMediaInfo();
|
||||||
|
if (!photoEntry.isVideo && photoEntry.imagePath != null) {
|
||||||
|
info.path = photoEntry.imagePath;
|
||||||
|
} else if (photoEntry.path != null) {
|
||||||
|
info.path = photoEntry.path;
|
||||||
|
}
|
||||||
|
info.thumbPath = photoEntry.thumbPath;
|
||||||
|
info.isVideo = photoEntry.isVideo;
|
||||||
|
info.caption = photoEntry.caption != null ? photoEntry.caption.toString() : null;
|
||||||
|
info.entities = photoEntry.entities;
|
||||||
|
info.masks = photoEntry.stickers;
|
||||||
|
info.ttl = photoEntry.ttl;
|
||||||
|
info.videoEditedInfo = videoEditedInfo;
|
||||||
|
info.canDeleteAfter = true;
|
||||||
|
photos.add(info);
|
||||||
|
photoEntry.reset();
|
||||||
|
sending = true;
|
||||||
|
SendMessagesHelper.prepareSendingMedia(accountInstance, photos, dialog_id, replyingMessageObject, getThreadMessage(), null, false, false, editingMessageObject, notify, scheduleDate);
|
||||||
|
if (delegate != null) {
|
||||||
|
delegate.onMessageSend(null, true, scheduleDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void willHidePhotoViewer() {
|
||||||
|
if (!sending) {
|
||||||
|
try {
|
||||||
|
file.delete();
|
||||||
|
} catch (Throwable ignore) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, parentFragment);
|
||||||
|
});
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
messageEditText.setDelegate(() -> {
|
messageEditText.setDelegate(() -> {
|
||||||
if (delegate != null) {
|
if (delegate != null) {
|
||||||
|
@ -2012,7 +2025,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
preferences.edit().putInt("hidekeyboard_" + dialog_id, botButtonsMessageObject.getId()).commit();
|
preferences.edit().putInt("hidekeyboard_" + dialog_id, botButtonsMessageObject.getId()).commit();
|
||||||
}
|
}
|
||||||
if (searchingType != 0) {
|
if (searchingType != 0) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, true);
|
||||||
if (emojiView != null) {
|
if (emojiView != null) {
|
||||||
emojiView.closeSearch(true);
|
emojiView.closeSearch(true);
|
||||||
}
|
}
|
||||||
|
@ -2305,7 +2318,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
attachLayout.addView(botButton, LayoutHelper.createLinear(48, 48));
|
attachLayout.addView(botButton, LayoutHelper.createLinear(48, 48));
|
||||||
botButton.setOnClickListener(v -> {
|
botButton.setOnClickListener(v -> {
|
||||||
if (searchingType != 0) {
|
if (searchingType != 0) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, false);
|
||||||
emojiView.closeSearch(false);
|
emojiView.closeSearch(false);
|
||||||
messageEditText.requestFocus();
|
messageEditText.requestFocus();
|
||||||
}
|
}
|
||||||
|
@ -2921,7 +2934,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
}
|
}
|
||||||
if (stickersExpanded) {
|
if (stickersExpanded) {
|
||||||
if (searchingType != 0) {
|
if (searchingType != 0) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, true);
|
||||||
emojiView.closeSearch(true);
|
emojiView.closeSearch(true);
|
||||||
emojiView.hideSearchKeyboard();
|
emojiView.hideSearchKeyboard();
|
||||||
if (emojiTabOpen) {
|
if (emojiTabOpen) {
|
||||||
|
@ -3699,7 +3712,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
hideKeyboardRunnable = null;
|
hideKeyboardRunnable = null;
|
||||||
}
|
}
|
||||||
int visibility = getVisibility();
|
int visibility = getVisibility();
|
||||||
if (showKeyboardOnResume) {
|
if (showKeyboardOnResume && parentFragment.isLastFragment()) {
|
||||||
showKeyboardOnResume = false;
|
showKeyboardOnResume = false;
|
||||||
if (searchingType == 0) {
|
if (searchingType == 0) {
|
||||||
messageEditText.requestFocus();
|
messageEditText.requestFocus();
|
||||||
|
@ -4167,7 +4180,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (searchingType != 0) {
|
if (searchingType != 0) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, true);
|
||||||
emojiView.closeSearch(false);
|
emojiView.closeSearch(false);
|
||||||
|
|
||||||
if (stickersExpanded) {
|
if (stickersExpanded) {
|
||||||
|
@ -5805,7 +5818,9 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
}
|
}
|
||||||
if (draftMessage == null && !hadEditingMessage) {
|
if (draftMessage == null && !hadEditingMessage) {
|
||||||
draftMessage = messageEditText.length() > 0 ? messageEditText.getText() : null;
|
draftMessage = messageEditText.length() > 0 ? messageEditText.getText() : null;
|
||||||
|
draftSearchWebpage = messageWebPageSearch;
|
||||||
}
|
}
|
||||||
|
messageWebPageSearch = editingMessageObject.messageOwner.media instanceof TLRPC.TL_messageMediaWebPage;
|
||||||
if (!keyboardVisible) {
|
if (!keyboardVisible) {
|
||||||
AndroidUtilities.runOnUIThread(setTextFieldRunnable = () -> {
|
AndroidUtilities.runOnUIThread(setTextFieldRunnable = () -> {
|
||||||
setFieldText(textToSetWithKeyboard);
|
setFieldText(textToSetWithKeyboard);
|
||||||
|
@ -5896,6 +5911,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
scheduledButton.setVisibility(VISIBLE);
|
scheduledButton.setVisibility(VISIBLE);
|
||||||
}
|
}
|
||||||
messageEditText.setText(draftMessage);
|
messageEditText.setText(draftMessage);
|
||||||
|
messageWebPageSearch = draftSearchWebpage;
|
||||||
messageEditText.setSelection(messageEditText.length());
|
messageEditText.setSelection(messageEditText.length());
|
||||||
if (getVisibility() == VISIBLE) {
|
if (getVisibility() == VISIBLE) {
|
||||||
delegate.onAttachButtonShow();
|
delegate.onAttachButtonShow();
|
||||||
|
@ -6433,7 +6449,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
if (emojiView != null) {
|
if (emojiView != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emojiView = new EmojiView(allowStickers, allowGifs, parentActivity, true, info) {
|
emojiView = new EmojiView(allowStickers, allowGifs, getContext(), true, info, sizeNotifierLayout) {
|
||||||
@Override
|
@Override
|
||||||
public void setTranslationY(float translationY) {
|
public void setTranslationY(float translationY) {
|
||||||
super.setTranslationY(translationY);
|
super.setTranslationY(translationY);
|
||||||
|
@ -6443,6 +6459,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
emojiView.setVisibility(GONE);
|
emojiView.setVisibility(GONE);
|
||||||
|
emojiView.setShowing(false);
|
||||||
emojiView.setDelegate(new EmojiView.EmojiViewDelegate() {
|
emojiView.setDelegate(new EmojiView.EmojiViewDelegate() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -6487,7 +6504,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
}
|
}
|
||||||
if (stickersExpanded) {
|
if (stickersExpanded) {
|
||||||
if (searchingType != 0) {
|
if (searchingType != 0) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, true);
|
||||||
emojiView.closeSearch(true, MessageObject.getStickerSetId(sticker));
|
emojiView.closeSearch(true, MessageObject.getStickerSetId(sticker));
|
||||||
emojiView.hideSearchKeyboard();
|
emojiView.hideSearchKeyboard();
|
||||||
}
|
}
|
||||||
|
@ -6550,7 +6567,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
SendMessagesHelper.prepareSendingBotContextResult(accountInstance, result, params, dialog_id, replyingMessageObject, getThreadMessage(), notify, scheduleDate);
|
SendMessagesHelper.prepareSendingBotContextResult(accountInstance, result, params, dialog_id, replyingMessageObject, getThreadMessage(), notify, scheduleDate);
|
||||||
|
|
||||||
if (searchingType != 0) {
|
if (searchingType != 0) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, true);
|
||||||
emojiView.closeSearch(true);
|
emojiView.closeSearch(true);
|
||||||
emojiView.hideSearchKeyboard();
|
emojiView.hideSearchKeyboard();
|
||||||
}
|
}
|
||||||
|
@ -6621,13 +6638,9 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSearchOpenClose(int type) {
|
public void onSearchOpenClose(int type) {
|
||||||
searchingType = type;
|
setSearchingTypeInternal(type, true);
|
||||||
if (type != 0) {
|
if (type != 0) {
|
||||||
// expandStickersWithKeyboard = true;
|
|
||||||
// if (expandStickersWithKeyboard) {
|
|
||||||
// expandStickersWithKeyboard = false;
|
|
||||||
setStickersExpanded(true, true, false);
|
setStickersExpanded(true, true, false);
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
if (emojiTabOpen && searchingType == 2) {
|
if (emojiTabOpen && searchingType == 2) {
|
||||||
checkStickresExpandHeight();
|
checkStickresExpandHeight();
|
||||||
|
@ -6659,6 +6672,11 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
return dialog_id;
|
return dialog_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getThreadId() {
|
||||||
|
return getThreadMessageId();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void showTrendingStickersAlert(TrendingStickersLayout layout) {
|
public void showTrendingStickersAlert(TrendingStickersLayout layout) {
|
||||||
if (parentActivity != null && parentFragment != null) {
|
if (parentActivity != null && parentFragment != null) {
|
||||||
|
@ -6680,6 +6698,16 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
trendingStickersAlert.show();
|
trendingStickersAlert.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateEnterView() {
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getProgressToSearchOpened() {
|
||||||
|
return searchToOpenProgress;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
emojiView.setDragListener(new EmojiView.DragListener() {
|
emojiView.setDragListener(new EmojiView.DragListener() {
|
||||||
|
|
||||||
|
@ -6752,7 +6780,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
return stickersTabOpen && !(!stickersExpanded && messageEditText.length() > 0) && emojiView.areThereAnyStickers() && !waitingForKeyboardOpen;
|
return stickersTabOpen && !(!stickersExpanded && messageEditText.length() > 0) && emojiView.areThereAnyStickers() && !waitingForKeyboardOpen;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
sizeNotifierLayout.addView(emojiView, sizeNotifierLayout.getChildCount() - 1);
|
sizeNotifierLayout.addView(emojiView, sizeNotifierLayout.getChildCount() - 5);
|
||||||
checkChannelRights();
|
checkChannelRights();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6768,7 +6796,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (searchingType != 0) {
|
if (searchingType != 0) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, true);
|
||||||
emojiView.closeSearch(true);
|
emojiView.closeSearch(true);
|
||||||
emojiView.hideSearchKeyboard();
|
emojiView.hideSearchKeyboard();
|
||||||
}
|
}
|
||||||
|
@ -6803,6 +6831,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
if (!emojiViewVisible && emojiView != null && emojiView.getVisibility() != GONE) {
|
if (!emojiViewVisible && emojiView != null && emojiView.getVisibility() != GONE) {
|
||||||
sizeNotifierLayout.removeView(emojiView);
|
sizeNotifierLayout.removeView(emojiView);
|
||||||
emojiView.setVisibility(GONE);
|
emojiView.setVisibility(GONE);
|
||||||
|
emojiView.setShowing(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6828,7 +6857,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
int previusHeight = 0;
|
int previusHeight = 0;
|
||||||
if (contentType == 0) {
|
if (contentType == 0) {
|
||||||
if (emojiView.getParent() == null) {
|
if (emojiView.getParent() == null) {
|
||||||
sizeNotifierLayout.addView(emojiView, sizeNotifierLayout.getChildCount() - 1);
|
sizeNotifierLayout.addView(emojiView, sizeNotifierLayout.getChildCount() - 5);
|
||||||
}
|
}
|
||||||
samePannelWasVisible = emojiViewVisible && emojiView.getVisibility() == View.VISIBLE;
|
samePannelWasVisible = emojiViewVisible && emojiView.getVisibility() == View.VISIBLE;
|
||||||
emojiView.setVisibility(VISIBLE);
|
emojiView.setVisibility(VISIBLE);
|
||||||
|
@ -6839,6 +6868,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
anotherPanelWasVisible = true;
|
anotherPanelWasVisible = true;
|
||||||
previusHeight = botKeyboardView.getMeasuredHeight();
|
previusHeight = botKeyboardView.getMeasuredHeight();
|
||||||
}
|
}
|
||||||
|
emojiView.setShowing(true);
|
||||||
currentView = emojiView;
|
currentView = emojiView;
|
||||||
animatingContentType = 0;
|
animatingContentType = 0;
|
||||||
} else if (contentType == 1) {
|
} else if (contentType == 1) {
|
||||||
|
@ -6847,6 +6877,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
if (emojiView != null && emojiView.getVisibility() != GONE) {
|
if (emojiView != null && emojiView.getVisibility() != GONE) {
|
||||||
sizeNotifierLayout.removeView(emojiView);
|
sizeNotifierLayout.removeView(emojiView);
|
||||||
emojiView.setVisibility(GONE);
|
emojiView.setVisibility(GONE);
|
||||||
|
emojiView.setShowing(false);
|
||||||
emojiViewVisible = false;
|
emojiViewVisible = false;
|
||||||
anotherPanelWasVisible = true;
|
anotherPanelWasVisible = true;
|
||||||
previusHeight = emojiView.getMeasuredHeight();
|
previusHeight = emojiView.getMeasuredHeight();
|
||||||
|
@ -6918,6 +6949,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
if (emojiViewVisible = true) {
|
if (emojiViewVisible = true) {
|
||||||
animatingContentType = 0;
|
animatingContentType = 0;
|
||||||
}
|
}
|
||||||
|
emojiView.setShowing(false);
|
||||||
panelAnimation = new AnimatorSet();
|
panelAnimation = new AnimatorSet();
|
||||||
panelAnimation.playTogether(ObjectAnimator.ofFloat(emojiView, View.TRANSLATION_Y, emojiView.getMeasuredHeight()));
|
panelAnimation.playTogether(ObjectAnimator.ofFloat(emojiView, View.TRANSLATION_Y, emojiView.getMeasuredHeight()));
|
||||||
panelAnimation.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator);
|
panelAnimation.setInterpolator(AdjustPanLayoutHelper.keyboardInterpolator);
|
||||||
|
@ -6955,6 +6987,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
emojiPadding = 0;
|
emojiPadding = 0;
|
||||||
sizeNotifierLayout.removeView(emojiView);
|
sizeNotifierLayout.removeView(emojiView);
|
||||||
emojiView.setVisibility(GONE);
|
emojiView.setVisibility(GONE);
|
||||||
|
emojiView.setShowing(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
removeEmojiViewAfterAnimation = false;
|
removeEmojiViewAfterAnimation = false;
|
||||||
|
@ -6966,7 +6999,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
}
|
}
|
||||||
emojiViewVisible = false;
|
emojiViewVisible = false;
|
||||||
}
|
}
|
||||||
if (botKeyboardView != null) {
|
if (botKeyboardView != null && botKeyboardView.getVisibility() == View.VISIBLE) {
|
||||||
if (show != 2 || AndroidUtilities.usingHardwareInput || AndroidUtilities.isInMultiwindow) {
|
if (show != 2 || AndroidUtilities.usingHardwareInput || AndroidUtilities.isInMultiwindow) {
|
||||||
if (smoothKeyboard && !keyboardVisible) {
|
if (smoothKeyboard && !keyboardVisible) {
|
||||||
if (botKeyboardViewVisible) {
|
if (botKeyboardViewVisible) {
|
||||||
|
@ -7124,7 +7157,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
preferences.edit().putInt("hidekeyboard_" + dialog_id, botButtonsMessageObject.getId()).commit();
|
preferences.edit().putInt("hidekeyboard_" + dialog_id, botButtonsMessageObject.getId()).commit();
|
||||||
}
|
}
|
||||||
if (byBackButton && searchingType != 0) {
|
if (byBackButton && searchingType != 0) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, true);
|
||||||
if (emojiView != null) {
|
if (emojiView != null) {
|
||||||
emojiView.closeSearch(true);
|
emojiView.closeSearch(true);
|
||||||
}
|
}
|
||||||
|
@ -7135,7 +7168,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (searchingType != 0) {
|
if (searchingType != 0) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, false);
|
||||||
emojiView.closeSearch(false);
|
emojiView.closeSearch(false);
|
||||||
messageEditText.requestFocus();
|
messageEditText.requestFocus();
|
||||||
}
|
}
|
||||||
|
@ -7144,6 +7177,45 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setSearchingTypeInternal(int searchingType, boolean animated) {
|
||||||
|
boolean showSearchingNew = searchingType != 0;
|
||||||
|
boolean showSearchingOld = this.searchingType != 0;
|
||||||
|
if (showSearchingNew != showSearchingOld) {
|
||||||
|
if (searchAnimator != null) {
|
||||||
|
searchAnimator.removeAllListeners();
|
||||||
|
searchAnimator.cancel();
|
||||||
|
}
|
||||||
|
if (!animated) {
|
||||||
|
searchToOpenProgress = showSearchingNew ? 1f : 0f;
|
||||||
|
if (emojiView != null) {
|
||||||
|
emojiView.searchProgressChanged();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
searchAnimator = ValueAnimator.ofFloat(searchToOpenProgress, showSearchingNew ? 1f : 0f);
|
||||||
|
searchAnimator.addUpdateListener(valueAnimator -> {
|
||||||
|
searchToOpenProgress = (float) valueAnimator.getAnimatedValue();
|
||||||
|
if (emojiView != null) {
|
||||||
|
emojiView.searchProgressChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
searchAnimator.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
searchToOpenProgress = showSearchingNew ? 1f : 0f;
|
||||||
|
if (emojiView != null) {
|
||||||
|
emojiView.searchProgressChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
searchAnimator.setDuration(220);
|
||||||
|
searchAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
searchAnimator.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.searchingType = searchingType;
|
||||||
|
}
|
||||||
|
|
||||||
private void openKeyboardInternal() {
|
private void openKeyboardInternal() {
|
||||||
showPopup(AndroidUtilities.usingHardwareInput || AndroidUtilities.isInMultiwindow || parentFragment != null && parentFragment.isInBubbleMode() || isPaused ? 0 : 2, 0);
|
showPopup(AndroidUtilities.usingHardwareInput || AndroidUtilities.isInMultiwindow || parentFragment != null && parentFragment.isInBubbleMode() || isPaused ? 0 : 2, 0);
|
||||||
messageEditText.requestFocus();
|
messageEditText.requestFocus();
|
||||||
|
@ -7206,7 +7278,7 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
super.onSizeChanged(w, h, oldw, oldh);
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
if (w != oldw && stickersExpanded) {
|
if (w != oldw && stickersExpanded) {
|
||||||
searchingType = 0;
|
setSearchingTypeInternal(0, false);
|
||||||
emojiView.closeSearch(false);
|
emojiView.closeSearch(false);
|
||||||
setStickersExpanded(false, false, false);
|
setStickersExpanded(false, false, false);
|
||||||
}
|
}
|
||||||
|
@ -7349,6 +7421,9 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
if (botKeyboardView != null) {
|
if (botKeyboardView != null) {
|
||||||
botKeyboardView.invalidateViews();
|
botKeyboardView.invalidateViews();
|
||||||
}
|
}
|
||||||
|
if (messageEditText != null) {
|
||||||
|
messageEditText.postInvalidate();
|
||||||
|
}
|
||||||
} else if (id == NotificationCenter.recordProgressChanged) {
|
} else if (id == NotificationCenter.recordProgressChanged) {
|
||||||
int guid = (Integer) args[0];
|
int guid = (Integer) args[0];
|
||||||
if (guid != recordingGuid) {
|
if (guid != recordingGuid) {
|
||||||
|
@ -8404,4 +8479,17 @@ public class ChatActivityEnterView extends FrameLayout implements NotificationCe
|
||||||
AndroidUtilities.cancelRunOnUIThread(runEmojiPanelAnimation);
|
AndroidUtilities.cancelRunOnUIThread(runEmojiPanelAnimation);
|
||||||
runEmojiPanelAnimation.run();
|
runEmojiPanelAnimation.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dispatchDraw(Canvas canvas) {
|
||||||
|
if (emojiView == null || emojiView.getVisibility() != View.VISIBLE || emojiView.getStickersExpandOffset() == 0) {
|
||||||
|
super.dispatchDraw(canvas);
|
||||||
|
} else {
|
||||||
|
canvas.save();
|
||||||
|
canvas.clipRect(0, AndroidUtilities.dp(2), getMeasuredWidth(), getMeasuredHeight());
|
||||||
|
canvas.translate(0, -emojiView.getStickersExpandOffset());
|
||||||
|
super.dispatchDraw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.AnimatorSet;
|
import android.animation.AnimatorSet;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
@ -53,7 +54,7 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent
|
||||||
private ImageView timeItem;
|
private ImageView timeItem;
|
||||||
private TimerDrawable timerDrawable;
|
private TimerDrawable timerDrawable;
|
||||||
private ChatActivity parentFragment;
|
private ChatActivity parentFragment;
|
||||||
private StatusDrawable[] statusDrawables = new StatusDrawable[5];
|
private StatusDrawable[] statusDrawables = new StatusDrawable[6];
|
||||||
private AvatarDrawable avatarDrawable = new AvatarDrawable();
|
private AvatarDrawable avatarDrawable = new AvatarDrawable();
|
||||||
private int currentAccount = UserConfig.selectedAccount;
|
private int currentAccount = UserConfig.selectedAccount;
|
||||||
private boolean occupyStatusBar = true;
|
private boolean occupyStatusBar = true;
|
||||||
|
@ -156,6 +157,7 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent
|
||||||
statusDrawables[2] = new SendingFileDrawable(false);
|
statusDrawables[2] = new SendingFileDrawable(false);
|
||||||
statusDrawables[3] = new PlayingGameDrawable(false);
|
statusDrawables[3] = new PlayingGameDrawable(false);
|
||||||
statusDrawables[4] = new RoundStatusDrawable(false);
|
statusDrawables[4] = new RoundStatusDrawable(false);
|
||||||
|
statusDrawables[5] = new ChoosingStickerStatusDrawable(false);
|
||||||
for (int a = 0; a < statusDrawables.length; a++) {
|
for (int a = 0; a < statusDrawables.length; a++) {
|
||||||
statusDrawables[a].setIsChat(chat != null);
|
statusDrawables[a].setIsChat(chat != null);
|
||||||
}
|
}
|
||||||
|
@ -380,8 +382,14 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent
|
||||||
private void setTypingAnimation(boolean start) {
|
private void setTypingAnimation(boolean start) {
|
||||||
if (start) {
|
if (start) {
|
||||||
try {
|
try {
|
||||||
Integer type = MessagesController.getInstance(currentAccount).getPrintingStringType(parentFragment.getDialogId(), parentFragment.getThreadId());
|
int type = MessagesController.getInstance(currentAccount).getPrintingStringType(parentFragment.getDialogId(), parentFragment.getThreadId());
|
||||||
subtitleTextView.setLeftDrawable(statusDrawables[type]);
|
if (type == 5) {
|
||||||
|
subtitleTextView.replaceTextWithDrawable(statusDrawables[type], "**oo**");
|
||||||
|
subtitleTextView.setLeftDrawable(null);
|
||||||
|
} else {
|
||||||
|
subtitleTextView.replaceTextWithDrawable(null, null);
|
||||||
|
subtitleTextView.setLeftDrawable(statusDrawables[type]);
|
||||||
|
}
|
||||||
for (int a = 0; a < statusDrawables.length; a++) {
|
for (int a = 0; a < statusDrawables.length; a++) {
|
||||||
if (a == type) {
|
if (a == type) {
|
||||||
statusDrawables[a].start();
|
statusDrawables[a].start();
|
||||||
|
@ -394,6 +402,7 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
subtitleTextView.setLeftDrawable(null);
|
subtitleTextView.setLeftDrawable(null);
|
||||||
|
subtitleTextView.replaceTextWithDrawable(null, null);
|
||||||
for (int a = 0; a < statusDrawables.length; a++) {
|
for (int a = 0; a < statusDrawables.length; a++) {
|
||||||
statusDrawables[a].stop();
|
statusDrawables[a].stop();
|
||||||
}
|
}
|
||||||
|
@ -745,4 +754,8 @@ public class ChatAvatarContainer extends FrameLayout implements NotificationCent
|
||||||
public SharedMediaLayout.SharedMediaPreloader getSharedMediaPreloader() {
|
public SharedMediaLayout.SharedMediaPreloader getSharedMediaPreloader() {
|
||||||
return sharedMediaPreloader;
|
return sharedMediaPreloader;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BackupImageView getAvatarImageView() {
|
||||||
|
return avatarImageView;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import android.animation.ObjectAnimator;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.LinearGradient;
|
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.Path;
|
import android.graphics.Path;
|
||||||
|
@ -16,6 +15,7 @@ import android.graphics.Rect;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import androidx.annotation.Keep;
|
import androidx.annotation.Keep;
|
||||||
|
|
||||||
|
import android.graphics.Shader;
|
||||||
import android.text.TextPaint;
|
import android.text.TextPaint;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
@ -314,9 +314,11 @@ public class CheckBoxBase {
|
||||||
if (backgroundType == 12 || backgroundType == 13) {
|
if (backgroundType == 12 || backgroundType == 13) {
|
||||||
backgroundPaint.setStyle(Paint.Style.FILL);
|
backgroundPaint.setStyle(Paint.Style.FILL);
|
||||||
if (messageDrawable != null && messageDrawable.hasGradient()) {
|
if (messageDrawable != null && messageDrawable.hasGradient()) {
|
||||||
LinearGradient shader = messageDrawable.getGradientShader();
|
Shader shader = messageDrawable.getGradientShader();
|
||||||
Matrix matrix = messageDrawable.getMatrix();
|
Matrix matrix = messageDrawable.getMatrix();
|
||||||
matrix.setTranslate(0, -messageDrawable.getTopY() + bounds.top);
|
matrix.reset();
|
||||||
|
messageDrawable.applyMatrixScale();
|
||||||
|
matrix.postTranslate(0, -messageDrawable.getTopY() + bounds.top);
|
||||||
shader.setLocalMatrix(matrix);
|
shader.setLocalMatrix(matrix);
|
||||||
backgroundPaint.setShader(shader);
|
backgroundPaint.setShader(shader);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
package org.telegram.ui.Components;
|
||||||
|
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.ColorFilter;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
|
import org.telegram.ui.ActionBar.Theme;
|
||||||
|
|
||||||
|
public class ChoosingStickerStatusDrawable extends StatusDrawable {
|
||||||
|
|
||||||
|
Paint strokePaint;
|
||||||
|
Paint fillPaint;
|
||||||
|
|
||||||
|
private boolean isChat = false;
|
||||||
|
private long lastUpdateTime = 0;
|
||||||
|
private boolean started = false;
|
||||||
|
float progress;
|
||||||
|
boolean increment = true;
|
||||||
|
|
||||||
|
public ChoosingStickerStatusDrawable(boolean createPaint) {
|
||||||
|
if (createPaint) {
|
||||||
|
strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
fillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
strokePaint.setStyle(Paint.Style.STROKE);
|
||||||
|
strokePaint.setStrokeWidth(AndroidUtilities.dpf2(1.2f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
lastUpdateTime = System.currentTimeMillis();
|
||||||
|
started = true;
|
||||||
|
invalidateSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() {
|
||||||
|
started = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setIsChat(boolean value) {
|
||||||
|
this.isChat = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColor(int color) {
|
||||||
|
fillPaint.setColor(color);
|
||||||
|
strokePaint.setColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(@NonNull Canvas canvas) {
|
||||||
|
float animationProgress = Math.min(progress, 1f);
|
||||||
|
float k = 0.3f;
|
||||||
|
float p = CubicBezierInterpolator.EASE_IN.getInterpolation(animationProgress < k ? animationProgress / k : 1f);
|
||||||
|
float p2 = CubicBezierInterpolator.EASE_OUT.getInterpolation(animationProgress < k ? 0 : (animationProgress - k) / (1f - k));
|
||||||
|
float cx, xOffset;
|
||||||
|
if (increment) {
|
||||||
|
cx = AndroidUtilities.dp(2.1f) * p + (AndroidUtilities.dp(7) - AndroidUtilities.dp(2.1f)) * (1f - p);
|
||||||
|
xOffset = AndroidUtilities.dpf2(1.5f) * (1f - CubicBezierInterpolator.EASE_OUT.getInterpolation(progress / 2));
|
||||||
|
} else {
|
||||||
|
cx = AndroidUtilities.dp(2.1f) * (1f - p) + (AndroidUtilities.dp(7) - AndroidUtilities.dp(2.1f)) * p;
|
||||||
|
xOffset = AndroidUtilities.dpf2(1.5f) * CubicBezierInterpolator.EASE_OUT_QUINT.getInterpolation(progress / 2);
|
||||||
|
}
|
||||||
|
float cy = AndroidUtilities.dp(11) / 2f;
|
||||||
|
float r = AndroidUtilities.dpf2(2f);
|
||||||
|
|
||||||
|
float scaleOffset = AndroidUtilities.dpf2(0.5f) * p - AndroidUtilities.dpf2(0.5f) * p2;
|
||||||
|
|
||||||
|
Paint strokePaint = this.strokePaint != null ? this.strokePaint : Theme.chat_statusRecordPaint;
|
||||||
|
Paint paint = this.fillPaint != null ? this.fillPaint : Theme.chat_statusPaint;
|
||||||
|
if (strokePaint.getStrokeWidth() != AndroidUtilities.dp(0.8f)) {
|
||||||
|
strokePaint.setStrokeWidth(AndroidUtilities.dp(0.8f));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(strokePaint.getStrokeWidth() / 2f + xOffset + AndroidUtilities.dp(9) * i + getBounds().left + AndroidUtilities.dpf2(0.2f), strokePaint.getStrokeWidth() / 2f + AndroidUtilities.dpf2(2f) + getBounds().top);
|
||||||
|
|
||||||
|
AndroidUtilities.rectTmp.set(0, scaleOffset, AndroidUtilities.dp(7), AndroidUtilities.dp(11) - scaleOffset);
|
||||||
|
canvas.drawOval(AndroidUtilities.rectTmp, strokePaint);
|
||||||
|
canvas.drawCircle(cx, cy, r, paint);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (started) {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
long newTime = System.currentTimeMillis();
|
||||||
|
long dt = newTime - lastUpdateTime;
|
||||||
|
lastUpdateTime = newTime;
|
||||||
|
if (dt > 16) {
|
||||||
|
dt = 16;
|
||||||
|
}
|
||||||
|
progress += dt / 500f;
|
||||||
|
if (progress >= 2f) {
|
||||||
|
progress = 0;
|
||||||
|
increment = !increment;
|
||||||
|
}
|
||||||
|
invalidateSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAlpha(int i) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColorFilter(@Nullable ColorFilter colorFilter) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOpacity() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIntrinsicWidth() {
|
||||||
|
return AndroidUtilities.dp(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIntrinsicHeight() {
|
||||||
|
return AndroidUtilities.dp(18);
|
||||||
|
}
|
||||||
|
}
|
|
@ -75,7 +75,6 @@ public class ColorPicker extends FrameLayout {
|
||||||
private EditTextBoldCursor[] colorEditText;
|
private EditTextBoldCursor[] colorEditText;
|
||||||
private ImageView clearButton;
|
private ImageView clearButton;
|
||||||
private ImageView addButton;
|
private ImageView addButton;
|
||||||
private ImageView exchangeButton;
|
|
||||||
private TextView resetButton;
|
private TextView resetButton;
|
||||||
private ActionBarMenuItem menuItem;
|
private ActionBarMenuItem menuItem;
|
||||||
|
|
||||||
|
@ -389,24 +388,6 @@ public class ColorPicker extends FrameLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exchangeButton = new ImageView(getContext());
|
|
||||||
exchangeButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_dialogButtonSelector), 1));
|
|
||||||
exchangeButton.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText), PorterDuff.Mode.MULTIPLY));
|
|
||||||
exchangeButton.setScaleType(ImageView.ScaleType.CENTER);
|
|
||||||
exchangeButton.setVisibility(GONE);
|
|
||||||
exchangeButton.setImageResource(R.drawable.themes_swapcolor);
|
|
||||||
exchangeButton.setOnClickListener(v -> {
|
|
||||||
if (exchangeButton.getAlpha() != 1.0f) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int color = radioButton[0].getColor();
|
|
||||||
radioButton[0].setColor(radioButton[1].getColor());
|
|
||||||
radioButton[1].setColor(color);
|
|
||||||
delegate.setColor(radioButton[0].getColor(), 0, false);
|
|
||||||
delegate.setColor(radioButton[1].getColor(), 1, true);
|
|
||||||
});
|
|
||||||
radioContainer.addView(exchangeButton, 1, LayoutHelper.createFrame(30, 30, Gravity.LEFT | Gravity.TOP, 0, 1, 0, 0));
|
|
||||||
|
|
||||||
addButton = new ImageView(getContext());
|
addButton = new ImageView(getContext());
|
||||||
addButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_dialogButtonSelector), 1));
|
addButton.setBackground(Theme.createSelectorDrawable(Theme.getColor(Theme.key_dialogButtonSelector), 1));
|
||||||
addButton.setImageResource(R.drawable.themes_addcolor);
|
addButton.setImageResource(R.drawable.themes_addcolor);
|
||||||
|
@ -432,19 +413,7 @@ public class ColorPicker extends FrameLayout {
|
||||||
animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_X, 1.0f));
|
animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_X, 1.0f));
|
||||||
animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_Y, 1.0f));
|
animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_Y, 1.0f));
|
||||||
animators.add(ObjectAnimator.ofFloat(addButton, View.TRANSLATION_X, AndroidUtilities.dp(30) + AndroidUtilities.dp(13)));
|
animators.add(ObjectAnimator.ofFloat(addButton, View.TRANSLATION_X, AndroidUtilities.dp(30) + AndroidUtilities.dp(13)));
|
||||||
if (myMessagesColor) {
|
|
||||||
exchangeButton.setVisibility(VISIBLE);
|
|
||||||
animators.add(ObjectAnimator.ofFloat(addButton, View.ALPHA, 0.0f));
|
|
||||||
animators.add(ObjectAnimator.ofFloat(addButton, View.SCALE_X, 0.0f));
|
|
||||||
animators.add(ObjectAnimator.ofFloat(addButton, View.SCALE_Y, 0.0f));
|
|
||||||
animators.add(ObjectAnimator.ofFloat(exchangeButton, View.ALPHA, 1.0f));
|
|
||||||
animators.add(ObjectAnimator.ofFloat(exchangeButton, View.SCALE_X, 1.0f));
|
|
||||||
animators.add(ObjectAnimator.ofFloat(exchangeButton, View.SCALE_Y, 1.0f));
|
|
||||||
}
|
|
||||||
} else if (colorsCount == 2) {
|
} else if (colorsCount == 2) {
|
||||||
if (myMessagesColor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
colorsCount = 3;
|
colorsCount = 3;
|
||||||
if (radioButton[2].getColor() == 0) {
|
if (radioButton[2].getColor() == 0) {
|
||||||
int color = radioButton[0].getColor();
|
int color = radioButton[0].getColor();
|
||||||
|
@ -461,9 +430,6 @@ public class ColorPicker extends FrameLayout {
|
||||||
animators.add(ObjectAnimator.ofFloat(addButton, View.TRANSLATION_X, AndroidUtilities.dp(30) * 2 + AndroidUtilities.dp(13) * 2));
|
animators.add(ObjectAnimator.ofFloat(addButton, View.TRANSLATION_X, AndroidUtilities.dp(30) * 2 + AndroidUtilities.dp(13) * 2));
|
||||||
delegate.setColor(radioButton[2].getColor(), 2, true);
|
delegate.setColor(radioButton[2].getColor(), 2, true);
|
||||||
} else if (colorsCount == 3) {
|
} else if (colorsCount == 3) {
|
||||||
if (myMessagesColor) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
colorsCount = 4;
|
colorsCount = 4;
|
||||||
if (radioButton[3].getColor() == 0) {
|
if (radioButton[3].getColor() == 0) {
|
||||||
radioButton[3].setColor(generateGradientColors(radioButton[2].getColor()));
|
radioButton[3].setColor(generateGradientColors(radioButton[2].getColor()));
|
||||||
|
@ -486,7 +452,7 @@ public class ColorPicker extends FrameLayout {
|
||||||
colorsAnimator.addListener(new AnimatorListenerAdapter() {
|
colorsAnimator.addListener(new AnimatorListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
if (colorsCount == 4 || myMessagesColor && colorsCount == 2) {
|
if (colorsCount == 4) {
|
||||||
addButton.setVisibility(INVISIBLE);
|
addButton.setVisibility(INVISIBLE);
|
||||||
}
|
}
|
||||||
colorsAnimator = null;
|
colorsAnimator = null;
|
||||||
|
@ -524,15 +490,6 @@ public class ColorPicker extends FrameLayout {
|
||||||
animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_X, 0.0f));
|
animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_X, 0.0f));
|
||||||
animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_Y, 0.0f));
|
animators.add(ObjectAnimator.ofFloat(clearButton, View.SCALE_Y, 0.0f));
|
||||||
animators.add(ObjectAnimator.ofFloat(addButton, View.TRANSLATION_X, 0));
|
animators.add(ObjectAnimator.ofFloat(addButton, View.TRANSLATION_X, 0));
|
||||||
if (myMessagesColor) {
|
|
||||||
addButton.setVisibility(VISIBLE);
|
|
||||||
animators.add(ObjectAnimator.ofFloat(exchangeButton, View.ALPHA, 0.0f));
|
|
||||||
animators.add(ObjectAnimator.ofFloat(exchangeButton, View.SCALE_X, 0.0f));
|
|
||||||
animators.add(ObjectAnimator.ofFloat(exchangeButton, View.SCALE_Y, 0.0f));
|
|
||||||
animators.add(ObjectAnimator.ofFloat(addButton, View.ALPHA, 1.0f));
|
|
||||||
animators.add(ObjectAnimator.ofFloat(addButton, View.SCALE_X, 1.0f));
|
|
||||||
animators.add(ObjectAnimator.ofFloat(addButton, View.SCALE_Y, 1.0f));
|
|
||||||
}
|
|
||||||
} else if (colorsCount == 3) {
|
} else if (colorsCount == 3) {
|
||||||
colorsCount = 2;
|
colorsCount = 2;
|
||||||
animators = new ArrayList<>();
|
animators = new ArrayList<>();
|
||||||
|
@ -573,9 +530,6 @@ public class ColorPicker extends FrameLayout {
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
if (colorsCount == 1) {
|
if (colorsCount == 1) {
|
||||||
clearButton.setVisibility(INVISIBLE);
|
clearButton.setVisibility(INVISIBLE);
|
||||||
if (myMessagesColor) {
|
|
||||||
exchangeButton.setVisibility(INVISIBLE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (int a = 0; a < radioButton.length; a++) {
|
for (int a = 0; a < radioButton.length; a++) {
|
||||||
if (radioButton[a].getTag(R.id.index_tag) == null) {
|
if (radioButton[a].getTag(R.id.index_tag) == null) {
|
||||||
|
@ -641,9 +595,6 @@ public class ColorPicker extends FrameLayout {
|
||||||
private void updateColorsPosition(ArrayList<Animator> animators, int hidingIndex, boolean hiding, int width) {
|
private void updateColorsPosition(ArrayList<Animator> animators, int hidingIndex, boolean hiding, int width) {
|
||||||
int allX = 0;
|
int allX = 0;
|
||||||
int count = colorsCount;
|
int count = colorsCount;
|
||||||
if (myMessagesColor && colorsCount == 2) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
int visibleX = count * AndroidUtilities.dp(30) + (count - 1) * AndroidUtilities.dp(13);
|
int visibleX = count * AndroidUtilities.dp(30) + (count - 1) * AndroidUtilities.dp(13);
|
||||||
int left = radioContainer.getLeft() + visibleX;
|
int left = radioContainer.getLeft() + visibleX;
|
||||||
int w = width - AndroidUtilities.dp(currentResetType == 1 ? 50 : 0);
|
int w = width - AndroidUtilities.dp(currentResetType == 1 ? 50 : 0);
|
||||||
|
@ -661,10 +612,6 @@ public class ColorPicker extends FrameLayout {
|
||||||
for (int a = 0; a < radioButton.length; a++) {
|
for (int a = 0; a < radioButton.length; a++) {
|
||||||
boolean wasVisible = radioButton[a].getTag(R.id.index_tag) != null;
|
boolean wasVisible = radioButton[a].getTag(R.id.index_tag) != null;
|
||||||
if (a < colorsCount) {
|
if (a < colorsCount) {
|
||||||
if (a == 1 && myMessagesColor) {
|
|
||||||
exchangeButton.setTranslationX(allX);
|
|
||||||
allX += AndroidUtilities.dp(30) + AndroidUtilities.dp(13);
|
|
||||||
}
|
|
||||||
radioButton[a].setVisibility(VISIBLE);
|
radioButton[a].setVisibility(VISIBLE);
|
||||||
if (animators != null) {
|
if (animators != null) {
|
||||||
if (!wasVisible) {
|
if (!wasVisible) {
|
||||||
|
@ -969,7 +916,7 @@ public class ColorPicker extends FrameLayout {
|
||||||
addButton.setVisibility(GONE);
|
addButton.setVisibility(GONE);
|
||||||
clearButton.setVisibility(GONE);
|
clearButton.setVisibility(GONE);
|
||||||
} else {
|
} else {
|
||||||
if (newColorsCount < (myMessages ? 2 : 4)) {
|
if (newColorsCount < 4) {
|
||||||
addButton.setVisibility(VISIBLE);
|
addButton.setVisibility(VISIBLE);
|
||||||
addButton.setScaleX(1.0f);
|
addButton.setScaleX(1.0f);
|
||||||
addButton.setScaleY(1.0f);
|
addButton.setScaleY(1.0f);
|
||||||
|
@ -986,14 +933,6 @@ public class ColorPicker extends FrameLayout {
|
||||||
clearButton.setVisibility(GONE);
|
clearButton.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (myMessages) {
|
|
||||||
exchangeButton.setVisibility(newColorsCount == 2 ? VISIBLE : INVISIBLE);
|
|
||||||
exchangeButton.setAlpha(newColorsCount == 2 ? 1.0f : 0.0f);
|
|
||||||
exchangeButton.setScaleX(newColorsCount == 2 ? 1.0f : 0.0f);
|
|
||||||
exchangeButton.setScaleY(newColorsCount == 2 ? 1.0f : 0.0f);
|
|
||||||
} else {
|
|
||||||
exchangeButton.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
linearLayout.invalidate();
|
linearLayout.invalidate();
|
||||||
updateColorsPosition(null, 0, false, getMeasuredWidth());
|
updateColorsPosition(null, 0, false, getMeasuredWidth());
|
||||||
|
|
||||||
|
@ -1004,61 +943,6 @@ public class ColorPicker extends FrameLayout {
|
||||||
animators = null;
|
animators = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*if (!twoColors || !hasSecondColor) {
|
|
||||||
colorEditText[1].requestFocus();
|
|
||||||
}
|
|
||||||
for (int a = 2; a < colorEditText.length; a++) {
|
|
||||||
if (animated) {
|
|
||||||
if (twoColors) {
|
|
||||||
colorEditText[a].setVisibility(VISIBLE);
|
|
||||||
}
|
|
||||||
animators.add(ObjectAnimator.ofFloat(colorEditText[a], View.ALPHA, twoColors && hasSecondColor ? 1.0f : 0.0f));
|
|
||||||
} else {
|
|
||||||
colorEditText[a].setVisibility(twoColors ? VISIBLE : GONE);
|
|
||||||
colorEditText[a].setAlpha(twoColors && hasSecondColor ? 1.0f : 0.0f);
|
|
||||||
}
|
|
||||||
colorEditText[a].setTag(twoColors ? 1 : null);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/*if (animated) {
|
|
||||||
if (twoColors) {
|
|
||||||
exchangeButton.setVisibility(VISIBLE);
|
|
||||||
}
|
|
||||||
animators.add(ObjectAnimator.ofFloat(exchangeButton, View.ALPHA, twoColors && hasSecondColor ? 1.0f : 0.0f));
|
|
||||||
} else {
|
|
||||||
exchangeButton.setVisibility(twoColors ? VISIBLE : GONE);
|
|
||||||
exchangeButton.setAlpha(twoColors && hasSecondColor ? 1.0f : 0.0f);
|
|
||||||
}
|
|
||||||
if (twoColors) {
|
|
||||||
clearButton.setTag(hasSecondColor ? 1 : null);
|
|
||||||
clearButton.setRotation(hasSecondColor ? 0 : 45);
|
|
||||||
}
|
|
||||||
if (animated) {
|
|
||||||
if (twoColors) {
|
|
||||||
clearButton.setVisibility(VISIBLE);
|
|
||||||
}
|
|
||||||
animators.add(ObjectAnimator.ofFloat(clearButton, View.ALPHA, twoColors ? 1.0f : 0.0f));
|
|
||||||
} else {
|
|
||||||
clearButton.setVisibility(twoColors ? VISIBLE : GONE);
|
|
||||||
clearButton.setAlpha(twoColors ? 1.0f : 0.0f);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/*resetButton.setTag(hasChanges ? 1 : null);
|
|
||||||
resetButton.setText(resetType == 1 ? LocaleController.getString("ColorPickerResetAll", R.string.ColorPickerResetAll) : LocaleController.getString("ColorPickerReset", R.string.ColorPickerReset));
|
|
||||||
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) resetButton.getLayoutParams();
|
|
||||||
layoutParams.rightMargin = AndroidUtilities.dp(resetType == 1 ? 14 : (14 + 47));
|
|
||||||
if (animated) {
|
|
||||||
if (!hasChanges || resetButton.getVisibility() == VISIBLE && hasSecondColor) {
|
|
||||||
animators.add(ObjectAnimator.ofFloat(resetButton, View.ALPHA, 0.0f));
|
|
||||||
} else if (resetButton.getVisibility() != VISIBLE && !hasSecondColor) {
|
|
||||||
resetButton.setVisibility(VISIBLE);
|
|
||||||
animators.add(ObjectAnimator.ofFloat(resetButton, View.ALPHA, 1.0f));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resetButton.setAlpha(!hasChanges || hasSecondColor ? 0.0f : 1.0f);
|
|
||||||
resetButton.setVisibility(!hasChanges || hasSecondColor ? GONE : VISIBLE);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
if (animators != null && !animators.isEmpty()) {
|
if (animators != null && !animators.isEmpty()) {
|
||||||
AnimatorSet animatorSet = new AnimatorSet();
|
AnimatorSet animatorSet = new AnimatorSet();
|
||||||
animatorSet.playTogether(animators);
|
animatorSet.playTogether(animators);
|
||||||
|
@ -1066,12 +950,8 @@ public class ColorPicker extends FrameLayout {
|
||||||
animatorSet.addListener(new AnimatorListenerAdapter() {
|
animatorSet.addListener(new AnimatorListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
/*if (!hasChanges || hasSecondColor) {
|
|
||||||
resetButton.setVisibility(GONE);
|
|
||||||
}*/
|
|
||||||
if (!fewColors) {
|
if (!fewColors) {
|
||||||
clearButton.setVisibility(GONE);
|
clearButton.setVisibility(GONE);
|
||||||
exchangeButton.setVisibility(GONE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1135,8 +1015,6 @@ public class ColorPicker extends FrameLayout {
|
||||||
}
|
}
|
||||||
arrayList.add(new ThemeDescription(clearButton, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText));
|
arrayList.add(new ThemeDescription(clearButton, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText));
|
||||||
arrayList.add(new ThemeDescription(clearButton, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_dialogButtonSelector));
|
arrayList.add(new ThemeDescription(clearButton, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_dialogButtonSelector));
|
||||||
arrayList.add(new ThemeDescription(exchangeButton, ThemeDescription.FLAG_IMAGECOLOR, null, null, null, null, Theme.key_windowBackgroundWhiteBlackText));
|
|
||||||
arrayList.add(new ThemeDescription(exchangeButton, ThemeDescription.FLAG_BACKGROUNDFILTER, null, null, null, null, Theme.key_dialogButtonSelector));
|
|
||||||
if (menuItem != null) {
|
if (menuItem != null) {
|
||||||
ThemeDescription.ThemeDescriptionDelegate delegate = () -> {
|
ThemeDescription.ThemeDescriptionDelegate delegate = () -> {
|
||||||
menuItem.setIconColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText));
|
menuItem.setIconColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlackText));
|
||||||
|
|
|
@ -23,292 +23,384 @@ import org.telegram.ui.ActionBar.Theme;
|
||||||
|
|
||||||
public class CounterView extends View {
|
public class CounterView extends View {
|
||||||
|
|
||||||
private final static int ANIMATION_TYPE_IN = 0;
|
public CounterDrawable counterDrawable = new CounterDrawable(this);
|
||||||
private final static int ANIMATION_TYPE_OUT = 1;
|
|
||||||
private final static int ANIMATION_TYPE_REPLACE = 2;
|
|
||||||
|
|
||||||
int animationType = -1;
|
|
||||||
|
|
||||||
Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
RectF rectF = new RectF();
|
|
||||||
|
|
||||||
int currentCount;
|
|
||||||
private boolean countAnimationIncrement;
|
|
||||||
private ValueAnimator countAnimator;
|
|
||||||
private float countChangeProgress = 1f;
|
|
||||||
private StaticLayout countLayout;
|
|
||||||
private StaticLayout countOldLayout;
|
|
||||||
private StaticLayout countAnimationStableLayout;
|
|
||||||
private StaticLayout countAnimationInLayout;
|
|
||||||
|
|
||||||
private int countWidthOld;
|
|
||||||
private int countWidth;
|
|
||||||
|
|
||||||
private int circleColor;
|
|
||||||
private int textColor;
|
|
||||||
private String textColorKey = Theme.key_chat_goDownButtonCounter;
|
|
||||||
private String circleColorKey = Theme.key_chat_goDownButtonCounterBackground;
|
|
||||||
|
|
||||||
int lastH;
|
|
||||||
int gravity = Gravity.CENTER;
|
|
||||||
float countLeft;
|
|
||||||
float x;
|
|
||||||
|
|
||||||
private boolean reverseAnimation;
|
|
||||||
public float horizontalPadding;
|
|
||||||
|
|
||||||
|
|
||||||
public CounterView(Context context) {
|
public CounterView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
setVisibility(View.GONE);
|
setVisibility(View.GONE);
|
||||||
circlePaint.setColor(Color.BLACK);
|
counterDrawable.updateVisibility = true;
|
||||||
|
|
||||||
textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
|
||||||
textPaint.setTextSize(AndroidUtilities.dp(13));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
if (getMeasuredHeight() != lastH) {
|
counterDrawable.setSize(getMeasuredHeight(), getMeasuredWidth());
|
||||||
int count = currentCount;
|
|
||||||
currentCount = -1;
|
|
||||||
setCount(count, animationType == ANIMATION_TYPE_IN);
|
|
||||||
lastH = getMeasuredHeight();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCount(int count, boolean animated) {
|
|
||||||
if (count == currentCount) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (countAnimator != null) {
|
|
||||||
countAnimator.cancel();
|
|
||||||
}
|
|
||||||
if (count > 0) {
|
|
||||||
setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
if (Math.abs(count - currentCount) > 99) {
|
|
||||||
animated = false;
|
|
||||||
}
|
|
||||||
if (!animated) {
|
|
||||||
currentCount = count;
|
|
||||||
if (count == 0) {
|
|
||||||
setVisibility(View.GONE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String newStr = String.valueOf(count);
|
|
||||||
countWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(newStr)));
|
|
||||||
countLayout = new StaticLayout(newStr, textPaint, countWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
String newStr = String.valueOf(count);
|
|
||||||
|
|
||||||
if (animated) {
|
|
||||||
if (countAnimator != null) {
|
|
||||||
countAnimator.cancel();
|
|
||||||
}
|
|
||||||
countChangeProgress = 0f;
|
|
||||||
countAnimator = ValueAnimator.ofFloat(0, 1f);
|
|
||||||
countAnimator.addUpdateListener(valueAnimator -> {
|
|
||||||
countChangeProgress = (float) valueAnimator.getAnimatedValue();
|
|
||||||
invalidate();
|
|
||||||
});
|
|
||||||
countAnimator.addListener(new AnimatorListenerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
|
||||||
animationType = -1;
|
|
||||||
countChangeProgress = 1f;
|
|
||||||
countOldLayout = null;
|
|
||||||
countAnimationStableLayout = null;
|
|
||||||
countAnimationInLayout = null;
|
|
||||||
if (currentCount == 0) {
|
|
||||||
setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (currentCount <= 0) {
|
|
||||||
animationType = ANIMATION_TYPE_IN;
|
|
||||||
countAnimator.setDuration(220);
|
|
||||||
countAnimator.setInterpolator(new OvershootInterpolator());
|
|
||||||
} else if (count == 0) {
|
|
||||||
animationType = ANIMATION_TYPE_OUT;
|
|
||||||
countAnimator.setDuration(150);
|
|
||||||
countAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
|
||||||
} else {
|
|
||||||
animationType = ANIMATION_TYPE_REPLACE;
|
|
||||||
countAnimator.setDuration(430);
|
|
||||||
countAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
|
||||||
}
|
|
||||||
if (countLayout != null) {
|
|
||||||
String oldStr = String.valueOf(currentCount);
|
|
||||||
|
|
||||||
if (oldStr.length() == newStr.length()) {
|
|
||||||
SpannableStringBuilder oldSpannableStr = new SpannableStringBuilder(oldStr);
|
|
||||||
SpannableStringBuilder newSpannableStr = new SpannableStringBuilder(newStr);
|
|
||||||
SpannableStringBuilder stableStr = new SpannableStringBuilder(newStr);
|
|
||||||
for (int i = 0; i < oldStr.length(); i++) {
|
|
||||||
if (oldStr.charAt(i) == newStr.charAt(i)) {
|
|
||||||
oldSpannableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0);
|
|
||||||
newSpannableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0);
|
|
||||||
} else {
|
|
||||||
stableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int countOldWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(oldStr)));
|
|
||||||
countOldLayout = new StaticLayout(oldSpannableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
|
|
||||||
countAnimationStableLayout = new StaticLayout(stableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
|
|
||||||
countAnimationInLayout = new StaticLayout(newSpannableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
|
|
||||||
} else {
|
|
||||||
countOldLayout = countLayout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
countWidthOld = countWidth;
|
|
||||||
countAnimationIncrement = count > currentCount;
|
|
||||||
countAnimator.start();
|
|
||||||
}
|
|
||||||
if (count > 0) {
|
|
||||||
countWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(newStr)));
|
|
||||||
countLayout = new StaticLayout(newStr, textPaint, countWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentCount = count;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDraw(Canvas canvas) {
|
protected void onDraw(Canvas canvas) {
|
||||||
super.onDraw(canvas);
|
counterDrawable.draw(canvas);
|
||||||
int textColor = Theme.getColor(textColorKey);
|
|
||||||
int circleColor = Theme.getColor(circleColorKey);
|
|
||||||
if (this.textColor != textColor) {
|
|
||||||
this.textColor = textColor;
|
|
||||||
textPaint.setColor(textColor);
|
|
||||||
}
|
|
||||||
if (this.circleColor != circleColor) {
|
|
||||||
this.circleColor = circleColor;
|
|
||||||
circlePaint.setColor(circleColor);
|
|
||||||
}
|
|
||||||
if (countChangeProgress != 1f) {
|
|
||||||
if (animationType == ANIMATION_TYPE_IN || animationType == ANIMATION_TYPE_OUT) {
|
|
||||||
updateX(countWidth);
|
|
||||||
float cx = countLeft + countWidth / 2f;
|
|
||||||
float cy = getMeasuredHeight() / 2f;
|
|
||||||
canvas.save();
|
|
||||||
float progress = animationType == ANIMATION_TYPE_IN ? countChangeProgress : (1f - countChangeProgress);
|
|
||||||
canvas.scale(progress, progress, cx, cy);
|
|
||||||
drawInternal(canvas);
|
|
||||||
canvas.restore();
|
|
||||||
} else {
|
|
||||||
float progressHalf = countChangeProgress * 2;
|
|
||||||
if (progressHalf > 1f) {
|
|
||||||
progressHalf = 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
float countTop = (getMeasuredHeight() - AndroidUtilities.dp(23)) / 2f;
|
|
||||||
float countWidth;
|
|
||||||
if (this.countWidth == this.countWidthOld) {
|
|
||||||
countWidth = this.countWidth;
|
|
||||||
} else {
|
|
||||||
countWidth = this.countWidth * progressHalf + this.countWidthOld * (1f - progressHalf);
|
|
||||||
}
|
|
||||||
updateX(countWidth);
|
|
||||||
|
|
||||||
float scale = 1f;
|
|
||||||
if (countAnimationIncrement) {
|
|
||||||
if (countChangeProgress <= 0.5f) {
|
|
||||||
scale += 0.1f * CubicBezierInterpolator.EASE_OUT.getInterpolation(countChangeProgress * 2);
|
|
||||||
} else {
|
|
||||||
scale += 0.1f * CubicBezierInterpolator.EASE_IN.getInterpolation((1f - (countChangeProgress - 0.5f) * 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23));
|
|
||||||
canvas.save();
|
|
||||||
canvas.scale(scale, scale, rectF.centerX(), rectF.centerY());
|
|
||||||
canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, circlePaint);
|
|
||||||
canvas.clipRect(rectF);
|
|
||||||
|
|
||||||
boolean increment = reverseAnimation != countAnimationIncrement;
|
|
||||||
if (countAnimationInLayout != null) {
|
|
||||||
canvas.save();
|
|
||||||
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? AndroidUtilities.dp(13) : -AndroidUtilities.dp(13)) * (1f - progressHalf));
|
|
||||||
textPaint.setAlpha((int) (255 * progressHalf));
|
|
||||||
countAnimationInLayout.draw(canvas);
|
|
||||||
canvas.restore();
|
|
||||||
} else if (countLayout != null) {
|
|
||||||
canvas.save();
|
|
||||||
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? AndroidUtilities.dp(13) : -AndroidUtilities.dp(13)) * (1f - progressHalf));
|
|
||||||
textPaint.setAlpha((int) (255 * progressHalf));
|
|
||||||
countLayout.draw(canvas);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (countOldLayout != null) {
|
|
||||||
canvas.save();
|
|
||||||
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? -AndroidUtilities.dp(13) : AndroidUtilities.dp(13)) * (progressHalf));
|
|
||||||
textPaint.setAlpha((int) (255 * (1f - progressHalf)));
|
|
||||||
countOldLayout.draw(canvas);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (countAnimationStableLayout != null) {
|
|
||||||
canvas.save();
|
|
||||||
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4));
|
|
||||||
textPaint.setAlpha(255);
|
|
||||||
countAnimationStableLayout.draw(canvas);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
textPaint.setAlpha(255);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
drawInternal(canvas);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateX(float countWidth) {
|
|
||||||
if (gravity == Gravity.RIGHT) {
|
|
||||||
countLeft = getMeasuredWidth() - AndroidUtilities.dp(5.5f);
|
|
||||||
if (horizontalPadding != 0) {
|
|
||||||
countLeft -= Math.max(horizontalPadding + countWidth / 2f, countWidth);
|
|
||||||
} else {
|
|
||||||
countLeft -= countWidth;
|
|
||||||
}
|
|
||||||
} else if (gravity == Gravity.LEFT) {
|
|
||||||
countLeft = AndroidUtilities.dp(5.5f);
|
|
||||||
} else {
|
|
||||||
countLeft = (int) ((getMeasuredWidth() - countWidth) / 2f);
|
|
||||||
}
|
|
||||||
x = countLeft - AndroidUtilities.dp(5.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void drawInternal(Canvas canvas) {
|
|
||||||
float countTop = (getMeasuredHeight() - AndroidUtilities.dp(23)) / 2f;
|
|
||||||
updateX(countWidth);
|
|
||||||
rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23));
|
|
||||||
canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, circlePaint);
|
|
||||||
if (countLayout != null) {
|
|
||||||
canvas.save();
|
|
||||||
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4));
|
|
||||||
countLayout.draw(canvas);
|
|
||||||
canvas.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setColors(String textKey, String circleKey){
|
public void setColors(String textKey, String circleKey){
|
||||||
this.textColorKey = textKey;
|
counterDrawable.textColorKey = textKey;
|
||||||
this.circleColorKey = circleKey;
|
counterDrawable.circleColorKey = circleKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGravity(int gravity) {
|
public void setGravity(int gravity) {
|
||||||
this.gravity = gravity;
|
counterDrawable.gravity = gravity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReverse(boolean b) {
|
public void setReverse(boolean b) {
|
||||||
reverseAnimation = b;
|
counterDrawable.reverseAnimation = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCount(int count, boolean animated) {
|
||||||
|
counterDrawable.setCount(count, animated);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CounterDrawable {
|
||||||
|
|
||||||
|
private final static int ANIMATION_TYPE_IN = 0;
|
||||||
|
private final static int ANIMATION_TYPE_OUT = 1;
|
||||||
|
private final static int ANIMATION_TYPE_REPLACE = 2;
|
||||||
|
|
||||||
|
int animationType = -1;
|
||||||
|
|
||||||
|
public Paint circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
public TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
public RectF rectF = new RectF();
|
||||||
|
|
||||||
|
int currentCount;
|
||||||
|
private boolean countAnimationIncrement;
|
||||||
|
private ValueAnimator countAnimator;
|
||||||
|
private float countChangeProgress = 1f;
|
||||||
|
private StaticLayout countLayout;
|
||||||
|
private StaticLayout countOldLayout;
|
||||||
|
private StaticLayout countAnimationStableLayout;
|
||||||
|
private StaticLayout countAnimationInLayout;
|
||||||
|
|
||||||
|
private int countWidthOld;
|
||||||
|
private int countWidth;
|
||||||
|
|
||||||
|
private int circleColor;
|
||||||
|
private int textColor;
|
||||||
|
private String textColorKey = Theme.key_chat_goDownButtonCounter;
|
||||||
|
private String circleColorKey = Theme.key_chat_goDownButtonCounterBackground;
|
||||||
|
|
||||||
|
int lastH;
|
||||||
|
int width;
|
||||||
|
public int gravity = Gravity.CENTER;
|
||||||
|
float countLeft;
|
||||||
|
float x;
|
||||||
|
|
||||||
|
private boolean reverseAnimation;
|
||||||
|
public float horizontalPadding;
|
||||||
|
|
||||||
|
boolean updateVisibility;
|
||||||
|
|
||||||
|
private View parent;
|
||||||
|
|
||||||
|
public final static int TYPE_DEFAULT = 0;
|
||||||
|
public final static int TYPE_CHAT_PULLING_DOWN = 1;
|
||||||
|
|
||||||
|
int type = TYPE_DEFAULT;
|
||||||
|
|
||||||
|
public CounterDrawable(View parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
circlePaint.setColor(Color.BLACK);
|
||||||
|
textPaint.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||||
|
textPaint.setTextSize(AndroidUtilities.dp(13));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSize(int h, int w) {
|
||||||
|
if (h != lastH) {
|
||||||
|
int count = currentCount;
|
||||||
|
currentCount = -1;
|
||||||
|
setCount(count, animationType == ANIMATION_TYPE_IN);
|
||||||
|
lastH = h;
|
||||||
|
}
|
||||||
|
width = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void drawInternal(Canvas canvas) {
|
||||||
|
float countTop = (lastH - AndroidUtilities.dp(23)) / 2f;
|
||||||
|
updateX(countWidth);
|
||||||
|
rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23));
|
||||||
|
canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, circlePaint);
|
||||||
|
if (Theme.hasGradientService()) {
|
||||||
|
canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, Theme.chat_actionBackgroundGradientDarkenPaint);
|
||||||
|
}
|
||||||
|
if (countLayout != null) {
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4));
|
||||||
|
countLayout.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCount(int count, boolean animated) {
|
||||||
|
if (count == currentCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (countAnimator != null) {
|
||||||
|
countAnimator.cancel();
|
||||||
|
}
|
||||||
|
if (count > 0 && updateVisibility && parent != null) {
|
||||||
|
parent.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
if (Math.abs(count - currentCount) > 99) {
|
||||||
|
animated = false;
|
||||||
|
}
|
||||||
|
if (!animated) {
|
||||||
|
currentCount = count;
|
||||||
|
if (count == 0) {
|
||||||
|
if (updateVisibility && parent != null) {
|
||||||
|
parent.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String newStr = String.valueOf(count);
|
||||||
|
countWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(newStr)));
|
||||||
|
countLayout = new StaticLayout(newStr, textPaint, countWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
|
||||||
|
if (parent != null) {
|
||||||
|
parent.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String newStr = String.valueOf(count);
|
||||||
|
|
||||||
|
if (animated) {
|
||||||
|
if (countAnimator != null) {
|
||||||
|
countAnimator.cancel();
|
||||||
|
}
|
||||||
|
countChangeProgress = 0f;
|
||||||
|
countAnimator = ValueAnimator.ofFloat(0, 1f);
|
||||||
|
countAnimator.addUpdateListener(valueAnimator -> {
|
||||||
|
countChangeProgress = (float) valueAnimator.getAnimatedValue();
|
||||||
|
if (parent != null) {
|
||||||
|
parent.invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
countAnimator.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
animationType = -1;
|
||||||
|
countChangeProgress = 1f;
|
||||||
|
countOldLayout = null;
|
||||||
|
countAnimationStableLayout = null;
|
||||||
|
countAnimationInLayout = null;
|
||||||
|
if (parent != null) {
|
||||||
|
if (currentCount == 0 && updateVisibility) {
|
||||||
|
parent.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
parent.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (currentCount <= 0) {
|
||||||
|
animationType = ANIMATION_TYPE_IN;
|
||||||
|
countAnimator.setDuration(220);
|
||||||
|
countAnimator.setInterpolator(new OvershootInterpolator());
|
||||||
|
} else if (count == 0) {
|
||||||
|
animationType = ANIMATION_TYPE_OUT;
|
||||||
|
countAnimator.setDuration(150);
|
||||||
|
countAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
} else {
|
||||||
|
animationType = ANIMATION_TYPE_REPLACE;
|
||||||
|
countAnimator.setDuration(430);
|
||||||
|
countAnimator.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
|
}
|
||||||
|
if (countLayout != null) {
|
||||||
|
String oldStr = String.valueOf(currentCount);
|
||||||
|
|
||||||
|
if (oldStr.length() == newStr.length()) {
|
||||||
|
SpannableStringBuilder oldSpannableStr = new SpannableStringBuilder(oldStr);
|
||||||
|
SpannableStringBuilder newSpannableStr = new SpannableStringBuilder(newStr);
|
||||||
|
SpannableStringBuilder stableStr = new SpannableStringBuilder(newStr);
|
||||||
|
for (int i = 0; i < oldStr.length(); i++) {
|
||||||
|
if (oldStr.charAt(i) == newStr.charAt(i)) {
|
||||||
|
oldSpannableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0);
|
||||||
|
newSpannableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0);
|
||||||
|
} else {
|
||||||
|
stableStr.setSpan(new EmptyStubSpan(), i, i + 1, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int countOldWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(oldStr)));
|
||||||
|
countOldLayout = new StaticLayout(oldSpannableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
|
||||||
|
countAnimationStableLayout = new StaticLayout(stableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
|
||||||
|
countAnimationInLayout = new StaticLayout(newSpannableStr, textPaint, countOldWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
|
||||||
|
} else {
|
||||||
|
countOldLayout = countLayout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
countWidthOld = countWidth;
|
||||||
|
countAnimationIncrement = count > currentCount;
|
||||||
|
countAnimator.start();
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
countWidth = Math.max(AndroidUtilities.dp(12), (int) Math.ceil(textPaint.measureText(newStr)));
|
||||||
|
countLayout = new StaticLayout(newStr, textPaint, countWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentCount = count;
|
||||||
|
if (parent != null) {
|
||||||
|
parent.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void draw(Canvas canvas) {
|
||||||
|
if (type != TYPE_CHAT_PULLING_DOWN) {
|
||||||
|
int textColor = Theme.getColor(textColorKey);
|
||||||
|
int circleColor = Theme.getColor(circleColorKey);
|
||||||
|
if (this.textColor != textColor) {
|
||||||
|
this.textColor = textColor;
|
||||||
|
textPaint.setColor(textColor);
|
||||||
|
}
|
||||||
|
if (this.circleColor != circleColor) {
|
||||||
|
this.circleColor = circleColor;
|
||||||
|
circlePaint.setColor(circleColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (countChangeProgress != 1f) {
|
||||||
|
if (animationType == ANIMATION_TYPE_IN || animationType == ANIMATION_TYPE_OUT) {
|
||||||
|
updateX(countWidth);
|
||||||
|
float cx = countLeft + countWidth / 2f;
|
||||||
|
float cy = lastH / 2f;
|
||||||
|
canvas.save();
|
||||||
|
float progress = animationType == ANIMATION_TYPE_IN ? countChangeProgress : (1f - countChangeProgress);
|
||||||
|
canvas.scale(progress, progress, cx, cy);
|
||||||
|
drawInternal(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
} else {
|
||||||
|
float progressHalf = countChangeProgress * 2;
|
||||||
|
if (progressHalf > 1f) {
|
||||||
|
progressHalf = 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float countTop = (lastH - AndroidUtilities.dp(23)) / 2f;
|
||||||
|
float countWidth;
|
||||||
|
if (this.countWidth == this.countWidthOld) {
|
||||||
|
countWidth = this.countWidth;
|
||||||
|
} else {
|
||||||
|
countWidth = this.countWidth * progressHalf + this.countWidthOld * (1f - progressHalf);
|
||||||
|
}
|
||||||
|
updateX(countWidth);
|
||||||
|
|
||||||
|
float scale = 1f;
|
||||||
|
if (countAnimationIncrement) {
|
||||||
|
if (countChangeProgress <= 0.5f) {
|
||||||
|
scale += 0.1f * CubicBezierInterpolator.EASE_OUT.getInterpolation(countChangeProgress * 2);
|
||||||
|
} else {
|
||||||
|
scale += 0.1f * CubicBezierInterpolator.EASE_IN.getInterpolation((1f - (countChangeProgress - 0.5f) * 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23));
|
||||||
|
canvas.save();
|
||||||
|
canvas.scale(scale, scale, rectF.centerX(), rectF.centerY());
|
||||||
|
canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, circlePaint);
|
||||||
|
if (Theme.hasGradientService()) {
|
||||||
|
canvas.drawRoundRect(rectF, 11.5f * AndroidUtilities.density, 11.5f * AndroidUtilities.density, Theme.chat_actionBackgroundGradientDarkenPaint);
|
||||||
|
}
|
||||||
|
canvas.clipRect(rectF);
|
||||||
|
|
||||||
|
boolean increment = reverseAnimation != countAnimationIncrement;
|
||||||
|
if (countAnimationInLayout != null) {
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? AndroidUtilities.dp(13) : -AndroidUtilities.dp(13)) * (1f - progressHalf));
|
||||||
|
textPaint.setAlpha((int) (255 * progressHalf));
|
||||||
|
countAnimationInLayout.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
} else if (countLayout != null) {
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? AndroidUtilities.dp(13) : -AndroidUtilities.dp(13)) * (1f - progressHalf));
|
||||||
|
textPaint.setAlpha((int) (255 * progressHalf));
|
||||||
|
countLayout.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countOldLayout != null) {
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4) + (increment ? -AndroidUtilities.dp(13) : AndroidUtilities.dp(13)) * (progressHalf));
|
||||||
|
textPaint.setAlpha((int) (255 * (1f - progressHalf)));
|
||||||
|
countOldLayout.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (countAnimationStableLayout != null) {
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(countLeft, countTop + AndroidUtilities.dp(4));
|
||||||
|
textPaint.setAlpha(255);
|
||||||
|
countAnimationStableLayout.draw(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
textPaint.setAlpha(255);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
drawInternal(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateBackgroundRect() {
|
||||||
|
if (countChangeProgress != 1f) {
|
||||||
|
if (animationType == ANIMATION_TYPE_IN || animationType == ANIMATION_TYPE_OUT) {
|
||||||
|
updateX(countWidth);
|
||||||
|
float countTop = (lastH - AndroidUtilities.dp(23)) / 2f;
|
||||||
|
rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23));
|
||||||
|
} else {
|
||||||
|
float progressHalf = countChangeProgress * 2;
|
||||||
|
if (progressHalf > 1f) {
|
||||||
|
progressHalf = 1f;
|
||||||
|
}
|
||||||
|
float countTop = (lastH - AndroidUtilities.dp(23)) / 2f;
|
||||||
|
float countWidth;
|
||||||
|
if (this.countWidth == this.countWidthOld) {
|
||||||
|
countWidth = this.countWidth;
|
||||||
|
} else {
|
||||||
|
countWidth = this.countWidth * progressHalf + this.countWidthOld * (1f - progressHalf);
|
||||||
|
}
|
||||||
|
updateX(countWidth);
|
||||||
|
rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
updateX(countWidth);
|
||||||
|
float countTop = (lastH - AndroidUtilities.dp(23)) / 2f;
|
||||||
|
rectF.set(x, countTop, x + countWidth + AndroidUtilities.dp(11), countTop + AndroidUtilities.dp(23));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateX(float countWidth) {
|
||||||
|
if (gravity == Gravity.RIGHT) {
|
||||||
|
countLeft = width - AndroidUtilities.dp(5.5f);
|
||||||
|
if (horizontalPadding != 0) {
|
||||||
|
countLeft -= Math.max(horizontalPadding + countWidth / 2f, countWidth);
|
||||||
|
} else {
|
||||||
|
countLeft -= countWidth;
|
||||||
|
}
|
||||||
|
} else if (gravity == Gravity.LEFT) {
|
||||||
|
countLeft = AndroidUtilities.dp(5.5f);
|
||||||
|
} else {
|
||||||
|
countLeft = (int) ((width - countWidth) / 2f);
|
||||||
|
}
|
||||||
|
x = countLeft - AndroidUtilities.dp(5.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getCenterX() {
|
||||||
|
updateX(countWidth);
|
||||||
|
return countLeft + countWidth / 2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(int type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(View parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -572,9 +572,9 @@ public class EditTextBoldCursor extends EditText {
|
||||||
}
|
}
|
||||||
if (supportRtlHint && LocaleController.isRTL) {
|
if (supportRtlHint && LocaleController.isRTL) {
|
||||||
float offset = getMeasuredWidth() - hintWidth;
|
float offset = getMeasuredWidth() - hintWidth;
|
||||||
canvas.translate(left + getScrollX() + offset, lineY - hintLayout.getHeight() - AndroidUtilities.dp(6));
|
canvas.translate(left + getScrollX() + offset, lineY - hintLayout.getHeight() - AndroidUtilities.dp(7));
|
||||||
} else {
|
} else {
|
||||||
canvas.translate(left + getScrollX(), lineY - hintLayout.getHeight() - AndroidUtilities.dp(6));
|
canvas.translate(left + getScrollX(), lineY - hintLayout.getHeight() - AndroidUtilities.dp2(7));
|
||||||
}
|
}
|
||||||
if (transformHintToHeader) {
|
if (transformHintToHeader) {
|
||||||
float scale = 1.0f - 0.3f * headerAnimationProgress;
|
float scale = 1.0f - 0.3f * headerAnimationProgress;
|
||||||
|
|
|
@ -470,7 +470,7 @@ public class EditTextEmoji extends FrameLayout implements NotificationCenter.Not
|
||||||
if (emojiView != null) {
|
if (emojiView != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
emojiView = new EmojiView(false, false, getContext(), false, null);
|
emojiView = new EmojiView(false, false, getContext(), false, null, null);
|
||||||
emojiView.setVisibility(GONE);
|
emojiView.setVisibility(GONE);
|
||||||
if (AndroidUtilities.isTablet()) {
|
if (AndroidUtilities.isTablet()) {
|
||||||
emojiView.setForseMultiwindowLayout(true);
|
emojiView.setForseMultiwindowLayout(true);
|
||||||
|
|
|
@ -15,6 +15,7 @@ import android.animation.ObjectAnimator;
|
||||||
import android.animation.StateListAnimator;
|
import android.animation.StateListAnimator;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.database.DataSetObserver;
|
import android.database.DataSetObserver;
|
||||||
|
@ -24,6 +25,7 @@ import android.graphics.Outline;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.PorterDuffColorFilter;
|
import android.graphics.PorterDuffColorFilter;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.LayerDrawable;
|
import android.graphics.drawable.LayerDrawable;
|
||||||
|
@ -37,10 +39,12 @@ import androidx.core.view.ViewCompat;
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
import androidx.recyclerview.widget.LinearSmoothScroller;
|
import androidx.recyclerview.widget.LinearSmoothScroller;
|
||||||
|
import androidx.recyclerview.widget.LinearSmoothScrollerCustom;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator;
|
import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||||
import androidx.viewpager.widget.PagerAdapter;
|
import androidx.viewpager.widget.PagerAdapter;
|
||||||
import androidx.viewpager.widget.ViewPager;
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -67,6 +71,8 @@ import android.widget.LinearLayout;
|
||||||
import android.widget.PopupWindow;
|
import android.widget.PopupWindow;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.util.Log;
|
||||||
|
|
||||||
import org.telegram.messenger.AndroidUtilities;
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
import org.telegram.messenger.BuildVars;
|
import org.telegram.messenger.BuildVars;
|
||||||
import org.telegram.messenger.ChatObject;
|
import org.telegram.messenger.ChatObject;
|
||||||
|
@ -139,9 +145,11 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
private EmojiSearchAdapter emojiSearchAdapter;
|
private EmojiSearchAdapter emojiSearchAdapter;
|
||||||
private SearchField emojiSearchField;
|
private SearchField emojiSearchField;
|
||||||
private AnimatorSet emojiTabShadowAnimator;
|
private AnimatorSet emojiTabShadowAnimator;
|
||||||
|
private RecyclerAnimationScrollHelper scrollHelper;
|
||||||
private boolean firstEmojiAttach = true;
|
private boolean firstEmojiAttach = true;
|
||||||
private boolean needEmojiSearch;
|
private boolean needEmojiSearch;
|
||||||
private int hasRecentEmoji = -1;
|
private int hasRecentEmoji = -1;
|
||||||
|
private boolean hasChatStickers;
|
||||||
|
|
||||||
private FrameLayout gifContainer;
|
private FrameLayout gifContainer;
|
||||||
private RecyclerListView gifGridView;
|
private RecyclerListView gifGridView;
|
||||||
|
@ -163,19 +171,34 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
private StickersSearchGridAdapter stickersSearchGridAdapter;
|
private StickersSearchGridAdapter stickersSearchGridAdapter;
|
||||||
private RecyclerListView.OnItemClickListener stickersOnItemClickListener;
|
private RecyclerListView.OnItemClickListener stickersOnItemClickListener;
|
||||||
private ScrollSlidingTabStrip stickersTab;
|
private ScrollSlidingTabStrip stickersTab;
|
||||||
|
private FrameLayout stickersTabContainer;
|
||||||
private RecyclerListView stickersGridView;
|
private RecyclerListView stickersGridView;
|
||||||
private GridLayoutManager stickersLayoutManager;
|
private GridLayoutManager stickersLayoutManager;
|
||||||
|
private TrendingAdapter trendingAdapter;
|
||||||
private SearchField stickersSearchField;
|
private SearchField stickersSearchField;
|
||||||
private int stickersMinusDy;
|
private int stickersMinusDy;
|
||||||
private boolean firstStickersAttach = true;
|
private boolean firstStickersAttach = true;
|
||||||
|
private boolean ignoreStickersScroll;
|
||||||
|
private boolean stickersContainerAttached;
|
||||||
|
|
||||||
private AnimatorSet searchAnimation;
|
private AnimatorSet searchAnimation;
|
||||||
|
|
||||||
private TextView mediaBanTooltip;
|
private TextView mediaBanTooltip;
|
||||||
private DragListener dragListener;
|
private DragListener dragListener;
|
||||||
|
private boolean showing;
|
||||||
|
|
||||||
private final int[] tabsMinusDy = new int[3];
|
private final int[] tabsMinusDy = new int[3];
|
||||||
private ObjectAnimator[] tabsYAnimators = new ObjectAnimator[3];
|
private ObjectAnimator[] tabsYAnimators = new ObjectAnimator[3];
|
||||||
|
private boolean firstTabUpdate;
|
||||||
|
|
||||||
|
public void setShowing(boolean showing) {
|
||||||
|
this.showing = showing;
|
||||||
|
updateStickerTabsPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMessageSend() {
|
||||||
|
chooseStickerActionTracker.reset();
|
||||||
|
}
|
||||||
|
|
||||||
@IntDef({Type.STICKERS, Type.EMOJIS, Type.GIFS})
|
@IntDef({Type.STICKERS, Type.EMOJIS, Type.GIFS})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@ -205,6 +228,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
private ArrayList<TLRPC.Document> recentGifs = new ArrayList<>();
|
private ArrayList<TLRPC.Document> recentGifs = new ArrayList<>();
|
||||||
private ArrayList<TLRPC.Document> recentStickers = new ArrayList<>();
|
private ArrayList<TLRPC.Document> recentStickers = new ArrayList<>();
|
||||||
private ArrayList<TLRPC.Document> favouriteStickers = new ArrayList<>();
|
private ArrayList<TLRPC.Document> favouriteStickers = new ArrayList<>();
|
||||||
|
private ArrayList<TLRPC.StickerSetCovered> featuredStickerSets = new ArrayList<>();
|
||||||
|
|
||||||
private Paint dotPaint;
|
private Paint dotPaint;
|
||||||
|
|
||||||
|
@ -249,6 +273,18 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
private float emojiLastY;
|
private float emojiLastY;
|
||||||
private float emojiTouchedX;
|
private float emojiTouchedX;
|
||||||
private float emojiTouchedY;
|
private float emojiTouchedY;
|
||||||
|
private float lastStickersX;
|
||||||
|
private boolean expandStickersByDragg;
|
||||||
|
|
||||||
|
private Runnable checkExpandStickerTabsRunnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!stickersTab.isDragging()) {
|
||||||
|
expandStickersByDragg = false;
|
||||||
|
updateStickerTabsPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public interface EmojiViewDelegate {
|
public interface EmojiViewDelegate {
|
||||||
default boolean onBackspace() {
|
default boolean onBackspace() {
|
||||||
|
@ -319,15 +355,30 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default int getThreadId() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
default void showTrendingStickersAlert(TrendingStickersLayout layout) {
|
default void showTrendingStickersAlert(TrendingStickersLayout layout) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void invalidateEnterView() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default float getProgressToSearchOpened() {
|
||||||
|
return 0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface DragListener {
|
public interface DragListener {
|
||||||
void onDragStart();
|
void onDragStart();
|
||||||
|
|
||||||
void onDragEnd(float velocity);
|
void onDragEnd(float velocity);
|
||||||
|
|
||||||
void onDragCancel();
|
void onDragCancel();
|
||||||
|
|
||||||
void onDrag(int offset);
|
void onDrag(int offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,6 +440,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final Field superListenerField;
|
private static final Field superListenerField;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Field f = null;
|
Field f = null;
|
||||||
try {
|
try {
|
||||||
|
@ -583,9 +635,15 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
if (!smoothScrolling) {
|
if (!smoothScrolling) {
|
||||||
animateTabsY(type);
|
animateTabsY(type);
|
||||||
}
|
}
|
||||||
|
if (ignoreStickersScroll) {
|
||||||
|
ignoreStickersScroll = false;
|
||||||
|
}
|
||||||
smoothScrolling = false;
|
smoothScrolling = false;
|
||||||
} else {
|
} else {
|
||||||
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
|
if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
|
||||||
|
if (ignoreStickersScroll) {
|
||||||
|
ignoreStickersScroll = false;
|
||||||
|
}
|
||||||
final SearchField searchField = getSearchFieldForType(type);
|
final SearchField searchField = getSearchFieldForType(type);
|
||||||
if (searchField != null) {
|
if (searchField != null) {
|
||||||
searchField.hideKeyboard();
|
searchField.hideKeyboard();
|
||||||
|
@ -595,6 +653,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
if (!smoothScrolling) {
|
if (!smoothScrolling) {
|
||||||
stopAnimatingTabsY(type);
|
stopAnimatingTabsY(type);
|
||||||
}
|
}
|
||||||
|
if (type == Type.STICKERS) {
|
||||||
|
chooseStickerActionTracker.doSomeAction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,6 +703,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||||
|
if (isDragging()) {
|
||||||
|
return super.onInterceptTouchEvent(ev);
|
||||||
|
}
|
||||||
if (getParent() != null) {
|
if (getParent() != null) {
|
||||||
getParent().requestDisallowInterceptTouchEvent(true);
|
getParent().requestDisallowInterceptTouchEvent(true);
|
||||||
}
|
}
|
||||||
|
@ -668,18 +732,27 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onTouchEvent(MotionEvent ev) {
|
public boolean onTouchEvent(MotionEvent ev) {
|
||||||
|
if (isDragging()) {
|
||||||
|
return super.onTouchEvent(ev);
|
||||||
|
}
|
||||||
if (first) {
|
if (first) {
|
||||||
first = false;
|
first = false;
|
||||||
lastX = ev.getX();
|
lastX = ev.getX();
|
||||||
}
|
}
|
||||||
|
if (ev.getAction() == MotionEvent.ACTION_DOWN || ev.getAction() == MotionEvent.ACTION_MOVE) {
|
||||||
|
lastStickersX = ev.getRawX();
|
||||||
|
}
|
||||||
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
|
||||||
draggingVertically = draggingHorizontally = false;
|
draggingVertically = draggingHorizontally = false;
|
||||||
downX = ev.getRawX();
|
downX = ev.getRawX();
|
||||||
downY = ev.getRawY();
|
downY = ev.getRawY();
|
||||||
} else {
|
} else {
|
||||||
if (!draggingVertically && !draggingHorizontally && dragListener != null) {
|
if (!draggingVertically && !draggingHorizontally && dragListener != null) {
|
||||||
if (Math.abs(ev.getRawX() - downX) >= touchSlop) {
|
if (Math.abs(ev.getRawX() - downX) >= touchSlop && canScrollHorizontally((int) (downX - ev.getRawX()))) {
|
||||||
draggingHorizontally = true;
|
draggingHorizontally = true;
|
||||||
|
AndroidUtilities.cancelRunOnUIThread(checkExpandStickerTabsRunnable);
|
||||||
|
expandStickersByDragg = true;
|
||||||
|
updateStickerTabsPosition();
|
||||||
} else if (Math.abs(ev.getRawY() - downY) >= touchSlop) {
|
} else if (Math.abs(ev.getRawY() - downY) >= touchSlop) {
|
||||||
draggingVertically = true;
|
draggingVertically = true;
|
||||||
downY = ev.getRawY();
|
downY = ev.getRawY();
|
||||||
|
@ -691,6 +764,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (expandStickersByDragg && (ev.getAction() == MotionEvent.ACTION_UP || ev.getAction() == MotionEvent.ACTION_CANCEL)) {
|
||||||
|
AndroidUtilities.runOnUIThread(checkExpandStickerTabsRunnable, 1500);
|
||||||
|
}
|
||||||
if (draggingVertically) {
|
if (draggingVertically) {
|
||||||
if (vTracker == null) {
|
if (vTracker == null) {
|
||||||
vTracker = VelocityTracker.obtain();
|
vTracker = VelocityTracker.obtain();
|
||||||
|
@ -711,6 +787,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
} else {
|
} else {
|
||||||
dragListener.onDrag(Math.round(ev.getRawY() - downY));
|
dragListener.onDrag(Math.round(ev.getRawY() - downY));
|
||||||
}
|
}
|
||||||
|
cancelLongPress();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
float newTranslationX = getTranslationX();
|
float newTranslationX = getTranslationX();
|
||||||
|
@ -999,7 +1076,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmojiView(boolean needStickers, boolean needGif, final Context context, boolean needSearch, final TLRPC.ChatFull chatFull) {
|
public EmojiView(boolean needStickers, boolean needGif, final Context context, boolean needSearch, final TLRPC.ChatFull chatFull, ViewGroup parentView) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
int color = Theme.getColor(Theme.key_chat_emojiBottomPanelIcon);
|
int color = Theme.getColor(Theme.key_chat_emojiBottomPanelIcon);
|
||||||
|
@ -1027,12 +1104,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
};
|
};
|
||||||
|
|
||||||
stickerIcons = new Drawable[]{
|
stickerIcons = new Drawable[]{
|
||||||
Theme.createEmojiIconSelectorDrawable(context, R.drawable.stickers_recent, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)),
|
Theme.createEmojiIconSelectorDrawable(context, R.drawable.emoji_tabs_recent, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)),
|
||||||
Theme.createEmojiIconSelectorDrawable(context, R.drawable.stickers_favorites, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)),
|
Theme.createEmojiIconSelectorDrawable(context, R.drawable.emoji_tabs_faves, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)),
|
||||||
Theme.createEmojiIconSelectorDrawable(context, R.drawable.stickers_trending3, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)),
|
Theme.createEmojiIconSelectorDrawable(context, R.drawable.emoji_tabs_new3, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)),
|
||||||
new LayerDrawable(new Drawable[]{
|
new LayerDrawable(new Drawable[]{
|
||||||
Theme.createEmojiIconSelectorDrawable(context, R.drawable.stickers_trending1, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)),
|
Theme.createEmojiIconSelectorDrawable(context, R.drawable.emoji_tabs_new1, Theme.getColor(Theme.key_chat_emojiBottomPanelIcon), Theme.getColor(Theme.key_chat_emojiPanelIconSelected)),
|
||||||
Theme.createEmojiIconSelectorDrawable(context, R.drawable.stickers_trending2, Theme.getColor(Theme.key_chat_emojiPanelStickerPackSelectorLine), Theme.getColor(Theme.key_chat_emojiPanelStickerPackSelectorLine))
|
Theme.createEmojiIconSelectorDrawable(context, R.drawable.emoji_tabs_new2, Theme.getColor(Theme.key_chat_emojiPanelStickerPackSelectorLine), Theme.getColor(Theme.key_chat_emojiPanelStickerPackSelectorLine))
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1551,7 +1628,24 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
gifAdapter.loadTrendingGifs();
|
gifAdapter.loadTrendingGifs();
|
||||||
}
|
}
|
||||||
|
|
||||||
stickersContainer = new FrameLayout(context);
|
stickersContainer = new FrameLayout(context) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
stickersContainerAttached = true;
|
||||||
|
updateStickerTabsPosition();
|
||||||
|
chooseStickerActionTracker.checkVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDetachedFromWindow() {
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
stickersContainerAttached = false;
|
||||||
|
updateStickerTabsPosition();
|
||||||
|
chooseStickerActionTracker.checkVisibility();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_IMAGE);
|
MediaDataController.getInstance(currentAccount).checkStickers(MediaDataController.TYPE_IMAGE);
|
||||||
MediaDataController.getInstance(currentAccount).checkFeaturedStickers();
|
MediaDataController.getInstance(currentAccount).checkFeaturedStickers();
|
||||||
|
@ -1591,7 +1685,29 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
stickersGridView.setLayoutManager(stickersLayoutManager = new GridLayoutManager(context, 5));
|
stickersGridView.setLayoutManager(stickersLayoutManager = new GridLayoutManager(context, 5) {
|
||||||
|
@Override
|
||||||
|
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
|
||||||
|
try {
|
||||||
|
LinearSmoothScrollerCustom linearSmoothScroller = new LinearSmoothScrollerCustom(recyclerView.getContext(), LinearSmoothScrollerCustom.POSITION_TOP);
|
||||||
|
linearSmoothScroller.setTargetPosition(position);
|
||||||
|
startSmoothScroll(linearSmoothScroller);
|
||||||
|
} catch (Exception e) {
|
||||||
|
FileLog.e(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||||
|
int i = super.scrollVerticallyBy(dy, recycler, state);
|
||||||
|
if (i != 0 && stickersGridView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) {
|
||||||
|
expandStickersByDragg = false;
|
||||||
|
updateStickerTabsPosition();
|
||||||
|
}
|
||||||
|
chooseStickerActionTracker.doSomeAction();
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
});
|
||||||
stickersLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
stickersLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
|
||||||
@Override
|
@Override
|
||||||
public int getSpanSize(int position) {
|
public int getSpanSize(int position) {
|
||||||
|
@ -1647,31 +1763,122 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
stickersGridView.setOnItemClickListener(stickersOnItemClickListener);
|
stickersGridView.setOnItemClickListener(stickersOnItemClickListener);
|
||||||
stickersGridView.setGlowColor(Theme.getColor(Theme.key_chat_emojiPanelBackground));
|
stickersGridView.setGlowColor(Theme.getColor(Theme.key_chat_emojiPanelBackground));
|
||||||
stickersContainer.addView(stickersGridView);
|
stickersContainer.addView(stickersGridView);
|
||||||
|
scrollHelper = new RecyclerAnimationScrollHelper(stickersGridView, stickersLayoutManager);
|
||||||
|
|
||||||
stickersSearchField = new SearchField(context, 0);
|
stickersSearchField = new SearchField(context, 0);
|
||||||
stickersContainer.addView(stickersSearchField, new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, searchFieldHeight + AndroidUtilities.getShadowHeight()));
|
stickersContainer.addView(stickersSearchField, new FrameLayout.LayoutParams(LayoutHelper.MATCH_PARENT, searchFieldHeight + AndroidUtilities.getShadowHeight()));
|
||||||
|
|
||||||
stickersTab = new DraggableScrollSlidingTabStrip(context);
|
stickersTab = new DraggableScrollSlidingTabStrip(context) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void updatePosition() {
|
||||||
|
updateStickerTabsPosition();
|
||||||
|
stickersTabContainer.invalidate();
|
||||||
|
invalidate();
|
||||||
|
if (delegate != null) {
|
||||||
|
delegate.invalidateEnterView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void stickerSetPositionChanged(int fromPosition, int toPosition) {
|
||||||
|
int index1 = fromPosition - stickersTabOffset;
|
||||||
|
int index2 = toPosition - stickersTabOffset;
|
||||||
|
|
||||||
|
final MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount);
|
||||||
|
|
||||||
|
swapListElements(stickerSets, index1, index2);
|
||||||
|
if (hasChatStickers) {
|
||||||
|
swapListElements(mediaDataController.getStickerSets(MediaDataController.TYPE_IMAGE), index1 - 1, index2 - 1);
|
||||||
|
} else {
|
||||||
|
swapListElements(mediaDataController.getStickerSets(MediaDataController.TYPE_IMAGE), index1, index2);
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadStickersAdapter();
|
||||||
|
AndroidUtilities.cancelRunOnUIThread(checkExpandStickerTabsRunnable);
|
||||||
|
AndroidUtilities.runOnUIThread(checkExpandStickerTabsRunnable, 1500);
|
||||||
|
sendReorder();
|
||||||
|
updateStickerTabs();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void swapListElements(List<TLRPC.TL_messages_stickerSet> list, int index1, int index2) {
|
||||||
|
final TLRPC.TL_messages_stickerSet set1 = list.remove(index1);
|
||||||
|
list.add(index2, set1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendReorder() {
|
||||||
|
MediaDataController.getInstance(currentAccount).calcNewHash(MediaDataController.TYPE_IMAGE);
|
||||||
|
TLRPC.TL_messages_reorderStickerSets req = new TLRPC.TL_messages_reorderStickerSets();
|
||||||
|
req.masks = false;
|
||||||
|
for (int a = hasChatStickers ? 1 : 0; a < stickerSets.size(); a++) {
|
||||||
|
req.order.add(stickerSets.get(a).set.id);
|
||||||
|
}
|
||||||
|
ConnectionsManager.getInstance(currentAccount).sendRequest(req, (response, error) -> { });
|
||||||
|
NotificationCenter.getInstance(currentAccount).postNotificationName(NotificationCenter.stickersDidLoad, MediaDataController.TYPE_IMAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void invalidateOverlays() {
|
||||||
|
stickersTabContainer.invalidate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
stickersTab.setDragEnabled(true);
|
||||||
|
stickersTab.setWillNotDraw(false);
|
||||||
stickersTab.setType(ScrollSlidingTabStrip.Type.TAB);
|
stickersTab.setType(ScrollSlidingTabStrip.Type.TAB);
|
||||||
stickersTab.setUnderlineHeight(AndroidUtilities.getShadowHeight());
|
stickersTab.setUnderlineHeight(AndroidUtilities.getShadowHeight());
|
||||||
stickersTab.setIndicatorColor(Theme.getColor(Theme.key_chat_emojiPanelStickerPackSelectorLine));
|
stickersTab.setIndicatorColor(Theme.getColor(Theme.key_chat_emojiPanelStickerPackSelectorLine));
|
||||||
stickersTab.setUnderlineColor(Theme.getColor(Theme.key_chat_emojiPanelShadowLine));
|
stickersTab.setUnderlineColor(Theme.getColor(Theme.key_chat_emojiPanelShadowLine));
|
||||||
stickersTab.setBackgroundColor(Theme.getColor(Theme.key_chat_emojiPanelBackground));
|
|
||||||
stickersContainer.addView(stickersTab, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP));
|
if (parentView != null) {
|
||||||
|
stickersTabContainer = new FrameLayout(context) {
|
||||||
|
|
||||||
|
Paint paint = new Paint();
|
||||||
|
@Override
|
||||||
|
protected void dispatchDraw(Canvas canvas) {
|
||||||
|
float searchProgress = delegate.getProgressToSearchOpened();
|
||||||
|
float searchProgressOffset = AndroidUtilities.dp(50) * delegate.getProgressToSearchOpened();
|
||||||
|
if (searchProgressOffset > getMeasuredHeight()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
canvas.save();
|
||||||
|
if (searchProgressOffset != 0) {
|
||||||
|
canvas.clipRect(0, searchProgressOffset, getMeasuredWidth(), getMeasuredHeight());
|
||||||
|
}
|
||||||
|
paint.setColor(Theme.getColor(Theme.key_chat_emojiPanelBackground));
|
||||||
|
canvas.drawRect(0, 0, getMeasuredWidth(), AndroidUtilities.dp(48) + stickersTab.getExpandedOffset(), paint);
|
||||||
|
super.dispatchDraw(canvas);
|
||||||
|
stickersTab.drawOverlays(canvas);
|
||||||
|
canvas.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||||
|
super.onLayout(changed, left, top, right, bottom);
|
||||||
|
updateStickerTabsPosition();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
stickersTabContainer.addView(stickersTab, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP));
|
||||||
|
parentView.addView(stickersTabContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
|
||||||
|
} else {
|
||||||
|
stickersContainer.addView(stickersTab, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.LEFT | Gravity.TOP));
|
||||||
|
}
|
||||||
updateStickerTabs();
|
updateStickerTabs();
|
||||||
stickersTab.setDelegate(page -> {
|
stickersTab.setDelegate(page -> {
|
||||||
|
if (firstTabUpdate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (page == trendingTabNum) {
|
if (page == trendingTabNum) {
|
||||||
openTrendingStickers(null);
|
openTrendingStickers(null);
|
||||||
return;
|
return;
|
||||||
} else if (page == recentTabBum) {
|
} else if (page == recentTabBum) {
|
||||||
stickersGridView.stopScroll();
|
stickersGridView.stopScroll();
|
||||||
stickersLayoutManager.scrollToPositionWithOffset(stickersGridAdapter.getPositionForPack("recent"), 0);
|
scrollStickersToPosition(stickersGridAdapter.getPositionForPack("recent"), 0);
|
||||||
resetTabsY(Type.STICKERS);
|
resetTabsY(Type.STICKERS);
|
||||||
stickersTab.onPageScrolled(recentTabBum, recentTabBum > 0 ? recentTabBum : stickersTabOffset);
|
stickersTab.onPageScrolled(recentTabBum, recentTabBum > 0 ? recentTabBum : stickersTabOffset);
|
||||||
return;
|
return;
|
||||||
} else if (page == favTabBum) {
|
} else if (page == favTabBum) {
|
||||||
stickersGridView.stopScroll();
|
stickersGridView.stopScroll();
|
||||||
stickersLayoutManager.scrollToPositionWithOffset(stickersGridAdapter.getPositionForPack("fav"), 0);
|
scrollStickersToPosition(stickersGridAdapter.getPositionForPack("fav"), 0);
|
||||||
resetTabsY(Type.STICKERS);
|
resetTabsY(Type.STICKERS);
|
||||||
stickersTab.onPageScrolled(favTabBum, favTabBum > 0 ? favTabBum : stickersTabOffset);
|
stickersTab.onPageScrolled(favTabBum, favTabBum > 0 ? favTabBum : stickersTabOffset);
|
||||||
return;
|
return;
|
||||||
|
@ -1686,9 +1893,20 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
}
|
}
|
||||||
firstStickersAttach = false;
|
firstStickersAttach = false;
|
||||||
stickersGridView.stopScroll();
|
stickersGridView.stopScroll();
|
||||||
stickersLayoutManager.scrollToPositionWithOffset(stickersGridAdapter.getPositionForPack(stickerSets.get(index)), 0);
|
scrollStickersToPosition(stickersGridAdapter.getPositionForPack(stickerSets.get(index)), 0);
|
||||||
resetTabsY(Type.STICKERS);
|
resetTabsY(Type.STICKERS);
|
||||||
checkScroll(Type.STICKERS);
|
checkScroll(Type.STICKERS);
|
||||||
|
int firstTab;
|
||||||
|
if (favTabBum > 0) {
|
||||||
|
firstTab = favTabBum;
|
||||||
|
} else if (recentTabBum > 0) {
|
||||||
|
firstTab = recentTabBum;
|
||||||
|
} else {
|
||||||
|
firstTab = stickersTabOffset;
|
||||||
|
}
|
||||||
|
stickersTab.onPageScrolled(page, firstTab);
|
||||||
|
expandStickersByDragg = false;
|
||||||
|
updateStickerTabsPosition();
|
||||||
});
|
});
|
||||||
|
|
||||||
stickersGridView.setOnScrollListener(new TypedScrollListener(Type.STICKERS));
|
stickersGridView.setOnScrollListener(new TypedScrollListener(Type.STICKERS));
|
||||||
|
@ -1809,6 +2027,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
typeTabs.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
typeTabs.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
checkGridVisibility(position, positionOffset);
|
||||||
EmojiView.this.onPageScrolled(position, getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), positionOffsetPixels);
|
EmojiView.this.onPageScrolled(position, getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), positionOffsetPixels);
|
||||||
showBottomTab(true, true);
|
showBottomTab(true, true);
|
||||||
SearchField currentField;
|
SearchField currentField;
|
||||||
|
@ -1837,6 +2056,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
field.searchEditText.setSelection(currentFieldText.length());
|
field.searchEditText.setSelection(currentFieldText.length());
|
||||||
}
|
}
|
||||||
startStopVisibleGifs((position == 0 && positionOffset > 0) || position == 1);
|
startStopVisibleGifs((position == 0 && positionOffset > 0) || position == 1);
|
||||||
|
updateStickerTabsPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1972,6 +2192,31 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkGridVisibility(int position, float positionOffset) {
|
||||||
|
if (stickersContainer == null || gifContainer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (position == 0) {
|
||||||
|
emojiGridView.setVisibility(View.VISIBLE);
|
||||||
|
gifGridView.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE);
|
||||||
|
gifTabs.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE);
|
||||||
|
stickersGridView.setVisibility(View.GONE);
|
||||||
|
stickersTab.setVisibility(View.GONE);
|
||||||
|
} else if (position == 1) {
|
||||||
|
emojiGridView.setVisibility(View.GONE);
|
||||||
|
gifGridView.setVisibility(View.VISIBLE);
|
||||||
|
gifTabs.setVisibility(View.VISIBLE);
|
||||||
|
stickersGridView.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE);
|
||||||
|
stickersTab.setVisibility(positionOffset == 0 ? View.GONE : View.VISIBLE);
|
||||||
|
} else if (position == 2) {
|
||||||
|
emojiGridView.setVisibility(View.GONE);
|
||||||
|
gifGridView.setVisibility(View.GONE);
|
||||||
|
gifTabs.setVisibility(View.GONE);
|
||||||
|
stickersGridView.setVisibility(View.VISIBLE);
|
||||||
|
stickersTab.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static String addColorToCode(String code, String color) {
|
private static String addColorToCode(String code, String color) {
|
||||||
String end = null;
|
String end = null;
|
||||||
int length = code.length();
|
int length = code.length();
|
||||||
|
@ -2051,6 +2296,41 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
public void setTranslationY(float translationY) {
|
public void setTranslationY(float translationY) {
|
||||||
super.setTranslationY(translationY);
|
super.setTranslationY(translationY);
|
||||||
updateBottomTabContainerPosition();
|
updateBottomTabContainerPosition();
|
||||||
|
updateStickerTabsPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect rect = new Rect();
|
||||||
|
private void updateStickerTabsPosition() {
|
||||||
|
if (stickersTabContainer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
boolean visible = getVisibility() == View.VISIBLE && stickersContainerAttached && delegate.getProgressToSearchOpened() != 1f;
|
||||||
|
stickersTabContainer.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
if (visible) {
|
||||||
|
rect.setEmpty();
|
||||||
|
pager.getChildVisibleRect(stickersContainer, rect, null);
|
||||||
|
float searchProgressOffset = AndroidUtilities.dp(50) * delegate.getProgressToSearchOpened();
|
||||||
|
int left = rect.left;
|
||||||
|
if (left != 0 || searchProgressOffset != 0) {
|
||||||
|
expandStickersByDragg = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stickersTabContainer.setTranslationX(left);
|
||||||
|
float y = getTop() + getTranslationY() - stickersTabContainer.getTop() - stickersTab.getExpandedOffset() - searchProgressOffset;
|
||||||
|
if (stickersTabContainer.getTranslationY() != y) {
|
||||||
|
stickersTabContainer.setTranslationY(y);
|
||||||
|
stickersTabContainer.invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expandStickersByDragg && (visible && showing)) {
|
||||||
|
stickersTab.expandStickers(lastStickersX, true);
|
||||||
|
} else {
|
||||||
|
expandStickersByDragg = false;
|
||||||
|
stickersTab.expandStickers(lastStickersX, false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateBottomTabContainerPosition() {
|
private void updateBottomTabContainerPosition() {
|
||||||
|
@ -2059,7 +2339,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
float y = getY() - parent.getHeight();
|
float y = getY() - parent.getHeight();
|
||||||
if (getLayoutParams().height > 0) {
|
if (getLayoutParams().height > 0) {
|
||||||
y += getLayoutParams().height;
|
y += getLayoutParams().height;
|
||||||
} else {
|
} else {
|
||||||
y += getMeasuredHeight();
|
y += getMeasuredHeight();
|
||||||
}
|
}
|
||||||
|
@ -2189,7 +2469,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
|
|
||||||
if (searchField == currentField && delegate != null && delegate.isExpanded()) {
|
if (searchField == currentField && delegate != null && delegate.isExpanded()) {
|
||||||
searchAnimation = new AnimatorSet();
|
searchAnimation = new AnimatorSet();
|
||||||
if (tabStrip != null) {
|
if (tabStrip != null && a != 2) {
|
||||||
searchAnimation.playTogether(
|
searchAnimation.playTogether(
|
||||||
ObjectAnimator.ofFloat(tabStrip, View.TRANSLATION_Y, -AndroidUtilities.dp(48)),
|
ObjectAnimator.ofFloat(tabStrip, View.TRANSLATION_Y, -AndroidUtilities.dp(48)),
|
||||||
ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, -AndroidUtilities.dp(48)),
|
ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, -AndroidUtilities.dp(48)),
|
||||||
|
@ -2199,8 +2479,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, -AndroidUtilities.dp(48)),
|
ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, -AndroidUtilities.dp(48)),
|
||||||
ObjectAnimator.ofFloat(currentField, View.TRANSLATION_Y, AndroidUtilities.dp(0)));
|
ObjectAnimator.ofFloat(currentField, View.TRANSLATION_Y, AndroidUtilities.dp(0)));
|
||||||
}
|
}
|
||||||
searchAnimation.setDuration(200);
|
searchAnimation.setDuration(220);
|
||||||
searchAnimation.setInterpolator(CubicBezierInterpolator.EASE_OUT_QUINT);
|
searchAnimation.setInterpolator(CubicBezierInterpolator.DEFAULT);
|
||||||
searchAnimation.addListener(new AnimatorListenerAdapter() {
|
searchAnimation.addListener(new AnimatorListenerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
@ -2225,7 +2505,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
searchAnimation.start();
|
searchAnimation.start();
|
||||||
} else {
|
} else {
|
||||||
currentField.setTranslationY(AndroidUtilities.dp(0));
|
currentField.setTranslationY(AndroidUtilities.dp(0));
|
||||||
if (tabStrip != null) {
|
if (tabStrip != null && a != 2) {
|
||||||
tabStrip.setTranslationY(-AndroidUtilities.dp(48));
|
tabStrip.setTranslationY(-AndroidUtilities.dp(48));
|
||||||
}
|
}
|
||||||
if (gridView == stickersGridView) {
|
if (gridView == stickersGridView) {
|
||||||
|
@ -2276,6 +2556,18 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
closeSearch(animated, -1);
|
closeSearch(animated, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scrollStickersToPosition(int p, int offset) {
|
||||||
|
View view = stickersLayoutManager.findViewByPosition(p);
|
||||||
|
int firstPosition = stickersLayoutManager.findFirstVisibleItemPosition();
|
||||||
|
if (view == null && Math.abs(p - firstPosition) > 40) {
|
||||||
|
scrollHelper.setScrollDirection(stickersLayoutManager.findFirstVisibleItemPosition() < p ? RecyclerAnimationScrollHelper.SCROLL_DIRECTION_DOWN : RecyclerAnimationScrollHelper.SCROLL_DIRECTION_UP);
|
||||||
|
scrollHelper.scrollToPosition(p, offset, false, true);
|
||||||
|
} else {
|
||||||
|
ignoreStickersScroll = true;
|
||||||
|
stickersGridView.smoothScrollToPosition(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void closeSearch(boolean animated, long scrollToSet) {
|
public void closeSearch(boolean animated, long scrollToSet) {
|
||||||
if (searchAnimation != null) {
|
if (searchAnimation != null) {
|
||||||
searchAnimation.cancel();
|
searchAnimation.cancel();
|
||||||
|
@ -2287,8 +2579,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
TLRPC.TL_messages_stickerSet set = MediaDataController.getInstance(currentAccount).getStickerSetById(scrollToSet);
|
TLRPC.TL_messages_stickerSet set = MediaDataController.getInstance(currentAccount).getStickerSetById(scrollToSet);
|
||||||
if (set != null) {
|
if (set != null) {
|
||||||
int pos = stickersGridAdapter.getPositionForPack(set);
|
int pos = stickersGridAdapter.getPositionForPack(set);
|
||||||
if (pos >= 0) {
|
if (pos >= 0 && pos < stickersGridAdapter.getItemCount()) {
|
||||||
stickersLayoutManager.scrollToPositionWithOffset(pos, AndroidUtilities.dp(48 + 12));
|
scrollStickersToPosition(pos, AndroidUtilities.dp(48 + 12));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2328,14 +2620,14 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
|
|
||||||
if (a == currentItem && animated) {
|
if (a == currentItem && animated) {
|
||||||
searchAnimation = new AnimatorSet();
|
searchAnimation = new AnimatorSet();
|
||||||
if (tabStrip != null) {
|
if (tabStrip != null && a != 2) {
|
||||||
searchAnimation.playTogether(
|
searchAnimation.playTogether(
|
||||||
ObjectAnimator.ofFloat(tabStrip, View.TRANSLATION_Y, 0),
|
ObjectAnimator.ofFloat(tabStrip, View.TRANSLATION_Y, 0),
|
||||||
ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, AndroidUtilities.dp(48) - searchFieldHeight),
|
ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, AndroidUtilities.dp(48) - searchFieldHeight),
|
||||||
ObjectAnimator.ofFloat(currentField, View.TRANSLATION_Y, AndroidUtilities.dp(48) - searchFieldHeight));
|
ObjectAnimator.ofFloat(currentField, View.TRANSLATION_Y, AndroidUtilities.dp(48) - searchFieldHeight));
|
||||||
} else {
|
} else {
|
||||||
searchAnimation.playTogether(
|
searchAnimation.playTogether(
|
||||||
ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, -searchFieldHeight),
|
ObjectAnimator.ofFloat(gridView, View.TRANSLATION_Y, AndroidUtilities.dp(48) - searchFieldHeight),
|
||||||
ObjectAnimator.ofFloat(currentField, View.TRANSLATION_Y, -searchFieldHeight));
|
ObjectAnimator.ofFloat(currentField, View.TRANSLATION_Y, -searchFieldHeight));
|
||||||
}
|
}
|
||||||
searchAnimation.setDuration(200);
|
searchAnimation.setDuration(200);
|
||||||
|
@ -2375,7 +2667,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
searchAnimation.start();
|
searchAnimation.start();
|
||||||
} else {
|
} else {
|
||||||
currentField.setTranslationY(AndroidUtilities.dp(48) - searchFieldHeight);
|
currentField.setTranslationY(AndroidUtilities.dp(48) - searchFieldHeight);
|
||||||
if (tabStrip != null) {
|
if (tabStrip != null && a != 2) {
|
||||||
tabStrip.setTranslationY(0);
|
tabStrip.setTranslationY(0);
|
||||||
}
|
}
|
||||||
if (gridView == stickersGridView) {
|
if (gridView == stickersGridView) {
|
||||||
|
@ -2537,7 +2829,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
checkEmojiTabY(emojiGridView, dy);
|
checkEmojiTabY(emojiGridView, dy);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (delegate != null && delegate.isSearchOpened()) {
|
if (delegate != null && delegate.isSearchOpened() || ignoreStickersScroll) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final RecyclerListView listView = getListViewForType(type);
|
final RecyclerListView listView = getListViewForType(type);
|
||||||
|
@ -2553,18 +2845,22 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
} else if (tabsMinusDy[type] < -AndroidUtilities.dp(48 * 6)) {
|
} else if (tabsMinusDy[type] < -AndroidUtilities.dp(48 * 6)) {
|
||||||
tabsMinusDy[type] = -AndroidUtilities.dp(48 * 6);
|
tabsMinusDy[type] = -AndroidUtilities.dp(48 * 6);
|
||||||
}
|
}
|
||||||
getTabsForType(type).setTranslationY(Math.max(-AndroidUtilities.dp(48), tabsMinusDy[type]));
|
if (type == 0) {
|
||||||
|
updateStickerTabsPosition();
|
||||||
|
} else {
|
||||||
|
getTabsForType(type).setTranslationY(Math.max(-AndroidUtilities.dp(48), tabsMinusDy[type]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetTabsY(@Type int type) {
|
private void resetTabsY(@Type int type) {
|
||||||
if (delegate != null && delegate.isSearchOpened()) {
|
if (delegate != null && delegate.isSearchOpened() || type == Type.STICKERS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
getTabsForType(type).setTranslationY(tabsMinusDy[type] = 0);
|
getTabsForType(type).setTranslationY(tabsMinusDy[type] = 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void animateTabsY(@Type int type) {
|
private void animateTabsY(@Type int type) {
|
||||||
if (delegate != null && delegate.isSearchOpened()) {
|
if ((delegate != null && delegate.isSearchOpened()) || type == Type.STICKERS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final float tabsHeight = AndroidUtilities.dpf2(type == Type.EMOJIS ? 38 : 48);
|
final float tabsHeight = AndroidUtilities.dpf2(type == Type.EMOJIS ? 38 : 48);
|
||||||
|
@ -2635,9 +2931,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
|
|
||||||
private ScrollSlidingTabStrip getTabsForType(@Type int type) {
|
private ScrollSlidingTabStrip getTabsForType(@Type int type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type.STICKERS: return stickersTab;
|
case Type.STICKERS:
|
||||||
case Type.EMOJIS: return emojiTabs;
|
return stickersTab;
|
||||||
case Type.GIFS: return gifTabs;
|
case Type.EMOJIS:
|
||||||
|
return emojiTabs;
|
||||||
|
case Type.GIFS:
|
||||||
|
return gifTabs;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unexpected argument: " + type);
|
throw new IllegalArgumentException("Unexpected argument: " + type);
|
||||||
}
|
}
|
||||||
|
@ -2645,9 +2944,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
|
|
||||||
private RecyclerListView getListViewForType(@Type int type) {
|
private RecyclerListView getListViewForType(@Type int type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type.STICKERS: return stickersGridView;
|
case Type.STICKERS:
|
||||||
case Type.EMOJIS: return emojiGridView;
|
return stickersGridView;
|
||||||
case Type.GIFS: return gifGridView;
|
case Type.EMOJIS:
|
||||||
|
return emojiGridView;
|
||||||
|
case Type.GIFS:
|
||||||
|
return gifGridView;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unexpected argument: " + type);
|
throw new IllegalArgumentException("Unexpected argument: " + type);
|
||||||
}
|
}
|
||||||
|
@ -2655,9 +2957,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
|
|
||||||
private GridLayoutManager getLayoutManagerForType(@Type int type) {
|
private GridLayoutManager getLayoutManagerForType(@Type int type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type.STICKERS: return stickersLayoutManager;
|
case Type.STICKERS:
|
||||||
case Type.EMOJIS: return emojiLayoutManager;
|
return stickersLayoutManager;
|
||||||
case Type.GIFS: return gifLayoutManager;
|
case Type.EMOJIS:
|
||||||
|
return emojiLayoutManager;
|
||||||
|
case Type.GIFS:
|
||||||
|
return gifLayoutManager;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unexpected argument: " + type);
|
throw new IllegalArgumentException("Unexpected argument: " + type);
|
||||||
}
|
}
|
||||||
|
@ -2665,9 +2970,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
|
|
||||||
private SearchField getSearchFieldForType(@Type int type) {
|
private SearchField getSearchFieldForType(@Type int type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type.STICKERS: return stickersSearchField;
|
case Type.STICKERS:
|
||||||
case Type.EMOJIS: return emojiSearchField;
|
return stickersSearchField;
|
||||||
case Type.GIFS: return gifSearchField;
|
case Type.EMOJIS:
|
||||||
|
return emojiSearchField;
|
||||||
|
case Type.GIFS:
|
||||||
|
return gifSearchField;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unexpected argument: " + type);
|
throw new IllegalArgumentException("Unexpected argument: " + type);
|
||||||
}
|
}
|
||||||
|
@ -2770,6 +3078,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
|
|
||||||
private void checkScroll(@Type int type) {
|
private void checkScroll(@Type int type) {
|
||||||
if (type == Type.STICKERS) {
|
if (type == Type.STICKERS) {
|
||||||
|
if (ignoreStickersScroll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
int firstVisibleItem = stickersLayoutManager.findFirstVisibleItemPosition();
|
int firstVisibleItem = stickersLayoutManager.findFirstVisibleItemPosition();
|
||||||
if (firstVisibleItem == RecyclerView.NO_POSITION) {
|
if (firstVisibleItem == RecyclerView.NO_POSITION) {
|
||||||
return;
|
return;
|
||||||
|
@ -2881,12 +3192,13 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStickerTabs() {
|
private void updateStickerTabs() {
|
||||||
if (stickersTab == null) {
|
if (stickersTab == null || stickersTab.isDragging()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
recentTabBum = -2;
|
recentTabBum = -2;
|
||||||
favTabBum = -2;
|
favTabBum = -2;
|
||||||
trendingTabNum = -2;
|
trendingTabNum = -2;
|
||||||
|
hasChatStickers = false;
|
||||||
|
|
||||||
stickersTabOffset = 0;
|
stickersTabOffset = 0;
|
||||||
int lastPosition = stickersTab.getCurrentPosition();
|
int lastPosition = stickersTab.getCurrentPosition();
|
||||||
|
@ -2895,10 +3207,22 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
final MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount);
|
final MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount);
|
||||||
|
|
||||||
SharedPreferences preferences = MessagesController.getEmojiSettings(currentAccount);
|
SharedPreferences preferences = MessagesController.getEmojiSettings(currentAccount);
|
||||||
|
featuredStickerSets.clear();
|
||||||
ArrayList<TLRPC.StickerSetCovered> featured = mediaDataController.getFeaturedStickerSets();
|
ArrayList<TLRPC.StickerSetCovered> featured = mediaDataController.getFeaturedStickerSets();
|
||||||
if (!featured.isEmpty() && (!BuildVars.DEBUG_PRIVATE_VERSION || (mediaDataController.getUnreadStickerSets().isEmpty() || preferences.getLong("featured_hidden", 0) == featured.get(0).set.id))) {
|
for (int a = 0, N = featured.size(); a < N; a++) {
|
||||||
|
TLRPC.StickerSetCovered set = featured.get(a);
|
||||||
|
if (mediaDataController.isStickerPackInstalled(set.set.id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
featuredStickerSets.add(set);
|
||||||
|
}
|
||||||
|
if (trendingAdapter != null) {
|
||||||
|
trendingAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
if (!featured.isEmpty() && (featuredStickerSets.isEmpty() || preferences.getLong("featured_hidden", 0) == featured.get(0).set.id)) {
|
||||||
final int id = mediaDataController.getUnreadStickerSets().isEmpty() ? 2 : 3;
|
final int id = mediaDataController.getUnreadStickerSets().isEmpty() ? 2 : 3;
|
||||||
final ImageView trendingStickersTabView = stickersTab.addIconTab(id, stickerIcons[id]);
|
final StickerTabView trendingStickersTabView = stickersTab.addStickerIconTab(id, stickerIcons[id]);
|
||||||
|
trendingStickersTabView.textView.setText(LocaleController.getString("FeaturedStickersShort", R.string.FeaturedStickersShort));
|
||||||
trendingStickersTabView.setContentDescription(LocaleController.getString("FeaturedStickers", R.string.FeaturedStickers));
|
trendingStickersTabView.setContentDescription(LocaleController.getString("FeaturedStickers", R.string.FeaturedStickers));
|
||||||
trendingTabNum = stickersTabOffset;
|
trendingTabNum = stickersTabOffset;
|
||||||
stickersTabOffset++;
|
stickersTabOffset++;
|
||||||
|
@ -2907,13 +3231,17 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
if (!favouriteStickers.isEmpty()) {
|
if (!favouriteStickers.isEmpty()) {
|
||||||
favTabBum = stickersTabOffset;
|
favTabBum = stickersTabOffset;
|
||||||
stickersTabOffset++;
|
stickersTabOffset++;
|
||||||
stickersTab.addIconTab(1, stickerIcons[1]).setContentDescription(LocaleController.getString("FavoriteStickers", R.string.FavoriteStickers));
|
StickerTabView stickerTabView = stickersTab.addStickerIconTab(1, stickerIcons[1]);
|
||||||
|
stickerTabView.textView.setText(LocaleController.getString("FavoriteStickersShort", R.string.FavoriteStickersShort));
|
||||||
|
stickerTabView.setContentDescription(LocaleController.getString("FavoriteStickers", R.string.FavoriteStickers));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!recentStickers.isEmpty()) {
|
if (!recentStickers.isEmpty()) {
|
||||||
recentTabBum = stickersTabOffset;
|
recentTabBum = stickersTabOffset;
|
||||||
stickersTabOffset++;
|
stickersTabOffset++;
|
||||||
stickersTab.addIconTab(0, stickerIcons[0]).setContentDescription(LocaleController.getString("RecentStickers", R.string.RecentStickers));
|
StickerTabView stickerTabView = stickersTab.addStickerIconTab(0, stickerIcons[0]);
|
||||||
|
stickerTabView.textView.setText(LocaleController.getString("RecentStickersShort", R.string.RecentStickersShort));
|
||||||
|
stickerTabView.setContentDescription(LocaleController.getString("RecentStickers", R.string.RecentStickers));
|
||||||
}
|
}
|
||||||
|
|
||||||
stickerSets.clear();
|
stickerSets.clear();
|
||||||
|
@ -2990,17 +3318,17 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
stickerSets.remove(0);
|
stickerSets.remove(0);
|
||||||
a--;
|
a--;
|
||||||
} else {
|
} else {
|
||||||
|
hasChatStickers = true;
|
||||||
stickersTab.addStickerTab(chat);
|
stickersTab.addStickerTab(chat);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TLRPC.TL_messages_stickerSet stickerSet = stickerSets.get(a);
|
TLRPC.TL_messages_stickerSet stickerSet = stickerSets.get(a);
|
||||||
TLRPC.Document document = stickerSet.documents.get(0);
|
TLRPC.Document document = stickerSet.documents.get(0);
|
||||||
TLObject thumb = FileLoader.getClosestPhotoSizeWithSize(stickerSet.set.thumbs, 90);
|
TLObject thumb = FileLoader.getClosestPhotoSizeWithSize(stickerSet.set.thumbs, 90);
|
||||||
SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(stickerSet.set.thumbs, Theme.key_emptyListPlaceholder, 0.2f);
|
|
||||||
if (thumb == null) {
|
if (thumb == null) {
|
||||||
thumb = document;
|
thumb = document;
|
||||||
}
|
}
|
||||||
stickersTab.addStickerTab(thumb, svgThumb, document, stickerSet).setContentDescription(stickerSet.set.title + ", " + LocaleController.getString("AccDescrStickerSet", R.string.AccDescrStickerSet));
|
stickersTab.addStickerTab(thumb, document, stickerSet).setContentDescription(stickerSet.set.title + ", " + LocaleController.getString("AccDescrStickerSet", R.string.AccDescrStickerSet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stickersTab.commitUpdate();
|
stickersTab.commitUpdate();
|
||||||
|
@ -3060,8 +3388,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
final Emoji.EmojiDrawable emojiDrawable = Emoji.getEmojiDrawable(emoji);
|
final Emoji.EmojiDrawable emojiDrawable = Emoji.getEmojiDrawable(emoji);
|
||||||
if (emojiDrawable != null) {
|
if (emojiDrawable != null) {
|
||||||
gifTabsCount++;
|
gifTabsCount++;
|
||||||
final ImageView iconTab = gifTabs.addIconTab(3 + i, emojiDrawable);
|
TLRPC.Document document = MediaDataController.getInstance(currentAccount).getEmojiAnimatedSticker(emoji);
|
||||||
iconTab.setPadding(hPadding, vPadding, hPadding, vPadding);
|
final View iconTab = gifTabs.addEmojiTab(3 + i, emojiDrawable, document);
|
||||||
|
// iconTab.setPadding(hPadding, vPadding, hPadding, vPadding);
|
||||||
iconTab.setContentDescription(emoji);
|
iconTab.setContentDescription(emoji);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3286,6 +3615,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onLayout(changed, left, top, right, bottom);
|
super.onLayout(changed, left, top, right, bottom);
|
||||||
|
updateStickerTabsPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reloadStickersAdapter() {
|
private void reloadStickersAdapter() {
|
||||||
|
@ -3339,6 +3669,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
pager.setCurrentItem(2, false);
|
pager.setCurrentItem(2, false);
|
||||||
}
|
}
|
||||||
if (stickersTab != null) {
|
if (stickersTab != null) {
|
||||||
|
firstTabUpdate = true;
|
||||||
if (favTabBum >= 0) {
|
if (favTabBum >= 0) {
|
||||||
stickersTab.selectTab(favTabBum);
|
stickersTab.selectTab(favTabBum);
|
||||||
} else if (recentTabBum >= 0) {
|
} else if (recentTabBum >= 0) {
|
||||||
|
@ -3346,6 +3677,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
} else {
|
} else {
|
||||||
stickersTab.selectTab(stickersTabOffset);
|
stickersTab.selectTab(stickersTabOffset);
|
||||||
}
|
}
|
||||||
|
firstTabUpdate = false;
|
||||||
|
stickersLayoutManager.scrollToPositionWithOffset(1, 0);
|
||||||
}
|
}
|
||||||
} else if (currentPage == 2) {
|
} else if (currentPage == 2) {
|
||||||
showBackspaceButton(false, false);
|
showBackspaceButton(false, false);
|
||||||
|
@ -3397,6 +3730,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
MediaDataController.getInstance(currentAccount).loadRecents(MediaDataController.TYPE_IMAGE, false, true, false);
|
MediaDataController.getInstance(currentAccount).loadRecents(MediaDataController.TYPE_IMAGE, false, true, false);
|
||||||
MediaDataController.getInstance(currentAccount).loadRecents(MediaDataController.TYPE_FAVE, false, true, false);
|
MediaDataController.getInstance(currentAccount).loadRecents(MediaDataController.TYPE_FAVE, false, true, false);
|
||||||
}
|
}
|
||||||
|
chooseStickerActionTracker.checkVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCurrentPage() {
|
public int getCurrentPage() {
|
||||||
|
@ -3684,7 +4018,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
||||||
BackupImageView imageView = (BackupImageView) holder.itemView;
|
BackupImageView imageView = (BackupImageView) holder.itemView;
|
||||||
TLRPC.StickerSetCovered set = MediaDataController.getInstance(currentAccount).getFeaturedStickerSets().get(position);
|
TLRPC.StickerSetCovered set = featuredStickerSets.get(position);
|
||||||
imageView.setTag(set);
|
imageView.setTag(set);
|
||||||
|
|
||||||
TLRPC.Document document = set.cover;
|
TLRPC.Document document = set.cover;
|
||||||
|
@ -3693,6 +4027,9 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
}
|
}
|
||||||
TLObject object = FileLoader.getClosestPhotoSizeWithSize(set.set.thumbs, 90);
|
TLObject object = FileLoader.getClosestPhotoSizeWithSize(set.set.thumbs, 90);
|
||||||
SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(set.set.thumbs, Theme.key_emptyListPlaceholder, 0.2f);
|
SvgHelper.SvgDrawable svgThumb = DocumentObject.getSvgThumb(set.set.thumbs, Theme.key_emptyListPlaceholder, 0.2f);
|
||||||
|
if (svgThumb != null) {
|
||||||
|
svgThumb.overrideWidthAndHeight(512, 512);
|
||||||
|
}
|
||||||
if (object == null) {
|
if (object == null) {
|
||||||
object = document;
|
object = document;
|
||||||
}
|
}
|
||||||
|
@ -3731,7 +4068,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return MediaDataController.getInstance(currentAccount).getFeaturedStickerSets().size();
|
return featuredStickerSets.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3882,12 +4219,12 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
((StickerSetNameCell) view).setOnIconClickListener(v -> {
|
((StickerSetNameCell) view).setOnIconClickListener(v -> {
|
||||||
MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount);
|
MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount);
|
||||||
ArrayList<TLRPC.StickerSetCovered> featured = mediaDataController.getFeaturedStickerSets();
|
ArrayList<TLRPC.StickerSetCovered> featured = mediaDataController.getFeaturedStickerSets();
|
||||||
if (!featured.isEmpty() && !mediaDataController.getUnreadStickerSets().isEmpty()) {
|
if (!featured.isEmpty()) {
|
||||||
MessagesController.getEmojiSettings(currentAccount).edit().putLong("featured_hidden", featured.get(0).set.id).commit();
|
MessagesController.getEmojiSettings(currentAccount).edit().putLong("featured_hidden", featured.get(0).set.id).commit();
|
||||||
updateStickerTabs();
|
|
||||||
if (stickersGridAdapter != null) {
|
if (stickersGridAdapter != null) {
|
||||||
stickersGridAdapter.notifyItemRangeRemoved(1, 2);
|
stickersGridAdapter.notifyItemRangeRemoved(1, 2);
|
||||||
}
|
}
|
||||||
|
updateStickerTabs();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
@ -3902,6 +4239,8 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
return super.onInterceptTouchEvent(e);
|
return super.onInterceptTouchEvent(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
horizontalListView.setSelectorRadius(AndroidUtilities.dp(4));
|
||||||
|
horizontalListView.setSelectorDrawableColor(Theme.getColor(Theme.key_listSelector));
|
||||||
horizontalListView.setTag(9);
|
horizontalListView.setTag(9);
|
||||||
horizontalListView.setItemAnimator(null);
|
horizontalListView.setItemAnimator(null);
|
||||||
horizontalListView.setLayoutAnimation(null);
|
horizontalListView.setLayoutAnimation(null);
|
||||||
|
@ -3913,7 +4252,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
};
|
};
|
||||||
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
|
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
|
||||||
horizontalListView.setLayoutManager(layoutManager);
|
horizontalListView.setLayoutManager(layoutManager);
|
||||||
horizontalListView.setAdapter(new TrendingAdapter());
|
horizontalListView.setAdapter(trendingAdapter = new TrendingAdapter());
|
||||||
horizontalListView.setOnItemClickListener((view1, position) -> {
|
horizontalListView.setOnItemClickListener((view1, position) -> {
|
||||||
openTrendingStickers((TLRPC.StickerSetCovered) view1.getTag());
|
openTrendingStickers((TLRPC.StickerSetCovered) view1.getTag());
|
||||||
});
|
});
|
||||||
|
@ -4037,7 +4376,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount);
|
MediaDataController mediaDataController = MediaDataController.getInstance(currentAccount);
|
||||||
SharedPreferences preferences = MessagesController.getEmojiSettings(currentAccount);
|
SharedPreferences preferences = MessagesController.getEmojiSettings(currentAccount);
|
||||||
ArrayList<TLRPC.StickerSetCovered> featured = mediaDataController.getFeaturedStickerSets();
|
ArrayList<TLRPC.StickerSetCovered> featured = mediaDataController.getFeaturedStickerSets();
|
||||||
if (BuildVars.DEBUG_PRIVATE_VERSION && !featured.isEmpty() && !mediaDataController.getUnreadStickerSets().isEmpty() && preferences.getLong("featured_hidden", 0) != featured.get(0).set.id) {
|
if (!featuredStickerSets.isEmpty() && preferences.getLong("featured_hidden", 0) != featured.get(0).set.id) {
|
||||||
cache.put(totalItems++, "trend1");
|
cache.put(totalItems++, "trend1");
|
||||||
cache.put(totalItems++, "trend2");
|
cache.put(totalItems++, "trend2");
|
||||||
startRow += 2;
|
startRow += 2;
|
||||||
|
@ -4475,7 +4814,7 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
}
|
}
|
||||||
|
|
||||||
public CharSequence getPageTitle(int position) {
|
public CharSequence getPageTitle(int position) {
|
||||||
switch(position) {
|
switch (position) {
|
||||||
case 0:
|
case 0:
|
||||||
return LocaleController.getString("Emoji", R.string.Emoji);
|
return LocaleController.getString("Emoji", R.string.Emoji);
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -4505,12 +4844,6 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
return view == object;
|
return view == object;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unregisterDataSetObserver(DataSetObserver observer) {
|
|
||||||
if (observer != null) {
|
|
||||||
super.unregisterDataSetObserver(observer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5649,4 +5982,51 @@ public class EmojiView extends FrameLayout implements NotificationCenter.Notific
|
||||||
super.notifyDataSetChanged();
|
super.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void searchProgressChanged() {
|
||||||
|
updateStickerTabsPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getStickersExpandOffset() {
|
||||||
|
return stickersTab == null ? 0 : stickersTab.getExpandedOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ChooseStickerActionTracker chooseStickerActionTracker = new ChooseStickerActionTracker();
|
||||||
|
|
||||||
|
private class ChooseStickerActionTracker {
|
||||||
|
|
||||||
|
boolean visible = false;
|
||||||
|
boolean typingWasSent;
|
||||||
|
long lastActionTime = -1;
|
||||||
|
|
||||||
|
void doSomeAction() {
|
||||||
|
if (visible) {
|
||||||
|
if (lastActionTime == -1) {
|
||||||
|
lastActionTime = System.currentTimeMillis();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (System.currentTimeMillis() - lastActionTime > 2000) {
|
||||||
|
typingWasSent = true;
|
||||||
|
MessagesController.getInstance(currentAccount).sendTyping(delegate.getDialogId(), delegate.getThreadId(), 10, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkVisibility() {
|
||||||
|
if (delegate == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
visible = getVisibility() == View.VISIBLE && stickersContainerAttached;
|
||||||
|
if (!visible) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset() {
|
||||||
|
if (typingWasSent) {
|
||||||
|
MessagesController.getInstance(currentAccount).sendTyping(delegate.getDialogId(), delegate.getThreadId(), 2, 0);
|
||||||
|
}
|
||||||
|
lastActionTime = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,998 @@
|
||||||
|
package org.telegram.ui.Components;
|
||||||
|
|
||||||
|
import android.animation.Animator;
|
||||||
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.ValueAnimator;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Outline;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.PorterDuffColorFilter;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewOutlineProvider;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.ScrollView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.ChatListItemAnimator;
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManagerFixed;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
|
import org.telegram.messenger.BuildVars;
|
||||||
|
import org.telegram.messenger.ChatObject;
|
||||||
|
import org.telegram.messenger.ContactsController;
|
||||||
|
import org.telegram.messenger.FileLog;
|
||||||
|
import org.telegram.messenger.ForwardingMessagesParams;
|
||||||
|
import org.telegram.messenger.LocaleController;
|
||||||
|
import org.telegram.messenger.MessageObject;
|
||||||
|
import org.telegram.messenger.NotificationCenter;
|
||||||
|
import org.telegram.messenger.R;
|
||||||
|
import org.telegram.tgnet.TLRPC;
|
||||||
|
import org.telegram.ui.ActionBar.ActionBar;
|
||||||
|
import org.telegram.ui.ActionBar.ActionBarMenuSubItem;
|
||||||
|
import org.telegram.ui.ActionBar.Theme;
|
||||||
|
import org.telegram.ui.Cells.BotHelpCell;
|
||||||
|
import org.telegram.ui.Cells.ChatMessageCell;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class ForwardingPreviewView extends FrameLayout {
|
||||||
|
|
||||||
|
SizeNotifierFrameLayout chatPreviewContainer;
|
||||||
|
ActionBar actionBar;
|
||||||
|
RecyclerListView chatListView;
|
||||||
|
ChatListItemAnimator itemAnimator;
|
||||||
|
GridLayoutManagerFixed chatLayoutManager;
|
||||||
|
|
||||||
|
ForwardingMessagesParams forwardingMessagesParams;
|
||||||
|
Adapter adapter;
|
||||||
|
|
||||||
|
ScrollView menuScrollView;
|
||||||
|
LinearLayout menuContainer;
|
||||||
|
LinearLayout buttonsLayout;
|
||||||
|
LinearLayout buttonsLayout2;
|
||||||
|
|
||||||
|
ActionBarMenuSubItem showSendersNameView;
|
||||||
|
ActionBarMenuSubItem hideSendersNameView;
|
||||||
|
|
||||||
|
ActionBarMenuSubItem showCaptionView;
|
||||||
|
ActionBarMenuSubItem hideCaptionView;
|
||||||
|
|
||||||
|
ActionBarMenuSubItem changeRecipientView;
|
||||||
|
ActionBarMenuSubItem sendMessagesView;
|
||||||
|
|
||||||
|
int chatTopOffset;
|
||||||
|
float yOffset;
|
||||||
|
int currentTopOffset;
|
||||||
|
float currentYOffset;
|
||||||
|
|
||||||
|
ArrayList<ActionBarMenuSubItem> actionItems = new ArrayList<>();
|
||||||
|
|
||||||
|
Rect rect = new Rect();
|
||||||
|
|
||||||
|
private boolean firstLayout = true;
|
||||||
|
ValueAnimator offsetsAnimator;
|
||||||
|
TLRPC.User currentUser;
|
||||||
|
TLRPC.Chat currentChat;
|
||||||
|
boolean showing;
|
||||||
|
boolean isLandscapeMode;
|
||||||
|
private final int currentAccount;
|
||||||
|
boolean returnSendersNames;
|
||||||
|
|
||||||
|
Runnable changeBoundsRunnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (offsetsAnimator != null && !offsetsAnimator.isRunning()) {
|
||||||
|
offsetsAnimator.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final ArrayList<MessageObject.GroupedMessages> drawingGroups = new ArrayList<>(10);
|
||||||
|
|
||||||
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
|
public ForwardingPreviewView(@NonNull Context context, ForwardingMessagesParams params, TLRPC.User user, TLRPC.Chat chat, int currentAccount) {
|
||||||
|
super(context);
|
||||||
|
this.currentAccount = currentAccount;
|
||||||
|
currentUser = user;
|
||||||
|
currentChat = chat;
|
||||||
|
forwardingMessagesParams = params;
|
||||||
|
|
||||||
|
chatPreviewContainer = new SizeNotifierFrameLayout(context);
|
||||||
|
chatPreviewContainer.setBackgroundImage(Theme.getCachedWallpaper(), Theme.isWallpaperMotion());
|
||||||
|
chatPreviewContainer.setOccupyStatusBar(false);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
|
chatPreviewContainer.setOutlineProvider(new ViewOutlineProvider() {
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||||
|
@Override
|
||||||
|
public void getOutline(View view, Outline outline) {
|
||||||
|
outline.setRoundRect(0, (int) (currentTopOffset + 1), view.getMeasuredWidth(), view.getMeasuredHeight(), AndroidUtilities.dp(6));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chatPreviewContainer.setClipToOutline(true);
|
||||||
|
chatPreviewContainer.setElevation(AndroidUtilities.dp(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
actionBar = new ActionBar(context);
|
||||||
|
actionBar.setBackgroundColor(Theme.getColor(Theme.key_actionBarDefault));
|
||||||
|
actionBar.setOccupyStatusBar(false);
|
||||||
|
|
||||||
|
chatListView = new RecyclerListView(context) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean drawChild(Canvas canvas, View child, long drawingTime) {
|
||||||
|
if (child instanceof ChatMessageCell) {
|
||||||
|
ChatMessageCell cell = (ChatMessageCell) child;
|
||||||
|
boolean r = super.drawChild(canvas, child, drawingTime);
|
||||||
|
cell.drawCheckBox(canvas);
|
||||||
|
canvas.save();
|
||||||
|
canvas.translate(cell.getX(), cell.getY());
|
||||||
|
cell.drawMessageText(canvas, cell.getMessageObject().textLayoutBlocks, true, 1f, false);
|
||||||
|
|
||||||
|
if (cell.getCurrentMessagesGroup() != null || cell.getTransitionParams().animateBackgroundBoundsInner) {
|
||||||
|
cell.drawNamesLayout(canvas, 1f);
|
||||||
|
}
|
||||||
|
if ((cell.getCurrentPosition() != null && cell.getCurrentPosition().last) || cell.getTransitionParams().animateBackgroundBoundsInner) {
|
||||||
|
cell.drawTime(canvas, 1f, true);
|
||||||
|
}
|
||||||
|
if ((cell.getCurrentPosition() != null && cell.getCurrentPosition().last) || cell.getCurrentPosition() == null) {
|
||||||
|
cell.drawCaptionLayout(canvas, false, 1f);
|
||||||
|
}
|
||||||
|
cell.getTransitionParams().recordDrawingStatePreview();
|
||||||
|
canvas.restore();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dispatchDraw(Canvas canvas) {
|
||||||
|
for (int i = 0; i < getChildCount(); i++) {
|
||||||
|
View child = getChildAt(i);
|
||||||
|
if (child instanceof ChatMessageCell) {
|
||||||
|
ChatMessageCell cell = (ChatMessageCell) child;
|
||||||
|
cell.setParentViewSize(chatPreviewContainer.getMeasuredWidth(), chatPreviewContainer.getBackgroundSizeY());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawChatBackgroundElements(canvas);
|
||||||
|
super.dispatchDraw(canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
|
super.onLayout(changed, l, t, r, b);
|
||||||
|
updatePositions();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawChatBackgroundElements(Canvas canvas) {
|
||||||
|
int count = getChildCount();
|
||||||
|
MessageObject.GroupedMessages lastDrawnGroup = null;
|
||||||
|
|
||||||
|
for (int a = 0; a < count; a++) {
|
||||||
|
View child = getChildAt(a);
|
||||||
|
if (child instanceof ChatMessageCell) {
|
||||||
|
ChatMessageCell cell = (ChatMessageCell) child;
|
||||||
|
MessageObject.GroupedMessages group = cell.getCurrentMessagesGroup();
|
||||||
|
if (group != null && group == lastDrawnGroup) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lastDrawnGroup = group;
|
||||||
|
MessageObject.GroupedMessagePosition position = cell.getCurrentPosition();
|
||||||
|
MessageBackgroundDrawable backgroundDrawable = cell.getBackgroundDrawable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MessageObject.GroupedMessages scrimGroup = null;
|
||||||
|
for (int k = 0; k < 3; k++) {
|
||||||
|
drawingGroups.clear();
|
||||||
|
if (k == 2 && !chatListView.isFastScrollAnimationRunning()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
View child = chatListView.getChildAt(i);
|
||||||
|
if (child instanceof ChatMessageCell) {
|
||||||
|
ChatMessageCell cell = (ChatMessageCell) child;
|
||||||
|
if (child.getY() > chatListView.getHeight() || child.getY() + child.getHeight() < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MessageObject.GroupedMessages group = cell.getCurrentMessagesGroup();
|
||||||
|
if (group == null || (k == 0 && group.messages.size() == 1) || (k == 1 && !group.transitionParams.drawBackgroundForDeletedItems)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((k == 0 && cell.getMessageObject().deleted) || (k == 1 && !cell.getMessageObject().deleted)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((k == 2 && !cell.willRemovedAfterAnimation()) || (k != 2 && cell.willRemovedAfterAnimation())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drawingGroups.contains(group)) {
|
||||||
|
group.transitionParams.left = 0;
|
||||||
|
group.transitionParams.top = 0;
|
||||||
|
group.transitionParams.right = 0;
|
||||||
|
group.transitionParams.bottom = 0;
|
||||||
|
|
||||||
|
group.transitionParams.pinnedBotton = false;
|
||||||
|
group.transitionParams.pinnedTop = false;
|
||||||
|
group.transitionParams.cell = cell;
|
||||||
|
drawingGroups.add(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
group.transitionParams.pinnedTop = cell.isPinnedTop();
|
||||||
|
group.transitionParams.pinnedBotton = cell.isPinnedBottom();
|
||||||
|
|
||||||
|
int left = (cell.getLeft() + cell.getBackgroundDrawableLeft());
|
||||||
|
int right = (cell.getLeft() + cell.getBackgroundDrawableRight());
|
||||||
|
int top = (cell.getTop() + cell.getBackgroundDrawableTop());
|
||||||
|
int bottom = (cell.getTop() + cell.getBackgroundDrawableBottom());
|
||||||
|
|
||||||
|
if ((cell.getCurrentPosition().flags & MessageObject.POSITION_FLAG_TOP) == 0) {
|
||||||
|
top -= AndroidUtilities.dp(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((cell.getCurrentPosition().flags & MessageObject.POSITION_FLAG_BOTTOM) == 0) {
|
||||||
|
bottom += AndroidUtilities.dp(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell.willRemovedAfterAnimation()) {
|
||||||
|
group.transitionParams.cell = cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.transitionParams.top == 0 || top < group.transitionParams.top) {
|
||||||
|
group.transitionParams.top = top;
|
||||||
|
}
|
||||||
|
if (group.transitionParams.bottom == 0 || bottom > group.transitionParams.bottom) {
|
||||||
|
group.transitionParams.bottom = bottom;
|
||||||
|
}
|
||||||
|
if (group.transitionParams.left == 0 || left < group.transitionParams.left) {
|
||||||
|
group.transitionParams.left = left;
|
||||||
|
}
|
||||||
|
if (group.transitionParams.right == 0 || right > group.transitionParams.right) {
|
||||||
|
group.transitionParams.right = right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < drawingGroups.size(); i++) {
|
||||||
|
MessageObject.GroupedMessages group = drawingGroups.get(i);
|
||||||
|
if (group == scrimGroup) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
float x = group.transitionParams.cell.getNonAnimationTranslationX(true);
|
||||||
|
float l = (group.transitionParams.left + x + group.transitionParams.offsetLeft);
|
||||||
|
float t = (group.transitionParams.top + group.transitionParams.offsetTop);
|
||||||
|
float r = (group.transitionParams.right + x + group.transitionParams.offsetRight);
|
||||||
|
float b = (group.transitionParams.bottom + group.transitionParams.offsetBottom);
|
||||||
|
|
||||||
|
if (!group.transitionParams.backgroundChangeBounds) {
|
||||||
|
t += group.transitionParams.cell.getTranslationY();
|
||||||
|
b += group.transitionParams.cell.getTranslationY();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t < -AndroidUtilities.dp(20)) {
|
||||||
|
t = -AndroidUtilities.dp(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (b > chatListView.getMeasuredHeight() + AndroidUtilities.dp(20)) {
|
||||||
|
b = chatListView.getMeasuredHeight() + AndroidUtilities.dp(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean useScale = group.transitionParams.cell.getScaleX() != 1f || group.transitionParams.cell.getScaleY() != 1f;
|
||||||
|
if (useScale) {
|
||||||
|
canvas.save();
|
||||||
|
canvas.scale(group.transitionParams.cell.getScaleX(), group.transitionParams.cell.getScaleY(), l + (r - l) / 2, t + (b - t) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
group.transitionParams.cell.drawBackground(canvas, (int) l, (int) t, (int) r, (int) b, group.transitionParams.pinnedTop, group.transitionParams.pinnedBotton, false, 0);
|
||||||
|
group.transitionParams.cell = null;
|
||||||
|
group.transitionParams.drawCaptionLayout = group.hasCaption;
|
||||||
|
if (useScale) {
|
||||||
|
canvas.restore();
|
||||||
|
for (int ii = 0; ii < count; ii++) {
|
||||||
|
View child = chatListView.getChildAt(ii);
|
||||||
|
if (child instanceof ChatMessageCell && ((ChatMessageCell) child).getCurrentMessagesGroup() == group) {
|
||||||
|
ChatMessageCell cell = ((ChatMessageCell) child);
|
||||||
|
int left = cell.getLeft();
|
||||||
|
int top = cell.getTop();
|
||||||
|
child.setPivotX(l - left + (r - l) / 2);
|
||||||
|
child.setPivotY(t - top + (b - t) / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
chatListView.setItemAnimator(itemAnimator = new ChatListItemAnimator(null, chatListView) {
|
||||||
|
|
||||||
|
int scrollAnimationIndex = -1;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationStart() {
|
||||||
|
super.onAnimationStart();
|
||||||
|
AndroidUtilities.cancelRunOnUIThread(changeBoundsRunnable);
|
||||||
|
changeBoundsRunnable.run();
|
||||||
|
|
||||||
|
if (scrollAnimationIndex == -1) {
|
||||||
|
scrollAnimationIndex = NotificationCenter.getInstance(currentAccount).setAnimationInProgress(scrollAnimationIndex, null, false);
|
||||||
|
}
|
||||||
|
if (finishRunnable != null) {
|
||||||
|
AndroidUtilities.cancelRunOnUIThread(finishRunnable);
|
||||||
|
finishRunnable = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Runnable finishRunnable;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAllAnimationsDone() {
|
||||||
|
super.onAllAnimationsDone();
|
||||||
|
if (finishRunnable != null) {
|
||||||
|
AndroidUtilities.cancelRunOnUIThread(finishRunnable);
|
||||||
|
}
|
||||||
|
AndroidUtilities.runOnUIThread(finishRunnable = () -> {
|
||||||
|
if (scrollAnimationIndex != -1) {
|
||||||
|
NotificationCenter.getInstance(currentAccount).onAnimationFinish(scrollAnimationIndex);
|
||||||
|
scrollAnimationIndex = -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updateAfterAnimations) {
|
||||||
|
updateAfterAnimations = false;
|
||||||
|
AndroidUtilities.runOnUIThread(() -> updateMessages());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endAnimations() {
|
||||||
|
super.endAnimations();
|
||||||
|
if (finishRunnable != null) {
|
||||||
|
AndroidUtilities.cancelRunOnUIThread(finishRunnable);
|
||||||
|
}
|
||||||
|
AndroidUtilities.runOnUIThread(finishRunnable = () -> {
|
||||||
|
if (scrollAnimationIndex != -1) {
|
||||||
|
NotificationCenter.getInstance(currentAccount).onAnimationFinish(scrollAnimationIndex);
|
||||||
|
scrollAnimationIndex = -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chatListView.setOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
|
@Override
|
||||||
|
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy);
|
||||||
|
|
||||||
|
for (int i = 0; i < chatListView.getChildCount(); i++) {
|
||||||
|
ChatMessageCell cell = (ChatMessageCell) chatListView.getChildAt(i);
|
||||||
|
cell.setParentViewSize(chatPreviewContainer.getMeasuredWidth(), chatPreviewContainer.getBackgroundSizeY());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chatListView.setOnItemClickListener(new RecyclerListView.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(View view, int position) {
|
||||||
|
if (forwardingMessagesParams.previewMessages.size() <= 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int id = params.previewMessages.get(position).getId();
|
||||||
|
boolean newSelected = !params.selectedIds.get(id, false);
|
||||||
|
if (forwardingMessagesParams.selectedIds.size() == 1 && !newSelected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!newSelected) {
|
||||||
|
params.selectedIds.delete(id);
|
||||||
|
} else {
|
||||||
|
params.selectedIds.put(id, newSelected);
|
||||||
|
}
|
||||||
|
ChatMessageCell chatMessageCell = (ChatMessageCell) view;
|
||||||
|
chatMessageCell.setChecked(newSelected, newSelected, true);
|
||||||
|
actionBar.setTitle(LocaleController.formatPluralString("PreviewForwardMessagesCount", params.selectedIds.size()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
chatListView.setAdapter(adapter = new Adapter());
|
||||||
|
chatListView.setPadding(0, AndroidUtilities.dp(4), 0, AndroidUtilities.dp(4));
|
||||||
|
chatLayoutManager = new GridLayoutManagerFixed(context, 1000, LinearLayoutManager.VERTICAL, true) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldLayoutChildFromOpositeSide(View child) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean hasSiblingChild(int position) {
|
||||||
|
MessageObject message = params.previewMessages.get(position);
|
||||||
|
MessageObject.GroupedMessages group = getValidGroupedMessage(message);
|
||||||
|
if (group != null) {
|
||||||
|
MessageObject.GroupedMessagePosition pos = group.positions.get(message);
|
||||||
|
if (pos.minX == pos.maxX || pos.minY != pos.maxY || pos.minY == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int count = group.posArray.size();
|
||||||
|
for (int a = 0; a < count; a++) {
|
||||||
|
MessageObject.GroupedMessagePosition p = group.posArray.get(a);
|
||||||
|
if (p == pos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (p.minY <= pos.minY && p.maxY >= pos.minY) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
|
||||||
|
if (BuildVars.DEBUG_PRIVATE_VERSION) {
|
||||||
|
super.onLayoutChildren(recycler, state);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
super.onLayoutChildren(recycler, state);
|
||||||
|
} catch (Exception e) {
|
||||||
|
FileLog.e(e);
|
||||||
|
AndroidUtilities.runOnUIThread(() -> adapter.notifyDataSetChanged());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
chatLayoutManager.setSpanSizeLookup(new GridLayoutManagerFixed.SpanSizeLookup() {
|
||||||
|
@Override
|
||||||
|
public int getSpanSize(int position) {
|
||||||
|
int idx = position;
|
||||||
|
if (idx >= 0 && idx < params.previewMessages.size()) {
|
||||||
|
MessageObject message = params.previewMessages.get(idx);
|
||||||
|
MessageObject.GroupedMessages groupedMessages = getValidGroupedMessage(message);
|
||||||
|
if (groupedMessages != null) {
|
||||||
|
return groupedMessages.positions.get(message).spanSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 1000;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
chatListView.setClipToPadding(false);
|
||||||
|
chatListView.setLayoutManager(chatLayoutManager);
|
||||||
|
chatListView.addItemDecoration(new RecyclerView.ItemDecoration() {
|
||||||
|
@Override
|
||||||
|
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
|
||||||
|
outRect.bottom = 0;
|
||||||
|
if (view instanceof ChatMessageCell) {
|
||||||
|
ChatMessageCell cell = (ChatMessageCell) view;
|
||||||
|
MessageObject.GroupedMessages group = cell.getCurrentMessagesGroup();
|
||||||
|
if (group != null) {
|
||||||
|
MessageObject.GroupedMessagePosition position = cell.getCurrentPosition();
|
||||||
|
if (position != null && position.siblingHeights != null) {
|
||||||
|
float maxHeight = Math.max(AndroidUtilities.displaySize.x, AndroidUtilities.displaySize.y) * 0.5f;
|
||||||
|
int h = cell.getExtraInsetHeight();
|
||||||
|
for (int a = 0; a < position.siblingHeights.length; a++) {
|
||||||
|
h += (int) Math.ceil(maxHeight * position.siblingHeights[a]);
|
||||||
|
}
|
||||||
|
h += (position.maxY - position.minY) * Math.round(7 * AndroidUtilities.density);
|
||||||
|
int count = group.posArray.size();
|
||||||
|
for (int a = 0; a < count; a++) {
|
||||||
|
MessageObject.GroupedMessagePosition pos = group.posArray.get(a);
|
||||||
|
if (pos.minY != position.minY || pos.minX == position.minX && pos.maxX == position.maxX && pos.minY == position.minY && pos.maxY == position.maxY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pos.minY == position.minY) {
|
||||||
|
h -= (int) Math.ceil(maxHeight * pos.ph) - AndroidUtilities.dp(4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outRect.bottom = -h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
chatPreviewContainer.addView(chatListView);
|
||||||
|
addView(chatPreviewContainer, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 400, 0, 8, 0, 8, 0));
|
||||||
|
chatPreviewContainer.addView(actionBar, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
|
||||||
|
|
||||||
|
menuScrollView = new ScrollView(context);
|
||||||
|
addView(menuScrollView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
|
||||||
|
|
||||||
|
menuContainer = new LinearLayout(context);
|
||||||
|
menuContainer.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
menuScrollView.addView(menuContainer);
|
||||||
|
|
||||||
|
buttonsLayout = new LinearLayout(context);
|
||||||
|
buttonsLayout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
Drawable shadowDrawable = getContext().getResources().getDrawable(R.drawable.popup_fixed_alert).mutate();
|
||||||
|
shadowDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogBackground), PorterDuff.Mode.MULTIPLY));
|
||||||
|
buttonsLayout.setBackground(shadowDrawable);
|
||||||
|
menuContainer.addView(buttonsLayout, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
|
||||||
|
|
||||||
|
showSendersNameView = new ActionBarMenuSubItem(context, true, true, false);
|
||||||
|
buttonsLayout.addView(showSendersNameView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48));
|
||||||
|
showSendersNameView.setTextAndIcon(forwardingMessagesParams.multiplyUsers ? LocaleController.getString("ShowSenderNames", R.string.ShowSenderNames) : LocaleController.getString("ShowSendersName", R.string.ShowSendersName), 0);
|
||||||
|
showSendersNameView.setChecked(true);
|
||||||
|
|
||||||
|
hideSendersNameView = new ActionBarMenuSubItem(context, true, false, !params.hasCaption);
|
||||||
|
buttonsLayout.addView(hideSendersNameView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48));
|
||||||
|
hideSendersNameView.setTextAndIcon(forwardingMessagesParams.multiplyUsers ? LocaleController.getString("HideSenderNames", R.string.HideSenderNames) : LocaleController.getString("HideSendersName", R.string.HideSendersName), 0);
|
||||||
|
hideSendersNameView.setChecked(false);
|
||||||
|
|
||||||
|
if (forwardingMessagesParams.hasCaption) {
|
||||||
|
View dividerView = new View(context) {
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(2, MeasureSpec.EXACTLY));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
dividerView.setBackgroundColor(Theme.getColor(Theme.key_divider));
|
||||||
|
buttonsLayout.addView(dividerView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT));
|
||||||
|
|
||||||
|
|
||||||
|
showCaptionView = new ActionBarMenuSubItem(context, true, false, false);
|
||||||
|
buttonsLayout.addView(showCaptionView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48));
|
||||||
|
showCaptionView.setTextAndIcon(LocaleController.getString("ShowCaption", R.string.ShowCaption), 0);
|
||||||
|
showCaptionView.setChecked(true);
|
||||||
|
|
||||||
|
hideCaptionView = new ActionBarMenuSubItem(context, true, false, true);
|
||||||
|
buttonsLayout.addView(hideCaptionView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48));
|
||||||
|
hideCaptionView.setTextAndIcon(LocaleController.getString("HideCaption", R.string.HideCaption), 0);
|
||||||
|
hideCaptionView.setChecked(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
buttonsLayout2 = new LinearLayout(context);
|
||||||
|
buttonsLayout2.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
shadowDrawable = getContext().getResources().getDrawable(R.drawable.popup_fixed_alert).mutate();
|
||||||
|
shadowDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_dialogBackground), PorterDuff.Mode.MULTIPLY));
|
||||||
|
buttonsLayout2.setBackground(shadowDrawable);
|
||||||
|
menuContainer.addView(buttonsLayout2, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.WRAP_CONTENT, 0, 0, forwardingMessagesParams.hasSenders ? -8 : 0, 0, 0));
|
||||||
|
|
||||||
|
changeRecipientView = new ActionBarMenuSubItem(context, true, false);
|
||||||
|
buttonsLayout2.addView(changeRecipientView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48));
|
||||||
|
changeRecipientView.setTextAndIcon(LocaleController.getString("ChangeRecipient", R.string.ChangeRecipient), R.drawable.msg_forward_replace);
|
||||||
|
|
||||||
|
sendMessagesView = new ActionBarMenuSubItem(context, false, true);
|
||||||
|
buttonsLayout2.addView(sendMessagesView, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48));
|
||||||
|
sendMessagesView.setTextAndIcon(LocaleController.getString("ForwardSendMessages", R.string.ForwardSendMessages), R.drawable.msg_forward_send);
|
||||||
|
|
||||||
|
if (forwardingMessagesParams.hasSenders) {
|
||||||
|
actionItems.add(showSendersNameView);
|
||||||
|
actionItems.add(hideSendersNameView);
|
||||||
|
|
||||||
|
if (params.hasCaption) {
|
||||||
|
actionItems.add(showCaptionView);
|
||||||
|
actionItems.add(hideCaptionView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actionItems.add(changeRecipientView);
|
||||||
|
actionItems.add(sendMessagesView);
|
||||||
|
|
||||||
|
showSendersNameView.setOnClickListener(view -> {
|
||||||
|
if (params.hideForwardSendersName) {
|
||||||
|
returnSendersNames = false;
|
||||||
|
showSendersNameView.setChecked(true);
|
||||||
|
hideSendersNameView.setChecked(false);
|
||||||
|
if (showCaptionView != null) {
|
||||||
|
showCaptionView.setChecked(true);
|
||||||
|
hideCaptionView.setChecked(false);
|
||||||
|
}
|
||||||
|
params.hideForwardSendersName = false;
|
||||||
|
params.hideCaption = false;
|
||||||
|
updateMessages();
|
||||||
|
updateSubtitle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
hideSendersNameView.setOnClickListener(view -> {
|
||||||
|
if (!params.hideForwardSendersName) {
|
||||||
|
returnSendersNames = false;
|
||||||
|
showSendersNameView.setChecked(false);
|
||||||
|
hideSendersNameView.setChecked(true);
|
||||||
|
params.hideForwardSendersName = true;
|
||||||
|
updateMessages();
|
||||||
|
updateSubtitle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (params.hasCaption) {
|
||||||
|
showCaptionView.setOnClickListener(view -> {
|
||||||
|
if (params.hideCaption) {
|
||||||
|
if (returnSendersNames) {
|
||||||
|
params.hideForwardSendersName = false;
|
||||||
|
}
|
||||||
|
returnSendersNames = false;
|
||||||
|
showCaptionView.setChecked(true);
|
||||||
|
hideCaptionView.setChecked(false);
|
||||||
|
showSendersNameView.setChecked(!params.hideForwardSendersName);
|
||||||
|
hideSendersNameView.setChecked(params.hideForwardSendersName);
|
||||||
|
params.hideCaption = false;
|
||||||
|
updateMessages();
|
||||||
|
updateSubtitle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
hideCaptionView.setOnClickListener(view -> {
|
||||||
|
if (!params.hideCaption) {
|
||||||
|
showCaptionView.setChecked(false);
|
||||||
|
hideCaptionView.setChecked(true);
|
||||||
|
showSendersNameView.setChecked(false);
|
||||||
|
hideSendersNameView.setChecked(true);
|
||||||
|
if (!params.hideForwardSendersName) {
|
||||||
|
params.hideForwardSendersName = true;
|
||||||
|
returnSendersNames = true;
|
||||||
|
}
|
||||||
|
params.hideCaption = true;
|
||||||
|
updateMessages();
|
||||||
|
updateSubtitle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showSendersNameView.setChecked(!params.hideForwardSendersName);
|
||||||
|
hideSendersNameView.setChecked(params.hideForwardSendersName);
|
||||||
|
|
||||||
|
if (params.hasCaption) {
|
||||||
|
showCaptionView.setChecked(!params.hideCaption);
|
||||||
|
hideCaptionView.setChecked(params.hideCaption);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.hasSenders) {
|
||||||
|
buttonsLayout.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessagesView.setOnClickListener(View -> didSendPressed());
|
||||||
|
changeRecipientView.setOnClickListener(view -> selectAnotherChat());
|
||||||
|
|
||||||
|
updateMessages();
|
||||||
|
updateSubtitle();
|
||||||
|
actionBar.setTitle(LocaleController.formatPluralString("PreviewForwardMessagesCount", params.selectedIds.size()));
|
||||||
|
|
||||||
|
menuScrollView.setOnTouchListener((view, motionEvent) -> {
|
||||||
|
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
|
||||||
|
dismiss(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
setOnTouchListener((view, motionEvent) -> {
|
||||||
|
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
|
||||||
|
dismiss(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
showing = true;
|
||||||
|
setAlpha(0);
|
||||||
|
setScaleX(0.95f);
|
||||||
|
setScaleY(0.95f);
|
||||||
|
animate().alpha(1f).scaleX(1f).setDuration(ChatListItemAnimator.DEFAULT_DURATION).setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR).scaleY(1f);
|
||||||
|
|
||||||
|
updateColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSubtitle() {
|
||||||
|
if (!forwardingMessagesParams.hasSenders) {
|
||||||
|
if (forwardingMessagesParams.willSeeSenders) {
|
||||||
|
if (currentUser != null) {
|
||||||
|
actionBar.setSubtitle(LocaleController.formatString("ForwardPreviewSendersNameVisible", R.string.ForwardPreviewSendersNameVisible, ContactsController.formatName(currentUser.first_name, currentUser.last_name)));
|
||||||
|
} else {
|
||||||
|
if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) {
|
||||||
|
actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameVisibleChannel", R.string.ForwardPreviewSendersNameVisibleChannel));
|
||||||
|
} else {
|
||||||
|
actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameVisibleGroup", R.string.ForwardPreviewSendersNameVisibleGroup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentUser != null) {
|
||||||
|
actionBar.setSubtitle(LocaleController.formatString("ForwardPreviewSendersNameVisible", R.string.ForwardPreviewSendersNameVisible, ContactsController.formatName(currentUser.first_name, currentUser.last_name)));
|
||||||
|
} else {
|
||||||
|
if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) {
|
||||||
|
actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameHiddenChannel", R.string.ForwardPreviewSendersNameHiddenChannel));
|
||||||
|
} else {
|
||||||
|
actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameHiddenGroup", R.string.ForwardPreviewSendersNameHiddenGroup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (!forwardingMessagesParams.hideForwardSendersName) {
|
||||||
|
if (currentUser != null) {
|
||||||
|
actionBar.setSubtitle(LocaleController.formatString("ForwardPreviewSendersNameVisible", R.string.ForwardPreviewSendersNameVisible, ContactsController.formatName(currentUser.first_name, currentUser.last_name)));
|
||||||
|
} else {
|
||||||
|
if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) {
|
||||||
|
actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameVisibleChannel", R.string.ForwardPreviewSendersNameVisibleChannel));
|
||||||
|
} else {
|
||||||
|
actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameVisibleGroup", R.string.ForwardPreviewSendersNameVisibleGroup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentUser != null) {
|
||||||
|
actionBar.setSubtitle(LocaleController.formatString("ForwardPreviewSendersNameHidden", R.string.ForwardPreviewSendersNameHidden, ContactsController.formatName(currentUser.first_name, currentUser.last_name)));
|
||||||
|
} else {
|
||||||
|
if (ChatObject.isChannel(currentChat) && !currentChat.megagroup) {
|
||||||
|
actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameHiddenChannel", R.string.ForwardPreviewSendersNameHiddenChannel));
|
||||||
|
} else {
|
||||||
|
actionBar.setSubtitle(LocaleController.getString("ForwardPreviewSendersNameHiddenGroup", R.string.ForwardPreviewSendersNameHiddenGroup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateColors() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dismiss(boolean canShowKeyboard) {
|
||||||
|
if (showing) {
|
||||||
|
showing = false;
|
||||||
|
animate().alpha(0).scaleX(0.95f).scaleY(0.95f).setDuration(ChatListItemAnimator.DEFAULT_DURATION).setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR).setListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
if (getParent() != null) {
|
||||||
|
ViewGroup parent = (ViewGroup) getParent();
|
||||||
|
parent.removeView(ForwardingPreviewView.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
onDismiss(canShowKeyboard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onDismiss(boolean canShowKeyboard) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean updateAfterAnimations;
|
||||||
|
|
||||||
|
private void updateMessages() {
|
||||||
|
if (itemAnimator.isRunning()) {
|
||||||
|
updateAfterAnimations = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < forwardingMessagesParams.previewMessages.size(); i++) {
|
||||||
|
MessageObject messageObject = forwardingMessagesParams.previewMessages.get(i);
|
||||||
|
messageObject.forceUpdate = true;
|
||||||
|
if (!forwardingMessagesParams.hideForwardSendersName) {
|
||||||
|
messageObject.messageOwner.flags |= TLRPC.MESSAGE_FLAG_FWD;
|
||||||
|
} else {
|
||||||
|
messageObject.messageOwner.flags &= ~TLRPC.MESSAGE_FLAG_FWD;
|
||||||
|
}
|
||||||
|
if (forwardingMessagesParams.hideCaption) {
|
||||||
|
messageObject.caption = null;
|
||||||
|
} else {
|
||||||
|
messageObject.generateCaption();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messageObject.isPoll()) {
|
||||||
|
ForwardingMessagesParams.PreviewMediaPoll mediaPoll = (ForwardingMessagesParams.PreviewMediaPoll) messageObject.messageOwner.media;
|
||||||
|
mediaPoll.results.total_voters = forwardingMessagesParams.hideCaption ? 0 : mediaPoll.totalVotersCached;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < forwardingMessagesParams.pollChoosenAnswers.size(); i++) {
|
||||||
|
forwardingMessagesParams.pollChoosenAnswers.get(i).chosen = !forwardingMessagesParams.hideForwardSendersName;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < forwardingMessagesParams.groupedMessagesMap.size(); i++) {
|
||||||
|
itemAnimator.groupWillChanged(forwardingMessagesParams.groupedMessagesMap.valueAt(i));
|
||||||
|
}
|
||||||
|
adapter.notifyItemRangeChanged(0, forwardingMessagesParams.previewMessages.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
int maxActionWidth = 0;
|
||||||
|
isLandscapeMode = MeasureSpec.getSize(widthMeasureSpec) > MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
|
||||||
|
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
if (isLandscapeMode) {
|
||||||
|
width = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.38f);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < actionItems.size(); i++) {
|
||||||
|
actionItems.get(i).measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED));
|
||||||
|
if (actionItems.get(i).getMeasuredWidth() > maxActionWidth) {
|
||||||
|
maxActionWidth = actionItems.get(i).getMeasuredWidth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buttonsLayout.getBackground().getPadding(rect);
|
||||||
|
int buttonsWidth = maxActionWidth + rect.left + rect.right;
|
||||||
|
buttonsLayout.getLayoutParams().width = buttonsWidth;
|
||||||
|
buttonsLayout2.getLayoutParams().width = buttonsWidth;
|
||||||
|
|
||||||
|
buttonsLayout.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED));
|
||||||
|
buttonsLayout2.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED));
|
||||||
|
|
||||||
|
((MarginLayoutParams) chatListView.getLayoutParams()).topMargin = ActionBar.getCurrentActionBarHeight();
|
||||||
|
if (isLandscapeMode) {
|
||||||
|
chatPreviewContainer.getLayoutParams().height = LayoutHelper.MATCH_PARENT;
|
||||||
|
((MarginLayoutParams) chatPreviewContainer.getLayoutParams()).topMargin = AndroidUtilities.dp(8);
|
||||||
|
((MarginLayoutParams) chatPreviewContainer.getLayoutParams()).bottomMargin = AndroidUtilities.dp(8);
|
||||||
|
chatPreviewContainer.getLayoutParams().width = (int) Math.min(MeasureSpec.getSize(widthMeasureSpec), Math.max(AndroidUtilities.dp(340), MeasureSpec.getSize(widthMeasureSpec) * 0.6f));
|
||||||
|
menuScrollView.getLayoutParams().height = LayoutHelper.MATCH_PARENT;
|
||||||
|
} else {
|
||||||
|
((MarginLayoutParams) chatPreviewContainer.getLayoutParams()).topMargin = 0;
|
||||||
|
((MarginLayoutParams) chatPreviewContainer.getLayoutParams()).bottomMargin = 0;
|
||||||
|
chatPreviewContainer.getLayoutParams().height = MeasureSpec.getSize(heightMeasureSpec) - AndroidUtilities.dp(16 - 10) - buttonsLayout.getMeasuredHeight() - buttonsLayout2.getMeasuredHeight();
|
||||||
|
if (chatPreviewContainer.getLayoutParams().height < MeasureSpec.getSize(heightMeasureSpec) * 0.5f) {
|
||||||
|
chatPreviewContainer.getLayoutParams().height = (int) (MeasureSpec.getSize(heightMeasureSpec) * 0.5f);
|
||||||
|
}
|
||||||
|
chatPreviewContainer.getLayoutParams().width = LayoutHelper.MATCH_PARENT;
|
||||||
|
menuScrollView.getLayoutParams().height = MeasureSpec.getSize(heightMeasureSpec) - chatPreviewContainer.getLayoutParams().height;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = MeasureSpec.getSize(widthMeasureSpec) + MeasureSpec.getSize(heightMeasureSpec) << 16;
|
||||||
|
if (lastSize != size) {
|
||||||
|
for (int i = 0; i < forwardingMessagesParams.previewMessages.size(); i++) {
|
||||||
|
if (isLandscapeMode) {
|
||||||
|
forwardingMessagesParams.previewMessages.get(i).parentWidth = chatPreviewContainer.getLayoutParams().width;
|
||||||
|
} else {
|
||||||
|
forwardingMessagesParams.previewMessages.get(i).parentWidth = MeasureSpec.getSize(widthMeasureSpec) - AndroidUtilities.dp(16);
|
||||||
|
}
|
||||||
|
forwardingMessagesParams.previewMessages.get(i).resetLayout();
|
||||||
|
forwardingMessagesParams.previewMessages.get(i).forceUpdate = true;
|
||||||
|
if (adapter != null) {
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
firstLayout = true;
|
||||||
|
}
|
||||||
|
lastSize = size;
|
||||||
|
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
int lastSize;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||||
|
super.onLayout(changed, left, top, right, bottom);
|
||||||
|
updatePositions();
|
||||||
|
firstLayout = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void updatePositions() {
|
||||||
|
int lastTopOffset = chatTopOffset;
|
||||||
|
float lastYOffset = yOffset;
|
||||||
|
|
||||||
|
if (!isLandscapeMode) {
|
||||||
|
if (chatListView.getChildCount() == 0 || chatListView.getChildCount() > forwardingMessagesParams.previewMessages.size()) {
|
||||||
|
chatTopOffset = 0;
|
||||||
|
} else {
|
||||||
|
int minTop = chatListView.getChildAt(0).getTop();
|
||||||
|
for (int i = 1; i < chatListView.getChildCount(); i++) {
|
||||||
|
if (chatListView.getChildAt(i).getTop() < minTop) {
|
||||||
|
minTop = chatListView.getChildAt(i).getTop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
minTop -= AndroidUtilities.dp(4);
|
||||||
|
if (minTop < 0) {
|
||||||
|
chatTopOffset = 0;
|
||||||
|
} else {
|
||||||
|
chatTopOffset = minTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float totalViewsHeight = buttonsLayout.getMeasuredHeight() + buttonsLayout2.getMeasuredHeight() - AndroidUtilities.dp(8) + (chatPreviewContainer.getMeasuredHeight() - chatTopOffset);
|
||||||
|
float totalHeight = getMeasuredHeight() - AndroidUtilities.dp(16);
|
||||||
|
yOffset = AndroidUtilities.dp(8) + (totalHeight - totalViewsHeight) / 2 - chatTopOffset;
|
||||||
|
if (yOffset > AndroidUtilities.dp(8)) {
|
||||||
|
yOffset = AndroidUtilities.dp(8);
|
||||||
|
}
|
||||||
|
float buttonX = getMeasuredWidth() - menuScrollView.getMeasuredWidth();
|
||||||
|
menuScrollView.setTranslationX(buttonX);
|
||||||
|
} else {
|
||||||
|
yOffset = 0;
|
||||||
|
chatTopOffset = 0;
|
||||||
|
menuScrollView.setTranslationX(chatListView.getMeasuredWidth() + AndroidUtilities.dp(8));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!firstLayout && (chatTopOffset != lastTopOffset || yOffset != lastYOffset)) {
|
||||||
|
if (offsetsAnimator != null) {
|
||||||
|
offsetsAnimator.cancel();
|
||||||
|
}
|
||||||
|
offsetsAnimator = ValueAnimator.ofFloat(0, 1f);
|
||||||
|
offsetsAnimator.addUpdateListener(valueAnimator -> {
|
||||||
|
float p = (float) valueAnimator.getAnimatedValue();
|
||||||
|
currentTopOffset = (int) (lastTopOffset * (1f - p) + chatTopOffset * p);
|
||||||
|
currentYOffset = lastYOffset * (1f - p) + yOffset * p;
|
||||||
|
setOffset(currentYOffset, currentTopOffset);
|
||||||
|
});
|
||||||
|
offsetsAnimator.setDuration(ChatListItemAnimator.DEFAULT_DURATION);
|
||||||
|
offsetsAnimator.setInterpolator(ChatListItemAnimator.DEFAULT_INTERPOLATOR);
|
||||||
|
offsetsAnimator.addListener(new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
offsetsAnimator = null;
|
||||||
|
setOffset(yOffset, chatTopOffset);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AndroidUtilities.runOnUIThread(changeBoundsRunnable, 50);
|
||||||
|
|
||||||
|
currentTopOffset = lastTopOffset;
|
||||||
|
currentYOffset = lastYOffset;
|
||||||
|
setOffset(lastYOffset, lastTopOffset);
|
||||||
|
} else if (firstLayout) {
|
||||||
|
setOffset(currentYOffset = yOffset, currentTopOffset = chatTopOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setOffset(float yOffset, int chatTopOffset) {
|
||||||
|
if (isLandscapeMode) {
|
||||||
|
actionBar.setTranslationY(0);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
chatPreviewContainer.invalidateOutline();
|
||||||
|
}
|
||||||
|
chatPreviewContainer.setTranslationY(0);
|
||||||
|
menuScrollView.setTranslationY(0);
|
||||||
|
} else {
|
||||||
|
actionBar.setTranslationY(chatTopOffset);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
chatPreviewContainer.invalidateOutline();
|
||||||
|
}
|
||||||
|
chatPreviewContainer.setTranslationY(yOffset);
|
||||||
|
menuScrollView.setTranslationY(yOffset + chatPreviewContainer.getMeasuredHeight() - AndroidUtilities.dp(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isShowing() {
|
||||||
|
return showing;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Adapter extends RecyclerView.Adapter {
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
ChatMessageCell chatMessageCell = new ChatMessageCell(parent.getContext());
|
||||||
|
return new RecyclerListView.Holder(chatMessageCell);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
|
ChatMessageCell cell = (ChatMessageCell) holder.itemView;
|
||||||
|
cell.setParentViewSize(chatListView.getMeasuredWidth(), chatListView.getMeasuredHeight());
|
||||||
|
int id = cell.getMessageObject() != null ? cell.getMessageObject().getId() : 0;
|
||||||
|
cell.setMessageObject(forwardingMessagesParams.previewMessages.get(position), forwardingMessagesParams.groupedMessagesMap.get(forwardingMessagesParams.previewMessages.get(position).getGroupId()), true, true);
|
||||||
|
cell.setDelegate(new ChatMessageCell.ChatMessageCellDelegate() {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if (forwardingMessagesParams.previewMessages.size() > 1) {
|
||||||
|
cell.setCheckBoxVisible(true, false);
|
||||||
|
boolean animated = id == forwardingMessagesParams.previewMessages.get(position).getId();
|
||||||
|
boolean checked = forwardingMessagesParams.selectedIds.get(forwardingMessagesParams.previewMessages.get(position).getId(), false);
|
||||||
|
cell.setChecked(checked, checked, animated);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return forwardingMessagesParams.previewMessages.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void selectAnotherChat() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void didSendPressed() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private MessageObject.GroupedMessages getValidGroupedMessage(MessageObject message) {
|
||||||
|
MessageObject.GroupedMessages groupedMessages = null;
|
||||||
|
if (message.getGroupId() != 0) {
|
||||||
|
groupedMessages = forwardingMessagesParams.groupedMessagesMap.get(message.getGroupId());
|
||||||
|
if (groupedMessages != null && (groupedMessages.messages.size() <= 1 || groupedMessages.positions.get(message) == null)) {
|
||||||
|
groupedMessages = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return groupedMessages;
|
||||||
|
}
|
||||||
|
}
|
|
@ -122,7 +122,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent
|
||||||
private StaticLayout timeLayout;
|
private StaticLayout timeLayout;
|
||||||
private RectF rect = new RectF();
|
private RectF rect = new RectF();
|
||||||
private boolean scheduleRunnableScheduled;
|
private boolean scheduleRunnableScheduled;
|
||||||
private Runnable updateScheduleTimeRunnable = new Runnable() {
|
private final Runnable updateScheduleTimeRunnable = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (gradientTextPaint == null || !(fragment instanceof ChatActivity)) {
|
if (gradientTextPaint == null || !(fragment instanceof ChatActivity)) {
|
||||||
|
@ -1382,6 +1382,9 @@ public class FragmentContextView extends FrameLayout implements NotificationCent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
if (playbackSpeedButton != null && playbackSpeedButton.isSubMenuShowing()) {
|
||||||
|
playbackSpeedButton.toggleSubMenu();
|
||||||
|
}
|
||||||
visible = false;
|
visible = false;
|
||||||
if (create) {
|
if (create) {
|
||||||
if (getVisibility() != GONE) {
|
if (getVisibility() != GONE) {
|
||||||
|
@ -1802,6 +1805,7 @@ public class FragmentContextView extends FrameLayout implements NotificationCent
|
||||||
updateStyle(4);
|
updateStyle(4);
|
||||||
|
|
||||||
ChatObject.Call call = ((ChatActivity) fragment).getGroupCall();
|
ChatObject.Call call = ((ChatActivity) fragment).getGroupCall();
|
||||||
|
TLRPC.Chat chat = ((ChatActivity) fragment).getCurrentChat();
|
||||||
if (call.isScheduled()) {
|
if (call.isScheduled()) {
|
||||||
if (gradientPaint == null) {
|
if (gradientPaint == null) {
|
||||||
gradientTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
gradientTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
@ -1818,7 +1822,11 @@ public class FragmentContextView extends FrameLayout implements NotificationCent
|
||||||
if (!TextUtils.isEmpty(call.call.title)) {
|
if (!TextUtils.isEmpty(call.call.title)) {
|
||||||
titleTextView.setText(call.call.title, false);
|
titleTextView.setText(call.call.title, false);
|
||||||
} else {
|
} else {
|
||||||
titleTextView.setText(LocaleController.getString("VoipGroupScheduledVoiceChat", R.string.VoipGroupScheduledVoiceChat), false);
|
if (ChatObject.isChannelOrGiga(chat)) {
|
||||||
|
titleTextView.setText(LocaleController.getString("VoipChannelScheduledVoiceChat", R.string.VoipChannelScheduledVoiceChat), false);
|
||||||
|
} else {
|
||||||
|
titleTextView.setText(LocaleController.getString("VoipGroupScheduledVoiceChat", R.string.VoipGroupScheduledVoiceChat), false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
subtitleTextView.setText(LocaleController.formatStartsTime(call.call.schedule_date, 4), false);
|
subtitleTextView.setText(LocaleController.formatStartsTime(call.call.schedule_date, 4), false);
|
||||||
if (!scheduleRunnableScheduled) {
|
if (!scheduleRunnableScheduled) {
|
||||||
|
@ -1828,7 +1836,11 @@ public class FragmentContextView extends FrameLayout implements NotificationCent
|
||||||
} else {
|
} else {
|
||||||
timeLayout = null;
|
timeLayout = null;
|
||||||
joinButton.setVisibility(VISIBLE);
|
joinButton.setVisibility(VISIBLE);
|
||||||
titleTextView.setText(LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat), false);
|
if (ChatObject.isChannelOrGiga(chat)) {
|
||||||
|
titleTextView.setText(LocaleController.getString("VoipChannelVoiceChat", R.string.VoipChannelVoiceChat), false);
|
||||||
|
} else {
|
||||||
|
titleTextView.setText(LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat), false);
|
||||||
|
}
|
||||||
if (call.call.participants_count == 0) {
|
if (call.call.participants_count == 0) {
|
||||||
subtitleTextView.setText(LocaleController.getString("MembersTalkingNobody", R.string.MembersTalkingNobody), false);
|
subtitleTextView.setText(LocaleController.getString("MembersTalkingNobody", R.string.MembersTalkingNobody), false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -2080,7 +2092,12 @@ public class FragmentContextView extends FrameLayout implements NotificationCent
|
||||||
titleTextView.setText(service.groupCall.call.title, false);
|
titleTextView.setText(service.groupCall.call.title, false);
|
||||||
} else {
|
} else {
|
||||||
if (fragment instanceof ChatActivity && ((ChatActivity) fragment).getCurrentChat() != null && ((ChatActivity) fragment).getCurrentChat().id == service.getChat().id) {
|
if (fragment instanceof ChatActivity && ((ChatActivity) fragment).getCurrentChat() != null && ((ChatActivity) fragment).getCurrentChat().id == service.getChat().id) {
|
||||||
titleTextView.setText(LocaleController.getString("VoipGroupViewVoiceChat", R.string.VoipGroupViewVoiceChat), false);
|
TLRPC.Chat chat = ((ChatActivity) fragment).getCurrentChat();
|
||||||
|
if (ChatObject.isChannelOrGiga(chat)) {
|
||||||
|
titleTextView.setText(LocaleController.getString("VoipChannelViewVoiceChat", R.string.VoipChannelViewVoiceChat), false);
|
||||||
|
} else {
|
||||||
|
titleTextView.setText(LocaleController.getString("VoipGroupViewVoiceChat", R.string.VoipGroupViewVoiceChat), false);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
titleTextView.setText(service.getChat().title, false);
|
titleTextView.setText(service.getChat().title, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import android.widget.TextView;
|
||||||
import androidx.core.graphics.ColorUtils;
|
import androidx.core.graphics.ColorUtils;
|
||||||
|
|
||||||
import org.telegram.messenger.AndroidUtilities;
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
|
import org.telegram.messenger.ChatObject;
|
||||||
import org.telegram.messenger.FileLog;
|
import org.telegram.messenger.FileLog;
|
||||||
import org.telegram.messenger.ImageLocation;
|
import org.telegram.messenger.ImageLocation;
|
||||||
import org.telegram.messenger.LocaleController;
|
import org.telegram.messenger.LocaleController;
|
||||||
|
@ -75,7 +76,12 @@ public class GroupCallPipAlertView extends LinearLayout implements VoIPService.S
|
||||||
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
|
||||||
super.onInitializeAccessibilityNodeInfo(info);
|
super.onInitializeAccessibilityNodeInfo(info);
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, LocaleController.getString("VoipGroupOpenVoiceChat", R.string.VoipGroupOpenVoiceChat)));
|
VoIPService service = VoIPService.getSharedInstance();
|
||||||
|
if (service != null && ChatObject.isChannelOrGiga(service.getChat())) {
|
||||||
|
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, LocaleController.getString("VoipChannelOpenVoiceChat", R.string.VoipChannelOpenVoiceChat)));
|
||||||
|
} else {
|
||||||
|
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, LocaleController.getString("VoipGroupOpenVoiceChat", R.string.VoipGroupOpenVoiceChat)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -407,7 +407,13 @@ public class GroupCallPipButton extends FrameLayout implements NotificationCente
|
||||||
}
|
}
|
||||||
wavesEnter = showWaves ? 1f : 0f;
|
wavesEnter = showWaves ? 1f : 0f;
|
||||||
}
|
}
|
||||||
String contentDescription = LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat);
|
String contentDescription;
|
||||||
|
VoIPService voIPService = VoIPService.getSharedInstance();
|
||||||
|
if (voIPService != null && ChatObject.isChannelOrGiga(voIPService.getChat())) {
|
||||||
|
contentDescription = LocaleController.getString("VoipChannelVoiceChat", R.string.VoipChannelVoiceChat);
|
||||||
|
} else {
|
||||||
|
contentDescription = LocaleController.getString("VoipGroupVoiceChat", R.string.VoipGroupVoiceChat);
|
||||||
|
}
|
||||||
if (state == MUTE_BUTTON_STATE_UNMUTE) {
|
if (state == MUTE_BUTTON_STATE_UNMUTE) {
|
||||||
contentDescription += ", " + LocaleController.getString("VoipTapToMute", R.string.VoipTapToMute);
|
contentDescription += ", " + LocaleController.getString("VoipTapToMute", R.string.VoipTapToMute);
|
||||||
} else if (state == MUTE_BUTTON_STATE_RECONNECT) {
|
} else if (state == MUTE_BUTTON_STATE_RECONNECT) {
|
||||||
|
|
|
@ -0,0 +1,346 @@
|
||||||
|
package org.telegram.ui.Components;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.DataSetObserver;
|
||||||
|
import android.graphics.Canvas;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.LinearGradient;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.PorterDuffColorFilter;
|
||||||
|
import android.graphics.Shader;
|
||||||
|
import android.graphics.drawable.GradientDrawable;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.telegram.messenger.AndroidUtilities;
|
||||||
|
import org.telegram.messenger.ChatObject;
|
||||||
|
import org.telegram.messenger.LocaleController;
|
||||||
|
import org.telegram.messenger.R;
|
||||||
|
import org.telegram.messenger.SvgHelper;
|
||||||
|
import org.telegram.tgnet.TLRPC;
|
||||||
|
import org.telegram.ui.ActionBar.BottomSheet;
|
||||||
|
import org.telegram.ui.ActionBar.Theme;
|
||||||
|
|
||||||
|
import androidx.core.graphics.ColorUtils;
|
||||||
|
import androidx.viewpager.widget.PagerAdapter;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
|
public class GroupCallRecordAlert extends BottomSheet {
|
||||||
|
|
||||||
|
private ViewPager viewPager;
|
||||||
|
private TextView positiveButton;
|
||||||
|
private LinearLayout titlesLayout;
|
||||||
|
private TextView[] titles;
|
||||||
|
|
||||||
|
private float pageOffset;
|
||||||
|
private int currentPage;
|
||||||
|
|
||||||
|
public GroupCallRecordAlert(Context context, TLRPC.Chat chat) {
|
||||||
|
super(context, false);
|
||||||
|
|
||||||
|
int color = Theme.getColor(Theme.key_voipgroup_inviteMembersBackground);
|
||||||
|
shadowDrawable.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY));
|
||||||
|
|
||||||
|
containerView = new FrameLayout(context) {
|
||||||
|
|
||||||
|
boolean ignoreLayout;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
boolean isLandscape = View.MeasureSpec.getSize(widthMeasureSpec) > View.MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
ViewGroup.MarginLayoutParams marginLayoutParams = (ViewGroup.MarginLayoutParams) positiveButton.getLayoutParams();
|
||||||
|
if (isLandscape) {
|
||||||
|
marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = AndroidUtilities.dp(80);
|
||||||
|
} else {
|
||||||
|
marginLayoutParams.rightMargin = marginLayoutParams.leftMargin = AndroidUtilities.dp(16);
|
||||||
|
}
|
||||||
|
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
int padding = (width - AndroidUtilities.dp(200)) / 2;
|
||||||
|
viewPager.setPadding(padding, 0, padding, 0);
|
||||||
|
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(370), MeasureSpec.EXACTLY));
|
||||||
|
measureChildWithMargins(titlesLayout, View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), 0, View.MeasureSpec.makeMeasureSpec(AndroidUtilities.dp(64), View.MeasureSpec.EXACTLY), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||||
|
super.onLayout(changed, left, top, right, bottom);
|
||||||
|
updateTitlesLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestLayout() {
|
||||||
|
if (ignoreLayout) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.requestLayout();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
containerView.setWillNotDraw(false);
|
||||||
|
containerView.setClipChildren(false);
|
||||||
|
containerView.setBackgroundDrawable(shadowDrawable);
|
||||||
|
containerView.setPadding(backgroundPaddingLeft, 0, backgroundPaddingLeft, 0);
|
||||||
|
|
||||||
|
TextView titleTextView = new TextView(getContext());
|
||||||
|
if (ChatObject.isChannelOrGiga(chat)) {
|
||||||
|
titleTextView.setText(LocaleController.getString("VoipChannelRecordVoiceChat", R.string.VoipChannelRecordVoiceChat));
|
||||||
|
} else {
|
||||||
|
titleTextView.setText(LocaleController.getString("VoipRecordVoiceChat", R.string.VoipRecordVoiceChat));
|
||||||
|
}
|
||||||
|
titleTextView.setTextColor(0xffffffff);
|
||||||
|
titleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
|
||||||
|
titleTextView.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||||
|
titleTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP);
|
||||||
|
containerView.addView(titleTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 24, 29, 24, 0));
|
||||||
|
|
||||||
|
TextView infoTextView = new TextView(getContext());
|
||||||
|
infoTextView.setText(LocaleController.getString("VoipRecordVoiceChatInfo", R.string.VoipRecordVoiceChatInfo));
|
||||||
|
infoTextView.setTextColor(0xffffffff);
|
||||||
|
infoTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||||
|
infoTextView.setGravity((LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP);
|
||||||
|
containerView.addView(infoTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.TOP, 24, 62, 24, 0));
|
||||||
|
|
||||||
|
titles = new TextView[3];
|
||||||
|
|
||||||
|
viewPager = new ViewPager(context);
|
||||||
|
viewPager.setClipChildren(false);
|
||||||
|
viewPager.setOffscreenPageLimit(4);
|
||||||
|
viewPager.setClipToPadding(false);
|
||||||
|
AndroidUtilities.setViewPagerEdgeEffectColor(viewPager, 0x7f000000);
|
||||||
|
viewPager.setAdapter(new Adapter());
|
||||||
|
viewPager.setPageMargin(0);
|
||||||
|
containerView.addView(viewPager, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.CENTER_HORIZONTAL, 0, 100, 0, 130));
|
||||||
|
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
currentPage = position;
|
||||||
|
pageOffset = positionOffset;
|
||||||
|
updateTitlesLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int i) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrollStateChanged(int state) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
View leftView = new View(getContext());
|
||||||
|
leftView.setBackground(new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, new int[]{color, 0}));
|
||||||
|
containerView.addView(leftView, LayoutHelper.createFrame(120, LayoutHelper.MATCH_PARENT, Gravity.LEFT | Gravity.TOP, 0, 100, 0, 130));
|
||||||
|
|
||||||
|
View rightView = new View(getContext());
|
||||||
|
rightView.setBackground(new GradientDrawable(GradientDrawable.Orientation.LEFT_RIGHT, new int[]{0, color}));
|
||||||
|
containerView.addView(rightView, LayoutHelper.createFrame(120, LayoutHelper.MATCH_PARENT, Gravity.RIGHT | Gravity.TOP, 0, 100, 0, 130));
|
||||||
|
|
||||||
|
positiveButton = new TextView(getContext()) {
|
||||||
|
|
||||||
|
private Paint[] gradientPaint = new Paint[titles.length];
|
||||||
|
{
|
||||||
|
for (int a = 0; a < gradientPaint.length; a++) {
|
||||||
|
gradientPaint[a] = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||||
|
super.onSizeChanged(w, h, oldw, oldh);
|
||||||
|
for (int a = 0; a < gradientPaint.length; a++) {
|
||||||
|
int color1;
|
||||||
|
int color2;
|
||||||
|
int color3;
|
||||||
|
if (a == 0) {
|
||||||
|
color1 = 0xff57A4FE;
|
||||||
|
color2 = 0xff766EE9;
|
||||||
|
color3 = 0;
|
||||||
|
} else if (a == 1) {
|
||||||
|
color1 = 0xff77E55C;
|
||||||
|
color2 = 0xff56C7FE;
|
||||||
|
color3 = 0;
|
||||||
|
} else {
|
||||||
|
color1 = 0xff766EE9;
|
||||||
|
color2 = 0xffF05459;
|
||||||
|
color3 = 0xffE4A756;
|
||||||
|
}
|
||||||
|
Shader gradient;
|
||||||
|
if (color3 != 0) {
|
||||||
|
gradient = new LinearGradient(0, 0, getMeasuredWidth(), 0, new int[]{color1, color2, color3}, null, Shader.TileMode.CLAMP);
|
||||||
|
} else {
|
||||||
|
gradient = new LinearGradient(0, 0, getMeasuredWidth(), 0, new int[]{color1, color2}, null, Shader.TileMode.CLAMP);
|
||||||
|
}
|
||||||
|
gradientPaint[a].setShader(gradient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas) {
|
||||||
|
AndroidUtilities.rectTmp.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||||
|
gradientPaint[currentPage].setAlpha(255);
|
||||||
|
canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(6), AndroidUtilities.dp(6), gradientPaint[currentPage]);
|
||||||
|
if (pageOffset > 0 && currentPage + 1 < gradientPaint.length) {
|
||||||
|
gradientPaint[currentPage + 1].setAlpha((int) (255 * pageOffset));
|
||||||
|
canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(6), AndroidUtilities.dp(6), gradientPaint[currentPage + 1]);
|
||||||
|
}
|
||||||
|
super.onDraw(canvas);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
positiveButton.setMinWidth(AndroidUtilities.dp(64));
|
||||||
|
positiveButton.setTag(Dialog.BUTTON_POSITIVE);
|
||||||
|
positiveButton.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
|
||||||
|
positiveButton.setTextColor(Theme.getColor(Theme.key_voipgroup_nameText));
|
||||||
|
positiveButton.setGravity(Gravity.CENTER);
|
||||||
|
positiveButton.setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||||
|
positiveButton.setText(LocaleController.getString("VoipRecordStart", R.string.VoipRecordStart));
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
positiveButton.setForeground(Theme.createSimpleSelectorRoundRectDrawable(AndroidUtilities.dp(6), Color.TRANSPARENT, ColorUtils.setAlphaComponent(Theme.getColor(Theme.key_voipgroup_nameText), (int) (255 * 0.3f))));
|
||||||
|
}
|
||||||
|
positiveButton.setPadding(0, AndroidUtilities.dp(12), 0, AndroidUtilities.dp(12));
|
||||||
|
positiveButton.setOnClickListener(view -> {
|
||||||
|
onStartRecord(currentPage);
|
||||||
|
dismiss();
|
||||||
|
});
|
||||||
|
|
||||||
|
containerView.addView(positiveButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.BOTTOM, 0, 0, 0, 64));
|
||||||
|
|
||||||
|
titlesLayout = new LinearLayout(context);
|
||||||
|
containerView.addView(titlesLayout, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, 64, Gravity.BOTTOM));
|
||||||
|
|
||||||
|
for (int a = 0; a < titles.length; a++) {
|
||||||
|
titles[a] = new TextView(context);
|
||||||
|
titles[a].setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);
|
||||||
|
titles[a].setTextColor(0xffffffff);
|
||||||
|
titles[a].setTypeface(AndroidUtilities.getTypeface("fonts/rmedium.ttf"));
|
||||||
|
titles[a].setPadding(AndroidUtilities.dp(10), 0, AndroidUtilities.dp(10), 0);
|
||||||
|
titles[a].setGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
titles[a].setSingleLine(true);
|
||||||
|
titlesLayout.addView(titles[a], LayoutHelper.createLinear(LayoutHelper.WRAP_CONTENT, LayoutHelper.MATCH_PARENT));
|
||||||
|
if (a == 0) {
|
||||||
|
titles[a].setText(LocaleController.getString("VoipRecordAudio", R.string.VoipRecordAudio));
|
||||||
|
} else if (a == 1) {
|
||||||
|
titles[a].setText(LocaleController.getString("VoipRecordPortrait", R.string.VoipRecordPortrait));
|
||||||
|
} else {
|
||||||
|
titles[a].setText(LocaleController.getString("VoipRecordLandscape", R.string.VoipRecordLandscape));
|
||||||
|
}
|
||||||
|
int num = a;
|
||||||
|
titles[a].setOnClickListener(view -> viewPager.setCurrentItem(num, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTitlesLayout() {
|
||||||
|
View current = titles[currentPage];
|
||||||
|
View next = currentPage < titles.length - 1 ? titles[currentPage + 1] : null;
|
||||||
|
float cx = containerView.getMeasuredWidth() / 2;
|
||||||
|
float currentCx = current.getLeft() + current.getMeasuredWidth() / 2;
|
||||||
|
float tx = containerView.getMeasuredWidth() / 2 - currentCx;
|
||||||
|
if (next != null) {
|
||||||
|
float nextCx = next.getLeft() + next.getMeasuredWidth() / 2;
|
||||||
|
tx -= (nextCx - currentCx) * pageOffset;
|
||||||
|
}
|
||||||
|
for (int a = 0; a < titles.length; a++) {
|
||||||
|
float alpha;
|
||||||
|
float scale;
|
||||||
|
if (a < currentPage || a > currentPage + 1) {
|
||||||
|
alpha = 0.7f;
|
||||||
|
scale = 0.9f;
|
||||||
|
} else if (a == currentPage) {
|
||||||
|
alpha = 1.0f - 0.3f * pageOffset;
|
||||||
|
scale = 1.0f - 0.1f * pageOffset;
|
||||||
|
} else {
|
||||||
|
alpha = 0.7f + 0.3f * pageOffset;
|
||||||
|
scale = 0.9f + 0.1f * pageOffset;
|
||||||
|
}
|
||||||
|
titles[a].setAlpha(alpha);
|
||||||
|
titles[a].setScaleX(scale);
|
||||||
|
titles[a].setScaleY(scale);
|
||||||
|
}
|
||||||
|
titlesLayout.setTranslationX(tx);
|
||||||
|
positiveButton.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onStartRecord(int type) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Adapter extends PagerAdapter {
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return titles.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object instantiateItem(ViewGroup container, int position) {
|
||||||
|
View view;
|
||||||
|
|
||||||
|
ImageView imageView = new ImageView(getContext());
|
||||||
|
imageView.setTag(position);
|
||||||
|
imageView.setPadding(AndroidUtilities.dp(18), 0, AndroidUtilities.dp(18), 0);
|
||||||
|
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
|
||||||
|
imageView.setLayoutParams(new ViewGroup.LayoutParams(AndroidUtilities.dp(200), ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
view = imageView;
|
||||||
|
int res;
|
||||||
|
if (position == 0) {
|
||||||
|
res = R.raw.record_audio;
|
||||||
|
} else if (position == 1) {
|
||||||
|
res = R.raw.record_video_p;
|
||||||
|
} else {
|
||||||
|
res = R.raw.record_video_l;
|
||||||
|
}
|
||||||
|
String svg = RLottieDrawable.readRes(null, res);
|
||||||
|
SvgHelper.SvgDrawable drawable = SvgHelper.getDrawable(svg);
|
||||||
|
drawable.setAspectFill(false);
|
||||||
|
imageView.setImageDrawable(drawable);
|
||||||
|
if (view.getParent() != null) {
|
||||||
|
ViewGroup parent = (ViewGroup) view.getParent();
|
||||||
|
parent.removeView(view);
|
||||||
|
}
|
||||||
|
container.addView(view, 0);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroyItem(ViewGroup container, int position, Object object) {
|
||||||
|
container.removeView((View) object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setPrimaryItem(ViewGroup container, int position, Object object) {
|
||||||
|
super.setPrimaryItem(container, position, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isViewFromObject(View view, Object object) {
|
||||||
|
return view.equals(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void restoreState(Parcelable arg0, ClassLoader arg1) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Parcelable saveState() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterDataSetObserver(DataSetObserver observer) {
|
||||||
|
if (observer != null) {
|
||||||
|
super.unregisterDataSetObserver(observer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue